@xano/xanoscript-language-server 11.9.1 → 11.10.1
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/.claude/memory/feedback_no_git.md +11 -0
- package/.claude/settings.local.json +0 -5
- package/cache/documentCache.js +19 -2
- package/language-server-worker.js +2 -1
- package/onCompletion/onCompletion.js +24 -3
- package/onDidChangeContent/onDidChangeContent.js +30 -6
- package/onHover/onHoverDocument.js +44 -1
- package/onHover/onHoverDocument.spec.js +174 -0
- package/onSemanticCheck/highlight.js +45 -4
- package/onSemanticCheck/onSemanticCheck.js +14 -3
- package/package.json +1 -1
- package/parser/multidoc.js +273 -0
- package/parser/multidoc.spec.js +940 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: no-git-interactions
|
|
3
|
+
description: User does not want Claude to interact with git (no commits, no staging, no git commands)
|
|
4
|
+
type: feedback
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Do not interact with git — no commits, no staging, no git commands. Focus purely on implementation and testing.
|
|
8
|
+
|
|
9
|
+
**Why:** User manages their own git workflow.
|
|
10
|
+
|
|
11
|
+
**How to apply:** Skip all git-related steps in plans. When dispatching subagents, explicitly tell them not to run any git commands. Remove commit steps from task instructions.
|
|
@@ -2,18 +2,13 @@
|
|
|
2
2
|
"permissions": {
|
|
3
3
|
"allow": [
|
|
4
4
|
"Bash(npm test:*)",
|
|
5
|
-
"Bash(node -e:*)",
|
|
6
|
-
"Bash(node --input-type=module -e:*)",
|
|
7
5
|
"Bash(git checkout:*)",
|
|
8
6
|
"Bash(ls:*)",
|
|
9
7
|
"Bash(npm run lint:*)",
|
|
10
8
|
"Bash(grep:*)",
|
|
11
|
-
"Bash(node --input-type=module:*)",
|
|
12
9
|
"Bash(find:*)",
|
|
13
10
|
"Read(//tmp/**)",
|
|
14
11
|
"Bash(git stash:*)",
|
|
15
|
-
"Bash(node:*)",
|
|
16
|
-
"Bash(for:*)",
|
|
17
12
|
"Bash(do)",
|
|
18
13
|
"Bash(if ! grep -q \"guidFieldAttribute\" \"$f\")",
|
|
19
14
|
"Bash(then)",
|
package/cache/documentCache.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { lexDocument } from "../lexer/lexer.js";
|
|
2
|
+
import { multidocParser } from "../parser/multidoc.js";
|
|
2
3
|
import { xanoscriptParser } from "../parser/parser.js";
|
|
3
4
|
import { getSchemeFromContent } from "../utils.js";
|
|
4
5
|
|
|
@@ -34,14 +35,30 @@ class DocumentCache {
|
|
|
34
35
|
cached.version === version &&
|
|
35
36
|
cached.textLength === textLength
|
|
36
37
|
) {
|
|
37
|
-
// Re-lex (cheap) to provide tokens; return cached parser state
|
|
38
38
|
return {
|
|
39
|
-
lexResult: lexDocument(text),
|
|
39
|
+
lexResult: cached.scheme === "multidoc" ? { tokens: [] } : lexDocument(text),
|
|
40
40
|
parser: cached.parserState,
|
|
41
41
|
scheme: cached.scheme,
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
// Multidoc detection — split on \n---\n separator
|
|
46
|
+
if (text.includes("\n---\n")) {
|
|
47
|
+
const parserState = multidocParser(text);
|
|
48
|
+
const cacheEntry = {
|
|
49
|
+
version,
|
|
50
|
+
textLength,
|
|
51
|
+
parserState,
|
|
52
|
+
scheme: "multidoc",
|
|
53
|
+
};
|
|
54
|
+
this.cache.set(uri, cacheEntry);
|
|
55
|
+
return {
|
|
56
|
+
lexResult: { tokens: [] },
|
|
57
|
+
parser: parserState,
|
|
58
|
+
scheme: "multidoc",
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
45
62
|
// Parse and cache a snapshot of the parser state
|
|
46
63
|
const scheme = getSchemeFromContent(text);
|
|
47
64
|
const lexResult = lexDocument(text);
|
|
@@ -11,6 +11,7 @@ import { onDidChangeContent } from "./onDidChangeContent/onDidChangeContent.js";
|
|
|
11
11
|
import { onHover } from "./onHover/onHover.js";
|
|
12
12
|
import { onSemanticCheck } from "./onSemanticCheck/onSemanticCheck.js";
|
|
13
13
|
import { TOKEN_TYPES } from "./onSemanticCheck/tokens.js";
|
|
14
|
+
import pkg from "./package.json" with { type: "json" };
|
|
14
15
|
|
|
15
16
|
const messageReader = new BrowserMessageReader(self);
|
|
16
17
|
const messageWriter = new BrowserMessageWriter(self);
|
|
@@ -47,6 +48,6 @@ connection.onRequest("textDocument/semanticTokens/full", (params) =>
|
|
|
47
48
|
documents.onDidChangeContent((params) =>
|
|
48
49
|
onDidChangeContent(params, connection)
|
|
49
50
|
);
|
|
50
|
-
connection.onInitialized(() => console.log(
|
|
51
|
+
connection.onInitialized(() => console.log(`XanoScript Language Server v${pkg.version}`));
|
|
51
52
|
documents.listen(connection);
|
|
52
53
|
connection.listen();
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { documentCache } from "../cache/documentCache.js";
|
|
1
2
|
import { mapToVirtualJS } from "../embedded/embeddedContent.js";
|
|
3
|
+
import { findSegmentAtOffset } from "../parser/multidoc.js";
|
|
2
4
|
import { getSchemeFromContent } from "../utils.js";
|
|
3
5
|
import { workspaceIndex } from "../workspace/workspaceIndex.js";
|
|
4
6
|
import { getContentAssistSuggestions } from "./contentAssist.js";
|
|
@@ -32,9 +34,28 @@ export function onCompletion(params, documents) {
|
|
|
32
34
|
return null;
|
|
33
35
|
}
|
|
34
36
|
|
|
35
|
-
//
|
|
36
|
-
const
|
|
37
|
-
|
|
37
|
+
// For multidoc, resolve to the correct segment
|
|
38
|
+
const { parser } = documentCache.getOrParse(
|
|
39
|
+
params.textDocument.uri,
|
|
40
|
+
document.version,
|
|
41
|
+
text,
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
let scheme;
|
|
45
|
+
let prefix;
|
|
46
|
+
|
|
47
|
+
if (parser.isMultidoc) {
|
|
48
|
+
const match = findSegmentAtOffset(parser, offset);
|
|
49
|
+
if (!match) return null;
|
|
50
|
+
scheme = getSchemeFromContent(match.segment.text);
|
|
51
|
+
// Prefix is everything in the full text up to the cursor — contentAssist
|
|
52
|
+
// parses backwards from the cursor to find context, so it needs the segment
|
|
53
|
+
// text up to the local offset
|
|
54
|
+
prefix = match.segment.text.slice(0, match.localOffset);
|
|
55
|
+
} else {
|
|
56
|
+
scheme = getSchemeFromContent(text);
|
|
57
|
+
prefix = text.slice(0, offset);
|
|
58
|
+
}
|
|
38
59
|
|
|
39
60
|
const suggestions = getContentAssistSuggestions(prefix, scheme);
|
|
40
61
|
|
|
@@ -73,6 +73,10 @@ export function onDidChangeContent(params, connection, features = {}) {
|
|
|
73
73
|
const text = document.getText();
|
|
74
74
|
|
|
75
75
|
try {
|
|
76
|
+
// Capture previous segment count before getOrParse overwrites the cache
|
|
77
|
+
const prevCached = documentCache.cache.get(document.uri);
|
|
78
|
+
const prevSegmentCount = prevCached?.parserState?.segmentCount ?? 0;
|
|
79
|
+
|
|
76
80
|
// Parse the XanoScript file using cache
|
|
77
81
|
const { lexResult, parser, scheme } = documentCache.getOrParse(
|
|
78
82
|
document.uri,
|
|
@@ -80,8 +84,28 @@ export function onDidChangeContent(params, connection, features = {}) {
|
|
|
80
84
|
text,
|
|
81
85
|
);
|
|
82
86
|
|
|
83
|
-
// Update workspace index
|
|
84
|
-
|
|
87
|
+
// Update workspace index
|
|
88
|
+
// Clean up old fragment URIs from previous parse
|
|
89
|
+
for (let i = 0; i < prevSegmentCount; i++) {
|
|
90
|
+
workspaceIndex.removeFile(`${document.uri}#${i}`);
|
|
91
|
+
}
|
|
92
|
+
if (parser.isMultidoc) {
|
|
93
|
+
// Remove the base URI in case document switched from single to multidoc
|
|
94
|
+
workspaceIndex.removeFile(document.uri);
|
|
95
|
+
// Index each segment with fragment URIs
|
|
96
|
+
if (parser.segments) {
|
|
97
|
+
for (let i = 0; i < parser.segments.length; i++) {
|
|
98
|
+
const seg = parser.segments[i];
|
|
99
|
+
workspaceIndex.addParsed(
|
|
100
|
+
`${document.uri}#${i}`,
|
|
101
|
+
seg.text,
|
|
102
|
+
seg.symbolTable,
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
} else {
|
|
107
|
+
workspaceIndex.addParsed(document.uri, text, parser.__symbolTable);
|
|
108
|
+
}
|
|
85
109
|
|
|
86
110
|
for (const error of parser.errors) {
|
|
87
111
|
console.error(
|
|
@@ -92,8 +116,8 @@ export function onDidChangeContent(params, connection, features = {}) {
|
|
|
92
116
|
// Create diagnostics in a single pass
|
|
93
117
|
const diagnostics = createDiagnostics(parser, document);
|
|
94
118
|
|
|
95
|
-
// Run cross-file validation
|
|
96
|
-
if (features.crossFileValidation !== false && parser.__symbolTable?.references) {
|
|
119
|
+
// Run cross-file validation (skip for multidoc — already done internally)
|
|
120
|
+
if (!parser.isMultidoc && features.crossFileValidation !== false && parser.__symbolTable?.references) {
|
|
97
121
|
const crossFileWarnings = crossFileValidate(
|
|
98
122
|
parser.__symbolTable.references,
|
|
99
123
|
workspaceIndex,
|
|
@@ -110,8 +134,8 @@ export function onDidChangeContent(params, connection, features = {}) {
|
|
|
110
134
|
}
|
|
111
135
|
}
|
|
112
136
|
|
|
113
|
-
// Run variable validation
|
|
114
|
-
if (features.variableValidation !== false && parser.__symbolTable?.varDeclarations) {
|
|
137
|
+
// Run variable validation (skip for multidoc — already done per-segment internally)
|
|
138
|
+
if (!parser.isMultidoc && features.variableValidation !== false && parser.__symbolTable?.varDeclarations) {
|
|
115
139
|
const varResult = validateVariables(
|
|
116
140
|
parser.__symbolTable,
|
|
117
141
|
lexResult.tokens,
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { documentCache } from "../cache/documentCache.js";
|
|
2
|
+
import { lexDocument } from "../lexer/lexer.js";
|
|
3
|
+
import { findSegmentAtOffset } from "../parser/multidoc.js";
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* Binary search to find token index at the given offset.
|
|
@@ -54,9 +56,50 @@ export function onHoverDocument(params, documents, hoverProviders = []) {
|
|
|
54
56
|
text,
|
|
55
57
|
);
|
|
56
58
|
|
|
59
|
+
let tokens;
|
|
60
|
+
let hoverParser;
|
|
61
|
+
let tokenOffsetAdjustment = 0;
|
|
62
|
+
|
|
63
|
+
if (parser.isMultidoc) {
|
|
64
|
+
// For multidoc, find the segment at the cursor position and lex it
|
|
65
|
+
const match = findSegmentAtOffset(parser, offset);
|
|
66
|
+
if (!match) return null;
|
|
67
|
+
|
|
68
|
+
const segLexResult = lexDocument(match.segment.text);
|
|
69
|
+
if (segLexResult.errors.length > 0) return null;
|
|
70
|
+
|
|
71
|
+
tokens = segLexResult.tokens;
|
|
72
|
+
hoverParser = match.segment.parser;
|
|
73
|
+
tokenOffsetAdjustment = match.segment.globalOffset;
|
|
74
|
+
|
|
75
|
+
// Find token using segment-local offset
|
|
76
|
+
const tokenIdx = findTokenAtOffset(tokens, match.localOffset);
|
|
77
|
+
if (tokenIdx === -1) return null;
|
|
78
|
+
|
|
79
|
+
const messageProvider = hoverProviders.find((provider) =>
|
|
80
|
+
provider.isMatch(tokenIdx, tokens, hoverParser),
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
if (messageProvider) {
|
|
84
|
+
return {
|
|
85
|
+
contents: {
|
|
86
|
+
kind: "markdown",
|
|
87
|
+
value: messageProvider.render(tokenIdx, tokens, hoverParser),
|
|
88
|
+
},
|
|
89
|
+
range: {
|
|
90
|
+
// Convert segment-local token offsets to global document positions
|
|
91
|
+
start: document.positionAt(tokens[tokenIdx].startOffset + tokenOffsetAdjustment),
|
|
92
|
+
end: document.positionAt(tokens[tokenIdx].endOffset + 1 + tokenOffsetAdjustment),
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
|
|
57
100
|
if (lexResult.errors.length > 0) return null;
|
|
58
101
|
|
|
59
|
-
|
|
102
|
+
tokens = lexResult.tokens;
|
|
60
103
|
|
|
61
104
|
// Find the token under the cursor using binary search (O(log n))
|
|
62
105
|
const tokenIdx = findTokenAtOffset(tokens, offset);
|
|
@@ -452,6 +452,180 @@ describe("onHoverDocument", () => {
|
|
|
452
452
|
expect(result).to.have.property("range");
|
|
453
453
|
});
|
|
454
454
|
|
|
455
|
+
it("should return hover info for var in second segment of multidoc", () => {
|
|
456
|
+
const code = `function foo {
|
|
457
|
+
input {
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
stack {
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
response = null
|
|
464
|
+
}
|
|
465
|
+
---
|
|
466
|
+
query test verb=GET {
|
|
467
|
+
stack {
|
|
468
|
+
var $result {
|
|
469
|
+
value = "hello"
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}`;
|
|
473
|
+
|
|
474
|
+
const document = TextDocument.create(
|
|
475
|
+
"file://test-multidoc.xs",
|
|
476
|
+
"xanoscript",
|
|
477
|
+
1,
|
|
478
|
+
code
|
|
479
|
+
);
|
|
480
|
+
const params = {
|
|
481
|
+
textDocument: { uri: "file://test-multidoc.xs" },
|
|
482
|
+
position: { line: 12, character: 4 }, // Position on "var" in second segment
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
const result = onHoverDocument(
|
|
486
|
+
params,
|
|
487
|
+
{ get: () => document },
|
|
488
|
+
hoverProviders
|
|
489
|
+
);
|
|
490
|
+
|
|
491
|
+
expect(result).to.not.be.null;
|
|
492
|
+
expect(result.contents.value).to.include("Create a variable");
|
|
493
|
+
expect(result).to.have.property("range");
|
|
494
|
+
// Range should be in the global document, not segment-local
|
|
495
|
+
expect(result.range.start.line).to.be.at.least(10);
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
it("should return hover info for input variable in multidoc segment", () => {
|
|
499
|
+
const code = `function foo {
|
|
500
|
+
input {
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
stack {
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
response = null
|
|
507
|
+
}
|
|
508
|
+
---
|
|
509
|
+
query test verb=GET {
|
|
510
|
+
input {
|
|
511
|
+
text username
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
stack {
|
|
515
|
+
var $result {
|
|
516
|
+
value = $input.username
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
response = $result
|
|
521
|
+
}`;
|
|
522
|
+
|
|
523
|
+
const document = TextDocument.create(
|
|
524
|
+
"file://test-multidoc-input.xs",
|
|
525
|
+
"xanoscript",
|
|
526
|
+
1,
|
|
527
|
+
code
|
|
528
|
+
);
|
|
529
|
+
// "username" after $input. in the second segment
|
|
530
|
+
const params = {
|
|
531
|
+
textDocument: { uri: "file://test-multidoc-input.xs" },
|
|
532
|
+
position: { line: 17, character: 20 },
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
const result = onHoverDocument(
|
|
536
|
+
params,
|
|
537
|
+
{ get: () => document },
|
|
538
|
+
hoverProviders
|
|
539
|
+
);
|
|
540
|
+
|
|
541
|
+
if (result) {
|
|
542
|
+
expect(result.contents.value).to.include("username");
|
|
543
|
+
expect(result.range.start.line).to.be.at.least(10);
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
it("should return hover info for first segment of multidoc", () => {
|
|
548
|
+
const code = `query test verb=GET {
|
|
549
|
+
stack {
|
|
550
|
+
var $result {
|
|
551
|
+
value = "hello"
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
---
|
|
556
|
+
function bar {
|
|
557
|
+
input {
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
stack {
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
response = null
|
|
564
|
+
}`;
|
|
565
|
+
|
|
566
|
+
const document = TextDocument.create(
|
|
567
|
+
"file://test-multidoc-first.xs",
|
|
568
|
+
"xanoscript",
|
|
569
|
+
1,
|
|
570
|
+
code
|
|
571
|
+
);
|
|
572
|
+
const params = {
|
|
573
|
+
textDocument: { uri: "file://test-multidoc-first.xs" },
|
|
574
|
+
position: { line: 2, character: 4 }, // "var" in first segment
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
const result = onHoverDocument(
|
|
578
|
+
params,
|
|
579
|
+
{ get: () => document },
|
|
580
|
+
hoverProviders
|
|
581
|
+
);
|
|
582
|
+
|
|
583
|
+
expect(result).to.not.be.null;
|
|
584
|
+
expect(result.contents.value).to.include("Create a variable");
|
|
585
|
+
expect(result.range.start.line).to.equal(2);
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
it("should return null for hover on separator line in multidoc", () => {
|
|
589
|
+
const code = `function foo {
|
|
590
|
+
input {
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
stack {
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
response = null
|
|
597
|
+
}
|
|
598
|
+
---
|
|
599
|
+
function bar {
|
|
600
|
+
input {
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
stack {
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
response = null
|
|
607
|
+
}`;
|
|
608
|
+
|
|
609
|
+
const document = TextDocument.create(
|
|
610
|
+
"file://test-multidoc-sep.xs",
|
|
611
|
+
"xanoscript",
|
|
612
|
+
1,
|
|
613
|
+
code
|
|
614
|
+
);
|
|
615
|
+
const params = {
|
|
616
|
+
textDocument: { uri: "file://test-multidoc-sep.xs" },
|
|
617
|
+
position: { line: 9, character: 1 }, // On the "---" separator line
|
|
618
|
+
};
|
|
619
|
+
|
|
620
|
+
const result = onHoverDocument(
|
|
621
|
+
params,
|
|
622
|
+
{ get: () => document },
|
|
623
|
+
hoverProviders
|
|
624
|
+
);
|
|
625
|
+
|
|
626
|
+
expect(result).to.be.null;
|
|
627
|
+
});
|
|
628
|
+
|
|
455
629
|
it("should return hover info for query filter tokens in search context", () => {
|
|
456
630
|
const code = `query books verb=GET {
|
|
457
631
|
where = $db.embedding.vector|inner_product:$input.query_vector
|
|
@@ -10,10 +10,51 @@ import { encodeTokenType } from "./tokens.js";
|
|
|
10
10
|
* @returns {SemanticTokensBuilder} Returns null if the scheme is not supported
|
|
11
11
|
*/
|
|
12
12
|
export function higlightText(scheme, text, SemanticTokensBuilder) {
|
|
13
|
-
return higlightDefault(text, SemanticTokensBuilder);
|
|
13
|
+
return higlightDefault(text, 0, SemanticTokensBuilder);
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Highlight a multidoc by highlighting each segment independently.
|
|
18
|
+
* @param {Array<{text: string, globalOffset: number}>} segments - The segments from multidocParser
|
|
19
|
+
* @param {SemanticTokensBuilder} SemanticTokensBuilder - The semantic tokens builder constructor
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Highlight a multidoc by highlighting each segment independently.
|
|
23
|
+
* @param {Array<{text: string, globalOffset: number}>} segments - The segments from multidocParser
|
|
24
|
+
* @param {SemanticTokensBuilder} SemanticTokensBuilder - The semantic tokens builder constructor
|
|
25
|
+
*/
|
|
26
|
+
export function higlightSegments(segments, SemanticTokensBuilder) {
|
|
27
|
+
const builder = new SemanticTokensBuilder();
|
|
28
|
+
|
|
29
|
+
// Pre-compute line offsets: count newlines in each previous segment + separator
|
|
30
|
+
let lineOffset = 0;
|
|
31
|
+
|
|
32
|
+
for (const segment of segments) {
|
|
33
|
+
const lexResult = lexDocument(segment.text, true);
|
|
34
|
+
|
|
35
|
+
for (const token of lexResult.tokens) {
|
|
36
|
+
const tokenType = mapTokenToType(token.tokenType.name);
|
|
37
|
+
if (tokenType) {
|
|
38
|
+
builder.push(
|
|
39
|
+
token.startLine - 1 + lineOffset,
|
|
40
|
+
token.startColumn - 1,
|
|
41
|
+
token.image.length,
|
|
42
|
+
encodeTokenType(tokenType),
|
|
43
|
+
0,
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Advance lineOffset: lines in this segment's text + 2 for the \n---\n separator
|
|
49
|
+
// (separator has 2 newlines: one before --- and one after)
|
|
50
|
+
const segmentLines = segment.text.split("\n").length - 1;
|
|
51
|
+
lineOffset += segmentLines + 2;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return builder.build();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function higlightDefault(text, lineOffset, SemanticTokensBuilder) {
|
|
17
58
|
const builder = new SemanticTokensBuilder();
|
|
18
59
|
|
|
19
60
|
// Map Chevrotain tokens to semantic token types
|
|
@@ -23,8 +64,8 @@ function higlightDefault(text, SemanticTokensBuilder) {
|
|
|
23
64
|
lexResult.tokens.forEach((token) => {
|
|
24
65
|
const tokenType = mapTokenToType(token.tokenType.name);
|
|
25
66
|
if (tokenType) {
|
|
26
|
-
const line = token.startLine - 1
|
|
27
|
-
const character = token.startColumn - 1;
|
|
67
|
+
const line = token.startLine - 1 + lineOffset;
|
|
68
|
+
const character = token.startColumn - 1;
|
|
28
69
|
builder.push(
|
|
29
70
|
line,
|
|
30
71
|
character,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { documentCache } from "../cache/documentCache.js";
|
|
2
|
+
import { getSchemeFromContent } from "../utils.js";
|
|
3
|
+
import { higlightSegments,higlightText } from "./highlight.js";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Handles a semantic tokens request for the full document.
|
|
@@ -19,7 +20,17 @@ export function onSemanticCheck(params, documents, SemanticTokensBuilder) {
|
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
const text = document.getText();
|
|
22
|
-
const scheme = getSchemeFromContent(text);
|
|
23
23
|
|
|
24
|
+
const { parser } = documentCache.getOrParse(
|
|
25
|
+
params.textDocument.uri,
|
|
26
|
+
document.version,
|
|
27
|
+
text,
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
if (parser.isMultidoc) {
|
|
31
|
+
return higlightSegments(parser.segments, SemanticTokensBuilder);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const scheme = getSchemeFromContent(text);
|
|
24
35
|
return higlightText(scheme, text, SemanticTokensBuilder);
|
|
25
36
|
}
|