
Code Convertion
rpkgkit 0.1.23
Yuxi Yang
2026-11-06
Source:vignettes/code_convertion.Rmd
code_convertion.RmdQualify package function calls
make_func_call_explicit() adds namespace qualifiers to
calls from selected packages. package_func_call_explicit()
applies the same operation throughout a package.
file <- tempfile(fileext = ".R")
writeLines(
c(
"starwars |>",
" mutate(name, bmi = mass / ((height / 100)^2)) |>",
" select(name:mass, bmi)"
),
file
)
make_func_call_explicit(file, use_packages = "dplyr")
#> ℹ Retrieving function calls from dplyr
#> ✔ Successfully made function call explicit in /tmp/RtmprZoMVF/file1f9ab097532.R
readLines(file) |> cli::cli_code()
#> dplyr::starwars |>
#> dplyr::mutate(name, bmi = mass / ((height / 100)^2)) |>
#> dplyr::select(name:mass, bmi)Find unmatched glue braces
detect_lost_glue_brace() checks one file;
package_lost_glue_brace() checks an entire package. Both
support glue and cli expressions, and print
the offending source line with cli syntax highlighting.
file <- tempfile(fileext = ".R")
writeLines(
c(
'name <- "world"',
'msg <- glue::glue("Hello, {name!")',
'cli::cli_alert_warning("{.field warning}}")'
),
file
)
detect_lost_glue_brace(file)
#> msg <- glue::glue("Hello, {name!")
#> ^^^^^^
#> cli::cli_alert_warning("{.field warning}}")
#> ^^^^^^^^^^^^^^^^^
#> ✖ Found 2 lines with mismatched braces: 2 and 3Name positional arguments
make_func_arg_explicit() makes arguments in one file
explicit, and package_func_arg_explicit() applies this
transformation across a package.
file <- tempfile(fileext = ".R")
writeLines("vapply(1:9, function(x) x * 2, numeric(1))", file)
make_func_arg_explicit(file)
#> ✔ Made function arguments explicit in /tmp/RtmprZoMVF/file1f9a61d94446.R
readLines(file) |> cli::cli_code()
#> vapply(X = 1:9, FUN = function(x) x * 2, FUN.VALUE = numeric(length = 1))Rename functions
Use rename_func() to apply a naming style to function
definitions in a file.
file <- tempfile(fileext = ".R")
writeLines("this_is_a_function <- function() message('Hello, world')", file)
rename_func(file, style = "camelCase")
#> ✔ Renamed 1 function to "camelCase" style in /tmp/RtmprZoMVF/file1f9a28000128.R
readLines(file) |> cli::cli_code()
#> thisIsAFunction <- function() message('Hello, world')Detect print() and cat()
detect_print_and_cat() reports print() and
cat() calls in one file.
package_print_and_cat() checks an entire package. Set
fix = TRUE to replace those calls in the selected file with
message(). As for the glue brace detection, the reported
source line is syntax highlighted.
file <- tempfile(fileext = ".R")
writeLines("print('Hello, world')", file)
detect_print_and_cat(file)
#> print('Hello, world') [message]
#> ^^^^^^
#> ✖ Found 1 unsupported call on line 1.
detect_print_and_cat(file, fix = TRUE)
#> ✔ Fixed 1 line in file1f9a14d478c.R.
#> print('Hello, world') [message]
#> ^^^^^^
#> ✖ Found 1 unsupported call on line 1.
readLines(file) |> cli::cli_code()
#> message('Hello, world')Detect unmatched nocov markers
detect_unmatched_nocov_marker() checks the
# nocov start and # nocov end markers of one
file, and package_unmatched_nocov_marker() checks a whole
package. A file may hold at most one marker of each kind, so every
further start or end is reported with its line and column, together with
a start or an end that has no counterpart and an end placed before the
start. Both counts must therefore match: an unclosed start is reported
as well, because covr::package_coverage() aborts on such a
range with a “range start but only range ends” error.
Detection is purely textual, so a marker inside a string literal or a roxygen comment is reported as well (as covr does); such a marker has no effect on coverage, so it is usually a leftover to remove. Nothing is rewritten — the offending comment is pointed at and the fix is left to you.
file <- tempfile(fileext = ".R")
writeLines(
c(
"# nocov start",
"f <- function(x) x + 1",
"# nocov start",
"# nocov end"
),
file
)
detect_unmatched_nocov_marker(file)
#> ✖ `nocov start` at line 3, column 1: only one `nocov start` is allowed per file, the first is `nocov start` at line 1, column 1.
#> # nocov start
#> ^^^^^^^^^^^^^
#> ✖ Found 1 mismatched `nocov` marker on line 3.A # nocov start that is never closed is reported as
well:
writeLines(c("# nocov start", "g <- function(y) y"), file)
detect_unmatched_nocov_marker(file)
#> ✖ `nocov start` at line 1, column 1 has no matching `nocov end`.
#> # nocov start
#> ^^^^^^^^^^^^^
#> ✖ Found 1 mismatched `nocov` marker on line 1.
pkg <- file.path(tempdir(), "demo")
dir.create(file.path(pkg, "R"), recursive = TRUE, showWarnings = FALSE)
writeLines(
c("Package: demo", "Version: 0.0.1"),
file.path(pkg, "DESCRIPTION")
)
writeLines(c("g <- function(y) y", "# nocov end"), file.path(pkg, "R", "g.R"))
package_unmatched_nocov_marker(pkg)
#> R/g.R: 2:
#> ✖ `nocov end` at line 2, column 1 has no matching `nocov start`.
#> # nocov end
#> ^^^^^^^^^^^
#> ✖ Found 1 mismatched `nocov` marker in 1 of 1 file.Detect default paths in exported arguments
CRAN does not allow a path-like argument of an exported function to
default to a concrete location such as "." or
"~/data.csv". detect_fn_arg_path() checks one
file and package_fn_arg_path() checks every .R
file of a package, using the exports declared in
NAMESPACE.
file <- tempfile(fileext = ".R")
writeLines(
c(
'read_csv <- function(path = "~/data.csv", n = 1L) NULL',
'read_all <- function(file = "data.txt") NULL',
'read_tsv <- function(path = NULL, n = 1L) NULL'
),
file
)
detect_fn_arg_path(file)
#> /tmp/RtmprZoMVF/file1f9a3718be4e.R:1:22 read_csv(): `path` defaults to
#> "~/data.csv"
#> read_csv <- function(path = "~/data.csv", n = 1L) NULL
#> ^^^^^^^^^^^^^^^^^^^
#> /tmp/RtmprZoMVF/file1f9a3718be4e.R:2:22 read_all(): `file` defaults to
#> "data.txt"
#> read_all <- function(file = "data.txt") NULL
#> ^^^^^^^^^^^^^^^^^
#> ✖ Found 2 arguments with a default path in /tmp/RtmprZoMVF/file1f9a3718be4e.RAn argument is reported when its name matches
arg_pattern and its default is not listed in
allow, so read_tsv() above is clean: its
default is NULL. An argument without a default is always
fine. Narrow the pattern when a loose match is not wanted:
detect_fn_arg_path(file, arg_pattern = "^path$")
#> /tmp/RtmprZoMVF/file1f9a3718be4e.R:1:22 read_csv(): `path` defaults to
#> "~/data.csv"
#> read_csv <- function(path = "~/data.csv", n = 1L) NULL
#> ^^^^^^^^^^^^^^^^^^^
#> ✖ Found 1 argument with a default path in /tmp/RtmprZoMVF/file1f9a3718be4e.Rdetect_fn_arg_path() uses the active document when
path is NULL, and checks every top-level
function of that file unless exports or
export_patterns say which ones count as exported. Use the
package-wide function to see all the occurrences at once:
Convert function syntax
Convert between function() and the short
\() syntax.
file <- tempfile(fileext = ".R")
writeLines("f <- function(x) x^2", file)
convert_func_syntax(file, "to_lambda")
#> ✔ Converted function definitions in /tmp/RtmprZoMVF/file1f9a26eb1b49.R to "to_lambda"
readLines(file) |> cli::cli_code()
#> f <- \(x) x^2
convert_func_syntax(file, "to_explicit")
#> ✔ Converted function definitions in /tmp/RtmprZoMVF/file1f9a26eb1b49.R to "to_explicit"
readLines(file) |> cli::cli_code()
#> f <- function(x) x^2Modernize knitr chunk headers
convert_knitr_chunk_header() converts legacy R Markdown
chunk options to the current comment-based syntax.
file <- tempfile(fileext = ".Rmd")
writeLines(c("```{r, echo=TRUE, fig.width=10}", "x <- 1", "```"), file)
convert_knitr_chunk_header(file)#> ℹ Converting knitr chunk headers in /tmp/RtmprZoMVF/file1f9a30f7181c.Rmd
readLines(file) |> cli::cli_code()
#> ```{r}
#> #| echo = TRUE,
#> #| fig.width = 10
#> x <- 1
#> ```
Switch pipe operators
convert_pipe() rewrites pipes in one file between
%>% and |>. Strings, comments, and the
other magrittr operators (%<>%, %$%) are
left unchanged.
file <- tempfile(fileext = ".R")
writeLines("mtcars %>% dplyr::filter(cyl > 4)", file)
convert_pipe(file, direction = "to_base")
#> ✔ Converted pipes in /tmp/RtmprZoMVF/file1f9a3fa9a86.R ("to_base")
readLines(file) |> cli::cli_code()
#> mtcars |> dplyr::filter(cyl > 4)Use package_convert_pipe() to process .R
and .r files below a package’s R/
directory.
package_convert_pipe(".", direction = "to_base")Add integer suffixes
convert_int_literals() adds L to bare
integer literals while leaving strings, comments, floating-point values,
and existing suffixes unchanged.
file <- tempfile(fileext = ".R")
writeLines("x <- seq_len(10) # length 10", file)
convert_int_literals(file)
#> ✔ Added explicit integer suffixes in /tmp/RtmprZoMVF/file1f9a7133c461.R
readLines(file) |> cli::cli_code()
#> x <- seq_len(10L) # length 10Use package_convert_int_literals() to process
.R and .r files below a package’s
R/ and tests/ directories.
Convert non-ASCII source code
convert_nonascii_code() converts non-ASCII source to
Unicode escape sequences when needed for CRAN-compatible source files.
It accepts either a file path or an R expression.
file <- tempfile()
writeLines("foo <- \\() message('滚滚长江东逝水')", file)
convert_nonascii_code(file, overwrite = TRUE)
#> ℹ Converted content written to /tmp/RtmprZoMVF/file1f9a4e3e82b4
readLines(file) |> cli::cli_code()
#> foo <- \() message('\u6eda\u6eda\u957f\u6c5f\u4e1c\u901d\u6c34')
convert_nonascii_code({
cli::cli_alert_info("明月几时有")
cli::cli_alert_warning("把酒问青天")
})
#> ℹ Converted code (copy from console):
#> {
#> cli::cli_alert_info("\u660e\u6708\u51e0\u65f6\u6709")
#> cli::cli_alert_warning("\u628a\u9152\u95ee\u9752\u5929")
#> }Lay out long vectors and lists as tables
Air 0.8.0 and newer can align the elements of a c() or
list() call column by column, giving the layout of
tibble::tribble(). The feature is switched on by the
experimental # fmt: table directive placed right above the
call, but air never re-flows the elements on its own: whatever sits on
one source line stays there.
use_air_table() does the two things air does not do by
itself. It runs air format, inserts the directive above
every unnamed c()/list() call that holds more
than min_length elements, and re-flows those elements so
that each row holds ncol of them; a second
air format run then pads the cells and aligns the
columns.
file <- tempfile(fileext = ".R")
writeLines(
'x <- c("alpha", "beta", "gamma", "delta", "epsilon", "zeta")',
file
)
use_air_table(file, ncol = 3L, min_length = 5L)
readLines(file) |> cli::cli_code()Keep in mind that:
-
airmust be installed and on thePATH;check_air_installed()reports the installation command for each operating system. - Only calls whose elements are all unnamed are rewritten: air expands named elements one per line whatever the directive says, so a named call is skipped instead of being decorated with a directive that has no effect.
- A call that already carries the directive is left untouched, so the function is idempotent.
- Only the outermost call of a statement is converted, because air ignores the directive on nested calls.