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 things worth knowing about 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 — 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. |
| 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 .warning: ignoring unknown config key `strict` — check the spelling, or update rywarning: ignoring unknown config key `check.stric` — check the spelling, or update rywarning: ignoring unknown config key `format.indent` — check the spelling, or update ry1 file checked, no problemsA hard error names the file, the key, and the exact position:
$ 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 $?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.