MCPcopy Create free account
hub / github.com/colbymchenry/codegraph / extractQueryPaths

Function extractQueryPaths

src/search/query-paths.ts:189–287  ·  view source on GitHub ↗
(
  query: string,
  indexedPaths: readonly string[],
  opts: { maxPins?: number; maxMatchesPerSpan?: number } = {},
)

Source from the content-addressed store, hash-verified

187}
188
189export function extractQueryPaths(
190 query: string,
191 indexedPaths: readonly string[],
192 opts: { maxPins?: number; maxMatchesPerSpan?: number } = {},
193): QueryPathExtraction {
194 const maxPins = Math.max(1, opts.maxPins ?? 8);
195 const maxMatchesPerSpan = Math.max(1, opts.maxMatchesPerSpan ?? 3);
196
197 const passthrough: QueryPathExtraction = {
198 strippedQuery: query,
199 pinnedFiles: [],
200 unresolvedPathSpans: [],
201 };
202 if (!query.trim() || indexedPaths.length === 0) return passthrough;
203
204 // Lowercase view of the index, built once per call. Last writer wins on a
205 // case-colliding pair, which is the existing file-view behavior too.
206 const lowerToOriginal = new Map<string, string>();
207 for (const p of indexedPaths) lowerToOriginal.set(p.toLowerCase(), p);
208
209 const tokens = query.split(/\s+/).filter(Boolean);
210 const consumed = new Set<number>();
211 const pinned: string[] = [];
212 const pinnedSeen = new Set<string>();
213 const unresolved: string[] = [];
214 let candidatesExamined = 0;
215
216 for (let i = 0; i < tokens.length; i++) {
217 if (pinned.length >= maxPins) break;
218 if (candidatesExamined >= MAX_CANDIDATE_SPANS) break;
219 const stripped = stripWrapping(tokens[i]!);
220 if (stripped.length < 4) continue;
221 const hasSlash = /[/\\]/.test(stripped);
222 if (!hasSlash && !DOTTED_BASENAME.test(stripped)) continue;
223
224 const normalized = normalizeSpan(stripped);
225 if (!normalized) continue;
226 candidatesExamined++;
227
228 const { matches, ambiguous } = resolveSpan(
229 normalized.toLowerCase(), lowerToOriginal, maxMatchesPerSpan,
230 );
231 if (matches.length > 0) {
232 consumed.add(i);
233 for (const m of matches) {
234 if (pinnedSeen.has(m) || pinned.length >= maxPins) continue;
235 pinnedSeen.add(m);
236 pinned.push(m);
237 }
238 } else if (ambiguous || isClearlyPathShaped(normalized)) {
239 // A real path that didn't resolve to a usable set. Keeping it in the
240 // query is strictly worse — its fragments are what minted the junk
241 // matches this module exists to stop — so strip it and say so.
242 consumed.add(i);
243 if (unresolved.length < 4) unresolved.push(normalized);
244 }
245 // Anything else (`and/or`, `call/2`, `foo.Bar`) is not a path reference:
246 // leave the token for the normal matching pipeline.

Callers 2

handleExploreMethod · 0.90

Calls 9

stripWrappingFunction · 0.85
normalizeSpanFunction · 0.85
resolveSpanFunction · 0.85
isClearlyPathShapedFunction · 0.85
buildBasenameStemsFunction · 0.85
hasMethod · 0.80
getMethod · 0.65
setMethod · 0.45
joinMethod · 0.45

Tested by

no test coverage detected