luaut-language-server 4.0.0 → 5.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 +10 -1
- package/dist/{chunk-XK3KS3T4.js → chunk-GHAR7NOH.js} +253 -53
- package/dist/chunk-GHAR7NOH.js.map +1 -0
- package/dist/cli.cjs +266 -66
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/index.cjs +266 -66
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -2
- package/dist/index.d.ts +10 -2
- package/dist/index.js +1 -1
- package/package.json +4 -4
- package/dist/chunk-XK3KS3T4.js.map +0 -1
package/dist/cli.cjs
CHANGED
|
@@ -81,7 +81,8 @@ function membersOf(type, aliases, seen = /* @__PURE__ */ new Set()) {
|
|
|
81
81
|
}
|
|
82
82
|
function takesSelf(type) {
|
|
83
83
|
for (const signature of signaturesOf(type)) {
|
|
84
|
-
|
|
84
|
+
const first = signature.params[0]?.name;
|
|
85
|
+
if (first === "self" || first === "this") return true;
|
|
85
86
|
}
|
|
86
87
|
return false;
|
|
87
88
|
}
|
|
@@ -102,7 +103,7 @@ function signatureLabel(signature) {
|
|
|
102
103
|
});
|
|
103
104
|
const generics = signature.typeParams?.length ? `<${signature.typeParams.join(", ")}>` : "";
|
|
104
105
|
const varargs = signature.varargs ? [`...: ${(0, import_luaut_parser.formatType)(signature.varargs)}`] : [];
|
|
105
|
-
const label = `${generics}(${[...parameters, ...varargs].join(", ")})
|
|
106
|
+
const label = `${generics}(${[...parameters, ...varargs].join(", ")}) => ${(0, import_luaut_parser.formatType)(signature.returns)}`;
|
|
106
107
|
return { label, parameters };
|
|
107
108
|
}
|
|
108
109
|
|
|
@@ -682,7 +683,7 @@ function exportDeclaration(analyzer, module2, name, seen = /* @__PURE__ */ new S
|
|
|
682
683
|
break;
|
|
683
684
|
case "ExportStatement": {
|
|
684
685
|
const declaration = statement.declaration;
|
|
685
|
-
if (declaration.type === "FunctionDeclaration") {
|
|
686
|
+
if (declaration.type === "FunctionDeclaration" || declaration.type === "ClassDeclaration") {
|
|
686
687
|
if (declaration.name.name === name) return here(declaration.name);
|
|
687
688
|
} else {
|
|
688
689
|
for (const target of declaration.names) {
|
|
@@ -794,16 +795,100 @@ function diagnostics(analysis) {
|
|
|
794
795
|
}
|
|
795
796
|
|
|
796
797
|
// src/features/hover.ts
|
|
798
|
+
var import_luaut_parser6 = require("luaut-parser");
|
|
799
|
+
|
|
800
|
+
// src/features/expand.ts
|
|
797
801
|
var import_luaut_parser5 = require("luaut-parser");
|
|
798
|
-
function
|
|
802
|
+
function expandAliases(type, aliases, depth, seen = []) {
|
|
803
|
+
if (depth <= 0) return type;
|
|
804
|
+
const name = withheldName(type);
|
|
805
|
+
if (name !== void 0 && !seen.includes(name)) {
|
|
806
|
+
const opened = aliases.get(name) ?? type;
|
|
807
|
+
const inner = [...seen, name];
|
|
808
|
+
return children2(unnamed(opened), (t) => expandAliases(t, aliases, depth - 1, inner));
|
|
809
|
+
}
|
|
810
|
+
return children2(type, (t) => expandAliases(t, aliases, depth, seen));
|
|
811
|
+
}
|
|
812
|
+
function withheldName(type) {
|
|
813
|
+
if ((0, import_luaut_parser5.isClassType)(type)) return void 0;
|
|
814
|
+
if (type.kind === "object" || type.kind === "intersection") return type.name;
|
|
815
|
+
if (type.kind === "genericRef") return type.typeArguments.length ? void 0 : type.name;
|
|
816
|
+
return void 0;
|
|
817
|
+
}
|
|
818
|
+
function unnamed(type) {
|
|
819
|
+
if (type.kind === "object") {
|
|
820
|
+
const out = (0, import_luaut_parser5.objectType)(type.properties, type.indexer, type.frozen);
|
|
821
|
+
return type.class ? Object.assign(out, { class: type.class, name: type.name }) : out;
|
|
822
|
+
}
|
|
823
|
+
if (type.kind === "intersection" && type.name) return { ...type, name: void 0 };
|
|
824
|
+
return type;
|
|
825
|
+
}
|
|
826
|
+
function children2(type, f) {
|
|
827
|
+
switch (type.kind) {
|
|
828
|
+
case "array":
|
|
829
|
+
return (0, import_luaut_parser5.arrayOf)(f(type.element));
|
|
830
|
+
case "tuple":
|
|
831
|
+
return (0, import_luaut_parser5.tuple)(type.elements.map(f), type.isPack);
|
|
832
|
+
case "union":
|
|
833
|
+
return (0, import_luaut_parser5.union)(type.types.map(f));
|
|
834
|
+
case "intersection": {
|
|
835
|
+
const out = (0, import_luaut_parser5.intersection)(type.types.map(f));
|
|
836
|
+
return type.name && out.kind === "intersection" ? { ...out, name: type.name } : out;
|
|
837
|
+
}
|
|
838
|
+
case "object": {
|
|
839
|
+
if ((0, import_luaut_parser5.isClassType)(type)) return type;
|
|
840
|
+
const out = (0, import_luaut_parser5.objectType)(
|
|
841
|
+
[...type.properties].map(([name, property]) => [name, { ...property, type: f(property.type) }]),
|
|
842
|
+
type.indexer && { key: type.indexer.key, value: f(type.indexer.value) },
|
|
843
|
+
type.frozen
|
|
844
|
+
);
|
|
845
|
+
if (type.name) out.name = type.name;
|
|
846
|
+
return out;
|
|
847
|
+
}
|
|
848
|
+
case "function":
|
|
849
|
+
return (0, import_luaut_parser5.fn)(
|
|
850
|
+
type.params.map((p) => ({ ...p, type: f(p.type) })),
|
|
851
|
+
f(type.returns),
|
|
852
|
+
type.varargs && f(type.varargs),
|
|
853
|
+
type.typeParams,
|
|
854
|
+
type.predicate
|
|
855
|
+
);
|
|
856
|
+
default:
|
|
857
|
+
return type;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
// src/features/hover.ts
|
|
862
|
+
function hover(analysis, position, depth = 0) {
|
|
799
863
|
const path = pathAt(analysis.program, position, true);
|
|
800
864
|
for (let i = path.length - 1; i >= 0; i--) {
|
|
801
865
|
if (UNNAMED.has(path[i].type)) return null;
|
|
802
|
-
const text =
|
|
803
|
-
if (text)
|
|
866
|
+
const text = at(analysis, path, i, depth);
|
|
867
|
+
if (!text) continue;
|
|
868
|
+
const canExpand = at(analysis, path, i, depth + 1) !== text;
|
|
869
|
+
return {
|
|
870
|
+
contents: { kind: "markdown", value: code(text) },
|
|
871
|
+
range: toRange(path[i]),
|
|
872
|
+
depth,
|
|
873
|
+
canExpand
|
|
874
|
+
};
|
|
804
875
|
}
|
|
805
876
|
return null;
|
|
806
877
|
}
|
|
878
|
+
function at(analysis, path, index, depth) {
|
|
879
|
+
const previous = expansion;
|
|
880
|
+
expansion = { depth, aliases: analysis.types.aliases, source: analysis.source };
|
|
881
|
+
try {
|
|
882
|
+
return describe(analysis, path, index);
|
|
883
|
+
} finally {
|
|
884
|
+
expansion = previous;
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
var expansion = {
|
|
888
|
+
depth: 0,
|
|
889
|
+
aliases: /* @__PURE__ */ new Map(),
|
|
890
|
+
source: ""
|
|
891
|
+
};
|
|
807
892
|
var UNNAMED = /* @__PURE__ */ new Set([
|
|
808
893
|
"BinaryExpression",
|
|
809
894
|
"UnaryExpression",
|
|
@@ -836,8 +921,8 @@ function describe(analysis, path, index) {
|
|
|
836
921
|
case "TableExpression": {
|
|
837
922
|
const field = fieldWithKey(parent, node);
|
|
838
923
|
if (!field) break;
|
|
839
|
-
const
|
|
840
|
-
const property =
|
|
924
|
+
const objectType2 = types.typeOf.get(parent);
|
|
925
|
+
const property = objectType2?.kind === "object" ? objectType2.properties.get(name) : void 0;
|
|
841
926
|
const type2 = property?.type ?? types.typeOf.get(field.value);
|
|
842
927
|
return type2 && `(property) ${name}: ${pretty(type2)}`;
|
|
843
928
|
}
|
|
@@ -885,6 +970,19 @@ function describe(analysis, path, index) {
|
|
|
885
970
|
case "DeclareClassStatement":
|
|
886
971
|
if (parent.name === node) return classText(analysis, name);
|
|
887
972
|
break;
|
|
973
|
+
case "ClassDeclaration":
|
|
974
|
+
if (parent.name === node) return classText(analysis, name);
|
|
975
|
+
break;
|
|
976
|
+
case "ClassField":
|
|
977
|
+
if (parent.name === node) {
|
|
978
|
+
const type2 = typeOfNode(parent.typeAnnotation) ?? types.typeOf.get(parent.init);
|
|
979
|
+
const prefix = parent.isStatic ? "(static) " : "(field) ";
|
|
980
|
+
return type2 ? `${prefix}${name}: ${pretty(type2)}` : `${prefix}${name}`;
|
|
981
|
+
}
|
|
982
|
+
break;
|
|
983
|
+
case "ClassAccessor":
|
|
984
|
+
if (parent.name === node) return `(${parent.kind === "get" ? "getter" : "setter"}) ${name}`;
|
|
985
|
+
break;
|
|
888
986
|
case "TableTypeProperty":
|
|
889
987
|
if (parent.key === node) {
|
|
890
988
|
const type2 = typeOfNode(parent.valueType);
|
|
@@ -907,7 +1005,7 @@ function describe(analysis, path, index) {
|
|
|
907
1005
|
case "MappedTypeNode":
|
|
908
1006
|
if (parent.parameterId === node) {
|
|
909
1007
|
const keys = typeOfNode(parent.constraint);
|
|
910
|
-
return `(type parameter) ${name}${keys ? ` in ${(0,
|
|
1008
|
+
return `(type parameter) ${name}${keys ? ` in ${(0, import_luaut_parser6.formatType)(keys)}` : ""}`;
|
|
911
1009
|
}
|
|
912
1010
|
break;
|
|
913
1011
|
}
|
|
@@ -935,7 +1033,7 @@ function describe(analysis, path, index) {
|
|
|
935
1033
|
case "TypedIdentifier": {
|
|
936
1034
|
const binding = bindingOfNode(analysis, node);
|
|
937
1035
|
const type2 = binding && types.bindingType.get(binding.id);
|
|
938
|
-
return type2 ? bindingText(binding, type2) : void 0;
|
|
1036
|
+
return type2 ? bindingText(binding, type2, asWritten(node.typeAnnotation)) : void 0;
|
|
939
1037
|
}
|
|
940
1038
|
// A type written by name: `number`, `Shape`, `Partial<User>`, or a type
|
|
941
1039
|
// parameter in scope.
|
|
@@ -949,7 +1047,7 @@ function describe(analysis, path, index) {
|
|
|
949
1047
|
if (!node.typeArguments.length) {
|
|
950
1048
|
const qualified = node.namespace ? `${node.namespace}.${base}` : base;
|
|
951
1049
|
const alias = types.aliases.get(qualified);
|
|
952
|
-
if (alias && (0,
|
|
1050
|
+
if (alias && (0, import_luaut_parser6.isClassType)(alias)) return classText(analysis, qualified);
|
|
953
1051
|
if (alias) return `type ${qualified} = ${pretty(alias)}`;
|
|
954
1052
|
}
|
|
955
1053
|
const type2 = typeOfNode(node);
|
|
@@ -977,20 +1075,29 @@ function declareText(analysis, statement) {
|
|
|
977
1075
|
const total = analysis.program.body.statements.filter((s) => s.type === "DeclareStatement" && s.name === name).reduce((n, s) => n + signaturesOf(analysis.types.typeOfTypeNode.get(s.valueType)).length, 0);
|
|
978
1076
|
const others = total - 1;
|
|
979
1077
|
const overloads = others > 0 ? ` (+${others} overload${others > 1 ? "s" : ""})` : "";
|
|
980
|
-
return `declare function ${name}${(0,
|
|
1078
|
+
return `declare function ${name}${(0, import_luaut_parser6.formatType)(own)}${overloads}`;
|
|
981
1079
|
}
|
|
982
1080
|
function classText(analysis, name) {
|
|
983
1081
|
const type = analysis.types.aliases.get(name);
|
|
984
|
-
if (!type || !(0,
|
|
1082
|
+
if (!type || !(0, import_luaut_parser6.isClassType)(type)) return void 0;
|
|
985
1083
|
const superclass = type.class.superclass;
|
|
986
1084
|
const inherited = superclass ? analysis.types.aliases.get(superclass) : void 0;
|
|
987
|
-
const own = [...type.properties].filter(([key, property]) =>
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
1085
|
+
const own = [...type.properties].filter(([key, property]) => (
|
|
1086
|
+
// `ClassObject` is on every instance and says nothing about this one.
|
|
1087
|
+
key !== "ClassObject" && (inherited?.kind !== "object" || inherited.properties.get(key) !== property)
|
|
1088
|
+
));
|
|
1089
|
+
const written = isRuntimeClass(analysis, name);
|
|
1090
|
+
const head = `${written ? "" : "declare "}class ${name}${superclass ? ` extends ${superclass}` : ""}`;
|
|
1091
|
+
const lines = own.map(([key, property]) => ` ${property.readonly ? "readonly " : ""}${key}${property.optional ? "?" : ""}: ${(0, import_luaut_parser6.formatType)(property.type)}${written ? "" : ","}`);
|
|
1092
|
+
return own.length ? `${head} {
|
|
992
1093
|
${lines.join("\n")}
|
|
993
|
-
}`;
|
|
1094
|
+
}` : `${head} {}`;
|
|
1095
|
+
}
|
|
1096
|
+
function isRuntimeClass(analysis, name) {
|
|
1097
|
+
return analysis.program.body.statements.some((statement) => {
|
|
1098
|
+
const declaration = statement.type === "ExportStatement" ? statement.declaration : statement;
|
|
1099
|
+
return declaration.type === "ClassDeclaration" && declaration.name.name === name;
|
|
1100
|
+
});
|
|
994
1101
|
}
|
|
995
1102
|
function typeParameterText(analysis, parameter) {
|
|
996
1103
|
return `(type parameter) ${typeParameterSignature(analysis, parameter)}`;
|
|
@@ -999,7 +1106,7 @@ function typeParameterSignature(analysis, parameter) {
|
|
|
999
1106
|
const p = parameter;
|
|
1000
1107
|
if (p.infer) return `infer ${p.name}`;
|
|
1001
1108
|
const constraint = p.constraint ? analysis.types.typeOfTypeNode.get(p.constraint) : void 0;
|
|
1002
|
-
return `${p.isConst ? "const " : ""}${p.name}${constraint ? ` extends ${(0,
|
|
1109
|
+
return `${p.isConst ? "const " : ""}${p.name}${constraint ? ` extends ${(0, import_luaut_parser6.formatType)(constraint)}` : ""}`;
|
|
1003
1110
|
}
|
|
1004
1111
|
function typeParameterInScope(path, index, name) {
|
|
1005
1112
|
for (let i = index - 1; i >= 0; i--) {
|
|
@@ -1024,7 +1131,7 @@ function referenceText(analysis, reference) {
|
|
|
1024
1131
|
if (!args.length) return name;
|
|
1025
1132
|
const resolved = args.map((a) => {
|
|
1026
1133
|
const t = analysis.types.typeOfTypeNode.get(a);
|
|
1027
|
-
return t ? (0,
|
|
1134
|
+
return t ? (0, import_luaut_parser6.formatType)(t) : "?";
|
|
1028
1135
|
});
|
|
1029
1136
|
return `${name}<${resolved.join(", ")}>`;
|
|
1030
1137
|
}
|
|
@@ -1032,30 +1139,50 @@ function fieldWithKey(table, key) {
|
|
|
1032
1139
|
const fields = table.fields;
|
|
1033
1140
|
return fields.find((f) => f.type === "TableFieldNamed" && f.key === key);
|
|
1034
1141
|
}
|
|
1035
|
-
function pretty(type) {
|
|
1036
|
-
const
|
|
1142
|
+
function pretty(type, written) {
|
|
1143
|
+
const named = render(expandAliases(type, expansion.aliases, 0));
|
|
1144
|
+
const shorthand = written !== void 0 && written !== named ? written : void 0;
|
|
1145
|
+
const depth = shorthand === void 0 ? expansion.depth : expansion.depth - 1;
|
|
1146
|
+
if (depth < 0) return shorthand;
|
|
1147
|
+
return depth === 0 ? named : render(expandAliases(type, expansion.aliases, depth));
|
|
1148
|
+
}
|
|
1149
|
+
function asWritten(node) {
|
|
1150
|
+
if (!isSpanned(node) || !expansion.source) return void 0;
|
|
1151
|
+
const lines = expansion.source.split(/\r?\n/);
|
|
1152
|
+
const { line, column } = node;
|
|
1153
|
+
if (line.start < 1 || line.end > lines.length) return void 0;
|
|
1154
|
+
const text = line.start === line.end ? lines[line.start - 1].slice(column.start - 1, column.end - 1) : [
|
|
1155
|
+
lines[line.start - 1].slice(column.start - 1),
|
|
1156
|
+
...lines.slice(line.start, line.end - 1),
|
|
1157
|
+
lines[line.end - 1].slice(0, column.end - 1)
|
|
1158
|
+
].join(" ");
|
|
1159
|
+
const folded = text.trim().replace(/\s+/g, " ");
|
|
1160
|
+
return folded.length ? folded : void 0;
|
|
1161
|
+
}
|
|
1162
|
+
function render(type) {
|
|
1163
|
+
const flat = (0, import_luaut_parser6.formatType)(type);
|
|
1037
1164
|
if (flat.length <= 80) return flat;
|
|
1038
1165
|
if (type.kind === "object") {
|
|
1039
1166
|
const lines = [];
|
|
1040
|
-
if (type.indexer) lines.push(` [${(0,
|
|
1167
|
+
if (type.indexer) lines.push(` [${(0, import_luaut_parser6.formatType)(type.indexer.key)}]: ${(0, import_luaut_parser6.formatType)(type.indexer.value)},`);
|
|
1041
1168
|
for (const [name, property] of type.properties) {
|
|
1042
1169
|
const readonly = property.readonly ? "readonly " : "";
|
|
1043
|
-
lines.push(` ${readonly}${name}${property.optional ? "?" : ""}: ${(0,
|
|
1170
|
+
lines.push(` ${readonly}${name}${property.optional ? "?" : ""}: ${(0, import_luaut_parser6.formatType)(property.type)},`);
|
|
1044
1171
|
}
|
|
1045
1172
|
return `{
|
|
1046
1173
|
${lines.join("\n")}
|
|
1047
1174
|
}`;
|
|
1048
1175
|
}
|
|
1049
1176
|
if (type.kind === "intersection" && type.types.every((t) => t.kind === "function")) {
|
|
1050
|
-
return type.types.map(
|
|
1177
|
+
return type.types.map(import_luaut_parser6.formatType).join("\n& ");
|
|
1051
1178
|
}
|
|
1052
1179
|
return flat;
|
|
1053
1180
|
}
|
|
1054
|
-
function bindingText(binding, type) {
|
|
1181
|
+
function bindingText(binding, type, written) {
|
|
1055
1182
|
if (binding.declaredBy === "function" && type.kind === "function") {
|
|
1056
|
-
return `function ${binding.name}${(
|
|
1183
|
+
return `function ${binding.name}${pretty(type)}`;
|
|
1057
1184
|
}
|
|
1058
|
-
return `${keyword(binding)} ${binding.name}: ${pretty(type)}`;
|
|
1185
|
+
return `${keyword(binding)} ${binding.name}: ${pretty(type, written)}`;
|
|
1059
1186
|
}
|
|
1060
1187
|
function keyword(binding) {
|
|
1061
1188
|
if (binding.kind === "param" || binding.kind === "self") return "(parameter)";
|
|
@@ -1131,7 +1258,7 @@ function isIdentifier(name) {
|
|
|
1131
1258
|
|
|
1132
1259
|
// src/features/completion.ts
|
|
1133
1260
|
var import_vscode_languageserver5 = require("vscode-languageserver");
|
|
1134
|
-
var
|
|
1261
|
+
var import_luaut_parser7 = require("luaut-parser");
|
|
1135
1262
|
|
|
1136
1263
|
// src/features/autoImport.ts
|
|
1137
1264
|
var import_node_fs3 = require("fs");
|
|
@@ -1168,7 +1295,7 @@ function importItems(analyzer, analysis, typePosition, taken) {
|
|
|
1168
1295
|
function serviceItems(analysis, taken) {
|
|
1169
1296
|
const services = analysis.types.aliases.get("Services");
|
|
1170
1297
|
if (!services || services.kind !== "object") return [];
|
|
1171
|
-
const
|
|
1298
|
+
const at2 = serviceInsertion(analysis.program.body.statements);
|
|
1172
1299
|
const items = [];
|
|
1173
1300
|
for (const name of services.properties.keys()) {
|
|
1174
1301
|
if (taken.has(name)) continue;
|
|
@@ -1179,8 +1306,8 @@ function serviceItems(analysis, taken) {
|
|
|
1179
1306
|
labelDetails: { description: "service" },
|
|
1180
1307
|
detail: line,
|
|
1181
1308
|
sortText: `5${name}`,
|
|
1182
|
-
additionalTextEdits: [{ range: { start:
|
|
1183
|
-
${
|
|
1309
|
+
additionalTextEdits: [{ range: { start: at2.position, end: at2.position }, newText: `${line}
|
|
1310
|
+
${at2.gap}` }]
|
|
1184
1311
|
});
|
|
1185
1312
|
}
|
|
1186
1313
|
return items;
|
|
@@ -1192,24 +1319,24 @@ function importEdit(analyzer, analysis, file, name, specifier, typePosition) {
|
|
|
1192
1319
|
if (existing) {
|
|
1193
1320
|
const last = existing.specifiers[existing.specifiers.length - 1];
|
|
1194
1321
|
if (last) {
|
|
1195
|
-
const
|
|
1196
|
-
return { range: { start:
|
|
1322
|
+
const at3 = endOf(last);
|
|
1323
|
+
return { range: { start: at3, end: at3 }, newText: `, ${name}` };
|
|
1197
1324
|
}
|
|
1198
1325
|
if (existing.defaultImport) {
|
|
1199
|
-
const
|
|
1200
|
-
return { range: { start:
|
|
1326
|
+
const at3 = endOf(existing.defaultImport);
|
|
1327
|
+
return { range: { start: at3, end: at3 }, newText: `, { ${name} }` };
|
|
1201
1328
|
}
|
|
1202
1329
|
}
|
|
1203
1330
|
const line = `import { ${name} } from "${specifier}"`;
|
|
1204
1331
|
const lastImport = imports[imports.length - 1];
|
|
1205
1332
|
if (lastImport) {
|
|
1206
|
-
const
|
|
1207
|
-
return { range: { start:
|
|
1333
|
+
const at3 = { line: lastImport.line.end, character: 0 };
|
|
1334
|
+
return { range: { start: at3, end: at3 }, newText: `${line}
|
|
1208
1335
|
` };
|
|
1209
1336
|
}
|
|
1210
1337
|
const first = statements[0];
|
|
1211
|
-
const
|
|
1212
|
-
return { range: { start:
|
|
1338
|
+
const at2 = { line: first ? first.line.start - 1 : 0, character: 0 };
|
|
1339
|
+
return { range: { start: at2, end: at2 }, newText: first ? `${line}
|
|
1213
1340
|
|
|
1214
1341
|
` : `${line}
|
|
1215
1342
|
` };
|
|
@@ -1302,12 +1429,12 @@ function completion(analyzer, document, position) {
|
|
|
1302
1429
|
const operator = memberOperator(source, start);
|
|
1303
1430
|
const alreadyCalled = /^\s*\(/.test(source.slice(end));
|
|
1304
1431
|
const standIns = operator === ":" ? [alreadyCalled ? PLACEHOLDER : `${PLACEHOLDER}()`] : operator === "." && !alreadyCalled ? [PLACEHOLDER, `${PLACEHOLDER}()`] : [PLACEHOLDER];
|
|
1305
|
-
const
|
|
1432
|
+
const at2 = { line: position.line, character: position.character - (offset - start) };
|
|
1306
1433
|
let first;
|
|
1307
1434
|
for (const standIn of standIns) {
|
|
1308
1435
|
const patched = source.slice(0, start) + standIn + source.slice(end);
|
|
1309
1436
|
const analysis = analyzer.analyze(document.uri, -1, patched);
|
|
1310
|
-
const path = pathAt(analysis.program,
|
|
1437
|
+
const path = pathAt(analysis.program, at2, true);
|
|
1311
1438
|
const index = path.findLastIndex(
|
|
1312
1439
|
(n) => n.type === "Identifier" && n.name === PLACEHOLDER
|
|
1313
1440
|
);
|
|
@@ -1323,8 +1450,8 @@ function completion(analyzer, document, position) {
|
|
|
1323
1450
|
if (inTypePosition(first.path)) {
|
|
1324
1451
|
const named = [...first.analysis.types.aliases].map(([name, type]) => ({
|
|
1325
1452
|
label: name,
|
|
1326
|
-
kind: (0,
|
|
1327
|
-
detail: (0,
|
|
1453
|
+
kind: (0, import_luaut_parser7.isClassType)(type) ? import_vscode_languageserver5.CompletionItemKind.Class : import_vscode_languageserver5.CompletionItemKind.Interface,
|
|
1454
|
+
detail: (0, import_luaut_parser7.isClassType)(type) ? "class" : "type"
|
|
1328
1455
|
}));
|
|
1329
1456
|
const primitives = PRIMITIVES2.map((name) => ({
|
|
1330
1457
|
label: name,
|
|
@@ -1344,7 +1471,7 @@ function completion(analyzer, document, position) {
|
|
|
1344
1471
|
for (const binding of first.analysis.scopes.bindings.values()) taken.add(binding.name);
|
|
1345
1472
|
const current = analyzer.get(document);
|
|
1346
1473
|
return [
|
|
1347
|
-
...valueItems(first.analysis,
|
|
1474
|
+
...valueItems(first.analysis, at2, insideFunction(first.path)),
|
|
1348
1475
|
...contextKeywords(source.slice(0, start)),
|
|
1349
1476
|
...importItems(analyzer, current, false, taken),
|
|
1350
1477
|
...serviceItems(current, taken)
|
|
@@ -1412,14 +1539,14 @@ function objectKeyItems(analysis, path, after) {
|
|
|
1412
1539
|
return members.filter((member) => !written.has(member.name)).map((member) => ({
|
|
1413
1540
|
label: member.name,
|
|
1414
1541
|
kind: import_vscode_languageserver5.CompletionItemKind.Field,
|
|
1415
|
-
detail: `${member.property.optional ? "?" : ""}: ${(0,
|
|
1542
|
+
detail: `${member.property.optional ? "?" : ""}: ${(0, import_luaut_parser7.formatType)(member.property.type)}`,
|
|
1416
1543
|
insertText: colon ? member.name : `${member.name}: `
|
|
1417
1544
|
}));
|
|
1418
1545
|
}
|
|
1419
1546
|
function indexKeys(analysis, position, literal) {
|
|
1420
1547
|
const path = pathAt(analysis.program, position, false);
|
|
1421
|
-
const
|
|
1422
|
-
const parent =
|
|
1548
|
+
const at2 = path.indexOf(literal);
|
|
1549
|
+
const parent = at2 > 0 ? path[at2 - 1] : void 0;
|
|
1423
1550
|
if (!parent) return [];
|
|
1424
1551
|
let indexed;
|
|
1425
1552
|
if (parent.type === "IndexedAccessTypeNode" && parent.indexType === literal) {
|
|
@@ -1511,20 +1638,24 @@ function isMethodOnly(type) {
|
|
|
1511
1638
|
return false;
|
|
1512
1639
|
}
|
|
1513
1640
|
}
|
|
1514
|
-
function
|
|
1641
|
+
function insideFunction(path) {
|
|
1642
|
+
return path.some((n) => n.type === "FunctionExpression" || n.type === "FunctionDeclaration" || n.type === "FunctionDeclarationStatement" || n.type === "FunctionBody");
|
|
1643
|
+
}
|
|
1644
|
+
function valueItems(analysis, at2, inFunction) {
|
|
1515
1645
|
const items = [];
|
|
1516
1646
|
const seen = /* @__PURE__ */ new Set();
|
|
1517
1647
|
for (const binding of analysis.scopes.bindings.values()) {
|
|
1518
1648
|
if (binding.name === PLACEHOLDER || seen.has(binding.name)) continue;
|
|
1519
1649
|
if (binding.declaredBy === "type") continue;
|
|
1520
1650
|
const declaration = binding.declarationNode;
|
|
1521
|
-
|
|
1651
|
+
const later = declaration !== void 0 && declaration.line.start - 1 > at2.line;
|
|
1652
|
+
if (later && !inFunction && binding.declaredBy !== "function") continue;
|
|
1522
1653
|
seen.add(binding.name);
|
|
1523
1654
|
const type = analysis.types.bindingType.get(binding.id);
|
|
1524
1655
|
items.push({
|
|
1525
1656
|
label: binding.name,
|
|
1526
1657
|
kind: kindOf(type, binding.kind),
|
|
1527
|
-
detail: type ? (0,
|
|
1658
|
+
detail: type ? (0, import_luaut_parser7.formatType)(type) : void 0,
|
|
1528
1659
|
// Locals before globals, and globals before library names.
|
|
1529
1660
|
sortText: `${binding.isBuiltin ? 2 : binding.kind === "global" ? 1 : 0}${binding.name}`
|
|
1530
1661
|
});
|
|
@@ -1548,7 +1679,7 @@ function memberItem(name, type, readonly) {
|
|
|
1548
1679
|
return {
|
|
1549
1680
|
label: name,
|
|
1550
1681
|
kind: import_vscode_languageserver5.CompletionItemKind.Field,
|
|
1551
|
-
detail: `${readonly ? "readonly " : ""}${(0,
|
|
1682
|
+
detail: `${readonly ? "readonly " : ""}${(0, import_luaut_parser7.formatType)(type)}`
|
|
1552
1683
|
};
|
|
1553
1684
|
}
|
|
1554
1685
|
function kindOf(type, bindingKind) {
|
|
@@ -1656,9 +1787,9 @@ function helpAt(analysis, position) {
|
|
|
1656
1787
|
function methodType(analysis, call) {
|
|
1657
1788
|
const object = call.object;
|
|
1658
1789
|
const method = call.method;
|
|
1659
|
-
const
|
|
1660
|
-
if (!
|
|
1661
|
-
return memberType(
|
|
1790
|
+
const objectType2 = analysis.types.typeOf.get(object);
|
|
1791
|
+
if (!objectType2) return void 0;
|
|
1792
|
+
return memberType(objectType2, method.name, analysis);
|
|
1662
1793
|
}
|
|
1663
1794
|
function memberType(type, name, analysis) {
|
|
1664
1795
|
if (type.kind === "object") return type.properties.get(name)?.type;
|
|
@@ -1689,7 +1820,7 @@ function activeArgument(call, position) {
|
|
|
1689
1820
|
|
|
1690
1821
|
// src/features/symbols.ts
|
|
1691
1822
|
var import_vscode_languageserver6 = require("vscode-languageserver");
|
|
1692
|
-
var
|
|
1823
|
+
var import_luaut_parser8 = require("luaut-parser");
|
|
1693
1824
|
function documentSymbols(analysis) {
|
|
1694
1825
|
const out = [];
|
|
1695
1826
|
walk(analysis.program, (node) => {
|
|
@@ -1706,10 +1837,19 @@ function documentSymbols(analysis) {
|
|
|
1706
1837
|
const name = typeof named === "string" ? named : named?.name;
|
|
1707
1838
|
if (name) {
|
|
1708
1839
|
const alias = analysis.types.aliases.get(name);
|
|
1709
|
-
out.push(symbol(name, import_vscode_languageserver6.SymbolKind.Interface, node, alias ? (0,
|
|
1840
|
+
out.push(symbol(name, import_vscode_languageserver6.SymbolKind.Interface, node, alias ? (0, import_luaut_parser8.formatType)(alias) : void 0));
|
|
1710
1841
|
}
|
|
1711
1842
|
break;
|
|
1712
1843
|
}
|
|
1844
|
+
case "ClassDeclaration": {
|
|
1845
|
+
const declaration = node;
|
|
1846
|
+
const superclass = declaration.superclass?.name;
|
|
1847
|
+
out.push({
|
|
1848
|
+
...symbol(declaration.name.name, import_vscode_languageserver6.SymbolKind.Class, node, superclass && `extends ${superclass}`),
|
|
1849
|
+
children: declaration.members.map((member) => classMember(analysis, member))
|
|
1850
|
+
});
|
|
1851
|
+
break;
|
|
1852
|
+
}
|
|
1713
1853
|
case "DeclareClassStatement": {
|
|
1714
1854
|
const name = node.name.name;
|
|
1715
1855
|
const superclass = node.superclass?.base;
|
|
@@ -1727,6 +1867,23 @@ function documentSymbols(analysis) {
|
|
|
1727
1867
|
});
|
|
1728
1868
|
return out;
|
|
1729
1869
|
}
|
|
1870
|
+
function classMember(analysis, member) {
|
|
1871
|
+
switch (member.type) {
|
|
1872
|
+
case "ClassConstructor":
|
|
1873
|
+
return symbol("constructor", import_vscode_languageserver6.SymbolKind.Constructor, member);
|
|
1874
|
+
case "ClassField":
|
|
1875
|
+
return symbol(
|
|
1876
|
+
member.name.name,
|
|
1877
|
+
member.isStatic ? import_vscode_languageserver6.SymbolKind.Constant : import_vscode_languageserver6.SymbolKind.Field,
|
|
1878
|
+
member,
|
|
1879
|
+
member.typeAnnotation ? void 0 : detailOf(analysis, member)
|
|
1880
|
+
);
|
|
1881
|
+
case "ClassAccessor":
|
|
1882
|
+
return symbol(member.name.name, import_vscode_languageserver6.SymbolKind.Property, member, member.kind);
|
|
1883
|
+
case "ClassMethod":
|
|
1884
|
+
return symbol(member.name.name, import_vscode_languageserver6.SymbolKind.Method, member, member.isStatic ? "static" : void 0);
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1730
1887
|
function functionName(node) {
|
|
1731
1888
|
const named = node;
|
|
1732
1889
|
if (typeof named.name === "string") return named.name;
|
|
@@ -1743,7 +1900,7 @@ function detailOf(analysis, node) {
|
|
|
1743
1900
|
if (name && typeof name === "object") {
|
|
1744
1901
|
const binding = bindingOfNode(analysis, name);
|
|
1745
1902
|
const type = binding && analysis.types.bindingType.get(binding.id);
|
|
1746
|
-
if (type) return (0,
|
|
1903
|
+
if (type) return (0, import_luaut_parser8.formatType)(type);
|
|
1747
1904
|
}
|
|
1748
1905
|
return void 0;
|
|
1749
1906
|
}
|
|
@@ -1753,7 +1910,7 @@ function symbol(name, kind, node, detail) {
|
|
|
1753
1910
|
}
|
|
1754
1911
|
|
|
1755
1912
|
// src/features/semanticTokens.ts
|
|
1756
|
-
var
|
|
1913
|
+
var import_luaut_parser9 = require("luaut-parser");
|
|
1757
1914
|
var TOKEN_TYPES = [
|
|
1758
1915
|
"namespace",
|
|
1759
1916
|
"type",
|
|
@@ -1783,21 +1940,23 @@ var SOFT_KEYWORDS = /* @__PURE__ */ new Set([
|
|
|
1783
1940
|
"asserts",
|
|
1784
1941
|
"satisfies",
|
|
1785
1942
|
"typeof",
|
|
1786
|
-
"default"
|
|
1943
|
+
"default",
|
|
1944
|
+
"new",
|
|
1945
|
+
"super"
|
|
1787
1946
|
]);
|
|
1788
1947
|
var CONTROL_KEYWORDS = /* @__PURE__ */ new Set(["default"]);
|
|
1789
1948
|
var PRIMITIVES3 = /* @__PURE__ */ new Set(["any", "unknown", "never", "nil", "boolean", "number", "string", "thread", "buffer"]);
|
|
1790
1949
|
function semanticTokens(analysis) {
|
|
1791
1950
|
const entries = /* @__PURE__ */ new Map();
|
|
1792
|
-
const add = (
|
|
1793
|
-
const line =
|
|
1794
|
-
const character =
|
|
1951
|
+
const add = (at2, length, type, modifiers = []) => {
|
|
1952
|
+
const line = at2.line.start - 1;
|
|
1953
|
+
const character = at2.column.start - 1;
|
|
1795
1954
|
const key = `${line}:${character}`;
|
|
1796
1955
|
if (!entries.has(key)) entries.set(key, { line, character, length, type, modifiers });
|
|
1797
1956
|
};
|
|
1798
1957
|
let tokens = [];
|
|
1799
1958
|
try {
|
|
1800
|
-
tokens = (0,
|
|
1959
|
+
tokens = (0, import_luaut_parser9.tokenize)(analysis.source);
|
|
1801
1960
|
} catch {
|
|
1802
1961
|
}
|
|
1803
1962
|
const identifiers = tokens.filter((t) => t.type === "Identifier");
|
|
@@ -1832,6 +1991,25 @@ function classify(analysis, spanned, ancestors, identifiers, add) {
|
|
|
1832
1991
|
add(node, name.length, valueKind(analysis, binding), modifiersOf(binding, true));
|
|
1833
1992
|
return;
|
|
1834
1993
|
}
|
|
1994
|
+
// A class body's own words. `get`, `set`, `static` and `constructor`
|
|
1995
|
+
// are ordinary names anywhere else, so they are coloured from the
|
|
1996
|
+
// member they open rather than wherever they are written.
|
|
1997
|
+
case "ClassMethod":
|
|
1998
|
+
case "ClassField":
|
|
1999
|
+
case "ClassAccessor":
|
|
2000
|
+
case "ClassConstructor": {
|
|
2001
|
+
const opener = node.type === "ClassConstructor" ? "constructor" : node.type === "ClassAccessor" ? node.kind : void 0;
|
|
2002
|
+
const words = firstTokensWithin(identifiers, node, 2);
|
|
2003
|
+
let index = 0;
|
|
2004
|
+
if (node.isStatic === true && words[index] && wordOf(words[index]) === "static") {
|
|
2005
|
+
add(words[index], "static".length, "keyword");
|
|
2006
|
+
index++;
|
|
2007
|
+
}
|
|
2008
|
+
if (opener && words[index] && wordOf(words[index]) === opener) {
|
|
2009
|
+
add(words[index], opener.length, "keyword");
|
|
2010
|
+
}
|
|
2011
|
+
return;
|
|
2012
|
+
}
|
|
1835
2013
|
case "TypeReference": {
|
|
1836
2014
|
const base = node.base;
|
|
1837
2015
|
const namespace = node.namespace;
|
|
@@ -1841,7 +2019,7 @@ function classify(analysis, spanned, ancestors, identifiers, add) {
|
|
|
1841
2019
|
if (!baseToken) return;
|
|
1842
2020
|
if (!namespace && typeParameterInScope2(ancestors, base)) {
|
|
1843
2021
|
add(baseToken, base.length, "typeParameter");
|
|
1844
|
-
} else if ((0,
|
|
2022
|
+
} else if ((0, import_luaut_parser9.isClassType)(analysis.types.aliases.get(namespace ? `${namespace}.${base}` : base) ?? import_luaut_parser9.unknownType)) {
|
|
1845
2023
|
add(baseToken, base.length, "class");
|
|
1846
2024
|
} else {
|
|
1847
2025
|
add(baseToken, base.length, "type", PRIMITIVES3.has(base) ? ["defaultLibrary"] : []);
|
|
@@ -1850,6 +2028,10 @@ function classify(analysis, spanned, ancestors, identifiers, add) {
|
|
|
1850
2028
|
}
|
|
1851
2029
|
}
|
|
1852
2030
|
}
|
|
2031
|
+
function wordOf(token) {
|
|
2032
|
+
const value = token.value;
|
|
2033
|
+
return typeof value === "string" ? value : void 0;
|
|
2034
|
+
}
|
|
1853
2035
|
function identifier(analysis, node, parent, add) {
|
|
1854
2036
|
const name = node.name;
|
|
1855
2037
|
const as = (type, modifiers = []) => add(node, name.length, type, modifiers);
|
|
@@ -1873,6 +2055,19 @@ function identifier(analysis, node, parent, add) {
|
|
|
1873
2055
|
case "DeclareClassStatement":
|
|
1874
2056
|
if (parent.name === node) return as("class", ["declaration"]);
|
|
1875
2057
|
break;
|
|
2058
|
+
case "ClassDeclaration":
|
|
2059
|
+
if (parent.name === node) return as("class", ["declaration"]);
|
|
2060
|
+
if (parent.superclass === node) return as("class");
|
|
2061
|
+
break;
|
|
2062
|
+
case "ClassMethod":
|
|
2063
|
+
if (parent.name === node) return as("method", ["declaration"]);
|
|
2064
|
+
break;
|
|
2065
|
+
case "ClassField":
|
|
2066
|
+
if (parent.name === node) return as("property", ["declaration"]);
|
|
2067
|
+
break;
|
|
2068
|
+
case "ClassAccessor":
|
|
2069
|
+
if (parent.name === node) return as("property", ["declaration"]);
|
|
2070
|
+
break;
|
|
1876
2071
|
case "DeclareStatement":
|
|
1877
2072
|
if (parent.id === node) return as(isFunction(typeOfNode(parent.valueType)) ? "function" : "variable", ["declaration"]);
|
|
1878
2073
|
break;
|
|
@@ -2093,6 +2288,11 @@ function createServer(connection, options = {}) {
|
|
|
2093
2288
|
(d) => hover(analyzer.get(d), p.position),
|
|
2094
2289
|
null
|
|
2095
2290
|
));
|
|
2291
|
+
connection.onRequest("luaut/hover", (p) => withDocument(
|
|
2292
|
+
p.textDocument.uri,
|
|
2293
|
+
(d) => hover(analyzer.get(d), p.position, Math.max(0, p.depth ?? 0)),
|
|
2294
|
+
null
|
|
2295
|
+
));
|
|
2096
2296
|
connection.onDefinition((p) => withDocument(
|
|
2097
2297
|
p.textDocument.uri,
|
|
2098
2298
|
(d) => {
|