summaryrefslogtreecommitdiff
path: root/docs/lsp.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/lsp.md')
-rw-r--r--docs/lsp.md35
1 files changed, 26 insertions, 9 deletions
diff --git a/docs/lsp.md b/docs/lsp.md
index bce9b2cc..0125f958 100644
--- a/docs/lsp.md
+++ b/docs/lsp.md
@@ -31,7 +31,11 @@ core shell worker
The shell already ran this exact pattern for pty readers, so the async part is
about thirty lines per shell: `tty.zig` uses `io.concurrent` + the vaxis loop
queue, `gui.zig` uses a detached thread + the mutex queue it already had. The
-web shell has no threads and no-ops the effect.
+web shell compiles in no backend at all (`zls_backend` is off for wasm), which
+makes `lsp.supports` empty, which makes `lspRequest` return before it emits —
+so on the web the effect is never even raised. `web.zig` carries a prong for it
+and exports `pardes_lsp_response` anyway; both are unreachable in the shipped
+configuration and wait for a host that links a backend.
Three rules make it safe:
@@ -144,8 +148,10 @@ loses the whole switch to the parser's error recovery, taking with it every
ancestor an expected-type resolution needs. ZLS answers this with a private
token scanner welded to its `*Server`. `lsp_zls.completionSource` instead makes
the tree PARSE — it splices a placeholder in after the dot, in the six
-spellings a half-typed line can need (an identifier, and the same again closing
-a prong, a statement, a paren or a brace), and keeps the one that both makes
+spellings a half-typed line can need — three shapes, each with and without a
+closer still hanging: a switch prong (`_p => {},` / `_p => {}, }`), an
+unterminated statement (`_p;` / `_p)`), and a bare identifier (`_p` / `_p }`)
+— and keeps the one that both makes
the dot reachable in the tree and leaves the fewest parse errors. Everything
after that is ZLS's ordinary public resolution over an ordinary tree.
@@ -164,7 +170,7 @@ Per press, measured by `zig build lspbench` on this repo:
| | ReleaseFast | Debug (what `zig build` installs) |
|---|---|---|
-| a switch arm in `src/pardes.zig` (12.8k lines) | 8.8 ms | 87 ms |
+| a switch arm in `src/pardes.zig` (14.6k lines) | 8.8 ms | 87 ms |
| `std.` — 91 candidates, each alias-resolved into the stdlib | 26 ms | 204 ms |
It is a worker thread, so the editor does not block; but the second press of
@@ -261,7 +267,8 @@ document — which matters precisely when the pane you are in is the problem.
## Writing a backend
`src/lsp/lsp.zig` is the seam. An implementation supplies three things and touches
-nothing else:
+nothing else (`backend_name` below is the SEAM's, not yours — it is a literal in
+`lsp.zig` naming whichever backend was compiled in):
```zig
pub fn query(gpa, arena, req: Req, out: *std.Io.Writer) void
@@ -280,16 +287,26 @@ read is in `req` (`path`, `source` (NUL-terminated), `offset`, `arg`, `root`).
`Io.Writer.Allocating`), so a backend never allocates the result, never frees
it, and cannot get the allocator wrong. `arena` is freed wholesale on return;
`gpa` is for a backend's own scratch. Use `lsp.row()` to emit a location,
-`lsp.rel()` to spell its path against `req.root`, `lsp.lineCol()` to convert an
-offset, and `lsp.edit()` for each half-open range of a rename response. Location
+`lsp.spanRow()` for one that carries the RANGE it matched (the
+`path:LINE:COL-ENDCOL` form above), `lsp.rel()` to spell a path against
+`req.root`, `lsp.lineCol()` to convert an offset, and `lsp.edit()` for each
+half-open range of a rename response. Location
rows are byte-identical across backends; rename ranges are consumed by the core
and never rendered. `rel` allocates nothing — it returns a slice of what you
hand it.
## How the implementations are judged
-`zig build lspbench` — same harness, same corpus (pardes's own `src/`), same 22
-probes, every backend.
+`zig build lspbench` — same harness, same corpus, same 22 probes, every backend.
+The corpus is pardes's own `src/`, plus `test/lspfixture/`: five of the probes
+point at fixtures rather than at real source, because on clean, already
+formatted code the correct answer to `diagnostics` and `format` is nothing, and
+that is indistinguishable from a backend that has neither. `broken.zig` carries
+an unused local and a misformatted fn; `dotcomplete.zig` and `dothalf.zig`
+carry the two shapes of half-typed dot. The 22 probes cover 15 of the 17
+`lsp.Kind`s
+— `definition` three times, `document_symbols` twice, `completion` five times,
+and the two introspection kinds (`status`, `explain`) not at all.
- **Feature completeness.** Which kinds return rows, and whether the rows
contain what they should. The harness trusts *results*, not the `supports`