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.

LSP over stdio no VS Code dependency 22 tests, real socket GitHub →

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 trades

Inside 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.

HoverWhat you get
x@<xSort 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.
-':xFirst difference. Each-prior applied to subtract.
_n%2Integer halving. % divides (always float), _ floors. Neither means what it means in C.
wavgWeighted average — weights first. Getting the order backwards produces a plausible wrong number.
ajThe 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

1

Install the extension

From the marketplace, or build a VSIX yourself: npx vsce package.

2

Start an engine

amberd --home /path/to/amber --port 5010

amberd ships with amber-arrow.

3

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

SettingDefaultMeaning
amber.server.host127.0.0.1engine host
amber.server.port5010engine port
amber.server.autoStartfalsestart amberd when none is reachable
amber.server.commandamberdpath to the binary, for auto-start
amber.server.home""--home for the auto-started engine
amber.diagnostics.enabletruesyntax problems as you type
amber.diagnostics.semantictruealso flag names unknown to the engine
amber.diagnostics.delay300ms of quiet typing before recomputing
amber.completion.columnstrueask the engine for real column names
Auto-start is off by default, and probes the port first

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 --stdio
Neovim — init.lua
vim.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 } } },
})
Helix — languages.toml
[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"]
Emacs — eglot
(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/b folds.
  • \ 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.vsix

22 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.