Skip to content

Configuration

Everything you can change about ry’s behavior lives in one file, ry.toml; editor settings only say where the binary is.

ry.toml is the only configuration file. There is no home-directory config, no environment variable naming one, and no merging — the nearest file replaces the built-in defaults wholesale.

Where ry runsSearch starts at
ry check R/utils.Rthe file’s own directory
ry check .that directory
The language serverthe workspace folder your editor announces; failing that, the process working directory
RuleBehavior
Searchwalk up from the starting directory; the first ry.toml wins. None found: built-in defaults.
Mergingnone — one file supplies every key.
Reloadthe language server watches ry.toml and re-discovers on every change, so deleting it falls back to an ancestor or to the defaults.
Several CLI targetsdiscovery runs once per argument, so two arguments can resolve two different files.
.. in a pathcancelled textually before the search, so project/ry.toml does not govern project/../outside.R.
Terminal window
$ cat project/ry.toml
spaces = 8
$ ry fmt --diff project/inside.R
Diff in project/inside.R:
1 1 | f <- function(x) {
2 |- x
2 |+ x
3 3 | }
1 file would be reformatted, 0 files already formatted
$ ry fmt --diff project/../outside.R
0 files would be reformatted, 1 file already formatted

The project root is a separate decision: it sets the analysis scope — which files see each other’s definitions — not which config is loaded.

SituationRoot
An ancestor holds ry.toml or DESCRIPTIONthe nearest such directory
Otherwise, the target is a directorythat directory
Otherwise, the target sits directly under an R/ directorythe parent of R/
Otherwisethe file’s own directory
KeyTypeDefaultEffect
indent-widthinteger2Spaces per indentation level, for ry fmt and for formatting in the editor.
line-ending"auto", "lf", "cr-lf""auto"Line ending the formatter writes. "auto" keeps whatever the file already uses.

Every key except naming-style takes a level: "off", "warn", "error", or "default" — which means the built-in severity, exactly as if you omitted the key.

KeyTypeDefaultEffect
naming-style"snake_case", "camelCase"unset — check offReports naming-style for variables and function parameters that do not match. SCREAMING_SNAKE_CASE always conforms. Always a warning; the value is a style, not a level.
assignment-operatorlevel"warn"= used for assignment.
boolean-shorthandlevel"warn"T or F written instead of TRUE or FALSE.
trailing-commalevel"error"A comma after the last argument of a call.
unused-parameterlevel"off"Function formals never read. S3 methods and your project’s own generics are exempt.
unused-importlevel"off"An importFrom(pkg, name) in NAMESPACE whose name appears nowhere in your sources. Whole-namespace import(pkg) is never checked, and this finding is raised by ry check only — not in the editor.
shadows-builtinlevel"off"A top-level binding with the same name as a base export.
shadows-namespacelevel"off"A top-level binding with the same name as an export of another namespace, such as stats::filter.

For a single exception, prefer a suppression comment over turning a lint off across the whole project.

Type inference always runs — hover, inlay hints, and signature help work regardless of these keys. [check] only decides which findings are reported.

KeyTypeDefaultEffect
unusedbooleantrueReport unused — bindings whose value is never read.
typingbooleanfalseReport type-mismatch. See the tutorial.
strictbooleanfalseReport each site with a genuinely undetermined type, and raise every unresolved finding from warning to error. See strict mode.
excludearray of strings[]Gitignore-style patterns the directory walk of ry check skips.

A # typing: off, # typing: on, or # typing: strict line at the top of a file replaces both typing and strict for that file — see the per-file directive.

Four things worth knowing about exclude:

  • Patterns are anchored at the directory holding ry.toml, and follow gitignore rules: scripts/ excludes that whole subtree, **/generated matches at any depth, ! re-includes.
  • Excluded directories are pruned without being walked, so exclusion cuts checking time, not just output.
  • A file named on the command line is always checked, files open in the editor are always analyzed, and ry fmt ignores the key entirely.
  • Some paths are skipped with no configuration at all, because they hold vendored dependencies rather than your code: renv/, packrat/, revdep/, .Rproj.user/, .Rcheck/. .gitignore is honored too, git checkout or not.
Terminal window
$ cat ry.toml
[check]
exclude = ["scripts/"]
$ ry check .
warning[unused]: `v` is assigned but never used.
--> R/a.R:1:19
1 | f <- function() { v <- 1; 2 }
^
1 problem in 1 file

An unknown key is never fatal — a config written for a newer ry still starts an older one.

SituationResult
Unknown keyIgnored, with one warning naming it. Known keys beside it still load, and the exit code is unaffected.
Wrong type on a known keyHard error.
Malformed TOMLHard error.
Invalid [check] exclude patternHard error.
The file disappears between discovery and readingSilently falls back to the defaults.
Any other read errorHard error.
Terminal window
$ cat ry.toml
strict = true
[check]
stric = true
typing = true
[format]
indent = 4
$ ry check .
warning: ignoring unknown config key `strict` — check the spelling, or update ry
warning: ignoring unknown config key `check.stric` — check the spelling, or update ry
warning: ignoring unknown config key `format.indent` — check the spelling, or update ry
1 file checked, no problems

A hard error names the file, the key, and the exact position:

Terminal window
$ cat ry.toml
[check]
typing = "yes"
$ ry check .
error: invalid config in /home/you/project/ry.toml for `check.typing` at line 2, column 10: invalid type: string "yes", expected a boolean
$ echo $?
2

Where that lands depends on how ry runs:

Behavior
CLIThe message goes to stderr and the command exits 2 — see the exit codes.
The language serverNever crashes. At startup it falls back to the defaults; on a live edit it keeps the previous configuration. Either way it shows the message and publishes a config finding on ry.toml at the offending line, cleared once the file loads again.
Old keyModern keyNote
case (top level)lint.naming-styleStill parses, and wins when both are set.
spaces (top level)format.indent-widthStill parses, and wins when both are set.
lint.missing-commanoneAccepted so old files keep loading, and does nothing: a missing argument comma is now a parse error.

These say where the binary is and how to launch it; none of them changes analysis. The language server ignores LSP workspace configuration outright, so every behavioural key must live in ry.toml.

SettingDefaultEffect
ry.pathnullLocation of the ry executable.
ry.argsnull, meaning ["server"]Arguments passed to the executable.
ry.experimentalFeaturesnullFeature names forwarded as --experimental-features. Currently only range_formatting — format the selected range instead of the whole file.

Changing any of the three prompts you to restart the server; it takes effect only then. The extension finds the binary in this order: the SERVER_PATH environment variable, ry.path, its own bundled copy, then ry on your PATH.

CommandDoes
ry: Restart ServerRestarts the language server
ry: Start ServerSame as restart
ry: Stop ServerShuts the language server down
ry: Open LogsOpens the server’s output channel

The extension contributes no settings of its own, so use Zed’s generic lsp.ry block.

SettingDefaultEffect
lsp.ry.binary.pathunsetAbsolute path to the binary. Setting it skips the PATH lookup and the release download.
lsp.ry.binary.argumentsunset, meaning ["server", "--stdio"]Arguments passed to the binary, however it was found.
lsp.ry.settingsunsetForwarded to the server, which ignores it. Put configuration in ry.toml.

Zed highlights R with tree-sitter, which sees a #: annotation as an ordinary comment. The colors come from the server as semantic tokens, which Zed leaves off by default:

settings.json
{
"languages": {
"R": {
"semantic_tokens": "combined"
}
}
}

Without a path or a binary on your PATH, the Zed extension downloads a release itself and reuses it afterwards.