| Title: | Code generation and linting functions for R packages |
|---|---|
| Description: | Perform common tasks and fix common errors in project and package development. This is a developer tool rather than an end user package. |
| Authors: | Robert Challen [aut, cre] (ORCID: <https://orcid.org/0000-0002-5504-7768>) |
| Maintainer: | Robert Challen <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.2.1 |
| Built: | 2026-05-23 22:20:26 UTC |
| Source: | https://github.com/terminological/pkgtools |
This makes no checks and accepts no responsibility. No backups are made.
bump_dev_version(pkg = ".")bump_dev_version(pkg = ".")
pkg |
the path to the package |
the new version
devtools::document() requires a DESCRIPTION file, which in general we don't
include in analysis projects. Sometimes we want to produce development
documentation for functions in analysis projects so we can describe options
and behaviour. pkgtools::document will also work in a non package project
to produce .Rd files in the man directory for any functions declared in
the R subfolder. It also override the default behaviour of ? to return
the development documentation of matching functions in the current project.
document(project.dir = ".", roclets = NULL, quiet = FALSE)document(project.dir = ".", roclets = NULL, quiet = FALSE)
project.dir |
the directory of the current (non package project) |
roclets |
Character vector of roclet names to use with package.
The default, |
quiet |
if |
nothing
Find and replace within a package after committing the current contents
find_and_replace( regex, replacement, pkg = ".", rDirectories = c("data-raw", "R", "vignettes", "tests") )find_and_replace( regex, replacement, pkg = ".", rDirectories = c("data-raw", "R", "vignettes", "tests") )
regex |
a PCRE regex |
replacement |
a replacement |
pkg |
the package to scan |
rDirectories |
the subdirectories (defaults to data-raw, R, vignettes, tests) |
a list of changed files
This function runs a set of common package development errors. It will create a commit before running checks and fixing unqualified functions, excluding non standard files, creating global variables, fixng utf8 encoding issues and ensuring all dependencies line up.
fix_check(pkg = ".")fix_check(pkg = ".")
pkg |
the package to fix |
nothing
Fixes dependencies in the namespace file using the output of R CMD check.
fix_dependencies(pkg = ".", check)fix_dependencies(pkg = ".", check)
pkg |
the package to scan |
check |
output of a |
nothing
R CMD check`` to a globals.R' fileAdds global variables identified at R CMD check`` to a globals.R' file
fix_global_variables(pkg = ".", check)fix_global_variables(pkg = ".", check)
pkg |
the package location |
check |
the results of a |
nothing
Rbuildignore fileAdds non standard and hidden files to the .Rbuildignore file
fix_non_standard_files(pkg = ".", check)fix_non_standard_files(pkg = ".", check)
pkg |
the package location |
check |
the results of a |
nothing
Interactively find and replace unqualified, e.g. mutate(...) calls with
fully qualified dplyr::mutate(...) calls. This version operates on the
currently open file in the RStudio and allows for interactive review of the
proposed changes.
fix_unqualified_fns()fix_unqualified_fns()
nothing - called for side effects
This is a code linting function and expected to be called at the console
during package development. It will scan the files in the current project and
replace unqualified references to e.g. mutate with ones to dplyr::mutate
etc. All changes are made after a commit whcih can be reverted.
fix_unqualified_fns_bulk( pkg = ".", rDirectories = c(here::here("R"), here::here("tests/testthat")), prioritise = c("dplyr", "rlang", "stringr", "forcats", "ggplot2", "purrr", "tidyr", "readr", "stats", "utils") )fix_unqualified_fns_bulk( pkg = ".", rDirectories = c(here::here("R"), here::here("tests/testthat")), prioritise = c("dplyr", "rlang", "stringr", "forcats", "ggplot2", "purrr", "tidyr", "readr", "stats", "utils") )
pkg |
the package |
rDirectories |
the locations of the R code to fix (by default R scripts, tests, but not data-raw or vignettes) |
prioritise |
a list of package names to pick from first |
nothing. called for side effects.
\uXXXX
Fixes utf8 encoded characters in source files replaincg them with \uXXXX
fix_utf8_encoding(pkg = ".", check)fix_utf8_encoding(pkg = ".", check)
pkg |
the package to scan |
check |
output of a |
nothing
renv if available.devtools::install_local does not play well with renv in this version of
install_local we intercept installation of locally developed packages when
we are in a renv managed project and installing a local dependency, it
builds a source project into renv cellar and installs it from there. This
allows a copy of a locally developed package to be deployed with the renv
managed analysis project without specifically being deployed to CRAN or
r-universe.
install_local( path = ".", ..., force = TRUE, upgrade = "never", quiet = TRUE, wd = here::here() )install_local( path = ".", ..., force = TRUE, upgrade = "never", quiet = TRUE, wd = here::here() )
path |
the package (defaults to |
... |
Named arguments passed on to
|
force |
do the install regardless of versions |
upgrade |
upgrade out of data CRAN packages |
quiet |
do it quietly |
wd |
the project root directory of the current project (defaults to |
If installed locally for a non-renv project (e.g. a package development)
the usual behaviour applies to version management. Installation of new
versions of the project will happen when the package is released and then
installed from the release location (e.g. github, cran, r-universe).
If a locally developed package is deployed to an renv project once it is
released onto a valid distribution platform e.g. CRAN, r-universe or github,
we will want to use that version in our renv. This we can do using the
rebuild = TRUE option of renv::install, e.g.:
renv::install(...pkgname/github..., repo = ...r-universe?..., rebuild = TRUE)
followed by a renv::snapshot() to update the lock file. The locally built
package version will remain in the <projroot>/renv/local cellar until
removed by hand.
Other package installation:
install_bioc(),
install_bitbucket(),
install_cran(),
install_dev(),
install_github(),
install_gitlab(),
install_git(),
install_svn(),
install_url(),
install_version()
## Not run: dir <- tempfile() dir.create(dir) pkg <- download.packages("testthat", dir, type = "source") install_local(pkg[, 2]) ## End(Not run)## Not run: dir <- tempfile() dir.create(dir) pkg <- download.packages("testthat", dir, type = "source") install_local(pkg[, 2]) ## End(Not run)
This function launches a Shiny gadget to visually merge two versions of code/YAML.
merge_code( value, new, old = NULL, lhs = "original", rhs = "suggested", accept = "Save", rhs_accept = "Accept all", mode = "r" )merge_code( value, new, old = NULL, lhs = "original", rhs = "suggested", accept = "Save", rhs_accept = "Accept all", mode = "r" )
value |
the content for the central panel. |
new |
character vector of code lines. Content of the changed / RHS version. |
old |
character vector of code lines. Content of the old / LHS version. |
lhs |
the title of the LHS |
rhs |
the title of the RHS |
accept |
the button text for the LHS |
rhs_accept |
the button text for the RHS |
mode |
the language mode |
Character string of the merged content, or stops with an error if cancelled.
if (FALSE) { old = paste0("line ",c(2:4,6:12)) new = paste0("line ",c(1:50)) merged_result <- merge_code(old, new) cat("Merged Content:\n") cat(paste0(merged_result, collapse = "\n"),"\n") }if (FALSE) { old = paste0("line ",c(2:4,6:12)) new = paste0("line ",c(1:50)) merged_result <- merge_code(old, new) cat("Merged Content:\n") cat(paste0(merged_result, collapse = "\n"),"\n") }
This function makes multiple changes to the package. It adds packaged data
files from the data directory to a pkgdown github published pin board and
constructs a minimal set of functions with the same names as the data to
access the data from the pinboard. It finds any references to the dataset in
the code directories (data-raw,R,vignettes,test) and replaces them
with a function call of the same name.
migrate_to_pins(pkg = ".", board = "data", migrate = NULL)migrate_to_pins(pkg = ".", board = "data", migrate = NULL)
pkg |
the package (defaults to current) |
board |
the board name (this is generally not exposed to user) |
migrate |
either "*" for all data files, a list of names or if null the user will be asked interactively. |
nothing
Check the package structure without running any code
qcheck(pkg = ".", ..., args = "", quiet = FALSE)qcheck(pkg = ".", ..., args = "", quiet = FALSE)
pkg |
the path of the package to check |
... |
Named arguments passed on to
|
args |
additional r cmd check args |
quiet |
do it without producing messages |
a check result
@dparam tagsReplaces the default behaviour of a @param tag and documents default
parameters automatically.
## S3 method for class 'roxy_tag_dparam' roxy_tag_parse(x)## S3 method for class 'roxy_tag_dparam' roxy_tag_parse(x)
x |
A tag |
a roxy_tag object with the val field set to the parsed value
# This provides support to `roxygen2` and only gets executed in the context # of `devtools::document()`. There is no interactive use of this function.# This provides support to `roxygen2` and only gets executed in the context # of `devtools::document()`. There is no interactive use of this function.
@unit tagsThe @unit tag can be used in roxygen2 documentation
of a function to define unit testing code. This code will be written to a
testthat file.
## S3 method for class 'roxy_tag_unit' roxy_tag_parse(x)## S3 method for class 'roxy_tag_unit' roxy_tag_parse(x)
x |
A tag |
a roxy_tag object with the val field set to the parsed value
# This provides support to `roxygen2` and only gets executed in the context # of `devtools::document()`. There is no interactive use of this function.# This provides support to `roxygen2` and only gets executed in the context # of `devtools::document()`. There is no interactive use of this function.
@dparam tagsReplaces the default behaviour of a @param tag and documents default
parameters automatically.
## S3 method for class 'roxy_tag_dparam' roxy_tag_rd(x, base_path, env)## S3 method for class 'roxy_tag_dparam' roxy_tag_rd(x, base_path, env)
x |
The tag |
base_path |
Path to package root directory. |
env |
Environment in which to evaluate code (if needed) |
an roxygen2::rd_section (see roxygen2 documentation)
# An example function definition: fn_definition <- " #' This is a title #' #' This is the description. #' #' @dparam x this parameter specifies a default #' f <- function(x = c(\"hello\",\"world\")) { return(\"test\") } " # For this example we manually parse the function specification in `fn_definition` # creating a .Rd block - normally this is done by `roxygen2` which then # writes this to an .Rd file. This function is not intended to be used # outside of a call to `devtools::document`. tmp = roxygen2::parse_text(fn_definition) print(tmp[[1]])# An example function definition: fn_definition <- " #' This is a title #' #' This is the description. #' #' @dparam x this parameter specifies a default #' f <- function(x = c(\"hello\",\"world\")) { return(\"test\") } " # For this example we manually parse the function specification in `fn_definition` # creating a .Rd block - normally this is done by `roxygen2` which then # writes this to an .Rd file. This function is not intended to be used # outside of a call to `devtools::document`. tmp = roxygen2::parse_text(fn_definition) print(tmp[[1]])
@unit tagsThe @unit tag can be used in roxygen2 documentation
of a function to define unit testing code. This code will be written to a
testthat file.
## S3 method for class 'roxy_tag_unit' roxy_tag_rd(x, base_path, env)## S3 method for class 'roxy_tag_unit' roxy_tag_rd(x, base_path, env)
x |
The tag |
base_path |
Path to package root directory. |
env |
Environment in which to evaluate code (if needed) |
an roxygen2::rd_section (see roxygen2 documentation)
# for testing
warning("should be suppressed")
# An example function definition: fn_definition <- " #' This is a title #' #' This is the description. #' #' @md #' @unit #' # unit testing code here: #' testthat::expect_equal(f(), \"test\") #' @export f <- function() { return(\"test\") } " # For this example we manually parse the function specification in `fn_definition` # creating a .Rd block - normally this is done by `roxygen2` which then # writes this to an .Rd file. This function is not intended to be used # outside of a call to `devtools::document`. fn_definition = gsub("@","@",fn_definition) tmp = roxygen2::parse_text(fn_definition) print(tmp[[1]])# An example function definition: fn_definition <- " #' This is a title #' #' This is the description. #' #' @md #' @unit #' # unit testing code here: #' testthat::expect_equal(f(), \"test\") #' @export f <- function() { return(\"test\") } " # For this example we manually parse the function specification in `fn_definition` # creating a .Rd block - normally this is done by `roxygen2` which then # writes this to an .Rd file. This function is not intended to be used # outside of a call to `devtools::document`. fn_definition = gsub("@","@",fn_definition) tmp = roxygen2::parse_text(fn_definition) print(tmp[[1]])
RStudio does this for examples but nothing else this allows singly commented
code or code in other tags e.g. @unit to be executed
run_commented_code()run_commented_code()
This is installed as an RStudio add in and can be given a keyboard shortcut
through Tools > Modify Keyboard Shortcuts...
nothing
renv lockfileSets custom repositories (e.g. r-universe repositories) in a renv
lockfile to override CRAN repositories. This is a persistent change can be
undone by manual editing of the lockfile.
set_renv_repos(..., .wd = here::here())set_renv_repos(..., .wd = here::here())
... |
a named list of repository urls |
.wd |
the working directory (defaults to |
nothing
testthat does not use hash based checking anymore, in favour of snapshots.
These don't work well in tests of standalones because the file will move
projects independently of the test directory. This function lets you quickly
create a check based on a gold standard
standalone_snapshot(obj, .clip = interactive(), .as = deparse(substitute(obj)))standalone_snapshot(obj, .clip = interactive(), .as = deparse(substitute(obj)))
obj |
the gold standard to test |
.clip |
copy the result to the clipboard (needs |
.as |
what name to use for the comparison |
a code snippet that can be pasted into the unit test case
pkgtools styleThis is adapted from https://github.com/gadenbuie/grkstyle
It differs in that it will break apart long lines regardless of their initial
line breaks to try and fit them into a set width. It does not unbreak lines.
style_text(text, ..., transformers = pkgtools_style_transformer(...))style_text(text, ..., transformers = pkgtools_style_transformer(...))
text |
A character vector with text to style. |
... |
Named arguments passed on to
Named arguments passed on to
|
transformers |
A set of transformer functions. This argument is most
conveniently constructed via the |
the formatted code
Switch expression for equality based test case.
switch_expect_equals()switch_expect_equals()
nothing
Switch global variable for hash based test case.
switch_standalone_snapshot()switch_standalone_snapshot()
nothing
This file syncs the current content of the rstudio editor window with a local master standalone file, which is assumed to be in a Git directory. This allows for changes to be propagated back to the master when in active development. The master copy is committed before changes.
sync_standalone_to_master(git_dir = fs::path_home("Git"))sync_standalone_to_master(git_dir = fs::path_home("Git"))
git_dir |
absolute or relative path to git base directory (e.g. ~/Git) |
nothing used for side effects
pkgtools operationReverts to last state committed by pkgtools, and stashes any unstaged changes.
undo(pkg = ".")undo(pkg = ".")
pkg |
the package |
nothing
Vignette building uses a new session. Any changes in current project or
dependent locally developed projects are not tested unless the packages are
all installed using devtools::install_local(...). This causes problems when
developing multiple packages in parallel.
unstable( path = ".", ..., force = TRUE, upgrade = "never", quiet = TRUE, load_lib = TRUE )unstable( path = ".", ..., force = TRUE, upgrade = "never", quiet = TRUE, load_lib = TRUE )
path |
the package local development repository path. This assumes you
have all your other package code in a sibling directory, e.g. |
... |
Named arguments passed on to
|
force |
Force installation, even if the remote state has not changed since the previous install. |
upgrade |
Should package dependencies be upgraded? One of "default", "ask", "always", or "never". "default"
respects the value of the |
quiet |
If |
load_lib |
load the package using a library command |
This function assumes the path variable is a path to a package which is under
version control in a Git directory. Other dependencies to this package may
also be under development in sibling directories. The aim is to install the
current version of the target package and all locally held dependencies that
have changed on the local disk compared to the locally installed version.
This function scans the current package and first order dependencies, looking for local development directories for any packages imported. Looks for changes in files in local development directories of package and first order dependencies versus files currently installed in r-library. If it finds any differences it checks if there is a version change of the package, bumps the version number of the development package, and installs it locally, After installation it restarts R.
Any recent file change in development directories triggers a dev version bump
and local package installation. After a call to unstable() any dependencies
in your local dev environment are up to date.
If unstable is called from within a non package project which is using
renv then rather than installing locally using devtools the package is
built and deployed locally in the renv local package directory (
<proj root>/renv/local) and installed from there. The renv local packages are
placed under version control. At the moment it is a manual job to tidy this up once the
development package is finalised and deployed
nothing
This function helps identify package imports and maintains standalone file metadata. It operates on the code in the Rstudio active window and suggests changes to be made to keep the standalone file metadata up to date. It is intended be used interactively.
update_standalone(repo = NULL, license = NULL, dependencies = NULL)update_standalone(repo = NULL, license = NULL, dependencies = NULL)
repo |
the github repository e.g. |
license |
a license URL (defaults to |
dependencies |
a optional list of standalone filenames in the same repository |
nothing, updates the RStudio editor content
use_standalone that works with analysis projectsusethis::use_standalone is a package development tool used in r-lib to
share useful functions between packages without creating a hard dependency on
them. This is also useful in data analysis projects where no package
infrastructure exists but you want to reuse common functions (e.g. plot
themes) between analysis projects. Developing a package containing these
shared functions and deploying to CRAN or r-universe is possible but it
is unwieldy and requires more infrastructure that needed.
use_standalone( repo_spec, file = NULL, ref = NULL, host = NULL, git_dir = fs::path_home("Git") )use_standalone( repo_spec, file = NULL, ref = NULL, host = NULL, git_dir = fs::path_home("Git") )
repo_spec |
A string identifying the GitHub repo in one of these forms:
|
file |
Name of standalone file. The |
ref |
The name of a branch, tag, or commit. By default, the file at
|
host |
GitHub host to target, passed to the If unspecified, gh defaults to "https://api.github.com", although gh's default can be customised by setting the GITHUB_API_URL environment variable. For a hypothetical GitHub Enterprise instance, either "https://github.acme.com/api/v3" or "https://github.acme.com" is acceptable. |
git_dir |
The directory to the users collection of Git projects |
Using a standalone file we can develop these functions in a basic git repository with no deployment (with or without package infrastructure), and import them into an analysis project as standalone files. From a reproducibility point of view this is sometimes beneficial if the functions in question are fairly dynamic as the version is hard wired into the analysis project.
The use cases supported by usethis::use_standalone are predicated around R
package development but here we extend this behaviour to analysis projects
with dependencies managed by renv, or not managed at all. If an analysis
project is being managed by renv then using a standalone file will install
missing renv dependencies and snapshot the project. If no renv is
detected a check is written to the .RProfile file which will produce a
message about missing dependencies when the project is opened.
In a non package project directives to try and source all standalone files
are added to the .RProfile file, so that standalone file functions are
immediately available.
If this is not working you may need to set the repository to HTTP/2:
git config --global http.version HTTP/2 && git push
A standalone file has YAML frontmatter that provides additional information, such as where the file originates from and when it was last updated. Here is an example:
--- repo: r-lib/rlang file: standalone-types-check.R last-updated: 2023-03-07 license: https://unlicense.org dependencies: standalone-obj-type.R imports: rlang (>= 1.1.0) ---
Two of these fields are consulted by use_standalone():
dependencies: A file or a list of files in the same repo that
the standalone file depends on. These files are retrieved
automatically by use_standalone().
imports: A package or list of packages that the standalone file
depends on. A minimal version may be specified in parentheses,
e.g. rlang (>= 1.0.0). These dependencies are passed to
use_package() to ensure they are included in the Imports:
field of the DESCRIPTION file.
Note that lists are specified with standard YAML syntax, using
square brackets, for example: imports: [rlang (>= 1.0.0), purrr].
## Not run: use_standalone("r-lib/rlang", file = "types-check") use_standalone("r-lib/rlang", file = "types-check", ref = "standalone-dep") ## End(Not run)## Not run: use_standalone("r-lib/rlang", file = "types-check") use_standalone("r-lib/rlang", file = "types-check", ref = "standalone-dep") ## End(Not run)
I frequently end up getting an unsaved change in RStudio conflicting with an update on disk. Often as a result of a global find and replace. This helps determine which version to keep, by bringing up a 2 way diff of editor content and disk content. The result of the merge is placed back in the editor.
what_has_changed()what_has_changed()
nothing