@lzehrung/codegraph 2.3.26 → 2.3.28

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.
Files changed (144) hide show
  1. package/codegraph-skill/codegraph/SKILL.md +1 -0
  2. package/dist/agent/explain.d.ts +9 -1
  3. package/dist/agent/explain.d.ts.map +1 -1
  4. package/dist/agent/explain.js +18 -1
  5. package/dist/agent/explain.js.map +1 -1
  6. package/dist/agent/refactor-plan.d.ts +2 -0
  7. package/dist/agent/refactor-plan.d.ts.map +1 -1
  8. package/dist/agent/refactor-plan.js +11 -0
  9. package/dist/agent/refactor-plan.js.map +1 -1
  10. package/dist/agent/rename-preview.d.ts.map +1 -1
  11. package/dist/agent/rename-preview.js +49 -11
  12. package/dist/agent/rename-preview.js.map +1 -1
  13. package/dist/agent-tools.d.ts +2 -1
  14. package/dist/agent-tools.d.ts.map +1 -1
  15. package/dist/agent-tools.js +8 -0
  16. package/dist/agent-tools.js.map +1 -1
  17. package/dist/agent.d.ts +1 -1
  18. package/dist/agent.d.ts.map +1 -1
  19. package/dist/agent.js.map +1 -1
  20. package/dist/bin/cli.js +3933 -2390
  21. package/dist/bin/query-index-worker.js +56 -18
  22. package/dist/cli/navigation.d.ts.map +1 -1
  23. package/dist/cli/navigation.js +4 -0
  24. package/dist/cli/navigation.js.map +1 -1
  25. package/dist/impact/call-compatibility.js +3 -3
  26. package/dist/impact/call-compatibility.js.map +1 -1
  27. package/dist/impact/direct.js +1 -1
  28. package/dist/impact/direct.js.map +1 -1
  29. package/dist/impact/reference-cache.d.ts +1 -0
  30. package/dist/impact/reference-cache.d.ts.map +1 -1
  31. package/dist/impact/reference-cache.js +44 -18
  32. package/dist/impact/reference-cache.js.map +1 -1
  33. package/dist/index.d.ts +1 -1
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js.map +1 -1
  36. package/dist/indexer/build-cache/module-cache.d.ts.map +1 -1
  37. package/dist/indexer/build-cache/module-cache.js +30 -0
  38. package/dist/indexer/build-cache/module-cache.js.map +1 -1
  39. package/dist/indexer/build-cache/options.d.ts +14 -2
  40. package/dist/indexer/build-cache/options.d.ts.map +1 -1
  41. package/dist/indexer/build-cache/options.js +14 -2
  42. package/dist/indexer/build-cache/options.js.map +1 -1
  43. package/dist/indexer/build-cache/project-snapshot.d.ts.map +1 -1
  44. package/dist/indexer/build-cache/project-snapshot.js +60 -5
  45. package/dist/indexer/build-cache/project-snapshot.js.map +1 -1
  46. package/dist/indexer/import-types.d.ts +14 -1
  47. package/dist/indexer/import-types.d.ts.map +1 -1
  48. package/dist/indexer/imports/binding-ranges.d.ts +50 -0
  49. package/dist/indexer/imports/binding-ranges.d.ts.map +1 -0
  50. package/dist/indexer/imports/binding-ranges.js +297 -0
  51. package/dist/indexer/imports/binding-ranges.js.map +1 -0
  52. package/dist/indexer/imports/js-text-imports.d.ts +4 -0
  53. package/dist/indexer/imports/js-text-imports.d.ts.map +1 -1
  54. package/dist/indexer/imports/js-text-imports.js +210 -48
  55. package/dist/indexer/imports/js-text-imports.js.map +1 -1
  56. package/dist/indexer/imports/language-specific.d.ts +3 -0
  57. package/dist/indexer/imports/language-specific.d.ts.map +1 -1
  58. package/dist/indexer/imports/language-specific.js +97 -23
  59. package/dist/indexer/imports/language-specific.js.map +1 -1
  60. package/dist/indexer/imports/native-captures.d.ts.map +1 -1
  61. package/dist/indexer/imports/native-captures.js +52 -30
  62. package/dist/indexer/imports/native-captures.js.map +1 -1
  63. package/dist/indexer/imports/python.d.ts +2 -0
  64. package/dist/indexer/imports/python.d.ts.map +1 -1
  65. package/dist/indexer/imports/python.js +55 -18
  66. package/dist/indexer/imports/python.js.map +1 -1
  67. package/dist/indexer/imports.d.ts.map +1 -1
  68. package/dist/indexer/imports.js +15 -1
  69. package/dist/indexer/imports.js.map +1 -1
  70. package/dist/indexer/navigation-goto.d.ts +1 -1
  71. package/dist/indexer/navigation-goto.d.ts.map +1 -1
  72. package/dist/indexer/navigation-goto.js +66 -17
  73. package/dist/indexer/navigation-goto.js.map +1 -1
  74. package/dist/indexer/navigation-references.d.ts +23 -2
  75. package/dist/indexer/navigation-references.d.ts.map +1 -1
  76. package/dist/indexer/navigation-references.js +196 -4
  77. package/dist/indexer/navigation-references.js.map +1 -1
  78. package/dist/indexer/navigation.d.ts +9 -3
  79. package/dist/indexer/navigation.d.ts.map +1 -1
  80. package/dist/indexer/navigation.js +154 -41
  81. package/dist/indexer/navigation.js.map +1 -1
  82. package/dist/indexer/scope.d.ts.map +1 -1
  83. package/dist/indexer/scope.js +18 -0
  84. package/dist/indexer/scope.js.map +1 -1
  85. package/dist/indexer/symbols.d.ts +1 -0
  86. package/dist/indexer/symbols.d.ts.map +1 -1
  87. package/dist/indexer/types.d.ts +10 -0
  88. package/dist/indexer/types.d.ts.map +1 -1
  89. package/dist/indexer.d.ts +1 -1
  90. package/dist/indexer.d.ts.map +1 -1
  91. package/dist/indexer.js.map +1 -1
  92. package/dist/languages/definitions/javascript.d.ts.map +1 -1
  93. package/dist/languages/definitions/javascript.js +10 -2
  94. package/dist/languages/definitions/javascript.js.map +1 -1
  95. package/dist/languages/definitions/php.d.ts.map +1 -1
  96. package/dist/languages/definitions/php.js +15 -8
  97. package/dist/languages/definitions/php.js.map +1 -1
  98. package/dist/languages/definitions/python.d.ts.map +1 -1
  99. package/dist/languages/definitions/python.js +11 -1
  100. package/dist/languages/definitions/python.js.map +1 -1
  101. package/dist/languages/definitions/typescript.d.ts.map +1 -1
  102. package/dist/languages/definitions/typescript.js +20 -3
  103. package/dist/languages/definitions/typescript.js.map +1 -1
  104. package/dist/languages/definitions/zig.d.ts.map +1 -1
  105. package/dist/languages/definitions/zig.js +10 -3
  106. package/dist/languages/definitions/zig.js.map +1 -1
  107. package/dist/languages/import-statement-parsers.d.ts +3 -0
  108. package/dist/languages/import-statement-parsers.d.ts.map +1 -1
  109. package/dist/languages/import-statement-parsers.js +4 -0
  110. package/dist/languages/import-statement-parsers.js.map +1 -1
  111. package/dist/mcp/handlers.d.ts +2 -1
  112. package/dist/mcp/handlers.d.ts.map +1 -1
  113. package/dist/mcp/handlers.js +15 -1
  114. package/dist/mcp/handlers.js.map +1 -1
  115. package/dist/review/summaries.d.ts.map +1 -1
  116. package/dist/review/summaries.js +11 -19
  117. package/dist/review/summaries.js.map +1 -1
  118. package/dist/review/types.d.ts +2 -1
  119. package/dist/review/types.d.ts.map +1 -1
  120. package/dist/review/types.js.map +1 -1
  121. package/dist/session.d.ts +2 -9
  122. package/dist/session.d.ts.map +1 -1
  123. package/dist/session.js.map +1 -1
  124. package/dist/sql/navigation.d.ts +6 -1
  125. package/dist/sql/navigation.d.ts.map +1 -1
  126. package/dist/sql/navigation.js +35 -3
  127. package/dist/sql/navigation.js.map +1 -1
  128. package/dist/util/comments.d.ts +10 -0
  129. package/dist/util/comments.d.ts.map +1 -1
  130. package/dist/util/comments.js +43 -2
  131. package/dist/util/comments.js.map +1 -1
  132. package/dist/util/lines.d.ts +2 -0
  133. package/dist/util/lines.d.ts.map +1 -1
  134. package/dist/util/lines.js +14 -0
  135. package/dist/util/lines.js.map +1 -1
  136. package/dist/util/resolution/cargo-targets.d.ts +8 -0
  137. package/dist/util/resolution/cargo-targets.d.ts.map +1 -0
  138. package/dist/util/resolution/cargo-targets.js +186 -0
  139. package/dist/util/resolution/cargo-targets.js.map +1 -0
  140. package/dist/util/resolution/rust.d.ts.map +1 -1
  141. package/dist/util/resolution/rust.js +264 -26
  142. package/dist/util/resolution/rust.js.map +1 -1
  143. package/docs/mcp.md +1 -1
  144. package/package.json +3 -3
@@ -6,4 +6,18 @@ export function collectLineStartOffsets(source) {
6
6
  }
7
7
  return lineStarts;
8
8
  }
9
+ export function positionAtOffset(lineStarts, index) {
10
+ let low = 0;
11
+ let high = lineStarts.length - 1;
12
+ while (low < high) {
13
+ const middle = Math.ceil((low + high) / 2);
14
+ if (lineStarts[middle] <= index) {
15
+ low = middle;
16
+ }
17
+ else {
18
+ high = middle - 1;
19
+ }
20
+ }
21
+ return { line: low + 1, column: index - lineStarts[low] + 1, index };
22
+ }
9
23
  //# sourceMappingURL=lines.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"lines.js","sourceRoot":"","sources":["../../src/util/lines.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,uBAAuB,CAAC,MAAc;IACpD,MAAM,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC;IACvB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACtD,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,IAAI;YAAE,UAAU,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC"}
1
+ {"version":3,"file":"lines.js","sourceRoot":"","sources":["../../src/util/lines.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,uBAAuB,CAAC,MAAc;IACpD,MAAM,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC;IACvB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACtD,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,IAAI;YAAE,UAAU,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,UAA6B,EAAE,KAAa;IAC3E,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,IAAI,IAAI,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC;IACjC,OAAO,GAAG,GAAG,IAAI,EAAE,CAAC;QAClB,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3C,IAAI,UAAU,CAAC,MAAM,CAAE,IAAI,KAAK,EAAE,CAAC;YACjC,GAAG,GAAG,MAAM,CAAC;QACf,CAAC;aAAM,CAAC;YACN,IAAI,GAAG,MAAM,GAAG,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC,EAAE,MAAM,EAAE,KAAK,GAAG,UAAU,CAAC,GAAG,CAAE,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC;AACxE,CAAC"}
@@ -0,0 +1,8 @@
1
+ export type RustCrateRoots = {
2
+ /** Existing, resolved, project-confined crate root files. */
3
+ roots: string[];
4
+ /** Every candidate path considered, existing or not, for cache revalidation. */
5
+ probed: string[];
6
+ };
7
+ export declare function rustCrateRootFiles(cargoRoot: string, projectRoot: string): Promise<RustCrateRoots>;
8
+ //# sourceMappingURL=cargo-targets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cargo-targets.d.ts","sourceRoot":"","sources":["../../../src/util/resolution/cargo-targets.ts"],"names":[],"mappings":"AAsKA,MAAM,MAAM,cAAc,GAAG;IAC3B,6DAA6D;IAC7D,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,gFAAgF;IAChF,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC;AAEF,wBAAsB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAsCxG"}
@@ -0,0 +1,186 @@
1
+ import fsp from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { parse as parseToml } from "smol-toml";
4
+ import { isFilePathWithinRoot } from "../paths.js";
5
+ const AUTO_DISCOVERY_GROUPS = [
6
+ { flag: "autobins", directory: path.join("src", "bin") },
7
+ { flag: "autotests", directory: "tests" },
8
+ { flag: "autoexamples", directory: "examples" },
9
+ { flag: "autobenches", directory: "benches" },
10
+ ];
11
+ function isTomlTable(value) {
12
+ return value !== null && typeof value === "object" && !Array.isArray(value);
13
+ }
14
+ function tomlTables(value) {
15
+ if (Array.isArray(value))
16
+ return value.filter(isTomlTable);
17
+ return isTomlTable(value) ? [value] : [];
18
+ }
19
+ function tomlString(table, key) {
20
+ const value = table[key];
21
+ return typeof value === "string" && value.length > 0 ? value : undefined;
22
+ }
23
+ function packageAutoFlag(parsed, key) {
24
+ const pkg = isTomlTable(parsed.package) ? parsed.package : undefined;
25
+ const fromPackage = pkg?.[key];
26
+ if (typeof fromPackage === "boolean")
27
+ return fromPackage;
28
+ const fromRoot = parsed[key];
29
+ if (typeof fromRoot === "boolean")
30
+ return fromRoot;
31
+ return true;
32
+ }
33
+ function packageBuildScriptPath(parsed) {
34
+ const pkg = isTomlTable(parsed.package) ? parsed.package : undefined;
35
+ const build = pkg?.build;
36
+ if (typeof build === "boolean") {
37
+ if (!build)
38
+ return null;
39
+ return "build.rs";
40
+ }
41
+ if (typeof build === "string" && build.length)
42
+ return build;
43
+ return "build.rs";
44
+ }
45
+ function packageName(parsed) {
46
+ const pkg = isTomlTable(parsed.package) ? parsed.package : undefined;
47
+ return pkg ? tomlString(pkg, "name") : undefined;
48
+ }
49
+ function inferredNamedTargetDirectory(kind) {
50
+ if (kind === "bin")
51
+ return path.join("src", "bin");
52
+ if (kind === "example")
53
+ return "examples";
54
+ if (kind === "test")
55
+ return "tests";
56
+ return "benches";
57
+ }
58
+ function inferredNamedTargetPaths(kind, name, pkgName) {
59
+ const directory = inferredNamedTargetDirectory(kind);
60
+ const candidates = [path.join(directory, `${name}.rs`), path.join(directory, name, "main.rs")];
61
+ if (kind === "bin" && pkgName && name === pkgName) {
62
+ candidates.push(path.join("src", "main.rs"));
63
+ }
64
+ return candidates;
65
+ }
66
+ function explicitTargetPaths(parsed) {
67
+ const paths = [];
68
+ const lib = isTomlTable(parsed.lib) ? parsed.lib : undefined;
69
+ const libPath = lib ? tomlString(lib, "path") : undefined;
70
+ if (libPath)
71
+ paths.push(libPath);
72
+ const pkgName = packageName(parsed);
73
+ for (const key of ["bin", "example", "test", "bench"]) {
74
+ for (const target of tomlTables(parsed[key])) {
75
+ const targetPath = tomlString(target, "path");
76
+ if (targetPath) {
77
+ paths.push(targetPath);
78
+ continue;
79
+ }
80
+ const name = tomlString(target, "name");
81
+ if (!name)
82
+ continue;
83
+ paths.push(...inferredNamedTargetPaths(key, name, pkgName));
84
+ }
85
+ }
86
+ return paths;
87
+ }
88
+ async function parseCargoToml(cargoRoot) {
89
+ try {
90
+ const raw = await fsp.readFile(path.join(cargoRoot, "Cargo.toml"), "utf8");
91
+ const parsed = parseToml(raw);
92
+ return isTomlTable(parsed) ? parsed : null;
93
+ }
94
+ catch {
95
+ return null;
96
+ }
97
+ }
98
+ async function acceptCrateRoot(candidate, projectRoot, requireRustExtension = false) {
99
+ const resolved = path.resolve(candidate);
100
+ if (requireRustExtension && !resolved.endsWith(".rs"))
101
+ return null;
102
+ try {
103
+ const stat = await fsp.stat(resolved);
104
+ if (!stat.isFile())
105
+ return null;
106
+ }
107
+ catch {
108
+ return null;
109
+ }
110
+ if (!isFilePathWithinRoot(projectRoot, resolved))
111
+ return null;
112
+ try {
113
+ const realRoot = await fsp.realpath(projectRoot);
114
+ const realCandidate = await fsp.realpath(resolved);
115
+ if (!isFilePathWithinRoot(realRoot, realCandidate))
116
+ return null;
117
+ }
118
+ catch {
119
+ return null;
120
+ }
121
+ return resolved;
122
+ }
123
+ async function addCrateRoot(roots, probed, candidate, projectRoot, requireRustExtension = false) {
124
+ const resolved = path.resolve(candidate);
125
+ probed.add(resolved);
126
+ const accepted = await acceptCrateRoot(resolved, projectRoot, requireRustExtension);
127
+ if (accepted)
128
+ roots.add(accepted);
129
+ }
130
+ async function addAutodiscoveredDirectory(roots, probed, directory, projectRoot) {
131
+ probed.add(path.resolve(directory));
132
+ let entries = [];
133
+ try {
134
+ entries = await fsp.readdir(directory, { withFileTypes: true });
135
+ }
136
+ catch {
137
+ return;
138
+ }
139
+ for (const entry of entries) {
140
+ if (entry.isDirectory()) {
141
+ await addCrateRoot(roots, probed, path.join(directory, entry.name, "main.rs"), projectRoot, true);
142
+ continue;
143
+ }
144
+ if (entry.name.endsWith(".rs")) {
145
+ await addCrateRoot(roots, probed, path.join(directory, entry.name), projectRoot, true);
146
+ }
147
+ }
148
+ }
149
+ export async function rustCrateRootFiles(cargoRoot, projectRoot) {
150
+ const root = path.resolve(cargoRoot);
151
+ const roots = new Set();
152
+ const probed = new Set();
153
+ const parsed = await parseCargoToml(root);
154
+ if (parsed && !isTomlTable(parsed.package)) {
155
+ return { roots: [], probed: [] };
156
+ }
157
+ if (parsed) {
158
+ for (const relativePath of explicitTargetPaths(parsed)) {
159
+ await addCrateRoot(roots, probed, path.resolve(root, relativePath), projectRoot);
160
+ }
161
+ const buildScript = packageBuildScriptPath(parsed);
162
+ if (buildScript) {
163
+ await addCrateRoot(roots, probed, path.resolve(root, buildScript), projectRoot);
164
+ }
165
+ const lib = isTomlTable(parsed.lib) ? parsed.lib : undefined;
166
+ const libPath = lib ? tomlString(lib, "path") : undefined;
167
+ if (!libPath && (lib || packageAutoFlag(parsed, "autolib"))) {
168
+ await addCrateRoot(roots, probed, path.join(root, "src", "lib.rs"), projectRoot);
169
+ }
170
+ if (packageAutoFlag(parsed, "autobins")) {
171
+ await addCrateRoot(roots, probed, path.join(root, "src", "main.rs"), projectRoot);
172
+ }
173
+ }
174
+ else {
175
+ await addCrateRoot(roots, probed, path.join(root, "build.rs"), projectRoot);
176
+ await addCrateRoot(roots, probed, path.join(root, "src", "lib.rs"), projectRoot);
177
+ await addCrateRoot(roots, probed, path.join(root, "src", "main.rs"), projectRoot);
178
+ }
179
+ for (const group of AUTO_DISCOVERY_GROUPS) {
180
+ if (parsed && !packageAutoFlag(parsed, group.flag))
181
+ continue;
182
+ await addAutodiscoveredDirectory(roots, probed, path.join(root, group.directory), projectRoot);
183
+ }
184
+ return { roots: [...roots], probed: [...probed] };
185
+ }
186
+ //# sourceMappingURL=cargo-targets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cargo-targets.js","sourceRoot":"","sources":["../../../src/util/resolution/cargo-targets.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"rust.d.ts","sourceRoot":"","sources":["../../../src/util/resolution/rust.ts"],"names":[],"mappings":"AAiHA,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAWzF;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAoB1E;AAED,wBAAgB,mCAAmC,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAGnG;AAWD,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM,GAAG,MAAM,EAAE,CAEnG;AAeD,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,mBAAmB,CAAC,EAAE,MAAM,GAAG,MAAM,CASjH;AAED,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,EACrB,mBAAmB,CAAC,EAAE,MAAM,EAC5B,UAAU,SAAI,GACb,MAAM,GAAG,SAAS,CAMpB;AA2HD,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,MAAM,EACnB,mBAAmB,CAAC,EAAE,MAAM,GAC3B,MAAM,GAAG,SAAS,CAWpB;AAmRD,wBAAsB,qBAAqB,CACzC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,aAAa,CAAC,EAAE,MAAM,EACtB,mBAAmB,CAAC,EAAE,MAAM,GAC3B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgDxB"}
1
+ {"version":3,"file":"rust.d.ts","sourceRoot":"","sources":["../../../src/util/resolution/rust.ts"],"names":[],"mappings":"AAmKA,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAWzF;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAoB1E;AAED,wBAAgB,mCAAmC,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAGnG;AAWD,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM,GAAG,MAAM,EAAE,CAEnG;AAeD,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,mBAAmB,CAAC,EAAE,MAAM,GAAG,MAAM,CASjH;AAED,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,EACrB,mBAAmB,CAAC,EAAE,MAAM,EAC5B,UAAU,SAAI,GACb,MAAM,GAAG,SAAS,CAMpB;AA8HD,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,MAAM,EACnB,mBAAmB,CAAC,EAAE,MAAM,GAC3B,MAAM,GAAG,SAAS,CAWpB;AAyfD,wBAAsB,qBAAqB,CACzC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,aAAa,CAAC,EAAE,MAAM,EACtB,mBAAmB,CAAC,EAAE,MAAM,GAC3B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiDxB"}
@@ -3,12 +3,16 @@ import path from "node:path";
3
3
  import { isRustItemStartBoundary, skipRustAttribute, skipRustBalancedDelimiters, skipRustCommentOrLiteral, skipRustCommentsAndWhitespaceBackward, skipRustMacroTokenTree, skipRustOuterAttribute, } from "../../languages/import-statement-parsers.js";
4
4
  import { XID_IDENTIFIER_SOURCE } from "../identifiers.js";
5
5
  import { lruMapGet, lruMapSet } from "../lru-map.js";
6
- import { isFilePathWithinRoot } from "../paths.js";
6
+ import { fileIdentityKey, isFilePathWithinRoot } from "../paths.js";
7
7
  import { fileExists } from "../workspace.js";
8
+ import { rustCrateRootFiles } from "./cargo-targets.js";
8
9
  function isWithinOrEqual(candidate, root) {
9
10
  const relative = path.relative(root, candidate);
10
11
  return !relative || (!relative.startsWith("..") && !path.isAbsolute(relative));
11
12
  }
13
+ function rustPathIdentity(filePath) {
14
+ return fileIdentityKey(path.resolve(filePath));
15
+ }
12
16
  async function findNearestCargoRoot(fromFile, projectRoot) {
13
17
  const root = path.resolve(projectRoot);
14
18
  let dir = path.dirname(path.resolve(fromFile));
@@ -60,16 +64,31 @@ async function resolveRustModuleParts(baseDir, parts) {
60
64
  const RUST_PATH_ATTRIBUTE_PATTERN = /#\s*\[\s*path\s*=\s*(?:r(#*)"([\s\S]*?)"\1|"([^"]*)")\s*\]/gu;
61
65
  const RUST_CFG_TEST_ATTRIBUTE_PATTERN = /#\s*\[\s*cfg\s*\(\s*test\s*\)\s*\]/;
62
66
  const RUST_VISIBILITY_PATTERN = /^(?:pub(?:\s*\([^)]*\))?\s+)/;
63
- const RUST_MOD_NAME_PATTERN = new RegExp(String.raw `^mod\s+(${XID_IDENTIFIER_SOURCE})`, "u");
67
+ const RUST_MOD_NAME_PATTERN = new RegExp(String.raw `^mod\s+(?:r#)?(${XID_IDENTIFIER_SOURCE})`, "u");
64
68
  const MAX_RUST_PATH_ATTRIBUTE_CACHE_ENTRIES = 256;
69
+ const MAX_RUST_MODULE_TREE_CACHE_ENTRIES = 256;
70
+ const MAX_RUST_MODULE_TREE_FILES = 4096;
71
+ const MAX_RUST_MODULE_TREE_DEPTH = 32;
72
+ const MISSING_PATH_SIGNATURE = "missing";
73
+ /**
74
+ * A cached module tree is re-checked against its recorded stat signatures at most once per
75
+ * interval. Every reachable file and every probed path contributes a signature, so validating on
76
+ * each lookup would cost one stat per crate file per resolved `super` specifier. The window bounds
77
+ * that to one sweep per interval; a source edit is therefore observed within the interval rather
78
+ * than on the next lookup.
79
+ */
80
+ const RUST_MODULE_TREE_REVALIDATE_INTERVAL_MS = 100;
65
81
  const EMPTY_RUST_MODULE_SCOPE = {
66
82
  pathAttributes: new Map(),
67
83
  inlineModules: new Map(),
84
+ declaredModules: [],
68
85
  };
69
86
  const rustPathAttributeCache = new Map();
70
87
  const rustPathAttributeInflight = new Map();
88
+ const rustModuleTreeCache = new Map();
89
+ const rustModuleTreeInflight = new Map();
71
90
  function createRustModuleScope() {
72
- return { pathAttributes: new Map(), inlineModules: new Map() };
91
+ return { pathAttributes: new Map(), inlineModules: new Map(), declaredModules: [] };
73
92
  }
74
93
  function pathAttributeFromAttributeBlock(attributes) {
75
94
  let last;
@@ -246,6 +265,9 @@ function scanRustModuleScope(source, start, end, scope) {
246
265
  if (!testOnly && pathValue && !scope.pathAttributes.has(moduleName)) {
247
266
  scope.pathAttributes.set(moduleName, pathValue);
248
267
  }
268
+ if (!testOnly) {
269
+ scope.declaredModules.push({ name: moduleName, pathValue });
270
+ }
249
271
  index = cursor + 1;
250
272
  continue;
251
273
  }
@@ -289,9 +311,21 @@ export function extractRustModPathAttribute(source, moduleName, statementStartIn
289
311
  return undefined;
290
312
  return extractRustModPathAttributeScopes(source).pathAttributes.get(moduleName);
291
313
  }
292
- async function isExistingAttributedPathInsideProject(projectRoot, attributedPath) {
293
- if (!(await fileExists(attributedPath)))
294
- return false;
314
+ async function isExistingAttributedPathInsideProject(projectRoot, attributedPath, knownStat) {
315
+ if (knownStat !== undefined) {
316
+ if (knownStat === null || !knownStat.isFile())
317
+ return false;
318
+ }
319
+ else {
320
+ try {
321
+ const stat = await fsp.stat(attributedPath);
322
+ if (!stat.isFile())
323
+ return false;
324
+ }
325
+ catch {
326
+ return false;
327
+ }
328
+ }
295
329
  if (!isFilePathWithinRoot(projectRoot, attributedPath))
296
330
  return false;
297
331
  try {
@@ -306,6 +340,27 @@ async function isExistingAttributedPathInsideProject(projectRoot, attributedPath
306
340
  function rustPathAttributeSignature(stat) {
307
341
  return `${stat.size}:${stat.mtimeMs}`;
308
342
  }
343
+ async function currentPathSignature(target) {
344
+ try {
345
+ const stat = await fsp.stat(target);
346
+ return rustPathAttributeSignature(stat);
347
+ }
348
+ catch {
349
+ return MISSING_PATH_SIGNATURE;
350
+ }
351
+ }
352
+ async function recordPathStat(target, signatures) {
353
+ const resolved = path.resolve(target);
354
+ try {
355
+ const stat = await fsp.stat(resolved);
356
+ signatures.set(resolved, rustPathAttributeSignature(stat));
357
+ return stat;
358
+ }
359
+ catch {
360
+ signatures.set(resolved, MISSING_PATH_SIGNATURE);
361
+ return null;
362
+ }
363
+ }
309
364
  async function loadRustPathAttributeScope(file) {
310
365
  const resolved = path.resolve(file);
311
366
  let signature;
@@ -349,6 +404,153 @@ function rustChildModuleDir(parentFile) {
349
404
  return dir;
350
405
  return path.join(dir, stem);
351
406
  }
407
+ async function resolveDeclaredConventionalModule(projectRoot, currentModuleDir, name, signatures) {
408
+ for (const candidate of rustModuleCandidates(currentModuleDir, [name])) {
409
+ const resolved = path.resolve(candidate);
410
+ const stat = await recordPathStat(resolved, signatures);
411
+ if (!stat?.isFile())
412
+ continue;
413
+ if (await isExistingAttributedPathInsideProject(projectRoot, resolved, stat))
414
+ return resolved;
415
+ return null;
416
+ }
417
+ return null;
418
+ }
419
+ function addAttributedOwner(ownerSets, target, owner) {
420
+ const targetKey = rustPathIdentity(target);
421
+ let set = ownerSets.get(targetKey);
422
+ if (!set) {
423
+ set = new Map();
424
+ ownerSets.set(targetKey, set);
425
+ }
426
+ const parentFile = path.resolve(owner.parentFile);
427
+ const parentModuleDir = path.resolve(owner.parentModuleDir);
428
+ set.set(`${rustPathIdentity(parentFile)}\0${rustPathIdentity(parentModuleDir)}`, {
429
+ parentFile,
430
+ parentModuleDir,
431
+ });
432
+ }
433
+ async function rustModuleTreeSignaturesMatch(tree) {
434
+ const entries = [...tree.signatures];
435
+ const actual = await Promise.all(entries.map(([filePath]) => currentPathSignature(filePath)));
436
+ for (let i = 0; i < entries.length; i += 1) {
437
+ if (actual[i] !== entries[i]?.[1])
438
+ return false;
439
+ }
440
+ return true;
441
+ }
442
+ async function buildRustModuleTree(cargoRoot, projectRoot) {
443
+ const signatures = new Map();
444
+ const reachable = new Set();
445
+ const ownerSets = new Map();
446
+ let truncated = false;
447
+ await recordPathStat(path.join(cargoRoot, "Cargo.toml"), signatures);
448
+ for (const relative of ["src", path.join("src", "bin"), "tests", "examples", "benches"]) {
449
+ await recordPathStat(path.join(cargoRoot, relative), signatures);
450
+ }
451
+ const crateRoots = await rustCrateRootFiles(cargoRoot, projectRoot);
452
+ const crateRootSet = new Set(crateRoots.roots.map((file) => rustPathIdentity(file)));
453
+ for (const candidate of crateRoots.probed) {
454
+ await recordPathStat(candidate, signatures);
455
+ }
456
+ const walkFile = async (file, depth) => {
457
+ if (truncated)
458
+ return;
459
+ if (depth > MAX_RUST_MODULE_TREE_DEPTH) {
460
+ truncated = true;
461
+ return;
462
+ }
463
+ const resolved = path.resolve(file);
464
+ const identity = rustPathIdentity(resolved);
465
+ if (reachable.has(identity))
466
+ return;
467
+ const stat = await recordPathStat(resolved, signatures);
468
+ if (!(await isExistingAttributedPathInsideProject(projectRoot, resolved, stat)))
469
+ return;
470
+ if (reachable.size >= MAX_RUST_MODULE_TREE_FILES) {
471
+ truncated = true;
472
+ return;
473
+ }
474
+ reachable.add(identity);
475
+ const scope = await loadRustPathAttributeScope(resolved);
476
+ const moduleDir = crateRootSet.has(identity) ? path.dirname(resolved) : rustChildModuleDir(resolved);
477
+ await walkScope(scope, resolved, moduleDir, path.dirname(resolved), depth);
478
+ };
479
+ const walkScope = async (scope, currentFile, currentModuleDir, attributeDirectory, depth) => {
480
+ if (truncated)
481
+ return;
482
+ if (depth > MAX_RUST_MODULE_TREE_DEPTH) {
483
+ truncated = true;
484
+ return;
485
+ }
486
+ for (const { name, pathValue } of scope.declaredModules) {
487
+ if (truncated)
488
+ return;
489
+ let target = null;
490
+ if (pathValue !== undefined) {
491
+ const attributedPath = path.resolve(attributeDirectory, pathValue);
492
+ const attributedStat = await recordPathStat(attributedPath, signatures);
493
+ if (await isExistingAttributedPathInsideProject(projectRoot, attributedPath, attributedStat)) {
494
+ target = path.resolve(attributedPath);
495
+ addAttributedOwner(ownerSets, target, { parentFile: currentFile, parentModuleDir: currentModuleDir });
496
+ }
497
+ }
498
+ else {
499
+ target = await resolveDeclaredConventionalModule(projectRoot, currentModuleDir, name, signatures);
500
+ }
501
+ if (target)
502
+ await walkFile(target, depth + 1);
503
+ }
504
+ for (const [name, child] of scope.inlineModules) {
505
+ if (truncated)
506
+ return;
507
+ const nextDir = path.resolve(currentModuleDir, child.inlineLocation?.directory ?? name);
508
+ await walkScope(child, currentFile, nextDir, nextDir, depth + 1);
509
+ }
510
+ };
511
+ for (const crateRoot of crateRoots.roots) {
512
+ await walkFile(crateRoot, 0);
513
+ }
514
+ const owners = new Map();
515
+ for (const [target, set] of ownerSets) {
516
+ owners.set(target, [...set.values()]);
517
+ }
518
+ return { signatures, truncated, reachable, owners, validatedAt: Date.now() };
519
+ }
520
+ async function getRustModuleTree(cargoRoot, projectRoot) {
521
+ const resolvedCargoRoot = path.resolve(cargoRoot);
522
+ const resolvedProjectRoot = path.resolve(projectRoot);
523
+ const key = `${rustPathIdentity(resolvedCargoRoot)}\0${rustPathIdentity(resolvedProjectRoot)}`;
524
+ const cached = lruMapGet(rustModuleTreeCache, key);
525
+ if (cached && Date.now() - cached.validatedAt < RUST_MODULE_TREE_REVALIDATE_INTERVAL_MS) {
526
+ return cached;
527
+ }
528
+ const inflight = rustModuleTreeInflight.get(key);
529
+ if (inflight)
530
+ return await inflight;
531
+ const pending = (async () => {
532
+ try {
533
+ const existing = lruMapGet(rustModuleTreeCache, key);
534
+ if (existing) {
535
+ if (Date.now() - existing.validatedAt < RUST_MODULE_TREE_REVALIDATE_INTERVAL_MS) {
536
+ return existing;
537
+ }
538
+ if (await rustModuleTreeSignaturesMatch(existing)) {
539
+ existing.validatedAt = Date.now();
540
+ return existing;
541
+ }
542
+ }
543
+ const tree = await buildRustModuleTree(resolvedCargoRoot, resolvedProjectRoot);
544
+ lruMapSet(rustModuleTreeCache, key, tree, MAX_RUST_MODULE_TREE_CACHE_ENTRIES);
545
+ return tree;
546
+ }
547
+ finally {
548
+ rustModuleTreeInflight.delete(key);
549
+ }
550
+ })();
551
+ rustModuleTreeInflight.set(key, pending);
552
+ return await pending;
553
+ }
352
554
  async function rustDeclaringFileCandidates(fromFile, sourceRoot) {
353
555
  const files = [];
354
556
  const seen = new Set();
@@ -424,37 +626,66 @@ async function findAttributedDeclarationParent(projectRoot, declaringFile, targe
424
626
  const scope = await loadRustPathAttributeScope(declaringFile);
425
627
  return walk(scope, path.resolve(declaringFile), rustChildModuleDir(declaringFile), path.dirname(declaringFile));
426
628
  }
427
- async function findPathAttributeParent(projectRoot, fromFile, sourceRoot) {
629
+ async function findPathAttributeParent(projectRoot, fromFile, sourceRoot, cargoRoot) {
630
+ if (cargoRoot) {
631
+ const tree = await getRustModuleTree(cargoRoot, projectRoot);
632
+ if (!tree.truncated) {
633
+ const fromKey = rustPathIdentity(fromFile);
634
+ const owners = tree.owners.get(fromKey) ?? [];
635
+ if (owners.length === 1) {
636
+ const owner = owners[0];
637
+ if (owner)
638
+ return { status: "resolved", parent: owner };
639
+ }
640
+ if (owners.length > 1)
641
+ return { status: "ambiguous" };
642
+ if (tree.reachable.has(fromKey))
643
+ return { status: "unresolved" };
644
+ }
645
+ }
428
646
  const candidates = await rustDeclaringFileCandidates(fromFile, sourceRoot);
429
647
  for (const candidate of candidates) {
430
648
  const found = await findAttributedDeclarationParent(projectRoot, candidate, fromFile);
431
649
  if (found)
432
- return found;
650
+ return { status: "resolved", parent: found };
433
651
  }
434
- return null;
652
+ return { status: "unresolved" };
435
653
  }
436
- async function rustSuperModuleContext(projectRoot, fromFile, sourceRoot) {
437
- const attributed = await findPathAttributeParent(projectRoot, fromFile, sourceRoot);
438
- if (attributed)
439
- return attributed;
654
+ async function rustSuperModuleContext(projectRoot, fromFile, sourceRoot, cargoRoot) {
655
+ const attributed = await findPathAttributeParent(projectRoot, fromFile, sourceRoot, cargoRoot);
656
+ if (attributed.status === "ambiguous")
657
+ return { status: "ambiguous" };
658
+ if (attributed.status === "resolved") {
659
+ return {
660
+ status: "resolved",
661
+ parentFile: attributed.parent.parentFile,
662
+ parentModuleDir: attributed.parent.parentModuleDir,
663
+ };
664
+ }
440
665
  const currentDir = path.dirname(fromFile);
441
666
  const parentModuleDir = parentRustModuleDir(fromFile, currentDir);
442
- return { parentFile: await resolveRustModuleParts(parentModuleDir, []), parentModuleDir };
667
+ return {
668
+ status: "resolved",
669
+ parentFile: await resolveRustModuleParts(parentModuleDir, []),
670
+ parentModuleDir,
671
+ };
443
672
  }
444
- async function declaringFileForSpecifierHead(projectRoot, fromFile, sourceRoot, head) {
673
+ async function declaringFileForSpecifierHead(projectRoot, fromFile, sourceRoot, cargoRoot, head) {
445
674
  if (head === "*")
446
- return null;
675
+ return { status: "resolved", file: null };
447
676
  if (head === "crate") {
448
- return resolveRustModuleParts(sourceRoot, []);
677
+ return { status: "resolved", file: await resolveRustModuleParts(sourceRoot, []) };
449
678
  }
450
679
  if (head === "self") {
451
- return path.resolve(fromFile);
680
+ return { status: "resolved", file: path.resolve(fromFile) };
452
681
  }
453
682
  if (head === "super") {
454
- const context = await rustSuperModuleContext(projectRoot, fromFile, sourceRoot);
455
- return context.parentFile;
683
+ const context = await rustSuperModuleContext(projectRoot, fromFile, sourceRoot, cargoRoot);
684
+ if (context.status === "ambiguous")
685
+ return { status: "ambiguous" };
686
+ return { status: "resolved", file: context.parentFile };
456
687
  }
457
- return path.resolve(fromFile);
688
+ return { status: "resolved", file: path.resolve(fromFile) };
458
689
  }
459
690
  function rustScopeAtDirectory(scope, fromDir, toDir) {
460
691
  const relative = path.relative(fromDir, toDir);
@@ -469,19 +700,24 @@ function rustScopeAtDirectory(scope, fromDir, toDir) {
469
700
  }
470
701
  return current;
471
702
  }
472
- async function resolveAttributedRustModulePath(projectRoot, fromFile, parts, sourceRoot) {
703
+ async function resolveAttributedRustModulePath(projectRoot, fromFile, parts, sourceRoot, cargoRoot) {
473
704
  const head = parts[0];
474
705
  if (!head)
475
706
  return undefined;
476
707
  let startFile;
477
708
  let startModuleDir;
478
709
  if (head === "super") {
479
- const context = await rustSuperModuleContext(projectRoot, fromFile, sourceRoot);
710
+ const context = await rustSuperModuleContext(projectRoot, fromFile, sourceRoot, cargoRoot);
711
+ if (context.status === "ambiguous")
712
+ return null;
480
713
  startFile = context.parentFile;
481
714
  startModuleDir = context.parentModuleDir;
482
715
  }
483
716
  else {
484
- startFile = await declaringFileForSpecifierHead(projectRoot, fromFile, sourceRoot, head);
717
+ const declaring = await declaringFileForSpecifierHead(projectRoot, fromFile, sourceRoot, cargoRoot, head);
718
+ if (declaring.status === "ambiguous")
719
+ return null;
720
+ startFile = declaring.file;
485
721
  }
486
722
  if (!startFile)
487
723
  return undefined;
@@ -572,7 +808,7 @@ export async function resolveRustImportPath(projectRoot, fromFile, spec, pathAtt
572
808
  }
573
809
  const cargoRoot = await findNearestCargoRoot(fromFile, projectRoot);
574
810
  const sourceRoot = crateSourceRoot(cargoRoot, projectRoot);
575
- const walked = await resolveAttributedRustModulePath(projectRoot, fromFile, parts, sourceRoot);
811
+ const walked = await resolveAttributedRustModulePath(projectRoot, fromFile, parts, sourceRoot, cargoRoot);
576
812
  if (walked !== undefined)
577
813
  return walked;
578
814
  const currentDir = path.dirname(fromFile);
@@ -587,7 +823,9 @@ export async function resolveRustImportPath(projectRoot, fromFile, spec, pathAtt
587
823
  return resolveRustModuleParts(currentDir, tail);
588
824
  }
589
825
  if (head === "super") {
590
- const context = await rustSuperModuleContext(projectRoot, fromFile, sourceRoot);
826
+ const context = await rustSuperModuleContext(projectRoot, fromFile, sourceRoot, cargoRoot);
827
+ if (context.status === "ambiguous")
828
+ return null;
591
829
  if (!tail.length)
592
830
  return context.parentFile;
593
831
  return resolveRustModuleParts(context.parentModuleDir, tail);