ry is four tools in one binary:
- A language server — hover, completion, go-to-definition, references, rename, and inlay hints, in any editor that supports LSP.
- A formatter — a single consistent style, with almost no configuration.
- An R console — a REPL with project-aware completion.
- A type checker — optional; its inferred types also power the editor features.
It requires no changes to your code, and the same binary runs in your editor and in CI.
This project was created to address the slow performance of the existing R language server on large codebases. Originally called "The R(oughly good enough) language server", it began as a minimal but fast language server that supported only go-to-definition using regex-based indexing.
The second iteration replaced the regex index with tree-sitter and performed proper analysis on syntax trees. Go-to-definition became reliable, formatting and linting were added, and the "good enough" part was dropped from the name — the project became Roughly.
The third iteration uses its own R parser and its own static analysis. That is what makes good error messages possible:
planets <- c("Mercury", "Venus" "Earth", "Mars")For comparison, R reports the same mistake as:
Error: unexpected string constant in "planets <- c("Mercury", "Venus" "Earth""
Tree-sitter also recovers from broken input, but a dedicated parser can report precisely what is wrong, and its syntax tree integrates directly with the static analysis, so one mistake does not hide the lints and type errors in the rest of the file. The trend has continued: the better the tool became, the shorter its name — first Roughly, now ry.
- VS Code — the extension bundles the binary, so there is nothing else to set up. It is still published under the project's previous name.
- A binary — from Releases, for CI or any other editor.
- Cargo —
cargo install --git https://github.com/felix-andreas/ry ry-lang.
Then, in a project directory:
ry check # lint and type-check the project
ry fmt # format it (--check and --diff for CI)
ry server # the language server; your editor starts this for you
No configuration is needed. Type errors are opt-in via a ry.toml:
[check]
typing = truery includes the first static type checker for R. It is novel and experimental: R has no established typing semantics, so ry defines its own. The tutorial introduces it on real code; the type system reference specifies the full semantics.
Most type errors in R are found by running the code. ry finds the ones that can be determined from
the source alone, before anything runs. Types are inferred by default; explicit annotations are
written in #: comments, so annotated files remain ordinary R code.
The static analysis is useful even with type errors disabled: completion, signature help, hover,
inlay hints, and rename are all based on it. The typing = true setting only controls whether type
mismatches are reported.
Checking all of R is not the goal. R is a highly dynamic language, and no static checker can
describe everything it can do. ry therefore aims for two properties instead of coverage: soundness —
what it claims must be true — and high performance, even on large codebases. This is why inference
is Hindley–Milner and why some concepts are deliberately not supported. Code with a describable
shape is checked; everything else becomes Unknown, which is compatible with everything, so a gap
means a check was skipped rather than a wrong answer produced.
Concepts introduces the type system, and
Limitations documents the limits of the static
analysis.
Development and Architecture describe the project layout, the test suites, and the design.
UPL-1.0 © Felix Andreas