Files
PgTidy/docs/lsp-status.md
T

5.5 KiB

PgTidy LSP Status & Roadmap

This document describes the current state of the PgTidy LSP server (pkg/lsp, cmd/pgtidy/lsp.go), what it actually provides today, and concrete next steps. It was written as part of issue #3 ("See what we can provide for LSP").

Note: The V3 milestone is already implemented and shipping (docs/todo.md marks V3 done), so this is an inventory + gap analysis, not a greenfield proposal.

What the server provides today

Verified by hand against the built binary (pgtidy lsp, JSON-RPC 2.0 over stdio, Content-Length framing) — no external LSP library, all wire types hand-rolled.

Advertised capabilities (initialize → capabilities)

Capability Value Where
textDocumentSync {openClose: true, change: 1 (full), willSaveWaitUntil: true} serverCaps
documentFormattingProvider true handle("initialize")
documentRangeFormattingProvider true handle("initialize")
codeActionProvider true handle("initialize")
hoverProvider true handle("initialize")
documentSymbolProvider true handle("initialize")

Supported methods

Method Direction Behavior
initialize / shutdown / exit / initialized req/resp + notif Lifecycle. exit/shutdown acknowledged with null result.
textDocument/didOpen notif Stores document text; triggers publishDiagnostics.
textDocument/didChange notif Stores latest content version; triggers publishDiagnostics.
textDocument/didClose notif Drops text + fix cache; clears diagnostics with empty list.
textDocument/formatting req/resp Full-doc format via pkg/format; returns one fullReplace TextEdit.
textDocument/rangeFormatting req/resp Formats doc, returns minimal edit over the selected line range.
textDocument/codeAction req/resp Returns quick-fix WorkspaceEdits for fixable diagnostics overlapping the range.
textDocument/willSaveWaitUntil req/resp Format-on-save: returns the same safety-gated edit as formatting.
textDocument/hover req/resp Markdown with the rule ID + message of the diagnostic under the cursor; null elsewhere.
textDocument/documentSymbol req/resp CREATE FUNCTION/PROCEDURE statements (name, kind Function, function/procedure detail, full range + name selection range).
textDocument/publishDiagnostics notif Sent on every open/change; diagnostic code = RuleID, source = pgtidy; the range covers the offending token, not one character.
$/cancelRequest req Ignored (per LSP, no response).
unknown req/resp -32601 method not found (when the request has an id).

Verified at runtime (e2e smoke test)

  • initialize returns the capability block above.
  • didOpen on select * from t; → publishDiagnostics with COR001 ("SELECT * is fragile…", severity 4 = hint, code COR001).
  • textDocument/formatting on that input → edit replacing with SELECT *\nFROM t;\n (keyword casing + clause-per-line applied).
  • textDocument/hover → -32601 method not found (not implemented — correct).

What the server does NOT provide (gaps)

Done since the first inventory: real diagnostic highlight range, hover, documentSymbol, willSaveWaitUntil. Still open:

  1. No completion. textDocument/completion is not implemented. Not urgent for a formatter/linter.
  2. No diagnostics debounce/coalescing beyond full-sync. Every didChange re-runs the full lint engine. Fine for now; matters on large files.
  3. initializationOptions / workspace config. initialize params are parsed nowhere — no way to pass style overrides or a config path over the protocol.
  4. No prepareRename, rename, references, foldingRange, documentLink. Low priority; documentSymbol now provides the symbol info they would build on.
  5. Hover is diagnostics-only. No keyword/type glossary for hover on plain identifiers.

Conventions to keep consistent

  • One core, many frontends. The LSP reuses pkg/diagnostics.Diagnostic and pkg/lint directly — no parallel diagnostic model. New LSP features should reuse these, not fork them.
  • Safety gate is non-negotiable. Both formatting and rangeFormatting call format.SemanticallyEqual(src, out) before returning edits; on failure they return an empty edit (keep original). Any new code path that formats must honor this invariant (invariant #5 in AGENTS.md).
  • No new external deps. The wire layer is intentionally dependency-free. New protocol types should be added as local structs, not pulled in from an LSP library.
  1. initializationOptions: accept a config path / style overrides in initialize.
  2. Debounce didChange diagnostics.
  3. Hover glossary for keywords/types.

Do NOT: expand the LSP surface into a broad design (workspace features, incremental parsing, custom textDocument/* extensions). Keep any change scoped and evidence-backed by a pkg/lsp test (see server_test.go for the framed-request/response harness).

Verification

  • go build ./cmd/pgtidy succeeds.
  • go test ./... passes (LSP unit tests in pkg/lsp/server_test.go exercise initialize, formatting, range formatting, didClose diagnostics clearing).
  • Runtime e2e smoke test (framed JSON-RPC over stdio) confirmed initialize capabilities, COR001 diagnostics, and a formatting edit. hover, documentSymbol and willSaveWaitUntil are covered by tests in pkg/lsp/server_test.go.