Language Server (onion-lsp)¶
The Onion Language Server provides IDE features for .on files. It is used
by the VSCode extension (vscode-onion/) and can be integrated with any
editor that supports the Language Server Protocol.
Starting the server¶
The server communicates over stdin/stdout using LSP.
Supported features¶
| Feature | Description |
|---|---|
| Syntax diagnostics | Compilation errors reported as you type |
| Hover | Documentation for keywords, built-in types, and user-defined symbols |
| Go to definition | Jump to the definition of a user-defined symbol |
| Code completion | Keywords, built-ins, modules, and user-defined symbols |
| Document symbols | Outline view of classes, interfaces, methods, enums, records, and fields |
| Workspace symbols | Search symbols across all open documents |
| Signature help | Method signatures while typing a call |
| Rename symbol | Rename occurrences of the symbol at the cursor in the current file |
| Formatting | Normalises spacing, sharing its implementation with onion fmt |
| Semantic tokens | Colours each identifier by what the document declares it to be |
What rename does, and does not, do¶
Rename rewrites every code occurrence of the identifier in the open document. It skips
comments and string literals — renaming count no longer rewrites "count of items" or
// count starts at zero — while a #{ … } interpolation counts as code, so the count
in "n=#{count}" is renamed with the rest. Renaming from a position inside a comment or a
literal is refused: the word there is not a reference to anything.
It is not scope-aware and not cross-file. Two unrelated locals that happen to share a name are renamed together, a local and a field of the same name are renamed together, and references in other files are not touched at all. Scope-aware rename needs the typed AST with real source ranges, and the parser currently produces spans only a token wide; that is the work this depends on. Until then, review the edits your editor previews before applying them.
Semantic tokens¶
This is the colouring a TextMate grammar cannot produce. A grammar decides what a word is
from the shape of the line around it, so Greeter, greet and count all look alike — they
are words. The server classifies each identifier by what the document actually declares it
to be: class, interface, enum, record, method, field, or local.
It emits nothing it is unsure of. An identifier the document does not declare produces no
token at all, and the editor falls back to the TextMate grammar for it. Semantic tokens
override the grammar, so a guess would replace a right answer with a wrong one. Soft keywords
are left alone for the same reason: conforms reaches the server as an identifier, because
only the parser's lookahead decides whether it is a keyword there — colouring it from the
lexer alone would light up a method named conforms.
Keyword and operator kinds are read from the generated parser's own token table rather than
listed in the server, so a keyword added to the grammar is classified without anyone
remembering to update it. That is the duplication that let the TextMate grammar rot to 70%
coverage; here there is nothing to keep in step. Onion's primitive types are the capitalised
entries in that same table, which is how Int is coloured as a type rather than a keyword.
Your theme has to opt in. In VS Code, semantic highlighting is on by default for themes that
declare support; "editor.semanticHighlighting.enabled": true forces it on for any theme.
Formatting¶
The formatting provider calls the same code as onion fmt, so an editor and a CI check can
never disagree about what formatted means — a difference there shows up as a file that
changes every time it crosses between the two.
It tightens punctuation and converts leading tabs to spaces. It does not reindent, does not
move a line break, and does not collapse runs of spaces used for alignment; the reasoning
is in the onion fmt section of the project CLI page.
The result is one edit replacing the whole document. A minimal diff would preserve editor decorations better, but computing one from two strings risks producing an edit that does not reconstruct the formatted text, and a formatter that corrupts a buffer on save is worse than one that scrolls it. Before any edit is offered the formatted text is re-lexed and compared token for token against the original; if they differ, no edit is offered at all.
Symbol support¶
The language server indexes the following symbols from open documents:
- Classes, interfaces, enums, and records
- Methods (
def) - Class-level fields (
val/var) - Method arguments and local variables
Symbol information is used for completion, hover, go-to-definition, and both document and workspace symbol requests.
VSCode integration¶
Install the vscode-onion extension. It will automatically start
onion-lsp when you open an .on file and connect it to the editor.