Configuration
Arity is configured with a TOML file named arity.toml. All keys are optional;
omitting a key uses its default. Keys are kebab-case, and unknown keys are
rejected with an error (so a typo never silently falls back to a default).
Run arity init to write a commented starter file.
Editor support
Arity publishes a JSON Schema for arity.toml, so
editors with TOML support can offer completion, inline documentation, and
validation while you edit your configuration.
Schema URL : https://arity.cc/arity.schema.json
The schema is generated from Arity’s configuration types and checked against the
repository’s own arity.toml. It therefore stays aligned with the keys, enums,
constraints, and defaults accepted by the CLI. The version at the published URL
tracks the latest Arity release.
Until arity.toml is registered with SchemaStore, editors need a manual
association. With the Even Better
TOML
extension for VS Code, add this to your user or workspace settings.json:
{
"evenBetterToml.schema.associations": {
"^(.*/)?arity\\.toml$": "https://arity.cc/arity.schema.json"
}
}
Editors and language servers that recognize TOML schema directives can instead
put this comment at the top of arity.toml:
#:schema https://arity.cc/arity.schema.json
Other editors—including Neovim with taplo-lsp, Helix, Zed, and IntelliJ—can
use the same URL through their JSON Schema association settings.
Discovery
For a given file, arity looks for arity.toml by walking up from the file’s
directory through its ancestors, stopping at the first arity.toml it finds or
at a directory containing a .git entry (the repository root), whichever comes
first.
If no project config is found, arity loads the file named by the ARITY_CONFIG
environment variable. This lets you keep a user config at a custom location,
such as a synced drive. Project configs take precedence and replace the user
config entirely; files from different discovery steps are not merged.
Relative ARITY_CONFIG paths resolve from the process’s working directory. An
absolute path is recommended for editor settings. An unset or empty value uses
the built-in defaults when no project config is found. A missing or invalid
fallback file is a configuration error.
To share settings between projects, put extend = "../shared/arity.toml" at the
top level of a project config. Arity resolves the path relative to the file that
declares it (and expands a leading ~/ to your home directory), then merges the
referenced config before the current file. Chains are allowed, and a cycle or
missing file is an error. Child values override inherited values; tables merge
by key, so overriding [format] line-width keeps an inherited indent-width.
extend-exclude arrays append in base-to-child order, while exclude replaces
the inherited array. For a project config or --config file, the selected file
still anchors exclude patterns, even when they come from an extended file.
On the command line:
--config <PATH>loads an explicit file and skips project discovery andARITY_CONFIG.--no-configignores all config files, includingARITY_CONFIG, and uses the built-in defaults.
In VS Code or Positron, set the variable in your user settings.json, then run
Arity: Restart Server:
{
"arity.serverEnv": {
"ARITY_CONFIG": "P:/Softwares/arity.toml"
}
}
Exclude patterns in a project config or an explicit --config file resolve
relative to that file’s directory. The ARITY_CONFIG fallback has no project
location, so its patterns resolve relative to the working directory for CLI
walks, or the directory being scanned in the language server.
Top-level keys
These apply to both format and lint. exclude and extend-exclude
govern the shared file walk.
| Key | Type | Default | Description |
|---|---|---|---|
extend | string | unset | Path to another config file, resolved relative to this file. |
exclude | array of strings | built-in set | gitignore-style patterns to skip, anchored according to the config source. Setting it replaces the built-in set (below). |
extend-exclude | array of strings | [] | Like exclude, but added to exclude rather than replacing it. Use this to skip extra paths while keeping the built-in defaults. |
cache | boolean | true | Enable the persistent result cache (currently the format --check already-formatted cache; the cache directory follows [index] cache-dir/$ARITY_CACHE_DIR). The --no-cache CLI flag overrides it per run. |
The built-in default exclude set (the default value of exclude; generated or
vendored files that should not be reformatted or linted) is:
.git/
renv/
revdep/
cpp11.R
RcppExports.R
extendr-wrappers.R
import-standalone-*.R
Excludes apply only to directory walks. A file named explicitly on the
command line is always processed, even if it matches an exclude pattern. Pass
--force-exclude (on format and lint) to apply the patterns to explicitly
named files too—useful for runners like pre-commit that pass staged files as
arguments. The CLI flag --exclude <PATTERN> (on format and lint) adds to
the configured exclude/extend-exclude for a single run.
# Keep the built-in defaults and also skip these:
extend-exclude = ["vendor/", "*.gen.R"]
# Or replace the built-in defaults entirely:
# exclude = ["vendor/", "*.gen.R"]
[format]
| Key | Type | Default | Description |
|---|---|---|---|
line-width | integer (1–1000) | 80 | The width the formatter tries to keep lines within. Not a hard cap. |
indent-width | integer (1–1000) | 2 | Number of spaces per indentation level. |
line-ending | string | "auto" | Newline style: "auto", "lf", "crlf", or "native" (see below). |
roxygen | boolean | true | Whether to format #' blocks in R files. |
description | boolean | true | Whether a package DESCRIPTION is formatted. |
line-ending = "auto" mirrors the source file’s first line ending (defaulting
to lf when the file has none); "native" is crlf on Windows and lf
elsewhere; "lf" and "crlf" force that ending.
[format]
line-width = 80
indent-width = 2
line-ending = "auto"
roxygen = true
description = true
line-width and indent-width can be overridden per run with the
--line-width/--indent-width flags on arity format. They apply to
DESCRIPTION too, except that its continuation indent is always four spaces —
the file format’s convention, and a different axis from R-code nesting.
Set roxygen = false to keep each #' block’s source layout while formatting
the R code around it. This also preserves #' blocks used as Markdown prose by
knitr::spin(). The file’s configured line-ending still applies to the
output.
Setting description = false leaves DESCRIPTION alone, in the CLI (including
a buffer piped in under --stdin-filename DESCRIPTION, which is passed through
untouched) and in the editor. Reach for it if arity and your package tooling end
up disagreeing about the file. Note that extend-exclude is not a substitute:
excludes are shared with the linter, so excluding DESCRIPTION would also
silence the packaging rules, and the language server applies no exclude filter
to formatting at all.
[lint]
| Key | Type | Default | Description |
|---|---|---|---|
select | array of strings | unset | If set, only these rule IDs run. |
ignore | array of strings | [] | Rule IDs to disable (applied on top of select or the default set). |
Rule IDs are the kebab-case names from the rule reference. Unknown
IDs are reported when linting runs, not when the config is parsed. The
--select/--ignore flags on arity lint override these for a single run.
[lint]
select = ["undefined-symbol", "equals-na"]
ignore = ["unused-binding"]
[lint.rules.<id>]
A few rules take options of their own, set in a table named after the rule ID. Rules that take no options have no table.
Unlike select/ignore—where rule IDs are data, checked when linting
runs—a rule ID here is part of the schema, so a mistyped one is reported when
the config is parsed, alongside any other unknown key.
[lint.rules.undesirable-function]
The function-name policy for
undesirable-function.
| Key | Type | Default | Description |
|---|---|---|---|
functions | table of name → hint | built-in set | Flagged functions. Replaces the built-in set. |
extend-functions | table of name → hint | {} | Entries added on top of functions, overriding same-named ones. |
The value is the advice shown as the diagnostic’s suggestion; an empty string
means “no alternative, just don’t call this”. The functions/extend-functions
split works like exclude/extend-exclude: reach for extend-functions unless
you really mean to discard the built-in set. Setting functions = {} silences
the rule entirely.
The built-in set covers base-R functions that mutate global state (attach,
detach, .libPaths, install.packages, setwd, sink, source, options,
par, Sys.setenv, Sys.setlocale) and the debugging entry points (debug,
debugonce, undebug, trace, untrace). browser() is deliberately absent:
it has its own browser rule.
[lint]
select = ["undesirable-function"]
[lint.rules.undesirable-function]
extend-functions = { sapply = "use `vapply()` for a stable return type" }
[compat]
The minimum tool versions the project supports, in the spirit of clippy’s msrv
and ruff’s target-version. Consumed by the version-aware lint rules
(r-compat, roxygen2-compat): syntax or documentation constructs that need a
newer version than the declared floor are flagged.
| Key | Type | Default | Description |
|---|---|---|---|
r | version string | unset | Minimum supported R version, e.g. "4.1". |
roxygen2 | version string | unset | The roxygen2 version the project documents with, e.g. "7.3.2". |
Values are plain version strings—the key is the >= floor, so there is no
operator syntax.
When a key is unset, the floor is derived per file from the enclosing package’s
DESCRIPTION: Depends: R (>= …) supplies r, and Config/roxygen2/version
(written by roxygen2 8.0.0 and later) or the legacy RoxygenNote supplies
roxygen2. An explicit key here always wins. With neither a key nor a
DESCRIPTION fact, the version-aware rules stay silent, so loose scripts see no
false positives.
[compat]
r = "4.1"
roxygen2 = "7.3.2"
[index]
Controls the R-package symbol index used by the language server (and by namespace-aware lint rules) to resolve names.
| Key | Type | Default | Description |
|---|---|---|---|
library-paths | array of paths | [] | Explicit R library directories, used when automatic discovery misses. |
cache-dir | path | unset | Override the index cache directory (otherwise XDG/$ARITY_CACHE_DIR). |
auto-build | boolean | true | Let the language server lazily index referenced-but-unindexed packages. |
help | boolean | true | Harvest help titles while indexing. false stores names only (faster). |
Note: the downloadable CRAN symbol sidecar is not configured here. Enabling network access is a per-user decision set via the
ARITY_REMOTE_URLenvironment variable, never committed in a sharedarity.toml.
Note: the same applies to the attach probe (
arity index --attach-probe), which observes what a meta-package attaches by runninglibrary()in a fresh R session. Because that executes package attach hooks, it is enabled per run by the flag or per user via theARITY_ATTACH_PROBEenvironment variable, never fromarity.toml. Without it, attach sets are still captured for packages following the tidyversecoreconvention, with a built-in table as the offline fallback.
Reserved for future use
The following are not yet implemented but are reserved so the schema can grow without breaking changes (adding a key is always backward-compatible under the strict unknown-key check):
[format].indent-style("space"or"tab")—tab indentation.[format].skipand a# fmt: skipcomment—opt specific calls out of formatting.severityin a[lint.rules.<id>]table—overriding a rule’s severity.- Category names (e.g.
"correctness") inselect/ignore.