Skip to content

Repository files navigation

emet.nvim

A tree-sitter grammar for the Emet configuration language, together with the Neovim plugin that registers it with nvim-treesitter.

This one repository is both:

  • the grammar (grammar.js, generated src/, queries/, corpus tests), and
  • the nvim plugin (plugin/emet.lua, queries/emet/), so the repo root is a valid Neovim runtimepath entry.

It covers Emet's Elm-like surface syntax: line comments, string literals with escapes and ${…} interpolation, integer/float numbers, lower/upper identifiers, dotted module names and qualified names (List.map, Limesurvey.Database.Config), keywords (let in if then else case of), the reserved glyph constructors (aptPackage systemdService file lineInFile scroll), the full operator table, lambdas, let … in, case … of, if … then … else, records and field access, lists, function application, type signatures, and type expressions including record types { r | field : a }.

Extracted from the golem monorepo (libs/tree-sitter-emet).

Install (LazyVim)

Add the plugin — one line — and let nvim-treesitter compile the parser:

{ "dull-ca/emet.nvim" }

Then, inside Neovim:

:TSInstall emet

The plugin (plugin/emet.lua) registers the emet parser (pointing install_info at this repository), maps the .emet file type, and ships the highlight queries under queries/emet/ on the runtimepath — so no manual query symlinks are needed.

:TSInstall emet compiles src/parser.c and the external scanner src/scanner.c, so a C compiler must be on PATH. Tree-sitter highlighting needs Neovim 0.9+; classic Vim has no tree-sitter support.

Layout handling

Emet is layout-sensitive (offside rule). This grammar does not reimplement the compiler's full layout algorithm — it only needs a robust, error-tolerant tree for highlighting. A tiny external scanner (src/scanner.c) emits two synthetic tokens:

  • a declaration boundary when a new line starts at column 0, which ends the previous top-level declaration; and
  • a line boundary inside case … of, which separates one arm from the next (Emet lays out case arms one per line).

Everything else relies on tree-sitter's error recovery, which is sufficient for highlighting.

Qualified names

A dot binds an upper-case segment to whatever sits on its left, in every position such a name may appear — module headers, imports, type annotations, expressions, and patterns:

module Limesurvey.Database exposing (Config(..), config)

import Limesurvey.Survey as Survey

describe : Limesurvey.Survey.Kind -> Str
describe kind =
  case kind of
    Survey.Poll -> Survey.label kind
    Survey.Quiz -> "quiz"

The segments have to touch. A.B is one name and A . B is two, which the grammar reports as an error — the compiler compares spans and rejects it too. A spaced dot before a lower-case name stays legal, because that is field access: shapes . area.

String interpolation

A string node is a sequence of string_content, escape_sequence, and interpolation nodes. An interpolation wraps a full Emet expression between interpolation_start (${) and interpolation_end (}) nodes, so the embedded expression is parsed and highlighted like any other code — including nested strings, applications, and parenthesised sub-expressions.

Queries

Two copies of the query files exist and must be kept in sync:

  • queries/*.scm — the canonical location (tree-sitter convention, listed in tree-sitter.json / package.json).
  • queries/emet/*.scm — a copy (not a symlink; portable across platforms) so that nvim-treesitter, which looks for queries/<lang>/ on the runtimepath, finds them when this repo root is on the runtimepath.

Edit queries/*.scm, then copy over queries/emet/*.scm.

  • queries/highlights.scm — maps nodes to standard capture names.
  • queries/injections.scm — treats -- comments as comment text.
  • queries/locals.scm — scopes and definitions for local variable resolution.

Developing

tree-sitter generate          # regenerate src/parser.c from grammar.js
tree-sitter test              # run test/corpus
tree-sitter parse FILE.emet   # inspect a parse tree

The TextMate grammar (for the golem docs site)

The golem documentation site (Astro + Starlight + Expressive Code / Shiki) highlights Emet code blocks with a TextMate grammar, not tree-sitter. That grammar is generated from this repository's grammar.js:

node scripts/generate-textmate.mjs [output.json]

With no argument it writes emet.tmLanguage.json at the repo root; pass a path to write elsewhere. The golem site consumes the committed generated JSON (vendored at sites/website/src/grammars/emet.tmLanguage.json) — it no longer runs the generator at build time. When the grammar changes, regenerate here and copy the resulting JSON into golem.

License

MIT — see LICENSE.

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages