Class: Pikuri::Lsp::Anchor
- Inherits:
-
Object
- Object
- Pikuri::Lsp::Anchor
- Defined in:
- lib/pikuri/lsp/anchor.rb
Overview
A resolved query point: the server to ask, the document, and the exact position inside it. Every position-based operation needs one, and the model is never asked for a column — Anchor.resolve derives it.
resolution = Anchor.resolve(servers: servers, filesystem: filesystem,
symbol: 'resolve_for_read', file: 'read.rb', line: 165)
resolution.anchors.first.params
# => {textDocument: {uri: "file:///…/read.rb"}, position: {line: 164, character: 14}}
Why there is no character parameter
Not because the arithmetic is hard, but because nothing in the model's
input contains a column: read prints line→text and grep prints
file:line:text. A column could only be invented, and being wrong is not
an error — one measured run had definition answering 0 locations from a
keyword column, another had references seven columns off returning
80,609 locations instead of 2.
The two ways in, and only one is reliable
symbol+file+line→ one line read, one string search, no index and no server involvement. Works on every server that will ever be wired.symbolalone →workspace/symbol, which fails in opposite directions per server: ruby-lsp scores the query against the fully qualified name with a 0.7 Jaro-Winkler threshold, so a bareConfirmerscores 0.455 and is never returned, while jdtls indexes no methods at all. Best effort, reported honestly when it resolves nothing.
The keyword trap, which is why Anchor.locate runs at both ends
A symbol's range routinely begins at the keyword — class Foo starts at
class — so feeding an index hit's range.start back in is the failure, not
the shortcut: prepareTypeHierarchy at the keyword column returned zero
items in 6 of 6 cases on ruby-lsp and one item every time at the symbol's own
column. jdtls disagrees about the details in both directions and needs the
same helper. So the column always comes from the line's text.
An anchor that resolves outside the root is dropped with its own note: every
position-based operation would otherwise need a document outside what the
servers index. What is lost is following a hop into a dependency, which
hover and the jdt: content path still answer.
Defined Under Namespace
Classes: Resolution
Constant Summary collapse
- MAX =
Anchors one call may fire. A name matching 14 declarations must not mean 14 fan-out queries, and a line mentioning the same identifier twice is queried at both columns on purpose (see locate).
5
Class Method Summary collapse
-
.locate(line_text, symbol) ⇒ Array<Integer>
1-based columns where
symboloccurs inline_text. -
.no_server_message(path) ⇒ String
The degrade string, naming the extension rather than the servers: which servers are installed is machine state the model cannot act on, and the file type is what the call got wrong.
-
.resolve(servers:, filesystem:, symbol:, file: nil, line: nil, ready: ->(_client) {}) ⇒ Resolution
Resolve the anchors for one call.
Instance Method Summary collapse
-
#params ⇒ Hash
The
{textDocument:, position:}every position-based request carries, encoded in this server's negotiatedpositionEncoding. -
#to_s ⇒ String
"read.rb:165:15"— how a result block is labelled when one line held the symbol twice and the two columns disagreed. -
#uri ⇒ String
The anchor document's
file:URI.
Class Method Details
.locate(line_text, symbol) ⇒ Array<Integer>
1-based columns where symbol occurs in line_text.
locate(' names = names.trim()', 'names') # => [5, 13]
Only occurrences bounded by non-identifier characters count, so greet
does not match inside greeting. There is deliberately no
any-occurrence fallback: a substring hit would anchor the query on a
different name and the server would answer confidently about it, which
is the one failure mode worth more than an empty answer. Columns are
character columns — the encoding conversion happens later, once, in
Position#to_wire.
99 100 101 102 103 104 105 106 107 |
# File 'lib/pikuri/lsp/anchor.rb', line 99 def self.locate(line_text, symbol) found = [] offset = 0 while (index = line_text.index(symbol, offset)) found << index + 1 if bounded?(line_text, index, symbol.length) offset = index + 1 end found end |
.no_server_message(path) ⇒ String
The degrade string, naming the extension rather than the servers: which servers are installed is machine state the model cannot act on, and the file type is what the call got wrong.
365 366 367 368 369 |
# File 'lib/pikuri/lsp/anchor.rb', line 365 def self.(path) extension = File.extname(path.to_s) subject = extension.empty? ? File.basename(path.to_s) : "#{extension} files" "no language server is configured for #{subject}" end |
.resolve(servers:, filesystem:, symbol:, file: nil, line: nil, ready: ->(_client) {}) ⇒ Resolution
Resolve the anchors for one call.
134 135 136 137 138 |
# File 'lib/pikuri/lsp/anchor.rb', line 134 def self.resolve(servers:, filesystem:, symbol:, file: nil, line: nil, ready: ->(_client) {}) return in_file(servers, filesystem, symbol, file, line, ready) if file in_index(servers, filesystem, symbol, ready) end |
Instance Method Details
#params ⇒ Hash
Returns the {textDocument:, position:} every position-based
request carries, encoded in this server's negotiated
positionEncoding.
72 73 74 75 |
# File 'lib/pikuri/lsp/anchor.rb', line 72 def params { textDocument: { uri: uri }, position: position.to_wire(line_text: line_text, encoding: client.position_encoding) } end |
#to_s ⇒ String
Returns "read.rb:165:15" — how a result block is labelled when
one line held the symbol twice and the two columns disagreed.
79 80 81 |
# File 'lib/pikuri/lsp/anchor.rb', line 79 def to_s "#{File.basename(path)}:#{position}" end |