Class: Pikuri::Lsp::Sources
- Inherits:
-
Object
- Object
- Pikuri::Lsp::Sources
- Defined in:
- lib/pikuri/lsp/sources.rb
Overview
Where the bytes behind a result come from, and what a result is called. One instance per tool call, so the memo and the fetch budget are per call.
sources = Sources.new(filesystem: filesystem)
sources.label_for(location.uri)
# => "pikuri-workspace/lib/pikuri/workspace/filesystem.rb"
# => "org.slf4j.LoggerFactory (slf4j-api-2.0.18.jar)"
sources.snippet_for(location, client: client) # => "def resolve_for_read(path)"
Three tiers, keyed on the URI's scheme
There is no standard "read this URI" request in the LSP that ships today and no way to discover a server's non-standard one, so this is policy, not a lookup:
- +file:+ — the only scheme pikuri reads itself. In-root results carry
the workspace-relative path, and the read goes through the
Workspaceseam. Out-of-root results (the 45 gem hits onegoToDefinitionon a constant returned) always carry the line of code — "what does this library method do" is a top reason to want LSP at all — and carry a path only when this workspace would read that file, which is a per-wiring answer:AllowAll(the--yoloand OS-agent wirings) opens a gem checkout happily, a root-confined one refuses it, and quoting a path thereadtool will reject is a dead-end citation. Everyfile:URI takes a #denied? pass first — a result is a walked path, so nothing else stops an index hit under~/.sshfrom being quoted. - A server-private scheme — standard first, one hardcoded row behind
it. LSP 3.18's
workspace/textDocumentContentis real discovery (the server lists theschemesit will read) and wins where advertised. Nothing implements it yet, and a standard-only stance made the Java arm untestable, so SCHEME_REQUESTS carries exactly one row with a written deletion condition: delete thejdt:row the day jdtls advertises +textDocumentContent+, and the standard path above it takes over with no other edit. The bar for adding a row is a server someone wired and can test. - +http(s):+ and any scheme with no row — printed, never dereferenced. The load-bearing refusal: fetching a URI the server chose is egress with the payload in the URL, and no network row may ever enter the table. A scheme with no row still gets the inline reason, never a bare count.
One reply of attached Java source measured 22,639 bytes in 10 ms — cheap to fetch, ruinous to inline — so content is sliced to the line the result points at, memoized per URI (a 45-hit answer repeats URIs) and capped at MAX_FETCHES documents per call.
Constant Summary collapse
- MAX_FETCHES =
Documents one tool call may fetch over the wire. A cap rather than a budget because the failure it prevents is one call inlining a jar.
5- MAX_SNIPPET_CHARS =
Characters of a source line a snippet keeps. A row is one line — not the result's whole
range, which for a reopened namespace is the whole file. 160- STANDARD_REQUEST =
LSP 3.18's client→server content request:
{uri}→{text}. 'workspace/textDocumentContent'- SCHEME_REQUESTS =
The scheme→request table. One row, and the deletion condition is in the class header.
java/classFileContentsanswers with a bare String where the standard request answers{text:}; #fetch_from_server accepts either, which is the whole reason both paths can share a call site. { 'jdt' => 'java/classFileContents' }.freeze
Instance Method Summary collapse
-
#actionable?(uri) ⇒ Boolean
Whether this workspace would actually open the document behind a
file:URI — what decides whether a citation may carry a link, since a link to a path thereadtool refuses is the dead-end citation this class exists to avoid. -
#denied?(uri) ⇒ Boolean
Whether this URI must be dropped rather than rendered.
-
#in_root_file?(uri) ⇒ Boolean
Whether this names a file under the workspace root — which is what decides whether a row carries a path the model can act on.
-
#initialize(filesystem:) ⇒ Sources
constructor
A new instance of Sources.
-
#label_for(uri) ⇒ String
What to call this URI in a rendered row.
-
#lines_for(uri, client: nil) ⇒ Array(Array<String>, nil), Array(nil, String)
The document behind a URI, memoized for this call.
-
#snippet_for(location, client: nil) ⇒ String
One line of code for a result, or the reason there is none.
Constructor Details
#initialize(filesystem:) ⇒ Sources
Returns a new instance of Sources.
69 70 71 72 73 74 75 |
# File 'lib/pikuri/lsp/sources.rb', line 69 def initialize(filesystem:) @filesystem = filesystem @root = filesystem.project_root.to_s @memo = {} @readable = {} @fetches = 0 end |
Instance Method Details
#actionable?(uri) ⇒ Boolean
Whether this workspace would actually open the document behind a file:
URI — what decides whether a citation may carry a link, since a link to
a path the read tool refuses is the dead-end citation this class exists
to avoid. Includes the #denied? pass, so it is safe to ask on its own.
114 115 116 117 |
# File 'lib/pikuri/lsp/sources.rb', line 114 def actionable?(uri) path = Uris.to_path(uri) !path.nil? && !@filesystem.denied?(path) && readable?(path) end |
#denied?(uri) ⇒ Boolean
Whether this URI must be dropped rather than rendered.
A result is a path the server walked to, so it reaches no
resolve_for_read and this check is the only thing standing between an
index hit under a credential root and the model's context. It fails
closed, never raises, and is exact — the file tools run as the real user.
129 130 131 132 |
# File 'lib/pikuri/lsp/sources.rb', line 129 def denied?(uri) path = Uris.to_path(uri) !path.nil? && @filesystem.denied?(path) end |
#in_root_file?(uri) ⇒ Boolean
Returns whether this names a file under the workspace root — which is what decides whether a row carries a path the model can act on.
101 102 103 104 |
# File 'lib/pikuri/lsp/sources.rb', line 101 def in_root_file?(uri) path = Uris.to_path(uri) !path.nil? && in_root?(path) end |
#label_for(uri) ⇒ String
What to call this URI in a rendered row.
86 87 88 89 90 91 92 93 94 95 |
# File 'lib/pikuri/lsp/sources.rb', line 86 def label_for(uri) path = Uris.to_path(uri) return relative(path) if path && in_root?(path) return out_of_root_label(path) if path scheme = Uris.scheme(uri) return jdt_label(uri) if scheme == 'jdt' truncate(uri, 200) end |
#lines_for(uri, client: nil) ⇒ Array(Array<String>, nil), Array(nil, String)
The document behind a URI, memoized for this call.
157 158 159 |
# File 'lib/pikuri/lsp/sources.rb', line 157 def lines_for(uri, client: nil) @memo[uri] ||= fetch(uri, client) end |
#snippet_for(location, client: nil) ⇒ String
One line of code for a result, or the reason there is none.
141 142 143 144 145 146 147 148 149 |
# File 'lib/pikuri/lsp/sources.rb', line 141 def snippet_for(location, client: nil) lines, reason = lines_for(location.uri, client: client) return "[#{reason}]" if lines.nil? line = lines[location.anchor.start.line - 1] return '[the server pointed past the end of this document]' if line.nil? truncate(line.strip, MAX_SNIPPET_CHARS) end |