MemQL in VS Code (offline language server)
The MemQL VS Code extension gives .memql authors first-class editing --
syntax highlighting, live diagnostics, context-aware completion, hover, and
signature help -- powered by the same MemQL Sense brain the Cockpit
Editor uses. It works fully offline against local
files: no running cluster, no auth. Open a folder of .memql files and
iterate on the syntax itself.
Sense stays the single language-intelligence component serving both the Cockpit and VS Code (see Sense & the DSL Spec). This extension adds a new delivery mechanism -- an offline language server -- on top of the existing Sense package; it forks no brain and changes no wire contract.
Architecture
VS Code (editors/vscode, TypeScript) - vscode-languageclient -- spawns --> memql-lsp (stdio) - memql.tmLanguage.json (baseline offline highlighting, generated) - language-configuration.json (comments / brackets / brace expansion + indentation via onEnterRules + indentationRules -- no longer reliant on the built-in bracket heuristic, and firing even when the `{` carries trailing text; VS Code gates onEnterRules at editor.autoIndent "advanced"+ and indentationRules at "full", the default) | LSP over stdio (JSON-RPC) v cmd/memql-lsp (Go binary) - LSP front end (tliron/glsp) - workspace loader: BuildOfflineSense(os.DirFS(root)) -- DB-free - per request: buffer text -> sense.{Tokenize,Diagnose,Complete,Hover,SignatureHelp} | in-process Go calls (no gRPC, no network) v component/memql/sense (the UNCHANGED brain) projects vocabulary from component/language/dslspec + .../annotationscmd/memql-lspspeaks LSP over stdio viatliron/glsp. Atinitializeit builds an offline Sense service from the workspace withmemql.BuildOfflineSense-- the DB-free construction the boot-time validation tier already uses (no database, no network). It advertises incremental text sync, push diagnostics, semantic tokens, completion, hover, and signature help.- Positions. Sense is 1-based (line and column-in-runes); LSP is 0-based
(line and character-in-UTF-16-code-units). The server converts on every
request (
cmd/memql-lsp/internal/position); the two diverge only on supplementary-plane runes (surrogate pairs). - Registry refresh. On save / watched-file change the server rebuilds Sense (debounced) and atomically swaps it, so a concept or shape added in one file becomes visible to completion/hover in the others.
- The extension (
editors/vscode) is a thinvscode-languageclient. It resolves the server binary in order: thememql.lsp.serverPathuser setting, then a bundledbin/<platform>-<arch>/memql-lsp, thenmemql-lsponPATH.serverPathis machine/user-scoped only, so an untrusted workspace cannot redirect it.
Baseline grammar (generated)
editors/vscode/syntaxes/memql.tmLanguage.json colors a file the instant it
opens -- before the server attaches, and in diffs / on GitHub. It is
generated from dslspec (the same source of truth Sense projects from),
never hand-written, so it cannot drift from the language:
make vscode-grammar # memql-lsp gen-grammar editors/vscode/syntaxes/memql.tmLanguage.jsonA drift test fails the build if the checked-in grammar falls behind dslspec
or the GrammarVersion. Regenerate on every GrammarVersion bump. Semantic
tokens from the server then refine the baseline coloring.
Setup and development
# Build the server binary.make memql-lsp # -> bin/memql-lsp # Develop the extension.cd editors/vscodenpm installnpm run compile# Press F5 (Extension Development Host), set memql.lsp.serverPath to the built# binary, and open a folder of .memql files.Install / update locally
The language intelligence lives in the bundled memql-lsp binary, so refreshing
your editor after a server or extension change is a build + reinstall + reload
loop. One command does the first two:
make vscode-install # build a fresh .vsix and (re)install it into VS Codemake vscode-install EDITOR_CMD=cursor # ... into Cursor / code-insiders / codiumThen run Developer: Reload Window so the new server process spawns.
scripts/vscode/install.sh wraps package.sh (the build) + code --install-extension --force (the reinstall, which overwrites the currently
installed build); --no-build reinstalls the last-built .vsix, --help lists
the flags. Re-run it whenever you change the server or extension -- there is no
marketplace auto-update for a locally-built extension, so a stale editor is
always a locally-installed VSIX, never a repo artifact.
Packaging
make vscode-package # build the darwin-arm64 binary, compile the client, vsce package -> .vsixThe offline LSP embeds the engine, so the binary is bundled per platform;
darwin-arm64 (standardized dev hardware) is built first. The vscode-extension
CI lane runs every drift guard under cmd/memql-lsp -- the grammar against
dslspec, language-configuration.json, and package.json's engines.vscode
/ engines.node floors -- then runs this packaging flow. It gates on the
vscode bucket, and because go-checks gates on the go bucket instead, an
editors/vscode-only change (the shape every dependabot bump to the extension
takes) skips go-checks entirely and this lane is the only place those guards
fire (memql#2792). Release targets the VS Code Marketplace and OpenVSX; version
the extension in lockstep with GrammarVersion.
Nothing bundled is tracked in git: editors/vscode/bin/ and *.vsix are
ignored (editors/vscode/.gitignore), so the platform binary is cross-built
fresh at package time. A stale extension on a developer machine is therefore
a locally-installed VSIX, not a stale artifact in the repository -- reinstall
from a fresh make vscode-package to pick up server changes.
0.2.0 -- the editor-intelligence epic (#2600)
Packaged against GrammarVersion 2026.07-null-coalescing-operator, this
release carries the epic end to end:
@offers only the enclosing construct's legal annotations, and body completion is scoped to that construct's blocks and verbs (#2626, #2627).- Member completion after a dot:
actor.,event.,args.,payload.(#2624), driven by the canonical actor envelope (#2623). - Block-specific completion inside
args/filter/insert/update/shape, teaching the post-grammar-epoch short forms (#2628). - Real snippet completions -- body blocks and construct skeletons (#2629).
- Edit-time errors mirroring the engine's load rules:
actor-undeclared(#2622) andactor-unknown-property(#2625).
0.3.0 -- the cross-reference resolution epic (#2728)
This release teaches Sense to RESOLVE cross-references, not just complete them,
backed by a workspace symbol graph built from the dslimports tree (#2729):
- Import diagnostics:
use fylo.concept.{ oder }now flags the wrong kind segment and the undeclared id (#2730), and amutate/query/shape/seed <Concept>bound to a concept that exists nowhere is an error (#2731) -- both conservative, so a legitimate external/global reference never squiggles. - Segment-aware
use-line completion: typingfylo.offers the module kinds, and the brace list offers that module's importable ids, instead of dumping the whole symbol table (#2732). - Kind-filtered invocation completion: in a behavioral body,
query <name>offers only queries,logic <name>only logic, etc. (#2733). - A syntax<->Sense parity gate keeps these tables in step with the grammar (#2734).
Updating: the intelligence lives in the bundled memql-lsp binary, so a new
release ships only when you rebuild it. Run make vscode-package, reinstall the
.vsix (code --install-extension editors/vscode/memql-0.3.0.vsix --force), and
reload the window -- a stale editor is a locally-installed VSIX, never a repo
artifact.
Snippet completions
The extension advertises snippetSupport (vscode-languageclient
default), and the server emits real snippets (#2629): body-block
snippets that open a block with the cursor inside, and construct
skeletons with tabstops at the names you fill. Items that are not
snippets declare InsertTextFormat=PlainText explicitly, so nothing
inserts tabstop syntax literally.