char/avril

(wip) why'd you have to go and make things so complicaaaaated?

git clone https://git.t4t.associates/char/avril

Charlotte Somdeslop: factor module prep/transpilatione/etc into loader.rsab5df08

main
3 folders3 files

files

src/
tests/
typescript/
BACKEND.md3.7 KiB
Cargo.toml465 B
README.md8.0 KiB

avril LSP

avril-lsp contains the Rust native TypeScript adapter and stdio LSP frontend. avril lsp supplies avril's live project service without initializing a guest runtime. A small in-tree patch exposes the pinned TypeScript 7.1.0-dev.20261013.1 native language service through --api. Build the executable using the backend instructions.

cargo build -p avril-cli --bin avril
target/debug/avril lsp --typescript=/absolute/path/to/avril-tsc \
  --project=/absolute/path/to/workspace

The project directory defaults to the current directory. With unrestricted execution authority, workspace JS/TS files are graph roots (respecting ignore files, excluding hidden directories, node_modules, cache and vendor directories, and not following directory symlinks). With restricted authority, only explicit open documents are roots; discovery never broadens that authority. Imports grow the graph automatically, including unsaved import edits. No manifest or generated tsconfig is needed. The VS Code client launches this mode.

avril check --typescript=PATH <module> reuses the same project service and backend without an LSP session: it builds one generation rooted at the entry module, prints every diagnostic, and exits nonzero on errors. See the check documentation.

Resolution and policy

The live service reuses ModuleGraph, LocalPackages, RemotePackages, and PackageStore, not a parallel implementation of avril resolution. The in-memory type collector in runtime/src/type_graph.rs feeds live generations. It follows type-only/dynamic literal imports, package exports, per-referrer maps, and @ts-self-types declaration redirects. Builtin declarations are embedded and need no source checkout.

Document changes and close/revert rebuild the graph. Directory watches cover the workspace, known source/config parents, explicit lock and vendor inputs, and external tsconfig extends (including failed config probes). Watch invalidation is coalesced on a 500 ms tick. Cache writes are excluded to avoid refresh loops. Under restricted execution capabilities, native directory watches are disabled; client workspace/didChangeWatchedFiles notifications still refresh the graph. The VS Code client supplies workspace file notifications. Malformed/unavailable inputs produce resolver diagnostics rather than silently retaining old edges.

Language service and boundaries

The native backend supplies rich hover and signature help, completion and resolve (including snippets, rewrites and graph-scoped auto-imports), definitions/type definitions/implementations, references, prepare-rename/rename, document and workspace symbols, highlights, folding/selection ranges, semantic tokens, inlay hints, call hierarchy, code lenses, JSX linked editing, formatting, and native quick fixes/fix-all/organize-import actions. Capabilities and the method allowlist come from the patch, not a parallel Rust feature catalog. Diagnostics include native suggestion tags and related locations as well as Avril's resolver errors.

Native TypeScript preferences can be provided in initialize.initializationOptions.preferences and replaced with workspace/didChangeConfiguration.settings.preferences. These include inlay hint and code-lens preferences (disabled by upstream defaults), formatting, and completion preferences. Missing properties preserve snapshot defaults.

Remaining limits

This is not a claim of complete classic tsserver parity. The pinned native backend does not implement the full classic refactor catalog or type hierarchy. File-move/import-update operations, project-reference discovery, untitled documents without file identities, and acquisition of otherwise unreachable packages solely for auto-import suggestions are not implemented. Unsupported providers are not advertised; unknown methods return MethodNotFound.

Document notifications are coalesced, and failed backends recover before the next query. Generations still rebuild the host graph and native session, favoring straightforward invalidation over incremental performance. Directory watches are coarse; deferred results must be requested again after a refresh. Compiler library globals are not yet curated to Avril's exact runtime.

The native executable must be trusted. Its environment is cleared and filesystem callbacks restrict compiler inputs, but they are not an OS sandbox. Project code and external compiler plugins are not executed. Guest npm/Node compatibility and automatic backend distribution are not introduced.

Validation

AVRIL_TEST_TYPESCRIPT="$TSC" cargo nextest run --workspace
cargo test --workspace --doc
cargo clippy -p avril-lsp -p avril-runtime --all-targets -- -D warnings
cd editors/vscode && pnpm test

$TSC is the backend's absolute path; tests that need it are skipped when AVRIL_TEST_TYPESCRIPT is unset. Lifecycle tests inspect Linux /proc. The pinned backend is the test suite's only external dependency. Actual editor-host tests and the required success marker are described in the client README.