Experimental ReScript Language Server in OCaml#8425
Draft
aspeddro wants to merge 109 commits into
Draft
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 ☂️ |
8 tasks
- 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
Publish syntax error diagnostic on open document
Track compiler diagnostics, compiler syntax diagnostics, and open-document syntax diagnostics independently so each source can be refreshed according to its own lifetime. A syntax-only compiler log is an incomplete build snapshot, so keep existing type diagnostics until the next complete compiler snapshot or successful empty log. Open documents continue to use in-memory syntax diagnostics, while closed documents can fall back to compiler syntax diagnostics.
Read diagnostics from the compiler-log roots recorded in .sourcedirs.json so opening a document can publish the last known compiler diagnostics even when the compiler is not currently running. Keep the root lib/bs/.compiler.log fallback only for older or partial builds without sourcedirs metadata, and document the diagnostic behavior and release notes.
Do not push open-buffer syntax diagnostics through textDocument/publishDiagnostics on didChange. They are already available through textDocument/diagnostic. Keep the changed URI force-published so any stale pushed syntax diagnostics are cleared when the filtered diagnostic set is empty.
1 task
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This branch introduces a standalone ReScript LSP server (
rescript-language-server) built on top of the existinganalysislibrary. It's a separate, OCaml-side exploration alongside 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 rewrite 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.That removes 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 beupdated in the same language and build system as the analysis code. That reduces
version skew and makes bugs easier to reproduce with Dune/expect tests.
What's here
lsp/package split intolsp/bin(entrypoint) andlsp/src(library), with its own opam file (rescript-language-server.opam). Depends onlsp(>= 1.22.0),eio/eio_main,ppx_deriving_yojson,ppx_expectand the in-treeanalysislibrary.Status / what's not here yet
The main objective is to first maintain resource 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 be implemented for now.textDocument/hover- client requesttextDocument/publishDiagnostics- server notification{start: {line: 0: character: 0}, end: {line: 0, character: 6}}let a=1. Protocol position is zero based so, length - 1.Analysisfor syntax errors and ignore them in the compiler log parser.TextDocumentDidChangeand provide instant feedback..compiler.log- server featurecompiler_log.ml. Seetests/build_testsfor more examples..sourcedirs.json- server feature.sourcedirs.jsonis always generated withbuild_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.rescript.jsonin workspace?rescript.json. For example,suffixandpackage-specsare needed to create the code actionOpen compiled js file. Changes independenciesimpact various functionalities because they modify the state inAnalysis.Shared_types.state.client/registerCapability- server requestworkspace/didChangeWatchedFilesis commonly registered dynamically. The server does not know all file-watch patterns during the staticinitializeresponse. In this LSP, the watcher list depends on project state, especially.sourcedirs.json, which tells us where each ReScript 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. When the client sees one change, it sendsworkspace/didChangeWatchedFiles, and the 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 implemented for now.textDocument/typeDefinition- client requesttextDocument/implementation- client request - It will not be implemented for now.textDocument/references- client requesttextDocument/documentHighlight- client request - It will not be implemented for now.textDocument/documentSymbol- client request"document_symbols": "on". When enabled, tree-sitter is not used for document symbols.workspace/symbol- client request - It will not be implemented for now.textDocument/codeAction- client requestrescript/openCompiled)window/showDocumentrescript/createInterface)rescript/switchImplementationInterface)window/showDocumentrequestcodeAction/resolve- client request - It will not be implemented for now.textDocument/codeLens- client requestworkspace/codeLens/refresh- server requestcodeLens/resolve- client request - It will not be implemented for now.textDocument/inlayHint- client requestinlayHint/resolve- client request - It will not be implemented for now.textDocument/semanticTokens/full- client requesttextDocument/semanticTokens/full/delta- client request - It will not be implemented for now.textDocument/semanticTokens/range- client request - It will not be implemented for now.textDocument/rename- client requesttextDocument/prepareRename- client requesttextDocument/formatting- client requesttextDocument/rangeFormatting- client request - It will not be implemented for now.textDocument/onTypeFormatting- client request - It will not be implemented for now.textDocument/foldingRange- client request - It will not be implemented for now.textDocument/selectionRange- client request - It will not be implemented for now.textDocument/documentLink- client request - It will not be implemented for now.documentLink/resolve- client request - It will not be implemented for now.textDocument/documentColor- client request - It will not be implemented for now.textDocument/linkedEditingRange- client request - It will not be implemented 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).rescript/openCompiled: Trigged by a code actionwindow/showDocumentrescript/createInterface: Trigged by a code actionwindow/showDocumentrequest.rescript/switchImplementationInterface: Trigged by a code actionwindow/showDocumentrequestwindow/showDocumentrequest. LSP: support windows/showDocument zed-industries/zed#58099.textDocument/openCompiled- client requesttextDocument/createInterface- client requesttextDocument/switchImplementationInterface: Currently it's done on the client side, but it could be a command trigged by a code action.rescript/startBuild- client request - How the build integration will be done. See last pointrescript/stopBuild?rescript/compilationFinished- server notification - Send from the server to client when compilation is finished.compiler.logchanges. Only VSCode use this notification.$/progressserver notification?rescript/compilationStatus- server notification - Send compilation status from the server to client.$/progressserver notification?rescript build, rather than the LSP starting or managing builds itself.rescript watchprocess, it's much harder to compile in memory?Tests
tests/lsp_tests/adds a dune-driven integration test (test.ml) that boots the server against a real ReScript workspace (basic-workspace/), initializes the LSP session, sends theinitializednotification, opens source documents, and snapshots responses for representative fixtures.The LSP test target now also runs
dune runtest, covering inline tests such as.sourcedirs.jsonbuild-root,.compiler.logparsing used by diagnostics and file-watcher setup.Breaking Changes
initializationOptions. This server ignore settings comming frominitializationOptions. See Spec should discourage abuse of initializationOptions and didChangeConfiguration microsoft/language-server-protocol#567 (comment). We useworkspace/configuration.Server settings
Proposed interface. Some notes:
supportMarkdownLinksis not a setting. It's a great feature, but some clients don't have good support; Neovim and Zed is an example. Therefore, I'm promoting it to a setting, so VSCode users can enable/disable.signatureHelp.enable. It's a basic feature on many servers.Release and transition plan
Some considerations
@rescript/language-serveror another) so the user can install it as a development dependency or globally.Release Proposal
lspbranch@rescript/language-server2.0.0-dev-SHA-.0with tagdev. The current version is1.72.0${{ startsWith(github.event.head_commit.message, 'publish language-server') && (github.ref == 'refs/heads/lsp') }})lsp/src/version.mlandpackage.jsonversion.mason.nvimcan install the server usingMasonInstall rescript-language-server@dev --forcelspuntil we get a stable language server. When we have a stable version we mergelspintomaster.Some points I have doubts about.
@rescript/language-serverwith tagnextin vscode-repo. I think it can be confusing? No?Neovim setup
This section descrive how Neovim user the experimantal server. The new server require some changes on setup of lsp. Use
root_dirhandler function instead ofroot_markers.Other topics related
Refactor analysis to use on server side
yojsonandlsplibrary for analysis library Use Yojson and lsp types for analysis JSON output #8436Shared_types.stateanalysis refactor: remove global stateShared_types.state#8465Relationship to #8243
#8243 collapses the build watcher and LSP into a single Rust process in rewatch, shelling out to
rescript-editor-analysis.exeover stdin. This PR keeps the LSP on the OCaml side and uses theanalysislibrary directly. Useful as a comparison point for the architecture discussion.