A Language Server Protocol
implementation for the ***plain spec-driven language
(Codeplain). It provides hover,
go-to-definition, rename, and diagnostics for .plain files in any
LSP-capable editor.
It's the editor-agnostic engine behind the plyn editor extensions (Zed, VS Code, Cursor), but it speaks plain LSP over stdio, so any client can use it.
***plain is a prose-heavy, line-oriented spec format. The server's only symbol
kind is a concept — a :concept_name: token. A concept is a definition
when it appears as a - :name: bullet inside the ***definitions*** section;
every other occurrence is a usage.
-
Hover — shows where a concept is defined (or, on a definition, where it's used). Built-in concepts (
:Implementation:,:ConformanceTests:,:AcceptanceTests:,:UnitTests:) show a fixed description instead. -
Go-to-definition — jumps from a usage to its definition (and from a definition to its usages).
-
Rename — renames a concept across the workspace.
-
Folding — each
***section***folds from its header to the section's last line, like a Markdown heading. (Served overtextDocument/foldingRange; note that Zed does not request folding ranges yet — see zed-industries/zed#28091.) -
Diagnostics
- Syntax — every top-level list item in a
***definitions***section must start with a concept, e.g.- :Concept:; a bullet that doesn't (prose, or a concept that isn't first) is an error. Nested sub-items (indented more than one space), prose,>comments and blank lines are left untouched. - Unused concept — a concept that is defined but never referenced is shown
as a greyed-out hint (like an unused variable). Concepts in
exported_concepts:are exempt. - Undefined concept — a concept that is referenced but never defined is an
error. A concept counts as defined if it is declared anywhere in the
workspace, listed in
required_concepts:, provided by a resolvableimport:(resolved to<name>.plainin the file's dir or atemplate/templates/importssubfolder), or one of the language's built-in concepts (:ConformanceTests:,:UnitTests:,:AcceptanceTests:,:Implementation:). - Cyclic concepts — definitions that reference each other in a cycle
(
:A:→:B:→:A:) are an error on each concept in the cycle. - Duplicate definition — a concept defined more than once is an error on each declaration.
- Unknown section — a
***...***header that isn't one ofdefinitions,implementation reqs,test reqs,functional specsis an error, with a "did you mean" hint for near-misses (e.g.***denitions***→***definitions***). - Acceptance tests placement —
***acceptance tests***is not a standalone section; it is valid only indented under a functionality inside a***functional specs***section. Standalone (column 0) or under any other section is an error. - Did you mean — when an undefined concept differs only in capitalization
from a known one, the error suggests the correct name; declaring a concept
that only differs in case from a built-in (e.g.
:implementation:) is a warning suggesting the built-in.
The last three are workspace-wide, so they are recomputed and re-published for every open document whenever the index changes.
- Syntax — every top-level list item in a
Indexing is workspace-wide and kept in sync with open buffers.
npm install -g plain-language-serverThis puts a plain-language-server binary on your PATH. Editors that bundle
the plyn extension install it automatically — you don't need to do this by
hand.
The server communicates over stdio:
plain-language-server --stdioPoint your editor's LSP client for .plain files at that command. For example,
in Zed (via the plyn extension) it's wired up automatically; for a manual setup,
configure a language server that runs plain-language-server --stdio for the
plain language.
npm install
npm run build # tsc -p . → out/Smoke / e2e checks (run after a build):
node scripts/smoke.js # LSP initialize handshake
node scripts/e2e.js # hover / go-to-definition / rename against scripts/fixtures/The entry point is out/cli.js (a thin #!/usr/bin/env node wrapper around
out/server.js). Source lives in src/:
| File | Role |
|---|---|
server.ts |
LSP wiring + document sync |
indexer.ts |
workspace concept index |
hover.ts |
hover provider |
definition.ts |
go-to-definition |
rename.ts |
rename provider |
diagnostics.ts |
syntax validation (definitions list items + section headers) |
semantics.ts |
unused / undefined / cyclic concept diagnostics |
folding.ts |
***section*** folding ranges |
builtins.ts |
built-in concept registry (names + hover descriptions) |
util.ts |
shared helpers |
MIT