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 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 `${keyword(binding)} ${binding.name}: ${pretty(type2)}`;
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, `const function f`.
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 ? `${keyword(binding)} ${binding.name}: ${pretty(type2)}` : void 0;
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.namespace && !node.typeArguments.length) {
876
- const alias = types.aliases.get(base);
877
- if (alias) return `type ${base} = ${pretty(alias)}`;
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.keys()].map((name) => ({
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-SXRYJV32.js.map
1877
+ //# sourceMappingURL=chunk-X2GHHRWT.js.map