Repository navigation
Conversation
- Move Io, Chan, and listen infrastructure to server.ml - Simplify on_request to take a packed request directly - Add basic hover response with markdown content - Rename public executable to rescript-language-server
- Add `parse_implementation_from_source` to parsing/print engine types and all engine implementations, enabling parsing from a string source rather than a filename - Use `parse_implementation_from_source` in CompletionFrontEnd - Rename package from `rescript-lsp` to `rescript-language-server` - Refactor LSP server with typed state, document store, and diagnostics - Add hover support via completion backend integration
Move LSP modules under lsp/src/ with a thin lsp/bin/ entry point, add a configuration module, and introduce a tests/lsp_tests workspace exercising hover end-to-end.
Splits `Commands.ml` into a pure layer that returns OCaml values (option, list, typed records like Protocol.hover, Protocol.signatureHelp, Protocol.completionItem) and a new `analysis/src/Cli.ml` that does the stringify-and-print step. `analysis/bin/main.ml` now dispatches to `Cli.*`, while the LSP server consumes `Commands.*` directly. Makes the parsers accept source strings: `res_driver` gains `parse_interface_from_source` alongside the existing `parse_implementation_from_source`
Welcome to Codecov 🎉Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests. Thanks for integrating Codecov - We've got you covered ☂️ |
- Rename Helper module to Client - Move expected output files into workspace directories - Improve request log format to show file path:line:col - Simplify hover.ml by removing intermediate result binding - Disable in-source compilation for basic-workspace
Changelog🏠 Internal
Semantic Diff143 commits · Full page Extract server I/O into Server module and simplify handlerse2989d0 · aspeddro · May 12, 2026, 01:02 UTC - Move Io, Chan, and listen infrastructure to server.ml - Simplify on_request to take a packed request directly - Add basic hover response with markdown content - Rename public executable to rescript-language-server Add parse_implementation_from_source and refactor LSP servera99abc6 · aspeddro · May 12, 2026, 18:49 UTC - Add Split LSP into bin/src layout and add hover integration test365836c · aspeddro · May 14, 2026, 08:11 UTC Move LSP modules under lsp/src/ with a thin lsp/bin/ entry point, add a configuration module, and introduce a tests/lsp_tests workspace exercising hover end-to-end. Refactor analysis to decouple I/O from core logic1692629 · aspeddro · May 16, 2026, 02:53 UTC Splits Makes the parsers accept source strings: Rename
|
Signed-off-by: Pedro Castro <aspeddro@gmail.com>
Signed-off-by: Pedro Castro <aspeddro@gmail.com>
|
@codex review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
This branch introduces a ReScript LSP server built on top of the existing
analysislibrary. It runs in-process throughrescript lsp --stdio, using thenew
rescriptCLI written in OCaml (rewatch-ocaml). There is no separaterescript-language-serverexecutable. It's a separate, OCaml-side explorationalongside the Rust/rewatch-based experiment in #8243, the two share the same
goal (a LSP server for ReScript) but approach it from different ends of the
toolchain.
Why rewriting the server in OCaml is a good fit
The editor features are already implemented in the OCaml analysis library.
Hover, completion, references, rename, document symbols, code actions, and
diagnostics all depend on compiler data structures such as
.cmt/.cmti,typed trees, locations, package metadata, and compiler diagnostics. Running the
LSP server in OCaml lets the server call those APIs directly instead of shelling
out to
rescript-editor-analysis.exeor passing JSON through stdin. Thatremoves a large amount of process orchestration and serialization glue, and
keeps the LSP closer to the compiler types it needs to understand. This also
makes the server easier to evolve with the compiler. When typedtree, diagnostic
formats, package discovery, or
.cmthandling changes, the LSP can be updatedin the same language and build system as the analysis code. That reduces version
skew and makes bugs easier to reproduce with Dune/expect tests.
Why ship the language server with the compiler
The language server is tightly coupled to the compiler version used by a
project. It reads
.cmtand.cmtifiles containing marshalled compilerinternals such as
Typedtree,Types, andEnv. These files are not a stable,schema-aware protocol: changing one of those OCaml types can make an analysis
binary incompatible with artifacts produced by another compiler revision.
This is not only a theoretical compatibility concern. In
#8475, adding typedtree
constructors for
for...ofwithout changing the CMT magic number allowed anewer analysis binary to read incompatible ReScript 12 artifacts and
segmentation fault. The same class of failure appeared after record-rest changes
altered compiler data stored in CMT files. Bumping the CMT magic number makes
these mismatches rejectable, but the project must still be rebuilt with the
matching compiler.
Shipping the server in the same platform package as the compiler, and exposing
it through
rescript lsp --stdio, gives clients one project-local entrypoint.The selected ReScript dependency then supplies
bsc, the analysis code, and thelanguage server from the same release. This has several practical benefits:
rescriptexecutable links the server library directly, so noseparate language-server executable needs to be located or launched.
Bundling does not make stale artifacts safe. Compiler changes that alter
marshalled CMT data must still bump the magic number, and the server should turn
version mismatches into an actionable request to rebuild. A separately versioned
server becomes practical only if analysis moves to a stable, explicitly
versioned artifact format or maintains decoders for multiple compiler-internal
formats. Until then, promising one server binary that supports multiple compiler
versions would hide a compatibility boundary that already exists.
What's here
lsp/src, with its own opam package(
rescript-language-server.opam). It useslsp(>= 1.22.0),eio,ppx_deriving_yojson,ppx_expectand the in-treeanalysislibrary.rewatch-ocaml/rescript_ocaml.ml. The OCamlrescriptexecutable links the server library and uses
Eio_main.runto supply itsfilesystem, stdin and stdout.
rescript lsp --stdio. The--stdioflag is required:rescript lspwithout it fails and displays usage help. The LSP subcommanddoes not accept
--quietor--verbose.tests/lsp_testsexercise the server through thesame OCaml CLI entrypoint.
Status / what's not here yet
The main objective is to first maintain feature parity with the current server
(server.ts) as much as possible. Below is a list of some requests and
notifications. Some are out of scope because they don't make sense.
initialize- client requestinitialized- client notificationshutdown- client requestexit- client notificationtextDocument/didOpen- client notificationtextDocument/didChange- client notificationtextDocument/didClose- client notificationtextDocument/didSave- client notification - It will not beimplemented for now.
textDocument/hover- client requesttextDocument/publishDiagnostics- server notificationdependency errors be published?
does not point at a precise source range. When the document is open, we
expand the diagnostic range to cover the whole document so the editor can
display a file-level diagnostic. If the document is not open, we keep the
range parsed from the compiler log, i.e.,
{start: {line: 0, character: 0}, end: {line: 0, character: 6}}let a=1.Protocol positions are zero-based, so the position is
length - 1.Analysisfor syntax errors and ignore them in the compiler logparser.
TextDocumentDidChangeandprovide instant feedback.
.compiler.log- server featurecompiler_log.ml. Seetests/build_testsfor more examples.
.sourcedirs.json- server feature.sourcedirs.jsonis alwaysgenerated with
build_rootfield for each subpackage.workspace/didChangeWatchedFiles- client notification.compiler.logfiles, which usually means a ReScript build has finished.When a watched log changes, the server re-reads every known compiler log and
republishes diagnostics so stale errors are cleared and monorepo diagnostics
stay in sync.
GlobPatternwithbaseUri(workspace root)?rescript.jsonfiles in the workspace root?rescript.json. For example,suffixandpackage-specsare needed to create the code actionOpen compiled JS file. Changes independenciesimpact variousfunctionalities because they modify the state in
Analysis.Shared_types.state.client/registerCapability- server requestworkspace/didChangeWatchedFilesis commonly registereddynamically. The server does not know all file-watch patterns during the
static
initializeresponse. In this LSP, the watcher list depends onproject state, especially
.sourcedirs.json, which tells us where eachReScript build root lives. In monorepos, that means the server needs to
register watchers for compiler logs after initialization, once it has
workspace context. That means watching generated
.compiler.logfiles. Whenthe client sees one change, it sends
workspace/didChangeWatchedFiles, andthe server refreshes diagnostics.
textDocument/diagnostic- client requesttextDocument/completion- client requestcompletionItem/resolve- client requesttextDocument/signatureHelp- client requesttextDocument/definition- client requesttextDocument/declaration- client request - It will not be implementedfor now.
textDocument/typeDefinition- client requesttextDocument/implementation- client request - It will not beimplemented for now.
textDocument/references- client requestSupport v12 CMT files in analysis #8477
textDocument/documentHighlight- client request - It will not beimplemented for now.
textDocument/documentSymbol- client request"document_symbols": "on"configuration. When enabled,tree-sitter is not used for document symbols.
workspace/symbol- client request - It will not be implemented fornow.
textDocument/codeAction- client requestrescript/openCompiled)window/showDocument.rescript/createInterface)rescript/switchImplementationInterface)window/showDocumentrequest.codeAction/resolve- client request - It will not be implemented fornow.
textDocument/codeLens- client requestworkspace/codeLens/refresh- server requestcodeLens/resolve- client request - It will not be implemented fornow.
textDocument/inlayHint- client requestinlayHint/resolve- client request - It will not be implemented fornow.
textDocument/semanticTokens/full- client requesttextDocument/semanticTokens/full/delta- client request - It will notbe implemented for now.
textDocument/semanticTokens/range- client request - It will not beimplemented for now.
textDocument/rename- client requesttextDocument/prepareRename- client requesttextDocument/formatting- client requesttextDocument/rangeFormatting- client request - It will not beimplemented for now.
textDocument/onTypeFormatting- client request - It will not beimplemented for now.
textDocument/foldingRange- client request - It will not beimplemented for now.
textDocument/selectionRange- client request - It will not beimplemented for now.
textDocument/documentLink- client request - It will not beimplemented for now.
documentLink/resolve- client request - It will not be implemented fornow.
textDocument/documentColor- client request - It will not beimplemented for now.
textDocument/linkedEditingRange- client request - It will not beimplemented for now.
workspace/didChangeConfiguration- client notificationworkspace/configuration- server requestworkspace/executeCommand- client requestrescript/dumpServerStateState.t(diagnostics, document store, status, configuration,analysis state (
Analysis.Shared_types.state) and compiler config)..jsonfile and open it usingwindow/showDocument.Requires client support for that request; the command returns no content.
rescript/openCompiled: Triggered by a code actionwindow/showDocument.rescript/createInterface: Triggered by a code actioninterface file if the client supports the
window/showDocumentrequest.rescript/switchImplementationInterface: Triggered by a code actionwindow/showDocumentrequest.rescript/dumpCmt: Dump cmt file content.txtfile and open it usingwindow/showDocument.Requires client support for that request; the command returns no content.
rescript/dumpParseTreeIt will not be implemented for now.rescript/dumpTypedTreeIt will not be implemented for now.window/showDocumentrequest.LSP: support windows/showDocument zed-industries/zed#58099.
textDocument/openCompiled- client requesttextDocument/createInterface- client requesttextDocument/switchImplementationInterface: Currently it's done on theclient side, but it could be a command triggered by a code action.
rescript/startBuild- client request - How the build integrationwill be done. See last point. It will not be implemented for now
initialization and drop this custom request?
rescript/stopBuild?rescript/compilationFinished- server notification - Sent from theserver to the client when compilation is finished
server.ts) sends this notification, and the VSCode clientuses it to run Code Analysis. The server sends it when
.compiler.logchanges. Only VSCode uses this notification.
reanalyze may be integrated into the server instead of running on the
client side.
$/progressserver notification?rescript/compilationStatus- server notification - Sends thecompilation status from the server to the client.
bar. Only VSCode uses this notification.
$/progressserver notification?server.ts) supports stdio and node-ipc. The VSCodeclient uses node-ipc. This implementation supports only stdio.
not be implemented for now.
produced by an existing
rescript build, rather than the LSP starting ormanaging builds itself.
for now. Idea to explore.
rescript watchprocess, it's much harder to compilein memory?
Tests
tests/lsp_tests/contains OCamlppx_expectend-to-end tests, with a namedlet%expect_testfor each scenario and separate modules for each feature.Representative cases are adapted from
tests/analysis_tests; the originalanalysis suite remains in place. Source snippets no longer contain cursor
markers: requests use explicit, zero-based LSP positions.
The shared client in
test.mlstartsrewatch-ocaml/rescript_ocaml.exe lsp --stdiothroughUnix.open_process_args,avoiding
Eio.Stdenv.process_mgrin the test harness so it can run on Windows,Linux and macOS. Dune declares the CLI executable as a test dependency. The
harness supplies
RESCRIPT_RUNTIMEbecause it bypasses the npm launcher.Tests create their source files and
rescript.jsonfrom OCaml.Test.setup_projectwrites the project and optionally builds it;Test.with_projectmanages a temporary workspace and cleanup. Tests can addmultiple documents, edit unsaved buffers, rebuild, change settings and vary
client capabilities. Snapshots normalize temporary workspace paths while
preserving ranges and response ordering.
Coverage includes hover and JSX hover, completion and JSX completion, completion
resolution, definitions, type definitions, references, prepare rename, rename,
document symbols, semantic tokens, signature help, interface creation, code
actions, inlay hints, syntax diagnostics and code lenses. Diagnostic regression
tests cover build-log updates, stale warnings, duplicate publications, flicker
and document closure. Execute-command tests check the temporary state/CMT dump
files and
window/showDocumentcapability handling. These tests do not implycomplete coverage or feature parity with the existing server.
Build the toolchain and runtime and run the LSP suite with:
opam exec -- make test-lspAfter the toolchain and runtime are built, run just the integration suite with
opam exec -- dune runtest tests/lsp_tests. Runopam exec -- dune runtesttoalso cover inline tests in the server library, including
.sourcedirs.jsonbuild-root extraction and
.compiler.logparsing. Seethe snapshot test guide for helpers and commands
to review and promote snapshots.
Breaking Changes
initializationOptions. This server ignoressettings coming from
initializationOptions. SeeSpec should discourage abuse of initializationOptions and didChangeConfiguration microsoft/language-server-protocol#567 (comment).
We use
workspace/configuration.rescript/dumpServerStateandrescript/dumpCmtopen temporary files throughwindow/showDocumentinstead of returning a JSON object with acontentfield. Clients need to support that request and no longer need to create their
own buffers from the command result.
Server settings
Proposed interface.
supportMarkdownLinksis not a setting. It's a greatfeature, but some clients don't have good support; Neovim are a example.
Therefore, I'm promoting it to a setting so users can enable or disable it.
Release and transition plan
the configuration example.
Other related topics
Refactor analysis for use on the server side
#8426
#8466
#8478
yojsonandlsplibraries in the analysis library##8436
Shared_types.state#8465