CodeMirror 6 language support and LSP server for JSONata, powered by gnata's parser.
Two delivery modes from the same codebase:
- Browser — TinyGo WASM module (380 KB, 145 KB gzipped) for CodeMirror diagnostics and autocomplete
- Server — Native Go LSP server (stdio JSON-RPC) for VS Code, Neovim, etc.
import { EditorView, basicSetup } from "codemirror"
import { jsonataFull, initWasm } from "@gnata-sqlite/codemirror"
// Load the WASM module (380 KB, 145 KB gzipped).
await initWasm("/gnata-lsp.wasm", "/lsp-wasm_exec.js")
new EditorView({
extensions: [basicSetup, jsonataFull({ schema: '{"fields":{"id":{},"name":{}}}' })],
parent: document.getElementById("editor")!,
})# Build the server binary.
go build -o gnata-lsp ./editor/VS Code (settings.json):
{
"jsonata.lsp.serverPath": "/path/to/gnata-lsp"
}Neovim (lspconfig):
require("lspconfig.configs").jsonata = {
default_config = {
cmd = { "/path/to/gnata-lsp" },
filetypes = { "jsonata" },
root_dir = function() return vim.loop.cwd() end,
},
}
require("lspconfig").jsonata.setup({}) +---------------------------+
| CodeMirror Editor |
+---------------------------+
| Lezer Grammar | WASM |
| (sync, every | (async, |
| keystroke) | 380 KB) |
| | |
| Highlighting | Diags |
| Brackets | Compl. |
| Folding | AST |
+---------------------------+
Same Go code ──> TinyGo WASM (browser)
(editor/*.go) go build (native LSP server)
The Go files in editor/ are shared between both targets via build tags:
| File | Purpose | Build constraint |
|---|---|---|
main_wasm.go |
syscall/js entry point | //go:build js && wasm |
main_lsp.go |
stdio JSON-RPC server | //go:build !js |
diagnostics.go |
Parse errors → diagnostics | (none — shared) |
completions.go |
Context-aware autocomplete | (none — shared) |
funcinfo.go |
Built-in function catalog | (none — shared) |
schema.go |
Schema parser for field completions | (none — shared) |
marshal.go |
Reflect-free AST JSON serializer | (none — shared) |
Works instantly, no WASM required. Highlights:
- Field names, variables (
$name), strings, numbers, regex - Keywords:
function,and,or,in,true,false,null - All operators: arithmetic, comparison, chain (
~>), range (..), etc. - Block comments (
/* ... */) - Bracket matching, auto-close, code folding
Real-time parse error detection using gnata's actual Pratt parser. Returns structured errors with:
- Error code (S0201, S0202, etc.) matching the JSONata specification
- Exact byte position in source
- Human-readable message
Context-aware completions:
| Context | What's suggested |
|---|---|
After $ |
Built-in function names (70+ functions with signatures) |
After . |
Schema fields at the resolved path depth |
| Start of expression | Top-level schema fields + functions + keywords |
| After operators | Fields + functions + keywords |
The schema describes your document structure for field autocompletion. Pass it as JSON:
{
"fields": {
"Account": {
"type": "object",
"fields": {
"Name": { "type": "string" },
"Email": { "type": "string" },
"Order": {
"type": "array",
"fields": {
"OrderID": { "type": "string" },
"Product": { "type": "object" },
"Price": { "type": "number" }
}
}
}
}
}
}For flat SQLite rows, this simplifies to:
{
"fields": {
"id": { "type": "number" },
"name": { "type": "string" },
"data": { "type": "string" }
}
}Pass the schema when the LSP client initializes:
{
"initializationOptions": {
"schema": "{\"fields\":{\"id\":{\"type\":\"number\"}}}"
}
}Syntax highlighting only. No WASM required.
import { jsonata } from "@gnata-sqlite/codemirror"
// extensions: [basicSetup, jsonata()]Full support: syntax highlighting + WASM linter + autocomplete.
import { jsonataFull } from "@gnata-sqlite/codemirror"
// extensions: [basicSetup, jsonataFull({ schema: '...' })]Load the TinyGo WASM module. Call once before creating editors.
Standalone linter extension. Use with jsonata() for manual composition.
Standalone autocompletion source.
The WASM module sets three functions on the global object:
| Function | Signature | Returns |
|---|---|---|
_gnataParse |
(expr: string) |
JSON AST string | Error |
_gnataDiagnostics |
(expr: string) |
JSON diagnostics array |
_gnataCompletions |
(expr, pos, schema) |
JSON completions array |
_gnataHover |
(expr, pos, schema?) |
JSON hover info | empty string |
The native LSP server supports:
| Method | Description |
|---|---|
initialize |
Returns server capabilities |
textDocument/didOpen |
Publishes diagnostics for opened documents |
textDocument/didChange |
Publishes diagnostics on every change |
textDocument/completion |
Returns context-aware completions |
shutdown / exit |
Clean shutdown |
Diagnostics are pushed as textDocument/publishDiagnostics notifications.
tinygo build \
-o gnata-lsp.wasm -no-debug \
-gc=conservative \
-target wasm ./editor/
# Copy TinyGo's WASM support file.
cp "$(tinygo env TINYGOROOT)/targets/wasm_exec.js" lsp-wasm_exec.jsOutput: gnata-lsp.wasm (380 KB raw, 145 KB gzipped)
go build -o gnata-lsp ./editor/cd editor/codemirror
npm install
npm run buildimport { useEffect, useRef } from "react"
import { EditorView, basicSetup } from "codemirror"
import { jsonataFull, initWasm } from "@gnata-sqlite/codemirror"
export function JsonataEditor({ schema }: { schema: string }) {
const ref = useRef<HTMLDivElement>(null)
useEffect(() => {
initWasm("/gnata-lsp.wasm", "/lsp-wasm_exec.js")
const view = new EditorView({
extensions: [basicSetup, jsonataFull({ schema })],
parent: ref.current!,
})
return () => view.destroy()
}, [])
return <div ref={ref} />
}<div id="editor"></div>
<script type="module">
import { EditorView, basicSetup } from "codemirror"
import { jsonata, initWasm, jsonataLint } from "@gnata-sqlite/codemirror"
// Syntax highlighting works immediately.
const view = new EditorView({
extensions: [basicSetup, jsonata()],
parent: document.getElementById("editor"),
})
// Add linting once WASM loads.
initWasm("./gnata-lsp.wasm", "./lsp-wasm_exec.js")
</script>editor/
README.md # This file
main_wasm.go # TinyGo WASM entry point (browser)
main_lsp.go # Native LSP server (VS Code, Neovim)
diagnostics.go # Parse errors → diagnostic format
completions.go # Context-aware completion engine
funcinfo.go # Built-in function catalog (70+ functions)
schema.go # Schema JSON parser (no encoding/json)
marshal.go # Reflect-free AST → JSON serializer
codemirror/ # npm package (@gnata-sqlite/codemirror)
src/
jsonata.grammar # Lezer grammar for syntax highlighting
highlight.js # CodeMirror style tag mappings
index.ts # Language support + WASM bridge
package.json
rollup.config.mjs
tsconfig.json