luaut-language-server 2.0.0 → 3.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/README.md +1 -1
- package/dist/{chunk-SXRYJV32.js → chunk-X2GHHRWT.js} +78 -14
- package/dist/chunk-X2GHHRWT.js.map +1 -0
- package/dist/cli.cjs +73 -10
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/index.cjs +73 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +3 -3
- package/dist/chunk-SXRYJV32.js.map +0 -1
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ luaut-language-server --stdio
|
|
|
17
17
|
| request | notes |
|
|
18
18
|
|---|---|
|
|
19
19
|
| `publishDiagnostics` | syntax, scope (redeclare, assign-to-`const`) and type errors, on open and on every keystroke |
|
|
20
|
-
| `hover` | the type as luaut writes it — the **narrowed** type at a reference, so a guarded `v` reads `string`, not `string \| nil`. Also every name in a type or definitions file: `declare` names (with their overload count), alias names, object-type properties, type parameters, `infer` names, and any type annotation, which reads as what it resolves to |
|
|
20
|
+
| `hover` | the type as luaut writes it — the **narrowed** type at a reference, so a guarded `v` reads `string`, not `string \| nil`. Also every name in a type or definitions file: `declare` names (with their overload count), classes (`declare class Part extends BasePart { ...what it adds }`), alias names, object-type properties, type parameters, `infer` names, and any type annotation, which reads as what it resolves to |
|
|
21
21
|
| `semanticTokens` | colours from the parser, not from patterns — see [Highlighting](#highlighting) |
|
|
22
22
|
| `definition` | the binding's declaration — and from an `import`, the export in the other module |
|
|
23
23
|
| `references`, `documentHighlight` | every use of the binding |
|
|
@@ -765,16 +765,34 @@ function diagnostics(analysis) {
|
|
|
765
765
|
|
|
766
766
|
// src/features/hover.ts
|
|
767
767
|
import {
|
|
768
|
-
formatType as formatType3
|
|
768
|
+
formatType as formatType3,
|
|
769
|
+
isClassType
|
|
769
770
|
} from "luaut-parser";
|
|
770
771
|
function hover(analysis, position) {
|
|
771
772
|
const path = pathAt(analysis.program, position, true);
|
|
772
773
|
for (let i = path.length - 1; i >= 0; i--) {
|
|
774
|
+
if (UNNAMED.has(path[i].type)) return null;
|
|
773
775
|
const text = describe(analysis, path, i);
|
|
774
776
|
if (text) return { contents: { kind: "markdown", value: code(text) }, range: toRange(path[i]) };
|
|
775
777
|
}
|
|
776
778
|
return null;
|
|
777
779
|
}
|
|
780
|
+
var UNNAMED = /* @__PURE__ */ new Set([
|
|
781
|
+
"BinaryExpression",
|
|
782
|
+
"UnaryExpression",
|
|
783
|
+
"CallExpression",
|
|
784
|
+
"MethodCallExpression",
|
|
785
|
+
"MemberExpression",
|
|
786
|
+
"IndexExpression",
|
|
787
|
+
"ParenthesizedExpression",
|
|
788
|
+
"IfElseExpression",
|
|
789
|
+
"TableExpression",
|
|
790
|
+
"ArrayExpression",
|
|
791
|
+
"TypeAssertionExpression",
|
|
792
|
+
"SatisfiesExpression",
|
|
793
|
+
"AsConstExpression",
|
|
794
|
+
"InterpolatedStringExpression"
|
|
795
|
+
]);
|
|
778
796
|
var PRIMITIVES = /* @__PURE__ */ new Set(["any", "unknown", "never", "nil", "boolean", "number", "string", "thread", "buffer"]);
|
|
779
797
|
function describe(analysis, path, index) {
|
|
780
798
|
const { types } = analysis;
|
|
@@ -816,6 +834,9 @@ function describe(analysis, path, index) {
|
|
|
816
834
|
case "DeclareStatement":
|
|
817
835
|
if (parent.id === node) return declareText(analysis, parent);
|
|
818
836
|
break;
|
|
837
|
+
case "DeclareClassStatement":
|
|
838
|
+
if (parent.name === node) return classText(analysis, name);
|
|
839
|
+
break;
|
|
819
840
|
case "TableTypeProperty":
|
|
820
841
|
if (parent.key === node) {
|
|
821
842
|
const type2 = typeOfNode(parent.valueType);
|
|
@@ -847,7 +868,7 @@ function describe(analysis, path, index) {
|
|
|
847
868
|
const binding = bindingOfNode(analysis, identifier2);
|
|
848
869
|
if (binding) {
|
|
849
870
|
const type2 = types.bindingType.get(binding.id);
|
|
850
|
-
if (type2) return
|
|
871
|
+
if (type2) return bindingText(binding, type2);
|
|
851
872
|
}
|
|
852
873
|
if (parent?.type === "MemberExpression" || parent?.type === "MethodCallExpression") {
|
|
853
874
|
const type2 = types.typeOf.get(parent);
|
|
@@ -855,13 +876,13 @@ function describe(analysis, path, index) {
|
|
|
855
876
|
}
|
|
856
877
|
return void 0;
|
|
857
878
|
}
|
|
858
|
-
// Declarations: `const x`, a parameter
|
|
879
|
+
// Declarations: `const x`, a parameter.
|
|
859
880
|
case "IdentifierPattern":
|
|
860
881
|
case "FunctionParameter":
|
|
861
882
|
case "TypedIdentifier": {
|
|
862
883
|
const binding = bindingOfNode(analysis, node);
|
|
863
884
|
const type2 = binding && types.bindingType.get(binding.id);
|
|
864
|
-
return type2 ?
|
|
885
|
+
return type2 ? bindingText(binding, type2) : void 0;
|
|
865
886
|
}
|
|
866
887
|
// A type written by name: `number`, `Shape`, `Partial<User>`, or a type
|
|
867
888
|
// parameter in scope.
|
|
@@ -872,9 +893,11 @@ function describe(analysis, path, index) {
|
|
|
872
893
|
if (parameter) return typeParameterText(analysis, parameter);
|
|
873
894
|
if (PRIMITIVES.has(base)) return `type ${base}`;
|
|
874
895
|
}
|
|
875
|
-
if (!node.
|
|
876
|
-
const
|
|
877
|
-
|
|
896
|
+
if (!node.typeArguments.length) {
|
|
897
|
+
const qualified = node.namespace ? `${node.namespace}.${base}` : base;
|
|
898
|
+
const alias = types.aliases.get(qualified);
|
|
899
|
+
if (alias && isClassType(alias)) return classText(analysis, qualified);
|
|
900
|
+
if (alias) return `type ${qualified} = ${pretty(alias)}`;
|
|
878
901
|
}
|
|
879
902
|
const type2 = typeOfNode(node);
|
|
880
903
|
return type2 && `type ${referenceText(analysis, node)} = ${pretty(type2)}`;
|
|
@@ -903,6 +926,19 @@ function declareText(analysis, statement) {
|
|
|
903
926
|
const overloads = others > 0 ? ` (+${others} overload${others > 1 ? "s" : ""})` : "";
|
|
904
927
|
return `declare function ${name}${formatType3(own)}${overloads}`;
|
|
905
928
|
}
|
|
929
|
+
function classText(analysis, name) {
|
|
930
|
+
const type = analysis.types.aliases.get(name);
|
|
931
|
+
if (!type || !isClassType(type)) return void 0;
|
|
932
|
+
const superclass = type.class.superclass;
|
|
933
|
+
const inherited = superclass ? analysis.types.aliases.get(superclass) : void 0;
|
|
934
|
+
const own = [...type.properties].filter(([key, property]) => inherited?.kind !== "object" || inherited.properties.get(key) !== property);
|
|
935
|
+
const head = `declare class ${name}${superclass ? ` extends ${superclass}` : ""}`;
|
|
936
|
+
if (!own.length) return `${head} {}`;
|
|
937
|
+
const lines = own.map(([key, property]) => ` ${property.readonly ? "readonly " : ""}${key}${property.optional ? "?" : ""}: ${formatType3(property.type)},`);
|
|
938
|
+
return `${head} {
|
|
939
|
+
${lines.join("\n")}
|
|
940
|
+
}`;
|
|
941
|
+
}
|
|
906
942
|
function typeParameterText(analysis, parameter) {
|
|
907
943
|
return `(type parameter) ${typeParameterSignature(analysis, parameter)}`;
|
|
908
944
|
}
|
|
@@ -962,10 +998,19 @@ ${lines.join("\n")}
|
|
|
962
998
|
}
|
|
963
999
|
return flat;
|
|
964
1000
|
}
|
|
1001
|
+
function bindingText(binding, type) {
|
|
1002
|
+
if (binding.declaredBy === "function" && type.kind === "function") {
|
|
1003
|
+
return `function ${binding.name}${formatType3(type)}`;
|
|
1004
|
+
}
|
|
1005
|
+
return `${keyword(binding)} ${binding.name}: ${pretty(type)}`;
|
|
1006
|
+
}
|
|
965
1007
|
function keyword(binding) {
|
|
966
1008
|
if (binding.kind === "param" || binding.kind === "self") return "(parameter)";
|
|
967
1009
|
if (binding.kind === "global") return "(global)";
|
|
968
1010
|
if (binding.kind.startsWith("for-")) return "(loop variable)";
|
|
1011
|
+
if (binding.declaredBy === "import" || binding.declaredBy === "namespace") return "(import)";
|
|
1012
|
+
if (binding.declaredBy === "type") return "(type import)";
|
|
1013
|
+
if (binding.declaredBy === "function") return "function";
|
|
969
1014
|
return binding.isConst ? "const" : "let";
|
|
970
1015
|
}
|
|
971
1016
|
function code(text) {
|
|
@@ -1038,7 +1083,7 @@ import {
|
|
|
1038
1083
|
CompletionItemKind as CompletionItemKind2,
|
|
1039
1084
|
InsertTextFormat
|
|
1040
1085
|
} from "vscode-languageserver";
|
|
1041
|
-
import { formatType as formatType4 } from "luaut-parser";
|
|
1086
|
+
import { formatType as formatType4, isClassType as isClassType2 } from "luaut-parser";
|
|
1042
1087
|
var PLACEHOLDER = "__luautCompletion__";
|
|
1043
1088
|
var IDENTIFIER_CHAR = /[A-Za-z0-9_]/;
|
|
1044
1089
|
function completion(analyzer, document, position) {
|
|
@@ -1072,10 +1117,10 @@ function completion(analyzer, document, position) {
|
|
|
1072
1117
|
}
|
|
1073
1118
|
if (operator || !first) return [];
|
|
1074
1119
|
if (inTypePosition(first.path)) {
|
|
1075
|
-
const named = [...first.analysis.types.aliases
|
|
1120
|
+
const named = [...first.analysis.types.aliases].map(([name, type]) => ({
|
|
1076
1121
|
label: name,
|
|
1077
|
-
kind: CompletionItemKind2.Interface,
|
|
1078
|
-
detail: "type"
|
|
1122
|
+
kind: isClassType2(type) ? CompletionItemKind2.Class : CompletionItemKind2.Interface,
|
|
1123
|
+
detail: isClassType2(type) ? "class" : "type"
|
|
1079
1124
|
}));
|
|
1080
1125
|
const primitives = PRIMITIVES2.map((name) => ({
|
|
1081
1126
|
label: name,
|
|
@@ -1166,6 +1211,7 @@ function valueItems(analysis, at) {
|
|
|
1166
1211
|
const seen = /* @__PURE__ */ new Set();
|
|
1167
1212
|
for (const binding of analysis.scopes.bindings.values()) {
|
|
1168
1213
|
if (binding.name === PLACEHOLDER || seen.has(binding.name)) continue;
|
|
1214
|
+
if (binding.declaredBy === "type") continue;
|
|
1169
1215
|
const declaration = binding.declarationNode;
|
|
1170
1216
|
if (declaration && declaration.line.start - 1 > at.line) continue;
|
|
1171
1217
|
seen.add(binding.name);
|
|
@@ -1207,7 +1253,7 @@ function kindOf(type, bindingKind) {
|
|
|
1207
1253
|
}
|
|
1208
1254
|
function inTypePosition(path) {
|
|
1209
1255
|
return path.some(
|
|
1210
|
-
(n) => !!n.type && (n.type.endsWith("TypeNode") || n.type === "TypeReference" || n.type === "TypeAliasStatement" || n.type === "ExportTypeAliasStatement")
|
|
1256
|
+
(n) => !!n.type && (n.type.endsWith("TypeNode") || n.type === "TypeReference" || n.type === "TypeAliasStatement" || n.type === "ExportTypeAliasStatement" || n.type === "DeclareClassStatement")
|
|
1211
1257
|
);
|
|
1212
1258
|
}
|
|
1213
1259
|
var PRIMITIVES2 = [
|
|
@@ -1348,6 +1394,12 @@ function documentSymbols(analysis) {
|
|
|
1348
1394
|
}
|
|
1349
1395
|
break;
|
|
1350
1396
|
}
|
|
1397
|
+
case "DeclareClassStatement": {
|
|
1398
|
+
const name = node.name.name;
|
|
1399
|
+
const superclass = node.superclass?.base;
|
|
1400
|
+
out.push(symbol(name, SymbolKind.Class, node, superclass && `extends ${superclass}`));
|
|
1401
|
+
break;
|
|
1402
|
+
}
|
|
1351
1403
|
case "VariableDeclaration": {
|
|
1352
1404
|
for (const target of node.names ?? []) {
|
|
1353
1405
|
const name = target.name;
|
|
@@ -1385,10 +1437,11 @@ function symbol(name, kind, node, detail) {
|
|
|
1385
1437
|
}
|
|
1386
1438
|
|
|
1387
1439
|
// src/features/semanticTokens.ts
|
|
1388
|
-
import { tokenize } from "luaut-parser";
|
|
1440
|
+
import { tokenize, isClassType as isClassType3, unknownType } from "luaut-parser";
|
|
1389
1441
|
var TOKEN_TYPES = [
|
|
1390
1442
|
"namespace",
|
|
1391
1443
|
"type",
|
|
1444
|
+
"class",
|
|
1392
1445
|
"typeParameter",
|
|
1393
1446
|
"parameter",
|
|
1394
1447
|
"variable",
|
|
@@ -1405,6 +1458,7 @@ var semanticTokensLegend = {
|
|
|
1405
1458
|
var SOFT_KEYWORDS = /* @__PURE__ */ new Set([
|
|
1406
1459
|
"type",
|
|
1407
1460
|
"declare",
|
|
1461
|
+
"class",
|
|
1408
1462
|
"extends",
|
|
1409
1463
|
"keyof",
|
|
1410
1464
|
"infer",
|
|
@@ -1471,6 +1525,8 @@ function classify(analysis, spanned, ancestors, identifiers, add) {
|
|
|
1471
1525
|
if (!baseToken) return;
|
|
1472
1526
|
if (!namespace && typeParameterInScope2(ancestors, base)) {
|
|
1473
1527
|
add(baseToken, base.length, "typeParameter");
|
|
1528
|
+
} else if (isClassType3(analysis.types.aliases.get(namespace ? `${namespace}.${base}` : base) ?? unknownType)) {
|
|
1529
|
+
add(baseToken, base.length, "class");
|
|
1474
1530
|
} else {
|
|
1475
1531
|
add(baseToken, base.length, "type", PRIMITIVES3.has(base) ? ["defaultLibrary"] : []);
|
|
1476
1532
|
}
|
|
@@ -1498,6 +1554,9 @@ function identifier(analysis, node, parent, add) {
|
|
|
1498
1554
|
case "ExportTypeAliasStatement":
|
|
1499
1555
|
if (parent.name === node) return as("type", ["declaration"]);
|
|
1500
1556
|
break;
|
|
1557
|
+
case "DeclareClassStatement":
|
|
1558
|
+
if (parent.name === node) return as("class", ["declaration"]);
|
|
1559
|
+
break;
|
|
1501
1560
|
case "DeclareStatement":
|
|
1502
1561
|
if (parent.id === node) return as(isFunction(typeOfNode(parent.valueType)) ? "function" : "variable", ["declaration"]);
|
|
1503
1562
|
break;
|
|
@@ -1521,10 +1580,14 @@ function identifier(analysis, node, parent, add) {
|
|
|
1521
1580
|
break;
|
|
1522
1581
|
case "ImportSpecifier": {
|
|
1523
1582
|
const binding2 = bindingOfNode(analysis, node);
|
|
1583
|
+
if (binding2?.declaredBy === "type") return as("type", ["declaration"]);
|
|
1524
1584
|
const value = binding2 && analysis.types.bindingType.get(binding2.id);
|
|
1525
1585
|
if (analysis.types.aliases.has(name) && (!value || value.kind === "any")) return as("type", ["declaration"]);
|
|
1526
1586
|
break;
|
|
1527
1587
|
}
|
|
1588
|
+
case "ImportStatement":
|
|
1589
|
+
if (parent.namespaceImport === node) return as("namespace", ["declaration"]);
|
|
1590
|
+
break;
|
|
1528
1591
|
case "ExportSpecifier":
|
|
1529
1592
|
if (!bindingOfNode(analysis, node) && analysis.types.aliases.has(name)) return as("type");
|
|
1530
1593
|
break;
|
|
@@ -1540,6 +1603,7 @@ function identifier(analysis, node, parent, add) {
|
|
|
1540
1603
|
function valueKind(analysis, binding) {
|
|
1541
1604
|
if (!binding) return "variable";
|
|
1542
1605
|
if (binding.kind === "param" || binding.kind === "self") return "parameter";
|
|
1606
|
+
if (binding.declaredBy === "namespace") return "namespace";
|
|
1543
1607
|
return isFunction(analysis.types.bindingType.get(binding.id)) ? "function" : "variable";
|
|
1544
1608
|
}
|
|
1545
1609
|
function modifiersOf(binding, isDeclaration) {
|
|
@@ -1810,4 +1874,4 @@ export {
|
|
|
1810
1874
|
createServer,
|
|
1811
1875
|
startServer
|
|
1812
1876
|
};
|
|
1813
|
-
//# sourceMappingURL=chunk-
|
|
1877
|
+
//# sourceMappingURL=chunk-X2GHHRWT.js.map
|