Quirlv0.5.1
Tooling

Quirl language service

Canonical Quirl project documentation synced from docs/language-service.md.

quirl lsp is Quirl's deterministic editor service for Lua extension files and native Quirl scripts. .qrl is the canonical native extension; .quirl and .๐ŸŒ€ are accepted input aliases. It communicates over standard LSP Content-Length framing on stdin/stdout, so editors can launch the Quirl binary directly.

The server accepts messages up to 4 MiB. Framing headers are limited to 8 KiB and 64 fields, duplicate Content-Length fields are rejected, and truncated bodies fail without dispatch. It implements:

  • initialize, initialized, shutdown, and exit
  • textDocument/didOpen, didChange, and didClose with full-document sync
  • textDocument/completion, completionItem/resolve, hover, signatureHelp, and diagnostic
  • quirl/moduleDocs, which returns generated Markdown for the Lua host module and installed command catalog

Lua completion, hover, signatures, and module docs are derived from quirl_lua::HOST_API. Diagnostics use the restricted Lua compiler and linter, but never call the resulting function or evaluate document text. This means editing a file containing os.execute(...), an infinite loop, or plugin registration cannot cause the language server to perform that operation.

For native Quirl files, command and option intelligence comes from the same versioned Catalog used by the REPL and quirl complete. The Phase 2 service also reports deterministic structural diagnostics for mismatched delimiters, unterminated strings, and empty pipeline stages. It does not spawn commands or resolve ambient shell state.

Native completion preserves the catalog's fuzzy ranking and returns exact UTF-16 text edits for command paths, options, bundled short flags, and static argument values. A response retains at most 200 items and 256 KiB of encoded JSON; truncated responses set isIncomplete. Documentation and detail are resolved lazily only when the client advertises support for both properties. Other clients receive them in the initial response, using Markdown or plain text according to the client's declared formats.

The CLI composition root supplies the complete native analyzer to the LSP. Command blocks continue to use quirl-syntax; inline and explicit data bodies use quirl-data's bounded parser and retain its UTF-8 byte spans and quirl-data diagnostic source. The callback receives only the already-bounded document text and has no process, filesystem, adapter, or evaluator capability. This preserves ADR 0016: quirl-lsp does not depend on quirl-data.

Editor command

Configure an LSP client with this stdio command:

quirl lsp

Use lua as the language id for Lua files and quirl for native Quirl files, regardless of whether they use .qrl, .quirl, or .๐ŸŒ€. The server uses UTF-16 LSP positions and publishes a complete replacement diagnostic set after every open or full-text change.

The service retains at most 128 open documents and 16 MiB of aggregate document state. Each URI is limited to 8 KiB, each language identifier to 64 bytes, and each document body to 1 MiB. didChange accepts exactly one full-document replacement without range or rangeLength, matching the advertised full-sync mode. Opens and changes require an integer document version; changes must advance beyond the retained version, though gaps are allowed. Incremental, stale, duplicate-version, and unversioned changes leave the previous document intact. Duplicate didOpen notifications atomically replace the existing document; a rejected open or change preserves the prior text, version, and accounting. Close, shutdown, and exit release retained state. These are UTF-8 byte limits, and limit failures include configured and observed usage where it is safe to do so.

Neovim 0.11 and newer

The supported Neovim runtime in editors/neovim registers quirl as the filetype for .qrl, .quirl, and .๐ŸŒ€ files, then enables a native Neovim LSP configuration that launches quirl lsp. When the server advertises completion, a client-local on_attach callback enables Neovim's builtin completion with automatic triggering on Quirl's advertised trigger characters. It does not create a global LspAttach autocmd or depend on nvim-lspconfig or a completion plugin.

First install quirl and confirm that Neovim can find the same executable as your shell:

quirl --version
nvim --clean --headless '+lua print(vim.fn.exepath("quirl"))' +qa

To use the integration from a source checkout, expose the runtime directory as a native Neovim package. Replace /path/to/quirl with the absolute checkout path; replace the data directory if :echo stdpath('data') reports a different location:

mkdir -p "${XDG_DATA_HOME:-$HOME/.local/share}/nvim/site/pack/quirl/start"
ln -s /path/to/quirl/editors/neovim \
  "${XDG_DATA_HOME:-$HOME/.local/share}/nvim/site/pack/quirl/start/quirl"

A plugin manager that supports local plugins can add /path/to/quirl/editors/neovim to Neovim's runtimepath instead. The package enables the server automatically; no vim.lsp.config entry is required in init.lua.

Open any of the supported native file extensions and verify the integration:

:set filetype?
:checkhealth vim.lsp
:lua =vim.lsp.get_clients({ bufnr = 0 })

The filetype should be quirl, and the buffer should have a client named quirl. Neovim publishes diagnostics automatically and requests builtin completion after ., -, and space, the trigger characters advertised by the server. When the server returns candidates, use CTRL-Y to accept an offered item. Use CTRL-X CTRL-O in Insert mode to request completion manually, K for hover, and CTRL-S in Insert mode for signature help. These are Neovim's builtin LSP behaviors and may be replaced by the user's configuration.

To disable the packaged automatic completion or replace its attachment behavior, define a higher-precedence callback in init.lua before the package loads:

vim.lsp.config("quirl", {
  on_attach = function() end,
})

The runtime lsp/quirl.lua file is the low-precedence default, so a custom on_attach callback in the user's named quirl configuration replaces the packaged callback without changing filetype detection or server startup.

The current Quirl server provides completion, hover, signature help, and diagnostics. The Neovim package does not yet add Quirl syntax highlighting, semantic tokens, formatting, navigation, rename, code actions, or inlay hints. If the client does not attach, check that Neovim is version 0.11 or newer and that :echo executable('quirl') returns 1; then inspect :checkhealth vim.lsp and :messages for the process startup error.

Custom module documentation request

Clients and documentation tools can request the exact generated module view:

{"jsonrpc":"2.0","id":1,"method":"quirl/moduleDocs","params":{}}

The response is a Markdown marked string. Because it is generated at request time from HOST_API and the loaded catalog, editor help does not drift from the runtime and CLI command metadata.

On this page