@lokascript/language-server 2.7.1 → 2.8.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/dist/{dist-MRVDB2YM.js → dist-6KATWM5A.js} +120 -21
- package/dist/server.js +1 -1
- package/package.json +6 -6
|
@@ -212,7 +212,14 @@ var DEFAULT_REFERENCES = /* @__PURE__ */ new Set([
|
|
|
212
212
|
"result",
|
|
213
213
|
"event",
|
|
214
214
|
"target",
|
|
215
|
-
"body"
|
|
215
|
+
"body",
|
|
216
|
+
// DOM context globals — as reference-typed as `body`, and already advertised as
|
|
217
|
+
// references by core's REFERENCE_KEYWORDS and the parser's PROPERTY_ACCESS_BASES.
|
|
218
|
+
// Without them here, a foreign surface (ja ドキュメント, es documento) lexes as a
|
|
219
|
+
// keyword but fails isValidReference, degrades to a literal, and leaks verbatim.
|
|
220
|
+
"document",
|
|
221
|
+
"window",
|
|
222
|
+
"detail"
|
|
216
223
|
]);
|
|
217
224
|
function isValidReference(value, referenceSet = DEFAULT_REFERENCES) {
|
|
218
225
|
return referenceSet.has(value);
|
|
@@ -227,16 +234,6 @@ var STRUCTURAL_ROLES = /* @__PURE__ */ new Set([
|
|
|
227
234
|
"catch",
|
|
228
235
|
"finally"
|
|
229
236
|
]);
|
|
230
|
-
function isNestedCommand(value) {
|
|
231
|
-
const inner = value.slice(1, -1);
|
|
232
|
-
let depth = 0;
|
|
233
|
-
for (const ch of inner) {
|
|
234
|
-
if (ch === "[") depth++;
|
|
235
|
-
else if (ch === "]") depth--;
|
|
236
|
-
else if (depth === 0 && (ch === " " || ch === ":")) return true;
|
|
237
|
-
}
|
|
238
|
-
return false;
|
|
239
|
-
}
|
|
240
237
|
function parseExplicit(input, options = {}) {
|
|
241
238
|
const trimmed = input.trim();
|
|
242
239
|
const collect = options.collectDiagnostics === true;
|
|
@@ -324,7 +321,7 @@ function parseExplicit(input, options = {}) {
|
|
|
324
321
|
}
|
|
325
322
|
const role = roleName;
|
|
326
323
|
const valueStr = token.slice(colonIndex + 1);
|
|
327
|
-
if (STRUCTURAL_ROLES.has(roleName) && valueStr.startsWith("[")
|
|
324
|
+
if (STRUCTURAL_ROLES.has(roleName) && valueStr.startsWith("[")) {
|
|
328
325
|
const nestedEnd = findMatchingBracket(token, colonIndex + 1);
|
|
329
326
|
const nestedSyntax = token.slice(colonIndex + 1, nestedEnd + 1);
|
|
330
327
|
roles.set(role, { type: "expression", raw: nestedSyntax });
|
|
@@ -576,26 +573,63 @@ function extractStructuralBody(roles, roleName, options) {
|
|
|
576
573
|
if (value.type === "expression") return [parseExplicit(value.raw, options)];
|
|
577
574
|
return [];
|
|
578
575
|
}
|
|
576
|
+
function hasUnquotedCombinator(value) {
|
|
577
|
+
let quote = "";
|
|
578
|
+
for (const ch of value) {
|
|
579
|
+
if (quote) {
|
|
580
|
+
if (ch === quote) quote = "";
|
|
581
|
+
continue;
|
|
582
|
+
}
|
|
583
|
+
if (ch === '"' || ch === "'") {
|
|
584
|
+
quote = ch;
|
|
585
|
+
continue;
|
|
586
|
+
}
|
|
587
|
+
if (ch === " " || ch === ">" || ch === "+" || ch === "~" || ch === ",") return true;
|
|
588
|
+
}
|
|
589
|
+
return false;
|
|
590
|
+
}
|
|
579
591
|
function inferSelectorKind(value) {
|
|
580
592
|
if (!value) return void 0;
|
|
593
|
+
if (hasUnquotedCombinator(value)) return "complex";
|
|
581
594
|
if (value.startsWith("#")) return "id";
|
|
582
595
|
if (value.startsWith(".")) return "class";
|
|
583
596
|
if (value.startsWith("[")) return "attribute";
|
|
584
|
-
if (value.startsWith("
|
|
585
|
-
if (/[>+~ ]/.test(value) && value.length > 1) return "complex";
|
|
597
|
+
if (value.startsWith("*")) return "element";
|
|
586
598
|
return void 0;
|
|
587
599
|
}
|
|
600
|
+
function unescapeString(s) {
|
|
601
|
+
if (!s.includes("\\")) return s;
|
|
602
|
+
let out = "";
|
|
603
|
+
for (let i = 0; i < s.length; i++) {
|
|
604
|
+
if (s[i] === "\\" && i + 1 < s.length) {
|
|
605
|
+
const next = s[i + 1];
|
|
606
|
+
const mapped = next === "\\" ? "\\" : next === '"' ? '"' : next === "'" ? "'" : next === "n" ? "\n" : next === "r" ? "\r" : next === "t" ? " " : "";
|
|
607
|
+
if (mapped) {
|
|
608
|
+
out += mapped;
|
|
609
|
+
i++;
|
|
610
|
+
continue;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
out += s[i];
|
|
614
|
+
}
|
|
615
|
+
return out;
|
|
616
|
+
}
|
|
588
617
|
function tokenizeExplicit(content) {
|
|
589
618
|
const tokens = [];
|
|
590
619
|
let current = "";
|
|
591
620
|
let inString = false;
|
|
592
621
|
let stringChar = "";
|
|
622
|
+
let inSelector = false;
|
|
593
623
|
let bracketDepth = 0;
|
|
594
624
|
for (let i = 0; i < content.length; i++) {
|
|
595
625
|
const char = content[i];
|
|
596
626
|
if (inString) {
|
|
597
627
|
current += char;
|
|
598
|
-
if (char === stringChar
|
|
628
|
+
if (char === stringChar) {
|
|
629
|
+
let backslashes = 0;
|
|
630
|
+
for (let j = i - 1; j >= 0 && content[j] === "\\"; j--) backslashes++;
|
|
631
|
+
if (backslashes % 2 === 0) inString = false;
|
|
632
|
+
}
|
|
599
633
|
continue;
|
|
600
634
|
}
|
|
601
635
|
if (char === '"' || char === "'") {
|
|
@@ -604,6 +638,16 @@ function tokenizeExplicit(content) {
|
|
|
604
638
|
current += char;
|
|
605
639
|
continue;
|
|
606
640
|
}
|
|
641
|
+
if (inSelector) {
|
|
642
|
+
current += char;
|
|
643
|
+
if (char === ">" && current.endsWith("/>")) inSelector = false;
|
|
644
|
+
continue;
|
|
645
|
+
}
|
|
646
|
+
if (char === "<" && (current === "" || current.endsWith(":"))) {
|
|
647
|
+
inSelector = true;
|
|
648
|
+
current += char;
|
|
649
|
+
continue;
|
|
650
|
+
}
|
|
607
651
|
if (char === "[") {
|
|
608
652
|
bracketDepth++;
|
|
609
653
|
current += char;
|
|
@@ -627,11 +671,19 @@ function tokenizeExplicit(content) {
|
|
|
627
671
|
return tokens;
|
|
628
672
|
}
|
|
629
673
|
function parseExplicitValue(valueStr, referenceSet) {
|
|
674
|
+
if (valueStr.startsWith("<")) {
|
|
675
|
+
if (!valueStr.endsWith("/>")) {
|
|
676
|
+
throw new Error(`Unterminated selector literal: "${valueStr}". Expected a closing "/>"`);
|
|
677
|
+
}
|
|
678
|
+
const inner = valueStr.slice(1, -2);
|
|
679
|
+
if (!inner) throw new Error(`Empty selector literal: "${valueStr}"`);
|
|
680
|
+
return createSelector(inner, inferSelectorKind(inner));
|
|
681
|
+
}
|
|
630
682
|
if (valueStr.startsWith("#") || valueStr.startsWith(".") || valueStr.startsWith("[") || valueStr.startsWith("@") || valueStr.startsWith("*")) {
|
|
631
683
|
return createSelector(valueStr, inferSelectorKind(valueStr));
|
|
632
684
|
}
|
|
633
685
|
if (valueStr.startsWith('"') || valueStr.startsWith("'")) {
|
|
634
|
-
return createLiteral(valueStr.slice(1, -1), "string");
|
|
686
|
+
return createLiteral(unescapeString(valueStr.slice(1, -1)), "string");
|
|
635
687
|
}
|
|
636
688
|
if (valueStr === "true") return createLiteral(true, "boolean");
|
|
637
689
|
if (valueStr === "false") return createLiteral(false, "boolean");
|
|
@@ -730,16 +782,23 @@ function renderBracketCommand(node) {
|
|
|
730
782
|
}
|
|
731
783
|
return `[${parts.join(" ")}]`;
|
|
732
784
|
}
|
|
785
|
+
function needsSelectorWrap(value) {
|
|
786
|
+
if (value.includes(" ")) return true;
|
|
787
|
+
const first = value[0];
|
|
788
|
+
return !(first === "#" || first === "." || first === "@" || first === "*");
|
|
789
|
+
}
|
|
733
790
|
function valueToString(value) {
|
|
734
791
|
switch (value.type) {
|
|
735
792
|
case "literal":
|
|
736
793
|
if (typeof value.value === "string") {
|
|
737
|
-
if (value.dataType === "string" || /\s/.test(value.value))
|
|
794
|
+
if (value.dataType === "string" || /\s/.test(value.value))
|
|
795
|
+
return JSON.stringify(value.value);
|
|
738
796
|
return value.value;
|
|
739
797
|
}
|
|
740
798
|
return String(value.value);
|
|
741
799
|
case "selector":
|
|
742
|
-
return value.value;
|
|
800
|
+
if (!value.value) return value.value;
|
|
801
|
+
return needsSelectorWrap(value.value) ? `<${value.value}/>` : value.value;
|
|
743
802
|
case "reference":
|
|
744
803
|
return value.value;
|
|
745
804
|
case "property-path":
|
|
@@ -837,6 +896,7 @@ function toProtocolJSON(node) {
|
|
|
837
896
|
if (node.diagnostics && node.diagnostics.length > 0) {
|
|
838
897
|
result.diagnostics = node.diagnostics.map((d) => {
|
|
839
898
|
const json = { level: d.severity, message: d.message };
|
|
899
|
+
if (d.role) json.role = d.role;
|
|
840
900
|
if (d.code) json.code = d.code;
|
|
841
901
|
if (d.source) json.source = d.source;
|
|
842
902
|
if (d.suggestions && d.suggestions.length > 0) json.suggestions = [...d.suggestions];
|
|
@@ -921,6 +981,7 @@ function fromProtocolJSON(json) {
|
|
|
921
981
|
const diagnostics = json.diagnostics?.map((d) => ({
|
|
922
982
|
severity: d.level,
|
|
923
983
|
message: d.message,
|
|
984
|
+
...d.role ? { role: d.role } : {},
|
|
924
985
|
...d.code ? { code: d.code } : {},
|
|
925
986
|
...d.source ? { source: d.source } : {},
|
|
926
987
|
...d.suggestions && d.suggestions.length > 0 ? { suggestions: d.suggestions } : {}
|
|
@@ -2674,7 +2735,8 @@ function buildFormatString(schema, profile, keyword) {
|
|
|
2674
2735
|
if (profile.wordOrder === "SVO" || profile.wordOrder === "VSO") {
|
|
2675
2736
|
parts.push(keyword);
|
|
2676
2737
|
}
|
|
2677
|
-
|
|
2738
|
+
const sortedRoles = sortRolesByWordOrder(schema.roles, profile.wordOrder);
|
|
2739
|
+
for (const role of sortedRoles) {
|
|
2678
2740
|
const marker = getMarkerForRole(role, profile);
|
|
2679
2741
|
const roleName = `{${role.role}}`;
|
|
2680
2742
|
if (marker) {
|
|
@@ -4085,7 +4147,9 @@ function buildProtocolSection() {
|
|
|
4085
4147
|
3. No spaces around the colon in role:value pairs
|
|
4086
4148
|
4. Strings with spaces must be quoted: \`patient:"hello world"\`
|
|
4087
4149
|
5. Selectors start with \`#\`, \`.\`, \`[\`, \`@\`, or \`*\`
|
|
4088
|
-
6.
|
|
4150
|
+
6. A selector containing a space, a combinator (\`>\` \`+\` \`~\`), or a comma must use a selector literal: \`patient:<ul > li/>\`, \`patient:<.a, .b/>\`
|
|
4151
|
+
7. Inside a structural role (\`body\`, \`then\`, \`else\`, \`condition\`, \`loop-body\`, \`variable\`, \`catch\`, \`finally\`) a \`[...]\` value is always a nested command. Write an attribute selector there as \`condition:<[data-active]/>\`
|
|
4152
|
+
8. Output must be valid bracket syntax: \`[action role:value ...]\``;
|
|
4089
4153
|
return {
|
|
4090
4154
|
id: "protocol",
|
|
4091
4155
|
title: "LSE Protocol",
|
|
@@ -4098,6 +4162,7 @@ function buildValueTypeSection() {
|
|
|
4098
4162
|
|
|
4099
4163
|
| Type | Syntax | Example |
|
|
4100
4164
|
|------|--------|---------|
|
|
4165
|
+
| Selector literal | Delimited by \`<\` and \`/>\` | \`<ul > li/>\`, \`<.a, .b/>\`, \`<[data-id]/>\` |
|
|
4101
4166
|
| Selector | Starts with \`#\` \`.\` \`[\` \`@\` \`*\` | \`#button\`, \`.active\`, \`[data-id]\` |
|
|
4102
4167
|
| String | Quoted with \`"\` or \`'\` | \`"hello world"\`, \`'json'\` |
|
|
4103
4168
|
| Boolean | Exact: \`true\` / \`false\` | \`visible:true\` |
|
|
@@ -6238,7 +6303,39 @@ var _BaseTokenizer = class _BaseTokenizer2 {
|
|
|
6238
6303
|
pos++;
|
|
6239
6304
|
}
|
|
6240
6305
|
}
|
|
6241
|
-
return new TokenStreamImpl(tokens, this.language);
|
|
6306
|
+
return new TokenStreamImpl(this.mergeColonQualifiedNames(tokens), this.language);
|
|
6307
|
+
}
|
|
6308
|
+
/**
|
|
6309
|
+
* Fuse `name` + `:qualifier` into ONE identifier (`draggable:start`).
|
|
6310
|
+
*
|
|
6311
|
+
* `:name` is hyperscript's local-variable sigil, but a colon IMMEDIATELY
|
|
6312
|
+
* preceded by an identifier is a qualifier (custom event namespace), not a
|
|
6313
|
+
* sigil. The English tokenizer already merges these inside
|
|
6314
|
+
* EnglishKeywordExtractor; this post-pass gives the other 23 languages the
|
|
6315
|
+
* same stream. Strict position adjacency is the discriminator: whitespace
|
|
6316
|
+
* between the tokens (`trigger :start`) breaks `end === start`, so a spaced
|
|
6317
|
+
* local-variable reference survives untouched.
|
|
6318
|
+
*
|
|
6319
|
+
* Self-gating for non-hyperscript tokenizers (domain DSLs): their extractor
|
|
6320
|
+
* sets tokenize `:` as bare punctuation (length 1), which never matches
|
|
6321
|
+
* COLON_QUALIFIER, so this pass is a no-op for them.
|
|
6322
|
+
*/
|
|
6323
|
+
mergeColonQualifiedNames(tokens) {
|
|
6324
|
+
const out = [];
|
|
6325
|
+
for (const tok of tokens) {
|
|
6326
|
+
const prev = out[out.length - 1];
|
|
6327
|
+
if (prev && _BaseTokenizer2.ASCII_WORD.test(prev.value) && _BaseTokenizer2.COLON_QUALIFIER.test(tok.value) && prev.position.end === tok.position.start) {
|
|
6328
|
+
const merged = prev.value + tok.value;
|
|
6329
|
+
out[out.length - 1] = createToken(
|
|
6330
|
+
merged,
|
|
6331
|
+
this.classifyToken(merged),
|
|
6332
|
+
createPosition(prev.position.start, tok.position.end)
|
|
6333
|
+
);
|
|
6334
|
+
continue;
|
|
6335
|
+
}
|
|
6336
|
+
out.push(tok);
|
|
6337
|
+
}
|
|
6338
|
+
return out;
|
|
6242
6339
|
}
|
|
6243
6340
|
/**
|
|
6244
6341
|
* Classify an unknown character when no extractor matches.
|
|
@@ -6744,6 +6841,8 @@ var _BaseTokenizer = class _BaseTokenizer2 {
|
|
|
6744
6841
|
return null;
|
|
6745
6842
|
}
|
|
6746
6843
|
};
|
|
6844
|
+
_BaseTokenizer.ASCII_WORD = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
6845
|
+
_BaseTokenizer.COLON_QUALIFIER = /^:[A-Za-z_][A-Za-z0-9_]*$/;
|
|
6747
6846
|
_BaseTokenizer.STANDARD_TIME_UNITS = [
|
|
6748
6847
|
{ pattern: "ms", suffix: "ms", length: 2 },
|
|
6749
6848
|
{ pattern: "s", suffix: "s", length: 1, checkBoundary: true },
|
package/dist/server.js
CHANGED
|
@@ -1134,7 +1134,7 @@ try {
|
|
|
1134
1134
|
}
|
|
1135
1135
|
var frameworkIR = null;
|
|
1136
1136
|
try {
|
|
1137
|
-
const fw = await import("./dist-
|
|
1137
|
+
const fw = await import("./dist-6KATWM5A.js");
|
|
1138
1138
|
frameworkIR = { fromInterchangeNode: fw.fromInterchangeNode, renderExplicit: fw.renderExplicit };
|
|
1139
1139
|
console.error(
|
|
1140
1140
|
"[lokascript-ls] @lokascript/framework loaded \u2014 LSE bracket notation in hover enabled"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lokascript/language-server",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.8.0",
|
|
4
4
|
"description": "Language Server Protocol implementation for LokaScript/hyperscript with 21 language support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/server.js",
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
"vscode-languageserver-textdocument": "^1.0.11"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
|
-
"@hyperfixi/core": "^2.
|
|
35
|
-
"@lokascript/semantic": "^2.
|
|
34
|
+
"@hyperfixi/core": "^2.8.0",
|
|
35
|
+
"@lokascript/semantic": "^2.8.0"
|
|
36
36
|
},
|
|
37
37
|
"peerDependenciesMeta": {
|
|
38
38
|
"@lokascript/semantic": {
|
|
@@ -43,12 +43,12 @@
|
|
|
43
43
|
}
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@hyperfixi/core": "^2.
|
|
47
|
-
"@lokascript/semantic": "^2.
|
|
46
|
+
"@hyperfixi/core": "^2.8.0",
|
|
47
|
+
"@lokascript/semantic": "^2.8.0",
|
|
48
48
|
"@types/node": "^20.0.0",
|
|
49
49
|
"@vitest/coverage-v8": "^4.0.0",
|
|
50
50
|
"tsup": "^8.0.0",
|
|
51
|
-
"tsx": "^4.
|
|
51
|
+
"tsx": "^4.23.1",
|
|
52
52
|
"typescript": "^5.0.0",
|
|
53
53
|
"vitest": "^4.1.5"
|
|
54
54
|
},
|