pmacs/docs/lsp-language-coverage-frami...

274 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# LSP language coverage: LaTeX (and the Haskell/OCaml question)
**Status: revision 3. Implemented at `d79afdc`; step zero discharged by observation, and its result corrected two things this document had wrong.**
*Recorded precisely: the user authorized dispatch after a summary of
revision 2's four corrections, rather than returning findings on the
document as they did for the other lanes. The §3 verification caveat is
therefore still live and binding — it is step zero, not a footnote.*
**Revision 2 corrects three facts revision 1 got wrong or stale, and
answers the question revision 1 named as most likely to make the entry
wrong in practice.** Haskell's server *is* installed; Slice 1 is
**smaller** than framed because the extension wiring already exists;
Q#LX3's deferral argument rests on a `COHERENCE.md` line the same
document contradicts twice; and Q#LX2 (the LaTeX root) now has a
proposal rather than a shrug.
**Revision 1 was untracked, on `main`, in one checkout.** Per the
handoff's own rule — work is portable only after it is committed and
pushed — it did not travel. That is fixed by this branch.
---
## 0. What prompted this
An audit of the host machine against `builtin/runtime/lsp.lua`. pmacs
configures LSP for fourteen languages — verified exactly, by extracting
the `pmacs.lsp.config.*` keys:
bash c cmake cpp cuda dockerfile go json lua
python rust toml yaml zig
**Lean is NOT among the gaps, and revision 1's first draft wrongly said
it was.** Lean 4 has `builtin/runtime/lean.lua`, `lean_abbrev.lua` and
`lean_input.lua` (all three present), an `arborium-lean` grammar, and
comment/typed-edit integration — Arc 8 Stages 14b, merged. The error
came from grepping `lsp.lua` alone, which is the wrong place to look
for a language that earned its own module.
## 1. The gap, re-measured
| Language | tree-sitter | LSP config | Server on this machine |
|---|---|---|---|
| LaTeX | ✅ grammar + `builtin/queries/latex/highlights.scm` | ❌ | **`texlab` 5.25.1 — installed** |
| Haskell | ❌ | ❌ | **`haskell-language-server` — INSTALLED** |
| OCaml | ❌ | ❌ | `ocaml`/`opam`/`dune` yes, `ocaml-lsp-server` **absent** |
**Correction: revision 1 said Haskell's server was missing.** Both
`haskell-language-server` and `haskell-language-server-wrapper` are on
this machine. That collapses revision 1's Slice 1 / Slice 2 split,
which rested on "Slice 2 needs servers installed first" — only OCaml
does now.
LaTeX remains the sharp case: the grammar work landed, so a `.tex`
buffer highlights correctly **and** offers no completion, no
diagnostics, no go-to-definition, while `texlab` sits on disk unused.
## 2. Ground truth — Slice 1 is smaller than revision 1 claimed
Revision 1 proposed "one `pmacs.lsp.config.latex` entry, **plus**
filetype mappings for `.tex`/`.latex`/`.sty`/`.cls`, matching the
grammar's existing extension set so highlighting and LSP agree on what
a LaTeX file is."
**The filetype mappings are redundant, and the rationale describes a
problem that cannot occur.** Three facts, read rather than assumed:
- **The grammar already carries exactly those extensions.**
`src/syntax.rs:1110-1112``name: "latex"`,
`extensions: &["tex", "latex", "sty", "cls"]`.
- **Grammar-extension detection sits AHEAD of the LSP filetype map.**
The merged `docs/latex-grammar-math-substrate-framing.md:166-171`
states the chain — *modeline → grammar extension → LSP filetype map →
filename map → shebang* — and concludes that adding those extensions
"**wires the whole chain with no Lua edit**".
- **The filetype map is explicitly a fallback.** `lsp.lua:267-270`:
"Every language with an LSP config now also ships a grammar, so this
is mainly the **LSP-only fallback** that keeps a language id stable if
a grammar is ever dropped, plus the seam for user-added mappings."
So a `.tex` buffer **already** resolves to language `latex`. They
cannot disagree, because the grammar's extension list *is* what drives
detection.
**Slice 1 is therefore one thing: the `pmacs.lsp.config.latex` entry**
(plus its root resolver, §3). Filetype-map entries may still be added
as the documented drop-a-grammar fallback, but that is belt-and-braces
and should be labelled as such rather than sold as making two systems
agree.
## 3. Q#LX2 — the LaTeX project root **(answered in rev 2)**
Revision 1 called this "the question most likely to make the entry
wrong in practice" and left it open. It is the difference between
texlab serving a multi-file thesis and serving isolated files, so it is
the whole value of the lane for the stated use case.
**The mechanism exists.** `pmacs.lsp.config.<lang>.root` accepts a
string **or a resolver function**, resolved through `resolve_root_fn`
(`lsp.lua:543`) with per-resolver memoization, and on the *reuse* path
as well as the spawn path (`:535`). A configured root "MUST be a
canonical absolute path" (`:525`). So this is a config entry, not new
machinery.
*My vote: **an upward marker walk with an explicit precedence, falling
back to the file's own directory.*** In order:
1. **`.texlabroot`** — if texlab honours it (see the verification
caveat below), an explicit user-placed marker should win over
everything inferred.
2. **`latexmkrc` / `.latexmkrc`** — a build config is a strong,
deliberate signal of a document root.
3. **`Tectonic.toml`** — the same for tectonic projects.
4. **The file's own directory**, as the fallback.
**Deliberately NOT in the walk: `.git`.** A repository root is the
wrong answer for LaTeX — texlab wants the *document* root, and a thesis
inside a monorepo would otherwise get the monorepo. This is the one
place where copying the other fourteen entries' instinct would be
actively wrong.
**And omitting it is NOT sufficient — revision 2 stopped one step
short.** `project_root_for` falls through to `pmacs.project.detect`
when a resolver returns `nil`, and **that** walk lists `.git` among its
markers (`src/project.rs:184`). So a resolver that politely declined on
a markerless file would hand texlab the monorepo **by the back door**,
with the exclusion looking correct at every line you would think to
read. The resolver therefore **never declines** for a file with a
directory, and the pin is end-to-end through attach — with the same
fixture asserting the shared detector really would have answered the
repository root, so the test cannot pass vacuously.
**Deliberately NOT proposed: scanning for `\documentclass`.** That is
the semantically correct notion of a root document, and it is a
directory scan on every resolve, with its own caching and invalidation
questions. If the marker walk proves insufficient in use, that is the
next increment — with evidence.
**CAVEAT DISCHARGED (revision 3), and the premise behind it was wrong
in a way that raises the lane's stakes.**
Established by driving a hand-written LSP client against `texlab run`
and reading texlab's source at the exact installed tag `v5.25.1`:
- **`.texlabroot` is real**, and so is a wider marker set than this
document listed. `crates/distro/src/language.rs` maps
`.texlabroot`/**`texlabroot`** → Root, `Tectonic.toml` → Tectonic,
`.latexmkrc`/**`latexmkrc`** → Latexmkrc, and
`ProjectRoot::walk_and_find` walks ancestors testing all three,
**innermost winning**. The implementation ships texlab's own set,
including the bare spellings §3 omitted.
- **But texlab cannot rescue a root we get wrong.** Every arm of that
walk searches `workspace.iter()` — documents *already loaded* — and
the workspace is built from the folders **the client supplies**.
Observed directly: with `rootUri` at `chapters/`, no ancestor marker
(`.texlabroot` included) widened texlab's view, and its dependency
graph never reached the parent document; with `rootUri` at the marker
directory, the parent resolved whether or not a marker was present.
**So `config.latex.root` IS the project scope.** Revision 2 framed the
resolver as choosing between plausible roots that texlab might refine.
It does not refine. The resolver is the whole value of the lane for a
multi-file thesis, not a nicety — which is the opposite of how §2's
"Slice 1 is one config entry" reads, and worth stating plainly.
*(Also observed rather than assumed: bare `texlab` answers `initialize`
over stdio, so `args = {}` is correct and the `run` subcommand is
unnecessary.)*
## 4. Open questions
### Q#LX1 — does `texlab` need `settings` or `init_options`?
It pulls configuration via `workspace/configuration` under a `texlab`
section, which pmacs answers (#13). An empty section takes defaults, as
the Go entry does for gopls.
*My vote: **ship nothing.*** Build-on-save and forward-search are the
two candidates and both are opinionated; forward-search additionally
needs a configured viewer, so a default would be wrong for most
machines. Users override through the same `init.lua` seam as the other
fourteen.
### Q#LX4 — do Haskell and OCaml belong in this lane at all? *(renumbered — see below)*
With HLS installed, Haskell is now the same shape as LaTeX: one entry,
no new dependency. **But the argument against it never rested on the
dependency.** The `.hs` files here are `levineuwirth.org`'s Hakyll
generator, edited rarely; HLS is version-coupled to GHC and is a large
resident process for a language touched a few times a year.
*My vote: **LaTeX only in this lane.*** Add Haskell when there is use
evidence, which is a one-line change at that point. OCaml needs
`ocaml-lsp-server` via opam (not packaged for Arch) and is not close.
**Renumbered from Q#HS1 deliberately.** The merged
`docs/latex-grammar-math-substrate-framing.md` already uses **Q#LX2**
for a different question — its grammar vendoring source (`:83`) — so
revision 1's Q#LX2 collided with a live ID in the same language area.
This document's LaTeX questions are Q#LX1 and the root question in §3;
the language-scope question takes Q#LX4 to avoid a second collision.
### Q#LX3 — does this touch multi-root LSP affinity? — **RESOLVED, and revision 1 read a stale line**
Revision 1 called this "the one item that could argue for deferring
Slice 1", on the basis that multi-root affinity was in flight.
**It merged as PR #161.** `COHERENCE.md:124` lists it among landed
coherence work, and `:867` says "First slice landed (PR #161)". Only
`:1669` still says "first slice in flight" — and that line contradicts
the other two **within the same document**.
So the deferral argument dissolves: a LaTeX entry keyed like the
existing servers rides the convention that already landed. **The
`COHERENCE.md:1669` inconsistency is real and should be fixed**, but by
whoever next touches §20 — not smuggled into this lane.
## 5. Coherence impact (§20)
- **Journey steps touched: none.** This adds a row to an existing
registry; no new surface, keybinding, or panel.
- **Interaction islands: none added.**
- **Config registry adoption: yes, and only that.** One entry in the
existing `pmacs.lsp.config` table, overridable from `init.lua` by the
same mechanism as the fourteen already there.
- **Background-work attribution (§9): unchanged, and NOT improved.**
texlab spawns under the existing LSP supervision path with no new
lifecycle — but it is another process that appears in `*lsp*` and
whose requests appear in `*workers*` with nothing joining them. The
worker-identity lane owns that; this lane neither helps nor worsens
it.
- **§20 classification: WIRING, not model.** It surfaces machinery that
already exists rather than adding a runtime entity — and §2 shows it
is *more* purely wiring than revision 1 thought.
## 6. Verification
- **A `.tex` buffer attaches texlab**, witnessed end to end rather than
by asserting the config table's contents.
- **Detection is unchanged**: `.tex`/`.latex`/`.sty`/`.cls` still
resolve to `latex` via the grammar path (§2), asserted so that a
later "helpful" filetype-map addition cannot be mistaken for the
thing that made it work.
- **The root resolver returns the marker directory**, witnessed on a
fixture with a `latexmkrc` above a `chapters/` subdirectory — the
thesis shape, which is the case a file-directory root gets wrong.
- **It falls back to the file's own directory** with no marker present.
- **`.git` does NOT become the root** (§3) — a fixture with a
repository above a document directory, asserting the document
directory wins. This is the case where copying the other entries'
instinct is wrong, so it is pinned.
- **A missing `texlab` surfaces guidance**, through the existing
spawn-failure path (#204) — asserted, not assumed, since that path is
what makes the failure honest.
- **Fixtures bound project detection** with
`pmacs.project.set_search_boundary`. R8 was a fixture letting
detection escape into the developer's environment; a LaTeX root
fixture is exactly that hazard's shape.
**What this will NOT prove:** that texlab resolves multi-file `\input`
graphs correctly (that is texlab's job, not pmacs's), or that Haskell
and OCaml work (Q#LX4).
## 7. Not in scope
New tree-sitter grammars — Haskell and OCaml would have LSP without
highlighting, a real asymmetry that must be stated in the PR rather
than discovered by a user. Any change to Lean, which needs none.
Math/typesetting work (`#172` owns it). Any change to the LSP
spawn-failure surface (#204). Scanning for `\documentclass` to find a
root document (§3). Fixing `COHERENCE.md:1669`'s stale multi-root line
(Q#LX3) — real, but another lane's edit. Haskell and OCaml entries
(Q#LX4).