pi-lens 4.4.3 → 4.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +281 -0
- package/README.md +13 -1
- package/dist/{chunk-SS36ZX4J.js → chunk-24AKHFGP.js} +6 -6
- package/dist/{chunk-JSQFTSC6.js → chunk-2FN63LPV.js} +11 -11
- package/dist/{chunk-5UCAH366.js → chunk-2KXTHF3M.js} +20 -20
- package/dist/{chunk-EU63SDCG.js → chunk-3DA2JDOL.js} +367 -281
- package/dist/{chunk-RWWYJ623.js → chunk-3KAZU3VR.js} +1 -1
- package/dist/chunk-3PTHXJXR.js +28 -0
- package/dist/chunk-3QED2P4V.js +31 -0
- package/dist/{chunk-N2H3ZPZY.js → chunk-3RKP3FMS.js} +1 -0
- package/dist/{chunk-XVF2VVXP.js → chunk-5HSRT26B.js} +2 -2
- package/dist/{chunk-MXGY6KPT.js → chunk-5IUSNGOC.js} +15 -15
- package/dist/{chunk-EJIUXPBX.js → chunk-5OFV52AM.js} +1 -1
- package/dist/{chunk-AW7R2GWG.js → chunk-5VSF4UUQ.js} +12 -7
- package/dist/{chunk-6STA6Z7I.js → chunk-5WRPDP2A.js} +18 -18
- package/dist/{chunk-YIUAVF7E.js → chunk-6DLWKFEX.js} +35 -34
- package/dist/{chunk-WOA4LN2R.js → chunk-7D6L4MTX.js} +2 -2
- package/dist/{chunk-GAEGEPOC.js → chunk-7HXE67BN.js} +2 -2
- package/dist/chunk-AGSS7277.js +84 -0
- package/dist/{chunk-R3PCY3YS.js → chunk-AMERCH6V.js} +13 -13
- package/dist/{chunk-WU567RSC.js → chunk-ATKJFDQN.js} +14 -10
- package/dist/{chunk-VSSBEUFT.js → chunk-AWSCPCN2.js} +16 -16
- package/dist/{chunk-VCHXCQFG.js → chunk-BWD5EPKO.js} +3 -3
- package/dist/{chunk-H6MF6EVN.js → chunk-CT4MN5IU.js} +1200 -230
- package/dist/{chunk-FCSOVF55.js → chunk-CUHSHSB6.js} +6 -6
- package/dist/{chunk-CHOW74WE.js → chunk-CYRKQRPM.js} +7 -10
- package/dist/{chunk-Q7DUK4LX.js → chunk-DEL2U6CM.js} +5 -5
- package/dist/{chunk-6C7JWPCP.js → chunk-DGUKIYSI.js} +26 -23
- package/dist/{chunk-PHDMFAKM.js → chunk-DUO2TU77.js} +2 -2
- package/dist/{chunk-MRXKYPCS.js → chunk-EBC36LNN.js} +2 -2
- package/dist/{chunk-CSHFH5IN.js → chunk-ED36RF6Y.js} +1 -1
- package/dist/{chunk-76MWC2K6.js → chunk-EUXZEFTV.js} +90 -97
- package/dist/{chunk-QOZXJ4NA.js → chunk-FM4RDMKT.js} +11 -8
- package/dist/{chunk-ACXEQZOU.js → chunk-GAQ7OQG6.js} +31 -28
- package/dist/{chunk-XAGLWMJ4.js → chunk-GL5LUMZE.js} +23 -23
- package/dist/{chunk-BKE7E7TC.js → chunk-GMX5OS7B.js} +9 -9
- package/dist/{chunk-ZHFZWTZV.js → chunk-GSLNCYGG.js} +11 -11
- package/dist/{chunk-3W7IEVVL.js → chunk-GSM5UU7A.js} +26 -34
- package/dist/{chunk-724UL2TJ.js → chunk-GXFV6MYU.js} +3 -3
- package/dist/chunk-HAD7QRQI.js +30 -0
- package/dist/{chunk-QOBUHWHX.js → chunk-HRNHZA3N.js} +6 -6
- package/dist/{chunk-VWCXW2E7.js → chunk-HWCCCQNX.js} +1 -1
- package/dist/{chunk-7DYBLXYA.js → chunk-HYGZEMX6.js} +3 -3
- package/dist/chunk-IJYGIDF4.js +186 -0
- package/dist/{chunk-4W4VLAPJ.js → chunk-IQWN5JCP.js} +11 -11
- package/dist/{chunk-SQ4TVJBR.js → chunk-IUE7SSKV.js} +7 -7
- package/dist/{chunk-IBGIOBEE.js → chunk-IWDLU6K3.js} +68 -5
- package/dist/{chunk-XCJN5IJU.js → chunk-IYNJ6YPZ.js} +4 -4
- package/dist/{chunk-JAU72XNY.js → chunk-J64T55EI.js} +3 -3
- package/dist/{chunk-2BBLFX75.js → chunk-J6MPGQTS.js} +5 -5
- package/dist/chunk-JORED7EX.js +44 -0
- package/dist/{chunk-6SDEP3AJ.js → chunk-JUKMK5Y4.js} +8 -8
- package/dist/{chunk-FMSO6RDZ.js → chunk-JW3YWBVB.js} +15 -15
- package/dist/{chunk-L3BLXZG7.js → chunk-KAPJINKN.js} +30 -69
- package/dist/{chunk-2B73ZPOK.js → chunk-KN35K52N.js} +3 -3
- package/dist/{chunk-C34ZXZ7O.js → chunk-KUA4OJ5Y.js} +24 -11
- package/dist/chunk-KYUB6QDC.js +72 -0
- package/dist/{chunk-67WMNWEE.js → chunk-LLJNQKQW.js} +12 -10
- package/dist/chunk-LZSEQW6A.js +57 -0
- package/dist/{chunk-ZANLILYD.js → chunk-M2IFVFWU.js} +2 -2
- package/dist/chunk-M2UD3LPU.js +88 -0
- package/dist/{chunk-XIJZVPD5.js → chunk-MBKQHGA7.js} +15 -15
- package/dist/{chunk-P7266HLM.js → chunk-MPNJLASA.js} +2 -2
- package/dist/{chunk-6GFRNVYR.js → chunk-MZGQNL3Y.js} +10 -10
- package/dist/{chunk-ZWFS3OEF.js → chunk-N5FLEO7W.js} +33 -32
- package/dist/{chunk-3JNKMP3B.js → chunk-O4NWQT6I.js} +4 -4
- package/dist/{chunk-T6CPIZNT.js → chunk-OHKJ7PQF.js} +33 -32
- package/dist/{chunk-VRZP76YM.js → chunk-P63GXC37.js} +4 -1
- package/dist/{chunk-XTMJ7XDT.js → chunk-POMLXPMY.js} +1 -1
- package/dist/{chunk-QQNQ7SJW.js → chunk-QBFBRV2B.js} +10 -4
- package/dist/{chunk-CMMHNUZ2.js → chunk-R3T7CLQ2.js} +60 -38
- package/dist/{chunk-23WOO7TX.js → chunk-R7YOCSNV.js} +10 -10
- package/dist/{chunk-2CD5RS6M.js → chunk-RKBXZN3A.js} +23 -2
- package/dist/{chunk-OCAGUNBC.js → chunk-RKKOYPRA.js} +12 -12
- package/dist/{chunk-52FUZI6K.js → chunk-RMLC67LF.js} +1 -1
- package/dist/{chunk-XMSXXAHW.js → chunk-ROYSX2S5.js} +2 -2
- package/dist/{chunk-M6HSLBWY.js → chunk-RVK72CZX.js} +6507 -7514
- package/dist/{chunk-FLKWCIZH.js → chunk-S2HEXIWI.js} +5 -5
- package/dist/{chunk-UZ3TUTRV.js → chunk-S67KYG37.js} +1 -1
- package/dist/{chunk-VCO24T4L.js → chunk-SD4I7ZO4.js} +14 -14
- package/dist/{chunk-Z527N6YQ.js → chunk-SKAMFC7A.js} +12 -12
- package/dist/{chunk-MQLUAEOC.js → chunk-SRYEJNES.js} +2 -2
- package/dist/{chunk-BRGDSBJE.js → chunk-SYE6T2EB.js} +12 -12
- package/dist/{chunk-GXW6K4YJ.js → chunk-T57LYLZT.js} +1 -1
- package/dist/{chunk-ZXYDQ3TZ.js → chunk-T7FEBKXN.js} +48 -15
- package/dist/{chunk-3EFKIGZN.js → chunk-TIQ2PKDW.js} +3 -3
- package/dist/{chunk-QILIYSJF.js → chunk-TLX72JTG.js} +1 -1
- package/dist/{chunk-TYM6OUIT.js → chunk-TOY6QKOH.js} +12 -12
- package/dist/{chunk-IZSHAIJ7.js → chunk-TRETHTQO.js} +12 -12
- package/dist/{chunk-3VPVG5SU.js → chunk-U44DJXQP.js} +10 -6
- package/dist/{chunk-PNQDPDRC.js → chunk-UA7ZXYDR.js} +1 -1
- package/dist/{chunk-OXXKQCSV.js → chunk-UBOQLVT2.js} +4 -4
- package/dist/{chunk-QGQ5CCCG.js → chunk-UMS2MFPZ.js} +2 -2
- package/dist/{chunk-QHJ6DQ2R.js → chunk-US7ECLD7.js} +55 -13
- package/dist/{chunk-AB3YYG6N.js → chunk-UZ2XCLYS.js} +8 -8
- package/dist/chunk-V35JG4JB.js +2667 -0
- package/dist/{chunk-N3X6Q4YT.js → chunk-VOOAPROD.js} +3 -3
- package/dist/{chunk-SAEFPSB6.js → chunk-W66O5E25.js} +21 -11
- package/dist/{chunk-KBG47GHP.js → chunk-WMYE35PX.js} +15 -11
- package/dist/{chunk-5NGR67U5.js → chunk-XBJVAEEO.js} +32 -5
- package/dist/{chunk-YYOIQFRS.js → chunk-XHJBS436.js} +122 -44
- package/dist/{chunk-GBPP5P7X.js → chunk-XTBDCR27.js} +4 -4
- package/dist/{chunk-4UNXBNE2.js → chunk-XTFDJI3T.js} +11 -11
- package/dist/{chunk-UXGETRP4.js → chunk-XYURS6EN.js} +13 -12
- package/dist/{chunk-RPLINGVB.js → chunk-Y6GZHWSJ.js} +6 -6
- package/dist/{chunk-2VUQMUMT.js → chunk-YCWYO6TQ.js} +41 -38
- package/dist/{chunk-77M37OKR.js → chunk-YH5MVY2U.js} +7 -7
- package/dist/{chunk-AOHZU2BM.js → chunk-YNE3RP52.js} +1 -1
- package/dist/{chunk-YSZNHOBY.js → chunk-ZHH37KZL.js} +12 -12
- package/dist/{chunk-ZGDGW2V3.js → chunk-ZIVJBOO3.js} +2 -2
- package/dist/{chunk-RXCOMYPY.js → chunk-ZVUL7QYY.js} +29 -28
- package/dist/index.js +11052 -5852
- package/dist/mcp/analyze-cli.js +10 -9
- package/dist/mcp/cli.js +41 -38
- package/dist/mcp/server.js +1120 -474
- package/dist/mcp/worker.js +61 -58
- package/dist/probes/installer.js +19 -19
- package/dist/workers/project-snapshot-persist-worker.js +28 -9
- package/dist/workers/review-graph-persist-worker.js +1 -1
- package/docs/configuration.md +11 -0
- package/docs/engineering-principles.md +32 -0
- package/docs/lsp-capability-matrix.md +1 -1
- package/docs/lsp-idle-eviction.md +15 -26
- package/docs/pi-lens-fixer.md +117 -13
- package/docs/pi-lens-merge-policy.md +97 -7
- package/docs/pi-lens-monitor.md +8 -2
- package/docs/pi-lens-retro.md +4 -1
- package/docs/pi-lens-reviewer.md +112 -4
- package/docs/pi-lens-subagent.md +22 -14
- package/docs/release-qa-baseline.md +3 -1
- package/docs/servercapabilities.md +9 -19
- package/package.json +7 -5
- package/scripts/download-grammars.js +40 -7
- package/scripts/lib/latency-log-phases.mjs +95 -0
- package/scripts/rpc-load-check.mjs +570 -81
- package/dist/chunk-3YN6V5TV.js +0 -85
- package/dist/chunk-466Z26MG.js +0 -30
- package/dist/chunk-EC47DWFA.js +0 -56
- package/dist/chunk-KDERNTCQ.js +0 -28
- package/dist/chunk-KSX3TXSZ.js +0 -69
- package/dist/chunk-LOP6GRPH.js +0 -31
- package/dist/chunk-UOPVJQ4N.js +0 -103
- /package/dist/{chunk-FC4RP4I2.js → chunk-6HWVI6MI.js} +0 -0
|
@@ -0,0 +1,2667 @@
|
|
|
1
|
+
import { createRequire as __pilensCreateRequire } from "node:module"; const require = __pilensCreateRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
buildModuleGraph
|
|
4
|
+
} from "./chunk-XTBDCR27.js";
|
|
5
|
+
import {
|
|
6
|
+
getSharedTreeSitterClient,
|
|
7
|
+
loadWebTreeSitter,
|
|
8
|
+
logTreeSitterDiagnostic,
|
|
9
|
+
resolveTreeSitterLanguage,
|
|
10
|
+
wasmQueryInput
|
|
11
|
+
} from "./chunk-US7ECLD7.js";
|
|
12
|
+
import {
|
|
13
|
+
EXTENSION_TO_GRAMMAR,
|
|
14
|
+
KIND_TO_GRAMMAR
|
|
15
|
+
} from "./chunk-GXFV6MYU.js";
|
|
16
|
+
import {
|
|
17
|
+
HOOK_WALL_BUDGET_MS,
|
|
18
|
+
bounded
|
|
19
|
+
} from "./chunk-RMLC67LF.js";
|
|
20
|
+
import {
|
|
21
|
+
freshnessFromMtime,
|
|
22
|
+
hashContentBytes,
|
|
23
|
+
toRunnerDisplayPath
|
|
24
|
+
} from "./chunk-AGSS7277.js";
|
|
25
|
+
import {
|
|
26
|
+
loadPiLensGlobalConfig
|
|
27
|
+
} from "./chunk-KUA4OJ5Y.js";
|
|
28
|
+
import {
|
|
29
|
+
isFileKind
|
|
30
|
+
} from "./chunk-DUO2TU77.js";
|
|
31
|
+
import {
|
|
32
|
+
isDeclarationFile
|
|
33
|
+
} from "./chunk-J6MPGQTS.js";
|
|
34
|
+
import {
|
|
35
|
+
findGoverningTsconfigDir,
|
|
36
|
+
getDirectoryMarkers,
|
|
37
|
+
registerWorkspaceTopologyReset
|
|
38
|
+
} from "./chunk-5HSRT26B.js";
|
|
39
|
+
import {
|
|
40
|
+
incrementDegradationCount,
|
|
41
|
+
recordDegradationOnce
|
|
42
|
+
} from "./chunk-J64T55EI.js";
|
|
43
|
+
import {
|
|
44
|
+
toPositiveFinite
|
|
45
|
+
} from "./chunk-YNE3RP52.js";
|
|
46
|
+
import {
|
|
47
|
+
BoundedFifoMap,
|
|
48
|
+
BoundedLruCache
|
|
49
|
+
} from "./chunk-ABBOA7UD.js";
|
|
50
|
+
import {
|
|
51
|
+
isUnderDir,
|
|
52
|
+
normalizeEphemeralMapKey,
|
|
53
|
+
normalizeMapKey
|
|
54
|
+
} from "./chunk-3RKP3FMS.js";
|
|
55
|
+
|
|
56
|
+
// dist/clients/blocker-freshness.js
|
|
57
|
+
import * as fs5 from "node:fs";
|
|
58
|
+
import * as nodePath from "node:path";
|
|
59
|
+
|
|
60
|
+
// dist/clients/lsp/implicit-scope.js
|
|
61
|
+
import * as fs from "node:fs";
|
|
62
|
+
import * as path from "node:path";
|
|
63
|
+
|
|
64
|
+
// dist/clients/deps/ast-grep-napi.js
|
|
65
|
+
import { createRequire } from "node:module";
|
|
66
|
+
import { pathToFileURL } from "node:url";
|
|
67
|
+
var _require = createRequire(import.meta.url);
|
|
68
|
+
function loadAstGrepNapi() {
|
|
69
|
+
try {
|
|
70
|
+
const entry = _require.resolve("@ast-grep/napi");
|
|
71
|
+
return import(pathToFileURL(entry).href);
|
|
72
|
+
} catch {
|
|
73
|
+
return import("@ast-grep/napi");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function loadAstGrepNapiSync() {
|
|
77
|
+
try {
|
|
78
|
+
return _require("@ast-grep/napi");
|
|
79
|
+
} catch {
|
|
80
|
+
return void 0;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// dist/clients/runtime-config.js
|
|
85
|
+
var _runnerTimeoutFloorCache;
|
|
86
|
+
function getRunnerTimeoutFloorMs() {
|
|
87
|
+
if (_runnerTimeoutFloorCache !== void 0)
|
|
88
|
+
return _runnerTimeoutFloorCache;
|
|
89
|
+
const config = loadPiLensGlobalConfig();
|
|
90
|
+
const configFloor = toPositiveFinite(config?.dispatch?.runnerTimeoutFloorMs);
|
|
91
|
+
const envFloor = toPositiveFinite(process.env.PI_LENS_RUNNER_TIMEOUT_FLOOR_MS);
|
|
92
|
+
_runnerTimeoutFloorCache = Math.max(configFloor, envFloor, 0);
|
|
93
|
+
return _runnerTimeoutFloorCache;
|
|
94
|
+
}
|
|
95
|
+
var RUNTIME_CONFIG = {
|
|
96
|
+
pipeline: {
|
|
97
|
+
lspMaxFileBytes: 2 * 1024 * 1024,
|
|
98
|
+
lspMaxFileLines: 5e3,
|
|
99
|
+
cascadeMaxFiles: 5,
|
|
100
|
+
cascadeMaxDiagnosticsPerFile: 20,
|
|
101
|
+
// Hard cap on how long the pipeline will wait for an LSP client to spawn.
|
|
102
|
+
// Keeps tool_result from blocking the TUI during cold LSP start (e.g.
|
|
103
|
+
// pyright workspace indexing). The LSP server continues spawning in the
|
|
104
|
+
// background; subsequent edits get full diagnostics once it is ready.
|
|
105
|
+
lspSpawnBudgetMs: 5e3
|
|
106
|
+
},
|
|
107
|
+
dispatch: {
|
|
108
|
+
runnerTimeoutMs: 3e4
|
|
109
|
+
},
|
|
110
|
+
crashNotice: {
|
|
111
|
+
alwaysShowFirstN: 2,
|
|
112
|
+
showEveryNth: 5
|
|
113
|
+
},
|
|
114
|
+
reviewGraph: {
|
|
115
|
+
// Deprecated (#776): no longer read by `review-graph/builder.ts`, which
|
|
116
|
+
// now derives its fallback from `project-scale.ts`'s `maxProjectFiles`
|
|
117
|
+
// knob (reproducing this same 1,000 default at the default base). Left
|
|
118
|
+
// in place only in case an external caller still reads the constant.
|
|
119
|
+
maxFiles: 1e3,
|
|
120
|
+
maxFileBytes: 1 * 1024 * 1024
|
|
121
|
+
},
|
|
122
|
+
turnEnd: {
|
|
123
|
+
maxLines: 20,
|
|
124
|
+
maxChars: 1e3
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// dist/clients/lsp/content-limits.js
|
|
129
|
+
var LSP_MAX_FILE_BYTES = RUNTIME_CONFIG.pipeline.lspMaxFileBytes;
|
|
130
|
+
var LSP_MAX_FILE_LINES = RUNTIME_CONFIG.pipeline.lspMaxFileLines;
|
|
131
|
+
function exceedsLspSyncLimits(content) {
|
|
132
|
+
const sizeBytes = Buffer.byteLength(content, "utf-8");
|
|
133
|
+
if (sizeBytes > LSP_MAX_FILE_BYTES) {
|
|
134
|
+
return {
|
|
135
|
+
tooLarge: true,
|
|
136
|
+
reason: `${sizeBytes} bytes > ${LSP_MAX_FILE_BYTES} limit`
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
const lineCount = content.split("\n").length;
|
|
140
|
+
if (lineCount > LSP_MAX_FILE_LINES) {
|
|
141
|
+
return {
|
|
142
|
+
tooLarge: true,
|
|
143
|
+
reason: `${lineCount} lines > ${LSP_MAX_FILE_LINES} limit`
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
return { tooLarge: false, reason: "" };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// dist/clients/lsp/implicit-scope.js
|
|
150
|
+
function isGoSourceFile(filePath) {
|
|
151
|
+
return path.extname(filePath).toLowerCase() === ".go";
|
|
152
|
+
}
|
|
153
|
+
function seesJsTsGlobalScope(filePath) {
|
|
154
|
+
return isFileKind(filePath, "jsts");
|
|
155
|
+
}
|
|
156
|
+
function inOneGoPackage(a, b) {
|
|
157
|
+
return isGoSourceFile(a) && isGoSourceFile(b) && path.posix.dirname(a) === path.posix.dirname(b);
|
|
158
|
+
}
|
|
159
|
+
function recordUnknownRole(filePath, cause, err) {
|
|
160
|
+
incrementDegradationCount({
|
|
161
|
+
kind: "lsp-implicit-scope-unknown",
|
|
162
|
+
subject: cause,
|
|
163
|
+
reason: `${cause}: ${filePath}${err === void 0 ? "" : ` (${err instanceof Error ? err.message : String(err)})`} \u2014 unresolved, so the diagnostics cache refuses its entry and the blocker sweep and the cascade see no role for it`,
|
|
164
|
+
metadata: {
|
|
165
|
+
file: filePath,
|
|
166
|
+
cause,
|
|
167
|
+
...err === void 0 ? {} : { err: err instanceof Error ? err.message : String(err) }
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
function goPackageFiles(dir) {
|
|
172
|
+
let entries;
|
|
173
|
+
try {
|
|
174
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
175
|
+
} catch (err) {
|
|
176
|
+
recordUnknownRole(dir, "unreadable-go-dir", err);
|
|
177
|
+
return void 0;
|
|
178
|
+
}
|
|
179
|
+
return entries.flatMap((entry) => entry.isFile() && isGoSourceFile(entry.name) ? [path.join(dir, entry.name)] : []);
|
|
180
|
+
}
|
|
181
|
+
function implicitScopeOf(filePath, feeds = feedsJsTsGlobalScope) {
|
|
182
|
+
if (isGoSourceFile(filePath)) {
|
|
183
|
+
return (other) => other !== filePath && inOneGoPackage(filePath, other);
|
|
184
|
+
}
|
|
185
|
+
if (seesJsTsGlobalScope(filePath)) {
|
|
186
|
+
return (other) => other !== filePath && feeds(other);
|
|
187
|
+
}
|
|
188
|
+
return void 0;
|
|
189
|
+
}
|
|
190
|
+
function goPackageChangedAt(dir) {
|
|
191
|
+
try {
|
|
192
|
+
let changedAt = fs.statSync(dir).mtimeMs;
|
|
193
|
+
for (const peer of goPackageFiles(dir) ?? []) {
|
|
194
|
+
changedAt = Math.max(changedAt, fs.statSync(peer).mtimeMs);
|
|
195
|
+
}
|
|
196
|
+
return changedAt;
|
|
197
|
+
} catch (err) {
|
|
198
|
+
recordUnknownRole(dir, "unreadable-mtime", err);
|
|
199
|
+
return void 0;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
var GO_HEADER_BYTES = 8 * 1024;
|
|
203
|
+
var KNOWN_GOOS = new Set("aix android darwin dragonfly freebsd hurd illumos ios js linux netbsd openbsd plan9 solaris wasip1 windows zos".split(" "));
|
|
204
|
+
var KNOWN_GOARCH = new Set("386 amd64 arm arm64 loong64 mips mips64 mips64le mipsle ppc64 ppc64le riscv64 s390x wasm".split(" "));
|
|
205
|
+
var GOOS_BY_PLATFORM = {
|
|
206
|
+
win32: "windows",
|
|
207
|
+
sunos: "solaris"
|
|
208
|
+
};
|
|
209
|
+
var GOARCH_BY_ARCH = { x64: "amd64", ia32: "386" };
|
|
210
|
+
var UNIX_GOOS = new Set("aix android darwin dragonfly freebsd hurd illumos ios linux netbsd openbsd solaris".split(" "));
|
|
211
|
+
function hostSatisfiesGoTag(tag) {
|
|
212
|
+
const goos = process.env.GOOS || GOOS_BY_PLATFORM[process.platform] || process.platform;
|
|
213
|
+
const goarch = process.env.GOARCH || GOARCH_BY_ARCH[process.arch] || process.arch;
|
|
214
|
+
return tag === goos || tag === goarch || tag === "unix" && UNIX_GOOS.has(goos);
|
|
215
|
+
}
|
|
216
|
+
function goBuildExcludesHost(filePath, header) {
|
|
217
|
+
const parts = path.basename(filePath, ".go").replace(/_test$/, "").split("_");
|
|
218
|
+
const last = parts.at(-1) ?? "";
|
|
219
|
+
const prev = parts.length > 2 ? parts.at(-2) ?? "" : "";
|
|
220
|
+
if (parts.length > 1 && KNOWN_GOARCH.has(last)) {
|
|
221
|
+
if (!hostSatisfiesGoTag(last))
|
|
222
|
+
return true;
|
|
223
|
+
if (KNOWN_GOOS.has(prev) && !hostSatisfiesGoTag(prev))
|
|
224
|
+
return true;
|
|
225
|
+
} else if (parts.length > 1 && KNOWN_GOOS.has(last) && !hostSatisfiesGoTag(last)) {
|
|
226
|
+
return true;
|
|
227
|
+
}
|
|
228
|
+
const line = /^\/\/go:build[ \t](.*)$/m.exec(header);
|
|
229
|
+
if (!line)
|
|
230
|
+
return /^\/\/[ \t]*\+build[ \t]/m.test(header);
|
|
231
|
+
const term = /^\s*(!?)([\w.]+)\s*$/.exec(line[1]);
|
|
232
|
+
const tag = term?.[2] ?? "";
|
|
233
|
+
if (!(KNOWN_GOOS.has(tag) || KNOWN_GOARCH.has(tag) || tag === "unix"))
|
|
234
|
+
return true;
|
|
235
|
+
return hostSatisfiesGoTag(tag) === (term?.[1] === "!");
|
|
236
|
+
}
|
|
237
|
+
function goHeaderOf(filePath) {
|
|
238
|
+
let fd;
|
|
239
|
+
try {
|
|
240
|
+
fd = fs.openSync(filePath, "r");
|
|
241
|
+
const buffer = Buffer.alloc(GO_HEADER_BYTES);
|
|
242
|
+
const read = fs.readSync(fd, buffer, 0, GO_HEADER_BYTES, 0);
|
|
243
|
+
return buffer.toString("utf8", 0, read);
|
|
244
|
+
} catch (err) {
|
|
245
|
+
recordUnknownRole(filePath, "unreadable-go-peer", err);
|
|
246
|
+
return void 0;
|
|
247
|
+
} finally {
|
|
248
|
+
if (fd !== void 0)
|
|
249
|
+
fs.closeSync(fd);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
function goPackageClauseOf(header) {
|
|
253
|
+
const code = header.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/\/\/[^\n]*/g, "");
|
|
254
|
+
return /^\s*package\s+([\p{L}_][\p{L}\p{N}_]*)/mu.exec(code)?.[1];
|
|
255
|
+
}
|
|
256
|
+
function goPackagePeersOf(filePath) {
|
|
257
|
+
const inScope = isGoSourceFile(filePath) ? implicitScopeOf(filePath) : void 0;
|
|
258
|
+
const ownHeader = inScope && goHeaderOf(filePath);
|
|
259
|
+
const clause = ownHeader ? goPackageClauseOf(ownHeader) : void 0;
|
|
260
|
+
if (!inScope || !clause)
|
|
261
|
+
return void 0;
|
|
262
|
+
const testFile = isGoTestFile(filePath);
|
|
263
|
+
return (peer) => {
|
|
264
|
+
if (!inScope(peer) || isGoTestFile(peer) && !testFile)
|
|
265
|
+
return false;
|
|
266
|
+
const header = goHeaderOf(peer);
|
|
267
|
+
return header !== void 0 && goPackageClauseOf(header) === clause && !goBuildExcludesHost(peer, header);
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
function isGoTestFile(filePath) {
|
|
271
|
+
return filePath.toLowerCase().endsWith("_test.go");
|
|
272
|
+
}
|
|
273
|
+
function probeFor(filePath) {
|
|
274
|
+
switch (path.extname(filePath).toLowerCase()) {
|
|
275
|
+
case ".ts":
|
|
276
|
+
case ".tsx":
|
|
277
|
+
return "ts-script-or-ambient";
|
|
278
|
+
// ES and CommonJS module extensions: never a script, but a TS one can
|
|
279
|
+
// still hold `declare global`.
|
|
280
|
+
case ".mts":
|
|
281
|
+
case ".cts":
|
|
282
|
+
return "ambient";
|
|
283
|
+
case ".js":
|
|
284
|
+
case ".jsx":
|
|
285
|
+
return "js-script";
|
|
286
|
+
default:
|
|
287
|
+
return void 0;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
function globalScopeRoleByName(filePath) {
|
|
291
|
+
if (isDeclarationFile(filePath))
|
|
292
|
+
return "feeds";
|
|
293
|
+
return probeFor(filePath) ? void 0 : "none";
|
|
294
|
+
}
|
|
295
|
+
var MODULE_LINE = /^[ \t]*(?:import|export)\b/m;
|
|
296
|
+
var AMBIENT_DECLARATION = /\bdeclare\s+(?:global|module)\b/;
|
|
297
|
+
function globalScopeRoleByText(content) {
|
|
298
|
+
return MODULE_LINE.test(content) && !AMBIENT_DECLARATION.test(content) ? "none" : void 0;
|
|
299
|
+
}
|
|
300
|
+
function classifyJsTsGlobalScope(filePath) {
|
|
301
|
+
const byName = globalScopeRoleByName(filePath);
|
|
302
|
+
if (byName)
|
|
303
|
+
return byName;
|
|
304
|
+
const probe = probeFor(filePath);
|
|
305
|
+
let content;
|
|
306
|
+
try {
|
|
307
|
+
content = fs.readFileSync(filePath, "utf8");
|
|
308
|
+
} catch (err) {
|
|
309
|
+
recordUnknownRole(filePath, "unreadable-file", err);
|
|
310
|
+
return "unknown";
|
|
311
|
+
}
|
|
312
|
+
if (exceedsLspSyncLimits(content).tooLarge)
|
|
313
|
+
return "unknown";
|
|
314
|
+
const napi = loadAstGrepNapiSync();
|
|
315
|
+
if (!napi) {
|
|
316
|
+
recordDegradationOnce({
|
|
317
|
+
kind: "ast-grep-napi-unavailable",
|
|
318
|
+
subject: "implicit-scope",
|
|
319
|
+
reason: "native addon failed to load; every created JS/TS file counts as feeding the global scope in the diagnostics cache, and its role is unknown to the blocker sweep and the cascade"
|
|
320
|
+
});
|
|
321
|
+
return "unknown";
|
|
322
|
+
}
|
|
323
|
+
const lang = probe === "js-script" ? napi.Lang.JavaScript : path.extname(filePath).toLowerCase() === ".tsx" ? napi.Lang.Tsx : napi.Lang.TypeScript;
|
|
324
|
+
let root;
|
|
325
|
+
try {
|
|
326
|
+
root = napi.parse(lang, content).root();
|
|
327
|
+
} catch (err) {
|
|
328
|
+
recordUnknownRole(filePath, "parse-failed", err);
|
|
329
|
+
return "unknown";
|
|
330
|
+
}
|
|
331
|
+
let isModule = false;
|
|
332
|
+
for (const child of root.children()) {
|
|
333
|
+
const kind = child.kind();
|
|
334
|
+
if (kind === "import_statement" || kind === "export_statement") {
|
|
335
|
+
isModule = true;
|
|
336
|
+
} else if (kind === "ambient_declaration" && child.children().some((part) => part.kind() === "global" || part.kind() === "module")) {
|
|
337
|
+
return "feeds";
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
if (probe === "ambient" || isModule)
|
|
341
|
+
return "none";
|
|
342
|
+
if (probe === "js-script") {
|
|
343
|
+
return ["require($$$ARGS)", "module.exports", "exports.$NAME"].some((pattern) => root.find(pattern) !== null) ? "none" : "feeds";
|
|
344
|
+
}
|
|
345
|
+
return "feeds";
|
|
346
|
+
}
|
|
347
|
+
function feedsJsTsGlobalScope(filePath) {
|
|
348
|
+
return classifyJsTsGlobalScope(filePath) !== "none";
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// dist/clients/path-birth.js
|
|
352
|
+
import * as fs2 from "node:fs";
|
|
353
|
+
var replacedPathBirths = new BoundedFifoMap(1024);
|
|
354
|
+
var inodeKey = (stat) => `${stat.dev}:${stat.ino}:${stat.birthtimeMs}`;
|
|
355
|
+
var appearedPaths = new BoundedFifoMap(1024);
|
|
356
|
+
function notePathAppeared(filePath, atMs = Date.now()) {
|
|
357
|
+
appearedPaths.set(normalizeMapKey(filePath), atMs);
|
|
358
|
+
}
|
|
359
|
+
var birthIsCtimeAlias;
|
|
360
|
+
function birthTimeIsCtimeAlias() {
|
|
361
|
+
if (birthIsCtimeAlias !== void 0)
|
|
362
|
+
return birthIsCtimeAlias;
|
|
363
|
+
birthIsCtimeAlias = false;
|
|
364
|
+
if (process.platform === "linux") {
|
|
365
|
+
try {
|
|
366
|
+
const proc = fs2.statSync("/proc");
|
|
367
|
+
birthIsCtimeAlias = proc.birthtimeMs !== 0 && proc.birthtimeMs === proc.ctimeMs;
|
|
368
|
+
} catch {
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return birthIsCtimeAlias;
|
|
372
|
+
}
|
|
373
|
+
function pathBirthtimeMs(stat, filePath) {
|
|
374
|
+
const appeared = filePath === void 0 ? void 0 : appearedPaths.get(normalizeMapKey(filePath));
|
|
375
|
+
const inode = inodeBirthtimeMs(stat);
|
|
376
|
+
if (appeared === void 0)
|
|
377
|
+
return inode;
|
|
378
|
+
return inode === void 0 ? appeared : Math.max(inode, appeared);
|
|
379
|
+
}
|
|
380
|
+
function inodeBirthtimeMs(stat) {
|
|
381
|
+
if (!(stat.birthtimeMs > 0)) {
|
|
382
|
+
recordDegradationOnce({
|
|
383
|
+
kind: "lsp-file-birth-unknown",
|
|
384
|
+
subject: "no-birth-time",
|
|
385
|
+
reason: "the filesystem reports no birth time; a file created while a language server runs is announced as Changed (#4320)"
|
|
386
|
+
});
|
|
387
|
+
return void 0;
|
|
388
|
+
}
|
|
389
|
+
if (birthTimeIsCtimeAlias()) {
|
|
390
|
+
recordDegradationOnce({
|
|
391
|
+
kind: "lsp-file-birth-unknown",
|
|
392
|
+
subject: "ctime-alias",
|
|
393
|
+
reason: "Node reports ctime as the birth time (no statx); a file created while a language server runs is announced as Changed (#4320)"
|
|
394
|
+
});
|
|
395
|
+
return void 0;
|
|
396
|
+
}
|
|
397
|
+
return replacedPathBirths.get(inodeKey(stat)) ?? stat.birthtimeMs;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// dist/clients/review-graph/import-resolvers.js
|
|
401
|
+
import * as fs4 from "node:fs";
|
|
402
|
+
import * as path3 from "node:path";
|
|
403
|
+
|
|
404
|
+
// dist/clients/review-graph/tsconfig-paths.js
|
|
405
|
+
import * as fs3 from "node:fs";
|
|
406
|
+
import * as os from "node:os";
|
|
407
|
+
import * as path2 from "node:path";
|
|
408
|
+
var cache = new BoundedLruCache(64);
|
|
409
|
+
var referencesCache = new BoundedLruCache(64);
|
|
410
|
+
registerWorkspaceTopologyReset(() => {
|
|
411
|
+
cache.clear();
|
|
412
|
+
referencesCache.clear();
|
|
413
|
+
});
|
|
414
|
+
function parseJsonc(content) {
|
|
415
|
+
let output = "";
|
|
416
|
+
let inString = false;
|
|
417
|
+
let escaped = false;
|
|
418
|
+
for (let i = 0; i < content.length; i++) {
|
|
419
|
+
const char = content[i];
|
|
420
|
+
const next = content[i + 1];
|
|
421
|
+
if (inString) {
|
|
422
|
+
output += char;
|
|
423
|
+
if (escaped)
|
|
424
|
+
escaped = false;
|
|
425
|
+
else if (char === "\\")
|
|
426
|
+
escaped = true;
|
|
427
|
+
else if (char === '"')
|
|
428
|
+
inString = false;
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
431
|
+
if (char === '"') {
|
|
432
|
+
inString = true;
|
|
433
|
+
output += char;
|
|
434
|
+
} else if (char === "/" && next === "/") {
|
|
435
|
+
while (i < content.length && content[i] !== "\n")
|
|
436
|
+
i++;
|
|
437
|
+
output += "\n";
|
|
438
|
+
} else if (char === "/" && next === "*") {
|
|
439
|
+
i += 2;
|
|
440
|
+
while (i < content.length && !(content[i] === "*" && content[i + 1] === "/"))
|
|
441
|
+
i++;
|
|
442
|
+
i++;
|
|
443
|
+
} else {
|
|
444
|
+
output += char;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
return JSON.parse(output.replace(/,\s*([}\]])/g, "$1"));
|
|
448
|
+
}
|
|
449
|
+
function configSignature(configPath) {
|
|
450
|
+
try {
|
|
451
|
+
const stat = fs3.statSync(configPath);
|
|
452
|
+
return `${stat.mtimeMs}:${stat.size}`;
|
|
453
|
+
} catch {
|
|
454
|
+
return "missing";
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
function configDependencyPaths(configPath) {
|
|
458
|
+
const paths = /* @__PURE__ */ new Set();
|
|
459
|
+
const visit = (currentPath) => {
|
|
460
|
+
const normalized = path2.resolve(currentPath);
|
|
461
|
+
if (paths.has(normalized))
|
|
462
|
+
return;
|
|
463
|
+
paths.add(normalized);
|
|
464
|
+
let json;
|
|
465
|
+
try {
|
|
466
|
+
json = parseJsonc(fs3.readFileSync(normalized, "utf8"));
|
|
467
|
+
} catch {
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
if (typeof json.extends === "string") {
|
|
471
|
+
const parent = resolveExtends(normalized, json.extends);
|
|
472
|
+
if (parent)
|
|
473
|
+
visit(parent);
|
|
474
|
+
}
|
|
475
|
+
for (const reference of json.references ?? []) {
|
|
476
|
+
if (typeof reference?.path !== "string")
|
|
477
|
+
continue;
|
|
478
|
+
const referenced = resolveReferenceConfig(normalized, reference.path);
|
|
479
|
+
if (referenced)
|
|
480
|
+
visit(referenced);
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
visit(configPath);
|
|
484
|
+
return [...paths].sort((a, b) => a.localeCompare(b));
|
|
485
|
+
}
|
|
486
|
+
function dependencySignature(configPath) {
|
|
487
|
+
return configDependencyPaths(configPath).map((dependency) => `${dependency}:${configSignature(dependency)}`).join("|");
|
|
488
|
+
}
|
|
489
|
+
function resolveExtends(configPath, value) {
|
|
490
|
+
if (!value.startsWith("."))
|
|
491
|
+
return void 0;
|
|
492
|
+
const resolved = path2.resolve(path2.dirname(configPath), value);
|
|
493
|
+
return resolved.toLowerCase().endsWith(".json") ? resolved : `${resolved}.json`;
|
|
494
|
+
}
|
|
495
|
+
function readConfig(configPath, seen) {
|
|
496
|
+
const normalized = path2.resolve(configPath);
|
|
497
|
+
if (seen.has(normalized))
|
|
498
|
+
return void 0;
|
|
499
|
+
seen.add(normalized);
|
|
500
|
+
let json;
|
|
501
|
+
try {
|
|
502
|
+
json = parseJsonc(fs3.readFileSync(normalized, "utf8"));
|
|
503
|
+
} catch {
|
|
504
|
+
return void 0;
|
|
505
|
+
}
|
|
506
|
+
let inherited;
|
|
507
|
+
if (typeof json.extends === "string") {
|
|
508
|
+
const parentPath = resolveExtends(normalized, json.extends);
|
|
509
|
+
if (parentPath)
|
|
510
|
+
inherited = readConfig(parentPath, seen);
|
|
511
|
+
}
|
|
512
|
+
const options = json.compilerOptions;
|
|
513
|
+
const baseUrl = typeof options?.baseUrl === "string" ? path2.resolve(path2.dirname(normalized), options.baseUrl) : inherited?.baseUrl ?? path2.dirname(normalized);
|
|
514
|
+
const paths = options?.paths && typeof options.paths === "object" ? Object.fromEntries(Object.entries(options.paths).map(([pattern, targets]) => [
|
|
515
|
+
pattern,
|
|
516
|
+
Array.isArray(targets) ? targets.map((target) => path2.resolve(baseUrl, target)) : targets
|
|
517
|
+
])) : inherited?.paths;
|
|
518
|
+
const rootDir = typeof options?.rootDir === "string" ? path2.resolve(path2.dirname(normalized), options.rootDir) : inherited?.rootDir;
|
|
519
|
+
const include = Array.isArray(json.include) ? json.include.filter((value) => typeof value === "string") : inherited?.include;
|
|
520
|
+
const references = Array.isArray(json.references) ? json.references.map((reference) => reference?.path).filter((value) => typeof value === "string" && value.startsWith(".")) : [];
|
|
521
|
+
return { baseUrl, paths, rootDir, include, references };
|
|
522
|
+
}
|
|
523
|
+
function resolveReferenceConfig(configPath, value) {
|
|
524
|
+
const target = path2.resolve(path2.dirname(configPath), value);
|
|
525
|
+
try {
|
|
526
|
+
if (fs3.statSync(target).isDirectory()) {
|
|
527
|
+
return getDirectoryMarkers(target).tsconfigPath;
|
|
528
|
+
}
|
|
529
|
+
if (fs3.statSync(target).isFile())
|
|
530
|
+
return target;
|
|
531
|
+
} catch {
|
|
532
|
+
return void 0;
|
|
533
|
+
}
|
|
534
|
+
return void 0;
|
|
535
|
+
}
|
|
536
|
+
function includeRoot(configDir, pattern) {
|
|
537
|
+
const wildcard = pattern.search(/[*?]/);
|
|
538
|
+
const prefix = wildcard === -1 ? pattern : pattern.slice(0, wildcard);
|
|
539
|
+
const trimmed = prefix.replace(/[\\/]+$/, "");
|
|
540
|
+
if (!trimmed)
|
|
541
|
+
return void 0;
|
|
542
|
+
const resolved = path2.resolve(configDir, trimmed);
|
|
543
|
+
return path2.extname(resolved) ? path2.dirname(resolved) : resolved;
|
|
544
|
+
}
|
|
545
|
+
function firstSourceEntry(configPath, parsed) {
|
|
546
|
+
const configDir = path2.dirname(configPath);
|
|
547
|
+
const roots = [
|
|
548
|
+
...parsed.rootDir ? [parsed.rootDir] : [],
|
|
549
|
+
...(parsed.include ?? []).map((pattern) => includeRoot(configDir, pattern)).filter((value) => value !== void 0)
|
|
550
|
+
];
|
|
551
|
+
const candidates = [
|
|
552
|
+
...roots.flatMap((root) => [
|
|
553
|
+
path2.join(root, "index.ts"),
|
|
554
|
+
path2.join(root, "index.tsx")
|
|
555
|
+
]),
|
|
556
|
+
path2.join(configDir, "src", "index.ts"),
|
|
557
|
+
path2.join(configDir, "src", "index.tsx"),
|
|
558
|
+
path2.join(configDir, "index.ts"),
|
|
559
|
+
path2.join(configDir, "index.tsx")
|
|
560
|
+
];
|
|
561
|
+
for (const candidate of candidates) {
|
|
562
|
+
try {
|
|
563
|
+
if (fs3.statSync(candidate).isFile())
|
|
564
|
+
return candidate;
|
|
565
|
+
} catch {
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
return void 0;
|
|
569
|
+
}
|
|
570
|
+
function collectReferencedProjects(configPath, result, visited) {
|
|
571
|
+
const normalized = path2.resolve(configPath);
|
|
572
|
+
if (visited.has(normalized))
|
|
573
|
+
return;
|
|
574
|
+
visited.add(normalized);
|
|
575
|
+
const parsed = readConfig(normalized, /* @__PURE__ */ new Set());
|
|
576
|
+
if (!parsed)
|
|
577
|
+
return;
|
|
578
|
+
for (const reference of parsed.references) {
|
|
579
|
+
const referencedConfig = resolveReferenceConfig(normalized, reference);
|
|
580
|
+
if (!referencedConfig)
|
|
581
|
+
continue;
|
|
582
|
+
const referenced = readConfig(referencedConfig, /* @__PURE__ */ new Set());
|
|
583
|
+
if (referenced) {
|
|
584
|
+
const packageJsonPath = getDirectoryMarkers(path2.dirname(referencedConfig)).packageJsonPath;
|
|
585
|
+
const entry = firstSourceEntry(referencedConfig, referenced);
|
|
586
|
+
if (packageJsonPath && entry) {
|
|
587
|
+
try {
|
|
588
|
+
const pkg = JSON.parse(fs3.readFileSync(packageJsonPath, "utf8"));
|
|
589
|
+
if (typeof pkg.name === "string" && !result.has(pkg.name)) {
|
|
590
|
+
result.set(pkg.name, entry);
|
|
591
|
+
}
|
|
592
|
+
} catch {
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
collectReferencedProjects(referencedConfig, result, visited);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
function parseTsconfigPaths(cwd, homeDir = os.homedir()) {
|
|
600
|
+
const normalizedCwd = path2.resolve(cwd);
|
|
601
|
+
const configDir = findGoverningTsconfigDir(normalizedCwd, homeDir);
|
|
602
|
+
const configPath = configDir ? path2.join(configDir, "tsconfig.json") : "";
|
|
603
|
+
const signature = configPath ? dependencySignature(configPath) : "missing";
|
|
604
|
+
const key = `${normalizedCwd}|${configPath}|${signature}`;
|
|
605
|
+
const cached = cache.get(key);
|
|
606
|
+
if (cached)
|
|
607
|
+
return cached;
|
|
608
|
+
if (!configDir) {
|
|
609
|
+
cache.set(key, []);
|
|
610
|
+
return [];
|
|
611
|
+
}
|
|
612
|
+
const parsed = readConfig(path2.join(configDir, "tsconfig.json"), /* @__PURE__ */ new Set());
|
|
613
|
+
const matchers = Object.entries(parsed?.paths ?? {}).filter((entry) => Array.isArray(entry[1]) && entry[1].every((target) => typeof target === "string")).map(([pattern, targets]) => {
|
|
614
|
+
const star = pattern.indexOf("*");
|
|
615
|
+
return {
|
|
616
|
+
pattern,
|
|
617
|
+
prefix: star === -1 ? pattern : pattern.slice(0, star),
|
|
618
|
+
suffix: star === -1 ? "" : pattern.slice(star + 1),
|
|
619
|
+
targets
|
|
620
|
+
};
|
|
621
|
+
}).sort((a, b) => b.prefix.length - a.prefix.length);
|
|
622
|
+
cache.set(key, matchers);
|
|
623
|
+
return matchers;
|
|
624
|
+
}
|
|
625
|
+
function aliasedImportTargets(specifier, importerDir) {
|
|
626
|
+
for (const matcher of parseTsconfigPaths(importerDir)) {
|
|
627
|
+
if (!specifier.startsWith(matcher.prefix) || !specifier.endsWith(matcher.suffix))
|
|
628
|
+
continue;
|
|
629
|
+
const wildcard = specifier.slice(matcher.prefix.length, specifier.length - matcher.suffix.length);
|
|
630
|
+
if (!matcher.pattern.includes("*") && wildcard)
|
|
631
|
+
continue;
|
|
632
|
+
return matcher.targets.map((target) => target.replaceAll("*", wildcard));
|
|
633
|
+
}
|
|
634
|
+
return [];
|
|
635
|
+
}
|
|
636
|
+
function referencedProjectImportTarget(specifier, importerDir) {
|
|
637
|
+
const normalizedImporterDir = path2.resolve(importerDir);
|
|
638
|
+
const governingDir = findGoverningTsconfigDir(normalizedImporterDir);
|
|
639
|
+
const governingPath = governingDir ? path2.join(governingDir, "tsconfig.json") : "";
|
|
640
|
+
const key = `${normalizedImporterDir}|${governingPath}|${governingPath ? dependencySignature(governingPath) : "missing"}`;
|
|
641
|
+
let projects = referencesCache.get(key);
|
|
642
|
+
if (!projects) {
|
|
643
|
+
projects = /* @__PURE__ */ new Map();
|
|
644
|
+
const configDir = governingDir;
|
|
645
|
+
if (configDir) {
|
|
646
|
+
collectReferencedProjects(path2.join(configDir, "tsconfig.json"), projects, /* @__PURE__ */ new Set());
|
|
647
|
+
}
|
|
648
|
+
referencesCache.set(key, projects);
|
|
649
|
+
}
|
|
650
|
+
return projects.get(specifier);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// dist/clients/review-graph/import-resolvers.js
|
|
654
|
+
function isWithin(cwd, p) {
|
|
655
|
+
return isUnderDir(path3.resolve(p), path3.resolve(cwd));
|
|
656
|
+
}
|
|
657
|
+
function isFile(p) {
|
|
658
|
+
try {
|
|
659
|
+
return fs4.statSync(p).isFile();
|
|
660
|
+
} catch {
|
|
661
|
+
return false;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
function isDir(p) {
|
|
665
|
+
try {
|
|
666
|
+
return fs4.statSync(p).isDirectory();
|
|
667
|
+
} catch {
|
|
668
|
+
return false;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
function firstExistingFile(cwd, candidates) {
|
|
672
|
+
for (const c of candidates) {
|
|
673
|
+
if (isFile(c) && isWithin(cwd, c))
|
|
674
|
+
return [normalizeMapKey(c)];
|
|
675
|
+
}
|
|
676
|
+
return [];
|
|
677
|
+
}
|
|
678
|
+
function sourceFilesIn(cwd, dir, ext) {
|
|
679
|
+
if (!isDir(dir) || !isWithin(cwd, dir))
|
|
680
|
+
return [];
|
|
681
|
+
try {
|
|
682
|
+
return fs4.readdirSync(dir).filter((n) => n.endsWith(ext)).map((n) => normalizeMapKey(path3.join(dir, n))).sort();
|
|
683
|
+
} catch {
|
|
684
|
+
return [];
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
function resolveRelative(cwd, filePath, source, exts) {
|
|
688
|
+
const base = path3.resolve(path3.dirname(filePath), source);
|
|
689
|
+
return firstExistingFile(cwd, [base, ...exts.map((e) => base + e)]);
|
|
690
|
+
}
|
|
691
|
+
function resolveDart(cwd, filePath, source) {
|
|
692
|
+
if (source.startsWith("package:") || source.startsWith("dart:"))
|
|
693
|
+
return [];
|
|
694
|
+
return resolveRelative(cwd, filePath, source, [".dart"]);
|
|
695
|
+
}
|
|
696
|
+
var JS_TS_EXT_RE = /\.(mjs|cjs|mts|cts|jsx?|tsx?)$/i;
|
|
697
|
+
function jsTsExtensionCandidates(base, strippedBase, ext) {
|
|
698
|
+
const candidates = [];
|
|
699
|
+
if (ext === ".mjs")
|
|
700
|
+
candidates.push(`${strippedBase}.mts`);
|
|
701
|
+
if (ext === ".cjs")
|
|
702
|
+
candidates.push(`${strippedBase}.cts`);
|
|
703
|
+
if (ext === ".mts")
|
|
704
|
+
candidates.push(`${strippedBase}.mts`);
|
|
705
|
+
if (ext === ".cts")
|
|
706
|
+
candidates.push(`${strippedBase}.cts`);
|
|
707
|
+
candidates.push(`${strippedBase}.ts`, `${strippedBase}.tsx`, `${strippedBase}.mts`, `${strippedBase}.cts`);
|
|
708
|
+
candidates.push(base, `${strippedBase}.js`, `${strippedBase}.jsx`, `${strippedBase}.mjs`, `${strippedBase}.cjs`);
|
|
709
|
+
for (const index of [
|
|
710
|
+
"index.ts",
|
|
711
|
+
"index.tsx",
|
|
712
|
+
"index.mts",
|
|
713
|
+
"index.cts",
|
|
714
|
+
"index.js",
|
|
715
|
+
"index.jsx",
|
|
716
|
+
"index.mjs",
|
|
717
|
+
"index.cjs"
|
|
718
|
+
]) {
|
|
719
|
+
candidates.push(path3.join(base, index));
|
|
720
|
+
}
|
|
721
|
+
return [...new Set(candidates)];
|
|
722
|
+
}
|
|
723
|
+
function jsTsCandidatePaths(filePath, source) {
|
|
724
|
+
const base = path3.resolve(path3.dirname(filePath), source);
|
|
725
|
+
const strippedSource = source.replace(JS_TS_EXT_RE, "");
|
|
726
|
+
const strippedBase = strippedSource === source ? base : path3.resolve(path3.dirname(filePath), strippedSource);
|
|
727
|
+
const ext = path3.extname(source).toLowerCase();
|
|
728
|
+
return jsTsExtensionCandidates(base, strippedBase, ext);
|
|
729
|
+
}
|
|
730
|
+
function findWorkspaceModuleForSpecifier(modules, source) {
|
|
731
|
+
let best;
|
|
732
|
+
for (const mod of modules) {
|
|
733
|
+
let subpath;
|
|
734
|
+
if (source === mod.name)
|
|
735
|
+
subpath = "";
|
|
736
|
+
else if (source.startsWith(`${mod.name}/`))
|
|
737
|
+
subpath = source.slice(mod.name.length + 1);
|
|
738
|
+
if (subpath === void 0)
|
|
739
|
+
continue;
|
|
740
|
+
if (!best || mod.name.length > best.mod.name.length)
|
|
741
|
+
best = { mod, subpath };
|
|
742
|
+
}
|
|
743
|
+
return best;
|
|
744
|
+
}
|
|
745
|
+
function pickExportsMain(exportsField) {
|
|
746
|
+
if (typeof exportsField === "string")
|
|
747
|
+
return exportsField;
|
|
748
|
+
if (!exportsField || typeof exportsField !== "object")
|
|
749
|
+
return void 0;
|
|
750
|
+
const obj = exportsField;
|
|
751
|
+
const dot = obj["."] ?? obj;
|
|
752
|
+
if (typeof dot === "string")
|
|
753
|
+
return dot;
|
|
754
|
+
if (dot && typeof dot === "object") {
|
|
755
|
+
for (const key of ["import", "require", "default"]) {
|
|
756
|
+
const v = dot[key];
|
|
757
|
+
if (typeof v === "string")
|
|
758
|
+
return v;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
return void 0;
|
|
762
|
+
}
|
|
763
|
+
function workspaceEntryCandidates(pkgRoot) {
|
|
764
|
+
let main;
|
|
765
|
+
try {
|
|
766
|
+
const pkg = JSON.parse(fs4.readFileSync(path3.join(pkgRoot, "package.json"), "utf-8"));
|
|
767
|
+
main = pickExportsMain(pkg.exports) ?? pkg.module ?? pkg.main;
|
|
768
|
+
} catch {
|
|
769
|
+
}
|
|
770
|
+
const candidates = [];
|
|
771
|
+
if (main) {
|
|
772
|
+
const mainPath = path3.resolve(pkgRoot, main);
|
|
773
|
+
const ext = path3.extname(mainPath).toLowerCase();
|
|
774
|
+
const stripped = mainPath.replace(JS_TS_EXT_RE, "");
|
|
775
|
+
candidates.push(...jsTsExtensionCandidates(mainPath, stripped, ext));
|
|
776
|
+
}
|
|
777
|
+
for (const base of [pkgRoot, path3.join(pkgRoot, "src")]) {
|
|
778
|
+
candidates.push(path3.join(base, "index.ts"), path3.join(base, "index.tsx"), path3.join(base, "index.mts"), path3.join(base, "index.cts"), path3.join(base, "index.js"), path3.join(base, "index.jsx"), path3.join(base, "index.mjs"), path3.join(base, "index.cjs"));
|
|
779
|
+
}
|
|
780
|
+
return [...new Set(candidates)];
|
|
781
|
+
}
|
|
782
|
+
function workspaceSubpathCandidates(pkgRoot, subpath) {
|
|
783
|
+
const base = path3.join(pkgRoot, subpath);
|
|
784
|
+
const strippedSubpath = subpath.replace(JS_TS_EXT_RE, "");
|
|
785
|
+
const strippedBase = strippedSubpath === subpath ? base : path3.join(pkgRoot, strippedSubpath);
|
|
786
|
+
const ext = path3.extname(subpath).toLowerCase();
|
|
787
|
+
return jsTsExtensionCandidates(base, strippedBase, ext);
|
|
788
|
+
}
|
|
789
|
+
function resolveAliasedImport(cwd, specifier, importerDir) {
|
|
790
|
+
for (const target of aliasedImportTargets(specifier, importerDir)) {
|
|
791
|
+
const ext = path3.extname(target).toLowerCase();
|
|
792
|
+
const stripped = target.replace(JS_TS_EXT_RE, "");
|
|
793
|
+
const found = firstExistingFile(cwd, jsTsExtensionCandidates(target, stripped, ext));
|
|
794
|
+
if (found.length)
|
|
795
|
+
return found;
|
|
796
|
+
}
|
|
797
|
+
return [];
|
|
798
|
+
}
|
|
799
|
+
function resolveProjectReferenceImport(cwd, specifier, importerDir) {
|
|
800
|
+
const target = referencedProjectImportTarget(specifier, importerDir);
|
|
801
|
+
return target ? firstExistingFile(cwd, [target]) : [];
|
|
802
|
+
}
|
|
803
|
+
function resolveWorkspacePackageImport(cwd, source) {
|
|
804
|
+
if (source.startsWith("."))
|
|
805
|
+
return [];
|
|
806
|
+
const graph = buildModuleGraph(cwd);
|
|
807
|
+
if (!graph)
|
|
808
|
+
return [];
|
|
809
|
+
const match = findWorkspaceModuleForSpecifier(graph.modules.values(), source);
|
|
810
|
+
if (!match)
|
|
811
|
+
return [];
|
|
812
|
+
const candidates = match.subpath ? workspaceSubpathCandidates(match.mod.root, match.subpath) : workspaceEntryCandidates(match.mod.root);
|
|
813
|
+
return firstExistingFile(cwd, candidates);
|
|
814
|
+
}
|
|
815
|
+
function resolveJsTs(cwd, filePath, source) {
|
|
816
|
+
if (!source.startsWith(".")) {
|
|
817
|
+
const aliased = resolveAliasedImport(cwd, source, path3.dirname(filePath));
|
|
818
|
+
if (aliased.length)
|
|
819
|
+
return aliased;
|
|
820
|
+
const referenced = resolveProjectReferenceImport(cwd, source, path3.dirname(filePath));
|
|
821
|
+
return referenced.length ? referenced : resolveWorkspacePackageImport(cwd, source);
|
|
822
|
+
}
|
|
823
|
+
return firstExistingFile(cwd, jsTsCandidatePaths(filePath, source));
|
|
824
|
+
}
|
|
825
|
+
function resolveCxx(cwd, filePath, source) {
|
|
826
|
+
if (source.startsWith("<"))
|
|
827
|
+
return [];
|
|
828
|
+
const dir = path3.dirname(path3.resolve(filePath));
|
|
829
|
+
return firstExistingFile(cwd, [
|
|
830
|
+
path3.resolve(dir, source),
|
|
831
|
+
path3.resolve(cwd, source),
|
|
832
|
+
path3.resolve(cwd, "include", source),
|
|
833
|
+
path3.resolve(cwd, "src", source)
|
|
834
|
+
]);
|
|
835
|
+
}
|
|
836
|
+
function pythonRoots(cwd, fileDir) {
|
|
837
|
+
let p = fileDir;
|
|
838
|
+
const root = path3.resolve(cwd);
|
|
839
|
+
while (isFile(path3.join(p, "__init__.py")) && isWithin(cwd, p)) {
|
|
840
|
+
const parent = path3.dirname(p);
|
|
841
|
+
if (parent === p)
|
|
842
|
+
break;
|
|
843
|
+
p = parent;
|
|
844
|
+
}
|
|
845
|
+
const roots = /* @__PURE__ */ new Set([p, root, path3.join(root, "src")]);
|
|
846
|
+
return [...roots].filter((r) => isDir(r));
|
|
847
|
+
}
|
|
848
|
+
function resolvePython(cwd, filePath, source) {
|
|
849
|
+
const fileDir = path3.dirname(path3.resolve(filePath));
|
|
850
|
+
if (source.startsWith(".")) {
|
|
851
|
+
const m = source.match(/^(\.+)(.*)$/);
|
|
852
|
+
if (!m)
|
|
853
|
+
return [];
|
|
854
|
+
const dots = m[1].length;
|
|
855
|
+
let baseDir = fileDir;
|
|
856
|
+
for (let i = 1; i < dots; i++)
|
|
857
|
+
baseDir = path3.dirname(baseDir);
|
|
858
|
+
const rest = m[2] ? m[2].split(".") : [];
|
|
859
|
+
const target = path3.join(baseDir, ...rest);
|
|
860
|
+
return firstExistingFile(cwd, [
|
|
861
|
+
`${target}.py`,
|
|
862
|
+
path3.join(target, "__init__.py")
|
|
863
|
+
]);
|
|
864
|
+
}
|
|
865
|
+
const parts = source.split(".");
|
|
866
|
+
for (const root of pythonRoots(cwd, fileDir)) {
|
|
867
|
+
const target = path3.join(root, ...parts);
|
|
868
|
+
const found = firstExistingFile(cwd, [
|
|
869
|
+
`${target}.py`,
|
|
870
|
+
path3.join(target, "__init__.py")
|
|
871
|
+
]);
|
|
872
|
+
if (found.length)
|
|
873
|
+
return found;
|
|
874
|
+
}
|
|
875
|
+
return [];
|
|
876
|
+
}
|
|
877
|
+
function findGoModule(cwd, filePath) {
|
|
878
|
+
let dir = path3.dirname(path3.resolve(filePath));
|
|
879
|
+
const root = path3.resolve(cwd);
|
|
880
|
+
while (true) {
|
|
881
|
+
try {
|
|
882
|
+
const content = fs4.readFileSync(path3.join(dir, "go.mod"), "utf-8");
|
|
883
|
+
const m = content.match(/^[^\S\n\r\u2028\u2029]*module\s+(\S+)/m);
|
|
884
|
+
if (m)
|
|
885
|
+
return { moduleDir: dir, modulePath: m[1] };
|
|
886
|
+
} catch {
|
|
887
|
+
}
|
|
888
|
+
if (dir === root)
|
|
889
|
+
break;
|
|
890
|
+
const parent = path3.dirname(dir);
|
|
891
|
+
if (parent === dir || !isWithin(cwd, parent))
|
|
892
|
+
break;
|
|
893
|
+
dir = parent;
|
|
894
|
+
}
|
|
895
|
+
return null;
|
|
896
|
+
}
|
|
897
|
+
function resolveGo(cwd, filePath, source) {
|
|
898
|
+
const mod = findGoModule(cwd, filePath);
|
|
899
|
+
if (!mod)
|
|
900
|
+
return [];
|
|
901
|
+
if (source !== mod.modulePath && !source.startsWith(`${mod.modulePath}/`)) {
|
|
902
|
+
return [];
|
|
903
|
+
}
|
|
904
|
+
const rel = source === mod.modulePath ? "" : source.slice(mod.modulePath.length + 1);
|
|
905
|
+
return sourceFilesIn(cwd, path3.join(mod.moduleDir, rel), ".go");
|
|
906
|
+
}
|
|
907
|
+
function javaSourceRoots(cwd, filePath) {
|
|
908
|
+
const root = path3.resolve(cwd);
|
|
909
|
+
const roots = /* @__PURE__ */ new Set();
|
|
910
|
+
for (const c of ["src/main/java", "src/test/java", "src", ""]) {
|
|
911
|
+
roots.add(path3.join(root, c));
|
|
912
|
+
}
|
|
913
|
+
let p = path3.dirname(path3.resolve(filePath));
|
|
914
|
+
while (true) {
|
|
915
|
+
roots.add(p);
|
|
916
|
+
if (p === root)
|
|
917
|
+
break;
|
|
918
|
+
const parent = path3.dirname(p);
|
|
919
|
+
if (parent === p || !isWithin(cwd, parent))
|
|
920
|
+
break;
|
|
921
|
+
p = parent;
|
|
922
|
+
}
|
|
923
|
+
return [...roots].filter((r) => isDir(r));
|
|
924
|
+
}
|
|
925
|
+
function resolveJava(cwd, filePath, source) {
|
|
926
|
+
const parts = source.split(".");
|
|
927
|
+
for (const root of javaSourceRoots(cwd, filePath)) {
|
|
928
|
+
const asFile = firstExistingFile(cwd, [
|
|
929
|
+
`${path3.join(root, ...parts)}.java`
|
|
930
|
+
]);
|
|
931
|
+
if (asFile.length)
|
|
932
|
+
return asFile;
|
|
933
|
+
const asPkg = sourceFilesIn(cwd, path3.join(root, ...parts), ".java");
|
|
934
|
+
if (asPkg.length)
|
|
935
|
+
return asPkg;
|
|
936
|
+
if (parts.length > 1) {
|
|
937
|
+
const dropLast = firstExistingFile(cwd, [
|
|
938
|
+
`${path3.join(root, ...parts.slice(0, -1))}.java`
|
|
939
|
+
]);
|
|
940
|
+
if (dropLast.length)
|
|
941
|
+
return dropLast;
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
return [];
|
|
945
|
+
}
|
|
946
|
+
function importSourceNameParts(source) {
|
|
947
|
+
return source.match(/[\w$-]+/g) ?? [];
|
|
948
|
+
}
|
|
949
|
+
function fileAnswerNames(file) {
|
|
950
|
+
return [
|
|
951
|
+
path3.basename(file).split(".")[0] ?? "",
|
|
952
|
+
path3.basename(path3.dirname(file))
|
|
953
|
+
].flatMap((name) => name.length > 0 ? [name] : []);
|
|
954
|
+
}
|
|
955
|
+
function resolveImportToFiles(cwd, filePath, languageId, source) {
|
|
956
|
+
switch (languageId) {
|
|
957
|
+
case "typescript":
|
|
958
|
+
case "tsx":
|
|
959
|
+
case "javascript":
|
|
960
|
+
case "jsts":
|
|
961
|
+
return resolveJsTs(cwd, filePath, source);
|
|
962
|
+
case "c":
|
|
963
|
+
case "cpp":
|
|
964
|
+
return resolveCxx(cwd, filePath, source);
|
|
965
|
+
case "ruby":
|
|
966
|
+
return resolveRelative(cwd, filePath, source, [".rb"]);
|
|
967
|
+
case "zig":
|
|
968
|
+
return resolveRelative(cwd, filePath, source, [".zig"]);
|
|
969
|
+
case "bash":
|
|
970
|
+
return resolveRelative(cwd, filePath, source, [".sh", ".bash"]);
|
|
971
|
+
case "dart":
|
|
972
|
+
return resolveDart(cwd, filePath, source);
|
|
973
|
+
case "python":
|
|
974
|
+
return resolvePython(cwd, filePath, source);
|
|
975
|
+
case "go":
|
|
976
|
+
return resolveGo(cwd, filePath, source);
|
|
977
|
+
case "java":
|
|
978
|
+
return resolveJava(cwd, filePath, source);
|
|
979
|
+
default:
|
|
980
|
+
return [];
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
// dist/clients/tree-sitter-symbol-extractor.js
|
|
985
|
+
import * as path4 from "node:path";
|
|
986
|
+
var SYMBOL_QUERIES = {
|
|
987
|
+
typescript: {
|
|
988
|
+
defs: `
|
|
989
|
+
;; Function declarations: function foo(params) { }
|
|
990
|
+
(function_declaration
|
|
991
|
+
name: (identifier) @funcName
|
|
992
|
+
parameters: (formal_parameters) @funcParams
|
|
993
|
+
body: (statement_block) @funcBody) @funcDef
|
|
994
|
+
|
|
995
|
+
;; Arrow functions: const foo = (params) => { }
|
|
996
|
+
(variable_declarator
|
|
997
|
+
name: (identifier) @arrowName
|
|
998
|
+
value: (arrow_function
|
|
999
|
+
parameters: (formal_parameters) @arrowParams
|
|
1000
|
+
body: (_) @arrowBody)) @arrowDef
|
|
1001
|
+
|
|
1002
|
+
;; Class declarations: class Foo { }
|
|
1003
|
+
(class_declaration
|
|
1004
|
+
name: (type_identifier) @className) @classDef
|
|
1005
|
+
|
|
1006
|
+
;; Method definitions: class Foo { bar() { } }
|
|
1007
|
+
(method_definition
|
|
1008
|
+
name: (property_identifier) @methodName
|
|
1009
|
+
parameters: (formal_parameters) @methodParams) @methodDef
|
|
1010
|
+
|
|
1011
|
+
;; Interface declarations: interface Foo { }
|
|
1012
|
+
(interface_declaration
|
|
1013
|
+
name: (type_identifier) @interfaceName) @interfaceDef
|
|
1014
|
+
|
|
1015
|
+
;; Type alias: type Foo = ...
|
|
1016
|
+
(type_alias_declaration
|
|
1017
|
+
name: (type_identifier) @typeName) @typeDef
|
|
1018
|
+
`,
|
|
1019
|
+
refs: `
|
|
1020
|
+
;; Function/method calls: foo() or obj.bar()
|
|
1021
|
+
(call_expression
|
|
1022
|
+
function: (identifier) @callIdent) @callRef
|
|
1023
|
+
|
|
1024
|
+
(call_expression
|
|
1025
|
+
function: (member_expression
|
|
1026
|
+
object: (_)
|
|
1027
|
+
property: (property_identifier) @callMethod)) @callMethodRef
|
|
1028
|
+
|
|
1029
|
+
;; New expressions: new Foo()
|
|
1030
|
+
(new_expression
|
|
1031
|
+
constructor: (identifier) @newIdent) @newRef
|
|
1032
|
+
|
|
1033
|
+
;; Type references: type T = Foo
|
|
1034
|
+
(type_identifier) @typeIdent
|
|
1035
|
+
`
|
|
1036
|
+
},
|
|
1037
|
+
python: {
|
|
1038
|
+
defs: `
|
|
1039
|
+
;; Function definitions: def foo(params):
|
|
1040
|
+
(function_definition
|
|
1041
|
+
name: (identifier) @funcName
|
|
1042
|
+
parameters: (parameters) @funcParams) @funcDef
|
|
1043
|
+
|
|
1044
|
+
;; Class definitions: class Foo:
|
|
1045
|
+
(class_definition
|
|
1046
|
+
name: (identifier) @className) @classDef
|
|
1047
|
+
|
|
1048
|
+
;; Method definitions (within class)
|
|
1049
|
+
(class_definition
|
|
1050
|
+
body: (block
|
|
1051
|
+
(function_definition
|
|
1052
|
+
name: (identifier) @methodName
|
|
1053
|
+
parameters: (parameters) @methodParams) @methodDef))
|
|
1054
|
+
`,
|
|
1055
|
+
refs: `
|
|
1056
|
+
;; Function calls: foo() or obj.bar()
|
|
1057
|
+
(call
|
|
1058
|
+
function: (identifier) @callIdent) @callRef
|
|
1059
|
+
|
|
1060
|
+
(call
|
|
1061
|
+
function: (attribute
|
|
1062
|
+
object: (_)
|
|
1063
|
+
attribute: (identifier) @callMethod)) @callMethodRef
|
|
1064
|
+
`
|
|
1065
|
+
},
|
|
1066
|
+
rust: {
|
|
1067
|
+
defs: `
|
|
1068
|
+
;; Function definitions: fn foo(params) { }
|
|
1069
|
+
(function_item
|
|
1070
|
+
name: (identifier) @funcName
|
|
1071
|
+
parameters: (parameters) @funcParams) @funcDef
|
|
1072
|
+
|
|
1073
|
+
;; Struct definitions: struct Foo { }
|
|
1074
|
+
(struct_item
|
|
1075
|
+
name: (type_identifier) @structName) @structDef
|
|
1076
|
+
|
|
1077
|
+
;; Impl blocks: impl Foo { fn bar() { } }
|
|
1078
|
+
(impl_item
|
|
1079
|
+
type: (type_identifier) @implType
|
|
1080
|
+
body: (declaration_list
|
|
1081
|
+
(function_item
|
|
1082
|
+
name: (identifier) @implMethodName) @implMethodDef))
|
|
1083
|
+
`,
|
|
1084
|
+
refs: `
|
|
1085
|
+
;; Function calls: foo() or obj.bar()
|
|
1086
|
+
(call_expression
|
|
1087
|
+
function: (identifier) @callIdent) @callRef
|
|
1088
|
+
|
|
1089
|
+
(call_expression
|
|
1090
|
+
function: (field_expression
|
|
1091
|
+
value: (_)
|
|
1092
|
+
field: (field_identifier) @callField)) @callFieldRef
|
|
1093
|
+
`
|
|
1094
|
+
},
|
|
1095
|
+
go: {
|
|
1096
|
+
defs: `
|
|
1097
|
+
(function_declaration
|
|
1098
|
+
name: (identifier) @funcName
|
|
1099
|
+
parameters: (parameter_list) @funcParams) @funcDef
|
|
1100
|
+
|
|
1101
|
+
(method_declaration
|
|
1102
|
+
name: (field_identifier) @methodName
|
|
1103
|
+
parameters: (parameter_list) @methodParams) @methodDef
|
|
1104
|
+
|
|
1105
|
+
(type_spec
|
|
1106
|
+
name: (type_identifier) @typeName) @typeDef
|
|
1107
|
+
`,
|
|
1108
|
+
refs: `
|
|
1109
|
+
(call_expression
|
|
1110
|
+
function: (identifier) @callIdent) @callRef
|
|
1111
|
+
|
|
1112
|
+
(call_expression
|
|
1113
|
+
function: (selector_expression
|
|
1114
|
+
field: (field_identifier) @callMethod)) @callMethodRef
|
|
1115
|
+
`
|
|
1116
|
+
},
|
|
1117
|
+
ruby: {
|
|
1118
|
+
defs: `
|
|
1119
|
+
(method
|
|
1120
|
+
name: (identifier) @methodName) @methodDef
|
|
1121
|
+
|
|
1122
|
+
(singleton_method
|
|
1123
|
+
name: (identifier) @methodName) @methodDef
|
|
1124
|
+
|
|
1125
|
+
(class
|
|
1126
|
+
name: (constant) @className) @classDef
|
|
1127
|
+
|
|
1128
|
+
(module
|
|
1129
|
+
name: (constant) @moduleName) @moduleDef
|
|
1130
|
+
`,
|
|
1131
|
+
refs: `
|
|
1132
|
+
(call
|
|
1133
|
+
method: (identifier) @callIdent) @callRef
|
|
1134
|
+
|
|
1135
|
+
(call
|
|
1136
|
+
method: (constant) @typeIdent) @typeRef
|
|
1137
|
+
`
|
|
1138
|
+
},
|
|
1139
|
+
c: {
|
|
1140
|
+
defs: `
|
|
1141
|
+
(function_definition
|
|
1142
|
+
declarator: (function_declarator
|
|
1143
|
+
declarator: (identifier) @funcName
|
|
1144
|
+
parameters: (parameter_list) @funcParams)) @funcDef
|
|
1145
|
+
|
|
1146
|
+
(type_definition
|
|
1147
|
+
declarator: (type_identifier) @typeName) @typeDef
|
|
1148
|
+
|
|
1149
|
+
(struct_specifier
|
|
1150
|
+
name: (type_identifier) @typeName) @typeDef
|
|
1151
|
+
|
|
1152
|
+
(enum_specifier
|
|
1153
|
+
name: (type_identifier) @typeName) @typeDef
|
|
1154
|
+
`,
|
|
1155
|
+
refs: `
|
|
1156
|
+
(call_expression
|
|
1157
|
+
function: (identifier) @callIdent) @callRef
|
|
1158
|
+
|
|
1159
|
+
(call_expression
|
|
1160
|
+
function: (field_expression
|
|
1161
|
+
field: (field_identifier) @callField)) @callFieldRef
|
|
1162
|
+
|
|
1163
|
+
(type_identifier) @typeIdent
|
|
1164
|
+
`
|
|
1165
|
+
},
|
|
1166
|
+
cpp: {
|
|
1167
|
+
defs: `
|
|
1168
|
+
(function_definition
|
|
1169
|
+
declarator: (function_declarator
|
|
1170
|
+
declarator: (identifier) @funcName
|
|
1171
|
+
parameters: (parameter_list) @funcParams)) @funcDef
|
|
1172
|
+
|
|
1173
|
+
(class_specifier
|
|
1174
|
+
name: (type_identifier) @className) @classDef
|
|
1175
|
+
|
|
1176
|
+
(struct_specifier
|
|
1177
|
+
name: (type_identifier) @className) @classDef
|
|
1178
|
+
|
|
1179
|
+
(type_definition
|
|
1180
|
+
declarator: (type_identifier) @typeName) @typeDef
|
|
1181
|
+
`,
|
|
1182
|
+
refs: `
|
|
1183
|
+
(call_expression
|
|
1184
|
+
function: (identifier) @callIdent) @callRef
|
|
1185
|
+
|
|
1186
|
+
(call_expression
|
|
1187
|
+
function: (field_expression
|
|
1188
|
+
field: (field_identifier) @callField)) @callFieldRef
|
|
1189
|
+
|
|
1190
|
+
(type_identifier) @typeIdent
|
|
1191
|
+
`
|
|
1192
|
+
},
|
|
1193
|
+
java: {
|
|
1194
|
+
defs: `
|
|
1195
|
+
(method_declaration
|
|
1196
|
+
name: (identifier) @methodName
|
|
1197
|
+
parameters: (formal_parameters) @methodParams) @methodDef
|
|
1198
|
+
|
|
1199
|
+
(class_declaration
|
|
1200
|
+
name: (identifier) @className) @classDef
|
|
1201
|
+
|
|
1202
|
+
(interface_declaration
|
|
1203
|
+
name: (identifier) @interfaceName) @interfaceDef
|
|
1204
|
+
|
|
1205
|
+
(constructor_declaration
|
|
1206
|
+
name: (identifier) @funcName
|
|
1207
|
+
parameters: (formal_parameters) @funcParams) @funcDef
|
|
1208
|
+
|
|
1209
|
+
(enum_declaration
|
|
1210
|
+
name: (identifier) @className) @classDef
|
|
1211
|
+
`,
|
|
1212
|
+
refs: `
|
|
1213
|
+
(method_invocation
|
|
1214
|
+
name: (identifier) @callMethod) @callRef
|
|
1215
|
+
|
|
1216
|
+
(object_creation_expression
|
|
1217
|
+
type: (type_identifier) @newIdent) @newRef
|
|
1218
|
+
|
|
1219
|
+
(type_identifier) @typeIdent
|
|
1220
|
+
`
|
|
1221
|
+
},
|
|
1222
|
+
kotlin: {
|
|
1223
|
+
// #251: class_declaration's name is type_identifier; the old extra
|
|
1224
|
+
// `(class_declaration (simple_identifier) @className)` was a bad pattern that
|
|
1225
|
+
// failed query compilation (taking the whole language down). Call refs use
|
|
1226
|
+
// no field name in the shipped grammar (the old `calleeExpression:` field
|
|
1227
|
+
// also failed to compile).
|
|
1228
|
+
defs: `
|
|
1229
|
+
(function_declaration
|
|
1230
|
+
(simple_identifier) @funcName) @funcDef
|
|
1231
|
+
|
|
1232
|
+
(class_declaration
|
|
1233
|
+
(type_identifier) @className) @classDef
|
|
1234
|
+
|
|
1235
|
+
(object_declaration
|
|
1236
|
+
(type_identifier) @className) @classDef
|
|
1237
|
+
`,
|
|
1238
|
+
refs: `
|
|
1239
|
+
(call_expression
|
|
1240
|
+
(simple_identifier) @callIdent) @callRef
|
|
1241
|
+
|
|
1242
|
+
(user_type (type_identifier) @typeIdent)
|
|
1243
|
+
`
|
|
1244
|
+
},
|
|
1245
|
+
dart: {
|
|
1246
|
+
// #251: function_signature/class_definition take their name/params as direct
|
|
1247
|
+
// children in the shipped grammar — the old `name:`/`parameters:` fields did
|
|
1248
|
+
// not exist and failed query compilation. method bodies' inner
|
|
1249
|
+
// function_signature also matches @funcDef (methods surface as functions).
|
|
1250
|
+
defs: `
|
|
1251
|
+
(function_signature
|
|
1252
|
+
(identifier) @funcName
|
|
1253
|
+
(formal_parameter_list) @funcParams) @funcDef
|
|
1254
|
+
|
|
1255
|
+
(class_definition
|
|
1256
|
+
(identifier) @className) @classDef
|
|
1257
|
+
`,
|
|
1258
|
+
refs: `
|
|
1259
|
+
(type_identifier) @typeIdent
|
|
1260
|
+
`
|
|
1261
|
+
},
|
|
1262
|
+
elixir: {
|
|
1263
|
+
defs: `
|
|
1264
|
+
(call
|
|
1265
|
+
target: (identifier) @_kw
|
|
1266
|
+
(arguments
|
|
1267
|
+
(call
|
|
1268
|
+
target: (identifier) @funcName
|
|
1269
|
+
(arguments) @funcParams) @funcDef)
|
|
1270
|
+
(#match? @_kw "^def[pm]?$"))
|
|
1271
|
+
|
|
1272
|
+
(call
|
|
1273
|
+
target: (identifier) @_kw
|
|
1274
|
+
(arguments (alias) @moduleName) @moduleDef
|
|
1275
|
+
(#match? @_kw "^defmodule$"))
|
|
1276
|
+
`,
|
|
1277
|
+
refs: `
|
|
1278
|
+
(call
|
|
1279
|
+
target: (identifier) @callIdent) @callRef
|
|
1280
|
+
|
|
1281
|
+
(alias) @typeIdent
|
|
1282
|
+
`
|
|
1283
|
+
},
|
|
1284
|
+
csharp: {
|
|
1285
|
+
defs: `
|
|
1286
|
+
(method_declaration
|
|
1287
|
+
name: (identifier) @methodName
|
|
1288
|
+
parameters: (parameter_list) @methodParams) @methodDef
|
|
1289
|
+
|
|
1290
|
+
(class_declaration
|
|
1291
|
+
name: (identifier) @className) @classDef
|
|
1292
|
+
|
|
1293
|
+
(struct_declaration
|
|
1294
|
+
name: (identifier) @className) @classDef
|
|
1295
|
+
|
|
1296
|
+
(interface_declaration
|
|
1297
|
+
name: (identifier) @interfaceName) @interfaceDef
|
|
1298
|
+
|
|
1299
|
+
(constructor_declaration
|
|
1300
|
+
name: (identifier) @funcName
|
|
1301
|
+
parameters: (parameter_list) @funcParams) @funcDef
|
|
1302
|
+
|
|
1303
|
+
(enum_declaration
|
|
1304
|
+
name: (identifier) @className) @classDef
|
|
1305
|
+
`,
|
|
1306
|
+
refs: `
|
|
1307
|
+
(invocation_expression
|
|
1308
|
+
function: (identifier) @callIdent) @callRef
|
|
1309
|
+
|
|
1310
|
+
(invocation_expression
|
|
1311
|
+
function: (member_access_expression
|
|
1312
|
+
name: (identifier) @callMethod)) @callMethodRef
|
|
1313
|
+
|
|
1314
|
+
(object_creation_expression
|
|
1315
|
+
type: (identifier) @newIdent) @newRef
|
|
1316
|
+
`
|
|
1317
|
+
},
|
|
1318
|
+
php: {
|
|
1319
|
+
defs: `
|
|
1320
|
+
(function_definition
|
|
1321
|
+
name: (name) @funcName
|
|
1322
|
+
parameters: (formal_parameters) @funcParams) @funcDef
|
|
1323
|
+
|
|
1324
|
+
(method_declaration
|
|
1325
|
+
name: (name) @methodName
|
|
1326
|
+
parameters: (formal_parameters) @methodParams) @methodDef
|
|
1327
|
+
|
|
1328
|
+
(class_declaration
|
|
1329
|
+
name: (name) @className) @classDef
|
|
1330
|
+
|
|
1331
|
+
(interface_declaration
|
|
1332
|
+
name: (name) @interfaceName) @interfaceDef
|
|
1333
|
+
|
|
1334
|
+
(trait_declaration
|
|
1335
|
+
name: (name) @className) @classDef
|
|
1336
|
+
`,
|
|
1337
|
+
refs: `
|
|
1338
|
+
(function_call_expression
|
|
1339
|
+
function: (name) @callIdent) @callRef
|
|
1340
|
+
|
|
1341
|
+
(member_call_expression
|
|
1342
|
+
name: (name) @callMethod) @callMethodRef
|
|
1343
|
+
|
|
1344
|
+
(object_creation_expression
|
|
1345
|
+
(name) @newIdent) @newRef
|
|
1346
|
+
`
|
|
1347
|
+
},
|
|
1348
|
+
swift: {
|
|
1349
|
+
defs: `
|
|
1350
|
+
(function_declaration
|
|
1351
|
+
(simple_identifier) @funcName) @funcDef
|
|
1352
|
+
|
|
1353
|
+
(class_declaration
|
|
1354
|
+
(type_identifier) @className) @classDef
|
|
1355
|
+
|
|
1356
|
+
(protocol_declaration
|
|
1357
|
+
(type_identifier) @interfaceName) @interfaceDef
|
|
1358
|
+
`,
|
|
1359
|
+
refs: `
|
|
1360
|
+
(call_expression
|
|
1361
|
+
(simple_identifier) @callIdent) @callRef
|
|
1362
|
+
|
|
1363
|
+
(navigation_expression
|
|
1364
|
+
(simple_identifier) @callMethod) @callMethodRef
|
|
1365
|
+
|
|
1366
|
+
(type_identifier) @typeIdent
|
|
1367
|
+
`
|
|
1368
|
+
},
|
|
1369
|
+
lua: {
|
|
1370
|
+
// #255: pulled from @tree-sitter-grammars/tree-sitter-lua (the aggregator's
|
|
1371
|
+
// build corrupts once a 2nd grammar loads). That grammar names function defs
|
|
1372
|
+
// `function_declaration` — the name is either a direct (identifier) (global /
|
|
1373
|
+
// `local function`) or a (dot_index_expression) for `function M.run`. Calls
|
|
1374
|
+
// are `function_call` with an (identifier) or (dot_index_expression) callee.
|
|
1375
|
+
defs: `
|
|
1376
|
+
(function_declaration
|
|
1377
|
+
(identifier) @funcName) @funcDef
|
|
1378
|
+
|
|
1379
|
+
(function_declaration
|
|
1380
|
+
(dot_index_expression) @funcName) @funcDef
|
|
1381
|
+
`,
|
|
1382
|
+
refs: `
|
|
1383
|
+
(function_call (identifier) @callIdent) @callRef
|
|
1384
|
+
|
|
1385
|
+
(function_call (dot_index_expression) @callIdent) @callRef
|
|
1386
|
+
`
|
|
1387
|
+
},
|
|
1388
|
+
ocaml: {
|
|
1389
|
+
// #251: let_binding holds value_name as a direct child (no `pattern:` /
|
|
1390
|
+
// value_pattern wrapper), and module_binding's module_name is a direct child
|
|
1391
|
+
// (no `name:` field) — the old patterns failed query compilation.
|
|
1392
|
+
defs: `
|
|
1393
|
+
(value_definition
|
|
1394
|
+
(let_binding (value_name) @funcName)) @funcDef
|
|
1395
|
+
|
|
1396
|
+
(module_definition
|
|
1397
|
+
(module_binding (module_name) @moduleName)) @moduleDef
|
|
1398
|
+
`,
|
|
1399
|
+
refs: `
|
|
1400
|
+
(application_expression
|
|
1401
|
+
(value_path (value_name) @callIdent)) @callRef
|
|
1402
|
+
|
|
1403
|
+
(value_path (value_name) @callIdent) @callRef
|
|
1404
|
+
`
|
|
1405
|
+
},
|
|
1406
|
+
zig: {
|
|
1407
|
+
defs: `
|
|
1408
|
+
(function_declaration
|
|
1409
|
+
(identifier) @funcName) @funcDef
|
|
1410
|
+
`,
|
|
1411
|
+
// #251: the old `field_access` node name doesn't exist in this grammar and
|
|
1412
|
+
// failed to compile; call_expression refs are valid.
|
|
1413
|
+
refs: `
|
|
1414
|
+
(call_expression
|
|
1415
|
+
(identifier) @callIdent) @callRef
|
|
1416
|
+
`
|
|
1417
|
+
},
|
|
1418
|
+
bash: {
|
|
1419
|
+
defs: `
|
|
1420
|
+
(function_definition
|
|
1421
|
+
name: (word) @funcName) @funcDef
|
|
1422
|
+
`,
|
|
1423
|
+
refs: `
|
|
1424
|
+
(command
|
|
1425
|
+
name: (command_name (word) @callIdent)) @callRef
|
|
1426
|
+
`
|
|
1427
|
+
},
|
|
1428
|
+
// #1522: tree-sitter-cue (eonpatapon/tree-sitter-cue, vendored per #1520) has
|
|
1429
|
+
// no `label:`/`value:` field names on `field` — verified directly against the
|
|
1430
|
+
// committed wasm (`value:` fails query compilation with "Bad field name");
|
|
1431
|
+
// only `let_clause`'s `left:`/`right:` and `call_expression`'s `function:`
|
|
1432
|
+
// are real fields. A `#Definition` is not a distinct node kind: `#Service`
|
|
1433
|
+
// parses as a plain (identifier) whose text happens to start with "#", so
|
|
1434
|
+
// definitions are told apart from ordinary struct fields with a `#match?`/
|
|
1435
|
+
// `#not-match?` text predicate rather than a grammar-level distinction —
|
|
1436
|
+
// both predicates must sit INSIDE the pattern's own parens (this repo's
|
|
1437
|
+
// tree-sitter gotcha). `#Definition`s map to `type` (the closest existing
|
|
1438
|
+
// SymbolKind to a reusable CUE schema); plain fields map to `property`;
|
|
1439
|
+
// `let` bindings map to `variable`.
|
|
1440
|
+
//
|
|
1441
|
+
// Two known upstream grammar rough edges (#1522 review round 1, F4;
|
|
1442
|
+
// docs/language-coverage.md's CUE row has the same note, kept in sync):
|
|
1443
|
+
// (1) an aliased field label (`X=name: value`) exposes BOTH identifiers
|
|
1444
|
+
// as untagged siblings under one `label` node — `(label alias:
|
|
1445
|
+
// (identifier) (identifier))` — with no field/structural way to tell the
|
|
1446
|
+
// alias from the real name, so `fieldDef` spuriously captures the alias
|
|
1447
|
+
// too, alongside the correct symbol for `name`. (2) a multi-hash raw
|
|
1448
|
+
// string (`##"..."##`, 2+ `#` delimiters) mis-parses regardless of
|
|
1449
|
+
// content — the field's value becomes a bare `identifier` instead of a
|
|
1450
|
+
// `string`, which can surface as a spurious `#`-prefixed ref via the
|
|
1451
|
+
// `typeIdent` pattern below. Both are grammar bugs, not query bugs — no
|
|
1452
|
+
// query change here can distinguish what the parse tree doesn't.
|
|
1453
|
+
cue: {
|
|
1454
|
+
defs: `
|
|
1455
|
+
(field
|
|
1456
|
+
(label (identifier) @defName)
|
|
1457
|
+
(#match? @defName "^#")) @defDef
|
|
1458
|
+
|
|
1459
|
+
(field
|
|
1460
|
+
(label (identifier) @fieldName)
|
|
1461
|
+
(value (_)) @fieldValue
|
|
1462
|
+
(#not-match? @fieldName "^#")) @fieldDef
|
|
1463
|
+
|
|
1464
|
+
(let_clause
|
|
1465
|
+
left: (identifier) @letName
|
|
1466
|
+
right: (_) @letValue) @letDef
|
|
1467
|
+
`,
|
|
1468
|
+
// `#Service` used as a value (e.g. `#Other: #Service & {...}`) is the same
|
|
1469
|
+
// (identifier) node kind as its own definition label — there is no
|
|
1470
|
+
// separate reference/definition node kind in this grammar, so the
|
|
1471
|
+
// definition's own label also matches this pattern, mirroring the same
|
|
1472
|
+
// self-match already accepted for swift/java's broad `type_identifier`
|
|
1473
|
+
// ref patterns above.
|
|
1474
|
+
refs: `
|
|
1475
|
+
((identifier) @typeIdent
|
|
1476
|
+
(#match? @typeIdent "^#"))
|
|
1477
|
+
|
|
1478
|
+
(call_expression
|
|
1479
|
+
function: (identifier) @callIdent) @callRef
|
|
1480
|
+
|
|
1481
|
+
(call_expression
|
|
1482
|
+
function: (selector_expression (identifier) (identifier) @callMethod)) @callMethodRef
|
|
1483
|
+
`
|
|
1484
|
+
}
|
|
1485
|
+
};
|
|
1486
|
+
SYMBOL_QUERIES.tsx = SYMBOL_QUERIES.typescript;
|
|
1487
|
+
SYMBOL_QUERIES.javascript = {
|
|
1488
|
+
defs: `
|
|
1489
|
+
;; Function declarations: function foo(params) { }
|
|
1490
|
+
(function_declaration
|
|
1491
|
+
name: (identifier) @funcName
|
|
1492
|
+
parameters: (formal_parameters) @funcParams
|
|
1493
|
+
body: (statement_block) @funcBody) @funcDef
|
|
1494
|
+
|
|
1495
|
+
;; Arrow functions: const foo = (params) => { }
|
|
1496
|
+
(variable_declarator
|
|
1497
|
+
name: (identifier) @arrowName
|
|
1498
|
+
value: (arrow_function
|
|
1499
|
+
parameters: (formal_parameters) @arrowParams
|
|
1500
|
+
body: (_) @arrowBody)) @arrowDef
|
|
1501
|
+
|
|
1502
|
+
;; Class declarations: class Foo { } \u2014 (identifier) name on this grammar.
|
|
1503
|
+
(class_declaration
|
|
1504
|
+
name: (identifier) @className) @classDef
|
|
1505
|
+
|
|
1506
|
+
;; Method definitions: class Foo { bar() { } }
|
|
1507
|
+
(method_definition
|
|
1508
|
+
name: (property_identifier) @methodName
|
|
1509
|
+
parameters: (formal_parameters) @methodParams) @methodDef
|
|
1510
|
+
`,
|
|
1511
|
+
refs: `
|
|
1512
|
+
;; Function/method calls: foo() or obj.bar()
|
|
1513
|
+
(call_expression
|
|
1514
|
+
function: (identifier) @callIdent) @callRef
|
|
1515
|
+
|
|
1516
|
+
(call_expression
|
|
1517
|
+
function: (member_expression
|
|
1518
|
+
object: (_)
|
|
1519
|
+
property: (property_identifier) @callMethod)) @callMethodRef
|
|
1520
|
+
|
|
1521
|
+
;; New expressions: new Foo()
|
|
1522
|
+
(new_expression
|
|
1523
|
+
constructor: (identifier) @newIdent) @newRef
|
|
1524
|
+
`
|
|
1525
|
+
};
|
|
1526
|
+
function symbolExtractionGrammar(kind, filePath) {
|
|
1527
|
+
if (!kind)
|
|
1528
|
+
return void 0;
|
|
1529
|
+
const kindGrammar = KIND_TO_GRAMMAR[kind];
|
|
1530
|
+
if (!kindGrammar)
|
|
1531
|
+
return void 0;
|
|
1532
|
+
const byExtension = filePath ? EXTENSION_TO_GRAMMAR[path4.extname(filePath).toLowerCase()] : void 0;
|
|
1533
|
+
const grammar = byExtension ?? kindGrammar;
|
|
1534
|
+
return grammar in SYMBOL_QUERIES ? grammar : void 0;
|
|
1535
|
+
}
|
|
1536
|
+
var JS_TS_IMPORT_QUERY = `
|
|
1537
|
+
(comment) @importComment
|
|
1538
|
+
(import_statement source: (string) @importSource)
|
|
1539
|
+
(export_statement source: (string) @importSource)
|
|
1540
|
+
(call_expression
|
|
1541
|
+
function: (import)
|
|
1542
|
+
arguments: (arguments . (string) @importSource))
|
|
1543
|
+
(call_expression
|
|
1544
|
+
function: (identifier) @_m
|
|
1545
|
+
arguments: (arguments . (string) @importSource)
|
|
1546
|
+
(#eq? @_m "require"))
|
|
1547
|
+
`;
|
|
1548
|
+
var IMPORT_QUERIES = {
|
|
1549
|
+
// ESM import + re-export source strings; `source:` is a (string) on both the
|
|
1550
|
+
// typescript and tsx grammars (validated). parseImportMatch strips the quotes.
|
|
1551
|
+
// #4438: a dynamic `import("x")` and a `require("x")` call with a string
|
|
1552
|
+
// argument count too, as the review graph's import facts already count them
|
|
1553
|
+
// (`dispatch/facts/import-facts.ts`): the inline-blocker sweep and the
|
|
1554
|
+
// created-module producer read this query, and a dependency it missed
|
|
1555
|
+
// stayed stale there. Node names probed on the shipped typescript, tsx and
|
|
1556
|
+
// javascript grammars; a pattern one grammar lacks would fail the whole
|
|
1557
|
+
// query. The ambient-module extension is TS/TSX-only; JavaScript uses
|
|
1558
|
+
// the common query. `import x = require()` remains outside this query.
|
|
1559
|
+
typescript: `${JS_TS_IMPORT_QUERY}
|
|
1560
|
+
(ambient_declaration (module name: (string) @importSource))`,
|
|
1561
|
+
tsx: `${JS_TS_IMPORT_QUERY}
|
|
1562
|
+
(ambient_declaration (module name: (string) @importSource))`,
|
|
1563
|
+
// #include "foo.h" (string_literal) and #include <stdio.h> (system_lib_string)
|
|
1564
|
+
// on both the c and cpp grammars (validated). parseImportMatch strips the quotes
|
|
1565
|
+
// from the local form; the system form keeps its <> so the resolver/bucketer can
|
|
1566
|
+
// tell a system header from a local include.
|
|
1567
|
+
c: `
|
|
1568
|
+
(preproc_include path: (string_literal) @importSource)
|
|
1569
|
+
(preproc_include path: (system_lib_string) @importSource)
|
|
1570
|
+
`,
|
|
1571
|
+
cpp: `
|
|
1572
|
+
(preproc_include path: (string_literal) @importSource)
|
|
1573
|
+
(preproc_include path: (system_lib_string) @importSource)
|
|
1574
|
+
`,
|
|
1575
|
+
python: `
|
|
1576
|
+
(import_statement name: (dotted_name) @importSource)
|
|
1577
|
+
(import_statement name: (aliased_import name: (dotted_name) @importSource))
|
|
1578
|
+
(import_from_statement module_name: (dotted_name) @importSource)
|
|
1579
|
+
(import_from_statement module_name: (relative_import) @importSource)
|
|
1580
|
+
`,
|
|
1581
|
+
go: `(import_spec path: (interpreted_string_literal) @importSource)`,
|
|
1582
|
+
rust: `(use_declaration argument: (_) @importSource)`,
|
|
1583
|
+
java: `(import_declaration (scoped_identifier) @importSource)`,
|
|
1584
|
+
kotlin: `(import_header (identifier) @importSource)`,
|
|
1585
|
+
csharp: `
|
|
1586
|
+
(using_directive (identifier) @importSource)
|
|
1587
|
+
(using_directive (qualified_name) @importSource)
|
|
1588
|
+
`,
|
|
1589
|
+
swift: `(import_declaration (identifier) @importSource)`,
|
|
1590
|
+
php: `(namespace_use_clause (name) @importSource)`,
|
|
1591
|
+
ocaml: `(open_module (module_path) @importSource)`,
|
|
1592
|
+
dart: `(import_specification (configurable_uri (uri (string_literal) @importSource)))`,
|
|
1593
|
+
// local x = require("mod.a") — a plain function call, not a statement. The
|
|
1594
|
+
// pulled @tree-sitter-grammars grammar (#255) names it function_call.
|
|
1595
|
+
lua: `
|
|
1596
|
+
(function_call
|
|
1597
|
+
(identifier) @_m
|
|
1598
|
+
(arguments (string) @importSource)
|
|
1599
|
+
(#match? @_m "^require$"))
|
|
1600
|
+
`,
|
|
1601
|
+
// require "x" / require_relative "x" — covers both via the "^require" prefix.
|
|
1602
|
+
ruby: `
|
|
1603
|
+
(call
|
|
1604
|
+
method: (identifier) @_m
|
|
1605
|
+
(argument_list (string (string_content) @importSource))
|
|
1606
|
+
(#match? @_m "^require"))
|
|
1607
|
+
`,
|
|
1608
|
+
// @import("std") — a builtin call, not a statement.
|
|
1609
|
+
zig: `
|
|
1610
|
+
(builtin_function (builtin_identifier) @_m
|
|
1611
|
+
(arguments (string (string_content) @importSource))
|
|
1612
|
+
(#match? @_m "^@import$"))
|
|
1613
|
+
`,
|
|
1614
|
+
// import/alias/require/use Foo — macro calls; excludes defmodule and friends.
|
|
1615
|
+
elixir: `
|
|
1616
|
+
(call target: (identifier) @_m
|
|
1617
|
+
(arguments (alias) @importSource)
|
|
1618
|
+
(#match? @_m "^(import|alias|require|use)$"))
|
|
1619
|
+
`,
|
|
1620
|
+
// source ./x.sh and . ./x.sh — the two POSIX file-include builtins.
|
|
1621
|
+
bash: `
|
|
1622
|
+
(command (command_name (word) @_m)
|
|
1623
|
+
(word) @importSource
|
|
1624
|
+
(#match? @_m "^(source|\\.)$"))
|
|
1625
|
+
`,
|
|
1626
|
+
// import "strings" / import x "encoding/json" — `import_spec`'s `path:`
|
|
1627
|
+
// field is verified against the committed wasm. Block-form
|
|
1628
|
+
// (`import (\n\t"strings"\n)`) nests each `import_spec` one level deeper,
|
|
1629
|
+
// under `import_spec_list`, rather than as a direct child of
|
|
1630
|
+
// `import_declaration` — a second pattern is needed, not a reshaped first
|
|
1631
|
+
// one (#1522 review round 1, F2: the single-line-only pattern extracted
|
|
1632
|
+
// zero imports from the block form).
|
|
1633
|
+
cue: `
|
|
1634
|
+
(import_declaration (import_spec path: (string) @importSource))
|
|
1635
|
+
(import_declaration (import_spec_list (import_spec path: (string) @importSource)))
|
|
1636
|
+
`
|
|
1637
|
+
};
|
|
1638
|
+
IMPORT_QUERIES.javascript = JS_TS_IMPORT_QUERY;
|
|
1639
|
+
function parseImportMatch(match) {
|
|
1640
|
+
const imports = [];
|
|
1641
|
+
for (const capture of match.captures) {
|
|
1642
|
+
if (capture.name === "importComment") {
|
|
1643
|
+
for (const tag of capture.node.text.matchAll(/@import\b[^@]*?\bfrom\s*(["'])([^"'\r\n]+)\1/g)) {
|
|
1644
|
+
imports.push({
|
|
1645
|
+
source: tag[2],
|
|
1646
|
+
line: capture.node.startPosition.row + 1
|
|
1647
|
+
});
|
|
1648
|
+
}
|
|
1649
|
+
continue;
|
|
1650
|
+
}
|
|
1651
|
+
if (capture.name !== "importSource")
|
|
1652
|
+
continue;
|
|
1653
|
+
const raw = String(capture.node.text ?? "").trim();
|
|
1654
|
+
const source = raw.replace(/^["'`]+|["'`]+$/g, "");
|
|
1655
|
+
if (!source)
|
|
1656
|
+
continue;
|
|
1657
|
+
imports.push({ source, line: capture.node.startPosition.row + 1 });
|
|
1658
|
+
}
|
|
1659
|
+
return imports;
|
|
1660
|
+
}
|
|
1661
|
+
var TreeSitterSymbolExtractor = class _TreeSitterSymbolExtractor {
|
|
1662
|
+
languageId;
|
|
1663
|
+
client;
|
|
1664
|
+
// biome-ignore lint/suspicious/noExplicitAny: Query type from web-tree-sitter
|
|
1665
|
+
defQuery = null;
|
|
1666
|
+
// biome-ignore lint/suspicious/noExplicitAny: Query type from web-tree-sitter
|
|
1667
|
+
refQuery = null;
|
|
1668
|
+
// biome-ignore lint/suspicious/noExplicitAny: Query type from web-tree-sitter
|
|
1669
|
+
importQuery = null;
|
|
1670
|
+
constructor(languageId, client) {
|
|
1671
|
+
this.languageId = languageId;
|
|
1672
|
+
this.client = client;
|
|
1673
|
+
}
|
|
1674
|
+
async init() {
|
|
1675
|
+
try {
|
|
1676
|
+
if (!await this.client.isLanguageSupported(this.languageId))
|
|
1677
|
+
return false;
|
|
1678
|
+
const language = this.client.getLanguage(this.languageId);
|
|
1679
|
+
if (!language)
|
|
1680
|
+
return false;
|
|
1681
|
+
const { Query } = await loadWebTreeSitter();
|
|
1682
|
+
const queries = SYMBOL_QUERIES[this.languageId];
|
|
1683
|
+
if (queries) {
|
|
1684
|
+
this.defQuery = this.compileQuery(Query, language, queries.defs, "defs");
|
|
1685
|
+
this.refQuery = this.compileQuery(Query, language, queries.refs, "refs");
|
|
1686
|
+
}
|
|
1687
|
+
const importQuerySrc = IMPORT_QUERIES[this.languageId];
|
|
1688
|
+
if (importQuerySrc) {
|
|
1689
|
+
this.importQuery = this.compileQuery(Query, language, importQuerySrc, "imports");
|
|
1690
|
+
}
|
|
1691
|
+
return Boolean(this.defQuery || this.importQuery);
|
|
1692
|
+
} catch (err) {
|
|
1693
|
+
this.client.reportWasmAbort(err);
|
|
1694
|
+
logTreeSitterDiagnostic({
|
|
1695
|
+
subsystem: "symbol-extractor",
|
|
1696
|
+
languageId: this.languageId,
|
|
1697
|
+
message: `Failed to init ${this.languageId}: ${err instanceof Error ? err.message : String(err)}`
|
|
1698
|
+
});
|
|
1699
|
+
return false;
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
// biome-ignore lint/suspicious/noExplicitAny: web-tree-sitter Query/Language types
|
|
1703
|
+
compileQuery(Query, language, src, label) {
|
|
1704
|
+
const input = wasmQueryInput(`${this.languageId}:${label}:${src}`);
|
|
1705
|
+
try {
|
|
1706
|
+
return new Query(language, src);
|
|
1707
|
+
} catch (err) {
|
|
1708
|
+
if (this.client.reportWasmAbort(err, input))
|
|
1709
|
+
throw err;
|
|
1710
|
+
logTreeSitterDiagnostic({
|
|
1711
|
+
subsystem: "symbol-extractor",
|
|
1712
|
+
languageId: this.languageId,
|
|
1713
|
+
message: `${this.languageId} ${label} query failed: ${err.message}`,
|
|
1714
|
+
metadata: { query: label }
|
|
1715
|
+
});
|
|
1716
|
+
return null;
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
/**
|
|
1720
|
+
* Extract symbols from a parsed tree-sitter tree
|
|
1721
|
+
*/
|
|
1722
|
+
extract(tree, filePath, content) {
|
|
1723
|
+
const symbols = [];
|
|
1724
|
+
const refs = [];
|
|
1725
|
+
const relativePath = path4.relative(process.cwd(), filePath);
|
|
1726
|
+
if (this.defQuery) {
|
|
1727
|
+
let lines;
|
|
1728
|
+
const lineAt = (row) => (lines ??= content.split("\n"))[row] ?? "";
|
|
1729
|
+
for (const match of this.queryMatches(this.defQuery, tree.rootNode)) {
|
|
1730
|
+
const symbol = this.parseDefMatch(match, relativePath, lineAt);
|
|
1731
|
+
if (symbol)
|
|
1732
|
+
symbols.push(symbol);
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
if (this.refQuery) {
|
|
1736
|
+
for (const match of this.queryMatches(this.refQuery, tree.rootNode)) {
|
|
1737
|
+
const ref = this.parseRefMatch(match, relativePath);
|
|
1738
|
+
if (ref)
|
|
1739
|
+
refs.push(ref);
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
return {
|
|
1743
|
+
symbols,
|
|
1744
|
+
refs,
|
|
1745
|
+
imports: this.extractImports(tree),
|
|
1746
|
+
coverage: {
|
|
1747
|
+
definitions: this.defQuery ? "complete" : "unavailable",
|
|
1748
|
+
references: this.refQuery ? "complete" : "unavailable",
|
|
1749
|
+
imports: this.importQuery ? "complete" : "unavailable"
|
|
1750
|
+
}
|
|
1751
|
+
};
|
|
1752
|
+
}
|
|
1753
|
+
/**
|
|
1754
|
+
* The import references alone (optional: only for languages with an
|
|
1755
|
+
* IMPORT_QUERIES entry). #4368 r1 F2: a caller that needs only imports
|
|
1756
|
+
* (the forward-import resolver) runs this instead of {@link extract}, whose
|
|
1757
|
+
* definitions pass is the cost of a large file.
|
|
1758
|
+
*/
|
|
1759
|
+
// biome-ignore lint/suspicious/noExplicitAny: Tree type
|
|
1760
|
+
extractImports(tree) {
|
|
1761
|
+
const imports = [];
|
|
1762
|
+
if (!this.importQuery)
|
|
1763
|
+
return imports;
|
|
1764
|
+
for (const match of this.queryMatches(this.importQuery, tree.rootNode)) {
|
|
1765
|
+
imports.push(...parseImportMatch(match));
|
|
1766
|
+
}
|
|
1767
|
+
return imports;
|
|
1768
|
+
}
|
|
1769
|
+
queryMatches(query, rootNode) {
|
|
1770
|
+
try {
|
|
1771
|
+
return query.matches(rootNode);
|
|
1772
|
+
} catch (error) {
|
|
1773
|
+
this.client.reportWasmAbort(error);
|
|
1774
|
+
throw error;
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
// biome-ignore lint/suspicious/noExplicitAny: Match type
|
|
1778
|
+
parseDefMatch(match, filePath, lineAt) {
|
|
1779
|
+
const captures = {};
|
|
1780
|
+
for (const capture of match.captures) {
|
|
1781
|
+
captures[capture.name] = {
|
|
1782
|
+
text: capture.node.text,
|
|
1783
|
+
// biome-ignore lint/suspicious/noExplicitAny: Node type
|
|
1784
|
+
node: capture.node
|
|
1785
|
+
};
|
|
1786
|
+
}
|
|
1787
|
+
let name;
|
|
1788
|
+
let kind;
|
|
1789
|
+
let params;
|
|
1790
|
+
let defNode;
|
|
1791
|
+
if (captures.funcName) {
|
|
1792
|
+
name = captures.funcName.text;
|
|
1793
|
+
kind = "function";
|
|
1794
|
+
params = captures.funcParams?.text;
|
|
1795
|
+
defNode = captures.funcDef?.node;
|
|
1796
|
+
} else if (captures.arrowName) {
|
|
1797
|
+
name = captures.arrowName.text;
|
|
1798
|
+
kind = "function";
|
|
1799
|
+
params = captures.arrowParams?.text;
|
|
1800
|
+
defNode = captures.arrowDef?.node;
|
|
1801
|
+
} else if (captures.className) {
|
|
1802
|
+
name = captures.className.text;
|
|
1803
|
+
kind = "class";
|
|
1804
|
+
defNode = captures.classDef?.node;
|
|
1805
|
+
} else if (captures.methodName) {
|
|
1806
|
+
name = captures.methodName.text;
|
|
1807
|
+
kind = "method";
|
|
1808
|
+
params = captures.methodParams?.text;
|
|
1809
|
+
defNode = captures.methodDef?.node;
|
|
1810
|
+
} else if (captures.interfaceName) {
|
|
1811
|
+
name = captures.interfaceName.text;
|
|
1812
|
+
kind = "interface";
|
|
1813
|
+
defNode = captures.interfaceDef?.node;
|
|
1814
|
+
} else if (captures.typeName) {
|
|
1815
|
+
name = captures.typeName.text;
|
|
1816
|
+
kind = "type";
|
|
1817
|
+
defNode = captures.typeDef?.node;
|
|
1818
|
+
} else if (captures.moduleName) {
|
|
1819
|
+
name = captures.moduleName.text;
|
|
1820
|
+
kind = "class";
|
|
1821
|
+
defNode = captures.moduleDef?.node;
|
|
1822
|
+
} else if (captures.defName) {
|
|
1823
|
+
name = captures.defName.text;
|
|
1824
|
+
kind = "type";
|
|
1825
|
+
defNode = captures.defDef?.node;
|
|
1826
|
+
} else if (captures.fieldName) {
|
|
1827
|
+
name = captures.fieldName.text;
|
|
1828
|
+
kind = "property";
|
|
1829
|
+
defNode = captures.fieldDef?.node;
|
|
1830
|
+
} else if (captures.letName) {
|
|
1831
|
+
name = captures.letName.text;
|
|
1832
|
+
kind = "variable";
|
|
1833
|
+
defNode = captures.letDef?.node;
|
|
1834
|
+
}
|
|
1835
|
+
if (!name || !kind || !defNode)
|
|
1836
|
+
return null;
|
|
1837
|
+
const isExported = this.isExported(defNode, lineAt);
|
|
1838
|
+
const signature = params ? this.extractSignature(params, kind) : void 0;
|
|
1839
|
+
const local = this.isFunctionLocal(defNode);
|
|
1840
|
+
const visibility = kind === "method" ? this.detectVisibility(defNode, name) : void 0;
|
|
1841
|
+
const decorators = this.extractDecorators(defNode);
|
|
1842
|
+
const isAsync = (kind === "function" || kind === "method") && this.isAsyncDecl(defNode);
|
|
1843
|
+
const docInfo = this.extractDocCommentInfo(defNode);
|
|
1844
|
+
return {
|
|
1845
|
+
id: `${filePath}:${name}`,
|
|
1846
|
+
name,
|
|
1847
|
+
kind,
|
|
1848
|
+
filePath,
|
|
1849
|
+
line: defNode.startPosition.row + 1,
|
|
1850
|
+
endLine: defNode.endPosition.row + 1,
|
|
1851
|
+
column: defNode.startPosition.column + 1,
|
|
1852
|
+
signature,
|
|
1853
|
+
isExported,
|
|
1854
|
+
...local ? { local: true } : {},
|
|
1855
|
+
...visibility ? { visibility } : {},
|
|
1856
|
+
...decorators.length > 0 ? { decorators } : {},
|
|
1857
|
+
...isAsync ? { isAsync: true } : {},
|
|
1858
|
+
...docInfo ? { doc: docInfo.text, docStartLine: docInfo.startLine } : {}
|
|
1859
|
+
};
|
|
1860
|
+
}
|
|
1861
|
+
// Comment node kinds across grammars that use tree-sitter's conventional
|
|
1862
|
+
// name. Line/block comments (`//`, `/* */`, `/** */`, `#`) all parse to a
|
|
1863
|
+
// single "comment" node type in every grammar checked (JS/TS, Python, Go,
|
|
1864
|
+
// Rust, Java, Kotlin, C#, C/C++, Ruby, PHP, Swift, Lua) — no per-grammar
|
|
1865
|
+
// query needed, unlike decorators/annotations which vary by node shape.
|
|
1866
|
+
static COMMENT_NODE_KIND = "comment";
|
|
1867
|
+
/**
|
|
1868
|
+
* First sentence/line of the doc comment immediately preceding a declaration,
|
|
1869
|
+
* whitespace-collapsed and capped ~120 chars — the highest-signal token for an
|
|
1870
|
+
* agent deciding which symbol to read (#512). Structural, not per-language:
|
|
1871
|
+
* walks contiguous preceding-sibling `comment` nodes (same traversal shape as
|
|
1872
|
+
* `extractDecorators`), stopping at the nearest non-comment/non-decorator named
|
|
1873
|
+
* sibling, and takes the LAST contiguous comment block (the one directly above
|
|
1874
|
+
* the declaration — an earlier unrelated comment separated by blank lines still
|
|
1875
|
+
* parses as a preceding sibling, but a real gap makes it not "attached").
|
|
1876
|
+
* JSDoc/TSDoc block comments strip the leading/trailing block markers and
|
|
1877
|
+
* per-line `*` gutters; plain `//`/`#` line comments strip the marker only.
|
|
1878
|
+
* Returns undefined when no comment is directly attached. Returns both the
|
|
1879
|
+
* summarized text (`doc`) and the 1-based start line of the attached comment
|
|
1880
|
+
* block (`docStartLine`, #523) — readSymbol extends its returned range to that
|
|
1881
|
+
* line so an agent reading a symbol sees its contract, not just its body.
|
|
1882
|
+
*/
|
|
1883
|
+
// biome-ignore lint/suspicious/noExplicitAny: web-tree-sitter node
|
|
1884
|
+
extractDocCommentInfo(declNode) {
|
|
1885
|
+
const isComment = (n) => !!n && n.type === _TreeSitterSymbolExtractor.COMMENT_NODE_KIND;
|
|
1886
|
+
const isDeco = (n) => !!n && _TreeSitterSymbolExtractor.DECORATOR_NODE_KINDS.has(n.type);
|
|
1887
|
+
const sameStartRow = (a, b) => a?.startPosition?.row === b?.startPosition?.row;
|
|
1888
|
+
let node = declNode;
|
|
1889
|
+
for (let hops = 0; hops < 4; hops++) {
|
|
1890
|
+
const parent = node.parent;
|
|
1891
|
+
if (!parent || !sameStartRow(parent, node))
|
|
1892
|
+
break;
|
|
1893
|
+
node = parent;
|
|
1894
|
+
}
|
|
1895
|
+
let cursor = node.previousSibling;
|
|
1896
|
+
while (cursor && isDeco(cursor))
|
|
1897
|
+
cursor = cursor.previousSibling;
|
|
1898
|
+
let boundaryRow = node.startPosition?.row;
|
|
1899
|
+
const run = [];
|
|
1900
|
+
while (cursor && isComment(cursor)) {
|
|
1901
|
+
const gapRows = boundaryRow !== void 0 && typeof cursor.endPosition?.row === "number" ? boundaryRow - cursor.endPosition.row : 0;
|
|
1902
|
+
if (gapRows > 1)
|
|
1903
|
+
break;
|
|
1904
|
+
run.push(cursor);
|
|
1905
|
+
boundaryRow = cursor.startPosition?.row;
|
|
1906
|
+
cursor = cursor.previousSibling;
|
|
1907
|
+
}
|
|
1908
|
+
if (run.length === 0)
|
|
1909
|
+
return void 0;
|
|
1910
|
+
run.reverse();
|
|
1911
|
+
const raw = run.map((n) => String(n.text ?? "")).join("\n");
|
|
1912
|
+
const text = this.summarizeDocComment(raw);
|
|
1913
|
+
if (!text)
|
|
1914
|
+
return void 0;
|
|
1915
|
+
const startLine = (run[0].startPosition?.row ?? 0) + 1;
|
|
1916
|
+
return { text, startLine };
|
|
1917
|
+
}
|
|
1918
|
+
static DOC_MAX_CHARS = 120;
|
|
1919
|
+
/** Strip comment markers, take the first sentence (or first line if no
|
|
1920
|
+
* sentence terminator), collapse whitespace, cap at ~120 chars. */
|
|
1921
|
+
summarizeDocComment(raw) {
|
|
1922
|
+
const cleaned = raw.replace(/^\s*\/\*\*?/, "").replace(/\*\/\s*$/, "").split(/\r?\n/).map((line) => line.replace(/^\s*(\*|\/\/\/?|#)\s?/, "").trimEnd()).filter((line) => line.trim().length > 0).join(" ").trim();
|
|
1923
|
+
if (!cleaned)
|
|
1924
|
+
return void 0;
|
|
1925
|
+
const sentenceMatch = cleaned.match(/^.*?[.!?](?:\s|$)/);
|
|
1926
|
+
let first = (sentenceMatch ? sentenceMatch[0] : cleaned).trim();
|
|
1927
|
+
first = first.replace(/\s+/g, " ");
|
|
1928
|
+
if (first.length > _TreeSitterSymbolExtractor.DOC_MAX_CHARS) {
|
|
1929
|
+
first = `${first.slice(0, _TreeSitterSymbolExtractor.DOC_MAX_CHARS - 1).trimEnd()}\u2026`;
|
|
1930
|
+
}
|
|
1931
|
+
return first || void 0;
|
|
1932
|
+
}
|
|
1933
|
+
/**
|
|
1934
|
+
* Structural async/suspend detection: an `async` keyword node (Python/JS/TS
|
|
1935
|
+
* have it as a direct child of the declaration) or `async`/`suspend` inside a
|
|
1936
|
+
* `*modifiers*` container (Rust `function_modifiers`, Kotlin/Java/C#
|
|
1937
|
+
* `modifiers`). Conservative — a grammar that spells it differently just
|
|
1938
|
+
* yields false (no false positives).
|
|
1939
|
+
*/
|
|
1940
|
+
// biome-ignore lint/suspicious/noExplicitAny: web-tree-sitter node
|
|
1941
|
+
isAsyncDecl(node) {
|
|
1942
|
+
const isAsyncTok = (n) => !!n && (n.type === "async" || n.type === "suspend" || /modifier/.test(String(n.type)) && /^(?:async|suspend)$/.test(String(n.text ?? "").trim()));
|
|
1943
|
+
for (const child of node.children ?? []) {
|
|
1944
|
+
if (isAsyncTok(child))
|
|
1945
|
+
return true;
|
|
1946
|
+
if (/modifiers/.test(String(child?.type))) {
|
|
1947
|
+
for (const gc of child.children ?? []) {
|
|
1948
|
+
if (isAsyncTok(gc))
|
|
1949
|
+
return true;
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
return false;
|
|
1954
|
+
}
|
|
1955
|
+
// Decorator/attribute/annotation node kinds across grammars. Python/TS/JS use
|
|
1956
|
+
// `decorator`; Rust `attribute_item`; Java/Kotlin/C# `marker_annotation` /
|
|
1957
|
+
// `annotation` (often nested inside a `modifiers` container).
|
|
1958
|
+
static DECORATOR_NODE_KINDS = /* @__PURE__ */ new Set([
|
|
1959
|
+
"decorator",
|
|
1960
|
+
"attribute_item",
|
|
1961
|
+
"marker_annotation",
|
|
1962
|
+
"annotation"
|
|
1963
|
+
]);
|
|
1964
|
+
// Containers that hold annotations as children (Java/Kotlin `modifiers`).
|
|
1965
|
+
static MODIFIERS_CONTAINER_KINDS = /* @__PURE__ */ new Set(["modifiers"]);
|
|
1966
|
+
/**
|
|
1967
|
+
* Decorators/attributes/annotations attached to a declaration node, in source
|
|
1968
|
+
* order. Structural (tree-sitter), not text heuristics, so it handles the three
|
|
1969
|
+
* placement shapes seen across grammars:
|
|
1970
|
+
* - preceding siblings: Python (`decorated_definition` children), Rust
|
|
1971
|
+
* (`attribute_item`), TS methods (`decorator`);
|
|
1972
|
+
* - own children: TS class decorators;
|
|
1973
|
+
* - nested in a `modifiers` container: Java/Kotlin/C# annotations.
|
|
1974
|
+
* Languages without these node kinds simply yield none.
|
|
1975
|
+
*/
|
|
1976
|
+
// biome-ignore lint/suspicious/noExplicitAny: web-tree-sitter node
|
|
1977
|
+
extractDecorators(node) {
|
|
1978
|
+
const isDeco = (n) => !!n && _TreeSitterSymbolExtractor.DECORATOR_NODE_KINDS.has(n.type);
|
|
1979
|
+
const text = (n) => {
|
|
1980
|
+
const first = String(n?.text ?? "").split(/\r?\n/, 1)[0]?.trim() ?? "";
|
|
1981
|
+
return first.length > 120 ? `${first.slice(0, 117)}\u2026` : first;
|
|
1982
|
+
};
|
|
1983
|
+
const out = [];
|
|
1984
|
+
const above = [];
|
|
1985
|
+
for (let sib = node.previousSibling; sib; sib = sib.previousSibling) {
|
|
1986
|
+
if (isDeco(sib))
|
|
1987
|
+
above.push(text(sib));
|
|
1988
|
+
else if (sib.isNamed)
|
|
1989
|
+
break;
|
|
1990
|
+
}
|
|
1991
|
+
out.push(...above.reverse());
|
|
1992
|
+
for (const child of node.children ?? []) {
|
|
1993
|
+
if (isDeco(child))
|
|
1994
|
+
out.push(text(child));
|
|
1995
|
+
else if (_TreeSitterSymbolExtractor.MODIFIERS_CONTAINER_KINDS.has(child?.type)) {
|
|
1996
|
+
for (const gc of child.children ?? []) {
|
|
1997
|
+
if (isDeco(gc))
|
|
1998
|
+
out.push(text(gc));
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
return [...new Set(out.filter(Boolean))].slice(0, 8);
|
|
2003
|
+
}
|
|
2004
|
+
// biome-ignore lint/suspicious/noExplicitAny: Match type
|
|
2005
|
+
parseRefMatch(match, filePath) {
|
|
2006
|
+
let name;
|
|
2007
|
+
let refNode;
|
|
2008
|
+
let referenceKind = "unknown";
|
|
2009
|
+
for (const capture of match.captures) {
|
|
2010
|
+
const captureName = String(capture.name);
|
|
2011
|
+
if (captureName.endsWith("Ident") || captureName.endsWith("Method") || captureName.endsWith("Field")) {
|
|
2012
|
+
name = capture.node.text;
|
|
2013
|
+
refNode = capture.node;
|
|
2014
|
+
if (captureName.startsWith("type"))
|
|
2015
|
+
referenceKind = "type";
|
|
2016
|
+
else if (captureName.startsWith("call") || captureName.startsWith("new")) {
|
|
2017
|
+
referenceKind = "call";
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
if (captureName.endsWith("Ref") && !refNode) {
|
|
2021
|
+
refNode = capture.node;
|
|
2022
|
+
if (captureName.startsWith("type"))
|
|
2023
|
+
referenceKind = "type";
|
|
2024
|
+
else if (captureName.startsWith("call") || captureName.startsWith("new")) {
|
|
2025
|
+
referenceKind = "call";
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
if (!name || !refNode)
|
|
2030
|
+
return null;
|
|
2031
|
+
return {
|
|
2032
|
+
symbolId: `${filePath}:${name}`,
|
|
2033
|
+
// Will be resolved later
|
|
2034
|
+
symbolName: name,
|
|
2035
|
+
filePath,
|
|
2036
|
+
line: refNode.startPosition.row + 1,
|
|
2037
|
+
column: refNode.startPosition.column + 1,
|
|
2038
|
+
referenceKind
|
|
2039
|
+
};
|
|
2040
|
+
}
|
|
2041
|
+
// biome-ignore lint/suspicious/noExplicitAny: Node type
|
|
2042
|
+
isExported(node, lineAt) {
|
|
2043
|
+
const line = lineAt(node.startPosition.row);
|
|
2044
|
+
return /^\s*export\b/.test(line) || this.hasExportModifier(node);
|
|
2045
|
+
}
|
|
2046
|
+
// biome-ignore lint/suspicious/noExplicitAny: Node type
|
|
2047
|
+
hasExportModifier(node) {
|
|
2048
|
+
let current = node.parent;
|
|
2049
|
+
while (current) {
|
|
2050
|
+
if (current.type === "statement_block")
|
|
2051
|
+
return false;
|
|
2052
|
+
if (current.type === "export_statement" || current.type === "export_declaration") {
|
|
2053
|
+
return true;
|
|
2054
|
+
}
|
|
2055
|
+
current = current.parent;
|
|
2056
|
+
}
|
|
2057
|
+
return false;
|
|
2058
|
+
}
|
|
2059
|
+
// biome-ignore lint/suspicious/noExplicitAny: Node type
|
|
2060
|
+
isFunctionLocal(node) {
|
|
2061
|
+
let current = node.parent;
|
|
2062
|
+
while (current) {
|
|
2063
|
+
if (current.type === "statement_block")
|
|
2064
|
+
return true;
|
|
2065
|
+
current = current.parent;
|
|
2066
|
+
}
|
|
2067
|
+
return false;
|
|
2068
|
+
}
|
|
2069
|
+
// biome-ignore lint/suspicious/noExplicitAny: Node type
|
|
2070
|
+
detectVisibility(node, nameText) {
|
|
2071
|
+
if (nameText.startsWith("#"))
|
|
2072
|
+
return "private";
|
|
2073
|
+
for (const child of node?.children ?? []) {
|
|
2074
|
+
if (child?.type === "accessibility_modifier") {
|
|
2075
|
+
if (child.text === "private")
|
|
2076
|
+
return "private";
|
|
2077
|
+
if (child.text === "protected")
|
|
2078
|
+
return "protected";
|
|
2079
|
+
return void 0;
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
return void 0;
|
|
2083
|
+
}
|
|
2084
|
+
extractSignature(paramsText, kind) {
|
|
2085
|
+
if (kind === "function" || kind === "method") {
|
|
2086
|
+
return paramsText.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/.*$/gm, "").replace(/\s+/g, " ").trim();
|
|
2087
|
+
}
|
|
2088
|
+
return void 0;
|
|
2089
|
+
}
|
|
2090
|
+
};
|
|
2091
|
+
|
|
2092
|
+
// dist/clients/blocker-freshness.js
|
|
2093
|
+
var DEPENDENCY_DRIFT_MAX_DELIVERIES = 3;
|
|
2094
|
+
var SELF_DRIFT_HASH_BUDGET_BYTES = 8 * 1024 * 1024;
|
|
2095
|
+
var MAX_IMPLICIT_SCOPE_PARSES = 32;
|
|
2096
|
+
var GLOBAL_SCOPE_ROLE_MEMO_ENTRIES = 1024;
|
|
2097
|
+
var globalScopeRoleMemo = new BoundedLruCache(GLOBAL_SCOPE_ROLE_MEMO_ENTRIES);
|
|
2098
|
+
function roleByTextOf(filePath) {
|
|
2099
|
+
try {
|
|
2100
|
+
return globalScopeRoleByText(fs5.readFileSync(filePath, "utf8"));
|
|
2101
|
+
} catch {
|
|
2102
|
+
return void 0;
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
function createImplicitScopeCheck(runtime, cwd) {
|
|
2106
|
+
let receipts;
|
|
2107
|
+
let receiptsDropped = false;
|
|
2108
|
+
const roles = /* @__PURE__ */ new Map();
|
|
2109
|
+
let parses = 0;
|
|
2110
|
+
const roleOf = (other) => {
|
|
2111
|
+
const known = roles.get(other);
|
|
2112
|
+
if (known !== void 0)
|
|
2113
|
+
return known;
|
|
2114
|
+
let stat;
|
|
2115
|
+
try {
|
|
2116
|
+
stat = fs5.statSync(other);
|
|
2117
|
+
} catch {
|
|
2118
|
+
roles.set(other, "none");
|
|
2119
|
+
return "none";
|
|
2120
|
+
}
|
|
2121
|
+
const memo = globalScopeRoleMemo.get(other);
|
|
2122
|
+
let role;
|
|
2123
|
+
if (memo?.mtimeMs === stat.mtimeMs && memo.ctimeMs === stat.ctimeMs && memo.size === stat.size) {
|
|
2124
|
+
role = memo.role;
|
|
2125
|
+
} else {
|
|
2126
|
+
const byName = globalScopeRoleByName(other);
|
|
2127
|
+
const byText = byName ? void 0 : roleByTextOf(other);
|
|
2128
|
+
if (byName ?? byText) {
|
|
2129
|
+
role = byName ?? byText;
|
|
2130
|
+
} else if (parses >= MAX_IMPLICIT_SCOPE_PARSES) {
|
|
2131
|
+
return "unknown";
|
|
2132
|
+
} else {
|
|
2133
|
+
parses += 1;
|
|
2134
|
+
role = classifyJsTsGlobalScope(other);
|
|
2135
|
+
}
|
|
2136
|
+
globalScopeRoleMemo.set(other, {
|
|
2137
|
+
mtimeMs: stat.mtimeMs,
|
|
2138
|
+
ctimeMs: stat.ctimeMs,
|
|
2139
|
+
size: stat.size,
|
|
2140
|
+
role
|
|
2141
|
+
});
|
|
2142
|
+
}
|
|
2143
|
+
roles.set(other, role);
|
|
2144
|
+
return role;
|
|
2145
|
+
};
|
|
2146
|
+
const rootKey = normalizeMapKey(nodePath.resolve(cwd));
|
|
2147
|
+
const rootPrefix = rootKey.endsWith("/") ? rootKey : `${rootKey}/`;
|
|
2148
|
+
const inRoot = (file) => file === rootKey || file.startsWith(rootPrefix);
|
|
2149
|
+
let scanned;
|
|
2150
|
+
const feeding = [];
|
|
2151
|
+
let newestUnknownTs = Number.NEGATIVE_INFINITY;
|
|
2152
|
+
const scanBackTo = (all, recordedAtMs) => {
|
|
2153
|
+
scanned ??= all.length;
|
|
2154
|
+
while (scanned > 0 && all[scanned - 1].ts > recordedAtMs) {
|
|
2155
|
+
const receipt = all[--scanned];
|
|
2156
|
+
if (!inRoot(receipt.filePath))
|
|
2157
|
+
continue;
|
|
2158
|
+
const role = roleOf(receipt.filePath);
|
|
2159
|
+
if (role === "feeds")
|
|
2160
|
+
feeding.push(receipt);
|
|
2161
|
+
else if (role === "unknown") {
|
|
2162
|
+
newestUnknownTs = Math.max(newestUnknownTs, receipt.ts);
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2165
|
+
};
|
|
2166
|
+
const feedsGlobalScope = (other) => roles.get(other) === "feeds";
|
|
2167
|
+
return (filePath, recordedAtMs) => {
|
|
2168
|
+
const key = normalizeMapKey(filePath);
|
|
2169
|
+
const goFile = isGoSourceFile(key);
|
|
2170
|
+
const inScope = goFile ? goPackagePeersOf(key) : implicitScopeOf(key, feedsGlobalScope);
|
|
2171
|
+
if (!inScope)
|
|
2172
|
+
return { drifted: [], truncated: false };
|
|
2173
|
+
if (receipts === void 0) {
|
|
2174
|
+
const coordinator = runtime;
|
|
2175
|
+
receipts = coordinator.getMutationsSince?.(0) ?? [];
|
|
2176
|
+
receiptsDropped = (coordinator.droppedMutationReceiptCount ?? 0) > 0;
|
|
2177
|
+
}
|
|
2178
|
+
let drifted;
|
|
2179
|
+
let unknownAfter = false;
|
|
2180
|
+
if (goFile) {
|
|
2181
|
+
for (let i = receipts.length - 1; i >= 0; i--) {
|
|
2182
|
+
const receipt = receipts[i];
|
|
2183
|
+
if (receipt.ts <= recordedAtMs)
|
|
2184
|
+
break;
|
|
2185
|
+
if (inScope(receipt.filePath)) {
|
|
2186
|
+
drifted = receipt.filePath;
|
|
2187
|
+
break;
|
|
2188
|
+
}
|
|
2189
|
+
}
|
|
2190
|
+
} else {
|
|
2191
|
+
scanBackTo(receipts, recordedAtMs);
|
|
2192
|
+
drifted = feeding.find((receipt) => receipt.ts > recordedAtMs && inScope(receipt.filePath))?.filePath;
|
|
2193
|
+
unknownAfter = newestUnknownTs > recordedAtMs;
|
|
2194
|
+
}
|
|
2195
|
+
if (drifted)
|
|
2196
|
+
return { drifted: [drifted], truncated: false };
|
|
2197
|
+
const evictedAfter = receiptsDropped && (receipts.length === 0 || receipts[0].ts > recordedAtMs);
|
|
2198
|
+
return { drifted: [], truncated: unknownAfter || evictedAfter };
|
|
2199
|
+
};
|
|
2200
|
+
}
|
|
2201
|
+
var MAX_DRIFT_CHECK_IMPORTS = 128;
|
|
2202
|
+
var extractorCache = /* @__PURE__ */ new Map();
|
|
2203
|
+
function getExtractor(languageId) {
|
|
2204
|
+
let cached = extractorCache.get(languageId);
|
|
2205
|
+
if (!cached) {
|
|
2206
|
+
cached = (async () => {
|
|
2207
|
+
const client = getSharedTreeSitterClient();
|
|
2208
|
+
if (!client)
|
|
2209
|
+
return null;
|
|
2210
|
+
const extractor = new TreeSitterSymbolExtractor(languageId, client);
|
|
2211
|
+
const ok = await extractor.init();
|
|
2212
|
+
return ok ? extractor : null;
|
|
2213
|
+
})().catch(() => null);
|
|
2214
|
+
extractorCache.set(languageId, cached);
|
|
2215
|
+
}
|
|
2216
|
+
return cached;
|
|
2217
|
+
}
|
|
2218
|
+
var importResolutionMemo = /* @__PURE__ */ new Map();
|
|
2219
|
+
var importResolutionMemoTurnIndex;
|
|
2220
|
+
function noteImportResolutionMemoTurn(turnIndex) {
|
|
2221
|
+
if (turnIndex === void 0)
|
|
2222
|
+
return;
|
|
2223
|
+
if (importResolutionMemoTurnIndex !== turnIndex) {
|
|
2224
|
+
importResolutionMemo.clear();
|
|
2225
|
+
importResolutionMemoTurnIndex = turnIndex;
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
async function resolveForwardImportsMemoized(cwd, filePath, resolveForwardImports) {
|
|
2229
|
+
const sig = await statSignature(filePath);
|
|
2230
|
+
if (sig === void 0) {
|
|
2231
|
+
try {
|
|
2232
|
+
return await resolveForwardImports(cwd, filePath);
|
|
2233
|
+
} catch {
|
|
2234
|
+
return [];
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
const cached = importResolutionMemo.get(filePath);
|
|
2238
|
+
if (cached && cached.mtimeMs === sig.mtimeMs && cached.size === sig.size)
|
|
2239
|
+
return cached.imports;
|
|
2240
|
+
let imports;
|
|
2241
|
+
try {
|
|
2242
|
+
imports = await resolveForwardImports(cwd, filePath);
|
|
2243
|
+
} catch {
|
|
2244
|
+
imports = [];
|
|
2245
|
+
}
|
|
2246
|
+
importResolutionMemo.set(filePath, {
|
|
2247
|
+
mtimeMs: sig.mtimeMs,
|
|
2248
|
+
size: sig.size,
|
|
2249
|
+
imports
|
|
2250
|
+
});
|
|
2251
|
+
return imports;
|
|
2252
|
+
}
|
|
2253
|
+
var FORWARD_IMPORT_SOURCE_MEMO_ENTRIES = 1024;
|
|
2254
|
+
var JS_TS_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript"]);
|
|
2255
|
+
var RELATIVE_SPECIFIER = /(["'])(\.{1,2}\/(?:(?!\1)[^\n])*)\1/g;
|
|
2256
|
+
var DEPENDENCY_CALL = /\bimport\s*\(|\brequire\s*\(|@import\b|\bdeclare\s+module\s*["']|\/\/\/\s*<reference\s+(?:path|types)/;
|
|
2257
|
+
function holdsUncapturedDependency(content, sources) {
|
|
2258
|
+
if (DEPENDENCY_CALL.test(content))
|
|
2259
|
+
return true;
|
|
2260
|
+
const captured = new Set(sources);
|
|
2261
|
+
for (const match of content.matchAll(RELATIVE_SPECIFIER)) {
|
|
2262
|
+
if (!captured.has(match[2]))
|
|
2263
|
+
return true;
|
|
2264
|
+
}
|
|
2265
|
+
return false;
|
|
2266
|
+
}
|
|
2267
|
+
var forwardImportSourceMemo = new BoundedLruCache(FORWARD_IMPORT_SOURCE_MEMO_ENTRIES);
|
|
2268
|
+
var forwardImportParses = 0;
|
|
2269
|
+
function _getForwardImportParsesForTests() {
|
|
2270
|
+
return forwardImportParses;
|
|
2271
|
+
}
|
|
2272
|
+
async function forwardImportSources(filePath, languageId) {
|
|
2273
|
+
let stat;
|
|
2274
|
+
try {
|
|
2275
|
+
stat = fs5.statSync(filePath);
|
|
2276
|
+
} catch {
|
|
2277
|
+
return void 0;
|
|
2278
|
+
}
|
|
2279
|
+
const key = normalizeMapKey(filePath);
|
|
2280
|
+
const memo = forwardImportSourceMemo.get(key);
|
|
2281
|
+
if (memo?.mtimeMs === stat.mtimeMs && memo.ctimeMs === stat.ctimeMs && memo.size === stat.size) {
|
|
2282
|
+
return memo;
|
|
2283
|
+
}
|
|
2284
|
+
const client = getSharedTreeSitterClient();
|
|
2285
|
+
if (!client)
|
|
2286
|
+
return void 0;
|
|
2287
|
+
const extractor = await getExtractor(languageId);
|
|
2288
|
+
if (!extractor)
|
|
2289
|
+
return void 0;
|
|
2290
|
+
let content;
|
|
2291
|
+
try {
|
|
2292
|
+
content = fs5.readFileSync(filePath, "utf-8");
|
|
2293
|
+
} catch {
|
|
2294
|
+
return void 0;
|
|
2295
|
+
}
|
|
2296
|
+
const outcome = await client.withParsedTree(
|
|
2297
|
+
filePath,
|
|
2298
|
+
languageId,
|
|
2299
|
+
content,
|
|
2300
|
+
// Imports only (#4368 r1 F2): the definitions pass of a full extract is
|
|
2301
|
+
// what made a large file cost seconds.
|
|
2302
|
+
(tree) => extractor.extractImports(tree),
|
|
2303
|
+
"blocker-freshness"
|
|
2304
|
+
);
|
|
2305
|
+
if (!outcome.parsed)
|
|
2306
|
+
return void 0;
|
|
2307
|
+
forwardImportParses++;
|
|
2308
|
+
const sources = [...new Set(outcome.value.map((ref) => ref.source))];
|
|
2309
|
+
const entry = {
|
|
2310
|
+
mtimeMs: stat.mtimeMs,
|
|
2311
|
+
ctimeMs: stat.ctimeMs,
|
|
2312
|
+
size: stat.size,
|
|
2313
|
+
sources,
|
|
2314
|
+
unresolved: memo?.unresolved ?? /* @__PURE__ */ new Set(),
|
|
2315
|
+
uncaptured: JS_TS_LANGUAGES.has(languageId) && holdsUncapturedDependency(content, sources)
|
|
2316
|
+
};
|
|
2317
|
+
forwardImportSourceMemo.set(key, entry);
|
|
2318
|
+
return entry;
|
|
2319
|
+
}
|
|
2320
|
+
async function extractForwardImports(cwd, filePath) {
|
|
2321
|
+
const languageId = resolveTreeSitterLanguage(filePath);
|
|
2322
|
+
if (!languageId)
|
|
2323
|
+
return { targets: [], unresolved: [], complete: false };
|
|
2324
|
+
const entry = await forwardImportSources(filePath, languageId);
|
|
2325
|
+
if (!entry)
|
|
2326
|
+
return { targets: [], unresolved: [], complete: false };
|
|
2327
|
+
const resolved = /* @__PURE__ */ new Set();
|
|
2328
|
+
const unresolved = /* @__PURE__ */ new Set();
|
|
2329
|
+
for (const source of entry.sources) {
|
|
2330
|
+
const targets = resolveImportToFiles(cwd, filePath, languageId, source);
|
|
2331
|
+
if (targets.length === 0)
|
|
2332
|
+
unresolved.add(source);
|
|
2333
|
+
for (const target of targets) {
|
|
2334
|
+
if (entry.unresolved.has(source))
|
|
2335
|
+
notePathAppeared(target);
|
|
2336
|
+
resolved.add(target);
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
entry.unresolved = unresolved;
|
|
2340
|
+
return {
|
|
2341
|
+
targets: [...resolved],
|
|
2342
|
+
unresolved: [...unresolved],
|
|
2343
|
+
complete: !entry.uncaptured
|
|
2344
|
+
};
|
|
2345
|
+
}
|
|
2346
|
+
function extractForwardImportPaths(cwd, filePath) {
|
|
2347
|
+
return extractForwardImports(cwd, filePath).then(({ targets }) => targets);
|
|
2348
|
+
}
|
|
2349
|
+
async function statSignature(filePath) {
|
|
2350
|
+
try {
|
|
2351
|
+
const stat = await fs5.promises.stat(filePath);
|
|
2352
|
+
return { mtimeMs: stat.mtimeMs, size: stat.size };
|
|
2353
|
+
} catch {
|
|
2354
|
+
return void 0;
|
|
2355
|
+
}
|
|
2356
|
+
}
|
|
2357
|
+
async function statMtimeMs(filePath) {
|
|
2358
|
+
try {
|
|
2359
|
+
const stat = await fs5.promises.stat(filePath);
|
|
2360
|
+
return stat.mtimeMs;
|
|
2361
|
+
} catch {
|
|
2362
|
+
return void 0;
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
async function detectSelfDrift(args) {
|
|
2366
|
+
const boundOptions = (label) => ({
|
|
2367
|
+
ms: HOOK_WALL_BUDGET_MS.turn_end,
|
|
2368
|
+
signal: args.signal,
|
|
2369
|
+
hook: "turn_end",
|
|
2370
|
+
label
|
|
2371
|
+
});
|
|
2372
|
+
const stat = await bounded(fs5.promises.stat(args.filePath), boundOptions("selfDriftStat"));
|
|
2373
|
+
if (stat === void 0)
|
|
2374
|
+
return { verdict: "unverifiable", unverifiableReason: "stat-unavailable" };
|
|
2375
|
+
if (args.recordedSize === void 0)
|
|
2376
|
+
return { verdict: "unverifiable", unverifiableReason: "missing-baseline" };
|
|
2377
|
+
if (stat.size !== args.recordedSize)
|
|
2378
|
+
return { verdict: "drift" };
|
|
2379
|
+
if (args.recordedHash === void 0)
|
|
2380
|
+
return { verdict: "unverifiable", unverifiableReason: "missing-baseline" };
|
|
2381
|
+
if (stat.size > args.budget.bytesLeft) {
|
|
2382
|
+
args.budget.exhausted = true;
|
|
2383
|
+
return {
|
|
2384
|
+
verdict: "unverifiable",
|
|
2385
|
+
unverifiableReason: "hash-budget-exhausted"
|
|
2386
|
+
};
|
|
2387
|
+
}
|
|
2388
|
+
const content = await bounded(fs5.promises.readFile(args.filePath), boundOptions("selfDriftHash"));
|
|
2389
|
+
if (content === void 0)
|
|
2390
|
+
return { verdict: "unverifiable", unverifiableReason: "hash-unavailable" };
|
|
2391
|
+
args.budget.bytesLeft -= content.byteLength;
|
|
2392
|
+
const hash = hashContentBytes(content);
|
|
2393
|
+
return { verdict: hash === args.recordedHash ? "unchanged" : "drift" };
|
|
2394
|
+
}
|
|
2395
|
+
async function collectForwardImportMtimes(cwd, filePath, resolveForwardImports = extractForwardImportPaths, turnIndex) {
|
|
2396
|
+
noteImportResolutionMemoTurn(turnIndex);
|
|
2397
|
+
let imports = [];
|
|
2398
|
+
try {
|
|
2399
|
+
imports = await resolveForwardImportsMemoized(cwd, filePath, resolveForwardImports);
|
|
2400
|
+
} catch {
|
|
2401
|
+
imports = [];
|
|
2402
|
+
}
|
|
2403
|
+
const truncated = imports.length > MAX_DRIFT_CHECK_IMPORTS;
|
|
2404
|
+
const out = [];
|
|
2405
|
+
for (const dep of imports.slice(0, MAX_DRIFT_CHECK_IMPORTS)) {
|
|
2406
|
+
const mtimeMs = await statMtimeMs(dep);
|
|
2407
|
+
if (mtimeMs !== void 0)
|
|
2408
|
+
out.push({ path: dep, mtimeMs });
|
|
2409
|
+
}
|
|
2410
|
+
return { mtimes: out, truncated };
|
|
2411
|
+
}
|
|
2412
|
+
async function detectDrift(cwd, filePath, recordedAtMs, resolveForwardImports, turnIndex, implicitScope, skipOwnFile = false) {
|
|
2413
|
+
const drifted = [];
|
|
2414
|
+
if (!skipOwnFile) {
|
|
2415
|
+
const ownFreshness = freshnessFromMtime({
|
|
2416
|
+
mtimeMs: await statMtimeMs(filePath),
|
|
2417
|
+
referenceMs: recordedAtMs
|
|
2418
|
+
});
|
|
2419
|
+
if (ownFreshness.verdict === "stale")
|
|
2420
|
+
drifted.push(filePath);
|
|
2421
|
+
}
|
|
2422
|
+
const { mtimes, truncated } = await collectForwardImportMtimes(cwd, filePath, resolveForwardImports, turnIndex);
|
|
2423
|
+
for (const { path: depPath, mtimeMs } of mtimes) {
|
|
2424
|
+
const verdict = freshnessFromMtime({
|
|
2425
|
+
mtimeMs,
|
|
2426
|
+
referenceMs: recordedAtMs
|
|
2427
|
+
});
|
|
2428
|
+
if (verdict.verdict === "stale")
|
|
2429
|
+
drifted.push(depPath);
|
|
2430
|
+
}
|
|
2431
|
+
const implicit = implicitScope(filePath, recordedAtMs);
|
|
2432
|
+
drifted.push(...implicit.drifted);
|
|
2433
|
+
return { drifted, truncated: truncated || implicit.truncated };
|
|
2434
|
+
}
|
|
2435
|
+
function isEligibleForDriftCheck(entry) {
|
|
2436
|
+
if (entry.stale)
|
|
2437
|
+
return false;
|
|
2438
|
+
if (entry.recordedAtMs === void 0)
|
|
2439
|
+
return false;
|
|
2440
|
+
return entry.sources !== void 0 && entry.sources.length > 0;
|
|
2441
|
+
}
|
|
2442
|
+
function isAllLspSourced(sources) {
|
|
2443
|
+
return sources !== void 0 && sources.length > 0 && sources.every((source) => source === "lsp");
|
|
2444
|
+
}
|
|
2445
|
+
function canSubsumeLspWidgetRow(entry) {
|
|
2446
|
+
return isEligibleForDriftCheck(entry) && isAllLspSourced(entry.sources);
|
|
2447
|
+
}
|
|
2448
|
+
async function sweepInlineBlockerFreshness(runtime, cwd, options) {
|
|
2449
|
+
const counts = {
|
|
2450
|
+
total: 0,
|
|
2451
|
+
kept: 0,
|
|
2452
|
+
revalidated: 0,
|
|
2453
|
+
alreadyStale: 0,
|
|
2454
|
+
truncatedImports: 0,
|
|
2455
|
+
selfHealed: 0,
|
|
2456
|
+
selfUnverifiable: 0,
|
|
2457
|
+
hashBudgetExhausted: 0
|
|
2458
|
+
};
|
|
2459
|
+
const resolveForwardImports = options?.resolveForwardImports ?? extractForwardImportPaths;
|
|
2460
|
+
const implicitScope = createImplicitScopeCheck(runtime, cwd);
|
|
2461
|
+
let turnIndex;
|
|
2462
|
+
try {
|
|
2463
|
+
turnIndex = runtime.turnIndex;
|
|
2464
|
+
} catch {
|
|
2465
|
+
turnIndex = void 0;
|
|
2466
|
+
}
|
|
2467
|
+
let inlineEntries;
|
|
2468
|
+
try {
|
|
2469
|
+
inlineEntries = runtime.getInlineBlockersSnapshot();
|
|
2470
|
+
} catch {
|
|
2471
|
+
return counts;
|
|
2472
|
+
}
|
|
2473
|
+
const population = inlineEntries.map((entry) => ({
|
|
2474
|
+
filePath: entry.filePath,
|
|
2475
|
+
stale: entry.stale ?? false,
|
|
2476
|
+
recordedAtMs: entry.recordedAtMs,
|
|
2477
|
+
sources: entry.sources,
|
|
2478
|
+
staleReason: entry.staleReason,
|
|
2479
|
+
recordedSize: entry.recordedSize,
|
|
2480
|
+
recordedHash: entry.recordedHash,
|
|
2481
|
+
demote: () => runtime.markInlineBlockerStale(entry.filePath, "dependency-drift"),
|
|
2482
|
+
setSelfDrift: (isSelfDrift) => runtime.setInlineBlockerSelfDriftStale(entry.filePath, isSelfDrift)
|
|
2483
|
+
}));
|
|
2484
|
+
const inlineByKey = /* @__PURE__ */ new Map();
|
|
2485
|
+
for (const entry of population) {
|
|
2486
|
+
inlineByKey.set(normalizeEphemeralMapKey(entry.filePath), entry);
|
|
2487
|
+
}
|
|
2488
|
+
for (const extra of options?.additionalEntries ?? []) {
|
|
2489
|
+
const key = normalizeEphemeralMapKey(extra.filePath);
|
|
2490
|
+
const inlineEntry = inlineByKey.get(key);
|
|
2491
|
+
if (inlineEntry && canSubsumeLspWidgetRow(inlineEntry)) {
|
|
2492
|
+
const demoteInline = inlineEntry.demote;
|
|
2493
|
+
const demoteWidget = extra.demote;
|
|
2494
|
+
inlineEntry.demote = () => {
|
|
2495
|
+
const inlineChanged = demoteInline();
|
|
2496
|
+
const widgetChanged = demoteWidget();
|
|
2497
|
+
return inlineChanged || widgetChanged;
|
|
2498
|
+
};
|
|
2499
|
+
continue;
|
|
2500
|
+
}
|
|
2501
|
+
population.push({
|
|
2502
|
+
filePath: extra.filePath,
|
|
2503
|
+
stale: false,
|
|
2504
|
+
recordedAtMs: extra.recordedAtMs,
|
|
2505
|
+
sources: ["lsp"],
|
|
2506
|
+
demote: extra.demote,
|
|
2507
|
+
// A widget row is pure-LSP by construction, so it takes the import
|
|
2508
|
+
// axis and never reaches the self axis. No `setSelfDrift` (#2982):
|
|
2509
|
+
// the widget store has no re-arming self-drift setter, and an entry
|
|
2510
|
+
// without one is not eligible for that axis.
|
|
2511
|
+
staleReason: void 0,
|
|
2512
|
+
recordedSize: void 0,
|
|
2513
|
+
recordedHash: void 0
|
|
2514
|
+
});
|
|
2515
|
+
}
|
|
2516
|
+
counts.total = population.length;
|
|
2517
|
+
const hashBudget = {
|
|
2518
|
+
bytesLeft: SELF_DRIFT_HASH_BUDGET_BYTES,
|
|
2519
|
+
exhausted: false
|
|
2520
|
+
};
|
|
2521
|
+
for (const entry of population) {
|
|
2522
|
+
try {
|
|
2523
|
+
const selfDriftDemoted = entry.stale && entry.staleReason === "self-drift";
|
|
2524
|
+
if (entry.stale && !selfDriftDemoted) {
|
|
2525
|
+
counts.alreadyStale += 1;
|
|
2526
|
+
continue;
|
|
2527
|
+
}
|
|
2528
|
+
if (entry.recordedAtMs === void 0) {
|
|
2529
|
+
counts.kept += 1;
|
|
2530
|
+
continue;
|
|
2531
|
+
}
|
|
2532
|
+
const recordedSources = entry.sources !== void 0 && entry.sources.length > 0 ? entry.sources : void 0;
|
|
2533
|
+
if (recordedSources === void 0) {
|
|
2534
|
+
counts.kept += 1;
|
|
2535
|
+
continue;
|
|
2536
|
+
}
|
|
2537
|
+
const setSelfDrift = entry.setSelfDrift;
|
|
2538
|
+
const isLspSourced = isAllLspSourced(recordedSources);
|
|
2539
|
+
let ownVerdict;
|
|
2540
|
+
if (setSelfDrift) {
|
|
2541
|
+
const hashBudgetWasExhausted = hashBudget.exhausted;
|
|
2542
|
+
const selfDrift = await bounded(detectSelfDrift({
|
|
2543
|
+
filePath: entry.filePath,
|
|
2544
|
+
recordedSize: entry.recordedSize,
|
|
2545
|
+
recordedHash: entry.recordedHash,
|
|
2546
|
+
signal: options?.signal,
|
|
2547
|
+
budget: hashBudget
|
|
2548
|
+
}), {
|
|
2549
|
+
ms: HOOK_WALL_BUDGET_MS.turn_end,
|
|
2550
|
+
signal: options?.signal,
|
|
2551
|
+
hook: "turn_end",
|
|
2552
|
+
label: "detectSelfDrift"
|
|
2553
|
+
}) ?? {
|
|
2554
|
+
verdict: "unverifiable",
|
|
2555
|
+
unverifiableReason: "bound-expired"
|
|
2556
|
+
};
|
|
2557
|
+
ownVerdict = selfDrift.verdict;
|
|
2558
|
+
if (ownVerdict === "unverifiable") {
|
|
2559
|
+
counts.selfUnverifiable += 1;
|
|
2560
|
+
const reason = selfDrift.unverifiableReason ?? (hashBudgetWasExhausted || hashBudget.exhausted ? "hash-budget-exhausted" : "hash-unavailable");
|
|
2561
|
+
recordDegradationOnce({
|
|
2562
|
+
kind: "self-drift-unverifiable",
|
|
2563
|
+
subject: `inline-blocker:${toRunnerDisplayPath(cwd, entry.filePath)}#${reason}`,
|
|
2564
|
+
reason: `the self-drift check was unverifiable: ${reason}`
|
|
2565
|
+
});
|
|
2566
|
+
if (reason === "hash-budget-exhausted") {
|
|
2567
|
+
recordDegradationOnce({
|
|
2568
|
+
kind: "self-drift-hash-budget-exhausted",
|
|
2569
|
+
subject: `inline-blocker:${toRunnerDisplayPath(cwd, entry.filePath)}`,
|
|
2570
|
+
reason: "the aggregate self-drift hash budget was exhausted"
|
|
2571
|
+
});
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2574
|
+
}
|
|
2575
|
+
if (!isLspSourced) {
|
|
2576
|
+
if (!setSelfDrift || ownVerdict === void 0) {
|
|
2577
|
+
counts.kept += 1;
|
|
2578
|
+
continue;
|
|
2579
|
+
}
|
|
2580
|
+
if (ownVerdict === "unverifiable") {
|
|
2581
|
+
if (selfDriftDemoted)
|
|
2582
|
+
counts.alreadyStale += 1;
|
|
2583
|
+
else
|
|
2584
|
+
counts.kept += 1;
|
|
2585
|
+
continue;
|
|
2586
|
+
}
|
|
2587
|
+
const shouldDemote = ownVerdict === "drift";
|
|
2588
|
+
const transitioned = setSelfDrift(shouldDemote);
|
|
2589
|
+
if (shouldDemote) {
|
|
2590
|
+
if (transitioned)
|
|
2591
|
+
counts.revalidated += 1;
|
|
2592
|
+
else
|
|
2593
|
+
counts.alreadyStale += 1;
|
|
2594
|
+
} else if (transitioned) {
|
|
2595
|
+
counts.selfHealed += 1;
|
|
2596
|
+
} else {
|
|
2597
|
+
counts.kept += 1;
|
|
2598
|
+
}
|
|
2599
|
+
continue;
|
|
2600
|
+
}
|
|
2601
|
+
let contentDecidedOwnFile = false;
|
|
2602
|
+
if (setSelfDrift !== void 0 && ownVerdict !== void 0 && entry.recordedSize !== void 0) {
|
|
2603
|
+
if (ownVerdict === "drift") {
|
|
2604
|
+
const transitioned = setSelfDrift(true);
|
|
2605
|
+
if (transitioned)
|
|
2606
|
+
counts.revalidated += 1;
|
|
2607
|
+
else
|
|
2608
|
+
counts.alreadyStale += 1;
|
|
2609
|
+
continue;
|
|
2610
|
+
}
|
|
2611
|
+
if (ownVerdict === "unchanged") {
|
|
2612
|
+
contentDecidedOwnFile = true;
|
|
2613
|
+
const transitioned = setSelfDrift(false);
|
|
2614
|
+
if (transitioned)
|
|
2615
|
+
counts.selfHealed += 1;
|
|
2616
|
+
} else {
|
|
2617
|
+
if (selfDriftDemoted) {
|
|
2618
|
+
counts.alreadyStale += 1;
|
|
2619
|
+
continue;
|
|
2620
|
+
}
|
|
2621
|
+
}
|
|
2622
|
+
}
|
|
2623
|
+
const skipOwnFile = contentDecidedOwnFile;
|
|
2624
|
+
const { drifted, truncated } = await detectDrift(cwd, entry.filePath, entry.recordedAtMs, resolveForwardImports, turnIndex, implicitScope, skipOwnFile);
|
|
2625
|
+
if (truncated)
|
|
2626
|
+
counts.truncatedImports += 1;
|
|
2627
|
+
if (drifted.length > 0) {
|
|
2628
|
+
entry.demote();
|
|
2629
|
+
counts.revalidated += 1;
|
|
2630
|
+
} else {
|
|
2631
|
+
counts.kept += 1;
|
|
2632
|
+
}
|
|
2633
|
+
} catch {
|
|
2634
|
+
counts.kept += 1;
|
|
2635
|
+
}
|
|
2636
|
+
}
|
|
2637
|
+
counts.hashBudgetExhausted = hashBudget.exhausted ? 1 : 0;
|
|
2638
|
+
return counts;
|
|
2639
|
+
}
|
|
2640
|
+
|
|
2641
|
+
export {
|
|
2642
|
+
getRunnerTimeoutFloorMs,
|
|
2643
|
+
RUNTIME_CONFIG,
|
|
2644
|
+
exceedsLspSyncLimits,
|
|
2645
|
+
loadAstGrepNapi,
|
|
2646
|
+
isGoSourceFile,
|
|
2647
|
+
goPackageFiles,
|
|
2648
|
+
implicitScopeOf,
|
|
2649
|
+
goPackageChangedAt,
|
|
2650
|
+
feedsJsTsGlobalScope,
|
|
2651
|
+
pathBirthtimeMs,
|
|
2652
|
+
jsTsCandidatePaths,
|
|
2653
|
+
resolveAliasedImport,
|
|
2654
|
+
resolveProjectReferenceImport,
|
|
2655
|
+
resolveWorkspacePackageImport,
|
|
2656
|
+
importSourceNameParts,
|
|
2657
|
+
fileAnswerNames,
|
|
2658
|
+
resolveImportToFiles,
|
|
2659
|
+
symbolExtractionGrammar,
|
|
2660
|
+
TreeSitterSymbolExtractor,
|
|
2661
|
+
DEPENDENCY_DRIFT_MAX_DELIVERIES,
|
|
2662
|
+
_getForwardImportParsesForTests,
|
|
2663
|
+
extractForwardImports,
|
|
2664
|
+
extractForwardImportPaths,
|
|
2665
|
+
collectForwardImportMtimes,
|
|
2666
|
+
sweepInlineBlockerFreshness
|
|
2667
|
+
};
|