find_references
Locate all references to a symbol in Godot projects using LSP or text search to track code usage and dependencies.
Instructions
Find all references to a symbol at a given position. LSP-only (returns error without LSP). Falls back to text search.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| file | Yes | Path to the file containing the symbol | |
| line | Yes | Line number (1-based) | |
| character | No | Character offset (0-based) |
Implementation Reference
- src/tools/symbol-tools.ts:209-328 (handler)Handler for find_references tool. Uses LSP for accurate reference finding or falls back to basic regex-based text search if LSP is unavailable.
name: "find_references", description: "Find all references to a symbol at a given position. LSP-only (returns error without LSP). Falls back to text search.", schema: { file: z.string().describe("Path to the file containing the symbol"), line: z.number().int().min(1).describe("Line number (1-based)"), character: z .number() .int() .min(0) .optional() .default(0) .describe("Character offset (0-based)"), }, handler: async (ctx) => { const { file, line, character = 0 } = ctx.args; validatePath(file); const lsp = getLspClient(); const cache = getLspCache(); if (lsp?.connected) { const uri = `file://${file}`; const lspLine = line - 1; // Convert 1-based to 0-based const cacheKey = `${uri}:${lspLine}:${character}`; // Check cache first if (cache) { const cached = cache.getReferences(cacheKey); if (cached) { return makeTextResponse({ data: cached.map((l) => ({ file: l.uri.replace("file://", ""), line: l.range.start.line + 1, character: l.range.start.character, })), totalCount: cached.length, metadata: { source: "lsp-cache" }, }); } } try { const locations = await lsp.findReferences(uri, lspLine, character); // Cache the result if (cache) { cache.setReferences(cacheKey, locations); } return makeTextResponse({ data: locations.map((l) => ({ file: l.uri.replace("file://", ""), line: l.range.start.line + 1, character: l.range.start.character, })), totalCount: locations.length, metadata: { source: "lsp" }, }); } catch (err) { return makeTextResponse({ error: `LSP error: ${(err as Error).message}`, data: null, metadata: { source: "lsp" }, }); } } // Fallback: grep-style text search for the symbol name try { const source = readFileSync(file, "utf-8"); const lines = source.split("\n"); const targetLine = lines[line - 1] ?? ""; // Extract symbol name at position const wordMatch = targetLine.slice(character).match(/^(\w+)/); const symbolName = wordMatch?.[1]; if (!symbolName) { return makeTextResponse({ error: "Could not determine symbol name at position", data: null, }); } // Search all scripts for this symbol name const refs: Array<{ file: string; line: number; context: string; }> = []; for (const [scriptPath, _script] of await index.scriptIndex.all()) { try { const scriptSource = readFileSync(scriptPath, "utf-8"); const scriptLines = scriptSource.split("\n"); for (let i = 0; i < scriptLines.length; i++) { if (scriptLines[i].includes(symbolName)) { refs.push({ file: scriptPath, line: i + 1, context: scriptLines[i].trim(), }); } } } catch { // Skip unreadable files } } return makeTextResponse({ data: refs, totalCount: refs.length, metadata: { source: "fallback", note: "Text search only. LSP unavailable." }, }); } catch (err) { return makeTextResponse({ error: `Failed: ${(err as Error).message}`, data: null, }); } },