├── .github ├── .gitignore └── workflows │ ├── pkgdown-build │ └── action.yml │ ├── roxygenize │ └── action.yml │ ├── git-identity │ └── action.yml │ ├── pkgdown-deploy │ └── action.yml │ ├── dep-suggests-matrix │ ├── action.yml │ └── action.R │ ├── rate-limit │ └── action.yml │ ├── versions-matrix │ ├── action.yml │ └── action.R │ ├── get-extra │ └── action.yml │ ├── matrix-check │ └── action.yml │ ├── lock.yaml │ ├── check │ └── action.yml │ ├── repo-state │ └── action.yml │ ├── style │ └── action.yml │ ├── covr │ └── action.yml │ ├── pkgdown.yaml │ ├── format-suggest.yaml │ ├── copilot-setup-steps.yaml │ ├── R-CMD-check-status.yaml │ ├── commit-suggest.yaml │ ├── pr-commands.yaml │ ├── update-snapshots │ └── action.yml │ ├── R-CMD-check-dev.yaml │ ├── dep-matrix │ └── action.yml │ ├── commit │ └── action.yml │ ├── install │ └── action.yml │ ├── revdep.yaml │ └── R-CMD-check.yaml ├── .gitignore ├── man ├── figures │ ├── poof1.gif │ ├── poof2.gif │ └── poof3.gif ├── addin.Rd ├── system_has_internet.Rd ├── file-condition-helpers.Rd ├── project-condition-helpers.Rd ├── edit_yaml_tricks.Rd ├── fails.Rd ├── use_yaml_tricks.Rd ├── load_tricks.Rd ├── clipboard-condition-helpers.Rd ├── install_tricks.Rd ├── load_yaml_tricks.Rd ├── mock_context.Rd ├── new_trick.Rd ├── yaml_to_trick_list.Rd ├── action-helpers.Rd ├── current_selection.Rd └── selection-condition-helpers.Rd ├── R ├── 00_globals.R ├── 09_system-condition-helpers.R ├── 05_project-condition-helpers.R ├── 04_current_file-condition-helpers.R ├── 06_clipboard-condition-helpers.R ├── 11-mock.R ├── new_trick.R ├── zzz.R ├── 01_addin.R ├── 02_tricks.R ├── 07_action-helpers.R ├── 09_utils.R ├── 08_current_selection.R ├── 03_selection-condition-helpers.R └── 10-yaml.R ├── .Rbuildignore ├── inst ├── rstudio │ └── addins.dcf ├── tricks.yaml └── default_tricks │ └── default_tricks.R ├── tricks.Rproj ├── DESCRIPTION ├── NAMESPACE ├── README.md └── README.Rmd /.github/.gitignore: -------------------------------------------------------------------------------- 1 | /pkg.lock 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .Rdata 4 | .httr-oauth 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /man/figures/poof1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moodymudskipper/tricks/HEAD/man/figures/poof1.gif -------------------------------------------------------------------------------- /man/figures/poof2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moodymudskipper/tricks/HEAD/man/figures/poof2.gif -------------------------------------------------------------------------------- /man/figures/poof3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moodymudskipper/tricks/HEAD/man/figures/poof3.gif -------------------------------------------------------------------------------- /R/00_globals.R: -------------------------------------------------------------------------------- 1 | 2 | globals <- new.env() 3 | globals$tricks <- new.env() 4 | globals$mock_contest <- NULL 5 | -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^tricks\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^examples$ 4 | ^README.Rmd 5 | ^\.r-tricks\.yaml$ 6 | ^\.github 7 | -------------------------------------------------------------------------------- /inst/rstudio/addins.dcf: -------------------------------------------------------------------------------- 1 | Name: tricks 2 | Description: Trigger `tricks::addin()` 3 | Binding: addin 4 | Interactive: false 5 | 6 | -------------------------------------------------------------------------------- /man/addin.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/01_addin.R 3 | \name{addin} 4 | \alias{addin} 5 | \title{addin} 6 | \usage{ 7 | addin() 8 | } 9 | \description{ 10 | addin 11 | } 12 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown-build/action.yml: -------------------------------------------------------------------------------- 1 | name: "Action to build a pkgdown website" 2 | 3 | runs: 4 | using: "composite" 5 | steps: 6 | - name: Build site 7 | run: | 8 | pkgdown::build_site() 9 | shell: Rscript {0} 10 | -------------------------------------------------------------------------------- /.github/workflows/roxygenize/action.yml: -------------------------------------------------------------------------------- 1 | name: "Action to create documentation with roxygen2" 2 | 3 | runs: 4 | using: "composite" 5 | steps: 6 | - name: Roxygenize 7 | run: | 8 | try(roxygen2::roxygenize()) 9 | shell: Rscript {0} 10 | -------------------------------------------------------------------------------- /R/09_system-condition-helpers.R: -------------------------------------------------------------------------------- 1 | #' System Focused Condition Helpers 2 | #' 3 | #' @export 4 | system_has_internet <- function() { 5 | curl::has_internet() 6 | } 7 | 8 | # system_is_windows <- function() { 9 | # 10 | # } 11 | # 12 | # system_is_mac <- function() { 13 | # 14 | # } 15 | # 16 | # system_is_linux <- function() { 17 | # 18 | # } 19 | -------------------------------------------------------------------------------- /R/05_project-condition-helpers.R: -------------------------------------------------------------------------------- 1 | #' Project Focused Condition Helpers 2 | #' @export 3 | #' @rdname project-condition-helpers 4 | project_is_package <- function() { 5 | file.exists("NAMESPACE") 6 | } 7 | 8 | 9 | #' @export 10 | #' @rdname project-condition-helpers 11 | project_uses_git <- function() { 12 | file.exists(".gitignore") 13 | } 14 | -------------------------------------------------------------------------------- /man/system_has_internet.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/09_system-condition-helpers.R 3 | \name{system_has_internet} 4 | \alias{system_has_internet} 5 | \title{System Focused Condition Helpers} 6 | \usage{ 7 | system_has_internet() 8 | } 9 | \description{ 10 | System Focused Condition Helpers 11 | } 12 | -------------------------------------------------------------------------------- /.github/workflows/git-identity/action.yml: -------------------------------------------------------------------------------- 1 | name: "Actions to set up a Git identity" 2 | 3 | runs: 4 | using: "composite" 5 | steps: 6 | - name: Configure Git identity 7 | run: | 8 | env | sort 9 | git config --local user.name "$GITHUB_ACTOR" 10 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 11 | shell: bash 12 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown-deploy/action.yml: -------------------------------------------------------------------------------- 1 | name: "Action to deploy a pkgdown website" 2 | 3 | runs: 4 | using: "composite" 5 | steps: 6 | - name: Deploy site 7 | uses: nick-fields/retry@v3 8 | with: 9 | timeout_minutes: 15 10 | max_attempts: 10 11 | command: | 12 | R -q -e 'pkgdown::deploy_to_branch(new_process = FALSE)' 13 | -------------------------------------------------------------------------------- /R/04_current_file-condition-helpers.R: -------------------------------------------------------------------------------- 1 | #' File Focused Condition Helpers 2 | #' @export 3 | #' @rdname file-condition-helpers 4 | document_is_r_script <- function() { 5 | toupper(tools::file_ext(current_path())) == "R" 6 | } 7 | 8 | #' @export 9 | #' @rdname file-condition-helpers 10 | document_is_rmd <- function() { 11 | toupper(tools::file_ext(current_path())) == "RMD" 12 | } 13 | -------------------------------------------------------------------------------- /man/file-condition-helpers.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/04_current_file-condition-helpers.R 3 | \name{document_is_r_script} 4 | \alias{document_is_r_script} 5 | \alias{document_is_rmd} 6 | \title{File Focused Condition Helpers} 7 | \usage{ 8 | document_is_r_script() 9 | 10 | document_is_rmd() 11 | } 12 | \description{ 13 | File Focused Condition Helpers 14 | } 15 | -------------------------------------------------------------------------------- /man/project-condition-helpers.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/05_project-condition-helpers.R 3 | \name{project_is_package} 4 | \alias{project_is_package} 5 | \alias{project_uses_git} 6 | \title{Project Focused Condition Helpers} 7 | \usage{ 8 | project_is_package() 9 | 10 | project_uses_git() 11 | } 12 | \description{ 13 | Project Focused Condition Helpers 14 | } 15 | -------------------------------------------------------------------------------- /.github/workflows/dep-suggests-matrix/action.yml: -------------------------------------------------------------------------------- 1 | name: "Actions to compute a matrix with all suggested packages" 2 | outputs: 3 | matrix: 4 | description: "Generated matrix" 5 | value: ${{ steps.set-matrix.outputs.matrix }} 6 | 7 | runs: 8 | using: "composite" 9 | steps: 10 | - id: set-matrix 11 | run: | 12 | Rscript ./.github/workflows/dep-suggests-matrix/action.R 13 | shell: bash 14 | -------------------------------------------------------------------------------- /.github/workflows/rate-limit/action.yml: -------------------------------------------------------------------------------- 1 | name: "Check GitHub rate limits" 2 | inputs: 3 | token: # id of input 4 | description: GitHub token, pass secrets.GITHUB_TOKEN 5 | required: true 6 | 7 | runs: 8 | using: "composite" 9 | steps: 10 | - name: Check rate limits 11 | run: | 12 | curl -s --header "authorization: Bearer ${{ inputs.token }}" https://api.github.com/rate_limit 13 | shell: bash 14 | -------------------------------------------------------------------------------- /tricks.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: No 4 | SaveWorkspace: No 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | LineEndingConversion: Posix 18 | 19 | BuildType: Package 20 | PackageUseDevtools: Yes 21 | PackageInstallArgs: --no-multiarch --with-keep.source 22 | PackageRoxygenize: rd,collate,namespace 23 | -------------------------------------------------------------------------------- /.github/workflows/versions-matrix/action.yml: -------------------------------------------------------------------------------- 1 | name: "Actions to compute a matrix with all R and OS versions" 2 | 3 | outputs: 4 | matrix: 5 | description: "Generated matrix" 6 | value: ${{ steps.set-matrix.outputs.matrix }} 7 | 8 | runs: 9 | using: "composite" 10 | steps: 11 | - name: Install json2yaml 12 | run: | 13 | sudo npm install -g json2yaml 14 | shell: bash 15 | 16 | - id: set-matrix 17 | run: | 18 | Rscript ./.github/workflows/versions-matrix/action.R 19 | shell: bash 20 | -------------------------------------------------------------------------------- /.github/workflows/get-extra/action.yml: -------------------------------------------------------------------------------- 1 | name: "Action to determine extra packages to be installed" 2 | outputs: 3 | packages: 4 | description: "List of extra packages" 5 | value: ${{ steps.get-extra.outputs.packages }} 6 | 7 | runs: 8 | using: "composite" 9 | steps: 10 | - name: Get extra packages 11 | id: get-extra 12 | run: | 13 | set -x 14 | packages=$( ( grep Config/gha/extra-packages DESCRIPTION || true ) | cut -d " " -f 2) 15 | echo packages=$packages >> $GITHUB_OUTPUT 16 | shell: bash 17 | -------------------------------------------------------------------------------- /man/edit_yaml_tricks.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/10-yaml.R 3 | \name{edit_yaml_tricks} 4 | \alias{edit_yaml_tricks} 5 | \title{Edit YAML tricks} 6 | \usage{ 7 | edit_yaml_tricks(project_level = FALSE) 8 | } 9 | \arguments{ 10 | \item{project_level}{whether to open the project level file ("./.r-tricks.yaml"), 11 | by default opens the user level file ("~/.r-tricks.yaml")} 12 | } 13 | \value{ 14 | Returns the input invisibly, called for side effects. 15 | } 16 | \description{ 17 | Edit YAML tricks 18 | } 19 | -------------------------------------------------------------------------------- /.github/workflows/matrix-check/action.yml: -------------------------------------------------------------------------------- 1 | name: "Actions to check a matrix with all R and OS versions, computed with the versions-matrix action" 2 | inputs: 3 | matrix: 4 | description: "Generated matrix" 5 | required: true 6 | 7 | runs: 8 | using: "composite" 9 | steps: 10 | - name: Install json2yaml 11 | run: | 12 | sudo npm install -g json2yaml 13 | shell: bash 14 | 15 | - run: | 16 | matrix='${{ inputs.matrix }}' 17 | if [ -n "${matrix}" ]; then 18 | echo $matrix | jq . 19 | echo $matrix | json2yaml 20 | else 21 | echo "No matrix found" 22 | fi 23 | shell: bash 24 | -------------------------------------------------------------------------------- /man/fails.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/09_utils.R 3 | \name{fails} 4 | \alias{fails} 5 | \title{Test if a Call Fails} 6 | \usage{ 7 | fails(call) 8 | } 9 | \arguments{ 10 | \item{call}{A call} 11 | } 12 | \description{ 13 | This is mainly used internally and should be rarely needed but can be useful 14 | for some custom trick conditions. Use this with caution! The tested call is run 15 | so you don't want it to have potential side effects or be slow! 16 | } 17 | \details{ 18 | The potential failure will be totally silent (no printed output, no error, no message, no warning). 19 | Other side effects should not be an issues if the function is used properly. 20 | } 21 | -------------------------------------------------------------------------------- /.github/workflows/lock.yaml: -------------------------------------------------------------------------------- 1 | name: "Lock threads" 2 | permissions: 3 | issues: write 4 | pull-requests: write 5 | discussions: write 6 | on: 7 | workflow_dispatch: 8 | schedule: 9 | - cron: "37 2 * * *" 10 | 11 | jobs: 12 | lock: 13 | runs-on: ubuntu-24.04 14 | steps: 15 | - uses: krlmlr/lock-threads@patch-1 16 | with: 17 | github-token: ${{ github.token }} 18 | issue-inactive-days: "365" 19 | issue-lock-reason: "" 20 | issue-comment: > 21 | This old thread has been automatically locked. If you think you have 22 | found something related to this, please open a new issue and link to this 23 | old issue if necessary. 24 | -------------------------------------------------------------------------------- /man/use_yaml_tricks.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/10-yaml.R 3 | \name{use_yaml_tricks} 4 | \alias{use_yaml_tricks} 5 | \title{Use YAML tricks} 6 | \usage{ 7 | use_yaml_tricks(project_level = FALSE) 8 | } 9 | \arguments{ 10 | \item{project_level}{Whether to setup tricks at the project level} 11 | } 12 | \value{ 13 | Returns the input invisibly, called for side effects. 14 | } 15 | \description{ 16 | Sets up YAML tricks for user or project 17 | } 18 | \details{ 19 | \itemize{ 20 | \item Edits user level or project level ".RProfile" to add a call to \code{load_yaml_tricks()} 21 | \item Optionally runs installation of tricks from \pkg{tricks} package (default for user level YAML) 22 | \item Adds the project level \code{.r-tricks.yaml} file to \code{.Rbuildignore} 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: tricks 2 | Title: A Single Addin for Context Dependent Actions 3 | Version: 0.0.0.9000 4 | Authors@R: 5 | person(given = "Antoine", 6 | family = "Fabri", 7 | role = c("aut", "cre"), 8 | email = "antoine.fabri@gmail.com") 9 | Description: A Single Addin for Context Dependent Actions. 10 | License: GPL-3 11 | Encoding: UTF-8 12 | Language: en 13 | LazyData: true 14 | Roxygen: list(markdown = TRUE) 15 | RoxygenNote: 7.3.3.9000 16 | URL: https://github.com/moodymudskipper/tricks 17 | BugReports: https://github.com/moodymudskipper/tricks/issues 18 | Imports: 19 | glue, 20 | jsonlite, 21 | memoise, 22 | rstudioapi, 23 | usethis, 24 | methods, 25 | RCurl, 26 | clipr, 27 | yaml, 28 | rlang 29 | Config/gha/filter: os != "windows-latest" | r != "devel" 30 | -------------------------------------------------------------------------------- /man/load_tricks.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/02_tricks.R 3 | \name{load_tricks} 4 | \alias{load_tricks} 5 | \alias{unload_tricks} 6 | \alias{uninstall_tricks} 7 | \alias{loaded_tricks} 8 | \alias{edit_tricks} 9 | \title{Add, Remove or Show Tricks} 10 | \usage{ 11 | load_tricks(..., .reset = FALSE) 12 | 13 | unload_tricks(...) 14 | 15 | uninstall_tricks(..., project_level = FALSE) 16 | 17 | loaded_tricks() 18 | 19 | edit_tricks(project_level = FALSE) 20 | } 21 | \arguments{ 22 | \item{...}{tricks to add (or replace), or names of tricks to remove} 23 | 24 | \item{.reset}{whether to remove existing tricks} 25 | 26 | \item{project_level}{whether to open the project level file ("./.r-tricks.yaml"), 27 | by default opens the user level file ("~/.r-tricks.yaml")} 28 | } 29 | \description{ 30 | Add 31 | } 32 | -------------------------------------------------------------------------------- /man/clipboard-condition-helpers.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/06_clipboard-condition-helpers.R 3 | \name{clipboard_contains_text} 4 | \alias{clipboard_contains_text} 5 | \alias{clipboard_is_parsable} 6 | \alias{clipboard_text} 7 | \alias{clip_board_text_matches} 8 | \title{Clipboard Focused Condition Helpers} 9 | \usage{ 10 | clipboard_contains_text() 11 | 12 | clipboard_is_parsable() 13 | 14 | clipboard_text() 15 | 16 | clip_board_text_matches(pattern, n_min = 1L, n_max = Inf, ...) 17 | } 18 | \arguments{ 19 | \item{pattern}{regular expression} 20 | 21 | \item{n_min}{minimal number of occurences to validate match} 22 | 23 | \item{n_max}{maximal number of occurences to validate match} 24 | 25 | \item{...}{additional arguments passed to \code{regexpr()}} 26 | } 27 | \description{ 28 | Clipboard Focused Condition Helpers 29 | } 30 | -------------------------------------------------------------------------------- /man/install_tricks.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/10-yaml.R 3 | \name{install_tricks} 4 | \alias{install_tricks} 5 | \title{Install tricks from a package or path} 6 | \usage{ 7 | install_tricks(source = "tricks", project_level = FALSE, all = FALSE) 8 | } 9 | \arguments{ 10 | \item{source}{The name of a package containing tricks., or a path to a YAML 11 | file defining tricks, By default proposes tricks from the \pkg{tricks} package} 12 | 13 | \item{project_level}{whether to install tricks at the project level (in "./.r-tricks.yaml"), 14 | by default they're installed at the user level (in "~/.r-tricks.yaml")} 15 | 16 | \item{all}{whether to install all tricks without prompting} 17 | } 18 | \value{ 19 | Returns the first argument invisibly, called for side effects. 20 | } 21 | \description{ 22 | Install tricks from a package or path 23 | } 24 | -------------------------------------------------------------------------------- /man/load_yaml_tricks.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/10-yaml.R 3 | \name{load_yaml_tricks} 4 | \alias{load_yaml_tricks} 5 | \title{Load tricks from YAML file} 6 | \usage{ 7 | load_yaml_tricks(file = NULL, labels = NULL, reset = FALSE) 8 | } 9 | \arguments{ 10 | \item{file}{Path to a YAML file, if \code{NULL} (the default) tricks are loaded 11 | from the user level and project level \code{.r-tricks.yaml} files if they exist} 12 | 13 | \item{labels}{labels of tricks to load, by default all tricks are loaded} 14 | 15 | \item{reset}{Whether to unload previously loaded tricks} 16 | } 17 | \value{ 18 | Returns the first argument invisibly, called for side effects. 19 | } 20 | \description{ 21 | Load tricks from YAML file 22 | } 23 | \examples{ 24 | yaml_path <- system.file("tricks.yaml", package = "tricks") 25 | load_yaml_tricks(yaml_path) 26 | } 27 | -------------------------------------------------------------------------------- /.github/workflows/check/action.yml: -------------------------------------------------------------------------------- 1 | name: "Actions to check an R package" 2 | inputs: 3 | results: 4 | description: Slug for check results 5 | required: true 6 | 7 | runs: 8 | using: "composite" 9 | steps: 10 | - uses: r-lib/actions/check-r-package@v2 11 | with: 12 | # Fails on R 3.6 on Windows, remove when this job is removed? 13 | args: 'c("--no-manual", "--as-cran", "--no-multiarch")' 14 | error-on: ${{ env.RCMDCHECK_ERROR_ON || '"note"' }} 15 | 16 | - name: Show test output 17 | if: always() 18 | run: | 19 | ## -- Show test output -- 20 | echo "::group::Test output" 21 | find check -name '*.Rout*' -exec head -n 1000000 '{}' \; || true 22 | echo "::endgroup::" 23 | shell: bash 24 | 25 | - name: Upload check results 26 | if: failure() 27 | uses: actions/upload-artifact@main 28 | with: 29 | name: ${{ inputs.results }}-results 30 | path: check 31 | -------------------------------------------------------------------------------- /R/06_clipboard-condition-helpers.R: -------------------------------------------------------------------------------- 1 | #' Clipboard Focused Condition Helpers 2 | #' @export 3 | #' @rdname clipboard-condition-helpers 4 | #' @inheritParams current_selection 5 | #' @inheritParams selection-condition-helpers 6 | clipboard_contains_text <- function() { 7 | w <- options(warn = 2) 8 | on.exit(options(warn = w$warn)) 9 | !fails(clipboard_text()) 10 | } 11 | 12 | #' @export 13 | #' @rdname clipboard-condition-helpers 14 | clipboard_is_parsable <- function() { 15 | clipboard_contains_text() && !fails(parse(text = clipboard_text())) 16 | } 17 | 18 | #' @export 19 | #' @rdname clipboard-condition-helpers 20 | clipboard_text <- function() { 21 | clipr::read_clip() 22 | } 23 | 24 | #' @export 25 | #' 26 | #' @rdname clipboard-condition-helpers 27 | clip_board_text_matches <- function(pattern, n_min = 1L, n_max = Inf, ...) { 28 | clipboard_contains_text() && { 29 | sum_ <- sum(regexpr(pattern, clipboard_text(), ...)) 30 | sum_ >= n_min && sum_ <= n_max 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /man/mock_context.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/11-mock.R 3 | \name{mock_context} 4 | \alias{mock_context} 5 | \title{Mock context to test tricks} 6 | \usage{ 7 | mock_context(contents = "", id = 1, path = "", selection = NULL) 8 | } 9 | \arguments{ 10 | \item{contents}{A character vector containing lines of code} 11 | 12 | \item{id}{A number} 13 | 14 | \item{path}{A string} 15 | 16 | \item{selection}{A nested list where elements are named lists of \code{range} created 17 | by \code{rstudioapi::document_range()} and \code{position} created by \code{rstudioapi::document_range()}. 18 | By default \code{contents} is fully selected, which should be enough for most tests.} 19 | } 20 | \value{ 21 | Returns \code{NULL} invisibly, Called for side effects. 22 | } 23 | \description{ 24 | \code{mock_context()} does some magic so calls to \code{current_selection()} and other 25 | functions using context information (i.e. document content, id, path, and selection(s)) 26 | will fetch preset data. It is useful to design tests. call \code{unmock} 27 | } 28 | -------------------------------------------------------------------------------- /man/new_trick.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/new_trick.R 3 | \name{new_trick} 4 | \alias{new_trick} 5 | \title{Create a new trick} 6 | \usage{ 7 | new_trick(label, condition, action, description = label, ...) 8 | } 9 | \arguments{ 10 | \item{label}{A string} 11 | 12 | \item{condition}{A call or a one sided formula, using condition helpers and context informers, whose 13 | right hand side expressions returns a boolean when evaluated by \pkg{tricks}} 14 | 15 | \item{action}{A call or a one sided formula, using action helpers and context informers, whose 16 | right hand side triggers the desired actions when evaluated by \pkg{tricks}} 17 | 18 | \item{description}{The description of the trick, for documentation purposes} 19 | 20 | \item{...}{other properties to print with the object and in YAML files} 21 | } 22 | \value{ 23 | A list of class "trick_object" 24 | } 25 | \description{ 26 | Create a trick object 27 | } 28 | \examples{ 29 | new_trick( 30 | "Edit user '.Rprofile'", 31 | ~ selection_is_empty(), 32 | ~ usethis::edit_r_profile() 33 | ) 34 | } 35 | -------------------------------------------------------------------------------- /.github/workflows/repo-state/action.yml: -------------------------------------------------------------------------------- 1 | name: "Determine repository state" 2 | description: "Expose protected, foreign, and is_pr context variables" 3 | outputs: 4 | protected: 5 | description: "Whether the current branch is protected" 6 | value: ${{ steps.state.outputs.protected }} 7 | foreign: 8 | description: "Whether the PR is from a foreign repository" 9 | value: ${{ steps.state.outputs.foreign }} 10 | is_pr: 11 | description: "Whether the event is a pull request" 12 | value: ${{ steps.state.outputs.is_pr }} 13 | 14 | runs: 15 | using: "composite" 16 | steps: 17 | - name: Determine repository state 18 | id: state 19 | run: | 20 | set -x 21 | protected=${{ github.ref_protected }} 22 | foreign=${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository }} 23 | is_pr=${{ github.event_name == 'pull_request' }} 24 | 25 | echo "protected=${protected}" | tee -a $GITHUB_OUTPUT 26 | echo "foreign=${foreign}" | tee -a $GITHUB_OUTPUT 27 | echo "is_pr=${is_pr}" | tee -a $GITHUB_OUTPUT 28 | shell: bash 29 | -------------------------------------------------------------------------------- /man/yaml_to_trick_list.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/10-yaml.R 3 | \name{yaml_to_trick_list} 4 | \alias{yaml_to_trick_list} 5 | \alias{trick_list_to_yaml} 6 | \title{Read YAML file into a list of trick objects and back} 7 | \usage{ 8 | yaml_to_trick_list(file) 9 | 10 | trick_list_to_yaml(tricks, file, append = TRUE) 11 | } 12 | \arguments{ 13 | \item{file}{Path to a YAML file defining tricks} 14 | 15 | \item{tricks}{list of tricks to write to \code{file}} 16 | 17 | \item{append}{Whether to append to the \code{file}, if \code{FALSE} the file will be overwrtitten.} 18 | } 19 | \value{ 20 | Returns the first argument invisibly, called for side effects. 21 | } 22 | \description{ 23 | You should rarely need to manipulate trick objects. A use case is creating a 24 | library of tricks from an existing 25 | } 26 | \examples{ 27 | yaml_path <- system.file("tricks.yaml", package = "tricks") 28 | tricks <- yaml_to_trick_list(yaml_path) 29 | labels <- c("Edit user '.Rprofile'","Edit project '.Rprofile'") 30 | trick_subset <- tricks[labels] 31 | new_trick_library_path <- tempfile(fileext = ".yaml") 32 | trick_list_to_yaml(trick_subset, new_trick_library_path) 33 | } 34 | -------------------------------------------------------------------------------- /man/action-helpers.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/07_action-helpers.R 3 | \name{action-helpers} 4 | \alias{action-helpers} 5 | \alias{call_addin} 6 | \alias{replace_selection} 7 | \alias{replace_current_lines} 8 | \alias{insert_at_position} 9 | \alias{send_cursor_at_position} 10 | \title{Action Helpers} 11 | \usage{ 12 | call_addin(package, name) 13 | 14 | replace_selection(txt) 15 | 16 | replace_current_lines(txt) 17 | 18 | insert_at_position(txt, row = Inf, col = Inf) 19 | 20 | send_cursor_at_position(row = 1, col = 1) 21 | } 22 | \arguments{ 23 | \item{package, name}{package and addin name} 24 | 25 | \item{txt}{A character object, or will be coerced to character using \code{deparse()}. 26 | Used as a replacement for the selection text.} 27 | 28 | \item{row, col}{row and column in he editor} 29 | } 30 | \description{ 31 | These helpers are meant to be used on the right hand side of the trick specification, 32 | which is evaluated when the trick is selected. 33 | } 34 | \details{ 35 | \itemize{ 36 | \item \code{replace_selection} replaces the selection in the editor with its argument 37 | \item \code{call_addin} calls a standard RStudio addin 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /.github/workflows/style/action.yml: -------------------------------------------------------------------------------- 1 | name: "Action to auto-style a package" 2 | 3 | runs: 4 | using: "composite" 5 | steps: 6 | - name: Check air.toml 7 | id: check 8 | run: | 9 | set -x 10 | if [ -f air.toml ]; then 11 | echo air_enabled=true >> $GITHUB_OUTPUT 12 | else 13 | echo air_enabled=false >> $GITHUB_OUTPUT 14 | fi 15 | if [ -f .clang-format ]; then 16 | echo clang_format_enabled=true >> $GITHUB_OUTPUT 17 | else 18 | echo clang_format_enabled=false >> $GITHUB_OUTPUT 19 | fi 20 | shell: bash 21 | 22 | - name: Install air 23 | if: ${{ steps.check.outputs.air_enabled == 'true' }} 24 | uses: posit-dev/setup-air@v1 25 | 26 | - name: Run air 27 | if: ${{ steps.check.outputs.air_enabled == 'true' }} 28 | run: | 29 | air format . 30 | shell: bash 31 | 32 | - name: Run clang-format 33 | if: ${{ steps.check.outputs.clang_format_enabled == 'true' }} 34 | run: | 35 | shopt -s nullglob 36 | clang-format -i src/*.{c,cc,cpp,h,hpp} 37 | shell: bash 38 | 39 | - name: Check Git status 40 | if: ${{ steps.check.outputs.air_enabled == 'true' || steps.check.outputs.clang_format_enabled == 'true' }} 41 | run: | 42 | git status 43 | shell: bash 44 | -------------------------------------------------------------------------------- /R/11-mock.R: -------------------------------------------------------------------------------- 1 | #' Mock context to test tricks 2 | #' 3 | #' `mock_context()` does some magic so calls to `current_selection()` and other 4 | #' functions using context information (i.e. document content, id, path, and selection(s)) 5 | #' will fetch preset data. It is useful to design tests. call `unmock` 6 | #' 7 | #' @param contents A character vector containing lines of code 8 | #' @param id A number 9 | #' @param path A string 10 | #' @param selection A nested list where elements are named lists of `range` created 11 | #' by `rstudioapi::document_range()` and `position` created by `rstudioapi::document_range()`. 12 | #' By default `contents` is fully selected, which should be enough for most tests. 13 | #' 14 | #' @return Returns `NULL` invisibly, Called for side effects. 15 | #' @export 16 | mock_context <- function( 17 | contents = "", 18 | id = 1, 19 | path = "", 20 | selection = NULL 21 | ) { 22 | if(is.null(selection)) { 23 | n <- length(contents) 24 | selection <- list(list( 25 | range = rstudioapi::document_range( 26 | start = rstudioapi::document_position(1, 1), 27 | end = rstudioapi::document_position(n, nchar(contents[[n]])) 28 | ), 29 | text = paste(contents, collapse = "\n") 30 | )) 31 | } 32 | # 33 | class(selection) <- "document_selection" 34 | context <- list(id = id, path = path, contents = contents, selection = selection) 35 | class(context) <- "document_context" 36 | globals$mock_context <- context 37 | invisible(NULL) 38 | } 39 | 40 | -------------------------------------------------------------------------------- /.github/workflows/covr/action.yml: -------------------------------------------------------------------------------- 1 | name: "Actions to run covr for an R package" 2 | description: "Run covr to check code coverage for an R package and upload results to Codecov." 3 | inputs: 4 | token: 5 | description: codecov token 6 | required: false 7 | 8 | runs: 9 | using: "composite" 10 | steps: 11 | - name: Run coverage check 12 | run: | 13 | if (dir.exists("tests/testthat")) { 14 | cov <- covr::package_coverage( 15 | quiet = FALSE, 16 | clean = FALSE, 17 | install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package") 18 | ) 19 | covr::to_cobertura(cov) 20 | } else { 21 | message("No tests found, coverage not tested.") 22 | } 23 | shell: Rscript {0} 24 | 25 | - uses: codecov/codecov-action@v5 26 | with: 27 | # Fail if token is given 28 | fail_ci_if_error: ${{ inputs.token != '' }} 29 | files: ./cobertura.xml 30 | plugins: noop 31 | disable_search: true 32 | token: ${{ inputs.token }} 33 | 34 | - name: Show testthat output 35 | if: always() 36 | run: | 37 | ## -------------------------------------------------------------------- 38 | find '${{ runner.temp }}/package' -name 'testthat.Rout*' -exec cat '{}' \; || true 39 | shell: bash 40 | 41 | - name: Upload test results 42 | if: failure() 43 | uses: actions/upload-artifact@v5 44 | with: 45 | name: coverage-test-failures 46 | path: ${{ runner.temp }}/package 47 | -------------------------------------------------------------------------------- /man/current_selection.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/08_current_selection.R 3 | \name{current_selection} 4 | \alias{current_selection} 5 | \alias{current_call} 6 | \alias{current_expr} 7 | \alias{current_value} 8 | \alias{current_line_numbers} 9 | \alias{current_lines} 10 | \alias{current_indentation} 11 | \alias{current_path} 12 | \alias{document_code} 13 | \alias{current_code_block} 14 | \alias{current_quoted_status} 15 | \title{Current selection} 16 | \usage{ 17 | current_selection( 18 | target = c("default", "lines", "script"), 19 | window = c("source", "console", "active") 20 | ) 21 | 22 | current_call(target = c("default", "lines", "script")) 23 | 24 | current_expr(target = c("default", "lines", "script")) 25 | 26 | current_value(target = c("default", "lines", "script")) 27 | 28 | current_line_numbers(target = c("default", "lines", "script")) 29 | 30 | current_lines(target = c("default", "lines", "script")) 31 | 32 | current_indentation(target = c("default", "lines", "script")) 33 | 34 | current_path(full = TRUE) 35 | 36 | document_code() 37 | 38 | current_code_block() 39 | 40 | current_quoted_status() 41 | } 42 | \arguments{ 43 | \item{target}{If \code{target} is \code{"lines"} the selection is extended to lines, 44 | if it is \code{"script"} it is extended to the full script} 45 | 46 | \item{window}{One of \code{"source"} (for the source window), \code{"console"} 47 | (for the console) or \code{"active"} (for the active document). 48 | See \link[rstudioapi:rstudio-editors]{rstudioapi::rstudio-editors}.} 49 | 50 | \item{full}{boolean. Whether to return full path or base name} 51 | } 52 | \description{ 53 | Utilities to get data about the selection 54 | } 55 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Also included in R-CMD-check.yaml, this workflow only listens to pushes to branches 3 | # that start with "docs*" or "cran-*" and does not need to act on pushes to the main branch. 4 | on: 5 | push: 6 | branches: 7 | - "docs*" 8 | - "cran-*" 9 | # The main branch is excluded here, it is handled by the R-CMD-check workflow. 10 | # This workflow is only for handling pushes to designated branches. 11 | workflow_dispatch: 12 | 13 | name: pkgdown 14 | 15 | concurrency: 16 | group: ${{ github.workflow }}-${{ github.ref }}-${{ github.head_ref || github.sha }}-${{ github.base_ref || '' }} 17 | cancel-in-progress: true 18 | 19 | jobs: 20 | pkgdown: 21 | runs-on: ubuntu-24.04 22 | 23 | name: "pkgdown" 24 | 25 | # Begin custom: services 26 | # End custom: services 27 | 28 | steps: 29 | - uses: actions/checkout@v4 30 | 31 | - uses: ./.github/workflows/rate-limit 32 | with: 33 | token: ${{ secrets.GITHUB_TOKEN }} 34 | 35 | - uses: ./.github/workflows/git-identity 36 | if: github.event_name == 'push' 37 | 38 | - uses: ./.github/workflows/custom/before-install 39 | if: hashFiles('.github/workflows/custom/before-install/action.yml') != '' 40 | 41 | - uses: ./.github/workflows/install 42 | with: 43 | token: ${{ secrets.GITHUB_TOKEN }} 44 | cache-version: pkgdown-2 45 | needs: website 46 | extra-packages: r-lib/pkgdown local::. 47 | 48 | - uses: ./.github/workflows/custom/after-install 49 | if: hashFiles('.github/workflows/custom/after-install/action.yml') != '' 50 | 51 | - uses: ./.github/workflows/pkgdown-build 52 | if: github.event_name != 'push' 53 | 54 | - uses: ./.github/workflows/pkgdown-deploy 55 | if: github.event_name == 'push' 56 | -------------------------------------------------------------------------------- /R/new_trick.R: -------------------------------------------------------------------------------- 1 | #' Create a new trick 2 | #' 3 | #' Create a trick object 4 | #' 5 | #' @param label A string 6 | #' @param condition A call or a one sided formula, using condition helpers and context informers, whose 7 | #' right hand side expressions returns a boolean when evaluated by \pkg{tricks} 8 | #' @param action A call or a one sided formula, using action helpers and context informers, whose 9 | #' right hand side triggers the desired actions when evaluated by \pkg{tricks} 10 | #' @param description The description of the trick, for documentation purposes 11 | #' @param ... other properties to print with the object and in YAML files 12 | #' 13 | #' @return A list of class "trick_object" 14 | #' @export 15 | #' 16 | #' @examples 17 | #' new_trick( 18 | #' "Edit user '.Rprofile'", 19 | #' ~ selection_is_empty(), 20 | #'~ usethis::edit_r_profile() 21 | #' ) 22 | new_trick <- function(label, condition, action, description = label, ...) { 23 | 24 | if (rlang::is_formula(condition)) { 25 | condition <- condition[[2]] 26 | } 27 | 28 | if (rlang::is_formula(action)) { 29 | action <- action[[2]] 30 | } 31 | 32 | if (!rlang::is_string(label)) { 33 | rlang::abort("`label` should be a string") 34 | } 35 | 36 | if (!rlang::is_string(description)) { 37 | rlang::abort("`description` should be a string") 38 | } 39 | 40 | if (!is.call(condition)) { 41 | rlang::abort("`condition` should be a call or a formula") 42 | } 43 | 44 | if (!is.call(action)) { 45 | rlang::abort("`action` should be a call or a formula") 46 | } 47 | 48 | trick <- list( 49 | label = label, 50 | description = description, 51 | condition = condition, 52 | action = action, 53 | ... 54 | ) 55 | structure(trick, class = "trick_object") 56 | } 57 | 58 | #' @export 59 | print.trick_object <- function(x, ...) { 60 | writeLines(c( 61 | "# A trick for the {tricks} package", 62 | trick_to_yaml_string(x) 63 | )) 64 | invisible(x) 65 | } 66 | 67 | 68 | -------------------------------------------------------------------------------- /.github/workflows/format-suggest.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/posit-dev/setup-air/tree/main/examples 2 | 3 | on: 4 | # Using `pull_request_target` over `pull_request` for elevated `GITHUB_TOKEN` 5 | # privileges, otherwise we can't set `pull-requests: write` when the pull 6 | # request comes from a fork, which is our main use case (external contributors). 7 | # 8 | # `pull_request_target` runs in the context of the target branch (`main`, usually), 9 | # rather than in the context of the pull request like `pull_request` does. Due 10 | # to this, we must explicitly checkout `ref: ${{ github.event.pull_request.head.sha }}`. 11 | # This is typically frowned upon by GitHub, as it exposes you to potentially running 12 | # untrusted code in a context where you have elevated privileges, but they explicitly 13 | # call out the use case of reformatting and committing back / commenting on the PR 14 | # as a situation that should be safe (because we aren't actually running the untrusted 15 | # code, we are just treating it as passive data). 16 | # https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/ 17 | pull_request_target: 18 | 19 | name: format-suggest.yaml 20 | 21 | jobs: 22 | format-suggest: 23 | name: format-suggest 24 | runs-on: ubuntu-latest 25 | # Only run this job if changes come from a fork. 26 | # We commit changes directly on the main repository. 27 | if: github.event.pull_request.head.repo.full_name != github.repository 28 | 29 | permissions: 30 | # Required to push suggestion comments to the PR 31 | pull-requests: write 32 | 33 | steps: 34 | - uses: actions/checkout@v4 35 | with: 36 | ref: ${{ github.event.pull_request.head.sha }} 37 | 38 | - uses: ./.github/workflows/style 39 | 40 | - name: Suggest 41 | uses: reviewdog/action-suggester@v1 42 | with: 43 | level: error 44 | fail_level: error 45 | tool_name: air-and-clang-format 46 | -------------------------------------------------------------------------------- /.github/workflows/dep-suggests-matrix/action.R: -------------------------------------------------------------------------------- 1 | # FIXME: Dynamic lookup by parsing https://svn.r-project.org/R/tags/ 2 | get_deps <- function() { 3 | # Determine package dependencies 4 | if (!requireNamespace("desc", quietly = TRUE)) { 5 | install.packages("desc") 6 | } 7 | 8 | deps_df <- desc::desc_get_deps() 9 | deps_df_optional <- deps_df$package[deps_df$type %in% c("Suggests", "Enhances")] 10 | deps_df_hard <- deps_df$package[deps_df$type %in% c("Depends", "Imports", "LinkingTo")] 11 | deps_df_base <- unlist(tools::standard_package_names(), use.names = FALSE) 12 | 13 | packages <- sort(deps_df_optional) 14 | packages <- intersect(packages, rownames(available.packages())) 15 | 16 | # Too big to fail, or can't be avoided: 17 | off_limits <- c("testthat", "rmarkdown", "rcmdcheck", deps_df_hard, deps_df_base) 18 | off_limits_dep <- unlist(tools::package_dependencies(off_limits, recursive = TRUE, which = "strong")) 19 | setdiff(packages, c(off_limits, off_limits_dep)) 20 | } 21 | 22 | if (Sys.getenv("GITHUB_BASE_REF") != "") { 23 | print(Sys.getenv("GITHUB_BASE_REF")) 24 | system("git fetch origin ${GITHUB_BASE_REF}") 25 | # Use .. to avoid having to fetch the entire history 26 | # https://github.com/krlmlr/actions-sync/issues/45 27 | diff_cmd <- "git diff origin/${GITHUB_BASE_REF}.. -- R/ tests/ | egrep '^[+][^+]' | grep -q ::" 28 | diff_lines <- system(diff_cmd, intern = TRUE) 29 | if (length(diff_lines) > 0) { 30 | writeLines("Changes using :: in R/ or tests/:") 31 | writeLines(diff_lines) 32 | packages <- get_deps() 33 | } else { 34 | writeLines("No changes using :: found in R/ or tests/, not checking without suggested packages") 35 | packages <- character() 36 | } 37 | } else { 38 | writeLines("No GITHUB_BASE_REF, checking without suggested packages") 39 | packages <- get_deps() 40 | } 41 | 42 | if (length(packages) > 0) { 43 | json <- paste0( 44 | '{"package":[', 45 | paste0('"', packages, '"', collapse = ","), 46 | "]}" 47 | ) 48 | writeLines(paste0("matrix=", json), Sys.getenv("GITHUB_OUTPUT")) 49 | writeLines(json) 50 | } else { 51 | writeLines("No suggested packages found.") 52 | } 53 | -------------------------------------------------------------------------------- /R/zzz.R: -------------------------------------------------------------------------------- 1 | #' @importFrom methods allNames 2 | #' @importFrom utils capture.output file.edit globalVariables assignInNamespace 3 | 4 | globalVariables(c(".rs.rpc.transform_snippet")) 5 | 6 | forget_all <- NULL 7 | 8 | .onLoad <- function(libname, pkgname) { 9 | # Use packages to avoid notes 10 | # I think a memoised function's body doesn't contain the original so the calls to 11 | # clipr:: and RCurl:: are hidden and then the checks for old Ubuntu 18.04 complain that some imported packages 12 | # are not used 13 | quote(clipr::read_clip()) 14 | quote(RCurl::url.exists()) 15 | 16 | # to avoid check complaints since we know what we're doing here 17 | # update our namespace with memoised functions 18 | aiN <- assignInNamespace 19 | ns <- asNamespace(pkgname) 20 | 21 | # - We memoise functions to avoid redundant computation of the current selection 22 | # - We build a `forget_all()` function that invalidates all memoised funs 23 | # - We call `load_yaml_tricks()` 24 | 25 | # identify functions to memoise ---------------------------------------------- 26 | all_fun_nms <- ls(ns) 27 | prefixes <- c("clipboard_", "current_", "project_", "selection_" , "system_") 28 | pattern <- paste0("^", prefixes, collapse = "|") 29 | prefixed_fun_nms <- grep(pattern, all_fun_nms, value = TRUE) 30 | 31 | # memoise them and replace them in namespace --------------------------------- 32 | for (nm in prefixed_fun_nms) { 33 | aiN( 34 | x = nm, 35 | value = memoise::memoise(ns[[nm]]), 36 | ns = ns 37 | ) 38 | } 39 | 40 | # build the forget_all() function -------------------------------------------- 41 | 42 | txt <- paste0("memoise::forget(", prefixed_fun_nms, ")") 43 | forget_all_body <- as.call(c(quote(`{`), as.list(parse(text = txt)))) 44 | forget_all <- as.function(list(forget_all_body), envir = ns) 45 | aiN( 46 | x = "forget_all", 47 | value = forget_all, 48 | ns = ns 49 | ) 50 | 51 | # load YAML tricks ----------------------------------------------------------- 52 | load_yaml_tricks() 53 | } 54 | 55 | 56 | 57 | 58 | # for backward compatibility 59 | str2lang <- function(x) { 60 | parse(text = x)[[1]] 61 | } 62 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method(print,trick_object) 4 | export(addin) 5 | export(call_addin) 6 | export(clip_board_text_matches) 7 | export(clipboard_contains_text) 8 | export(clipboard_is_parsable) 9 | export(clipboard_text) 10 | export(current_call) 11 | export(current_code_block) 12 | export(current_expr) 13 | export(current_indentation) 14 | export(current_line_numbers) 15 | export(current_lines) 16 | export(current_path) 17 | export(current_quoted_status) 18 | export(current_selection) 19 | export(current_value) 20 | export(document_code) 21 | export(document_is_r_script) 22 | export(document_is_rmd) 23 | export(edit_tricks) 24 | export(edit_yaml_tricks) 25 | export(fails) 26 | export(insert_at_position) 27 | export(install_tricks) 28 | export(load_tricks) 29 | export(load_yaml_tricks) 30 | export(loaded_tricks) 31 | export(mock_context) 32 | export(new_trick) 33 | export(project_is_package) 34 | export(project_uses_git) 35 | export(replace_current_lines) 36 | export(replace_selection) 37 | export(selection_inherits) 38 | export(selection_is_call) 39 | export(selection_is_comment_block) 40 | export(selection_is_comment_line) 41 | export(selection_is_cran_package) 42 | export(selection_is_data_frame) 43 | export(selection_is_empty) 44 | export(selection_is_evaluable) 45 | export(selection_is_function) 46 | export(selection_is_in_rmd_chunk) 47 | export(selection_is_in_rmd_text) 48 | export(selection_is_installed_package) 49 | export(selection_is_litteral) 50 | export(selection_is_n_lines) 51 | export(selection_is_parsable) 52 | export(selection_is_reserved_word) 53 | export(selection_is_single_line) 54 | export(selection_is_symbol) 55 | export(selection_is_syntactic_package_name) 56 | export(selection_matches) 57 | export(send_cursor_at_position) 58 | export(system_has_internet) 59 | export(trick_list_to_yaml) 60 | export(uninstall_tricks) 61 | export(unload_tricks) 62 | export(use_yaml_tricks) 63 | export(yaml_to_trick_list) 64 | importFrom(methods,allNames) 65 | importFrom(utils,assignInNamespace) 66 | importFrom(utils,capture.output) 67 | importFrom(utils,file.edit) 68 | importFrom(utils,getFromNamespace) 69 | importFrom(utils,globalVariables) 70 | importFrom(utils,select.list) 71 | -------------------------------------------------------------------------------- /R/01_addin.R: -------------------------------------------------------------------------------- 1 | # to avoid RStudio's margin warning 2 | forget_all <- NULL 3 | 4 | #' addin 5 | #' @importFrom utils getFromNamespace select.list 6 | #' @export 7 | addin <- function() { 8 | if(called_through_snippets()) { 9 | # snippets hide the output, we free it here 10 | sink() 11 | # and sink again just to avoid being warned that there's nothing to unsink 12 | file <- textConnection("rval", "w", local = TRUE) 13 | on.exit(sink(file, type = "output", split = FALSE)) 14 | } 15 | # make all functions available for the time of the call 16 | if(!"package:tricks" %in% search()) { 17 | library(tricks) 18 | on.exit(detach("package:tricks"), add = TRUE) 19 | } 20 | # reset all memoised functions 21 | forget_all() 22 | 23 | # cache selection and environment 24 | current_selection() 25 | current_env() 26 | 27 | tricks <- as.list(globals$tricks) 28 | if(!length(tricks)) { 29 | message("No tricks to display") 30 | return(invisible(NULL)) 31 | } 32 | 33 | # compute condition and label 34 | tricks_processed <- lapply(tricks, function(x) { 35 | # provide an env to every trick 36 | x$.env <- new.env(parent = current_env()) 37 | # eval cond 38 | x$condition <- try(eval(x$condition, x$.env), silent = TRUE) 39 | if(inherits(x$condition, "try-error")) { 40 | warning( 41 | "The condition couldn't be evaluated for the trick \"", x$label, "\"", 42 | immediate. = TRUE, 43 | call. = FALSE) 44 | x$condition <- FALSE 45 | } else { 46 | x$label <- glue::glue(x$label, .envir = x$.env) 47 | } 48 | x 49 | }) 50 | 51 | tricks_applicable <- Filter(function(x) x$condition, tricks_processed) 52 | 53 | if(!length(tricks_applicable)) { 54 | message("No tricks to display") 55 | return(invisible(NULL)) 56 | } 57 | 58 | rstudioapi::sendToConsole("", FALSE) 59 | 60 | trick_label <- select.list(names(tricks_applicable)) 61 | if(trick_label == "") { 62 | rstudioapi::sendToConsole("") 63 | return(NULL) 64 | } 65 | # extract action call 66 | trick <- tricks_applicable[[trick_label]] 67 | action <- do.call(bquote, list(trick$action)) 68 | 69 | eval(action, trick$.env) 70 | rstudioapi::sendToConsole("") 71 | } 72 | 73 | 74 | -------------------------------------------------------------------------------- /R/02_tricks.R: -------------------------------------------------------------------------------- 1 | 2 | #' Add, Remove or Show Tricks 3 | #' 4 | #' Add 5 | #' @param ... tricks to add (or replace), or names of tricks to remove 6 | #' @param .reset whether to remove existing tricks 7 | #' @inheritParams edit_yaml_tricks 8 | #' @export 9 | load_tricks <- function(..., .reset = FALSE) { 10 | if(.reset) unload_tricks() 11 | new_tricks <- rlang::dots_list( 12 | ..., 13 | .ignore_empty = "all", 14 | .homonyms = "error" 15 | ) 16 | new_tricks <- fmls_to_tricks(new_tricks) 17 | loaded_tricks <- globals$tricks 18 | list2env(new_tricks, globals$tricks) 19 | invisible(NULL) 20 | } 21 | 22 | #' @export 23 | #' @rdname load_tricks 24 | unload_tricks <- function(...) { 25 | rm(list = c(...), envir = globals$tricks) 26 | invisible(NULL) 27 | } 28 | 29 | fmls_to_tricks <- function(tricks) { 30 | fml_to_trick <- function(x, nm) { 31 | if(inherits(x, "tricks_object")) return(x) 32 | if (nm == "" | !rlang::is_formula(x, lhs = TRUE)) rlang::abort( 33 | "`add_tricks()` accepts only trick objects or argument of form `