Development
Project layout
Section titled “Project layout”ry is a Rust workspace of six product crates, described file by file in Structure and as a system in Architecture.
The console embeds R without linking against it at build time. It finds R’s shared library through
R_HOME (or by asking R RHOME on the PATH) and loads it at runtime, so the whole workspace
builds, and its unit tests run, on a machine with no R at all. Only running the console needs R. On
Unix the console hooks in through the ptr_R_ReadConsole globals, and on Windows through the
Rstart callback struct. Its end-to-end tests drive the real binary through a pseudo-terminal (a
Unix pty or a Windows ConPTY) and skip cleanly where R is missing. Run just repl-e2e locally before
you touch the console.
Architecture describes how the analysis works, and
Structure maps every source file. The editor extensions live under
editors/: code is the VS Code extension, and zed is the Zed extension.
Build and test
Section titled “Build and test”The justfile at the repository root names every day-to-day action, and running just with no
arguments lists them. These are the ones you will reach for most:
just gate # the full gate: battery, clippy -D warnings, fmt checkjust battery # the workspace test battery, excluding zed_ryjust fixture <group__case> # one focused fixture casejust bless -p semantics --test ... # re-bless fixture expectationsjust fuzz-deep # the long-running seeded fuzz passjust stats <path> # the workspace performance diagnosisjust perf # the corpus-wide perf and memory budgets (needs corpus/)just fixture runs a case from the test_typing_fixtures target in semantics unless you pass a
different target and package as its second and third arguments. Whenever you bless expectations,
review the diff before you commit it.
Without just, these are the raw commands:
cargo build # the product crate, the workspace default membercargo test # the product crate's suitescargo test --workspace --exclude zed_ry # everything: all six cratescargo test -p semantics # the analysis core's fixture and fuzz suitescargo test -p format --test test_format_fixturesMost behavior is verified by fixture tests: human-readable .test files, each rendered and compared
to its expected output. Read Testing for the fixture contract before you
add or change one. Two environment variables come up every day:
RY_BLESS=1rewrites the expected#++++blocks in place from the current output.FIXTURE_FILTER=group__caseruns a single fixture case.
Some suites, and every measurement instrument, read a corpus of real-world R code. Fetch it with
scripts/fetch-corpus.rs, a single-file script that needs cargo +nightly -Zscript and writes into
the gitignored corpus/ directory. The resolved inventory is committed as
scripts/corpus-manifest.txt. The performance and memory witness is
crates/semantics/tests/test_perf.rs, and Testing documents it.
Debug mode
Section titled “Debug mode”ry server --debug surfaces internal analysis facts in the editor: every hover gains a “Debug”
section showing the Lowering, Naming, and Parsing views of the expression under the cursor. Setting
RY_DEBUG=1 in the server’s environment does the same, for setups where changing the server’s
arguments is awkward. It is deliberately not a ry.toml key, because the configuration file is a
user-facing contract and this switch is a tool for people working on ry itself.
Diagnosing a slow workspace
Section titled “Diagnosing a slow workspace”ry debug analysis-stats [path] runs the full analysis pipeline over a workspace, through the same
queries the language server uses, and reports where the time and memory go. For each phase (load,
parse, lowering and naming, type checking, diagnostics) it prints the wall time and how much the
resident set grew. After that come the slowest files by type-checking time, and an incremental probe
on representative files that reports keystroke latency, item rechecks and resolve steps per
keystroke, and the raw re-parse time as a floor to compare against.
The command forces [check] typing on, since a diagnosis without the type checker measures nothing
interesting, and it tells you when your configuration had it off. Build with --release when the
absolute numbers matter; a debug build still gives honest ratios.
ry debug ast <file> prints a file’s syntax tree.
Working on this site
Section titled “Working on this site”Run just docs for a live preview, and cd docs && bun run build for a full build.
The formatter reference at docs/src/content/docs/reference/formatting-rules.md is generated, so
do not edit it by hand. Edit crates/format/tests/formatter.template.md instead, and regenerate the
page with just format-docs.
Setting up the VS Code extension
Section titled “Setting up the VS Code extension”- Run
cd editors/code && bun installto install the npm modules. - Press Ctrl+Shift+B in VS Code to compile the client in watch mode.
- Open the Run and Debug view with Ctrl+Shift+D.
- Select
Launch Clientfrom the drop-down, if it is not already selected. - Press F5 to run the launch configuration.
- In the
Extension Development Host
window that opens, open any
.Rfile.
Why the formatter code is imperative
Section titled “Why the formatter code is imperative”The hard part of formatting R is comments. A comment can sit between any two tokens: inside an if
header, between a call and its argument list, after an operator. A concise “format each field” style
drops such a comment without a word, so the formatter walks the concrete children token by token and
decides placement at the token level.
The rowan tree adds a twist. A trailing comment attaches inside an expression node, so any
decision about where a closing token goes has to look at tokens, never at whole elements. See
crates/format/src/format.rs and the formatter fixtures under crates/format/tests/format/.
References
Section titled “References”- https://github.com/wch/r-source/blob/trunk/src/main/gram.y
- https://cran.r-project.org/doc/manuals/r-release/R-lang.html
- https://www.reddit.com/r/rust/comments/uu47mk/comment/i9dn0yg/
VS Code extensions
Section titled “VS Code extensions”- docs:
- examples:
Other language servers written in Rust
Section titled “Other language servers written in Rust”- https://github.com/gleam-lang/gleam/tree/main/compiler-core/src/language_server
- https://github.com/supabase-community/postgres-language-server
- tower-lsp
- https://github.com/FuelLabs/sway
- https://github.com/IWANABETHATGUY/tower-lsp-boilerplate
- https://github.com/TenStrings/glicol-lsp/blob/77e97d9c687dc5d66871ad5ec91b6f049de2b8e8/src/main.rs#L16
- https://github.com/Automattic/harper
- https://github.com/jfecher/ante/blob/5f7446375bc1c6c94b44a44bfb89777c1437aaf5/ante-ls/src/main.rs#L163
- async_lsp
Formatting
Section titled “Formatting”Language design and typing
Section titled “Language design and typing”- https://github.com/Glyphack/enderpy
- https://github.com/dgkf/R
- https://github.com/fabriceHategekimana/typr
- https://github.com/salsa-rs/salsa/blob/master/examples/calc/type_check.rs