@shanepadgett/tau-agent 0.27.0 → 0.28.0
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/extensions/context/sync.ts +1 -9
- package/extensions/context-pruning/prune.ts +2 -2
- package/extensions/explore/README.md +13 -21
- package/extensions/explore/ast/adapter.ts +97 -0
- package/extensions/explore/ast/engine.ts +470 -0
- package/extensions/explore/ast/format/ast-search.ts +97 -0
- package/extensions/explore/ast/format/composite.ts +26 -0
- package/extensions/explore/ast/format/context.ts +66 -0
- package/extensions/explore/ast/format/deps.ts +86 -0
- package/extensions/explore/ast/format/discover.ts +85 -0
- package/extensions/explore/ast/format/impact.ts +105 -0
- package/extensions/explore/ast/format/outline.ts +66 -0
- package/extensions/explore/ast/format/relationships.ts +81 -0
- package/extensions/explore/ast/format/show.ts +38 -0
- package/extensions/explore/ast/grammars/kotlin.wasm +0 -0
- package/extensions/explore/ast/grammars/manifest.json +41 -0
- package/extensions/explore/ast/grammars/manifest.ts +99 -0
- package/extensions/explore/ast/grammars/odin.wasm +0 -0
- package/extensions/explore/ast/grammars/swift.wasm +0 -0
- package/extensions/explore/ast/graph/file-graph.ts +360 -0
- package/extensions/explore/ast/graph/relationships.ts +597 -0
- package/extensions/explore/ast/identity.ts +119 -0
- package/extensions/explore/ast/ir.ts +130 -0
- package/extensions/explore/ast/languages/csharp-file-deps.ts +189 -0
- package/extensions/explore/ast/languages/csharp.ts +493 -0
- package/extensions/explore/ast/languages/file-dep-util.ts +120 -0
- package/extensions/explore/ast/languages/fixtures/sample.cs +17 -0
- package/extensions/explore/ast/languages/fixtures/sample.java +21 -0
- package/extensions/explore/ast/languages/fixtures/sample.kt +15 -0
- package/extensions/explore/ast/languages/fixtures/sample.odin +28 -0
- package/extensions/explore/ast/languages/fixtures/sample.rs +31 -0
- package/extensions/explore/ast/languages/fixtures/sample.swift +25 -0
- package/extensions/explore/ast/languages/go-file-deps.ts +139 -0
- package/extensions/explore/ast/languages/go.ts +545 -0
- package/extensions/explore/ast/languages/java.ts +412 -0
- package/extensions/explore/ast/languages/jvm-file-deps.ts +217 -0
- package/extensions/explore/ast/languages/kotlin.ts +611 -0
- package/extensions/explore/ast/languages/odin-file-deps.ts +175 -0
- package/extensions/explore/ast/languages/odin.ts +407 -0
- package/extensions/explore/ast/languages/rust-file-deps.ts +159 -0
- package/extensions/explore/ast/languages/rust.ts +506 -0
- package/extensions/explore/ast/languages/swift-file-deps.ts +59 -0
- package/extensions/explore/ast/languages/swift.ts +466 -0
- package/extensions/explore/ast/languages/tree.ts +220 -0
- package/extensions/explore/ast/languages/typescript-file-deps.ts +259 -0
- package/extensions/explore/ast/languages/typescript-package-surface.ts +440 -0
- package/extensions/explore/ast/languages/typescript.ts +716 -0
- package/extensions/explore/ast/markdown.ts +145 -0
- package/extensions/explore/ast/package-surface.ts +42 -0
- package/extensions/explore/ast/queries/ast-search.ts +306 -0
- package/extensions/explore/ast/queries/composite-target.ts +77 -0
- package/extensions/explore/ast/queries/context.ts +406 -0
- package/extensions/explore/ast/queries/discover.ts +427 -0
- package/extensions/explore/ast/queries/impact.ts +250 -0
- package/extensions/explore/ast/queries/outline.ts +203 -0
- package/extensions/explore/ast/queries/show.ts +262 -0
- package/extensions/explore/ast/query.ts +48 -0
- package/extensions/explore/ast/read/hook.ts +125 -0
- package/extensions/explore/ast/read/policy.ts +55 -0
- package/extensions/explore/ast/registry.ts +120 -0
- package/extensions/explore/ast/scan.ts +76 -0
- package/extensions/explore/ast/slice.ts +31 -0
- package/extensions/explore/ast/tools/ast-search.ts +138 -0
- package/extensions/explore/ast/tools/context.ts +126 -0
- package/extensions/explore/ast/tools/deps.ts +72 -0
- package/extensions/explore/ast/tools/discover.ts +208 -0
- package/extensions/explore/ast/tools/impact.ts +135 -0
- package/extensions/explore/ast/tools/outline.ts +200 -0
- package/extensions/explore/ast/tools/relationships.ts +160 -0
- package/extensions/explore/ast/tools/render.ts +121 -0
- package/extensions/explore/ast/tools/reverse-deps.ts +72 -0
- package/extensions/explore/ast/tools/show.ts +116 -0
- package/extensions/explore/guidance.ts +52 -0
- package/extensions/explore/index.ts +82 -92
- package/extensions/explore/read/autoread.ts +118 -0
- package/extensions/explore/settings.ts +49 -31
- package/extensions/explore/traverse.ts +254 -91
- package/extensions/handoff/index.ts +1 -1
- package/extensions/publish/index.ts +18 -85
- package/extensions/publish/publish-ui.ts +0 -4
- package/extensions/soul/README.md +7 -4
- package/extensions/soul/index.ts +11 -5
- package/extensions/soul/prompt.ts +31 -13
- package/extensions/soul/settings.ts +7 -2
- package/extensions/subagent/agents/context-sync.md +6 -10
- package/extensions/subagent/agents/review.md +24 -55
- package/extensions/subagent/agents/scout.md +38 -115
- package/extensions/subagent/run.ts +1 -1
- package/extensions/tau-help/help.md +2 -4
- package/package.json +6 -4
- package/schemas/tau.schema.json +35 -30
- package/{extensions/explore → shared}/autoread.ts +21 -7
- package/shared/events.ts +1 -1
- package/shared/full-file-knowledge.ts +49 -0
- package/extensions/explore/ast-guidance.ts +0 -67
- package/extensions/explore/ast-languages.ts +0 -61
- package/extensions/explore/ast-tools.ts +0 -1994
- package/extensions/explore/ast-worker.ts +0 -1025
- package/extensions/explore/find.ts +0 -147
- package/extensions/explore/full-file-knowledge.ts +0 -234
- package/extensions/explore/grep.ts +0 -749
- package/extensions/explore/limits.ts +0 -22
- package/extensions/explore/ls.ts +0 -113
- package/extensions/explore/orientation-state.ts +0 -280
- package/extensions/explore/path-display.ts +0 -41
- package/extensions/explore/path-tree.ts +0 -150
- package/extensions/explore/read-cache.ts +0 -273
- package/extensions/explore/read-snapshots.ts +0 -57
- package/extensions/explore/read-stats-panel.ts +0 -176
- package/extensions/explore/read-stats.ts +0 -138
- package/extensions/explore/read.ts +0 -372
- package/extensions/explore/result.ts +0 -29
- package/native-bin/darwin-arm64/tau-ast +0 -0
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
import type { Node, Tree } from "web-tree-sitter";
|
|
2
|
+
import type { ExtractResult, GrammarAdapter, LanguageCapabilities } from "../adapter.ts";
|
|
3
|
+
import { resolveCsharpFileDep } from "./csharp-file-deps.ts";
|
|
4
|
+
import type { CallSite, Decl, ImportRef, Visibility } from "../ir.ts";
|
|
5
|
+
import {
|
|
6
|
+
applyDoc,
|
|
7
|
+
callSite,
|
|
8
|
+
docSpanBefore,
|
|
9
|
+
endLine,
|
|
10
|
+
field,
|
|
11
|
+
finishDecl,
|
|
12
|
+
nameText,
|
|
13
|
+
qualify,
|
|
14
|
+
startLine,
|
|
15
|
+
walkBodyNodes,
|
|
16
|
+
type DocSpan,
|
|
17
|
+
} from "./tree.ts";
|
|
18
|
+
|
|
19
|
+
const CAPABILITIES: LanguageCapabilities = {
|
|
20
|
+
shape: true,
|
|
21
|
+
search: true,
|
|
22
|
+
fileDeps: true,
|
|
23
|
+
callEdges: true,
|
|
24
|
+
packageSurface: false,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const IMPORT_NOISE = new Set([
|
|
28
|
+
"abstract",
|
|
29
|
+
"as",
|
|
30
|
+
"base",
|
|
31
|
+
"bool",
|
|
32
|
+
"break",
|
|
33
|
+
"byte",
|
|
34
|
+
"case",
|
|
35
|
+
"catch",
|
|
36
|
+
"char",
|
|
37
|
+
"checked",
|
|
38
|
+
"class",
|
|
39
|
+
"const",
|
|
40
|
+
"continue",
|
|
41
|
+
"decimal",
|
|
42
|
+
"default",
|
|
43
|
+
"delegate",
|
|
44
|
+
"do",
|
|
45
|
+
"double",
|
|
46
|
+
"else",
|
|
47
|
+
"enum",
|
|
48
|
+
"event",
|
|
49
|
+
"explicit",
|
|
50
|
+
"extern",
|
|
51
|
+
"false",
|
|
52
|
+
"finally",
|
|
53
|
+
"fixed",
|
|
54
|
+
"float",
|
|
55
|
+
"for",
|
|
56
|
+
"foreach",
|
|
57
|
+
"goto",
|
|
58
|
+
"if",
|
|
59
|
+
"implicit",
|
|
60
|
+
"in",
|
|
61
|
+
"int",
|
|
62
|
+
"interface",
|
|
63
|
+
"internal",
|
|
64
|
+
"is",
|
|
65
|
+
"lock",
|
|
66
|
+
"long",
|
|
67
|
+
"namespace",
|
|
68
|
+
"new",
|
|
69
|
+
"null",
|
|
70
|
+
"object",
|
|
71
|
+
"operator",
|
|
72
|
+
"out",
|
|
73
|
+
"override",
|
|
74
|
+
"params",
|
|
75
|
+
"private",
|
|
76
|
+
"protected",
|
|
77
|
+
"public",
|
|
78
|
+
"readonly",
|
|
79
|
+
"ref",
|
|
80
|
+
"return",
|
|
81
|
+
"sbyte",
|
|
82
|
+
"sealed",
|
|
83
|
+
"short",
|
|
84
|
+
"sizeof",
|
|
85
|
+
"stackalloc",
|
|
86
|
+
"static",
|
|
87
|
+
"string",
|
|
88
|
+
"struct",
|
|
89
|
+
"switch",
|
|
90
|
+
"this",
|
|
91
|
+
"throw",
|
|
92
|
+
"true",
|
|
93
|
+
"try",
|
|
94
|
+
"typeof",
|
|
95
|
+
"uint",
|
|
96
|
+
"ulong",
|
|
97
|
+
"unchecked",
|
|
98
|
+
"unsafe",
|
|
99
|
+
"ushort",
|
|
100
|
+
"using",
|
|
101
|
+
"virtual",
|
|
102
|
+
"void",
|
|
103
|
+
"volatile",
|
|
104
|
+
"while",
|
|
105
|
+
]);
|
|
106
|
+
|
|
107
|
+
const EXTENSIONS = [".cs"] as const;
|
|
108
|
+
|
|
109
|
+
const NAMESPACE_DECLARATION = "namespace_declaration";
|
|
110
|
+
const FILE_SCOPED_NAMESPACE_DECLARATION = "file_scoped_namespace_declaration";
|
|
111
|
+
const CLASS_DECLARATION = "class_declaration";
|
|
112
|
+
const STRUCT_DECLARATION = "struct_declaration";
|
|
113
|
+
const RECORD_DECLARATION = "record_declaration";
|
|
114
|
+
const INTERFACE_DECLARATION = "interface_declaration";
|
|
115
|
+
const ENUM_DECLARATION = "enum_declaration";
|
|
116
|
+
const METHOD_DECLARATION = "method_declaration";
|
|
117
|
+
const PROPERTY_DECLARATION = "property_declaration";
|
|
118
|
+
const FIELD_DECLARATION = "field_declaration";
|
|
119
|
+
const CONSTRUCTOR_DECLARATION = "constructor_declaration";
|
|
120
|
+
const EVENT_FIELD_DECLARATION = "event_field_declaration";
|
|
121
|
+
const EVENT_DECLARATION = "event_declaration";
|
|
122
|
+
const DELEGATE_DECLARATION = "delegate_declaration";
|
|
123
|
+
const OPERATOR_DECLARATION = "operator_declaration";
|
|
124
|
+
const INDEXER_DECLARATION = "indexer_declaration";
|
|
125
|
+
const ENUM_MEMBER_DECLARATION = "enum_member_declaration";
|
|
126
|
+
const DECLARATION_LIST = "declaration_list";
|
|
127
|
+
const ENUM_MEMBER_DECLARATION_LIST = "enum_member_declaration_list";
|
|
128
|
+
const VARIABLE_DECLARATION = "variable_declaration";
|
|
129
|
+
const VARIABLE_DECLARATOR = "variable_declarator";
|
|
130
|
+
const USING_DIRECTIVE = "using_directive";
|
|
131
|
+
const MODIFIER = "modifier";
|
|
132
|
+
const COMMENT = "comment";
|
|
133
|
+
|
|
134
|
+
const INVOCATION = "invocation_expression";
|
|
135
|
+
const OBJECT_CREATION = "object_creation_expression";
|
|
136
|
+
const BASE_LIST = "base_list";
|
|
137
|
+
const IDENTIFIER = "identifier";
|
|
138
|
+
|
|
139
|
+
const NESTED_SCOPE = new Set([
|
|
140
|
+
"method_declaration",
|
|
141
|
+
"constructor_declaration",
|
|
142
|
+
"local_function_statement",
|
|
143
|
+
"class_declaration",
|
|
144
|
+
"struct_declaration",
|
|
145
|
+
"interface_declaration",
|
|
146
|
+
"record_declaration",
|
|
147
|
+
]);
|
|
148
|
+
|
|
149
|
+
function extractCalls(body: Node | null): CallSite[] {
|
|
150
|
+
if (body === null) return [];
|
|
151
|
+
const out: CallSite[] = [];
|
|
152
|
+
walkBodyNodes(
|
|
153
|
+
body,
|
|
154
|
+
(n) => NESTED_SCOPE.has(n.type),
|
|
155
|
+
(node) => {
|
|
156
|
+
if (node.type === INVOCATION) {
|
|
157
|
+
const fn = field(node, "function");
|
|
158
|
+
if (fn === null) return;
|
|
159
|
+
let leaf = fn;
|
|
160
|
+
let receiver = "";
|
|
161
|
+
if (fn.type === "member_access_expression") {
|
|
162
|
+
const name = field(fn, "name");
|
|
163
|
+
const expr = field(fn, "expression");
|
|
164
|
+
if (name !== null) leaf = name;
|
|
165
|
+
receiver = expr === null ? "" : expr.text;
|
|
166
|
+
}
|
|
167
|
+
const site = callSite(leaf.text, startLine(leaf), leaf.startIndex, leaf.endIndex, "call", receiver);
|
|
168
|
+
if (site !== undefined) out.push(site);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (node.type === OBJECT_CREATION || node.type === "implicit_object_creation_expression") {
|
|
172
|
+
const typeNode = field(node, "type");
|
|
173
|
+
if (typeNode === null) return;
|
|
174
|
+
const ids = typeNode.descendantsOfType(IDENTIFIER);
|
|
175
|
+
const leaf = ids[ids.length - 1];
|
|
176
|
+
if (leaf === undefined) return;
|
|
177
|
+
const site = callSite(leaf.text, startLine(leaf), leaf.startIndex, leaf.endIndex, "construct");
|
|
178
|
+
if (site !== undefined) out.push(site);
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
);
|
|
182
|
+
return out;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Base type simple name — drop namespace qualifiers and type arguments. */
|
|
186
|
+
function typeSimpleName(node: Node): string {
|
|
187
|
+
if (node.type === IDENTIFIER) return node.text;
|
|
188
|
+
if (node.type === "generic_name") {
|
|
189
|
+
const id = node.namedChildren.find((c) => c.type === IDENTIFIER);
|
|
190
|
+
return id === undefined ? "" : id.text;
|
|
191
|
+
}
|
|
192
|
+
if (node.type === "qualified_name") {
|
|
193
|
+
const last = node.namedChildren[node.namedChildren.length - 1];
|
|
194
|
+
return last === undefined ? "" : typeSimpleName(last);
|
|
195
|
+
}
|
|
196
|
+
for (const child of node.namedChildren) {
|
|
197
|
+
const inner = typeSimpleName(child);
|
|
198
|
+
if (inner.length > 0) return inner;
|
|
199
|
+
}
|
|
200
|
+
return "";
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function heritageBases(node: Node): string[] {
|
|
204
|
+
const bases: string[] = [];
|
|
205
|
+
for (const child of node.namedChildren) {
|
|
206
|
+
if (child.type !== BASE_LIST) continue;
|
|
207
|
+
for (const entry of child.namedChildren) {
|
|
208
|
+
const name = typeSimpleName(entry);
|
|
209
|
+
if (name.length > 0) bases.push(name);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return [...new Set(bases)];
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function csDoc(node: Node, source: string): DocSpan | undefined {
|
|
216
|
+
return docSpanBefore(node, source);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function modifiersOf(node: Node): string[] {
|
|
220
|
+
const out: string[] = [];
|
|
221
|
+
for (const child of node.namedChildren) {
|
|
222
|
+
if (child.type === MODIFIER) out.push(child.text);
|
|
223
|
+
}
|
|
224
|
+
return out;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function visibilityFromModifiers(mods: readonly string[], defaultVisibility: Visibility): Visibility {
|
|
228
|
+
if (mods.includes("public")) return "public";
|
|
229
|
+
if (mods.includes("private")) return "private";
|
|
230
|
+
if (mods.includes("protected")) return "protected";
|
|
231
|
+
if (mods.includes("internal")) return "internal";
|
|
232
|
+
return defaultVisibility;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function typeVisibility(node: Node): Visibility {
|
|
236
|
+
return visibilityFromModifiers(modifiersOf(node), "internal");
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function nameOf(node: Node): string {
|
|
240
|
+
return nameText(field(node, "name"));
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function bodyOf(node: Node): Node | null {
|
|
244
|
+
return field(node, "body") ?? field(node, "accessors") ?? field(node, "value");
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function leaf(
|
|
248
|
+
node: Node,
|
|
249
|
+
owner: string,
|
|
250
|
+
kind: Decl["kind"],
|
|
251
|
+
name: string,
|
|
252
|
+
visibility: Visibility,
|
|
253
|
+
body: Node | null,
|
|
254
|
+
children: Decl[],
|
|
255
|
+
doc: DocSpan | undefined,
|
|
256
|
+
): Decl | undefined {
|
|
257
|
+
if (name.length === 0) return undefined;
|
|
258
|
+
const callable = kind === "function" || kind === "method" || kind === "constructor";
|
|
259
|
+
const typeLike = kind === "class" || kind === "interface" || kind === "enum" || kind === "struct";
|
|
260
|
+
const decl = finishDecl(
|
|
261
|
+
{
|
|
262
|
+
kind,
|
|
263
|
+
name,
|
|
264
|
+
qualifiedName: qualify(owner, name),
|
|
265
|
+
startLine: startLine(node),
|
|
266
|
+
endLine: endLine(node),
|
|
267
|
+
startOffset: node.startIndex,
|
|
268
|
+
endOffset: node.endIndex,
|
|
269
|
+
visibility,
|
|
270
|
+
exported: visibility === "public",
|
|
271
|
+
children,
|
|
272
|
+
calls: callable ? extractCalls(body) : [],
|
|
273
|
+
bases: typeLike ? heritageBases(node) : [],
|
|
274
|
+
},
|
|
275
|
+
body,
|
|
276
|
+
);
|
|
277
|
+
if (body === null) decl.signatureEndOffset = node.endIndex;
|
|
278
|
+
return applyDoc(decl, doc);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function variableNames(declaration: Node | null): string[] {
|
|
282
|
+
if (declaration === null || declaration.type !== VARIABLE_DECLARATION) return [];
|
|
283
|
+
const names: string[] = [];
|
|
284
|
+
for (const child of declaration.namedChildren) {
|
|
285
|
+
if (child.type !== VARIABLE_DECLARATOR) continue;
|
|
286
|
+
const name = nameText(field(child, "name"));
|
|
287
|
+
if (name.length > 0) names.push(name);
|
|
288
|
+
}
|
|
289
|
+
return names;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function enumMembers(list: Node, owner: string): Decl[] {
|
|
293
|
+
const out: Decl[] = [];
|
|
294
|
+
for (const node of list.namedChildren) {
|
|
295
|
+
if (node.type !== ENUM_MEMBER_DECLARATION) continue;
|
|
296
|
+
const decl = leaf(node, owner, "enumMember", nameOf(node), "public", null, [], undefined);
|
|
297
|
+
if (decl !== undefined) out.push(decl);
|
|
298
|
+
}
|
|
299
|
+
return out;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function membersFromList(list: Node, owner: string, source: string, defaultMemberVisibility: Visibility): Decl[] {
|
|
303
|
+
const out: Decl[] = [];
|
|
304
|
+
for (const node of list.namedChildren) {
|
|
305
|
+
if (node.type === COMMENT) continue;
|
|
306
|
+
out.push(...declsFromNode(node, owner, source, defaultMemberVisibility));
|
|
307
|
+
}
|
|
308
|
+
return out;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function declsFromNode(node: Node, owner: string, source: string, defaultMemberVisibility: Visibility): Decl[] {
|
|
312
|
+
if (node.isError || node.type === "ERROR") {
|
|
313
|
+
const out: Decl[] = [];
|
|
314
|
+
for (const child of node.children) {
|
|
315
|
+
if (child.isNamed || child.isError) {
|
|
316
|
+
out.push(...declsFromNode(child, owner, source, defaultMemberVisibility));
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return out;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const doc = csDoc(node, source);
|
|
323
|
+
const memberVis = visibilityFromModifiers(modifiersOf(node), defaultMemberVisibility);
|
|
324
|
+
|
|
325
|
+
switch (node.type) {
|
|
326
|
+
case NAMESPACE_DECLARATION: {
|
|
327
|
+
const name = nameOf(node);
|
|
328
|
+
const body = field(node, "body");
|
|
329
|
+
const qn = qualify(owner, name);
|
|
330
|
+
const children =
|
|
331
|
+
body !== null && body.type === DECLARATION_LIST ? membersFromList(body, qn, source, "private") : [];
|
|
332
|
+
const decl = leaf(node, owner, "namespace", name, "public", body, children, doc);
|
|
333
|
+
return decl === undefined ? [] : [decl];
|
|
334
|
+
}
|
|
335
|
+
case FILE_SCOPED_NAMESPACE_DECLARATION: {
|
|
336
|
+
// Body is siblings of the file-scoped node; handled by extract walk.
|
|
337
|
+
const name = nameOf(node);
|
|
338
|
+
const decl = leaf(node, owner, "namespace", name, "public", null, [], doc);
|
|
339
|
+
return decl === undefined ? [] : [decl];
|
|
340
|
+
}
|
|
341
|
+
case CLASS_DECLARATION:
|
|
342
|
+
case RECORD_DECLARATION: {
|
|
343
|
+
// record class → class; record struct → struct.
|
|
344
|
+
const name = nameOf(node);
|
|
345
|
+
const body = field(node, "body");
|
|
346
|
+
const qn = qualify(owner, name);
|
|
347
|
+
const children =
|
|
348
|
+
body !== null && body.type === DECLARATION_LIST ? membersFromList(body, qn, source, "private") : [];
|
|
349
|
+
const isRecordStruct =
|
|
350
|
+
node.type === RECORD_DECLARATION && node.children.some((child) => child.type === "struct");
|
|
351
|
+
const kind = isRecordStruct ? "struct" : "class";
|
|
352
|
+
const decl = leaf(node, owner, kind, name, typeVisibility(node), body, children, doc);
|
|
353
|
+
return decl === undefined ? [] : [decl];
|
|
354
|
+
}
|
|
355
|
+
case STRUCT_DECLARATION: {
|
|
356
|
+
const name = nameOf(node);
|
|
357
|
+
const body = field(node, "body");
|
|
358
|
+
const qn = qualify(owner, name);
|
|
359
|
+
const children =
|
|
360
|
+
body !== null && body.type === DECLARATION_LIST ? membersFromList(body, qn, source, "private") : [];
|
|
361
|
+
const decl = leaf(node, owner, "struct", name, typeVisibility(node), body, children, doc);
|
|
362
|
+
return decl === undefined ? [] : [decl];
|
|
363
|
+
}
|
|
364
|
+
case INTERFACE_DECLARATION: {
|
|
365
|
+
const name = nameOf(node);
|
|
366
|
+
const body = field(node, "body");
|
|
367
|
+
const qn = qualify(owner, name);
|
|
368
|
+
// Interface members default public in C#.
|
|
369
|
+
const children =
|
|
370
|
+
body !== null && body.type === DECLARATION_LIST ? membersFromList(body, qn, source, "public") : [];
|
|
371
|
+
const decl = leaf(node, owner, "interface", name, typeVisibility(node), body, children, doc);
|
|
372
|
+
return decl === undefined ? [] : [decl];
|
|
373
|
+
}
|
|
374
|
+
case ENUM_DECLARATION: {
|
|
375
|
+
const name = nameOf(node);
|
|
376
|
+
const body = field(node, "body");
|
|
377
|
+
const qn = qualify(owner, name);
|
|
378
|
+
const children = body !== null && body.type === ENUM_MEMBER_DECLARATION_LIST ? enumMembers(body, qn) : [];
|
|
379
|
+
const decl = leaf(node, owner, "enum", name, typeVisibility(node), body, children, doc);
|
|
380
|
+
return decl === undefined ? [] : [decl];
|
|
381
|
+
}
|
|
382
|
+
case METHOD_DECLARATION: {
|
|
383
|
+
const decl = leaf(node, owner, "method", nameOf(node), memberVis, bodyOf(node), [], doc);
|
|
384
|
+
return decl === undefined ? [] : [decl];
|
|
385
|
+
}
|
|
386
|
+
case CONSTRUCTOR_DECLARATION: {
|
|
387
|
+
const decl = leaf(node, owner, "constructor", nameOf(node), memberVis, bodyOf(node), [], doc);
|
|
388
|
+
return decl === undefined ? [] : [decl];
|
|
389
|
+
}
|
|
390
|
+
case PROPERTY_DECLARATION:
|
|
391
|
+
case INDEXER_DECLARATION: {
|
|
392
|
+
const name = node.type === INDEXER_DECLARATION ? "this" : nameOf(node);
|
|
393
|
+
const decl = leaf(node, owner, "property", name, memberVis, bodyOf(node), [], doc);
|
|
394
|
+
return decl === undefined ? [] : [decl];
|
|
395
|
+
}
|
|
396
|
+
case OPERATOR_DECLARATION: {
|
|
397
|
+
const op = field(node, "operator");
|
|
398
|
+
const name = op === null ? "operator" : `operator ${op.text}`;
|
|
399
|
+
const decl = leaf(node, owner, "operator", name, memberVis, bodyOf(node), [], doc);
|
|
400
|
+
return decl === undefined ? [] : [decl];
|
|
401
|
+
}
|
|
402
|
+
case FIELD_DECLARATION:
|
|
403
|
+
case EVENT_FIELD_DECLARATION: {
|
|
404
|
+
const kind = node.type === EVENT_FIELD_DECLARATION ? "event" : "field";
|
|
405
|
+
const names = variableNames(node.namedChildren.find((c) => c.type === VARIABLE_DECLARATION) ?? null);
|
|
406
|
+
const out: Decl[] = [];
|
|
407
|
+
for (let i = 0; i < names.length; i += 1) {
|
|
408
|
+
const name = names[i];
|
|
409
|
+
if (name === undefined) continue;
|
|
410
|
+
const decl = leaf(node, owner, kind, name, memberVis, null, [], i === 0 ? doc : undefined);
|
|
411
|
+
if (decl !== undefined) out.push(decl);
|
|
412
|
+
}
|
|
413
|
+
return out;
|
|
414
|
+
}
|
|
415
|
+
case EVENT_DECLARATION: {
|
|
416
|
+
const decl = leaf(node, owner, "event", nameOf(node), memberVis, bodyOf(node), [], doc);
|
|
417
|
+
return decl === undefined ? [] : [decl];
|
|
418
|
+
}
|
|
419
|
+
case DELEGATE_DECLARATION: {
|
|
420
|
+
// Delegate type → function (closest shared kind).
|
|
421
|
+
const decl = leaf(node, owner, "function", nameOf(node), typeVisibility(node), null, [], doc);
|
|
422
|
+
return decl === undefined ? [] : [decl];
|
|
423
|
+
}
|
|
424
|
+
default:
|
|
425
|
+
return [];
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function collectImports(root: Node): ImportRef[] {
|
|
430
|
+
const imports: ImportRef[] = [];
|
|
431
|
+
for (const node of root.namedChildren) {
|
|
432
|
+
if (node.type !== USING_DIRECTIVE) continue;
|
|
433
|
+
// using X; / using static X; — take full directive text minus keyword noise via child names.
|
|
434
|
+
const nameNode =
|
|
435
|
+
node.namedChildren.find((child) => child.type === "identifier" || child.type === "qualified_name") ?? null;
|
|
436
|
+
const specifier = nameNode === null ? node.text.replace(/^using\s+/, "").replace(/;$/, "") : nameNode.text;
|
|
437
|
+
imports.push({
|
|
438
|
+
specifier,
|
|
439
|
+
startLine: startLine(node),
|
|
440
|
+
startOffset: node.startIndex,
|
|
441
|
+
endOffset: node.endIndex,
|
|
442
|
+
bindings: [],
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
return imports;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
function extractCsharp(tree: Tree, source: string): ExtractResult {
|
|
449
|
+
const root = tree.rootNode;
|
|
450
|
+
const decls: Decl[] = [];
|
|
451
|
+
// File-scoped namespace owns following type siblings for qualifiedName nesting.
|
|
452
|
+
let fileScopedOwner = "";
|
|
453
|
+
let fileScopedNamespace: Decl | undefined;
|
|
454
|
+
|
|
455
|
+
for (const node of root.children) {
|
|
456
|
+
if (!(node.isNamed || node.isError)) continue;
|
|
457
|
+
|
|
458
|
+
if (node.type === FILE_SCOPED_NAMESPACE_DECLARATION) {
|
|
459
|
+
const extracted = declsFromNode(node, "", source, "private");
|
|
460
|
+
const ns = extracted[0];
|
|
461
|
+
if (ns !== undefined) {
|
|
462
|
+
fileScopedNamespace = ns;
|
|
463
|
+
fileScopedOwner = ns.qualifiedName;
|
|
464
|
+
decls.push(ns);
|
|
465
|
+
}
|
|
466
|
+
continue;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
if (node.type === USING_DIRECTIVE || node.type === COMMENT) continue;
|
|
470
|
+
|
|
471
|
+
const owner = fileScopedOwner;
|
|
472
|
+
const extracted = declsFromNode(node, owner, source, "private");
|
|
473
|
+
if (fileScopedNamespace !== undefined && owner.length > 0) {
|
|
474
|
+
fileScopedNamespace.children.push(...extracted);
|
|
475
|
+
} else {
|
|
476
|
+
decls.push(...extracted);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
return { decls, imports: collectImports(root) };
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
export function csharpAdapter(): GrammarAdapter {
|
|
484
|
+
return {
|
|
485
|
+
mode: "grammar",
|
|
486
|
+
id: "c_sharp",
|
|
487
|
+
extensions: EXTENSIONS,
|
|
488
|
+
capabilities: CAPABILITIES,
|
|
489
|
+
importNoiseIdentifiers: IMPORT_NOISE,
|
|
490
|
+
extract: extractCsharp,
|
|
491
|
+
resolveFileDep: resolveCsharpFileDep,
|
|
492
|
+
};
|
|
493
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
3
|
+
import type { FileDepHost, FileDepResolution } from "../adapter.ts";
|
|
4
|
+
|
|
5
|
+
export function isWithin(parent: string, child: string): boolean {
|
|
6
|
+
const rel = relative(resolve(parent), resolve(child));
|
|
7
|
+
return rel === "" || (!rel.startsWith(`..${sep}`) && rel !== ".." && !isAbsolute(rel));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function internalPaths(paths: readonly string[]): FileDepResolution {
|
|
11
|
+
const unique = [...new Set(paths.map((path) => resolve(path)))].sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
|
|
12
|
+
if (unique.length === 0) return { kind: "unresolved" };
|
|
13
|
+
return { kind: "internal", paths: unique };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function externalId(id: string): FileDepResolution {
|
|
17
|
+
return { kind: "external", id };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Prefer a short internal path list. Large fan-out becomes an external id so
|
|
22
|
+
* namespace/package imports stay agent-usable instead of dumping whole trees.
|
|
23
|
+
*/
|
|
24
|
+
export function boundedInternalPaths(
|
|
25
|
+
paths: readonly string[],
|
|
26
|
+
externalFallback: string,
|
|
27
|
+
maxFiles = 12,
|
|
28
|
+
): FileDepResolution {
|
|
29
|
+
const unique = [...new Set(paths.map((path) => resolve(path)))].sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
|
|
30
|
+
if (unique.length === 0) return { kind: "unresolved" };
|
|
31
|
+
if (unique.length > maxFiles) return externalId(externalFallback);
|
|
32
|
+
return { kind: "internal", paths: unique };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function firstExistingFile(host: FileDepHost, candidates: readonly string[]): Promise<string | undefined> {
|
|
36
|
+
for (const candidate of candidates) {
|
|
37
|
+
if (await host.isFile(candidate)) return resolve(candidate);
|
|
38
|
+
}
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Walk ancestors from startDir up to and including stopDir. */
|
|
43
|
+
async function* walkAncestors(startDir: string, stopDir: string): AsyncGenerator<string> {
|
|
44
|
+
let dir = resolve(startDir);
|
|
45
|
+
const stop = resolve(stopDir);
|
|
46
|
+
for (;;) {
|
|
47
|
+
yield dir;
|
|
48
|
+
if (dir === stop) return;
|
|
49
|
+
const parent = dirname(dir);
|
|
50
|
+
if (parent === dir) return;
|
|
51
|
+
if (!isWithin(stop, parent) && parent !== stop) return;
|
|
52
|
+
dir = parent;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function findAncestorFile(
|
|
57
|
+
host: FileDepHost,
|
|
58
|
+
startDir: string,
|
|
59
|
+
stopDir: string,
|
|
60
|
+
fileName: string,
|
|
61
|
+
): Promise<string | undefined> {
|
|
62
|
+
for await (const dir of walkAncestors(startDir, stopDir)) {
|
|
63
|
+
const candidate = join(dir, fileName);
|
|
64
|
+
if (await host.isFile(candidate)) return candidate;
|
|
65
|
+
}
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export async function readTextFile(path: string): Promise<string | undefined> {
|
|
70
|
+
try {
|
|
71
|
+
return await readFile(path, "utf8");
|
|
72
|
+
} catch {
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Recursive file gather under root with extension filter; stays in scope via host.scopeRoot. */
|
|
78
|
+
export async function collectFilesWithExtensions(
|
|
79
|
+
host: FileDepHost,
|
|
80
|
+
root: string,
|
|
81
|
+
extensions: readonly string[],
|
|
82
|
+
signal: AbortSignal,
|
|
83
|
+
maxFiles = 5000,
|
|
84
|
+
): Promise<string[]> {
|
|
85
|
+
const extSet = new Set(extensions.map((ext) => ext.toLowerCase()));
|
|
86
|
+
const out: string[] = [];
|
|
87
|
+
const stack = [resolve(root)];
|
|
88
|
+
while (stack.length > 0) {
|
|
89
|
+
signal.throwIfAborted();
|
|
90
|
+
if (out.length >= maxFiles) break;
|
|
91
|
+
const dir = stack.pop();
|
|
92
|
+
if (dir === undefined) break;
|
|
93
|
+
if (!isWithin(host.scopeRoot, dir) && dir !== resolve(host.scopeRoot)) continue;
|
|
94
|
+
const names = await host.readDir(dir);
|
|
95
|
+
for (const name of names) {
|
|
96
|
+
if (name === "." || name === ".." || name === "node_modules" || name === "target" || name === "dist") {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (name.startsWith(".")) continue;
|
|
100
|
+
const full = join(dir, name);
|
|
101
|
+
if (await host.isFile(full)) {
|
|
102
|
+
const dot = name.lastIndexOf(".");
|
|
103
|
+
const ext = dot >= 0 ? name.slice(dot).toLowerCase() : "";
|
|
104
|
+
if (extSet.has(ext) && host.ownsPath(full)) out.push(full);
|
|
105
|
+
} else if (await host.pathExists(full)) {
|
|
106
|
+
stack.push(full);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return out;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function packageNameFromBareSpecifier(specifier: string): string {
|
|
114
|
+
if (specifier.startsWith("@")) {
|
|
115
|
+
const parts = specifier.split("/");
|
|
116
|
+
return parts.length >= 2 ? `${parts[0]}/${parts[1]}` : specifier;
|
|
117
|
+
}
|
|
118
|
+
const slash = specifier.indexOf("/");
|
|
119
|
+
return slash === -1 ? specifier : specifier.slice(0, slash);
|
|
120
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
|
|
3
|
+
namespace Demo;
|
|
4
|
+
|
|
5
|
+
/// <summary>Greeter</summary>
|
|
6
|
+
public class Greeter {
|
|
7
|
+
private int count;
|
|
8
|
+
public string Name { get; set; }
|
|
9
|
+
public Greeter() {}
|
|
10
|
+
public void Hello() {}
|
|
11
|
+
public event EventHandler Done;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
internal struct Box {}
|
|
15
|
+
public interface IGreeter { void Hello(); }
|
|
16
|
+
public enum Color { Red, Green }
|
|
17
|
+
public record Person(string Name);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
package com.demo;
|
|
2
|
+
|
|
3
|
+
import java.util.List;
|
|
4
|
+
|
|
5
|
+
/** Greeter type. */
|
|
6
|
+
public class Greeter {
|
|
7
|
+
private int count;
|
|
8
|
+
public Greeter() {}
|
|
9
|
+
public void hello() {}
|
|
10
|
+
public static final int MAX = 10;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface Named {
|
|
14
|
+
void name();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
enum Color {
|
|
18
|
+
RED,
|
|
19
|
+
GREEN;
|
|
20
|
+
void paint() {}
|
|
21
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
package demo
|
|
2
|
+
|
|
3
|
+
import "core:fmt"
|
|
4
|
+
|
|
5
|
+
// Add two ints
|
|
6
|
+
add :: proc(a: int, b: int) -> int {
|
|
7
|
+
return a + b
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
Point :: struct {
|
|
11
|
+
x: int,
|
|
12
|
+
y: int,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
Color :: enum {
|
|
16
|
+
Red,
|
|
17
|
+
Green,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
Value :: union {
|
|
21
|
+
int,
|
|
22
|
+
f32,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
PI :: 3.14
|
|
26
|
+
|
|
27
|
+
@(private)
|
|
28
|
+
secret :: proc() {}
|