Configuration
Everything you can change about ry’s behavior lives in one file, ry.toml. Editor settings only say where the binary is.
Project discovery
Section titled “Project discovery”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 runs | Search starts at |
|---|---|
ry check R/utils.R | the file’s own directory |
ry check . | that directory |
| The language server | the workspace folder your editor announces; failing that, the process working directory |
| Rule | Behavior |
|---|---|
| Search | walk up from the starting directory; the first ry.toml wins. None found: built-in defaults. |
| Merging | none — one file supplies every key. |
| Reload | the 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 targets | discovery runs once per argument, so two arguments can resolve two different files. |
.. in a path | cancelled textually before the search, so project/ry.toml does not govern project/../outside.R. |
$ cat project/ry.tomlspaces = 8$ ry fmt --diff project/inside.RDiff in project/inside.R:1 1 | f <- function(x) {2 |- x 2 |+ x3 3 | }1 file would be reformatted, 0 files already formatted$ ry fmt --diff project/../outside.R0 files would be reformatted, 1 file already formattedProject root
Section titled “Project root”The project root is a separate decision: it sets the analysis scope — which files see each other’s definitions — not which config is loaded.
| Situation | Root |
|---|---|
An ancestor holds ry.toml or DESCRIPTION | the nearest such directory |
| Otherwise, the target is a directory | that directory |
Otherwise, the target sits directly under an R/ directory | the parent of R/ |
| Otherwise | the file’s own directory |
[format]
Section titled “[format]”| Key | Type | Default | Effect |
|---|---|---|---|
indent-width | integer | 2 | Spaces 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. |
[lint]
Section titled “[lint]”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.
| Key | Type | Default | Effect |
|---|---|---|---|
naming-style | "snake_case", "camelCase" | unset — check off | Reports 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-operator | level | "warn" | = used for assignment. |
boolean-shorthand | level | "warn" | T or F written instead of TRUE or FALSE. |
trailing-comma | level | "error" | A comma after the last argument of a call. |
unused-parameter | level | "off" | Function formals never read. S3 methods and your project’s own generics are exempt. |
unused-import | level | "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-builtin | level | "off" | A top-level binding with the same name as a base export. |
shadows-namespace | level | "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.
[check]
Section titled “[check]”Type inference always runs — hover, inlay hints, and signature help work regardless of these keys. [check] only decides which findings are reported.
| Key | Type | Default | Effect |
|---|---|---|---|
unused | boolean | true | Report unused — bindings whose value is never read. |
typing | boolean | false | Report type-mismatch. See the tutorial. |
strict | boolean | false | Report each site with a genuinely undetermined type, and raise every unresolved finding from warning to error. See strict mode. |
exclude | array 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 rules govern exclude:
- Patterns are anchored at the directory holding
ry.toml, and follow gitignore rules:scripts/excludes that whole subtree,**/generatedmatches 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 fmtignores 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/..gitignoreis honored too, git checkout or not.
$ cat ry.toml[check]exclude = ["scripts/"]$ ry check .warning[unused]: `v` is assigned but never used. --> R/a.R:1:191 | f <- function() { v <- 1; 2 } ^
1 problem in 1 fileInvalid and unknown keys
Section titled “Invalid and unknown keys”An unknown key is never fatal, so a config written for a newer ry still starts an older one.
| Situation | Result |
|---|---|
| Unknown key | Ignored, with one warning naming it. Known keys beside it still load, and the exit code is unaffected. |
| Known key at the wrong level | Ignored, with a warning naming the table it belongs under. A typing = true written outside [check] sets nothing. |
| Wrong type on a known key | Hard error. |
| Malformed TOML | Hard error. |
Invalid [check] exclude pattern | Hard error. |
| The file disappears between discovery and reading | Silently falls back to the defaults. |
| Any other read error | Hard error. |
$ cat ry.tomlstrict = true
[check]stric = truetyping = true
[format]indent = 4$ ry check . ! ignoring config key `strict` — it belongs under `[check]`, and nothing outside a table sets it ! ignoring unknown config key `check.stric` — check the spelling, or update ry ! ignoring unknown config key `format.indent` — check the spelling, or update ry1 file checked, no problemsA hard error shows the offending line, so you do not have to count columns:
$ cat ry.toml[check]typing = "yes"$ ry check .config
x invalid config for `check.typing`: invalid type: string "yes", expected a boolean --[/home/you/project/ry.toml:2:10] 1 | [check] 2 | typing = "yes" | ^^^^^$ echo $?2Where that lands depends on how ry runs:
| Behavior | |
|---|---|
| CLI | The message goes to stderr and the command exits 2 — see the exit codes. |
| The language server | Never 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. |
Legacy keys
Section titled “Legacy keys”| Old key | Modern key | Note |
|---|---|---|
case (top level) | lint.naming-style | Still parses, and wins when both are set. |
spaces (top level) | format.indent-width | Still parses, and wins when both are set. |
lint.missing-comma | none | Accepted so old files keep loading, and does nothing: a missing argument comma is now a parse error. |
Editor settings
Section titled “Editor settings”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.
VS Code
Section titled “VS Code”| Setting | Default | Effect |
|---|---|---|
ry.path | null | Location of the ry executable. |
ry.args | null, meaning ["server"] | Arguments passed to the executable. |
ry.experimentalFeatures | null | Feature 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.
| Command | Does |
|---|---|
| ry: Restart Server | Restarts the language server |
| ry: Start Server | Same as restart |
| ry: Stop Server | Shuts the language server down |
| ry: Open Logs | Opens the server’s output channel |
The extension contributes no settings of its own, so use Zed’s generic lsp.ry block.
| Setting | Default | Effect |
|---|---|---|
lsp.ry.binary.path | unset | Absolute path to the binary. Setting it skips the PATH lookup and the release download. |
lsp.ry.binary.arguments | unset, meaning ["server", "--stdio"] | Arguments passed to the binary, however it was found. |
lsp.ry.settings | unset | Forwarded 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:
{ "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.