autosnippet 3.4.0 → 3.4.2
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 +43 -18
- package/dashboard/dist/assets/{index-8b1Gf3Bb.js → index-BX6r2fiy.js} +40 -40
- package/dashboard/dist/assets/index-BvZcGN02.css +1 -0
- package/dashboard/dist/index.html +2 -2
- package/dist/lib/core/AstAnalyzer.js +0 -1
- package/dist/lib/core/ast/lang-dart.js +118 -8
- package/dist/lib/core/ast/lang-go.js +0 -1
- package/dist/lib/core/ast/lang-java.js +25 -11
- package/dist/lib/core/ast/lang-javascript.js +103 -17
- package/dist/lib/core/ast/lang-objc.d.ts +1 -1
- package/dist/lib/core/ast/lang-objc.js +80 -4
- package/dist/lib/core/ast/lang-python.js +0 -1
- package/dist/lib/core/ast/lang-rust.js +0 -1
- package/dist/lib/core/ast/lang-swift.d.ts +1 -1
- package/dist/lib/core/ast/lang-swift.js +184 -7
- package/dist/lib/core/ast/lang-typescript.js +0 -1
- package/dist/lib/external/ai/AiFactory.d.ts +14 -0
- package/dist/lib/external/ai/AiFactory.js +33 -1
- package/dist/lib/external/ai/providers/GoogleGeminiProvider.js +7 -3
- package/dist/lib/external/ai/providers/OpenAiProvider.js +1 -1
- package/dist/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.d.ts +33 -1
- package/dist/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.js +392 -19
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.d.ts +1 -0
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +2 -1
- package/dist/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.d.ts +2 -0
- package/dist/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.js +4 -0
- package/dist/lib/external/mcp/handlers/guard.js +11 -6
- package/dist/lib/http/routes/ai.js +18 -1
- package/dist/lib/infrastructure/vector/IndexingPipeline.js +6 -1
- package/dist/lib/injection/modules/AiModule.js +22 -1
- package/dist/lib/service/bootstrap/BootstrapTaskManager.d.ts +7 -0
- package/dist/lib/service/bootstrap/BootstrapTaskManager.js +17 -0
- package/dist/lib/service/guard/ComplianceReporter.js +5 -1
- package/dist/lib/service/guard/GuardCheckEngine.d.ts +12 -1
- package/dist/lib/service/guard/GuardCheckEngine.js +36 -4
- package/dist/lib/service/guard/GuardCodeChecks.js +27 -9
- package/dist/lib/service/guard/SourceFileCollector.d.ts +3 -2
- package/dist/lib/service/guard/SourceFileCollector.js +3 -3
- package/dist/lib/service/search/SearchEngine.js +165 -61
- package/dist/lib/service/task/PrimeSearchPipeline.js +17 -2
- package/dist/lib/service/vector/VectorService.js +10 -1
- package/dist/lib/shared/LanguageService.d.ts +12 -0
- package/dist/lib/shared/LanguageService.js +85 -0
- package/dist/lib/shared/schemas/http-requests.d.ts +4 -0
- package/dist/lib/shared/schemas/http-requests.js +4 -0
- package/dist/lib/types/project-snapshot.d.ts +1 -0
- package/package.json +1 -1
- package/resources/grammars/tree-sitter-dart.wasm +0 -0
- package/dashboard/dist/assets/index-DHJ1Dj7u.css +0 -1
|
@@ -172,10 +172,8 @@ function _parseJavaClass(node) {
|
|
|
172
172
|
}
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
|
-
//
|
|
176
|
-
const annotations = node
|
|
177
|
-
.filter((c) => c.type === 'marker_annotation' || c.type === 'annotation')
|
|
178
|
-
.map((a) => a.text);
|
|
175
|
+
// 提取注解(tree-sitter-java: 注解在 modifiers 子节点内)
|
|
176
|
+
const annotations = _extractAnnotations(node);
|
|
179
177
|
// 修饰符
|
|
180
178
|
const modifiers = node.namedChildren.find((c) => c.type === 'modifiers');
|
|
181
179
|
const isAbstract = modifiers?.text?.includes('abstract') || false;
|
|
@@ -216,9 +214,7 @@ function _parseJavaMethod(node, className) {
|
|
|
216
214
|
const bodyLines = body ? body.endPosition.row - body.startPosition.row + 1 : 0;
|
|
217
215
|
const complexity = body ? _estimateComplexity(body) : 1;
|
|
218
216
|
const nestingDepth = body ? _maxNesting(body, 0) : 0;
|
|
219
|
-
const annotations = node
|
|
220
|
-
.filter((c) => c.type === 'marker_annotation' || c.type === 'annotation')
|
|
221
|
-
.map((a) => a.text);
|
|
217
|
+
const annotations = _extractAnnotations(node);
|
|
222
218
|
return {
|
|
223
219
|
name,
|
|
224
220
|
className,
|
|
@@ -241,9 +237,7 @@ function _parseJavaField(node, className) {
|
|
|
241
237
|
const isStatic = modifiers?.text?.includes('static') || false;
|
|
242
238
|
const isFinal = modifiers?.text?.includes('final') || false;
|
|
243
239
|
const isPrivate = modifiers?.text?.includes('private') || false;
|
|
244
|
-
const annotations = node
|
|
245
|
-
.filter((c) => c.type === 'marker_annotation' || c.type === 'annotation')
|
|
246
|
-
.map((a) => a.text);
|
|
240
|
+
const annotations = _extractAnnotations(node);
|
|
247
241
|
// Phase 5.3: Extract field type for DI resolution
|
|
248
242
|
// field_declaration: [modifiers] type_identifier variable_declarator
|
|
249
243
|
let typeAnnotation = null;
|
|
@@ -267,6 +261,27 @@ function _parseJavaField(node, className) {
|
|
|
267
261
|
line: node.startPosition.row + 1,
|
|
268
262
|
};
|
|
269
263
|
}
|
|
264
|
+
/**
|
|
265
|
+
* tree-sitter-java 中注解位于 modifiers 子节点内,而非声明节点的直接子节点。
|
|
266
|
+
* 此辅助函数同时搜索两层:node.namedChildren + modifiers.namedChildren。
|
|
267
|
+
*/
|
|
268
|
+
function _extractAnnotations(node) {
|
|
269
|
+
// 1. 直接子节点(兼容其他可能的 AST 结构)
|
|
270
|
+
const direct = node.namedChildren
|
|
271
|
+
.filter((c) => c.type === 'marker_annotation' || c.type === 'annotation')
|
|
272
|
+
.map((a) => a.text);
|
|
273
|
+
if (direct.length > 0) {
|
|
274
|
+
return direct;
|
|
275
|
+
}
|
|
276
|
+
// 2. modifiers 子节点
|
|
277
|
+
const modifiers = node.namedChildren.find((c) => c.type === 'modifiers');
|
|
278
|
+
if (modifiers) {
|
|
279
|
+
return modifiers.namedChildren
|
|
280
|
+
.filter((c) => c.type === 'marker_annotation' || c.type === 'annotation')
|
|
281
|
+
.map((a) => a.text);
|
|
282
|
+
}
|
|
283
|
+
return [];
|
|
284
|
+
}
|
|
270
285
|
// ── Java 模式检测 ──
|
|
271
286
|
function detectJavaPatterns(root, lang, methods, properties, classes) {
|
|
272
287
|
const patterns = [];
|
|
@@ -387,7 +402,6 @@ function _maxNesting(node, depth) {
|
|
|
387
402
|
'enhanced_for_statement',
|
|
388
403
|
'while_statement',
|
|
389
404
|
'switch_expression',
|
|
390
|
-
'block',
|
|
391
405
|
'try_statement',
|
|
392
406
|
]);
|
|
393
407
|
let max = depth;
|
|
@@ -75,9 +75,69 @@ function _walkJSClassBody(body, ctx, className) {
|
|
|
75
75
|
else if (child.type === 'field_definition' || child.type === 'public_field_definition') {
|
|
76
76
|
const name = child.namedChildren.find((c) => c.type === 'property_identifier')?.text;
|
|
77
77
|
if (name) {
|
|
78
|
-
|
|
78
|
+
const isStatic = child.text.trimStart().startsWith('static');
|
|
79
|
+
ctx.properties.push({
|
|
80
|
+
name,
|
|
81
|
+
className,
|
|
82
|
+
isStatic,
|
|
83
|
+
isConstant: false,
|
|
84
|
+
line: child.startPosition.row + 1,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// 从 constructor 中提取 this.xxx = ... 赋值属性
|
|
90
|
+
_extractConstructorProperties(body, ctx, className);
|
|
91
|
+
}
|
|
92
|
+
function _extractConstructorProperties(body, ctx, className) {
|
|
93
|
+
if (!body) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
for (let i = 0; i < body.namedChildCount; i++) {
|
|
97
|
+
const child = body.namedChild(i);
|
|
98
|
+
if (child.type !== 'method_definition') {
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
const nameNode = child.namedChildren.find((c) => c.type === 'property_identifier' || c.type === 'identifier');
|
|
102
|
+
if (nameNode?.text !== 'constructor') {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
const stmtBlock = child.namedChildren.find((c) => c.type === 'statement_block');
|
|
106
|
+
if (!stmtBlock) {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
const seen = new Set(ctx.properties.filter((p) => p.className === className).map((p) => p.name));
|
|
110
|
+
_walkForThisAssignments(stmtBlock, ctx, className, seen);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function _walkForThisAssignments(node, ctx, className, seen) {
|
|
114
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
115
|
+
const child = node.child(i);
|
|
116
|
+
if (child.type === 'expression_statement') {
|
|
117
|
+
const expr = child.namedChildren.find((c) => c.type === 'assignment_expression');
|
|
118
|
+
if (expr) {
|
|
119
|
+
const left = expr.namedChildren[0];
|
|
120
|
+
if (left?.type === 'member_expression') {
|
|
121
|
+
const obj = left.namedChildren.find((c) => c.type === 'this');
|
|
122
|
+
const prop = left.namedChildren.find((c) => c.type === 'property_identifier');
|
|
123
|
+
if (obj && prop && !seen.has(prop.text)) {
|
|
124
|
+
seen.add(prop.text);
|
|
125
|
+
ctx.properties.push({
|
|
126
|
+
name: prop.text,
|
|
127
|
+
className,
|
|
128
|
+
isStatic: false,
|
|
129
|
+
isConstant: false,
|
|
130
|
+
line: child.startPosition.row + 1,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
79
134
|
}
|
|
80
135
|
}
|
|
136
|
+
else if (child.namedChildCount > 0 &&
|
|
137
|
+
child.type !== 'function' &&
|
|
138
|
+
child.type !== 'arrow_function') {
|
|
139
|
+
_walkForThisAssignments(child, ctx, className, seen);
|
|
140
|
+
}
|
|
81
141
|
}
|
|
82
142
|
}
|
|
83
143
|
function _parseJSClass(node) {
|
|
@@ -85,12 +145,10 @@ function _parseJSClass(node) {
|
|
|
85
145
|
let superclass = null;
|
|
86
146
|
for (const child of node.namedChildren) {
|
|
87
147
|
if (child.type === 'class_heritage') {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
superclass = typeNode.text;
|
|
93
|
-
}
|
|
148
|
+
// tree-sitter-javascript: class_heritage → identifier (直接子节点, 无 extends_clause 包装)
|
|
149
|
+
const typeNode = child.namedChildren.find((c) => c.type === 'identifier' || c.type === 'member_expression');
|
|
150
|
+
if (typeNode) {
|
|
151
|
+
superclass = typeNode.text;
|
|
94
152
|
}
|
|
95
153
|
}
|
|
96
154
|
}
|
|
@@ -142,6 +200,44 @@ function _parseJSVariableDecl(node, ctx, parentClassName) {
|
|
|
142
200
|
}
|
|
143
201
|
function detectJSPatterns(root, lang, methods, properties, classes) {
|
|
144
202
|
const patterns = [];
|
|
203
|
+
// 按 className 分组
|
|
204
|
+
const methodsByClass = new Map();
|
|
205
|
+
const propsByClass = new Map();
|
|
206
|
+
for (const m of methods) {
|
|
207
|
+
const k = m.className || '';
|
|
208
|
+
if (!methodsByClass.has(k)) {
|
|
209
|
+
methodsByClass.set(k, []);
|
|
210
|
+
}
|
|
211
|
+
methodsByClass.get(k).push(m);
|
|
212
|
+
}
|
|
213
|
+
for (const p of properties) {
|
|
214
|
+
const k = p.className || '';
|
|
215
|
+
if (!propsByClass.has(k)) {
|
|
216
|
+
propsByClass.set(k, []);
|
|
217
|
+
}
|
|
218
|
+
propsByClass.get(k).push(p);
|
|
219
|
+
}
|
|
220
|
+
for (const cls of classes) {
|
|
221
|
+
const clsMethods = methodsByClass.get(cls.name) || [];
|
|
222
|
+
const clsProps = propsByClass.get(cls.name) || [];
|
|
223
|
+
// ── Singleton: static getInstance() / static instance ──
|
|
224
|
+
const hasGetInstance = clsMethods.some((m) => m.isClassMethod && /^getInstance$|^shared$/.test(m.name));
|
|
225
|
+
const hasStaticInstance = clsProps.some((p) => p.isStatic && /^instance$|^shared$|^default$/.test(p.name));
|
|
226
|
+
if (hasGetInstance || hasStaticInstance) {
|
|
227
|
+
patterns.push({ type: 'singleton', className: cls.name, line: cls.line, confidence: 0.9 });
|
|
228
|
+
}
|
|
229
|
+
// ── Observer / EventEmitter ──
|
|
230
|
+
const emitMethods = clsMethods.filter((m) => /^on$|^emit$|^addEventListener$|^removeEventListener$|^subscribe$|^addListener$/.test(m.name));
|
|
231
|
+
if (emitMethods.length >= 2) {
|
|
232
|
+
patterns.push({ type: 'observer', className: cls.name, line: cls.line, confidence: 0.85 });
|
|
233
|
+
}
|
|
234
|
+
// ── Middleware ──
|
|
235
|
+
if (/middleware|interceptor/i.test(cls.name) ||
|
|
236
|
+
clsMethods.some((m) => /^use$|^handle$/.test(m.name) && m.isClassMethod === false)) {
|
|
237
|
+
patterns.push({ type: 'middleware', className: cls.name, line: cls.line, confidence: 0.8 });
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
// 自由函数级别的模式
|
|
145
241
|
for (const m of methods) {
|
|
146
242
|
if (/^use[A-Z]/.test(m.name) && !m.className) {
|
|
147
243
|
patterns.push({ type: 'react-hook', methodName: m.name, line: m.line, confidence: 0.9 });
|
|
@@ -155,15 +251,6 @@ function detectJSPatterns(root, lang, methods, properties, classes) {
|
|
|
155
251
|
confidence: 0.8,
|
|
156
252
|
});
|
|
157
253
|
}
|
|
158
|
-
if (/^on[A-Z]|^emit$|^addEventListener$|^subscribe$/.test(m.name)) {
|
|
159
|
-
patterns.push({
|
|
160
|
-
type: 'observer',
|
|
161
|
-
className: m.className,
|
|
162
|
-
methodName: m.name,
|
|
163
|
-
line: m.line,
|
|
164
|
-
confidence: 0.7,
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
254
|
}
|
|
168
255
|
return patterns;
|
|
169
256
|
}
|
|
@@ -204,7 +291,6 @@ function _maxNesting(node, depth) {
|
|
|
204
291
|
'for_in_statement',
|
|
205
292
|
'while_statement',
|
|
206
293
|
'switch_statement',
|
|
207
|
-
'statement_block',
|
|
208
294
|
]);
|
|
209
295
|
let max = depth;
|
|
210
296
|
const nextDepth = NESTING_TYPES.has(node.type) ? depth + 1 : depth;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @description ObjC AST Walker 插件 - 从 AstAnalyzer.js 迁移
|
|
4
4
|
*/
|
|
5
5
|
declare function walkObjC(root: any, ctx: any): void;
|
|
6
|
-
declare function detectObjCPatterns(root: any, lang: any, methods: any, properties: any, classes: any):
|
|
6
|
+
declare function detectObjCPatterns(root: any, lang: any, methods: any, properties: any, classes: any): any[];
|
|
7
7
|
declare function getGrammar(): any;
|
|
8
8
|
export declare function setGrammar(grammar: any): void;
|
|
9
9
|
export declare const plugin: {
|
|
@@ -257,9 +257,86 @@ function _parseObjCProperty(node, className) {
|
|
|
257
257
|
}
|
|
258
258
|
// ── ObjC 模式检测 ──
|
|
259
259
|
function detectObjCPatterns(root, lang, methods, properties, classes) {
|
|
260
|
-
|
|
261
|
-
//
|
|
262
|
-
|
|
260
|
+
const patterns = [];
|
|
261
|
+
// 按 className 分组
|
|
262
|
+
const methodsByClass = new Map();
|
|
263
|
+
const propsByClass = new Map();
|
|
264
|
+
for (const m of methods) {
|
|
265
|
+
const k = m.className || '';
|
|
266
|
+
if (!methodsByClass.has(k)) {
|
|
267
|
+
methodsByClass.set(k, []);
|
|
268
|
+
}
|
|
269
|
+
methodsByClass.get(k).push(m);
|
|
270
|
+
}
|
|
271
|
+
for (const p of properties) {
|
|
272
|
+
const k = p.className || '';
|
|
273
|
+
if (!propsByClass.has(k)) {
|
|
274
|
+
propsByClass.set(k, []);
|
|
275
|
+
}
|
|
276
|
+
propsByClass.get(k).push(p);
|
|
277
|
+
}
|
|
278
|
+
for (const cls of classes) {
|
|
279
|
+
const clsMethods = methodsByClass.get(cls.name) || [];
|
|
280
|
+
const clsProps = propsByClass.get(cls.name) || [];
|
|
281
|
+
// ── Singleton: +sharedInstance / +shared / +defaultManager ──
|
|
282
|
+
const singletonMethod = clsMethods.find((m) => m.isClassMethod && /^shared|^default|^current|^instance$/.test(m.name));
|
|
283
|
+
if (singletonMethod) {
|
|
284
|
+
patterns.push({
|
|
285
|
+
type: 'singleton',
|
|
286
|
+
className: cls.name,
|
|
287
|
+
methodName: singletonMethod.name,
|
|
288
|
+
line: singletonMethod.line,
|
|
289
|
+
confidence: 0.9,
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
// ── Delegate: @property (weak) id<XXXDelegate> delegate ──
|
|
293
|
+
for (const p of clsProps) {
|
|
294
|
+
if (/delegate/i.test(p.name)) {
|
|
295
|
+
const isWeak = (p.attributes || []).some((a) => a === 'weak');
|
|
296
|
+
patterns.push({
|
|
297
|
+
type: 'delegate',
|
|
298
|
+
className: cls.name,
|
|
299
|
+
propertyName: p.name,
|
|
300
|
+
isWeakRef: isWeak,
|
|
301
|
+
line: p.line,
|
|
302
|
+
confidence: 0.95,
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
if (/dataSource/i.test(p.name)) {
|
|
306
|
+
patterns.push({
|
|
307
|
+
type: 'delegate',
|
|
308
|
+
className: cls.name,
|
|
309
|
+
propertyName: p.name,
|
|
310
|
+
isWeakRef: true,
|
|
311
|
+
line: p.line,
|
|
312
|
+
confidence: 0.85,
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
// ── Factory: +classWithXxx / +xxxWithYyy (class factory methods) ──
|
|
317
|
+
for (const m of clsMethods) {
|
|
318
|
+
if (m.isClassMethod && /With[A-Z]/.test(m.name)) {
|
|
319
|
+
patterns.push({
|
|
320
|
+
type: 'factory',
|
|
321
|
+
className: cls.name,
|
|
322
|
+
methodName: m.name,
|
|
323
|
+
line: m.line,
|
|
324
|
+
confidence: 0.8,
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
// ── KVO Observer: observeValueForKeyPath / addObserver ──
|
|
329
|
+
const hasKVO = clsMethods.some((m) => /^observeValueForKeyPath$|^addObserver$|^removeObserver$/.test(m.name));
|
|
330
|
+
if (hasKVO) {
|
|
331
|
+
patterns.push({ type: 'observer', className: cls.name, line: cls.line, confidence: 0.85 });
|
|
332
|
+
}
|
|
333
|
+
// ── Notification Observer: NSNotificationCenter pattern ──
|
|
334
|
+
const hasNSNotif = clsMethods.some((m) => /notification|handleNotification|didReceiveNotification/i.test(m.name));
|
|
335
|
+
if (hasNSNotif) {
|
|
336
|
+
patterns.push({ type: 'observer', className: cls.name, line: cls.line, confidence: 0.7 });
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return patterns;
|
|
263
340
|
}
|
|
264
341
|
// ── 工具函数 ──
|
|
265
342
|
function _findIdentifier(node) {
|
|
@@ -312,7 +389,6 @@ function _maxNesting(node, depth) {
|
|
|
312
389
|
'for_in_statement',
|
|
313
390
|
'while_statement',
|
|
314
391
|
'switch_statement',
|
|
315
|
-
'compound_statement',
|
|
316
392
|
]);
|
|
317
393
|
let max = depth;
|
|
318
394
|
const nextDepth = NESTING_TYPES.has(node.type) ? depth + 1 : depth;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Phase 5: 新增 ImportRecord 结构化导入 + extractCallSites 调用点提取
|
|
6
6
|
*/
|
|
7
7
|
declare function walkSwift(root: any, ctx: any): void;
|
|
8
|
-
declare function detectSwiftPatterns(root: any,
|
|
8
|
+
declare function detectSwiftPatterns(root: any, _lang: any, methods: any, properties: any, classes: any): any[];
|
|
9
9
|
/**
|
|
10
10
|
* 从 Swift AST root 提取所有调用点
|
|
11
11
|
* 遍历 function_declaration 中的 function_body → call_expression
|
|
@@ -23,9 +23,29 @@ function _walkSwiftNode(node, ctx, parentClassName) {
|
|
|
23
23
|
case 'class_declaration':
|
|
24
24
|
case 'struct_declaration':
|
|
25
25
|
case 'enum_declaration': {
|
|
26
|
+
// tree-sitter-swift maps protocol/extension to class_declaration too.
|
|
27
|
+
// Detect the actual keyword child and dispatch accordingly.
|
|
28
|
+
const keyword = _findKeywordChild(child);
|
|
29
|
+
if (keyword === 'protocol') {
|
|
30
|
+
const protoInfo = _parseSwiftProtocol(child);
|
|
31
|
+
ctx.protocols.push(protoInfo);
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
if (keyword === 'extension') {
|
|
35
|
+
const extInfo = _parseSwiftExtension(child);
|
|
36
|
+
ctx.categories.push(extInfo);
|
|
37
|
+
const extBody = child.namedChildren.find((c) => c.type === 'class_body' || c.type === 'extension_body');
|
|
38
|
+
if (extBody) {
|
|
39
|
+
_walkSwiftNode(extBody, ctx, extInfo.className);
|
|
40
|
+
}
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
26
43
|
const classInfo = _parseSwiftTypeDecl(child);
|
|
27
44
|
ctx.classes.push(classInfo);
|
|
28
|
-
const body = child.namedChildren.find((c) => c.type === 'class_body' ||
|
|
45
|
+
const body = child.namedChildren.find((c) => c.type === 'class_body' ||
|
|
46
|
+
c.type === 'struct_body' ||
|
|
47
|
+
c.type === 'enum_body' ||
|
|
48
|
+
c.type === 'enum_class_body');
|
|
29
49
|
if (body) {
|
|
30
50
|
_walkSwiftNode(body, ctx, classInfo.name);
|
|
31
51
|
}
|
|
@@ -66,9 +86,22 @@ function _walkSwiftNode(node, ctx, parentClassName) {
|
|
|
66
86
|
}
|
|
67
87
|
}
|
|
68
88
|
}
|
|
89
|
+
/** Detect the actual Swift keyword from a class_declaration node's children. */
|
|
90
|
+
const _swiftKeywords = ['struct', 'class', 'enum', 'actor', 'protocol', 'extension'];
|
|
91
|
+
function _findKeywordChild(node) {
|
|
92
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
93
|
+
const childType = node.child(i).type;
|
|
94
|
+
if (_swiftKeywords.includes(childType)) {
|
|
95
|
+
return childType;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return 'class';
|
|
99
|
+
}
|
|
69
100
|
function _parseSwiftTypeDecl(node) {
|
|
70
101
|
const name = node.namedChildren.find((c) => c.type === 'type_identifier' || c.type === 'simple_identifier')?.text || 'Unknown';
|
|
71
|
-
|
|
102
|
+
// tree-sitter-swift maps struct/class/enum/actor all to `class_declaration`.
|
|
103
|
+
// Detect the actual kind from the keyword child node (e.g. struct="struct").
|
|
104
|
+
const kind = _findKeywordChild(node);
|
|
72
105
|
const _superclass = null;
|
|
73
106
|
const protocols = [];
|
|
74
107
|
for (const child of node.namedChildren) {
|
|
@@ -132,7 +165,7 @@ function _parseSwiftExtension(node) {
|
|
|
132
165
|
}
|
|
133
166
|
}
|
|
134
167
|
const methods = [];
|
|
135
|
-
const body = node.namedChildren.find((c) => c.type === 'extension_body');
|
|
168
|
+
const body = node.namedChildren.find((c) => c.type === 'extension_body' || c.type === 'class_body');
|
|
136
169
|
if (body) {
|
|
137
170
|
for (const child of body.namedChildren) {
|
|
138
171
|
if (child.type === 'function_declaration') {
|
|
@@ -196,8 +229,150 @@ function _parseSwiftProperty(node, className) {
|
|
|
196
229
|
};
|
|
197
230
|
}
|
|
198
231
|
// ── Swift 模式检测 ──
|
|
199
|
-
function detectSwiftPatterns(root,
|
|
200
|
-
|
|
232
|
+
function detectSwiftPatterns(root, _lang, methods, properties, classes) {
|
|
233
|
+
const patterns = [];
|
|
234
|
+
// Index properties by className for quick lookup
|
|
235
|
+
const propsByClass = new Map();
|
|
236
|
+
for (const p of properties) {
|
|
237
|
+
if (p.className) {
|
|
238
|
+
const arr = propsByClass.get(p.className) || [];
|
|
239
|
+
arr.push(p);
|
|
240
|
+
propsByClass.set(p.className, arr);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
// Index methods by className
|
|
244
|
+
const methodsByClass = new Map();
|
|
245
|
+
for (const m of methods) {
|
|
246
|
+
if (m.className && m.kind === 'definition') {
|
|
247
|
+
const arr = methodsByClass.get(m.className) || [];
|
|
248
|
+
arr.push(m);
|
|
249
|
+
methodsByClass.set(m.className, arr);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
for (const cls of classes) {
|
|
253
|
+
const kind = cls.kind;
|
|
254
|
+
const className = cls.name;
|
|
255
|
+
const clsProps = propsByClass.get(className) || [];
|
|
256
|
+
const clsMethods = methodsByClass.get(className) || [];
|
|
257
|
+
// ── 1. Singleton: `static let shared = ...` 属性 ──
|
|
258
|
+
const sharedProp = clsProps.find((p) => p.isStatic &&
|
|
259
|
+
(p.isConstant || p.isConst) &&
|
|
260
|
+
/^shared$|^default$|^instance$|^current$/.test(p.name));
|
|
261
|
+
if (sharedProp) {
|
|
262
|
+
patterns.push({
|
|
263
|
+
type: 'singleton',
|
|
264
|
+
className,
|
|
265
|
+
propertyName: sharedProp.name,
|
|
266
|
+
line: sharedProp.line,
|
|
267
|
+
confidence: 0.95,
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
// ── 2. Delegate: `weak var delegate` 属性或 protocol 命名以 Delegate/DataSource 结尾 ──
|
|
271
|
+
for (const p of clsProps) {
|
|
272
|
+
if (/delegate/i.test(p.name) && !p.name.startsWith('_')) {
|
|
273
|
+
patterns.push({
|
|
274
|
+
type: 'delegate',
|
|
275
|
+
className,
|
|
276
|
+
propertyName: p.name,
|
|
277
|
+
isWeakRef: /weak/.test(`${(p.attributes || []).join(' ')} ${p.modifiers || ''}`),
|
|
278
|
+
line: p.line,
|
|
279
|
+
confidence: 0.95,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
// ── 3. Factory: `static func make/create/from/build` ──
|
|
284
|
+
for (const m of clsMethods) {
|
|
285
|
+
if (m.isClassMethod && /^make|^create|^from|^build/.test(m.name)) {
|
|
286
|
+
patterns.push({
|
|
287
|
+
type: 'factory',
|
|
288
|
+
className,
|
|
289
|
+
methodName: m.name,
|
|
290
|
+
line: m.line,
|
|
291
|
+
confidence: 0.85,
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
// ── 4. Actor: Swift concurrency primitive ──
|
|
296
|
+
if (kind === 'actor') {
|
|
297
|
+
patterns.push({
|
|
298
|
+
type: 'actor',
|
|
299
|
+
className,
|
|
300
|
+
line: cls.line,
|
|
301
|
+
confidence: 0.95,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
// ── 5. Value type (struct): immutable-by-default semantics ──
|
|
305
|
+
if (kind === 'struct' && !className.endsWith('Error')) {
|
|
306
|
+
// Only flag structs with methods (not pure data containers)
|
|
307
|
+
if (clsMethods.length > 0) {
|
|
308
|
+
patterns.push({
|
|
309
|
+
type: 'value-type',
|
|
310
|
+
className,
|
|
311
|
+
methodCount: clsMethods.length,
|
|
312
|
+
line: cls.line,
|
|
313
|
+
confidence: 0.9,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
// ── 6. Protocol-oriented default implementation ──
|
|
318
|
+
// (detected via categories/extensions that match a protocol name)
|
|
319
|
+
// Handled below after the class loop.
|
|
320
|
+
// ── 7. ViewModel: class name ends with ViewModel ──
|
|
321
|
+
if (/ViewModel$/.test(className)) {
|
|
322
|
+
patterns.push({
|
|
323
|
+
type: 'viewmodel',
|
|
324
|
+
className,
|
|
325
|
+
line: cls.line,
|
|
326
|
+
confidence: 0.9,
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
// ── 8. Coordinator pattern ──
|
|
330
|
+
if (/Coordinator$/.test(className)) {
|
|
331
|
+
patterns.push({
|
|
332
|
+
type: 'coordinator',
|
|
333
|
+
className,
|
|
334
|
+
line: cls.line,
|
|
335
|
+
confidence: 0.85,
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
// ── 9. Error type: enum conforming to Error ──
|
|
339
|
+
if (kind === 'enum') {
|
|
340
|
+
const conformsError = (cls.protocols || []).some((p) => p === 'Error' || p === 'LocalizedError');
|
|
341
|
+
if (conformsError || className.endsWith('Error')) {
|
|
342
|
+
patterns.push({
|
|
343
|
+
type: 'error-type',
|
|
344
|
+
className,
|
|
345
|
+
line: cls.line,
|
|
346
|
+
confidence: 0.9,
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
// ── 10. Middleware / Interceptor pattern ──
|
|
351
|
+
if (/Middleware$|Interceptor$/.test(className)) {
|
|
352
|
+
patterns.push({
|
|
353
|
+
type: 'middleware',
|
|
354
|
+
className,
|
|
355
|
+
line: cls.line,
|
|
356
|
+
confidence: 0.85,
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
// ── 11. Observer: methods matching didChange/willChange/observe/subscribe ──
|
|
361
|
+
for (const m of methods) {
|
|
362
|
+
if (m.kind !== 'definition') {
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
if (/^didChange|^willChange|^observe|^addObserver|^subscribe|^on[A-Z]/.test(m.name)) {
|
|
366
|
+
patterns.push({
|
|
367
|
+
type: 'observer',
|
|
368
|
+
className: m.className,
|
|
369
|
+
methodName: m.name,
|
|
370
|
+
line: m.line,
|
|
371
|
+
confidence: 0.7,
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
return patterns;
|
|
201
376
|
}
|
|
202
377
|
// ── 工具函数 ──
|
|
203
378
|
function _findIdentifier(node) {
|
|
@@ -249,7 +424,6 @@ function _maxNesting(node, depth) {
|
|
|
249
424
|
'for_in_statement',
|
|
250
425
|
'while_statement',
|
|
251
426
|
'switch_statement',
|
|
252
|
-
'compound_statement',
|
|
253
427
|
]);
|
|
254
428
|
let max = depth;
|
|
255
429
|
const nextDepth = NESTING_TYPES.has(node.type) ? depth + 1 : depth;
|
|
@@ -282,7 +456,10 @@ function _collectSwiftScopes(root) {
|
|
|
282
456
|
child.type === 'struct_declaration' ||
|
|
283
457
|
child.type === 'enum_declaration') {
|
|
284
458
|
const name = child.namedChildren.find((c) => c.type === 'type_identifier' || c.type === 'simple_identifier')?.text;
|
|
285
|
-
const body = child.namedChildren.find((c) => c.type === 'class_body' ||
|
|
459
|
+
const body = child.namedChildren.find((c) => c.type === 'class_body' ||
|
|
460
|
+
c.type === 'struct_body' ||
|
|
461
|
+
c.type === 'enum_body' ||
|
|
462
|
+
c.type === 'enum_class_body');
|
|
286
463
|
if (body) {
|
|
287
464
|
visit(body, name || className);
|
|
288
465
|
}
|
|
@@ -27,10 +27,23 @@ export declare function isGeoOrProviderError(err: unknown): boolean;
|
|
|
27
27
|
* 当主 provider 调用失败(地理限制等)时自动切换到备选 provider
|
|
28
28
|
*/
|
|
29
29
|
export declare function getProviderWithFallback(): Promise<ClaudeProvider | GoogleGeminiProvider | MockProvider | OpenAiProvider | null>;
|
|
30
|
+
/**
|
|
31
|
+
* 创建独立的 Embedding Provider
|
|
32
|
+
*
|
|
33
|
+
* 当 ASD_EMBED_PROVIDER 被设置时,创建一个专用于 embedding 的 provider 实例,
|
|
34
|
+
* 使 embedding 和 LLM 生成可以使用不同的提供商/模型。
|
|
35
|
+
*
|
|
36
|
+
* 典型场景:LLM 用 Google Gemini,Embedding 用本地 Ollama + qwen3-embedding
|
|
37
|
+
*
|
|
38
|
+
* @returns 独立的 embed provider,或 null(未配置时)
|
|
39
|
+
*/
|
|
40
|
+
export declare function createEmbedProvider(): ReturnType<typeof createProvider> | null;
|
|
30
41
|
/** 获取当前 AI 配置信息(同步,用于 UI 展示) */
|
|
31
42
|
export declare function getAiConfigInfo(): {
|
|
32
43
|
provider: string;
|
|
33
44
|
model: string;
|
|
45
|
+
embedProvider: string;
|
|
46
|
+
embedModel: string;
|
|
34
47
|
hasKey: boolean;
|
|
35
48
|
keys: {
|
|
36
49
|
google: boolean;
|
|
@@ -46,6 +59,7 @@ export { MockProvider } from './providers/MockProvider.js';
|
|
|
46
59
|
export { OpenAiProvider } from './providers/OpenAiProvider.js';
|
|
47
60
|
declare const _default: {
|
|
48
61
|
createProvider: typeof createProvider;
|
|
62
|
+
createEmbedProvider: typeof createEmbedProvider;
|
|
49
63
|
autoDetectProvider: typeof autoDetectProvider;
|
|
50
64
|
getAiConfigInfo: typeof getAiConfigInfo;
|
|
51
65
|
getProviderWithFallback: typeof getProviderWithFallback;
|