Markdown
Catenary’s out-of-box markdown server is
Lattice — a Two Wells sibling project
(same AGPL-3.0-or-later + commercial dual-license as Catenary). Lattice is a
markdown predicate linter / backlink reconciler shipped as an LSP server. It
answers documentSymbol, workspace symbols, references, rename, hover,
folding, and document links, so it powers Catenary’s catenary grep /
catenary glob enrichment and the catenary diagnostics pipeline on markdown.
Install
Lattice ships as the lattice binary. Install it from the
Lattice repository (build from source
with cargo install, or grab a release binary) and put lattice on your PATH.
# From a checkout of TwoWells/Lattice
cargo install --path .
Verify it is reachable:
lattice --version
Config
Catenary ships a built-in definition for lattice — no [lsp.server.*] config is
needed. If lattice is on PATH it works automatically:
[lsp.server.lattice]
args = ["serve"]
Root markers
The default markdown root_markers are [".lattice.toml", ".git"]:
.gitroots Lattice at the repository — correct, since its backlink graph spans the repo’s markdown..lattice.toml(optional) gives a tighter root and project configuration when present, even in a subdirectory.
Model
Lattice treats a markdown tree as a predicate/backlink graph:
- Predicates live in CommonMark title text.
- Backlinks live in YAML frontmatter.
catenary diagnostics surfaces Lattice’s link/predicate checks on edited
markdown, and grep/glob enrichment exposes the document structure Lattice
reports.
Opting out (marksman)
marksman remains a shipped server
definition — it is simply no longer the default. Re-enable it with a one-line
binding (no need to redefine the server), in either your user config
(~/.config/catenary/config.toml) or a project .catenary.toml at a
workspace root:
[lsp.language.markdown]
servers = ["marksman"]
If marksman is on PATH, that binding is all you need. Both layers reach server
dispatch: the user binding reroutes everywhere, and a project .catenary.toml
binding reroutes that root — the project layer wins per root. A project
[lsp.language.*] servers list replaces the binding (array-replace, never
append), and a server the project defines in its own [lsp.server.*] is a legal
binding target.
Notes
- OOB markdown intelligence requires
latticeon PATH. Without it the daemon emits a benignFailed to spawn LSP server: latticewarning — not a wedge; every other server and all of Catenary’s core (grep/glob/diagnostics on other languages) is unaffected.