Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Language Server

The FOL language server is started with:

fol tool lsp

It runs over stdio and is meant to be launched by editors.

Current Feature Set

The current working surface includes:

  • initialize / shutdown / exit
  • document open / change / close
  • diagnostics
  • hover
  • go-to-definition
  • go-to-type-definition (a value’s declared type, unwrapping optional, container, ownership/borrow, pointer, channel, and eventual shells)
  • go-to-implementation (a protocol standard’s conforming types)
  • document highlight (occurrences of the symbol under the cursor, current file)
  • whole-document formatting
  • code actions for compiler-suggested unresolved-name replacements
  • signature help for plain and qualified routine calls
  • references
  • rename for same-file local bindings, routine parameters, and current-package top-level symbols, with prepare-rename validation
  • semantic tokens
  • inlay hints (inferred type for var/lab/con bindings without an explicit annotation)
  • folding ranges (brace blocks)
  • selection ranges (word -> enclosing block -> file)
  • document symbols
  • workspace symbols across discovered .fol files under the mapped workspace root
  • completion, including language-keyword suggestions and completion-item resolve
  • the same compiler-backed behavior for ordinary source files and build.fol

The general editor UX above is shared across language versions. Semantic coverage claims are deliberately bounded to the implemented V1 contract, the checked-in V2 generic-routine, generic-type, constrained-generic, and protocol-standard example subset, and the checked-in V3 memory and processor inventories. The server does not claim broad rename outside the documented safe classes, range formatting, or editor-owned semantics beyond what compiler-backed analysis supplies.

The server also does not advertise workspace/executeCommand, so it does not emit a dead run code lens. Package execution remains the explicit fol code run CLI path. That path still enforces compiler-owned API legality, but bundled std is not an execution permission: host-compatible core and memo artifacts can run without it.

That means build files should not need a separate editor mode. build.fol goes through the same language server entrypoint as the rest of the package.

textDocument/didChange accepts both whole-document replacements and ranged change events. The server applies ranged changes in order against the current in-memory document version.

The server advertises incremental text sync by default. Full-document sync remains an explicit opt-in for clients/configs that need it.

Formatting is intentionally limited to textDocument/formatting. textDocument/rangeFormatting remains unsupported until the formatter can preserve surrounding structure safely instead of guessing at partial blocks.

Current whole-document formatting also normalizes blank-line runs outside multiline quote/comment payloads: leading blank lines are removed and repeated blank lines collapse to one. Payload lines are preserved, and braces inside quotes or compiler-recognized comments do not affect indentation.

Analysis Model

The server works like this:

  1. receive editor request/notification
  2. map the open document to a package/workspace context
  3. materialize an analysis overlay for the in-memory document state
  4. run parse/package/resolve/typecheck as needed
  5. convert compiler results into LSP responses

Package/workspace root discovery is cached for the current session once a document directory has been mapped.

When evaluated build.fol state activates a bundled internal standard dependency, the disposable analysis overlay materializes that dependency under its active declared alias. Conditional declarations that are inactive for the selected target/options do not become resolvable merely because their text is present in build.fol. Hosted examples therefore resolve in a fresh checkout; editor tests and users do not need to pre-populate .fol/pkg first.

The same evaluated state supplies the artifact’s core or memo contract to compiler-backed analysis. The LSP never widens that contract because an artifact has a run or test step: execution registration is not semantic input.

That means diagnostics and navigation are compiler-backed, not guessed from Tree-sitter alone.

For open documents, diagnostics and semantic snapshots are cached separately.

didOpen and didChange refresh diagnostics for the current in-memory text.

Hover, definition, type definition, implementation, document highlight, signature help, references, prepare rename, rename, semantic tokens, document symbols, workspace symbols, completion, inlay hints, folding ranges, and selection ranges use a semantic snapshot keyed by document version and reuse it until didChange or didClose invalidates it.

Diagnostics

Compiler diagnostics remain canonical.

The server adapts them into LSP diagnostics for the currently open document.

LSP diagnostics include the diagnostic code in the message (e.g. [R1003] could not resolve name 'answer') so editors display the code inline.

The server removes only exact wire-identical diagnostics before publishing. Diagnostics that share a line and code but differ in range, message, severity, or related information remain visible. This catches true cross-stage duplicates without hiding distinct failures that happen to occupy one line.

Expected Behavior

If the LSP client is attached correctly, you should expect:

  • source-file diagnostics in the open file
  • hover on resolved symbols
  • go-to-definition across current-package and imported symbols where supported
  • go-to-type-definition for values with a compiler-known declared type
  • go-to-implementation from protocol standards to conforming types
  • current-document highlights for a resolved symbol’s declaration and uses
  • whole-document formatting edits from textDocument/formatting
  • code actions only when the compiler attached an exact replacement suggestion The current shipped code-action inventory is intentionally narrow: unresolved-name replacements only.
  • signature help for supported routine call sites
  • references for the supported symbol classes
  • rename for same-file local bindings, routine parameters, and current-package top-level symbols only, with prepareRename enforcing the same eligibility
  • semantic tokens for semantic identifier categories
  • inferred-type inlay hints for unannotated var/lab/con bindings
  • brace-block folding ranges and word/block/file selection ranges
  • document symbols for the current file
  • workspace symbols across discovered .fol files under the mapped workspace root
  • completion in ordinary source files and build.fol, including language keyword suggestions (declaration, control-flow, literal, and diagnostic keywords from the lexer’s keyword tables) alongside resolver-backed symbols in plain positions
  • shipped V2 example diagnostics/navigation for:
    • generic routines
    • generic types
    • constrained generics
    • protocol standards
  • current positive executable V2 example roots covered directly in editor tests:
    • examples/generic_type_exec_m1m2
    • examples/generic_standard_constraint_m1m2
    • examples/standards_protocol_m2
  • compiler-backed V3 diagnostics and navigation for the checked-in ownership, borrowing, pointer, dfr/edf, spawn, channel, select, mux[T], async, and await examples
  • positive V3 roots in LSP/open/navigation inventory sweeps and fail_mem_* / fail_proc_* roots in the guarded diagnostic inventory; the exact processor list is maintained in the shipped processor inventory
  • model-aware completion and semantic-token coverage for the shipped V3 type, declaration-sigil, channel, mutex, and processor syntax, without suggesting hosted processor surfaces in core or unhosted memo

The LSP does not independently decide ownership, thread safety, channel lifecycle, or delayed mutex effects. Those rules come from compiler analysis. Current negative coverage includes unique-pointer field dereference without place-aware projection IR, moved-owner reinitialization inside dfr/edf, terminating report inside deferred cleanup, indirect stored/parameter routine targets for spawn and async, implicit nested-routine capture of outer locals, recoverable eventuals left live at fallthrough or an exiting break/return/report, awaiting an eventual inside edf, and mutex field, guard, or mux[T] forwarding effects inside deferred bodies.

If the client is attached but you see no diagnostics at all, the likely issue is not Tree-sitter. It is the LSP request/attach path.

For the compiler-owned contracts behind diagnostics and semantic editor behavior, see Compiler Integration.