Skip to content

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

plain-language-server

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.

What it understands

***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 over textDocument/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 resolvable import: (resolved to <name>.plain in the file's dir or a template/templates/imports subfolder), 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 of definitions, implementation reqs, test reqs, functional specs is 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.

Indexing is workspace-wide and kept in sync with open buffers.

Install

npm install -g plain-language-server

This 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.

Use from an editor

The server communicates over stdio:

plain-language-server --stdio

Point 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.

Development

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

License

MIT

About

Language server for the ***plain spec-driven language

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages