28. Hover text is compiled from the specification, not read at runtime
- Status: Accepted
- Date: 2026-09-04
Context
textDocument/hover needs somewhere to pull explanatory text from for a
builtin function (print) or a built-in method (str.trim). Korrin has one:
docs/spec/08-builtins.md. Its free functions (§8.1) are each a
### \name(sig)`` heading followed by prose and an example; its methods
(§8.2-8.4) are markdown tables, one row per method, no heading at all — two
different shapes in one file, both needing a parser.
The question is where that file is read from when korrin-lsp actually runs.
The binary's install location has no fixed relationship to a checkout of the
Korrin repository: it might be on $PATH after cargo install, sitting
wherever a VS Code extension chose to bundle it, or run straight out of
target/debug/ during development. A runtime path read (docs/spec/ relative
to something) means solving "relative to what" for every one of those cases,
and shipping the markdown file as a separate asset alongside the binary.
Decision
hover.rs embeds docs/spec/08-builtins.md at compile time with
include_str!, and parses it once, lazily, into two lookup tables — one for
free functions, one for methods (a method name maps to every type that has
one, since Korrin has no static types to disambiguate by receiver: hovering
.len() shows str, list, and map together rather than guessing).
No new dependency for the parsing itself: the heading and table-row structure
is mechanical enough (### \...`for a heading, `` |...` `` for a table
data row) that splitting on those markers is simpler than reaching for a
markdown-parsing crate for two shapes this specific.
Every documented name must be a real one, checked automatically:
korrin-lsp's own test suite compares the parsed function and method names
against korrin::builtins::names() and korrin::interpreter::methods::names()
— the same interpreter functions crates/korrin/tests/builtins.rs already
uses to hold the spec accountable to the code. This is a second copy of that
check because korrin-lsp now depends on the doc's structure being
accurate, not just its prose, and a spec/code drift that the existing test
would catch should be caught here too, before it silently produces wrong or
missing hover text.
Consequences
korrin-lspis fully self-contained: copy the binary anywhere and hover works, with no path discovery, no bundled sidecar file, no "cannot find docs/spec" failure mode to handle.- The spec file's prose is now also product copy shown directly in an editor, not only documentation read on the website. It was already held to a high bar by ADR 0002; this raises the cost of a sloppy edit slightly further, which is the right direction.
- Changing
docs/spec/08-builtins.md's structure (adding a new subsection shape, say) means updatinghover.rs's parser in the same change, or hover text silently goes missing for whatever no longer parses. The anti-drift test catches exactly this — a name in the interpreter with no matching parsed entry fails the build — but only for missing/extra names, not for a correctly-named entry whose body parsed wrong. korrin-lspmust be rebuilt for hover text to reflect a spec edit; a doc change alone (with no crate rebuild) has no effect on a running server, since the file is compiled in rather than watched.
Alternatives considered
- Read
docs/spec/08-builtins.mdat runtime, from a path discovered relative to the binary or a workspace root. Solves nothing thatinclude_str!doesn't already solve for free, and adds real path-discovery logic (search upward for adocs/directory? require an environment variable? fail gracefully?) purely to reproduce compile-time embedding by hand. - Duplicate the hover text as Rust doc comments or a separate data file maintained independently of the spec. Two sources of truth for the same information, which is exactly the kind of drift ADR 0002's documentation discipline exists to prevent elsewhere in this project.
- A real markdown-parsing dependency, for correctness beyond the two mechanical shapes this file actually uses. Not justified yet — revisit if the spec's structure grows more varied than headings and tables.