summaryrefslogtreecommitdiff
path: root/docs/lsp.md
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-08-09 09:43:51 -0300
committerGabriel Schneider <[email protected]>2026-08-10 09:17:07 -0300
commit218d8577cbffb45a6ee80cea52418864a044e452 (patch)
treeb0b31c1e8091d6dea6fb767883e8c78e48cc385e /docs/lsp.md
parent599dd82f96b9d091aae78300aa6c3fbc81f9eb69 (diff)
downloadpardes-218d8577cbffb45a6ee80cea52418864a044e452.tar.gz
pardes-218d8577cbffb45a6ee80cea52418864a044e452.zip
lsp rows: paths relative to the asking file, and the completion text
Diffstat (limited to 'docs/lsp.md')
-rw-r--r--docs/lsp.md56
1 files changed, 45 insertions, 11 deletions
diff --git a/docs/lsp.md b/docs/lsp.md
index 0c6a2f97..258e409e 100644
--- a/docs/lsp.md
+++ b/docs/lsp.md
@@ -51,11 +51,12 @@ Three rules make it safe:
Every backend renders into one format:
```
-/abs/path/to/file.zig:LINE:COL text
-/abs/path/to/file.zig:LINE:COL-ENDCOL text
+sub/file.zig:LINE:COL text under the asking window's dir
+sub/file.zig:LINE:COL-ENDCOL text
+/abs/path/elsewhere.zig:LINE:COL text anywhere else
```
-1-based line and column, absolute path. The second form carries the answer's
+1-based line and column. The second form carries the answer's
RANGE where the protocol gave one on a single line (a token, a symbol's name),
and a look on it SELECTS that span rather than parking at its first cell — so
`gd` lands on the whole name and `gr` steps references with each one
@@ -71,14 +72,46 @@ locations (`hover`, `code_action`, `format`, `rename`) open `+Hover`/`+Lsp`
instead and do not arm the stepper — `n` over a documentation blurb would step
to nowhere.
+**A path UNDER `Req.root` is written relative to it; everything else keeps its
+full absolute path** (`lsp.rel`). `Req.root` is the directory of the file the
+query was asked about — the window that generated the buffer — and a `gr` over
+one file was otherwise the same forty-character prefix repeated down the whole
+pane, with the part you came to read pushed off the right edge. The short form
+resolves because `Req.root` is also the directory the results buffer is *named*
+in (`output_pane.open`), and a look resolves a relative word against the
+directory of the pane it was clicked in — which is that buffer.
+
+Under, never "shorter": a hit outside the tree is **not** walked up to with
+`../`. An absolute path resolves from anywhere and says where it is; a `../..`
+chain says neither, and stops being true the moment the row is read anywhere but
+beside its own buffer. `test/snapshots/lsprelpath.snap` pins both directions end
+to end — the enum one level up (absolute row) and one level down (`inner/tint.zig`,
+a stripped path that still has a separator in it), each with the `n` step that
+opens it, plus a right click.
+
+This is the rule `look.grep` already follows for its own rows (`look.zig`, the
+`shown` computation), written a second time; the two are now the same function
+and want to become one.
+
`completion` is the kind this shape changes the most. Every other editor answers
a dot with a popup of NAMES to insert; a seam that returns locations cannot
insert anything, so this one answers with the candidates' **declarations** —
-one `path:LINE:COL-ENDCOL` row each, in the same `+Search` buffer, steppable
-with `n`. That is a different and arguably better answer to "what goes here":
-you read the definitions rather than a list of words. It is the one location
-kind that does NOT jump on a single row, because with one candidate you still
-want to see the list rather than be teleported into it.
+one row each, in the same `+Search` buffer, steppable with `n`:
+
+```
+path:LINE:COL-ENDCOL name the candidate's declaration line
+a.zig:2:5-13 verdigris verdigris,
+```
+
+The name comes first because that is the thing you would type — the row answers
+"what goes here" before "where does it come from", which is the order the
+question was asked in; every other kind here answers a WHERE, and for this one
+the location is the evidence rather than the answer. It is not padded into a
+column: the location in front of it is already ragged, so there is nothing to
+align to. That is a different and arguably better answer to "what goes here":
+you get the word AND you can read the definition rather than a list of words. It
+is the one location kind that does NOT jump on a single row, because with one
+candidate you still want to see the list rather than be teleported into it.
A results buffer is REFILLED rather than reopened when the same kind is asked
again — the rule `runSearch` always had, and which the language path was
@@ -232,9 +265,10 @@ read is in `req` (`path`, `source` (NUL-terminated), `offset`, `arg`, `root`).
`out` is a plain `std.Io.Writer`: the shell owns the buffer behind it (an
`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 and
-`lsp.lineCol()` to convert an offset, so every backend's rows are
-byte-identical in shape.
+`gpa` is for a backend's own scratch. Use `lsp.row()` to emit a location,
+`lsp.rel()` to spell its path against `req.root` and `lsp.lineCol()` to convert
+an offset, so every backend's rows are byte-identical in shape. `rel` allocates
+nothing — it returns a slice of what you hand it.
## How the implementations are judged