@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,59 @@
|
|
|
1
|
+
import { basename, join, resolve } from "node:path";
|
|
2
|
+
import type { FileDepResolver } from "../adapter.ts";
|
|
3
|
+
import { collectFilesWithExtensions, externalId, internalPaths, isWithin } from "./file-dep-util.ts";
|
|
4
|
+
|
|
5
|
+
async function findModuleDirs(
|
|
6
|
+
host: Parameters<FileDepResolver>[2],
|
|
7
|
+
moduleName: string,
|
|
8
|
+
signal: AbortSignal,
|
|
9
|
+
): Promise<string[]> {
|
|
10
|
+
const key = `swift-mod:${host.scopeRoot}:${moduleName}`;
|
|
11
|
+
const cached = host.memo.get(key);
|
|
12
|
+
if (cached !== undefined) return cached as string[];
|
|
13
|
+
|
|
14
|
+
const hits: string[] = [];
|
|
15
|
+
const stack = [resolve(host.scopeRoot)];
|
|
16
|
+
while (stack.length > 0) {
|
|
17
|
+
signal.throwIfAborted();
|
|
18
|
+
const dir = stack.pop();
|
|
19
|
+
if (dir === undefined) break;
|
|
20
|
+
const names = await host.readDir(dir);
|
|
21
|
+
for (const name of names) {
|
|
22
|
+
if (name === "." || name === ".." || name.startsWith(".") || name === "node_modules") continue;
|
|
23
|
+
const full = join(dir, name);
|
|
24
|
+
const isDir = !(await host.isFile(full)) && (await host.pathExists(full));
|
|
25
|
+
if (!isDir) continue;
|
|
26
|
+
// Sources/Module or directory named Module with swift files
|
|
27
|
+
if (name === moduleName) hits.push(full);
|
|
28
|
+
if ((name === "Sources" || name === "Source") && (await host.pathExists(join(full, moduleName)))) {
|
|
29
|
+
hits.push(join(full, moduleName));
|
|
30
|
+
}
|
|
31
|
+
// Bound depth a bit via path length under scope
|
|
32
|
+
if (full.split("/").length - host.scopeRoot.split("/").length < 8) stack.push(full);
|
|
33
|
+
}
|
|
34
|
+
if (hits.length >= 8) break;
|
|
35
|
+
}
|
|
36
|
+
host.memo.set(key, hits);
|
|
37
|
+
return hits;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const resolveSwiftFileDep: FileDepResolver = async (_fromPath, specifier, host, signal) => {
|
|
41
|
+
signal.throwIfAborted();
|
|
42
|
+
const moduleName = specifier.trim().split(/\s+/u)[0] ?? "";
|
|
43
|
+
if (moduleName.length === 0) return { kind: "unresolved" };
|
|
44
|
+
|
|
45
|
+
// Skip obvious system modules when no local dir exists — still try local first.
|
|
46
|
+
const dirs = await findModuleDirs(host, moduleName, signal);
|
|
47
|
+
const files: string[] = [];
|
|
48
|
+
for (const dir of dirs) {
|
|
49
|
+
if (!isWithin(host.scopeRoot, dir)) continue;
|
|
50
|
+
const found = await collectFilesWithExtensions(host, dir, [".swift"], signal, 500);
|
|
51
|
+
for (const file of found) {
|
|
52
|
+
// Don't treat the module root listing as importing itself only — include all.
|
|
53
|
+
if (basename(file) === moduleName) continue;
|
|
54
|
+
files.push(file);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (files.length === 0) return externalId(moduleName);
|
|
58
|
+
return internalPaths(files);
|
|
59
|
+
};
|
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
import type { Node, Tree } from "web-tree-sitter";
|
|
2
|
+
import type { ExtractResult, GrammarAdapter, LanguageCapabilities } from "../adapter.ts";
|
|
3
|
+
import { resolveSwiftFileDep } from "./swift-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
|
+
"associatedtype",
|
|
29
|
+
"class",
|
|
30
|
+
"deinit",
|
|
31
|
+
"enum",
|
|
32
|
+
"extension",
|
|
33
|
+
"fileprivate",
|
|
34
|
+
"func",
|
|
35
|
+
"import",
|
|
36
|
+
"init",
|
|
37
|
+
"inout",
|
|
38
|
+
"internal",
|
|
39
|
+
"let",
|
|
40
|
+
"open",
|
|
41
|
+
"operator",
|
|
42
|
+
"private",
|
|
43
|
+
"protocol",
|
|
44
|
+
"public",
|
|
45
|
+
"rethrows",
|
|
46
|
+
"static",
|
|
47
|
+
"struct",
|
|
48
|
+
"subscript",
|
|
49
|
+
"typealias",
|
|
50
|
+
"var",
|
|
51
|
+
"break",
|
|
52
|
+
"case",
|
|
53
|
+
"continue",
|
|
54
|
+
"default",
|
|
55
|
+
"defer",
|
|
56
|
+
"do",
|
|
57
|
+
"else",
|
|
58
|
+
"fallthrough",
|
|
59
|
+
"for",
|
|
60
|
+
"guard",
|
|
61
|
+
"if",
|
|
62
|
+
"in",
|
|
63
|
+
"repeat",
|
|
64
|
+
"return",
|
|
65
|
+
"switch",
|
|
66
|
+
"where",
|
|
67
|
+
"while",
|
|
68
|
+
"as",
|
|
69
|
+
"Any",
|
|
70
|
+
"catch",
|
|
71
|
+
"false",
|
|
72
|
+
"is",
|
|
73
|
+
"nil",
|
|
74
|
+
"super",
|
|
75
|
+
"self",
|
|
76
|
+
"Self",
|
|
77
|
+
"throw",
|
|
78
|
+
"throws",
|
|
79
|
+
"true",
|
|
80
|
+
"try",
|
|
81
|
+
]);
|
|
82
|
+
|
|
83
|
+
const EXTENSIONS = [".swift"] as const;
|
|
84
|
+
|
|
85
|
+
const CLASS_DECLARATION = "class_declaration";
|
|
86
|
+
const PROTOCOL_DECLARATION = "protocol_declaration";
|
|
87
|
+
const FUNCTION_DECLARATION = "function_declaration";
|
|
88
|
+
const PROPERTY_DECLARATION = "property_declaration";
|
|
89
|
+
const INIT_DECLARATION = "init_declaration";
|
|
90
|
+
const DEINIT_DECLARATION = "deinit_declaration";
|
|
91
|
+
const SUBSCRIPT_DECLARATION = "subscript_declaration";
|
|
92
|
+
const TYPEALIAS_DECLARATION = "typealias_declaration";
|
|
93
|
+
const PROTOCOL_FUNCTION_DECLARATION = "protocol_function_declaration";
|
|
94
|
+
const PROTOCOL_PROPERTY_DECLARATION = "protocol_property_declaration";
|
|
95
|
+
const ASSOCIATEDTYPE_DECLARATION = "associatedtype_declaration";
|
|
96
|
+
const ENUM_ENTRY = "enum_entry";
|
|
97
|
+
const CLASS_BODY = "class_body";
|
|
98
|
+
const ENUM_CLASS_BODY = "enum_class_body";
|
|
99
|
+
const PROTOCOL_BODY = "protocol_body";
|
|
100
|
+
const IMPORT_DECLARATION = "import_declaration";
|
|
101
|
+
const MODIFIERS = "modifiers";
|
|
102
|
+
const VISIBILITY_MODIFIER = "visibility_modifier";
|
|
103
|
+
const TYPE_IDENTIFIER = "type_identifier";
|
|
104
|
+
const USER_TYPE = "user_type";
|
|
105
|
+
const SIMPLE_IDENTIFIER = "simple_identifier";
|
|
106
|
+
const COMMENT = "comment";
|
|
107
|
+
|
|
108
|
+
const NESTED_SCOPE = new Set([
|
|
109
|
+
"function_declaration",
|
|
110
|
+
"class_declaration",
|
|
111
|
+
"struct_declaration",
|
|
112
|
+
"enum_declaration",
|
|
113
|
+
"protocol_declaration",
|
|
114
|
+
]);
|
|
115
|
+
|
|
116
|
+
function extractCalls(body: Node | null): CallSite[] {
|
|
117
|
+
if (body === null) return [];
|
|
118
|
+
const out: CallSite[] = [];
|
|
119
|
+
walkBodyNodes(
|
|
120
|
+
body,
|
|
121
|
+
(n) => NESTED_SCOPE.has(n.type),
|
|
122
|
+
(node) => {
|
|
123
|
+
if (node.type === "call_expression") {
|
|
124
|
+
const fn = node.namedChildren[0];
|
|
125
|
+
if (fn === undefined) return;
|
|
126
|
+
let leafNode = fn;
|
|
127
|
+
let receiver = "";
|
|
128
|
+
if (fn.type === "navigation_expression") {
|
|
129
|
+
const ids = fn.descendantsOfType("simple_identifier");
|
|
130
|
+
const last = ids[ids.length - 1];
|
|
131
|
+
if (last !== undefined) leafNode = last;
|
|
132
|
+
receiver = fn.text.slice(0, Math.max(0, fn.text.length - leafNode.text.length - 1));
|
|
133
|
+
}
|
|
134
|
+
const site = callSite(
|
|
135
|
+
leafNode.text,
|
|
136
|
+
startLine(leafNode),
|
|
137
|
+
leafNode.startIndex,
|
|
138
|
+
leafNode.endIndex,
|
|
139
|
+
"call",
|
|
140
|
+
receiver,
|
|
141
|
+
);
|
|
142
|
+
if (site !== undefined) out.push(site);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (node.type === "constructor_expression") {
|
|
146
|
+
const ids = node.descendantsOfType("type_identifier");
|
|
147
|
+
const leafNode = ids[0];
|
|
148
|
+
if (leafNode === undefined) return;
|
|
149
|
+
const site = callSite(
|
|
150
|
+
leafNode.text,
|
|
151
|
+
startLine(leafNode),
|
|
152
|
+
leafNode.startIndex,
|
|
153
|
+
leafNode.endIndex,
|
|
154
|
+
"construct",
|
|
155
|
+
);
|
|
156
|
+
if (site !== undefined) out.push(site);
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
);
|
|
160
|
+
return out;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function heritageBases(node: Node): string[] {
|
|
164
|
+
const bases: string[] = [];
|
|
165
|
+
for (const child of node.namedChildren) {
|
|
166
|
+
if (child.type === "inheritance_specifier" || child.type === "type_inheritance_clause") {
|
|
167
|
+
for (const t of child.descendantsOfType("type_identifier")) {
|
|
168
|
+
if (t.text.length > 0) bases.push(t.text);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return [...new Set(bases)];
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function swiftDoc(node: Node, source: string): DocSpan | undefined {
|
|
176
|
+
return docSpanBefore(node, source);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function visibilityOf(node: Node): Visibility {
|
|
180
|
+
const mods = node.namedChildren.find((child) => child.type === MODIFIERS);
|
|
181
|
+
if (mods === undefined) return "internal";
|
|
182
|
+
for (const child of mods.namedChildren) {
|
|
183
|
+
if (child.type !== VISIBILITY_MODIFIER) continue;
|
|
184
|
+
const text = child.text;
|
|
185
|
+
if (text === "open" || text === "public") return "public";
|
|
186
|
+
if (text === "internal") return "internal";
|
|
187
|
+
if (text === "fileprivate" || text === "private") return "private";
|
|
188
|
+
}
|
|
189
|
+
return "internal";
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function declarationKind(node: Node): string {
|
|
193
|
+
const kind = field(node, "declaration_kind");
|
|
194
|
+
return kind === null ? "" : kind.text;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function typeName(node: Node): string {
|
|
198
|
+
const named = field(node, "name");
|
|
199
|
+
if (named === null) return "";
|
|
200
|
+
if (named.type === TYPE_IDENTIFIER) return named.text;
|
|
201
|
+
if (named.type === USER_TYPE) {
|
|
202
|
+
// Keep dotted path for `extension Foo.Bar` (not only the first segment).
|
|
203
|
+
return named.text.replace(/\s+/g, "");
|
|
204
|
+
}
|
|
205
|
+
return named.text;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function simpleName(node: Node | null): string {
|
|
209
|
+
if (node === null) return "";
|
|
210
|
+
if (node.type === SIMPLE_IDENTIFIER) return node.text;
|
|
211
|
+
const inner = node.namedChildren.find((child) => child.type === SIMPLE_IDENTIFIER);
|
|
212
|
+
return inner === undefined ? nameText(node) : inner.text;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function propertyName(node: Node): string {
|
|
216
|
+
const pattern = field(node, "name");
|
|
217
|
+
if (pattern === null) {
|
|
218
|
+
// protocol_property_declaration nests pattern differently.
|
|
219
|
+
const nested = node.namedChildren.find((child) => child.type === "pattern");
|
|
220
|
+
return simpleName(nested ?? null);
|
|
221
|
+
}
|
|
222
|
+
if (pattern.type === "pattern") {
|
|
223
|
+
const bound = field(pattern, "bound_identifier");
|
|
224
|
+
if (bound !== null) return simpleName(bound);
|
|
225
|
+
return simpleName(pattern.namedChildren.find((child) => child.type === SIMPLE_IDENTIFIER) ?? null);
|
|
226
|
+
}
|
|
227
|
+
return simpleName(pattern);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function functionName(node: Node): string {
|
|
231
|
+
const name = field(node, "name");
|
|
232
|
+
if (name !== null) return simpleName(name);
|
|
233
|
+
return simpleName(node.namedChildren.find((child) => child.type === SIMPLE_IDENTIFIER) ?? null);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function bodyOf(node: Node): Node | null {
|
|
237
|
+
return (
|
|
238
|
+
field(node, "body") ??
|
|
239
|
+
field(node, "computed_value") ??
|
|
240
|
+
node.namedChildren.find((child) => child.type === "function_body" || child.type === "computed_property") ??
|
|
241
|
+
null
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function leaf(
|
|
246
|
+
node: Node,
|
|
247
|
+
owner: string,
|
|
248
|
+
kind: Decl["kind"],
|
|
249
|
+
name: string,
|
|
250
|
+
visibility: Visibility,
|
|
251
|
+
body: Node | null,
|
|
252
|
+
children: Decl[],
|
|
253
|
+
doc: DocSpan | undefined,
|
|
254
|
+
): Decl | undefined {
|
|
255
|
+
if (name.length === 0) return undefined;
|
|
256
|
+
const callable = kind === "function" || kind === "method" || kind === "constructor";
|
|
257
|
+
const typeLike =
|
|
258
|
+
kind === "class" || kind === "interface" || kind === "enum" || kind === "struct" || kind === "object";
|
|
259
|
+
const decl = finishDecl(
|
|
260
|
+
{
|
|
261
|
+
kind,
|
|
262
|
+
name,
|
|
263
|
+
qualifiedName: qualify(owner, name),
|
|
264
|
+
startLine: startLine(node),
|
|
265
|
+
endLine: endLine(node),
|
|
266
|
+
startOffset: node.startIndex,
|
|
267
|
+
endOffset: node.endIndex,
|
|
268
|
+
visibility,
|
|
269
|
+
exported: visibility === "public",
|
|
270
|
+
children,
|
|
271
|
+
calls: callable ? extractCalls(body) : [],
|
|
272
|
+
bases: typeLike ? heritageBases(node) : [],
|
|
273
|
+
},
|
|
274
|
+
body,
|
|
275
|
+
);
|
|
276
|
+
if (body === null) decl.signatureEndOffset = node.endIndex;
|
|
277
|
+
return applyDoc(decl, doc);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function classKind(kindText: string): Decl["kind"] {
|
|
281
|
+
if (kindText === "struct") return "struct";
|
|
282
|
+
if (kindText === "enum") return "enum";
|
|
283
|
+
if (kindText === "actor") return "class";
|
|
284
|
+
return "class";
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const TYPE_KINDS = new Set<Decl["kind"]>(["class", "struct", "enum", "interface", "object"]);
|
|
288
|
+
|
|
289
|
+
function findTypeDecl(decls: readonly Decl[], typeQn: string): Decl | undefined {
|
|
290
|
+
for (const decl of decls) {
|
|
291
|
+
if (TYPE_KINDS.has(decl.kind) && decl.qualifiedName === typeQn) return decl;
|
|
292
|
+
const nested = findTypeDecl(decl.children, typeQn);
|
|
293
|
+
if (nested !== undefined) return nested;
|
|
294
|
+
}
|
|
295
|
+
return undefined;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Same-file extensions: attach members under the extended type's children.
|
|
300
|
+
* Type not in this file: keep top-level decls with Type.member qualifiedName.
|
|
301
|
+
*/
|
|
302
|
+
function mergeExtensionMembers(decls: Decl[], extensions: readonly { typeQn: string; members: Decl[] }[]): Decl[] {
|
|
303
|
+
const leftover: Decl[] = [];
|
|
304
|
+
for (const ext of extensions) {
|
|
305
|
+
const target = findTypeDecl(decls, ext.typeQn);
|
|
306
|
+
if (target === undefined) {
|
|
307
|
+
leftover.push(...ext.members);
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
target.children.push(...ext.members);
|
|
311
|
+
}
|
|
312
|
+
if (leftover.length === 0) return decls;
|
|
313
|
+
return [...decls, ...leftover];
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function walkBody(body: Node, owner: string, source: string): Decl[] {
|
|
317
|
+
const out: Decl[] = [];
|
|
318
|
+
for (const node of body.namedChildren) {
|
|
319
|
+
if (node.type === COMMENT) continue;
|
|
320
|
+
if (node.type === ENUM_ENTRY) {
|
|
321
|
+
const name = simpleName(
|
|
322
|
+
field(node, "name") ?? node.namedChildren.find((c) => c.type === SIMPLE_IDENTIFIER) ?? null,
|
|
323
|
+
);
|
|
324
|
+
const decl = leaf(node, owner, "enumMember", name, "public", null, [], undefined);
|
|
325
|
+
if (decl !== undefined) out.push(decl);
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
out.push(...declsFromNode(node, owner, source));
|
|
329
|
+
}
|
|
330
|
+
return out;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function declsFromNode(node: Node, owner: string, source: string): Decl[] {
|
|
334
|
+
if (node.isError || node.type === "ERROR") {
|
|
335
|
+
const out: Decl[] = [];
|
|
336
|
+
for (const child of node.children) {
|
|
337
|
+
if (child.isNamed || child.isError) out.push(...declsFromNode(child, owner, source));
|
|
338
|
+
}
|
|
339
|
+
return out;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const doc = swiftDoc(node, source);
|
|
343
|
+
const visibility = visibilityOf(node);
|
|
344
|
+
|
|
345
|
+
switch (node.type) {
|
|
346
|
+
case CLASS_DECLARATION: {
|
|
347
|
+
const kindText = declarationKind(node);
|
|
348
|
+
const name = typeName(node);
|
|
349
|
+
const body =
|
|
350
|
+
field(node, "body") ??
|
|
351
|
+
node.namedChildren.find((child) => child.type === CLASS_BODY || child.type === ENUM_CLASS_BODY) ??
|
|
352
|
+
null;
|
|
353
|
+
|
|
354
|
+
// Extensions are collected by extractSwift and merged onto same-file types.
|
|
355
|
+
if (kindText === "extension") return [];
|
|
356
|
+
|
|
357
|
+
const qn = qualify(owner, name);
|
|
358
|
+
const children = body === null ? [] : walkBody(body, qn, source);
|
|
359
|
+
const decl = leaf(node, owner, classKind(kindText), name, visibility, body, children, doc);
|
|
360
|
+
return decl === undefined ? [] : [decl];
|
|
361
|
+
}
|
|
362
|
+
case PROTOCOL_DECLARATION: {
|
|
363
|
+
const name = typeName(node);
|
|
364
|
+
const body = field(node, "body") ?? node.namedChildren.find((child) => child.type === PROTOCOL_BODY) ?? null;
|
|
365
|
+
const qn = qualify(owner, name);
|
|
366
|
+
const children = body === null ? [] : walkBody(body, qn, source);
|
|
367
|
+
const decl = leaf(node, owner, "interface", name, visibility, body, children, doc);
|
|
368
|
+
return decl === undefined ? [] : [decl];
|
|
369
|
+
}
|
|
370
|
+
case FUNCTION_DECLARATION:
|
|
371
|
+
case PROTOCOL_FUNCTION_DECLARATION: {
|
|
372
|
+
const name = functionName(node);
|
|
373
|
+
const kind = owner.length > 0 ? "method" : "function";
|
|
374
|
+
const decl = leaf(node, owner, kind, name, visibility, bodyOf(node), [], doc);
|
|
375
|
+
return decl === undefined ? [] : [decl];
|
|
376
|
+
}
|
|
377
|
+
case INIT_DECLARATION: {
|
|
378
|
+
const decl = leaf(node, owner, "constructor", "init", visibility, bodyOf(node), [], doc);
|
|
379
|
+
return decl === undefined ? [] : [decl];
|
|
380
|
+
}
|
|
381
|
+
case DEINIT_DECLARATION: {
|
|
382
|
+
const decl = leaf(node, owner, "method", "deinit", visibility, bodyOf(node), [], doc);
|
|
383
|
+
return decl === undefined ? [] : [decl];
|
|
384
|
+
}
|
|
385
|
+
case SUBSCRIPT_DECLARATION: {
|
|
386
|
+
const decl = leaf(node, owner, "method", "subscript", visibility, bodyOf(node), [], doc);
|
|
387
|
+
return decl === undefined ? [] : [decl];
|
|
388
|
+
}
|
|
389
|
+
case PROPERTY_DECLARATION:
|
|
390
|
+
case PROTOCOL_PROPERTY_DECLARATION: {
|
|
391
|
+
const name = propertyName(node);
|
|
392
|
+
const decl = leaf(node, owner, "property", name, visibility, bodyOf(node), [], doc);
|
|
393
|
+
return decl === undefined ? [] : [decl];
|
|
394
|
+
}
|
|
395
|
+
case TYPEALIAS_DECLARATION: {
|
|
396
|
+
const name = typeName(node);
|
|
397
|
+
const decl = leaf(node, owner, "typeAlias", name, visibility, null, [], doc);
|
|
398
|
+
return decl === undefined ? [] : [decl];
|
|
399
|
+
}
|
|
400
|
+
case ASSOCIATEDTYPE_DECLARATION: {
|
|
401
|
+
const name = typeName(node);
|
|
402
|
+
const decl = leaf(node, owner, "typeParameter", name, visibility, null, [], doc);
|
|
403
|
+
return decl === undefined ? [] : [decl];
|
|
404
|
+
}
|
|
405
|
+
default:
|
|
406
|
+
return [];
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function collectImports(root: Node): ImportRef[] {
|
|
411
|
+
const imports: ImportRef[] = [];
|
|
412
|
+
for (const node of root.namedChildren) {
|
|
413
|
+
if (node.type !== IMPORT_DECLARATION) continue;
|
|
414
|
+
const id = node.namedChildren.find((child) => child.type === "identifier" || child.type === SIMPLE_IDENTIFIER);
|
|
415
|
+
const specifier = id === undefined ? node.text.replace(/^import\s+/, "").trim() : id.text.replace(/\s+/g, "");
|
|
416
|
+
imports.push({
|
|
417
|
+
specifier,
|
|
418
|
+
startLine: startLine(node),
|
|
419
|
+
startOffset: node.startIndex,
|
|
420
|
+
endOffset: node.endIndex,
|
|
421
|
+
bindings: [],
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
return imports;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
function extensionMembers(node: Node, owner: string, source: string): { typeQn: string; members: Decl[] } | undefined {
|
|
428
|
+
if (node.type !== CLASS_DECLARATION || declarationKind(node) !== "extension") return undefined;
|
|
429
|
+
const name = typeName(node);
|
|
430
|
+
const typeQn = owner.length === 0 ? name : qualify(owner, name);
|
|
431
|
+
if (typeQn.length === 0) return undefined;
|
|
432
|
+
const body =
|
|
433
|
+
field(node, "body") ??
|
|
434
|
+
node.namedChildren.find((child) => child.type === CLASS_BODY || child.type === ENUM_CLASS_BODY) ??
|
|
435
|
+
null;
|
|
436
|
+
if (body === null) return { typeQn, members: [] };
|
|
437
|
+
return { typeQn, members: walkBody(body, typeQn, source) };
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function extractSwift(tree: Tree, source: string): ExtractResult {
|
|
441
|
+
const root = tree.rootNode;
|
|
442
|
+
const decls: Decl[] = [];
|
|
443
|
+
const extensions: { typeQn: string; members: Decl[] }[] = [];
|
|
444
|
+
for (const node of root.children) {
|
|
445
|
+
if (!(node.isNamed || node.isError)) continue;
|
|
446
|
+
const ext = extensionMembers(node, "", source);
|
|
447
|
+
if (ext !== undefined) {
|
|
448
|
+
extensions.push(ext);
|
|
449
|
+
continue;
|
|
450
|
+
}
|
|
451
|
+
decls.push(...declsFromNode(node, "", source));
|
|
452
|
+
}
|
|
453
|
+
return { decls: mergeExtensionMembers(decls, extensions), imports: collectImports(root) };
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
export function swiftAdapter(): GrammarAdapter {
|
|
457
|
+
return {
|
|
458
|
+
mode: "grammar",
|
|
459
|
+
id: "swift",
|
|
460
|
+
extensions: EXTENSIONS,
|
|
461
|
+
capabilities: CAPABILITIES,
|
|
462
|
+
importNoiseIdentifiers: IMPORT_NOISE,
|
|
463
|
+
extract: extractSwift,
|
|
464
|
+
resolveFileDep: resolveSwiftFileDep,
|
|
465
|
+
};
|
|
466
|
+
}
|