umple-lsp-server 0.4.2 → 1.0.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/CHANGELOG.md +10 -0
- package/README.md +35 -0
- package/completions.scm +9 -3
- package/definitions.scm +5 -0
- package/highlights.scm +487 -0
- package/out/codeActions.d.ts +31 -0
- package/out/codeActions.js +361 -0
- package/out/codeActions.js.map +1 -0
- package/out/completionAnalysis.d.ts +9 -1
- package/out/completionAnalysis.js +1211 -64
- package/out/completionAnalysis.js.map +1 -1
- package/out/completionBuilder.d.ts +1 -1
- package/out/completionBuilder.js +463 -319
- package/out/completionBuilder.js.map +1 -1
- package/out/completionTriggers.d.ts +20 -0
- package/out/completionTriggers.js +69 -0
- package/out/completionTriggers.js.map +1 -0
- package/out/diagnosticSources.d.ts +3 -0
- package/out/diagnosticSources.js +11 -0
- package/out/diagnosticSources.js.map +1 -0
- package/out/diagramNavigation.js +3 -3
- package/out/diagramNavigation.js.map +1 -1
- package/out/documentSymbolBuilder.js +2 -37
- package/out/documentSymbolBuilder.js.map +1 -1
- package/out/formatter.d.ts +13 -1
- package/out/formatter.js +303 -10
- package/out/formatter.js.map +1 -1
- package/out/hoverBuilder.js +90 -23
- package/out/hoverBuilder.js.map +1 -1
- package/out/inlayHints.d.ts +21 -0
- package/out/inlayHints.js +98 -0
- package/out/inlayHints.js.map +1 -0
- package/out/referenceSearch.d.ts +1 -1
- package/out/referenceSearch.js +134 -7
- package/out/referenceSearch.js.map +1 -1
- package/out/resolver.js +82 -3
- package/out/resolver.js.map +1 -1
- package/out/semanticTokens.d.ts +32 -0
- package/out/semanticTokens.js +228 -0
- package/out/semanticTokens.js.map +1 -0
- package/out/server.js +216 -36
- package/out/server.js.map +1 -1
- package/out/snippets.d.ts +39 -0
- package/out/snippets.js +328 -0
- package/out/snippets.js.map +1 -0
- package/out/symbolIndex.d.ts +50 -0
- package/out/symbolIndex.js +170 -7
- package/out/symbolIndex.js.map +1 -1
- package/out/symbolPresentation.d.ts +3 -0
- package/out/symbolPresentation.js +45 -0
- package/out/symbolPresentation.js.map +1 -0
- package/out/symbolTypes.d.ts +1 -0
- package/out/tokenAnalysis.js +77 -4
- package/out/tokenAnalysis.js.map +1 -1
- package/out/tokenTypes.d.ts +8 -1
- package/out/tokenTypes.js +2 -0
- package/out/tokenTypes.js.map +1 -1
- package/out/treeUtils.js +17 -4
- package/out/treeUtils.js.map +1 -1
- package/out/workspaceSymbolBuilder.d.ts +3 -0
- package/out/workspaceSymbolBuilder.js +117 -0
- package/out/workspaceSymbolBuilder.js.map +1 -0
- package/package.json +5 -2
- package/references.scm +31 -3
- package/tree-sitter-umple.wasm +0 -0
- package/out/bin.d.ts +0 -2
- package/out/bin.js +0 -5
- package/out/bin.js.map +0 -1
- package/out/log.d.ts +0 -7
- package/out/log.js +0 -22
- package/out/log.js.map +0 -1
- package/out/tsconfig.tsbuildinfo +0 -1
package/out/completionBuilder.js
CHANGED
|
@@ -15,7 +15,23 @@ exports.symbolKindToCompletionKind = symbolKindToCompletionKind;
|
|
|
15
15
|
exports.buildSemanticCompletionItems = buildSemanticCompletionItems;
|
|
16
16
|
const node_1 = require("vscode-languageserver/node");
|
|
17
17
|
const keywords_1 = require("./keywords");
|
|
18
|
+
const snippets_1 = require("./snippets");
|
|
18
19
|
const path = require("path");
|
|
20
|
+
/**
|
|
21
|
+
* Topic 054 — emit snippet completion items for the given scope when the
|
|
22
|
+
* client advertised snippet support. No-op for typed-prefix / suppress /
|
|
23
|
+
* symbol-only scopes (they have no snippet entries registered).
|
|
24
|
+
*/
|
|
25
|
+
function appendSnippetsForScope(items, seen, scope, snippetSupport) {
|
|
26
|
+
if (!snippetSupport)
|
|
27
|
+
return;
|
|
28
|
+
for (const entry of (0, snippets_1.getSnippetsForScope)(scope)) {
|
|
29
|
+
if (seen.has(entry.label))
|
|
30
|
+
continue;
|
|
31
|
+
seen.add(entry.label);
|
|
32
|
+
items.push((0, snippets_1.snippetEntryToItem)(entry));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
19
35
|
// ── Curated top-level construct keywords ───────────────────────────────────
|
|
20
36
|
// Derived from the grammar's _definition rule (grammar.js lines 66-92).
|
|
21
37
|
// Raw parser lookahead is NOT surfaced at this scope.
|
|
@@ -57,7 +73,7 @@ const CLASS_BODY_KEYWORDS = [
|
|
|
57
73
|
// Trace
|
|
58
74
|
"trace", "tracecase",
|
|
59
75
|
// Nested definitions
|
|
60
|
-
"class", "enum", "mixset",
|
|
76
|
+
"inner", "class", "enum", "mixset",
|
|
61
77
|
// Class-level declarations
|
|
62
78
|
"abstract", "singleton",
|
|
63
79
|
// Attribute modifiers
|
|
@@ -196,40 +212,18 @@ const ASSOCIATION_MULTIPLICITY_STARTERS = [
|
|
|
196
212
|
"1..*",
|
|
197
213
|
"0..*",
|
|
198
214
|
];
|
|
199
|
-
//
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
"
|
|
204
|
-
"
|
|
205
|
-
"
|
|
206
|
-
|
|
207
|
-
"
|
|
208
|
-
"
|
|
209
|
-
"
|
|
210
|
-
|
|
211
|
-
"GvClassTraitDiagram", "GvEntityRelationshipDiagram",
|
|
212
|
-
"Alloy", "NuSMV", "NuSMVOptimizer", "Papyrus", "Ecore", "Xmi",
|
|
213
|
-
"Xtext", "Sql", "StateTables", "EventSequence", "InstanceDiagram",
|
|
214
|
-
"Umple", "UmpleSelf", "USE", "Test", "SimpleMetrics",
|
|
215
|
-
"PlainRequirementsDoc", "Uigu2", "ExternalGrammar", "Mermaid",
|
|
216
|
-
// Other top-level directives
|
|
217
|
-
"use", "strictness",
|
|
218
|
-
// Class-level keywords invalid inside constraints
|
|
219
|
-
"isA", "req", "require", "subfeature", "isFeature",
|
|
220
|
-
"depend", "singleton", "displayColor", "displayColour",
|
|
221
|
-
"implementsReq", "filter", "includeFilter",
|
|
222
|
-
// Trace/SM/attribute keywords that can't appear in expressions
|
|
223
|
-
"trace", "tracecase", "activate", "deactivate",
|
|
224
|
-
"onAllObjects", "onThisThreadOnly", "onThisObject",
|
|
225
|
-
"where", "until", "giving", "record",
|
|
226
|
-
"queued", "pooled", "as",
|
|
227
|
-
"key", "immutable", "unique", "lazy", "settable",
|
|
228
|
-
"internal", "defaulted", "autounique",
|
|
229
|
-
"entry", "exit", "do",
|
|
230
|
-
"emit", "around", "custom", "generated",
|
|
231
|
-
"include", "hops", "super", "sub",
|
|
232
|
-
]);
|
|
215
|
+
// All seven Umple association arrow operators, mirroring the grammar rule
|
|
216
|
+
// arrow: choice("--", "->", "<-", "<@>-", "-<@>", ">->", "<-<")
|
|
217
|
+
// Offered after the left multiplicity (`1 |`) before the arrow is committed.
|
|
218
|
+
const ASSOCIATION_ARROW_STARTERS = [
|
|
219
|
+
"--",
|
|
220
|
+
"->",
|
|
221
|
+
"<-",
|
|
222
|
+
"<@>-",
|
|
223
|
+
"-<@>",
|
|
224
|
+
">->",
|
|
225
|
+
"<-<",
|
|
226
|
+
];
|
|
233
227
|
// ── Kind → CompletionItemKind mapping ───────────────────────────────────────
|
|
234
228
|
function symbolKindToCompletionKind(kind) {
|
|
235
229
|
switch (kind) {
|
|
@@ -245,8 +239,12 @@ function symbolKindToCompletionKind(kind) {
|
|
|
245
239
|
return node_1.CompletionItemKind.EnumMember;
|
|
246
240
|
case "statemachine":
|
|
247
241
|
return node_1.CompletionItemKind.Enum;
|
|
242
|
+
case "event":
|
|
243
|
+
return node_1.CompletionItemKind.Event;
|
|
248
244
|
case "attribute":
|
|
249
245
|
return node_1.CompletionItemKind.Field;
|
|
246
|
+
case "port":
|
|
247
|
+
return node_1.CompletionItemKind.Property;
|
|
250
248
|
case "const":
|
|
251
249
|
return node_1.CompletionItemKind.Constant;
|
|
252
250
|
case "method":
|
|
@@ -263,6 +261,69 @@ function symbolKindToCompletionKind(kind) {
|
|
|
263
261
|
return node_1.CompletionItemKind.Text;
|
|
264
262
|
}
|
|
265
263
|
}
|
|
264
|
+
/**
|
|
265
|
+
* Append symbols of the given kinds into `items`/`seen`. Each kind is looked
|
|
266
|
+
* up either globally (no `container`) or container-scoped with optional
|
|
267
|
+
* inheritance. Dedup is by label against the shared `seen` set, so callers
|
|
268
|
+
* can interleave this with curated keyword / built-in emission.
|
|
269
|
+
*
|
|
270
|
+
* Order: kinds iterated in declared order; within a kind, whatever order
|
|
271
|
+
* `symbolIndex.getSymbols` returns.
|
|
272
|
+
*/
|
|
273
|
+
function appendSymbolsOfKinds(items, seen, symbolIndex, reachableFiles, opts) {
|
|
274
|
+
for (const symKind of opts.kinds) {
|
|
275
|
+
const query = opts.container !== undefined
|
|
276
|
+
? { container: opts.container, kind: symKind, inherited: opts.inherited }
|
|
277
|
+
: { kind: symKind };
|
|
278
|
+
const symbols = symbolIndex
|
|
279
|
+
.getSymbols(query)
|
|
280
|
+
.filter((s) => reachableFiles.has(path.normalize(s.file)));
|
|
281
|
+
for (const sym of symbols) {
|
|
282
|
+
if (!seen.has(sym.name)) {
|
|
283
|
+
seen.add(sym.name);
|
|
284
|
+
items.push({
|
|
285
|
+
label: sym.name,
|
|
286
|
+
kind: symbolKindToCompletionKind(symKind),
|
|
287
|
+
detail: symKind,
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Assemble a symbol-only completion list: optional built-ins followed by
|
|
295
|
+
* named symbols across the given kinds. A single dedup Set shares across
|
|
296
|
+
* built-ins and symbols so a user-defined type that shadows a built-in name
|
|
297
|
+
* (rare) doesn't appear twice.
|
|
298
|
+
*
|
|
299
|
+
* Used by every typed-prefix scope and by `association_type`. Preserves the
|
|
300
|
+
* exact emission ordering the pre-refactor branches relied on:
|
|
301
|
+
* 1. built-ins in BUILTIN_TYPES order (optionally skipping `void`)
|
|
302
|
+
* 2. symbols per-kind, kinds iterated in declared order
|
|
303
|
+
* 3. within each kind, whatever order `SymbolIndex.getSymbols` returns
|
|
304
|
+
*/
|
|
305
|
+
function buildTypeCompletionItems(symbolIndex, reachableFiles, opts) {
|
|
306
|
+
const items = [];
|
|
307
|
+
const seen = new Set();
|
|
308
|
+
if (opts.includeBuiltins) {
|
|
309
|
+
for (const typ of keywords_1.BUILTIN_TYPES) {
|
|
310
|
+
if (opts.excludeVoid && typ === "void")
|
|
311
|
+
continue;
|
|
312
|
+
if (!seen.has(typ)) {
|
|
313
|
+
seen.add(typ);
|
|
314
|
+
items.push({
|
|
315
|
+
label: typ,
|
|
316
|
+
kind: node_1.CompletionItemKind.TypeParameter,
|
|
317
|
+
detail: "built-in",
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
appendSymbolsOfKinds(items, seen, symbolIndex, reachableFiles, {
|
|
323
|
+
kinds: opts.kinds,
|
|
324
|
+
});
|
|
325
|
+
return items;
|
|
326
|
+
}
|
|
266
327
|
// ── Main builder ────────────────────────────────────────────────────────────
|
|
267
328
|
/**
|
|
268
329
|
* Build semantic completion items from a CompletionInfo + SymbolIndex.
|
|
@@ -277,15 +338,16 @@ function symbolKindToCompletionKind(kind) {
|
|
|
277
338
|
* @param symbolKinds - the normalized symbolKinds (after use_path → mixset conversion).
|
|
278
339
|
* Caller should not pass "suppress", "use_path", isComment, or isDefinitionName states.
|
|
279
340
|
*/
|
|
280
|
-
function buildSemanticCompletionItems(info, symbolKinds, symbolIndex, reachableFiles) {
|
|
281
|
-
const items = [];
|
|
282
|
-
const seen = new Set();
|
|
341
|
+
function buildSemanticCompletionItems(info, symbolKinds, symbolIndex, reachableFiles, snippetSupport = false) {
|
|
283
342
|
// Top-level scope: curated keywords only, no raw lookahead or symbols.
|
|
284
343
|
if (symbolKinds === "top_level") {
|
|
285
|
-
|
|
344
|
+
const tlItems = TOP_LEVEL_KEYWORDS.map((kw) => ({
|
|
286
345
|
label: kw,
|
|
287
346
|
kind: node_1.CompletionItemKind.Keyword,
|
|
288
347
|
}));
|
|
348
|
+
const tlSeen = new Set(TOP_LEVEL_KEYWORDS);
|
|
349
|
+
appendSnippetsForScope(tlItems, tlSeen, "top_level", snippetSupport);
|
|
350
|
+
return tlItems;
|
|
289
351
|
}
|
|
290
352
|
// Class-body scope: curated keywords + built-in types + type symbols.
|
|
291
353
|
if (symbolKinds === "class_body") {
|
|
@@ -301,21 +363,10 @@ function buildSemanticCompletionItems(info, symbolKinds, symbolIndex, reachableF
|
|
|
301
363
|
cbItems.push({ label: typ, kind: node_1.CompletionItemKind.TypeParameter, detail: "type" });
|
|
302
364
|
}
|
|
303
365
|
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
for (const sym of symbols) {
|
|
309
|
-
if (!cbSeen.has(sym.name)) {
|
|
310
|
-
cbSeen.add(sym.name);
|
|
311
|
-
cbItems.push({
|
|
312
|
-
label: sym.name,
|
|
313
|
-
kind: symbolKindToCompletionKind(symKind),
|
|
314
|
-
detail: symKind,
|
|
315
|
-
});
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
}
|
|
366
|
+
appendSymbolsOfKinds(cbItems, cbSeen, symbolIndex, reachableFiles, {
|
|
367
|
+
kinds: ["class", "interface", "trait", "enum"],
|
|
368
|
+
});
|
|
369
|
+
appendSnippetsForScope(cbItems, cbSeen, "class_body", snippetSupport);
|
|
319
370
|
return cbItems;
|
|
320
371
|
}
|
|
321
372
|
// Trait-body scope: curated keywords + built-in types + type symbols.
|
|
@@ -332,21 +383,10 @@ function buildSemanticCompletionItems(info, symbolKinds, symbolIndex, reachableF
|
|
|
332
383
|
tbItems.push({ label: typ, kind: node_1.CompletionItemKind.TypeParameter, detail: "type" });
|
|
333
384
|
}
|
|
334
385
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
for (const sym of symbols) {
|
|
340
|
-
if (!tbSeen.has(sym.name)) {
|
|
341
|
-
tbSeen.add(sym.name);
|
|
342
|
-
tbItems.push({
|
|
343
|
-
label: sym.name,
|
|
344
|
-
kind: symbolKindToCompletionKind(symKind),
|
|
345
|
-
detail: symKind,
|
|
346
|
-
});
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
}
|
|
386
|
+
appendSymbolsOfKinds(tbItems, tbSeen, symbolIndex, reachableFiles, {
|
|
387
|
+
kinds: ["class", "interface", "trait", "enum"],
|
|
388
|
+
});
|
|
389
|
+
appendSnippetsForScope(tbItems, tbSeen, "trait_body", snippetSupport);
|
|
350
390
|
return tbItems;
|
|
351
391
|
}
|
|
352
392
|
// Interface-body scope: curated keywords + built-in types + type symbols.
|
|
@@ -363,21 +403,10 @@ function buildSemanticCompletionItems(info, symbolKinds, symbolIndex, reachableF
|
|
|
363
403
|
ibItems.push({ label: typ, kind: node_1.CompletionItemKind.TypeParameter, detail: "type" });
|
|
364
404
|
}
|
|
365
405
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
for (const sym of symbols) {
|
|
371
|
-
if (!ibSeen.has(sym.name)) {
|
|
372
|
-
ibSeen.add(sym.name);
|
|
373
|
-
ibItems.push({
|
|
374
|
-
label: sym.name,
|
|
375
|
-
kind: symbolKindToCompletionKind(symKind),
|
|
376
|
-
detail: symKind,
|
|
377
|
-
});
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
}
|
|
406
|
+
appendSymbolsOfKinds(ibItems, ibSeen, symbolIndex, reachableFiles, {
|
|
407
|
+
kinds: ["class", "interface", "trait", "enum"],
|
|
408
|
+
});
|
|
409
|
+
appendSnippetsForScope(ibItems, ibSeen, "interface_body", snippetSupport);
|
|
381
410
|
return ibItems;
|
|
382
411
|
}
|
|
383
412
|
// Association-class-body scope: same as class body (shares _class_content).
|
|
@@ -394,49 +423,31 @@ function buildSemanticCompletionItems(info, symbolKinds, symbolIndex, reachableF
|
|
|
394
423
|
abItems.push({ label: typ, kind: node_1.CompletionItemKind.TypeParameter, detail: "type" });
|
|
395
424
|
}
|
|
396
425
|
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
for (const sym of symbols) {
|
|
402
|
-
if (!abSeen.has(sym.name)) {
|
|
403
|
-
abSeen.add(sym.name);
|
|
404
|
-
abItems.push({
|
|
405
|
-
label: sym.name,
|
|
406
|
-
kind: symbolKindToCompletionKind(symKind),
|
|
407
|
-
detail: symKind,
|
|
408
|
-
});
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
}
|
|
426
|
+
appendSymbolsOfKinds(abItems, abSeen, symbolIndex, reachableFiles, {
|
|
427
|
+
kinds: ["class", "interface", "trait", "enum"],
|
|
428
|
+
});
|
|
429
|
+
appendSnippetsForScope(abItems, abSeen, "assoc_class_body", snippetSupport);
|
|
412
430
|
return abItems;
|
|
413
431
|
}
|
|
414
432
|
// Filter-body scope: curated filter-statement starters only.
|
|
415
433
|
if (symbolKinds === "filter_body") {
|
|
416
|
-
|
|
434
|
+
const fbItems = FILTER_BODY_KEYWORDS.map((kw) => ({
|
|
417
435
|
label: kw,
|
|
418
436
|
kind: node_1.CompletionItemKind.Keyword,
|
|
419
437
|
}));
|
|
438
|
+
const fbSeen = new Set(FILTER_BODY_KEYWORDS);
|
|
439
|
+
appendSnippetsForScope(fbItems, fbSeen, "filter_body", snippetSupport);
|
|
440
|
+
return fbItems;
|
|
420
441
|
}
|
|
421
442
|
// Trace entity scope: scoped attrs/methods only, no raw keywords.
|
|
422
443
|
if (symbolKinds === "trace_attribute_method" && info.enclosingClass) {
|
|
423
444
|
const trItems = [];
|
|
424
445
|
const trSeen = new Set();
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
if (!trSeen.has(sym.name)) {
|
|
431
|
-
trSeen.add(sym.name);
|
|
432
|
-
trItems.push({
|
|
433
|
-
label: sym.name,
|
|
434
|
-
kind: symbolKindToCompletionKind(kind),
|
|
435
|
-
detail: kind,
|
|
436
|
-
});
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
}
|
|
446
|
+
appendSymbolsOfKinds(trItems, trSeen, symbolIndex, reachableFiles, {
|
|
447
|
+
kinds: ["attribute", "method"],
|
|
448
|
+
container: info.enclosingClass,
|
|
449
|
+
inherited: true,
|
|
450
|
+
});
|
|
440
451
|
return trItems;
|
|
441
452
|
}
|
|
442
453
|
// Guard scope: scoped attrs/methods + boolean literals only, no raw keywords.
|
|
@@ -449,23 +460,36 @@ function buildSemanticCompletionItems(info, symbolKinds, symbolIndex, reachableF
|
|
|
449
460
|
gItems.push({ label: lit, kind: node_1.CompletionItemKind.Keyword });
|
|
450
461
|
}
|
|
451
462
|
// Scoped attrs + methods from enclosing class
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
if (!gSeen.has(sym.name)) {
|
|
458
|
-
gSeen.add(sym.name);
|
|
459
|
-
gItems.push({
|
|
460
|
-
label: sym.name,
|
|
461
|
-
kind: symbolKindToCompletionKind(kind),
|
|
462
|
-
detail: kind,
|
|
463
|
-
});
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
}
|
|
463
|
+
appendSymbolsOfKinds(gItems, gSeen, symbolIndex, reachableFiles, {
|
|
464
|
+
kinds: ["attribute", "method"],
|
|
465
|
+
container: info.enclosingClass,
|
|
466
|
+
inherited: true,
|
|
467
|
+
});
|
|
467
468
|
return gItems;
|
|
468
469
|
}
|
|
470
|
+
// Constraint scope `[...]`: own attributes only (Umple E28 — no inherited
|
|
471
|
+
// attrs in constraints). Symbol-only; no raw lookahead, no operators.
|
|
472
|
+
if (symbolKinds === "own_attribute" && info.enclosingClass) {
|
|
473
|
+
const oaItems = [];
|
|
474
|
+
const oaSeen = new Set();
|
|
475
|
+
appendSymbolsOfKinds(oaItems, oaSeen, symbolIndex, reachableFiles, {
|
|
476
|
+
kinds: ["attribute"],
|
|
477
|
+
container: info.enclosingClass,
|
|
478
|
+
});
|
|
479
|
+
return oaItems;
|
|
480
|
+
}
|
|
481
|
+
// Sorted-key scope `sorted{...}`: attributes of the owner class with
|
|
482
|
+
// inheritance. Symbol-only; no raw lookahead, no operators.
|
|
483
|
+
if (symbolKinds === "sorted_attribute" && info.sortedKeyOwner) {
|
|
484
|
+
const skItems = [];
|
|
485
|
+
const skSeen = new Set();
|
|
486
|
+
appendSymbolsOfKinds(skItems, skSeen, symbolIndex, reachableFiles, {
|
|
487
|
+
kinds: ["attribute"],
|
|
488
|
+
container: info.sortedKeyOwner,
|
|
489
|
+
inherited: true,
|
|
490
|
+
});
|
|
491
|
+
return skItems;
|
|
492
|
+
}
|
|
469
493
|
// Transition-target scope: state symbols only, no keywords.
|
|
470
494
|
if (symbolKinds === "transition_target") {
|
|
471
495
|
const ttItems = [];
|
|
@@ -482,19 +506,10 @@ function buildSemanticCompletionItems(info, symbolKinds, symbolIndex, reachableF
|
|
|
482
506
|
}
|
|
483
507
|
}
|
|
484
508
|
else {
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
if (!ttSeen.has(sym.name)) {
|
|
490
|
-
ttSeen.add(sym.name);
|
|
491
|
-
ttItems.push({
|
|
492
|
-
label: sym.name,
|
|
493
|
-
kind: symbolKindToCompletionKind("state"),
|
|
494
|
-
detail: "state",
|
|
495
|
-
});
|
|
496
|
-
}
|
|
497
|
-
}
|
|
509
|
+
appendSymbolsOfKinds(ttItems, ttSeen, symbolIndex, reachableFiles, {
|
|
510
|
+
kinds: ["state"],
|
|
511
|
+
container: info.enclosingStateMachine,
|
|
512
|
+
});
|
|
498
513
|
}
|
|
499
514
|
}
|
|
500
515
|
return ttItems;
|
|
@@ -513,21 +528,9 @@ function buildSemanticCompletionItems(info, symbolKinds, symbolIndex, reachableF
|
|
|
513
528
|
mbItems.push({ label: typ, kind: node_1.CompletionItemKind.TypeParameter, detail: "type" });
|
|
514
529
|
}
|
|
515
530
|
}
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
.filter((s) => reachableFiles.has(path.normalize(s.file)));
|
|
520
|
-
for (const sym of symbols) {
|
|
521
|
-
if (!mbSeen.has(sym.name)) {
|
|
522
|
-
mbSeen.add(sym.name);
|
|
523
|
-
mbItems.push({
|
|
524
|
-
label: sym.name,
|
|
525
|
-
kind: symbolKindToCompletionKind(symKind),
|
|
526
|
-
detail: symKind,
|
|
527
|
-
});
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
+
appendSymbolsOfKinds(mbItems, mbSeen, symbolIndex, reachableFiles, {
|
|
532
|
+
kinds: ["class", "interface", "trait", "enum"],
|
|
533
|
+
});
|
|
531
534
|
return mbItems;
|
|
532
535
|
}
|
|
533
536
|
// Statemachine-body scope: curated keywords + state symbols from enclosing SM.
|
|
@@ -540,20 +543,12 @@ function buildSemanticCompletionItems(info, symbolKinds, symbolIndex, reachableF
|
|
|
540
543
|
}
|
|
541
544
|
// Offer existing state names from the enclosing SM
|
|
542
545
|
if (info.enclosingStateMachine) {
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
if (!smSeen.has(sym.name)) {
|
|
548
|
-
smSeen.add(sym.name);
|
|
549
|
-
smItems.push({
|
|
550
|
-
label: sym.name,
|
|
551
|
-
kind: symbolKindToCompletionKind("state"),
|
|
552
|
-
detail: "state",
|
|
553
|
-
});
|
|
554
|
-
}
|
|
555
|
-
}
|
|
546
|
+
appendSymbolsOfKinds(smItems, smSeen, symbolIndex, reachableFiles, {
|
|
547
|
+
kinds: ["state"],
|
|
548
|
+
container: info.enclosingStateMachine,
|
|
549
|
+
});
|
|
556
550
|
}
|
|
551
|
+
appendSnippetsForScope(smItems, smSeen, "statemachine_body", snippetSupport);
|
|
557
552
|
return smItems;
|
|
558
553
|
}
|
|
559
554
|
// State-body scope: curated keywords + state symbols + built-in types for methods.
|
|
@@ -573,20 +568,12 @@ function buildSemanticCompletionItems(info, symbolKinds, symbolIndex, reachableF
|
|
|
573
568
|
}
|
|
574
569
|
// Offer state names from enclosing SM (for nested state references)
|
|
575
570
|
if (info.enclosingStateMachine) {
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
if (!sbSeen.has(sym.name)) {
|
|
581
|
-
sbSeen.add(sym.name);
|
|
582
|
-
sbItems.push({
|
|
583
|
-
label: sym.name,
|
|
584
|
-
kind: symbolKindToCompletionKind("state"),
|
|
585
|
-
detail: "state",
|
|
586
|
-
});
|
|
587
|
-
}
|
|
588
|
-
}
|
|
571
|
+
appendSymbolsOfKinds(sbItems, sbSeen, symbolIndex, reachableFiles, {
|
|
572
|
+
kinds: ["state"],
|
|
573
|
+
container: info.enclosingStateMachine,
|
|
574
|
+
});
|
|
589
575
|
}
|
|
576
|
+
appendSnippetsForScope(sbItems, sbSeen, "state_body", snippetSupport);
|
|
590
577
|
return sbItems;
|
|
591
578
|
}
|
|
592
579
|
// Trait SM op contexts are symbol-only — no keywords or operators.
|
|
@@ -617,21 +604,209 @@ function buildSemanticCompletionItems(info, symbolKinds, symbolIndex, reachableF
|
|
|
617
604
|
if (symbolKinds === "trace_method" && info.enclosingClass) {
|
|
618
605
|
const items = [];
|
|
619
606
|
const seen = new Set();
|
|
620
|
-
|
|
621
|
-
|
|
607
|
+
appendSymbolsOfKinds(items, seen, symbolIndex, reachableFiles, {
|
|
608
|
+
kinds: ["method"],
|
|
609
|
+
container: info.enclosingClass,
|
|
610
|
+
inherited: true,
|
|
611
|
+
});
|
|
612
|
+
return items;
|
|
613
|
+
}
|
|
614
|
+
if (symbolKinds === "trace_event" && info.enclosingClass) {
|
|
615
|
+
const items = [];
|
|
616
|
+
const seen = new Set();
|
|
617
|
+
appendSymbolsOfKinds(items, seen, symbolIndex, reachableFiles, {
|
|
618
|
+
kinds: ["event"],
|
|
619
|
+
container: info.enclosingClass,
|
|
620
|
+
inherited: true,
|
|
621
|
+
});
|
|
622
|
+
return items;
|
|
623
|
+
}
|
|
624
|
+
// Topic 052 item 2 — `before |` / `after |` / `before p|` / `after p|`
|
|
625
|
+
// code-injection slot. Method symbols of the enclosing class with
|
|
626
|
+
// inheritance, no built-ins, no LookaheadIterator keywords. Same shape
|
|
627
|
+
// as trace_method but kept on a distinct scalar for semantic clarity
|
|
628
|
+
// (and to leave room for `around` etc. later).
|
|
629
|
+
if (symbolKinds === "code_injection_method" && info.enclosingClass) {
|
|
630
|
+
const items = [];
|
|
631
|
+
const seen = new Set();
|
|
632
|
+
appendSymbolsOfKinds(items, seen, symbolIndex, reachableFiles, {
|
|
633
|
+
kinds: ["method"],
|
|
634
|
+
container: info.enclosingClass,
|
|
635
|
+
inherited: true,
|
|
636
|
+
});
|
|
637
|
+
return items;
|
|
638
|
+
}
|
|
639
|
+
// Topic 052 item 3 — `filter { include ... }` class-target slot. Class
|
|
640
|
+
// symbols only — no built-ins, no `void`, no LookaheadIterator
|
|
641
|
+
// keywords. Covers blank `include |` and typed `include S|`.
|
|
642
|
+
if (symbolKinds === "filter_include_target") {
|
|
643
|
+
const items = [];
|
|
644
|
+
const seen = new Set();
|
|
645
|
+
appendSymbolsOfKinds(items, seen, symbolIndex, reachableFiles, {
|
|
646
|
+
kinds: ["class"],
|
|
647
|
+
});
|
|
648
|
+
return items;
|
|
649
|
+
}
|
|
650
|
+
// Topic 055 — `class C { sm name as |` (referenced_statemachine target).
|
|
651
|
+
// Offer SMs valid for class-local reuse: class-local statemachines from the
|
|
652
|
+
// enclosing class plus top-level standalone statemachine_definitions
|
|
653
|
+
// reachable from this file. No keywords, operators, types.
|
|
654
|
+
if (symbolKinds === "referenced_sm_target") {
|
|
655
|
+
const items = [];
|
|
656
|
+
const seen = new Set();
|
|
657
|
+
if (info.enclosingClass) {
|
|
658
|
+
// Class-local SMs use the convention `<className>.<smName>` for their
|
|
659
|
+
// container; symbol container === `${enclosingClass}.${name}`.
|
|
660
|
+
const local = symbolIndex
|
|
661
|
+
.getSymbols({ kind: "statemachine" })
|
|
662
|
+
.filter((s) => s.container === `${info.enclosingClass}.${s.name}`)
|
|
663
|
+
.filter((s) => reachableFiles.has(path.normalize(s.file)));
|
|
664
|
+
for (const sym of local) {
|
|
665
|
+
if (!seen.has(sym.name)) {
|
|
666
|
+
seen.add(sym.name);
|
|
667
|
+
items.push({
|
|
668
|
+
label: sym.name,
|
|
669
|
+
kind: symbolKindToCompletionKind("statemachine"),
|
|
670
|
+
detail: "statemachine",
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
// Top-level standalone statemachines: container === name (no class prefix).
|
|
676
|
+
const topLevel = symbolIndex
|
|
677
|
+
.getSymbols({ kind: "statemachine" })
|
|
678
|
+
.filter((s) => s.container === s.name)
|
|
622
679
|
.filter((s) => reachableFiles.has(path.normalize(s.file)));
|
|
623
|
-
for (const sym of
|
|
680
|
+
for (const sym of topLevel) {
|
|
681
|
+
if (!seen.has(sym.name)) {
|
|
682
|
+
seen.add(sym.name);
|
|
683
|
+
items.push({
|
|
684
|
+
label: sym.name,
|
|
685
|
+
kind: symbolKindToCompletionKind("statemachine"),
|
|
686
|
+
detail: "top-level statemachine",
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
return items;
|
|
691
|
+
}
|
|
692
|
+
// Topic 055 — `isA T<sm as |...` first-segment slot. Same SM universe as
|
|
693
|
+
// referenced_sm_target but kept separate so future divergence (e.g., trait
|
|
694
|
+
// type parameter constraints) is easy to contain.
|
|
695
|
+
if (symbolKinds === "trait_sm_binding_target") {
|
|
696
|
+
const items = [];
|
|
697
|
+
const seen = new Set();
|
|
698
|
+
if (info.enclosingClass) {
|
|
699
|
+
const local = symbolIndex
|
|
700
|
+
.getSymbols({ kind: "statemachine" })
|
|
701
|
+
.filter((s) => s.container === `${info.enclosingClass}.${s.name}`)
|
|
702
|
+
.filter((s) => reachableFiles.has(path.normalize(s.file)));
|
|
703
|
+
for (const sym of local) {
|
|
704
|
+
if (!seen.has(sym.name)) {
|
|
705
|
+
seen.add(sym.name);
|
|
706
|
+
items.push({
|
|
707
|
+
label: sym.name,
|
|
708
|
+
kind: symbolKindToCompletionKind("statemachine"),
|
|
709
|
+
detail: "statemachine",
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
const topLevel = symbolIndex
|
|
715
|
+
.getSymbols({ kind: "statemachine" })
|
|
716
|
+
.filter((s) => s.container === s.name)
|
|
717
|
+
.filter((s) => reachableFiles.has(path.normalize(s.file)));
|
|
718
|
+
for (const sym of topLevel) {
|
|
624
719
|
if (!seen.has(sym.name)) {
|
|
625
720
|
seen.add(sym.name);
|
|
626
|
-
items.push({
|
|
721
|
+
items.push({
|
|
722
|
+
label: sym.name,
|
|
723
|
+
kind: symbolKindToCompletionKind("statemachine"),
|
|
724
|
+
detail: "top-level statemachine",
|
|
725
|
+
});
|
|
627
726
|
}
|
|
628
727
|
}
|
|
629
728
|
return items;
|
|
630
729
|
}
|
|
730
|
+
// Topic 055 — `isA T<sm as <smName>(.<seg>)*.|...` dotted-state continuation.
|
|
731
|
+
// The analyzer captures (smName, statePrefix) from the in-progress
|
|
732
|
+
// qualified_name. Resolve smContainer as class-local first
|
|
733
|
+
// (`${enclosingClass}.${smName}`), falling back to the top-level standalone
|
|
734
|
+
// statemachine container (`${smName}` self-container). When the state
|
|
735
|
+
// prefix is empty, offer depth-1 states of the SM root; otherwise descend
|
|
736
|
+
// via `getChildStateNames` so nested paths like `Sm.S1.|` surface S1's
|
|
737
|
+
// direct children.
|
|
738
|
+
if (symbolKinds === "trait_sm_binding_state_target") {
|
|
739
|
+
const items = [];
|
|
740
|
+
const seen = new Set();
|
|
741
|
+
const enclosingClass = info.enclosingClass;
|
|
742
|
+
const smName = info.traitSmBindingSmName;
|
|
743
|
+
const statePrefix = info.traitSmBindingStatePrefix ?? [];
|
|
744
|
+
if (smName) {
|
|
745
|
+
const candidates = [];
|
|
746
|
+
if (enclosingClass)
|
|
747
|
+
candidates.push(`${enclosingClass}.${smName}`);
|
|
748
|
+
candidates.push(smName); // top-level standalone fallback
|
|
749
|
+
let smContainer;
|
|
750
|
+
for (const cand of candidates) {
|
|
751
|
+
const exists = symbolIndex
|
|
752
|
+
.getSymbols({ container: cand, kind: "statemachine", name: smName })
|
|
753
|
+
.some((s) => reachableFiles.has(path.normalize(s.file)));
|
|
754
|
+
if (exists) {
|
|
755
|
+
smContainer = cand;
|
|
756
|
+
break;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
if (smContainer) {
|
|
760
|
+
if (statePrefix.length === 0) {
|
|
761
|
+
// Top-level states of the SM (depth 1).
|
|
762
|
+
const directStates = symbolIndex
|
|
763
|
+
.getSymbols({ container: smContainer, kind: "state" })
|
|
764
|
+
.filter((s) => reachableFiles.has(path.normalize(s.file)))
|
|
765
|
+
.filter((s) => (s.statePath?.length ?? 0) === 1);
|
|
766
|
+
for (const sym of directStates) {
|
|
767
|
+
if (!seen.has(sym.name)) {
|
|
768
|
+
seen.add(sym.name);
|
|
769
|
+
items.push({
|
|
770
|
+
label: sym.name,
|
|
771
|
+
kind: symbolKindToCompletionKind("state"),
|
|
772
|
+
detail: "state",
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
else {
|
|
778
|
+
// Nested states under the prefix (e.g., Sm.S1.|).
|
|
779
|
+
const childNames = symbolIndex.getChildStateNames(statePrefix, smContainer, reachableFiles);
|
|
780
|
+
for (const name of childNames) {
|
|
781
|
+
if (!seen.has(name)) {
|
|
782
|
+
seen.add(name);
|
|
783
|
+
items.push({
|
|
784
|
+
label: name,
|
|
785
|
+
kind: symbolKindToCompletionKind("state"),
|
|
786
|
+
detail: "state",
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
return items;
|
|
794
|
+
}
|
|
795
|
+
// Topic 052 item 4 — method parameter-type slot. Built-ins (excluding
|
|
796
|
+
// `void`) + class / interface / trait / enum. Same shape as
|
|
797
|
+
// decl_type_typed_prefix but on a distinct scalar so divergence is
|
|
798
|
+
// contained.
|
|
799
|
+
if (symbolKinds === "param_type_typed_prefix") {
|
|
800
|
+
return buildTypeCompletionItems(symbolIndex, reachableFiles, {
|
|
801
|
+
kinds: ["class", "interface", "trait", "enum"],
|
|
802
|
+
includeBuiltins: true,
|
|
803
|
+
excludeVoid: true,
|
|
804
|
+
});
|
|
805
|
+
}
|
|
631
806
|
if (symbolKinds === "trace_state_method" && info.enclosingClass) {
|
|
632
807
|
const items = [];
|
|
633
808
|
const seen = new Set();
|
|
634
|
-
// States from all SMs in the enclosing class
|
|
809
|
+
// States from all SMs in the enclosing class — pattern C, specialized.
|
|
635
810
|
const states = symbolIndex
|
|
636
811
|
.getSymbols({ kind: "state" })
|
|
637
812
|
.filter((s) => s.container?.startsWith(info.enclosingClass + "."))
|
|
@@ -643,37 +818,42 @@ function buildSemanticCompletionItems(info, symbolKinds, symbolIndex, reachableF
|
|
|
643
818
|
}
|
|
644
819
|
}
|
|
645
820
|
// Methods from the enclosing class
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
seen.add(sym.name);
|
|
652
|
-
items.push({ label: sym.name, kind: symbolKindToCompletionKind("method"), detail: "method" });
|
|
653
|
-
}
|
|
654
|
-
}
|
|
821
|
+
appendSymbolsOfKinds(items, seen, symbolIndex, reachableFiles, {
|
|
822
|
+
kinds: ["method"],
|
|
823
|
+
container: info.enclosingClass,
|
|
824
|
+
inherited: true,
|
|
825
|
+
});
|
|
655
826
|
return items;
|
|
656
827
|
}
|
|
657
828
|
if (symbolKinds === "trace_attribute" && info.enclosingClass) {
|
|
658
829
|
const items = [];
|
|
659
830
|
const seen = new Set();
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
seen.add(sym.name);
|
|
666
|
-
items.push({ label: sym.name, kind: symbolKindToCompletionKind("attribute"), detail: "attribute" });
|
|
667
|
-
}
|
|
668
|
-
}
|
|
831
|
+
appendSymbolsOfKinds(items, seen, symbolIndex, reachableFiles, {
|
|
832
|
+
kinds: ["attribute"],
|
|
833
|
+
container: info.enclosingClass,
|
|
834
|
+
inherited: true,
|
|
835
|
+
});
|
|
669
836
|
return items;
|
|
670
837
|
}
|
|
671
838
|
// Structured userStory body — curated tag starters only.
|
|
672
839
|
if (symbolKinds === "userstory_body") {
|
|
673
|
-
|
|
840
|
+
const usItems = USER_STORY_BODY_STARTERS.map((kw) => ({
|
|
674
841
|
label: kw,
|
|
675
842
|
kind: node_1.CompletionItemKind.Keyword,
|
|
676
843
|
}));
|
|
844
|
+
const usSeen = new Set(USER_STORY_BODY_STARTERS);
|
|
845
|
+
appendSnippetsForScope(usItems, usSeen, "userstory_body", snippetSupport);
|
|
846
|
+
return usItems;
|
|
847
|
+
}
|
|
848
|
+
// Partial inline association — arrow slot after the left multiplicity
|
|
849
|
+
// (e.g. `1 |`, `1 -|`, `1 <|`). Curated arrow operators only — no
|
|
850
|
+
// multiplicity, no type symbols, no class-body keywords.
|
|
851
|
+
if (symbolKinds === "association_arrow") {
|
|
852
|
+
return ASSOCIATION_ARROW_STARTERS.map((a) => ({
|
|
853
|
+
label: a,
|
|
854
|
+
kind: node_1.CompletionItemKind.Operator,
|
|
855
|
+
detail: "association arrow",
|
|
856
|
+
}));
|
|
677
857
|
}
|
|
678
858
|
// Partial inline association — right-multiplicity slot after the arrow
|
|
679
859
|
// (e.g. `1 -> |`). Curated multiplicities only; no class-body junk.
|
|
@@ -687,22 +867,9 @@ function buildSemanticCompletionItems(info, symbolKinds, symbolIndex, reachableF
|
|
|
687
867
|
// Partial inline association — right-type slot after the right multiplicity
|
|
688
868
|
// (e.g. `1 -> * |`). Offer class symbols only, no keywords.
|
|
689
869
|
if (symbolKinds === "association_type") {
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
.getSymbols({ kind: "class" })
|
|
694
|
-
.filter((s) => reachableFiles.has(path.normalize(s.file)));
|
|
695
|
-
for (const sym of classes) {
|
|
696
|
-
if (!atSeen.has(sym.name)) {
|
|
697
|
-
atSeen.add(sym.name);
|
|
698
|
-
atItems.push({
|
|
699
|
-
label: sym.name,
|
|
700
|
-
kind: symbolKindToCompletionKind("class"),
|
|
701
|
-
detail: "class",
|
|
702
|
-
});
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
return atItems;
|
|
870
|
+
return buildTypeCompletionItems(symbolIndex, reachableFiles, {
|
|
871
|
+
kinds: ["class"],
|
|
872
|
+
});
|
|
706
873
|
}
|
|
707
874
|
// Typed-prefix on the right_type identifier (e.g. `1 -> * O|`). Once the
|
|
708
875
|
// parser commits to a complete `association_inline` / `association_member`,
|
|
@@ -710,31 +877,54 @@ function buildSemanticCompletionItems(info, symbolKinds, symbolIndex, reachableF
|
|
|
710
877
|
// raw LookaheadIterator keyword junk; this narrower scope returns just the
|
|
711
878
|
// legitimate type symbols (class / interface / trait) without keywords.
|
|
712
879
|
if (symbolKinds === "association_typed_prefix") {
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
880
|
+
return buildTypeCompletionItems(symbolIndex, reachableFiles, {
|
|
881
|
+
kinds: ["class", "interface", "trait"],
|
|
882
|
+
});
|
|
883
|
+
}
|
|
884
|
+
// Typed-prefix on the isa_declaration type identifier (topic 047 item 1).
|
|
885
|
+
// Same motivation as association_typed_prefix above: the generic
|
|
886
|
+
// (isa_declaration) @scope.class_interface_trait capture pulls in raw
|
|
887
|
+
// LookaheadIterator keyword junk when recovering from incomplete parses.
|
|
888
|
+
// This narrower scope returns only class / interface / trait symbols.
|
|
889
|
+
if (symbolKinds === "isa_typed_prefix") {
|
|
890
|
+
return buildTypeCompletionItems(symbolIndex, reachableFiles, {
|
|
891
|
+
kinds: ["class", "interface", "trait"],
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
// Typed-prefix on attribute/const declaration type identifier (topic 047
|
|
895
|
+
// item 2). Cursor sits inside a type_name under attribute_declaration or
|
|
896
|
+
// const_declaration. Default class_body scope leaks 54+ LookaheadIterator
|
|
897
|
+
// keywords (test, generic, class, isA, trace, ...) and surfaces zero type
|
|
898
|
+
// symbols. Symbol-only early-return: built-in types + class / interface /
|
|
899
|
+
// trait / enum. `void` is deliberately excluded — method-return only.
|
|
900
|
+
if (symbolKinds === "decl_type_typed_prefix") {
|
|
901
|
+
return buildTypeCompletionItems(symbolIndex, reachableFiles, {
|
|
902
|
+
kinds: ["class", "interface", "trait", "enum"],
|
|
903
|
+
includeBuiltins: true,
|
|
904
|
+
excludeVoid: true,
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
// Typed-prefix on method return-type identifier (topic 047 item 3).
|
|
908
|
+
// Same shape as decl_type_typed_prefix, but fires on method_declaration /
|
|
909
|
+
// abstract_method_declaration / method_signature / trait_method_signature
|
|
910
|
+
// and KEEPS `void`. Parameter types sit under `param` (not a method rule)
|
|
911
|
+
// and are excluded by detection, so they fall through to the class_body
|
|
912
|
+
// path and remain untouched by this branch.
|
|
913
|
+
if (symbolKinds === "return_type_typed_prefix") {
|
|
914
|
+
return buildTypeCompletionItems(symbolIndex, reachableFiles, {
|
|
915
|
+
kinds: ["class", "interface", "trait", "enum"],
|
|
916
|
+
includeBuiltins: true,
|
|
917
|
+
});
|
|
731
918
|
}
|
|
732
919
|
// Structured useCase body — userStoryTags plus useCaseStep starters.
|
|
733
920
|
if (symbolKinds === "usecase_body") {
|
|
734
|
-
|
|
921
|
+
const ucItems = USE_CASE_BODY_STARTERS.map((kw) => ({
|
|
735
922
|
label: kw,
|
|
736
923
|
kind: node_1.CompletionItemKind.Keyword,
|
|
737
924
|
}));
|
|
925
|
+
const ucSeen = new Set(USE_CASE_BODY_STARTERS);
|
|
926
|
+
appendSnippetsForScope(ucItems, ucSeen, "usecase_body", snippetSupport);
|
|
927
|
+
return ucItems;
|
|
738
928
|
}
|
|
739
929
|
// implementsReq scope — symbol-only, no keywords/operators. The req_implementation
|
|
740
930
|
// grammar rule only permits `implementsReq <id>(, <id>)* ;`, so surfacing class /
|
|
@@ -757,33 +947,42 @@ function buildSemanticCompletionItems(info, symbolKinds, symbolIndex, reachableF
|
|
|
757
947
|
}
|
|
758
948
|
return reqItems;
|
|
759
949
|
}
|
|
760
|
-
//
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
950
|
+
// Every specialized / curated scope has early-returned above. Everything
|
|
951
|
+
// else falls through to the raw-lookahead path — it is the ONLY place in
|
|
952
|
+
// this builder where `info.keywords` (LookaheadIterator output) reaches
|
|
953
|
+
// the user. See `buildLookaheadFallbackItems` for the contract.
|
|
954
|
+
return buildLookaheadFallbackItems(info, symbolKinds, symbolIndex, reachableFiles);
|
|
955
|
+
}
|
|
956
|
+
/**
|
|
957
|
+
* The raw-lookahead fallback path for `buildSemanticCompletionItems`.
|
|
958
|
+
*
|
|
959
|
+
* After topic 049 phase 2 this is reached only when `symbolKinds` is an
|
|
960
|
+
* array (`SymbolKind[]`) — every scalar scope has its own early-return
|
|
961
|
+
* branch above. This is the ONLY place in the builder where raw
|
|
962
|
+
* `info.keywords` (LookaheadIterator output) is surfaced to the user, and
|
|
963
|
+
* it is now array-only: no scalar scope surfaces raw lookahead anywhere.
|
|
964
|
+
*
|
|
965
|
+
* If you add a new scalar scope, route it via an early-return in
|
|
966
|
+
* `buildSemanticCompletionItems`, not through this path.
|
|
967
|
+
*/
|
|
968
|
+
function buildLookaheadFallbackItems(info, symbolKinds, symbolIndex, reachableFiles) {
|
|
969
|
+
const items = [];
|
|
970
|
+
const seen = new Set();
|
|
971
|
+
// 1. Keywords from LookaheadIterator.
|
|
972
|
+
for (const kw of info.keywords) {
|
|
769
973
|
if (!seen.has(kw)) {
|
|
770
974
|
seen.add(kw);
|
|
771
975
|
items.push({ label: kw, kind: node_1.CompletionItemKind.Keyword });
|
|
772
976
|
}
|
|
773
977
|
}
|
|
774
|
-
// 2. Operators
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
for (const op of info.operators) {
|
|
780
|
-
if (!seen.has(op)) {
|
|
781
|
-
seen.add(op);
|
|
782
|
-
items.push({ label: op, kind: node_1.CompletionItemKind.Operator });
|
|
783
|
-
}
|
|
978
|
+
// 2. Operators.
|
|
979
|
+
for (const op of info.operators) {
|
|
980
|
+
if (!seen.has(op)) {
|
|
981
|
+
seen.add(op);
|
|
982
|
+
items.push({ label: op, kind: node_1.CompletionItemKind.Operator });
|
|
784
983
|
}
|
|
785
984
|
}
|
|
786
|
-
// 3. Built-in types (when in type-compatible scope)
|
|
985
|
+
// 3. Built-in types (when in type-compatible scope).
|
|
787
986
|
if (Array.isArray(symbolKinds) &&
|
|
788
987
|
symbolKinds.some((k) => ["class", "interface", "trait", "enum"].includes(k))) {
|
|
789
988
|
for (const typ of keywords_1.BUILTIN_TYPES) {
|
|
@@ -797,62 +996,7 @@ function buildSemanticCompletionItems(info, symbolKinds, symbolIndex, reachableF
|
|
|
797
996
|
}
|
|
798
997
|
}
|
|
799
998
|
}
|
|
800
|
-
// 4.
|
|
801
|
-
if (symbolKinds === "own_attribute" && info.enclosingClass) {
|
|
802
|
-
const symbols = symbolIndex
|
|
803
|
-
.getSymbols({ container: info.enclosingClass, kind: "attribute" })
|
|
804
|
-
.filter((s) => reachableFiles.has(path.normalize(s.file)));
|
|
805
|
-
for (const sym of symbols) {
|
|
806
|
-
if (!seen.has(sym.name)) {
|
|
807
|
-
seen.add(sym.name);
|
|
808
|
-
items.push({
|
|
809
|
-
label: sym.name,
|
|
810
|
-
kind: symbolKindToCompletionKind("attribute"),
|
|
811
|
-
detail: "attribute",
|
|
812
|
-
});
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
return items;
|
|
816
|
-
}
|
|
817
|
-
// 5. Sorted key scope: attributes of the owner class (with inheritance)
|
|
818
|
-
if (symbolKinds === "sorted_attribute" && info.sortedKeyOwner) {
|
|
819
|
-
const symbols = symbolIndex
|
|
820
|
-
.getSymbols({ container: info.sortedKeyOwner, kind: "attribute", inherited: true })
|
|
821
|
-
.filter((s) => reachableFiles.has(path.normalize(s.file)));
|
|
822
|
-
for (const sym of symbols) {
|
|
823
|
-
if (!seen.has(sym.name)) {
|
|
824
|
-
seen.add(sym.name);
|
|
825
|
-
items.push({
|
|
826
|
-
label: sym.name,
|
|
827
|
-
kind: symbolKindToCompletionKind("attribute"),
|
|
828
|
-
detail: "attribute",
|
|
829
|
-
});
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
return items;
|
|
833
|
-
}
|
|
834
|
-
// 6. Guard/trace scope: attributes + methods from enclosing class (with inheritance)
|
|
835
|
-
if ((symbolKinds === "guard_attribute_method" ||
|
|
836
|
-
symbolKinds === "trace_attribute_method") &&
|
|
837
|
-
info.enclosingClass) {
|
|
838
|
-
for (const kind of ["attribute", "method"]) {
|
|
839
|
-
const symbols = symbolIndex
|
|
840
|
-
.getSymbols({ container: info.enclosingClass, kind, inherited: true })
|
|
841
|
-
.filter((s) => reachableFiles.has(path.normalize(s.file)));
|
|
842
|
-
for (const sym of symbols) {
|
|
843
|
-
if (!seen.has(sym.name)) {
|
|
844
|
-
seen.add(sym.name);
|
|
845
|
-
items.push({
|
|
846
|
-
label: sym.name,
|
|
847
|
-
kind: symbolKindToCompletionKind(kind),
|
|
848
|
-
detail: kind,
|
|
849
|
-
});
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
return items;
|
|
854
|
-
}
|
|
855
|
-
// 6. Symbol completions from index (scoped to reachable files)
|
|
999
|
+
// 4. Symbol completions from index (scoped to reachable files).
|
|
856
1000
|
if (Array.isArray(symbolKinds)) {
|
|
857
1001
|
for (const symKind of symbolKinds) {
|
|
858
1002
|
let symbols;
|