{"version":3,"file":"symbols.d.ts","sourceRoot":"","sources":["../../../src/core/workspace/symbols.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;CACnB;AA+CD;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,eAAe,EAAE,CAyE9F;AAED,uEAAuE;AACvE,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,EAAE,CAmBnE;AAED,6EAA6E;AAC7E,wBAAgB,YAAY,CAAC,SAAS,EAAE,eAAe,EAAE,EAAE,GAAG,EAAE,cAAc,EAAE,GAAG,SAAS,GAAG,eAAe,EAAE,CAa/G","sourcesContent":["/**\n * Symbol extraction via a deterministic heuristic line-based parser, plus LSP\n * document-symbol integration. The heuristic parser provides lexical symbol\n * boundaries for common languages without requiring a language server; when an\n * LSP server is present, its document symbols are the preferred evidence source.\n */\n\nexport interface ExtractedSymbol {\n\tname: string;\n\tkind: string;\n\tlanguageId: string;\n\tstartLine: number;\n\tstartCharacter: number;\n\tendLine: number;\n\tendCharacter: number;\n\tqualifiedName?: string;\n\tcontainerSymbolId?: string;\n\tsignatureHash?: string;\n}\n\nexport interface LspSymbolInput {\n\tname: string;\n\tkind: string;\n\tstartLine: number;\n\tstartCharacter: number;\n\tendLine: number;\n\tendCharacter: number;\n\tcontainerName?: string;\n\tlanguageId: string;\n}\n\nconst KIND_BY_KEYWORD: Record<string, string> = {\n\tfunction: \"function\",\n\tfn: \"function\",\n\tfunc: \"function\",\n\tdef: \"function\",\n\tclass: \"class\",\n\tstruct: \"class\",\n\tinterface: \"interface\",\n\ttrait: \"interface\",\n\tenum: \"enum\",\n\tconstructor: \"constructor\",\n\t\"public class\": \"class\",\n\t\"private class\": \"class\",\n\t\"internal class\": \"class\",\n\t\"public interface\": \"interface\",\n\t\"public enum\": \"enum\",\n\t\"record struct\": \"class\",\n\trecord: \"class\",\n\tmacro: \"macro\",\n\tmodule: \"module\",\n\tnamespace: \"namespace\",\n\tpackage: \"package\",\n\ttype: \"type\",\n\tprotocol: \"interface\",\n\textension: \"extension\",\n};\n\nfunction extractNameFromDef(line: string): string {\n\t// Strip leading decorators/annotations and visibility.\n\tconst cleaned = line\n\t\t.replace(/^[@#].*/, \"\")\n\t\t.replace(/^[ \\t]+/, \"\")\n\t\t.replace(\n\t\t\t/^(async |public |private |protected |internal |static |final |abstract |export |default |virtual |override )+/g,\n\t\t\t\"\",\n\t\t);\n\tconst m = cleaned.match(\n\t\t/(?:function|func|fn|def|class|struct|interface|trait|enum|record|type|protocol|extension|namespace|module|package)\\s+([A-Za-z_$][\\w$]*)/,\n\t);\n\tif (m) return m[1];\n\tconst m2 = cleaned.match(/^([A-Za-z_$][\\w$]*)\\s*\\(/);\n\tif (m2) return m2[1];\n\treturn \"\";\n}\n\n/**\n * Heuristic symbol extraction for a supported language. Returns symbols ordered\n * by start line. Non-authoritative; used to bound chunking and to provide\n * parser-only symbols when no LSP server is present.\n */\nexport function extractSymbolsHeuristic(languageId: string, content: string): ExtractedSymbol[] {\n\tconst lines = content.replace(/^\\uFEFF/, \"\").split(/\\r?\\n/);\n\tconst symbols: ExtractedSymbol[] = [];\n\tconst indentStack: Array<{ indent: number; name: string; kind: string; startLine: number }> = [];\n\n\tconst keywordRe =\n\t\t/\\b(function|func|fn|def|class|struct|interface|trait|enum|record|type|protocol|extension|namespace|module|package)\\b/;\n\t// Only treat as a definition if the line declares a top-level-ish symbol.\n\tconst defRe =\n\t\t/^\\s*(?:async\\s+|public\\s+|private\\s+|protected\\s+|internal\\s+|static\\s+|final\\s+|abstract\\s+|export\\s+|default\\s+|virtual\\s+|override\\s+)*\\s*(function|func|fn|def|class|struct|interface|trait|enum|record|type|protocol|extension|namespace|module|package)\\b/;\n\n\tconst braceLanguages = new Set([\"typescript\", \"javascript\", \"csharp\", \"java\", \"go\", \"rust\", \"css\"]);\n\n\tfor (let i = 0; i < lines.length; i++) {\n\t\tconst raw = lines[i];\n\t\tconst indent = raw.length - raw.trimStart().length;\n\t\tif (defRe.test(raw)) {\n\t\t\tconst name = extractNameFromDef(raw);\n\t\t\tif (name) {\n\t\t\t\tconst kind =\n\t\t\t\t\tKIND_BY_KEYWORD[\n\t\t\t\t\t\traw\n\t\t\t\t\t\t\t.trim()\n\t\t\t\t\t\t\t.match(\n\t\t\t\t\t\t\t\t/\\b(function|func|fn|def|class|struct|interface|trait|enum|record|type|protocol|extension|namespace|module|package)\\b/,\n\t\t\t\t\t\t\t)?.[1] ?? \"\"\n\t\t\t\t\t] ?? \"other\";\n\t\t\t\t// Pop stack entries deeper than current indent.\n\t\t\t\twhile (indentStack.length && indentStack[indentStack.length - 1].indent >= indent) {\n\t\t\t\t\tconst top = indentStack.pop() as { indent: number; name: string; kind: string; startLine: number };\n\t\t\t\t\tconst existing = symbols.find((s) => s.name === top.name && s.startLine === top.startLine);\n\t\t\t\t\tif (existing) existing.endLine = Math.max(existing.endLine, i);\n\t\t\t\t}\n\t\t\t\tindentStack.push({ indent, name, kind, startLine: i });\n\t\t\t\tsymbols.push({\n\t\t\t\t\tname,\n\t\t\t\t\tkind,\n\t\t\t\t\tlanguageId,\n\t\t\t\t\tstartLine: i,\n\t\t\t\t\tstartCharacter: raw.indexOf(name),\n\t\t\t\t\tendLine: i + 1,\n\t\t\t\t\tendCharacter: 0,\n\t\t\t\t\tqualifiedName: name,\n\t\t\t\t});\n\t\t\t}\n\t\t} else if (braceLanguages.has(languageId) && indentStack.length && /\\{\\s*$/.test(raw)) {\n\t\t\t// Extend the current top symbol's end to the closing brace later.\n\t\t\tvoid indent;\n\t\t\tvoid keywordRe;\n\t\t}\n\t}\n\n\t// Close brace matching: extend symbol endLine to matching closing brace.\n\tif (braceLanguages.has(languageId)) {\n\t\tfor (const sym of symbols) {\n\t\t\tlet depth = 0;\n\t\t\tlet end = sym.endLine;\n\t\t\tfor (let j = sym.startLine; j < lines.length; j++) {\n\t\t\t\tconst line = lines[j];\n\t\t\t\tconst opens = (line.match(/{/g) || []).length;\n\t\t\t\tconst closes = (line.match(/}/g) || []).length;\n\t\t\t\tdepth += opens - closes;\n\t\t\t\tif (depth <= 0 && closes > 0) {\n\t\t\t\t\tend = j + 1;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (depth > 0) end = j + 1;\n\t\t\t}\n\t\t\tsym.endLine = Math.max(sym.endLine, end);\n\t\t}\n\t}\n\n\treturn symbols;\n}\n\n/** Map string symbols for markdown (headers) and structured config. */\nexport function markdownSections(content: string): ExtractedSymbol[] {\n\tconst lines = content.replace(/^\\uFEFF/, \"\").split(/\\r?\\n/);\n\tconst out: ExtractedSymbol[] = [];\n\tfor (let i = 0; i < lines.length; i++) {\n\t\tconst m = lines[i].match(/^(#{1,6})\\s+(.+)/);\n\t\tif (m) {\n\t\t\tout.push({\n\t\t\t\tname: m[2].trim(),\n\t\t\t\tkind: \"heading\",\n\t\t\t\tlanguageId: \"markdown\",\n\t\t\t\tstartLine: i,\n\t\t\t\tstartCharacter: lines[i].indexOf(m[2].trim()),\n\t\t\t\tendLine: Math.min(i + 40, lines.length - 1),\n\t\t\t\tendCharacter: 0,\n\t\t\t\tqualifiedName: m[2].trim(),\n\t\t\t});\n\t\t}\n\t}\n\treturn out;\n}\n\n/** Merge heuristic symbols with LSP symbols, preferring LSP when present. */\nexport function mergeSymbols(heuristic: ExtractedSymbol[], lsp: LspSymbolInput[] | undefined): ExtractedSymbol[] {\n\tif (!lsp || lsp.length === 0) return heuristic;\n\tconst mapped: ExtractedSymbol[] = lsp.map((s) => ({\n\t\tname: s.name,\n\t\tkind: s.kind,\n\t\tlanguageId: s.languageId,\n\t\tstartLine: s.startLine,\n\t\tstartCharacter: s.startCharacter,\n\t\tendLine: s.endLine,\n\t\tendCharacter: s.endCharacter,\n\t\tqualifiedName: s.containerName ? `${s.containerName}.${s.name}` : s.name,\n\t}));\n\treturn mapped.length > 0 ? mapped : heuristic;\n}\n"]}