ts-r-lsp

Language server. Replaces tsserver and the ESLint server.

  • Pre-alpha
  • Works from source

ts-r-lsp gives every editor window on a workspace one shared server instead of a TypeScript process each. Each window starts a small front process, about 3 MB, that connects to one daemon per workspace. The daemon runs the type backend, watches memory, and adds lint diagnostics from ts-r-lint.

First diagnostics, largest file (168 KB)Shorter is faster. Pre-release build, real app.
  • ts-r-lsp18 ms
  • tsserver 6.0.3218 ms

12× faster first errors

What switching looks like

Nothing is on npm yet. Once it is, switching looks like this: ts-r-lsp reads the config you already have, so the change is to the command, not the project.

languages.toml (Helix)
  [language-server.typescript]
-   command = "typescript-language-server"
+   command = "ts-r-lsp"
  args = ["--stdio"]

What works, and what doesn’t yet

Works today

  • One daemon per workspace, shared by every window
  • A memory budget: the backend restarts when idle above it, and open documents are replayed
  • The ts-r checker or your own TypeScript as the type backend, with a per-project parity check
  • Lint diagnostics and code actions: fixes, suggestions, Fix all and disable comments
  • A tsserver stand-in so VS Code’s built-in TypeScript support can share the daemon

Not yet

  • Misses its 500 MB target with every file of a large project open
  • Typing in TSX files with React types is slower than tsserver
  • 22 of 24 reference-app projects fall back to tsserver for diagnostics
  • The VS Code extension isn’t packaged yet
  • Windows is untested

Measured

Every run, on the same reference app as the home page, with the command and commit that recorded it.

ts-r-lsp with our checker against tsserver and TypeScript 7's LSP

Misses the 500 MB target

The editor target is at most 500 MB with every file of a large project open. ts-r-lsp misses it: 587 MB with all 649 private-app sources open, about the same as TypeScript 7's native LSP. Typing in a React TSX file is also slower than tsserver today, because every edit re-checks on a fresh checker. On this project 22 of 24 tsconfig projects still fall back to tsserver for diagnostics, because our checker does not match tsc on them yet.

Memory with all 649 files openShorter is faster, in MB
  • ts-r
  • Other tools
  • TypeScript 7 LSP (tsc --lsp)578 MB
  • ts-r-lsp587 MB
  • tsserver 6.0.31,132 MB
Keystroke to diagnostics in card-form.tsx, p50Shorter is faster, in ms
  • ts-r
  • Other tools
  • TypeScript 7 LSP4.3 ms
  • tsserver 6.0.39.9 ms
  • ts-r-lsp25.7 ms
First diagnostics, largest file (168 KB)Shorter is faster, in ms
  • ts-r
  • Other tools
  • ts-r-lsp18 ms
  • TypeScript 7 LSP34 ms
  • tsserver 6.0.3218 ms
Open all 649 files to diagnostics on eachShorter is faster, in s
  • ts-r
  • Other tools
  • ts-r-lsp0.32 s
  • TypeScript 7 LSP1.3 s
  • tsserver 6.0.34.4 s
More editor measures
Measurets-r-lsptsserver 6.0.3TypeScript 7 LSP
Largest file: keystroke to diagnostics p50 / p954.3 / 5.1 ms10.5 / 16.1 ms4.4 / 6.9 ms
Largest file: hover p50 / p950.21 / 0.45 ms0.42 / 3.49 ms0.14 / 0.70 ms
Largest file: definition p50 / p950.27 / 0.52 ms0.22 / 0.65 ms0.10 / 0.20 ms
Memory with the largest file open43 MB442 MB108 MB
card-form.tsx: first diagnostics44 ms270 ms27 ms
card-form.tsx: completion p951.19 ms7.35 ms2.69 ms
How we measured
Measured
A scripted LSP client: first diagnostics, hover, definition and completion latency, keystroke to diagnostics (100 keys, 150 ms apart) on one file, and memory with one file or all 649 open (24 projects, 15 s settle)
Machine
Apple M-series Mac, macOS arm64, release build (the exact chip was not recorded)
Software
ts-r-lsp release build with the ts-r backend and the tsc parity check off; tsserver 6.0.3 (the project's TypeScript); TypeScript 7.0.2's native LSP (tsc --lsp)
Input
private-app; the largest file is packages/api-client/src/schema.gen.ts, the TSX file apps/web/.../card-form.tsx
Command
scripts/limit.sh -- node editors/vscode/bench/editor-latency.js ts-r target/release/ts-r-lsp <root> <file> (tsserver and tsgo modes for the others)
Recorded in
commit 59268bf

Lint in the editor: ts-r-lsp against the ESLint extension's server

Time from a keystroke to new lint diagnostics, and server memory, in the largest linted private-app file (20 KB) with the project's own configs. The ESLint server also runs 41 type-aware rules that ts-r skips.

Keystroke to lint diagnostics, p50Shorter is faster, in ms
  • ts-r
  • Other tools
  • ts-r-lsp --lint-only15.7 ms
  • ESLint server 3.0.3447.1 ms
Server memory, one file openShorter is faster, in MB
  • ts-r
  • Other tools
  • ts-r-lsp --lint-only6.5 MB
  • ESLint server 3.0.34874 MB
How we measured
Measured
A scripted LSP client types 300 keystrokes 100 ms apart into a comment and times didChange to publishDiagnostics; RSS of the server
Machine
Apple M-series Mac, macOS arm64, release build (the exact chip was not recorded)
Software
ts-r-lsp release build; the ESLint VS Code extension's server 3.0.34 run headlessly with pull diagnostics
Input
private-app domains/listings/src/application/profiles.test.ts (20 KB), project configs
Command
scripts/limit.sh -- node editors/vscode/bench/lint-keystrokes.js ts-r target/release/ts-r-lsp <root> <file> (eslint mode with eslintServer.js for the other)
Recorded in
commit 9555833

ts-r-lsp: two editor windows on your own TypeScript

ts-r-lsp can also run your project's own TypeScript behind one shared server per workspace, instead of our checker. This measures total memory for two editor windows on the same project in that mode.

Memory for two editor windowsShorter is faster, in MB
  • ts-r
  • Other tools
  • ts-r-lsp, one shared server318 MB
  • Two plain tsservers561 MB
  • VS Code default setup≈1,180 MB

Time to first diagnostics in the same run: cold window about 420 ms (plain tsserver about 370 ms), second window about 140 ms (plain tsserver about 330 ms).

How we measured
Measured
Total resident memory (RSS) of every process in each setup, summed with their children, after both windows have diagnostics for every open file
Machine
Apple M-series Mac, macOS arm64, release build (the exact chip was not recorded)
Software
TypeScript 6.0.3 (tsserver) as the type backend
Input
private-app, two windows with 2 files open in each
Command
cargo build --release -p ts_r_lsp && scripts/limit.sh -- cargo run --release -p ts_r_lsp --example two_windows -- /path/to/repo
Recorded in
commit bf06dc4

Command line

An excerpt of ts-r-lsp --help. You can also run it through the launcher as ts-r lsp.

Next to tsserver

Editors other than VS Code start ts-r-lsp --stdio as an ordinary language server. When a project opens, the daemon runs your own tsc in the background and compares results; where they differ, diagnostics come from tsserver while hover, definition and the rest stay on ts-r.

ts-r-lsp --help
ts-r-lsp: one shared, memory-budgeted language server per workspace

  ts-r-lsp [--stdio] [OPTIONS]     LSP server on stdio
  ts-r-lsp tsserver [ARGS]         tsserver stand-in on stdio
  ts-r-lsp status [--root DIR]     print the daemon's status as JSON
  ts-r-lsp stop [--root DIR]       ask the daemon to exit