vscode-amber
Language support for Amber: syntax highlighting, a live language server, qSQL-aware completion, hover documentation for the idioms, and diagnostics that never evaluate your code.
Completion that knows what a table is
select vwap:wavg[sz;px] by sym from trades where p▮
└─ px · column of trades
sz · column of tradesInside a query, the clause decides the vocabulary. After from you get
table names and nothing else. Everywhere else in the query you get the real columns of whichever table
the from clause named — asked for, live, from the connected engine. That is the difference
between a list of four column names and a list of every global in the process.
Hover that explains the idioms
The hard part of a K-family language is not the concepts, it is that the notation is
dense and unsearchable. You cannot paste x@<x into a search engine,
and reading it aloud does not help.
| Hover | What you get |
|---|---|
x@<x | Sort x ascending. <x is the ascending grade — the indices that would put x in order — and x@ applies them. y@<x sorts y by x. |
-':x | First difference. Each-prior applied to subtract. |
_n%2 | Integer halving. % divides (always float), _ floors. Neither means what it means in C. |
wavg | Weighted average — weights first. Getting the order backwards produces a plausible wrong number. |
aj | The as-of join: for each trade, the quote standing when it printed. |
Symbols are matched longest-first, so hovering the @
in x@<x explains the phrase rather than the character. Documentation for a single
operator is exactly the unhelpful kind that sends you back to a search engine.
Diagnostics that never run your code
my_name: 42
~~~~~~~ '_' is a verb in Amber (drop, and floor), so 'my_name' parses as
'my _ name' rather than as one name. Use a dotted name (my.name)
or camel case.An editor that evaluated the line you were halfway through typing would be unusable and
occasionally destructive. So syntax problems come from a local lexer that understands K's two
context-sensitive rules, and the engine is consulted only through \parse — which runs the
real parser via \ast and never touches the compiler or the evaluator.
With an engine connected you also get undefined-name diagnostics, scoped to names that are neither assigned anywhere in the file nor defined in the engine.
Evaluate from the editor
Ctrl/Cmd+Enter sends the selection (or the file) to the connected engine and prints the result in the Amber output channel. A failing line stops the run rather than executing the rest of a script against state the failed line was meant to set up.
Setup
Install the extension
From the marketplace, or build a VSIX yourself: npx vsce package.
Open a .k file
Without an engine you still get highlighting, syntax diagnostics, document symbols, vocabulary completion and every hover. You lose table names, column completion and live values. Nothing degrades into an error dialog — the normal state of an editor window is "opened to read a file", with nothing running, and that has to work.
Settings
| Setting | Default | Meaning |
|---|---|---|
amber.server.host | 127.0.0.1 | engine host |
amber.server.port | 5010 | engine port |
amber.server.autoStart | false | start amberd when none is reachable |
amber.server.command | amberd | path to the binary, for auto-start |
amber.server.home | "" | --home for the auto-started engine |
amber.diagnostics.enable | true | syntax problems as you type |
amber.diagnostics.semantic | true | also flag names unknown to the engine |
amber.diagnostics.delay | 300 | ms of quiet typing before recomputing |
amber.completion.columns | true | ask the engine for real column names |
An editor that starts a second engine beside the one you are already working in gives you two independent namespaces, and every completion it then offers is a claim about the wrong process.
Other editors
The language server has no VS Code dependency. It is a standalone daemon that speaks LSP on stdio, and everything above works in any editor that speaks LSP.
scripts/amber-language-server --stdiovim.lsp.start({
name = 'amber',
cmd = { '/path/to/vscode-amber/scripts/amber-language-server', '--stdio' },
root_dir = vim.fn.getcwd(),
settings = { amber = { server = { host = '127.0.0.1', port = 5010 } } },
})[language-server.amber]
command = "/path/to/vscode-amber/scripts/amber-language-server"
args = ["--stdio"]
[[language]]
name = "amber"
scope = "source.amber"
file-types = ["k", "amber"]
language-servers = ["amber"](add-to-list 'eglot-server-programs
'(amber-mode . ("amber-language-server" "--stdio")))Features implemented in the extension client would be features only VS Code users get, so the client is deliberately thin: it starts the server, registers four commands, and gets out of the way.
The grammar
A TextMate grammar cannot in general decide K's lexical ambiguities, so this one resolves the two that matter by anchoring rather than guessing:
/is the over adverb after a value (+/x), and a comment at the start of a line or when surrounded by whitespace (a / b).a/bfolds.\is the scan adverb mid-expression and a REPL command at the start of a line.
Where a case is genuinely ambiguous it stays an operator: colouring code as a comment is a far worse
failure than the reverse. x, y and z get their own scope, because
assigning to y inside a lambda silently makes it dyadic — one of the two mistakes everyone
makes exactly once. (The other is _ in a name, which the linter catches.)
Build and test
npm install
npm run compile
node test/run.js # with an engine, if amberd is on the path
node test/run.js --no-engine # force the engine-absent path
npx vsce package # -> vscode-amber-1.9.6.vsix22 tests, driven over real LSP framing on stdio against a real
amberd on a real socket. Nothing is mocked: everything interesting here lives in
the seams — the LSP header framing, the amberd reply framing, the configuration round-trip, and the
requirement that it all keep working when the engine is gone. The suite runs twice, with and without
an engine, because "degrades gracefully" is a claim that has to be tested, not asserted.