Files
PgTidy/docs/lsp-status.md
T

98 lines
5.5 KiB
Markdown

# 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 `WorkspaceEdit`s 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.
## Concrete next steps (recommended, smallest-first)
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`.