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

LSP protocol coverage

Which LSP methods noyalib-lsp implements, which it acknowledges but treats as no-ops, and which it deliberately does not support. Coverage is conservative: every implemented method has end-to-end tests in crates/noyalib-lsp/tests/ and behavioural tests exercising the JSON-RPC envelope.

LSP version targeted: 3.17.

Implemented

MethodDirectionBehaviour
initializeclient → serverReturns server capabilities (formatting, hover, didOpen / didChange / didClose, publishDiagnostics)
initializedclient → serverAcknowledged; server-side initialisation runs here
shutdownclient → serverAcknowledged; server prepares for exit
exitnotificationServer exits with code 0 (or 1 if shutdown was not requested)
textDocument/didOpennotificationDocument tracked in the open-documents map; publishDiagnostics runs against the new content
textDocument/didChangenotificationDocument content updated; publishDiagnostics runs against the new content
textDocument/didClosenotificationDocument removed from the open-documents map
textDocument/formattingrequestReturns a TextEdit[] that rewrites the document via noyalib::cst::format. Lossless: comments, anchors, indent style preserved
textDocument/hoverrequestReturns the parsed value type at the cursor; if a JSON Schema is associated with the document, returns the schema’s description field for the value at the cursor path
textDocument/publishDiagnosticsserver → clientEmitted on every didOpen / didChange. Carries parse errors with span attached; carries schema-validation errors when a schema is associated

Acknowledged but no-op

These method names are recognised so the client doesn’t see “method not found” errors, but the server returns an empty result. They’re candidates for future implementation when there’s a proven user request.

MethodWhy no-op
textDocument/didSaveThe CST formatter doesn’t need a save signal — didChange already flushes
workspace/didChangeConfigurationNo tunable configuration exposed yet; future noyalib.fmt.indent etc. will land here
workspace/didChangeWatchedFilesDiagnostics run on the open buffer; on-disk changes are picked up on the next didOpen

Deliberately unsupported

MethodReason
textDocument/completionYAML’s grammar makes meaningful completions context-heavy. The LSP intentionally defers to the YAML schema-aware completion in editor extensions like yaml-language-server until we have a clear value proposition
textDocument/codeActionNo quick-fix logic implemented yet. Schema validation errors carry recovery hints in their messages but no WorkspaceEdit
textDocument/renameAnchor / alias renaming is a real ask but requires careful handling of cross-document references; planned post-v0.0.1
textDocument/referencesSame as rename — anchor reference graph
textDocument/definitionSame family
textDocument/documentSymbolOutline view; planned but not yet implemented
textDocument/foldingRangeEditors typically derive folding from indentation directly
workspace/symbolCross-document symbols; out of scope for a YAML server

Methods not listed above return methodNotFound per the LSP spec.

Diagnostic coverage

textDocument/publishDiagnostics payloads are produced by:

  1. Parse errors — every noyalib::Error::ParseWithLocation becomes a Diagnostic with severity Error, span attached. See crate noyalib’s errors.md for the full variant list.
  2. Schema validation errors — when a JSON Schema is associated with the document (via the editor’s schema-mapping config or an inline # yaml-language-server: $schema=... pragma), each violation becomes a Diagnostic with severity Warning. The diagnostic’s code field carries the schema keyword that failed (type, enum, required, etc.).

Diagnostics are produced eagerly on every didOpen / didChange. There is no debounce — the parser is fast enough that 10ms keystroke cadence on a 50KB document is comfortably under one event loop tick.

Capabilities advertised at initialize

{
  "capabilities": {
    "textDocumentSync": {
      "openClose": true,
      "change": 1
    },
    "documentFormattingProvider": true,
    "hoverProvider": true,
    "diagnosticProvider": {
      "interFileDependencies": false,
      "workspaceDiagnostics": false
    }
  },
  "serverInfo": {
    "name": "noyalib-lsp",
    "version": "0.0.1"
  }
}

textDocumentSync.change is 1 (full-document sync). Incremental sync (2) would shave a small amount off the wire bandwidth on very large files but adds protocol complexity that hasn’t paid off in benchmarks. Revisit if profiling shows it matters.

Where the JSON-RPC envelope is handled

crates/noyalib-lsp/src/
├── lib.rs                # Server type, handle_message dispatch
├── main.rs               # stdio loop, main()
├── format.rs             # textDocument/formatting handler
├── hover.rs              # textDocument/hover handler
└── diagnostics.rs        # publishDiagnostics builder

The dispatch in lib.rs::handle_message is a simple match on the incoming method name; each handler returns a HandleOutcome (reply / notify / silent) that tells the transport layer what to send back.

Testing

SuiteWhat it covers
crates/noyalib-lsp/tests/protocol.rsEnd-to-end JSON-RPC round-trips (subprocess-driven; not run under llvm-cov)
Per-handler unit tests in each *.rsDirect handler invocation without the JSON-RPC envelope
crates/noyalib-lsp/benches/lsp_handlers.rsPer-handler latency on small + Kubernetes-shaped inputs

The full pyramid is documented in docs/TESTING.md.