Skip to content

feat: add MinML Language Server (LSP) with VSCode client integration#18

Open
phamelink wants to merge 7 commits into
mainfrom
minml-lsp-v2
Open

feat: add MinML Language Server (LSP) with VSCode client integration#18
phamelink wants to merge 7 commits into
mainfrom
minml-lsp-v2

Conversation

@phamelink
Copy link
Copy Markdown
Contributor

feat: add MinML Language Server (LSP) with VSCode client integration

Summary

This PR implements a full Language Server Protocol (LSP) server for MinML and wires it into the VSCode extension as an LSP client.

What's new:

  • A Go LSP server (go/markup/minml/lsp/) backed by glsp and the go-tree-sitter CGo bindings
  • Diagnostics — parse errors and missing nodes from the tree-sitter AST are surfaced as real-time error squiggles
  • Hover — hovering over a tag name shows its HTML description (sourced from a bundled HTMLElements map); hovering over attribute names shows the attribute name
  • Completions — tag name completions drawn from the full set of HTML elements; attribute completions fire when inside a {…} attribute block (including incomplete/ERROR parses where the closing } hasn't been typed yet)
  • A document store that keeps the most-recently-parsed tree-sitter Tree per open URI, updated on every textDocument/didOpen and textDocument/didChange notification
  • The VSCode extension now starts the minml-lsp binary as a child process and connects to it as an LSP client via stdio, with a configurable minml.lspPath setting and an opt-in minml.debug flag
  • Updated tree-sitter highlight queries and a new indents.scm for auto-indentation support
  • Makefile targets: gen-parser (regenerates the C parser from grammar.js) and build-lsp (compiles the Go LSP binary)

Design decisions

glsp over a hand-rolled protocol layerglsp provides typed handlers for every LSP 3.16 method, handles the JSON-RPC framing, and lets the server declare capabilities at Initialize time without boilerplate. It was the cleanest way to get a correct server fast.

Full document sync — the server advertises TextDocumentSyncKindFull and re-parses the entire document on every change. MinML files are small and tree-sitter parses are sub-millisecond, so incremental sync would add complexity for no measurable benefit.

LSP binary path resolution — the extension looks up the binary in order: user config (minml.lspPath) → extension directory (used by make vscode-live-preview which copies the binary next to the .vsix) → system PATH. This means it works both for end-users who install the packaged extension and for developers iterating locally.

Completions inside incomplete parses — when a user types { to start an attribute block but hasn't closed it yet, tree-sitter produces an ERROR node. The completion handler walks up the ancestor chain looking for either a valid attr_block or an ERROR node containing a literal { child, so attribute suggestions fire immediately even before the block is syntactically complete.

Diagnostics from tree-sitter ERROR/MISSING nodes — rather than writing a separate validator, parse errors are read directly from the tree-sitter CST. The server stops recursing into ERROR subtrees to avoid flooding the diagnostics list with cascading phantom errors from a single typo.

How to test

Prerequisites: Go ≥ 1.21, Node.js, tree-sitter CLI, a C toolchain (for the CGo tree-sitter bindings).

# 1. Generate the C parser from grammar.js
make gen-parser

# 2. Build the LSP binary and copy it next to the extension
make build-lsp

# 3. Open the VSCode extension in the Extension Development Host
cd dev/vscode/minml-preview
npm install
# Press F5 in VSCode (or use the "Run Extension" launch config)

Manual tests to run in the Extension Development Host:

Scenario Expected
Open a .m file with a typo (e.g. [div without ]) Red squiggle on the error range
Type [ then start a tag name Completion list of HTML elements appears
Type [div{ inside an element Attribute completions appear immediately
Hover over div in [div]…[/div] Tooltip shows the HTML div description
Fix the typo Squiggle disappears on the next keystroke
Set minml.lspPath to a custom binary path in settings Extension uses that binary instead

Copy link
Copy Markdown
Contributor

@AntoineBastide47 AntoineBastide47 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall nice looking PR, just some slight changes needed.

The ide I use for golang is GoLand from Jetbrains, and it tells me that the code has a lot of unused parameters and some slice declaration warnings (these aren't code errors, just flag that should be fixed for better go code).

Also when I try and build the LSP I get this error:

make build-lsp
go build -o minml-lsp ./go/markup/minml/cmd/lsp/
# github.com/dedis/matchertext/go/markup/minml/lsp
go/markup/minml/lsp/server.go:39:37: s.SemanticTokensFull undefined (type *Server has no field or method SemanticTokensFull)
go/markup/minml/lsp/server.go:58:21: undefined: tokenTypes
go/markup/minml/lsp/server.go:59:21: undefined: tokenModifiers
make: *** [build-lsp] Error 1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants