LSP for SRF (AI generated)
Find a file
2026-09-01 14:48:22 -07:00
src hover support, better diagnostics 2026-09-01 14:48:22 -07:00
.gitignore upgrade to latest srf and zig 0.16 2026-09-01 13:40:36 -07:00
.mise.toml upgrade to latest srf and zig 0.16 2026-09-01 13:40:36 -07:00
.pre-commit-config.yaml first working version (still zig 0.15) 2026-09-01 13:31:17 -07:00
build.zig first working version (still zig 0.15) 2026-09-01 13:31:17 -07:00
build.zig.zon hover support, better diagnostics 2026-09-01 14:48:22 -07:00
LICENSE first working version (still zig 0.15) 2026-09-01 13:31:17 -07:00
README.md hover support, better diagnostics 2026-09-01 14:48:22 -07:00

srf-lsp

Language Server Protocol implementation for SRF (Simple Record Format), written in Zig.

Provides real-time parse error diagnostics using the SRF library's parser directly.

Features

  • Parse error diagnostics on open and edit, using the SRF library's parser directly: missing or duplicate #!srfv1, bad type hints, unparseable values, data after #!eof, and so on.
  • All errors from a parse are reported, not just the first.
  • Length-prefix checking for the cases srf does not cover. A numeric type hint declares an exact byte count. srf reports a wrong one in compact format, but in long format it keeps the declared bytes and discards the rest of the line without a word, and a count running past the end of the file fails with no location at all. Both are detected here and reported on the offending bytes.
  • Hover, covering every construct in the format:
    • #!srfv1 and each directive, explained.
    • #!expires, #!created and #!modified render their Unix timestamp as a UTC date, because nobody can read 1772589213.
    • Keys and values show the parsed interpretation: numbers, booleans, byte counts for strings, and for binary values the decoded content when it is text (decoding to 5 bytes: "hello") or a note when it is not.
    • Type hints are explained, including a numeric hint's length-prefix meaning.
  • Position encoding is negotiated during initialize (utf-8, utf-16 or utf-32), so columns line up even on lines containing multi-byte characters.
  • Full-document sync, which matches SRF's single-pass parser.
  • stdio transport.

Value interpretation defers to the srf library rather than reimplementing its rules, so hover and diagnostics cannot disagree about the same text.

Not implemented: completion, document symbols, formatting, goto definition, semantic tokens, and pull diagnostics (textDocument/diagnostic). Requests for them are answered with MethodNotFound rather than left unanswered, and none of them are advertised as server capabilities.

Requirements

Zig 0.16.0. Pinned in .mise.toml, along with the other tooling.

Setup

mise install

Build & Test

zig build
zig build test

The binary is at zig-out/bin/srf-lsp.

Install

The Neovim config below expects srf-lsp on PATH:

zig build && install -m755 zig-out/bin/srf-lsp ~/.local/bin/srf-lsp

Neovim Integration

Neovim 0.11 or newer. Add to your config:

vim.filetype.add({
  extension = {
    srf = "srf",
  },
})

vim.lsp.config("srf_lsp", {
  cmd = { "srf-lsp" },
  filetypes = { "srf" },
  root_markers = { ".git", "/" }, -- single file server
})

vim.lsp.enable("srf_lsp")

Check it attached with :checkhealth vim.lsp on a .srf buffer. Server logs go to stderr, which Neovim captures in :LspLog.

Combined Setup (with srf-tree-sitter)

For both syntax highlighting and error detection:

vim.filetype.add({
  extension = {
    srf = "srf",
  },
})

-- Diagnostics
vim.lsp.config("srf_lsp", {
  cmd = { "srf-lsp" },
  filetypes = { "srf" },
  root_markers = { ".git", "/" },
})
vim.lsp.enable("srf_lsp")

-- Syntax highlighting
vim.api.nvim_create_autocmd("User", {
  pattern = "TSUpdate",
  callback = function()
    require("nvim-treesitter.parsers").srf = {
      install_info = {
        url = "https://github.com/elerch/srf-tree-sitter",
        branch = "master",
        queries = "queries",
      },
    }
  end,
})

vim.api.nvim_create_autocmd("FileType", {
  pattern = "srf",
  callback = function(args)
    pcall(vim.treesitter.start, args.buf)
  end,
})

Then install the tree-sitter parser (:TSInstall srf no-ops if it is already present, so use :TSUpdate srf to pick up a newer grammar):

:TSUpdate srf