dsh-plugin-codegraph-tree-sitter 0.1.0 → 0.1.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/README.md +1 -1
- package/lib/extract.d.ts +17 -0
- package/lib/extract.js +1001 -19
- package/lib/index.d.ts +7 -3
- package/lib/index.js +7 -3
- package/lib/languages.d.ts +93 -1
- package/lib/languages.js +331 -2
- package/lib/resolve.d.ts +3 -0
- package/lib/resolve.js +44 -3
- package/lib/schema.js +3 -3
- package/lib/watcher.js +12 -0
- package/package.json +2 -2
package/lib/resolve.js
CHANGED
|
@@ -99,6 +99,28 @@ function buildNameIndex(nodesByFile) {
|
|
|
99
99
|
}
|
|
100
100
|
return index;
|
|
101
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* A name index restricted to `class`/`interface` nodes, for `extends`/`implements` resolution.
|
|
104
|
+
* `extractHeritage` only ever names a base class or interface, never a function or variable — indexing
|
|
105
|
+
* only those kinds keeps a same-named function or module-level `const` (`packages/tree-sitter/src/
|
|
106
|
+
* languages.ts` extracts those now too) from ever winning a heritage reference by coincidence, tighter
|
|
107
|
+
* than `buildNameIndex`'s workspace-wide, any-kind index that call resolution already accepts.
|
|
108
|
+
*/
|
|
109
|
+
function buildTypeNameIndex(nodesByFile) {
|
|
110
|
+
const index = new Map();
|
|
111
|
+
for (const nodes of nodesByFile.values()) {
|
|
112
|
+
for (const node of nodes) {
|
|
113
|
+
if (node.kind !== 'class' && node.kind !== 'interface')
|
|
114
|
+
continue;
|
|
115
|
+
const existing = index.get(node.name);
|
|
116
|
+
if (existing === undefined)
|
|
117
|
+
index.set(node.name, [node]);
|
|
118
|
+
else
|
|
119
|
+
existing.push(node);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return index;
|
|
123
|
+
}
|
|
102
124
|
/** Resolve one file's raw imports against the indexed workspace. */
|
|
103
125
|
function resolveImports(path, language, imports, indexed) {
|
|
104
126
|
const byLocalName = new Map();
|
|
@@ -143,6 +165,7 @@ export function resolveWorkspace(files, now) {
|
|
|
143
165
|
isExported: false,
|
|
144
166
|
isAsync: false,
|
|
145
167
|
isStatic: false,
|
|
168
|
+
decorators: [],
|
|
146
169
|
updatedAt: now,
|
|
147
170
|
};
|
|
148
171
|
nodes.push(fileNode);
|
|
@@ -168,6 +191,7 @@ export function resolveWorkspace(files, now) {
|
|
|
168
191
|
isExported: def.isExported,
|
|
169
192
|
isAsync: def.isAsync,
|
|
170
193
|
isStatic: def.isStatic,
|
|
194
|
+
decorators: def.decorators,
|
|
171
195
|
updatedAt: now,
|
|
172
196
|
});
|
|
173
197
|
}
|
|
@@ -180,6 +204,7 @@ export function resolveWorkspace(files, now) {
|
|
|
180
204
|
}
|
|
181
205
|
}
|
|
182
206
|
const nameIndex = buildNameIndex(nodesByFile);
|
|
207
|
+
const typeNameIndex = buildTypeNameIndex(nodesByFile);
|
|
183
208
|
const unresolved = [];
|
|
184
209
|
for (const file of files) {
|
|
185
210
|
const bindings = resolveImports(file.path, file.language, file.extraction.imports, indexed);
|
|
@@ -190,7 +215,7 @@ export function resolveWorkspace(files, now) {
|
|
|
190
215
|
const importedLocalNames = new Set(file.extraction.imports.map(binding => binding.localName));
|
|
191
216
|
for (const call of file.extraction.calls) {
|
|
192
217
|
const callerId = call.callerKey === null ? mustGet(fileNodeId, file.path) : mustGet(keyToId, call.callerKey);
|
|
193
|
-
const resolved =
|
|
218
|
+
const resolved = resolveName(call.calleeName, bindings, nodesByFile, nameIndex);
|
|
194
219
|
if (resolved === undefined) {
|
|
195
220
|
const likelyExternal = call.isMemberCall || importedLocalNames.has(call.calleeName);
|
|
196
221
|
unresolved.push({ source: callerId, filePath: file.path, calleeName: call.calleeName, line: call.line, col: call.column, likelyExternal });
|
|
@@ -198,11 +223,27 @@ export function resolveWorkspace(files, now) {
|
|
|
198
223
|
}
|
|
199
224
|
edges.push({ source: callerId, target: resolved, kind: 'calls', line: call.line, col: call.column, provenance: 'tree-sitter' });
|
|
200
225
|
}
|
|
226
|
+
// An unresolved `extends`/`implements` reference is dropped silently, not routed through
|
|
227
|
+
// `unresolved` — that array (and the `unresolvedCount`/`unresolvedLikelyInternalCount` split built
|
|
228
|
+
// on it) is a calibrated signal specifically about call-resolution gaps; folding in a different edge
|
|
229
|
+
// kind would change what that count measures.
|
|
230
|
+
for (const ref of file.extraction.heritage) {
|
|
231
|
+
const resolved = resolveName(ref.targetName, bindings, nodesByFile, typeNameIndex);
|
|
232
|
+
if (resolved === undefined)
|
|
233
|
+
continue;
|
|
234
|
+
edges.push({ source: mustGet(keyToId, ref.sourceKey), target: resolved, kind: ref.relation, provenance: 'tree-sitter' });
|
|
235
|
+
}
|
|
201
236
|
}
|
|
202
237
|
return { nodes, edges, unresolved };
|
|
203
238
|
}
|
|
204
|
-
/**
|
|
205
|
-
|
|
239
|
+
/**
|
|
240
|
+
* Resolve a bare name to exactly one declaration, or `undefined` per rule 3 — shared by call
|
|
241
|
+
* resolution (against the workspace-wide, any-kind `nameIndex`) and `extends`/`implements` resolution
|
|
242
|
+
* (against the `class`/`interface`-only {@link buildTypeNameIndex}); the two-tier rule itself (an
|
|
243
|
+
* imported binding wins if unambiguous in its target file, else a workspace-wide-unique name wins) does
|
|
244
|
+
* not depend on which candidate pool the caller passes in.
|
|
245
|
+
*/
|
|
246
|
+
function resolveName(calleeName, bindings, nodesByFile, nameIndex) {
|
|
206
247
|
const bound = bindings.byLocalName.get(calleeName);
|
|
207
248
|
if (bound !== undefined) {
|
|
208
249
|
const targetNodes = mustGet(nodesByFile, bound.targetFile);
|
package/lib/schema.js
CHANGED
|
@@ -75,10 +75,10 @@ export async function writeGraph(databasePath, input) {
|
|
|
75
75
|
.run(SCHEMA_VERSION, input.indexedAt, 'dsh-codegraph-tree-sitter');
|
|
76
76
|
const insertNode = db.prepare(`INSERT INTO nodes
|
|
77
77
|
(id, kind, name, qualified_name, file_path, language, start_line, end_line, start_column,
|
|
78
|
-
end_column, is_exported, is_async, is_static, updated_at)
|
|
79
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
78
|
+
end_column, is_exported, is_async, is_static, decorators, updated_at)
|
|
79
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
80
80
|
for (const node of input.nodes) {
|
|
81
|
-
insertNode.run(node.id, node.kind, node.name, node.qualifiedName, node.filePath, node.language, node.startLine, node.endLine, node.startColumn, node.endColumn, node.isExported ? 1 : 0, node.isAsync ? 1 : 0, node.isStatic ? 1 : 0, node.updatedAt);
|
|
81
|
+
insertNode.run(node.id, node.kind, node.name, node.qualifiedName, node.filePath, node.language, node.startLine, node.endLine, node.startColumn, node.endColumn, node.isExported ? 1 : 0, node.isAsync ? 1 : 0, node.isStatic ? 1 : 0, JSON.stringify(node.decorators), node.updatedAt);
|
|
82
82
|
}
|
|
83
83
|
const insertEdge = db.prepare('INSERT INTO edges (source, target, kind, line, col, provenance) VALUES (?, ?, ?, ?, ?, ?)');
|
|
84
84
|
for (const edge of input.edges) {
|
package/lib/watcher.js
CHANGED
|
@@ -275,7 +275,19 @@ export function createWatcher(config, primitive = nodeWatch) {
|
|
|
275
275
|
syncFailureCount = 0;
|
|
276
276
|
pendingPaths.clear();
|
|
277
277
|
if (process.platform === 'darwin' || process.platform === 'win32') {
|
|
278
|
+
// Started immediately, unlike the per-directory branch below, because the recursive `fs.watch`
|
|
279
|
+
// call itself must stay synchronous within start() — nothing here should make callers await a
|
|
280
|
+
// gitignore read just to get a watch installed. `gitignoreRules` starts empty and is filled in
|
|
281
|
+
// once the read resolves; every event that arrives before then is a startup-window race no
|
|
282
|
+
// different in kind from the FSEvents kernel-side warm-up delay already documented above, and
|
|
283
|
+
// resolves in the time to read one small file, not the time a human takes to edit one.
|
|
278
284
|
startRecursive();
|
|
285
|
+
if (config.respectGitignore) {
|
|
286
|
+
void loadGitignore(config.root).then(rules => {
|
|
287
|
+
if (running)
|
|
288
|
+
gitignoreRules = rules;
|
|
289
|
+
});
|
|
290
|
+
}
|
|
279
291
|
return;
|
|
280
292
|
}
|
|
281
293
|
void (async () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-codegraph-tree-sitter",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Self-built code-graph indexer for DeepSeek Harness — parses a workspace with web-tree-sitter and writes schema version 4 to .codegraph/codegraph.db, the same on-disk format the codegraph CLI writes, so no external indexer is required",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dsh",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"@deepseek-ai/schemastery": "^3.18.1",
|
|
52
52
|
"tree-sitter-wasms": "^0.1.13",
|
|
53
53
|
"web-tree-sitter": "^0.25.10",
|
|
54
|
-
"dsh-plugin-codegraph-service": "^0.1.
|
|
54
|
+
"dsh-plugin-codegraph-service": "^0.1.4"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
57
|
"@deepseek-ai/cordis": ">=4.0.1",
|