luaut-language-server 4.0.0 → 5.1.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-7N7PVZTQ.js} +256 -53
- package/dist/chunk-7N7PVZTQ.js.map +1 -0
- package/dist/cli.cjs +259 -66
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/index.cjs +259 -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.js
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -136,7 +136,8 @@ function membersOf(type, aliases, seen = /* @__PURE__ */ new Set()) {
|
|
|
136
136
|
}
|
|
137
137
|
function takesSelf(type) {
|
|
138
138
|
for (const signature of signaturesOf(type)) {
|
|
139
|
-
|
|
139
|
+
const first = signature.params[0]?.name;
|
|
140
|
+
if (first === "self" || first === "this") return true;
|
|
140
141
|
}
|
|
141
142
|
return false;
|
|
142
143
|
}
|
|
@@ -157,7 +158,7 @@ function signatureLabel(signature) {
|
|
|
157
158
|
});
|
|
158
159
|
const generics = signature.typeParams?.length ? `<${signature.typeParams.join(", ")}>` : "";
|
|
159
160
|
const varargs = signature.varargs ? [`...: ${(0, import_luaut_parser.formatType)(signature.varargs)}`] : [];
|
|
160
|
-
const label = `${generics}(${[...parameters, ...varargs].join(", ")})
|
|
161
|
+
const label = `${generics}(${[...parameters, ...varargs].join(", ")}) => ${(0, import_luaut_parser.formatType)(signature.returns)}`;
|
|
161
162
|
return { label, parameters };
|
|
162
163
|
}
|
|
163
164
|
|
|
@@ -748,7 +749,7 @@ function exportDeclaration(analyzer, module2, name, seen = /* @__PURE__ */ new S
|
|
|
748
749
|
break;
|
|
749
750
|
case "ExportStatement": {
|
|
750
751
|
const declaration = statement.declaration;
|
|
751
|
-
if (declaration.type === "FunctionDeclaration") {
|
|
752
|
+
if (declaration.type === "FunctionDeclaration" || declaration.type === "ClassDeclaration") {
|
|
752
753
|
if (declaration.name.name === name) return here(declaration.name);
|
|
753
754
|
} else {
|
|
754
755
|
for (const target of declaration.names) {
|
|
@@ -860,16 +861,93 @@ function diagnostics(analysis) {
|
|
|
860
861
|
}
|
|
861
862
|
|
|
862
863
|
// src/features/hover.ts
|
|
864
|
+
var import_luaut_parser6 = require("luaut-parser");
|
|
865
|
+
|
|
866
|
+
// src/features/expand.ts
|
|
863
867
|
var import_luaut_parser5 = require("luaut-parser");
|
|
864
|
-
function
|
|
868
|
+
function expandAliases(type, aliases, depth, seen = []) {
|
|
869
|
+
if (depth <= 0) return type;
|
|
870
|
+
const name = withheldName(type);
|
|
871
|
+
if (name !== void 0 && !seen.includes(name)) {
|
|
872
|
+
const opened = aliases.get(name) ?? type;
|
|
873
|
+
const inner = [...seen, name];
|
|
874
|
+
return children2(unnamed(opened), (t) => expandAliases(t, aliases, depth - 1, inner));
|
|
875
|
+
}
|
|
876
|
+
return children2(type, (t) => expandAliases(t, aliases, depth, seen));
|
|
877
|
+
}
|
|
878
|
+
function withheldName(type) {
|
|
879
|
+
if (type.kind === "genericRef") return type.typeArguments.length ? void 0 : type.name;
|
|
880
|
+
return (0, import_luaut_parser5.aliasNameOf)(type);
|
|
881
|
+
}
|
|
882
|
+
function unnamed(type) {
|
|
883
|
+
return (0, import_luaut_parser5.withoutAliasName)(type);
|
|
884
|
+
}
|
|
885
|
+
function children2(type, f) {
|
|
886
|
+
switch (type.kind) {
|
|
887
|
+
case "array":
|
|
888
|
+
return (0, import_luaut_parser5.arrayOf)(f(type.element));
|
|
889
|
+
case "tuple":
|
|
890
|
+
return (0, import_luaut_parser5.tuple)(type.elements.map(f), type.isPack);
|
|
891
|
+
case "union":
|
|
892
|
+
return (0, import_luaut_parser5.union)(type.types.map(f));
|
|
893
|
+
case "intersection": {
|
|
894
|
+
const out = (0, import_luaut_parser5.intersection)(type.types.map(f));
|
|
895
|
+
return type.name && out.kind === "intersection" ? { ...out, name: type.name } : out;
|
|
896
|
+
}
|
|
897
|
+
case "object": {
|
|
898
|
+
if ((0, import_luaut_parser5.isClassType)(type)) return type;
|
|
899
|
+
const out = (0, import_luaut_parser5.objectType)(
|
|
900
|
+
[...type.properties].map(([name, property]) => [name, { ...property, type: f(property.type) }]),
|
|
901
|
+
type.indexer && { key: type.indexer.key, value: f(type.indexer.value) },
|
|
902
|
+
type.frozen
|
|
903
|
+
);
|
|
904
|
+
if (type.name) out.name = type.name;
|
|
905
|
+
return out;
|
|
906
|
+
}
|
|
907
|
+
case "function":
|
|
908
|
+
return (0, import_luaut_parser5.fn)(
|
|
909
|
+
type.params.map((p) => ({ ...p, type: f(p.type) })),
|
|
910
|
+
f(type.returns),
|
|
911
|
+
type.varargs && f(type.varargs),
|
|
912
|
+
type.typeParams,
|
|
913
|
+
type.predicate
|
|
914
|
+
);
|
|
915
|
+
default:
|
|
916
|
+
return type;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// src/features/hover.ts
|
|
921
|
+
function hover(analysis, position, depth = 0) {
|
|
865
922
|
const path = pathAt(analysis.program, position, true);
|
|
866
923
|
for (let i = path.length - 1; i >= 0; i--) {
|
|
867
924
|
if (UNNAMED.has(path[i].type)) return null;
|
|
868
|
-
const text =
|
|
869
|
-
if (text)
|
|
925
|
+
const text = at(analysis, path, i, depth);
|
|
926
|
+
if (!text) continue;
|
|
927
|
+
const canExpand = at(analysis, path, i, depth + 1) !== text;
|
|
928
|
+
return {
|
|
929
|
+
contents: { kind: "markdown", value: code(text) },
|
|
930
|
+
range: toRange(path[i]),
|
|
931
|
+
depth,
|
|
932
|
+
canExpand
|
|
933
|
+
};
|
|
870
934
|
}
|
|
871
935
|
return null;
|
|
872
936
|
}
|
|
937
|
+
function at(analysis, path, index, depth) {
|
|
938
|
+
const previous = expansion;
|
|
939
|
+
expansion = { depth, aliases: analysis.types.aliases, source: analysis.source };
|
|
940
|
+
try {
|
|
941
|
+
return describe(analysis, path, index);
|
|
942
|
+
} finally {
|
|
943
|
+
expansion = previous;
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
var expansion = {
|
|
947
|
+
depth: 0,
|
|
948
|
+
aliases: /* @__PURE__ */ new Map(),
|
|
949
|
+
source: ""
|
|
950
|
+
};
|
|
873
951
|
var UNNAMED = /* @__PURE__ */ new Set([
|
|
874
952
|
"BinaryExpression",
|
|
875
953
|
"UnaryExpression",
|
|
@@ -902,8 +980,8 @@ function describe(analysis, path, index) {
|
|
|
902
980
|
case "TableExpression": {
|
|
903
981
|
const field = fieldWithKey(parent, node);
|
|
904
982
|
if (!field) break;
|
|
905
|
-
const
|
|
906
|
-
const property =
|
|
983
|
+
const objectType2 = types.typeOf.get(parent);
|
|
984
|
+
const property = objectType2?.kind === "object" ? objectType2.properties.get(name) : void 0;
|
|
907
985
|
const type2 = property?.type ?? types.typeOf.get(field.value);
|
|
908
986
|
return type2 && `(property) ${name}: ${pretty(type2)}`;
|
|
909
987
|
}
|
|
@@ -951,6 +1029,19 @@ function describe(analysis, path, index) {
|
|
|
951
1029
|
case "DeclareClassStatement":
|
|
952
1030
|
if (parent.name === node) return classText(analysis, name);
|
|
953
1031
|
break;
|
|
1032
|
+
case "ClassDeclaration":
|
|
1033
|
+
if (parent.name === node) return classText(analysis, name);
|
|
1034
|
+
break;
|
|
1035
|
+
case "ClassField":
|
|
1036
|
+
if (parent.name === node) {
|
|
1037
|
+
const type2 = typeOfNode(parent.typeAnnotation) ?? types.typeOf.get(parent.init);
|
|
1038
|
+
const prefix = parent.isStatic ? "(static) " : "(field) ";
|
|
1039
|
+
return type2 ? `${prefix}${name}: ${pretty(type2)}` : `${prefix}${name}`;
|
|
1040
|
+
}
|
|
1041
|
+
break;
|
|
1042
|
+
case "ClassAccessor":
|
|
1043
|
+
if (parent.name === node) return `(${parent.kind === "get" ? "getter" : "setter"}) ${name}`;
|
|
1044
|
+
break;
|
|
954
1045
|
case "TableTypeProperty":
|
|
955
1046
|
if (parent.key === node) {
|
|
956
1047
|
const type2 = typeOfNode(parent.valueType);
|
|
@@ -973,7 +1064,7 @@ function describe(analysis, path, index) {
|
|
|
973
1064
|
case "MappedTypeNode":
|
|
974
1065
|
if (parent.parameterId === node) {
|
|
975
1066
|
const keys = typeOfNode(parent.constraint);
|
|
976
|
-
return `(type parameter) ${name}${keys ? ` in ${(0,
|
|
1067
|
+
return `(type parameter) ${name}${keys ? ` in ${(0, import_luaut_parser6.formatType)(keys)}` : ""}`;
|
|
977
1068
|
}
|
|
978
1069
|
break;
|
|
979
1070
|
}
|
|
@@ -1001,7 +1092,7 @@ function describe(analysis, path, index) {
|
|
|
1001
1092
|
case "TypedIdentifier": {
|
|
1002
1093
|
const binding = bindingOfNode(analysis, node);
|
|
1003
1094
|
const type2 = binding && types.bindingType.get(binding.id);
|
|
1004
|
-
return type2 ? bindingText(binding, type2) : void 0;
|
|
1095
|
+
return type2 ? bindingText(binding, type2, asWritten(node.typeAnnotation)) : void 0;
|
|
1005
1096
|
}
|
|
1006
1097
|
// A type written by name: `number`, `Shape`, `Partial<User>`, or a type
|
|
1007
1098
|
// parameter in scope.
|
|
@@ -1015,7 +1106,7 @@ function describe(analysis, path, index) {
|
|
|
1015
1106
|
if (!node.typeArguments.length) {
|
|
1016
1107
|
const qualified = node.namespace ? `${node.namespace}.${base}` : base;
|
|
1017
1108
|
const alias = types.aliases.get(qualified);
|
|
1018
|
-
if (alias && (0,
|
|
1109
|
+
if (alias && (0, import_luaut_parser6.isClassType)(alias)) return classText(analysis, qualified);
|
|
1019
1110
|
if (alias) return `type ${qualified} = ${pretty(alias)}`;
|
|
1020
1111
|
}
|
|
1021
1112
|
const type2 = typeOfNode(node);
|
|
@@ -1043,20 +1134,29 @@ function declareText(analysis, statement) {
|
|
|
1043
1134
|
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);
|
|
1044
1135
|
const others = total - 1;
|
|
1045
1136
|
const overloads = others > 0 ? ` (+${others} overload${others > 1 ? "s" : ""})` : "";
|
|
1046
|
-
return `declare function ${name}${(0,
|
|
1137
|
+
return `declare function ${name}${(0, import_luaut_parser6.formatType)(own)}${overloads}`;
|
|
1047
1138
|
}
|
|
1048
1139
|
function classText(analysis, name) {
|
|
1049
1140
|
const type = analysis.types.aliases.get(name);
|
|
1050
|
-
if (!type || !(0,
|
|
1141
|
+
if (!type || !(0, import_luaut_parser6.isClassType)(type)) return void 0;
|
|
1051
1142
|
const superclass = type.class.superclass;
|
|
1052
1143
|
const inherited = superclass ? analysis.types.aliases.get(superclass) : void 0;
|
|
1053
|
-
const own = [...type.properties].filter(([key, property]) =>
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1144
|
+
const own = [...type.properties].filter(([key, property]) => (
|
|
1145
|
+
// `ClassObject` is on every instance and says nothing about this one.
|
|
1146
|
+
key !== "ClassObject" && (inherited?.kind !== "object" || inherited.properties.get(key) !== property)
|
|
1147
|
+
));
|
|
1148
|
+
const written = isRuntimeClass(analysis, name);
|
|
1149
|
+
const head = `${written ? "" : "declare "}class ${name}${superclass ? ` extends ${superclass}` : ""}`;
|
|
1150
|
+
const lines = own.map(([key, property]) => ` ${property.readonly ? "readonly " : ""}${key}${property.optional ? "?" : ""}: ${(0, import_luaut_parser6.formatType)(property.type)}${written ? "" : ","}`);
|
|
1151
|
+
return own.length ? `${head} {
|
|
1058
1152
|
${lines.join("\n")}
|
|
1059
|
-
}`;
|
|
1153
|
+
}` : `${head} {}`;
|
|
1154
|
+
}
|
|
1155
|
+
function isRuntimeClass(analysis, name) {
|
|
1156
|
+
return analysis.program.body.statements.some((statement) => {
|
|
1157
|
+
const declaration = statement.type === "ExportStatement" ? statement.declaration : statement;
|
|
1158
|
+
return declaration.type === "ClassDeclaration" && declaration.name.name === name;
|
|
1159
|
+
});
|
|
1060
1160
|
}
|
|
1061
1161
|
function typeParameterText(analysis, parameter) {
|
|
1062
1162
|
return `(type parameter) ${typeParameterSignature(analysis, parameter)}`;
|
|
@@ -1065,7 +1165,7 @@ function typeParameterSignature(analysis, parameter) {
|
|
|
1065
1165
|
const p = parameter;
|
|
1066
1166
|
if (p.infer) return `infer ${p.name}`;
|
|
1067
1167
|
const constraint = p.constraint ? analysis.types.typeOfTypeNode.get(p.constraint) : void 0;
|
|
1068
|
-
return `${p.isConst ? "const " : ""}${p.name}${constraint ? ` extends ${(0,
|
|
1168
|
+
return `${p.isConst ? "const " : ""}${p.name}${constraint ? ` extends ${(0, import_luaut_parser6.formatType)(constraint)}` : ""}`;
|
|
1069
1169
|
}
|
|
1070
1170
|
function typeParameterInScope(path, index, name) {
|
|
1071
1171
|
for (let i = index - 1; i >= 0; i--) {
|
|
@@ -1090,7 +1190,7 @@ function referenceText(analysis, reference) {
|
|
|
1090
1190
|
if (!args.length) return name;
|
|
1091
1191
|
const resolved = args.map((a) => {
|
|
1092
1192
|
const t = analysis.types.typeOfTypeNode.get(a);
|
|
1093
|
-
return t ? (0,
|
|
1193
|
+
return t ? (0, import_luaut_parser6.formatType)(t) : "?";
|
|
1094
1194
|
});
|
|
1095
1195
|
return `${name}<${resolved.join(", ")}>`;
|
|
1096
1196
|
}
|
|
@@ -1098,30 +1198,50 @@ function fieldWithKey(table, key) {
|
|
|
1098
1198
|
const fields = table.fields;
|
|
1099
1199
|
return fields.find((f) => f.type === "TableFieldNamed" && f.key === key);
|
|
1100
1200
|
}
|
|
1101
|
-
function pretty(type) {
|
|
1102
|
-
const
|
|
1201
|
+
function pretty(type, written) {
|
|
1202
|
+
const named = render(expandAliases(type, expansion.aliases, 0));
|
|
1203
|
+
const shorthand = written !== void 0 && written !== named ? written : void 0;
|
|
1204
|
+
const depth = shorthand === void 0 ? expansion.depth : expansion.depth - 1;
|
|
1205
|
+
if (depth < 0) return shorthand;
|
|
1206
|
+
return depth === 0 ? named : render(expandAliases(type, expansion.aliases, depth));
|
|
1207
|
+
}
|
|
1208
|
+
function asWritten(node) {
|
|
1209
|
+
if (!isSpanned(node) || !expansion.source) return void 0;
|
|
1210
|
+
const lines = expansion.source.split(/\r?\n/);
|
|
1211
|
+
const { line, column } = node;
|
|
1212
|
+
if (line.start < 1 || line.end > lines.length) return void 0;
|
|
1213
|
+
const text = line.start === line.end ? lines[line.start - 1].slice(column.start - 1, column.end - 1) : [
|
|
1214
|
+
lines[line.start - 1].slice(column.start - 1),
|
|
1215
|
+
...lines.slice(line.start, line.end - 1),
|
|
1216
|
+
lines[line.end - 1].slice(0, column.end - 1)
|
|
1217
|
+
].join(" ");
|
|
1218
|
+
const folded = text.trim().replace(/\s+/g, " ");
|
|
1219
|
+
return folded.length ? folded : void 0;
|
|
1220
|
+
}
|
|
1221
|
+
function render(type) {
|
|
1222
|
+
const flat = (0, import_luaut_parser6.formatType)(type);
|
|
1103
1223
|
if (flat.length <= 80) return flat;
|
|
1104
1224
|
if (type.kind === "object") {
|
|
1105
1225
|
const lines = [];
|
|
1106
|
-
if (type.indexer) lines.push(` [${(0,
|
|
1226
|
+
if (type.indexer) lines.push(` [${(0, import_luaut_parser6.formatType)(type.indexer.key)}]: ${(0, import_luaut_parser6.formatType)(type.indexer.value)},`);
|
|
1107
1227
|
for (const [name, property] of type.properties) {
|
|
1108
1228
|
const readonly = property.readonly ? "readonly " : "";
|
|
1109
|
-
lines.push(` ${readonly}${name}${property.optional ? "?" : ""}: ${(0,
|
|
1229
|
+
lines.push(` ${readonly}${name}${property.optional ? "?" : ""}: ${(0, import_luaut_parser6.formatType)(property.type)},`);
|
|
1110
1230
|
}
|
|
1111
1231
|
return `{
|
|
1112
1232
|
${lines.join("\n")}
|
|
1113
1233
|
}`;
|
|
1114
1234
|
}
|
|
1115
1235
|
if (type.kind === "intersection" && type.types.every((t) => t.kind === "function")) {
|
|
1116
|
-
return type.types.map(
|
|
1236
|
+
return type.types.map(import_luaut_parser6.formatType).join("\n& ");
|
|
1117
1237
|
}
|
|
1118
1238
|
return flat;
|
|
1119
1239
|
}
|
|
1120
|
-
function bindingText(binding, type) {
|
|
1240
|
+
function bindingText(binding, type, written) {
|
|
1121
1241
|
if (binding.declaredBy === "function" && type.kind === "function") {
|
|
1122
|
-
return `function ${binding.name}${(
|
|
1242
|
+
return `function ${binding.name}${pretty(type)}`;
|
|
1123
1243
|
}
|
|
1124
|
-
return `${keyword(binding)} ${binding.name}: ${pretty(type)}`;
|
|
1244
|
+
return `${keyword(binding)} ${binding.name}: ${pretty(type, written)}`;
|
|
1125
1245
|
}
|
|
1126
1246
|
function keyword(binding) {
|
|
1127
1247
|
if (binding.kind === "param" || binding.kind === "self") return "(parameter)";
|
|
@@ -1197,7 +1317,7 @@ function isIdentifier(name) {
|
|
|
1197
1317
|
|
|
1198
1318
|
// src/features/completion.ts
|
|
1199
1319
|
var import_vscode_languageserver5 = require("vscode-languageserver");
|
|
1200
|
-
var
|
|
1320
|
+
var import_luaut_parser7 = require("luaut-parser");
|
|
1201
1321
|
|
|
1202
1322
|
// src/features/autoImport.ts
|
|
1203
1323
|
var import_node_fs3 = require("fs");
|
|
@@ -1234,7 +1354,7 @@ function importItems(analyzer, analysis, typePosition, taken) {
|
|
|
1234
1354
|
function serviceItems(analysis, taken) {
|
|
1235
1355
|
const services = analysis.types.aliases.get("Services");
|
|
1236
1356
|
if (!services || services.kind !== "object") return [];
|
|
1237
|
-
const
|
|
1357
|
+
const at2 = serviceInsertion(analysis.program.body.statements);
|
|
1238
1358
|
const items = [];
|
|
1239
1359
|
for (const name of services.properties.keys()) {
|
|
1240
1360
|
if (taken.has(name)) continue;
|
|
@@ -1245,8 +1365,8 @@ function serviceItems(analysis, taken) {
|
|
|
1245
1365
|
labelDetails: { description: "service" },
|
|
1246
1366
|
detail: line,
|
|
1247
1367
|
sortText: `5${name}`,
|
|
1248
|
-
additionalTextEdits: [{ range: { start:
|
|
1249
|
-
${
|
|
1368
|
+
additionalTextEdits: [{ range: { start: at2.position, end: at2.position }, newText: `${line}
|
|
1369
|
+
${at2.gap}` }]
|
|
1250
1370
|
});
|
|
1251
1371
|
}
|
|
1252
1372
|
return items;
|
|
@@ -1258,24 +1378,24 @@ function importEdit(analyzer, analysis, file, name, specifier, typePosition) {
|
|
|
1258
1378
|
if (existing) {
|
|
1259
1379
|
const last = existing.specifiers[existing.specifiers.length - 1];
|
|
1260
1380
|
if (last) {
|
|
1261
|
-
const
|
|
1262
|
-
return { range: { start:
|
|
1381
|
+
const at3 = endOf(last);
|
|
1382
|
+
return { range: { start: at3, end: at3 }, newText: `, ${name}` };
|
|
1263
1383
|
}
|
|
1264
1384
|
if (existing.defaultImport) {
|
|
1265
|
-
const
|
|
1266
|
-
return { range: { start:
|
|
1385
|
+
const at3 = endOf(existing.defaultImport);
|
|
1386
|
+
return { range: { start: at3, end: at3 }, newText: `, { ${name} }` };
|
|
1267
1387
|
}
|
|
1268
1388
|
}
|
|
1269
1389
|
const line = `import { ${name} } from "${specifier}"`;
|
|
1270
1390
|
const lastImport = imports[imports.length - 1];
|
|
1271
1391
|
if (lastImport) {
|
|
1272
|
-
const
|
|
1273
|
-
return { range: { start:
|
|
1392
|
+
const at3 = { line: lastImport.line.end, character: 0 };
|
|
1393
|
+
return { range: { start: at3, end: at3 }, newText: `${line}
|
|
1274
1394
|
` };
|
|
1275
1395
|
}
|
|
1276
1396
|
const first = statements[0];
|
|
1277
|
-
const
|
|
1278
|
-
return { range: { start:
|
|
1397
|
+
const at2 = { line: first ? first.line.start - 1 : 0, character: 0 };
|
|
1398
|
+
return { range: { start: at2, end: at2 }, newText: first ? `${line}
|
|
1279
1399
|
|
|
1280
1400
|
` : `${line}
|
|
1281
1401
|
` };
|
|
@@ -1368,12 +1488,12 @@ function completion(analyzer, document, position) {
|
|
|
1368
1488
|
const operator = memberOperator(source, start);
|
|
1369
1489
|
const alreadyCalled = /^\s*\(/.test(source.slice(end));
|
|
1370
1490
|
const standIns = operator === ":" ? [alreadyCalled ? PLACEHOLDER : `${PLACEHOLDER}()`] : operator === "." && !alreadyCalled ? [PLACEHOLDER, `${PLACEHOLDER}()`] : [PLACEHOLDER];
|
|
1371
|
-
const
|
|
1491
|
+
const at2 = { line: position.line, character: position.character - (offset - start) };
|
|
1372
1492
|
let first;
|
|
1373
1493
|
for (const standIn of standIns) {
|
|
1374
1494
|
const patched = source.slice(0, start) + standIn + source.slice(end);
|
|
1375
1495
|
const analysis = analyzer.analyze(document.uri, -1, patched);
|
|
1376
|
-
const path = pathAt(analysis.program,
|
|
1496
|
+
const path = pathAt(analysis.program, at2, true);
|
|
1377
1497
|
const index = path.findLastIndex(
|
|
1378
1498
|
(n) => n.type === "Identifier" && n.name === PLACEHOLDER
|
|
1379
1499
|
);
|
|
@@ -1389,8 +1509,8 @@ function completion(analyzer, document, position) {
|
|
|
1389
1509
|
if (inTypePosition(first.path)) {
|
|
1390
1510
|
const named = [...first.analysis.types.aliases].map(([name, type]) => ({
|
|
1391
1511
|
label: name,
|
|
1392
|
-
kind: (0,
|
|
1393
|
-
detail: (0,
|
|
1512
|
+
kind: (0, import_luaut_parser7.isClassType)(type) ? import_vscode_languageserver5.CompletionItemKind.Class : import_vscode_languageserver5.CompletionItemKind.Interface,
|
|
1513
|
+
detail: (0, import_luaut_parser7.isClassType)(type) ? "class" : "type"
|
|
1394
1514
|
}));
|
|
1395
1515
|
const primitives = PRIMITIVES2.map((name) => ({
|
|
1396
1516
|
label: name,
|
|
@@ -1410,7 +1530,7 @@ function completion(analyzer, document, position) {
|
|
|
1410
1530
|
for (const binding of first.analysis.scopes.bindings.values()) taken.add(binding.name);
|
|
1411
1531
|
const current = analyzer.get(document);
|
|
1412
1532
|
return [
|
|
1413
|
-
...valueItems(first.analysis,
|
|
1533
|
+
...valueItems(first.analysis, at2, insideFunction(first.path)),
|
|
1414
1534
|
...contextKeywords(source.slice(0, start)),
|
|
1415
1535
|
...importItems(analyzer, current, false, taken),
|
|
1416
1536
|
...serviceItems(current, taken)
|
|
@@ -1478,14 +1598,14 @@ function objectKeyItems(analysis, path, after) {
|
|
|
1478
1598
|
return members.filter((member) => !written.has(member.name)).map((member) => ({
|
|
1479
1599
|
label: member.name,
|
|
1480
1600
|
kind: import_vscode_languageserver5.CompletionItemKind.Field,
|
|
1481
|
-
detail: `${member.property.optional ? "?" : ""}: ${(0,
|
|
1601
|
+
detail: `${member.property.optional ? "?" : ""}: ${(0, import_luaut_parser7.formatType)(member.property.type)}`,
|
|
1482
1602
|
insertText: colon ? member.name : `${member.name}: `
|
|
1483
1603
|
}));
|
|
1484
1604
|
}
|
|
1485
1605
|
function indexKeys(analysis, position, literal) {
|
|
1486
1606
|
const path = pathAt(analysis.program, position, false);
|
|
1487
|
-
const
|
|
1488
|
-
const parent =
|
|
1607
|
+
const at2 = path.indexOf(literal);
|
|
1608
|
+
const parent = at2 > 0 ? path[at2 - 1] : void 0;
|
|
1489
1609
|
if (!parent) return [];
|
|
1490
1610
|
let indexed;
|
|
1491
1611
|
if (parent.type === "IndexedAccessTypeNode" && parent.indexType === literal) {
|
|
@@ -1577,20 +1697,24 @@ function isMethodOnly(type) {
|
|
|
1577
1697
|
return false;
|
|
1578
1698
|
}
|
|
1579
1699
|
}
|
|
1580
|
-
function
|
|
1700
|
+
function insideFunction(path) {
|
|
1701
|
+
return path.some((n) => n.type === "FunctionExpression" || n.type === "FunctionDeclaration" || n.type === "FunctionDeclarationStatement" || n.type === "FunctionBody");
|
|
1702
|
+
}
|
|
1703
|
+
function valueItems(analysis, at2, inFunction) {
|
|
1581
1704
|
const items = [];
|
|
1582
1705
|
const seen = /* @__PURE__ */ new Set();
|
|
1583
1706
|
for (const binding of analysis.scopes.bindings.values()) {
|
|
1584
1707
|
if (binding.name === PLACEHOLDER || seen.has(binding.name)) continue;
|
|
1585
1708
|
if (binding.declaredBy === "type") continue;
|
|
1586
1709
|
const declaration = binding.declarationNode;
|
|
1587
|
-
|
|
1710
|
+
const later = declaration !== void 0 && declaration.line.start - 1 > at2.line;
|
|
1711
|
+
if (later && !inFunction && binding.declaredBy !== "function") continue;
|
|
1588
1712
|
seen.add(binding.name);
|
|
1589
1713
|
const type = analysis.types.bindingType.get(binding.id);
|
|
1590
1714
|
items.push({
|
|
1591
1715
|
label: binding.name,
|
|
1592
1716
|
kind: kindOf(type, binding.kind),
|
|
1593
|
-
detail: type ? (0,
|
|
1717
|
+
detail: type ? (0, import_luaut_parser7.formatType)(type) : void 0,
|
|
1594
1718
|
// Locals before globals, and globals before library names.
|
|
1595
1719
|
sortText: `${binding.isBuiltin ? 2 : binding.kind === "global" ? 1 : 0}${binding.name}`
|
|
1596
1720
|
});
|
|
@@ -1614,7 +1738,7 @@ function memberItem(name, type, readonly) {
|
|
|
1614
1738
|
return {
|
|
1615
1739
|
label: name,
|
|
1616
1740
|
kind: import_vscode_languageserver5.CompletionItemKind.Field,
|
|
1617
|
-
detail: `${readonly ? "readonly " : ""}${(0,
|
|
1741
|
+
detail: `${readonly ? "readonly " : ""}${(0, import_luaut_parser7.formatType)(type)}`
|
|
1618
1742
|
};
|
|
1619
1743
|
}
|
|
1620
1744
|
function kindOf(type, bindingKind) {
|
|
@@ -1722,9 +1846,9 @@ function helpAt(analysis, position) {
|
|
|
1722
1846
|
function methodType(analysis, call) {
|
|
1723
1847
|
const object = call.object;
|
|
1724
1848
|
const method = call.method;
|
|
1725
|
-
const
|
|
1726
|
-
if (!
|
|
1727
|
-
return memberType(
|
|
1849
|
+
const objectType2 = analysis.types.typeOf.get(object);
|
|
1850
|
+
if (!objectType2) return void 0;
|
|
1851
|
+
return memberType(objectType2, method.name, analysis);
|
|
1728
1852
|
}
|
|
1729
1853
|
function memberType(type, name, analysis) {
|
|
1730
1854
|
if (type.kind === "object") return type.properties.get(name)?.type;
|
|
@@ -1755,7 +1879,7 @@ function activeArgument(call, position) {
|
|
|
1755
1879
|
|
|
1756
1880
|
// src/features/symbols.ts
|
|
1757
1881
|
var import_vscode_languageserver6 = require("vscode-languageserver");
|
|
1758
|
-
var
|
|
1882
|
+
var import_luaut_parser8 = require("luaut-parser");
|
|
1759
1883
|
function documentSymbols(analysis) {
|
|
1760
1884
|
const out = [];
|
|
1761
1885
|
walk(analysis.program, (node) => {
|
|
@@ -1772,10 +1896,19 @@ function documentSymbols(analysis) {
|
|
|
1772
1896
|
const name = typeof named === "string" ? named : named?.name;
|
|
1773
1897
|
if (name) {
|
|
1774
1898
|
const alias = analysis.types.aliases.get(name);
|
|
1775
|
-
out.push(symbol(name, import_vscode_languageserver6.SymbolKind.Interface, node, alias ? (0,
|
|
1899
|
+
out.push(symbol(name, import_vscode_languageserver6.SymbolKind.Interface, node, alias ? (0, import_luaut_parser8.formatType)(alias) : void 0));
|
|
1776
1900
|
}
|
|
1777
1901
|
break;
|
|
1778
1902
|
}
|
|
1903
|
+
case "ClassDeclaration": {
|
|
1904
|
+
const declaration = node;
|
|
1905
|
+
const superclass = declaration.superclass?.name;
|
|
1906
|
+
out.push({
|
|
1907
|
+
...symbol(declaration.name.name, import_vscode_languageserver6.SymbolKind.Class, node, superclass && `extends ${superclass}`),
|
|
1908
|
+
children: declaration.members.map((member) => classMember(analysis, member))
|
|
1909
|
+
});
|
|
1910
|
+
break;
|
|
1911
|
+
}
|
|
1779
1912
|
case "DeclareClassStatement": {
|
|
1780
1913
|
const name = node.name.name;
|
|
1781
1914
|
const superclass = node.superclass?.base;
|
|
@@ -1793,6 +1926,23 @@ function documentSymbols(analysis) {
|
|
|
1793
1926
|
});
|
|
1794
1927
|
return out;
|
|
1795
1928
|
}
|
|
1929
|
+
function classMember(analysis, member) {
|
|
1930
|
+
switch (member.type) {
|
|
1931
|
+
case "ClassConstructor":
|
|
1932
|
+
return symbol("constructor", import_vscode_languageserver6.SymbolKind.Constructor, member);
|
|
1933
|
+
case "ClassField":
|
|
1934
|
+
return symbol(
|
|
1935
|
+
member.name.name,
|
|
1936
|
+
member.isStatic ? import_vscode_languageserver6.SymbolKind.Constant : import_vscode_languageserver6.SymbolKind.Field,
|
|
1937
|
+
member,
|
|
1938
|
+
member.typeAnnotation ? void 0 : detailOf(analysis, member)
|
|
1939
|
+
);
|
|
1940
|
+
case "ClassAccessor":
|
|
1941
|
+
return symbol(member.name.name, import_vscode_languageserver6.SymbolKind.Property, member, member.kind);
|
|
1942
|
+
case "ClassMethod":
|
|
1943
|
+
return symbol(member.name.name, import_vscode_languageserver6.SymbolKind.Method, member, member.isStatic ? "static" : void 0);
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1796
1946
|
function functionName(node) {
|
|
1797
1947
|
const named = node;
|
|
1798
1948
|
if (typeof named.name === "string") return named.name;
|
|
@@ -1809,7 +1959,7 @@ function detailOf(analysis, node) {
|
|
|
1809
1959
|
if (name && typeof name === "object") {
|
|
1810
1960
|
const binding = bindingOfNode(analysis, name);
|
|
1811
1961
|
const type = binding && analysis.types.bindingType.get(binding.id);
|
|
1812
|
-
if (type) return (0,
|
|
1962
|
+
if (type) return (0, import_luaut_parser8.formatType)(type);
|
|
1813
1963
|
}
|
|
1814
1964
|
return void 0;
|
|
1815
1965
|
}
|
|
@@ -1819,7 +1969,7 @@ function symbol(name, kind, node, detail) {
|
|
|
1819
1969
|
}
|
|
1820
1970
|
|
|
1821
1971
|
// src/features/semanticTokens.ts
|
|
1822
|
-
var
|
|
1972
|
+
var import_luaut_parser9 = require("luaut-parser");
|
|
1823
1973
|
var TOKEN_TYPES = [
|
|
1824
1974
|
"namespace",
|
|
1825
1975
|
"type",
|
|
@@ -1849,21 +1999,23 @@ var SOFT_KEYWORDS = /* @__PURE__ */ new Set([
|
|
|
1849
1999
|
"asserts",
|
|
1850
2000
|
"satisfies",
|
|
1851
2001
|
"typeof",
|
|
1852
|
-
"default"
|
|
2002
|
+
"default",
|
|
2003
|
+
"new",
|
|
2004
|
+
"super"
|
|
1853
2005
|
]);
|
|
1854
2006
|
var CONTROL_KEYWORDS = /* @__PURE__ */ new Set(["default"]);
|
|
1855
2007
|
var PRIMITIVES3 = /* @__PURE__ */ new Set(["any", "unknown", "never", "nil", "boolean", "number", "string", "thread", "buffer"]);
|
|
1856
2008
|
function semanticTokens(analysis) {
|
|
1857
2009
|
const entries = /* @__PURE__ */ new Map();
|
|
1858
|
-
const add = (
|
|
1859
|
-
const line =
|
|
1860
|
-
const character =
|
|
2010
|
+
const add = (at2, length, type, modifiers = []) => {
|
|
2011
|
+
const line = at2.line.start - 1;
|
|
2012
|
+
const character = at2.column.start - 1;
|
|
1861
2013
|
const key = `${line}:${character}`;
|
|
1862
2014
|
if (!entries.has(key)) entries.set(key, { line, character, length, type, modifiers });
|
|
1863
2015
|
};
|
|
1864
2016
|
let tokens = [];
|
|
1865
2017
|
try {
|
|
1866
|
-
tokens = (0,
|
|
2018
|
+
tokens = (0, import_luaut_parser9.tokenize)(analysis.source);
|
|
1867
2019
|
} catch {
|
|
1868
2020
|
}
|
|
1869
2021
|
const identifiers = tokens.filter((t) => t.type === "Identifier");
|
|
@@ -1898,6 +2050,25 @@ function classify(analysis, spanned, ancestors, identifiers, add) {
|
|
|
1898
2050
|
add(node, name.length, valueKind(analysis, binding), modifiersOf(binding, true));
|
|
1899
2051
|
return;
|
|
1900
2052
|
}
|
|
2053
|
+
// A class body's own words. `get`, `set`, `static` and `constructor`
|
|
2054
|
+
// are ordinary names anywhere else, so they are coloured from the
|
|
2055
|
+
// member they open rather than wherever they are written.
|
|
2056
|
+
case "ClassMethod":
|
|
2057
|
+
case "ClassField":
|
|
2058
|
+
case "ClassAccessor":
|
|
2059
|
+
case "ClassConstructor": {
|
|
2060
|
+
const opener = node.type === "ClassConstructor" ? "constructor" : node.type === "ClassAccessor" ? node.kind : void 0;
|
|
2061
|
+
const words = firstTokensWithin(identifiers, node, 2);
|
|
2062
|
+
let index = 0;
|
|
2063
|
+
if (node.isStatic === true && words[index] && wordOf(words[index]) === "static") {
|
|
2064
|
+
add(words[index], "static".length, "keyword");
|
|
2065
|
+
index++;
|
|
2066
|
+
}
|
|
2067
|
+
if (opener && words[index] && wordOf(words[index]) === opener) {
|
|
2068
|
+
add(words[index], opener.length, "keyword");
|
|
2069
|
+
}
|
|
2070
|
+
return;
|
|
2071
|
+
}
|
|
1901
2072
|
case "TypeReference": {
|
|
1902
2073
|
const base = node.base;
|
|
1903
2074
|
const namespace = node.namespace;
|
|
@@ -1907,7 +2078,7 @@ function classify(analysis, spanned, ancestors, identifiers, add) {
|
|
|
1907
2078
|
if (!baseToken) return;
|
|
1908
2079
|
if (!namespace && typeParameterInScope2(ancestors, base)) {
|
|
1909
2080
|
add(baseToken, base.length, "typeParameter");
|
|
1910
|
-
} else if ((0,
|
|
2081
|
+
} else if ((0, import_luaut_parser9.isClassType)(analysis.types.aliases.get(namespace ? `${namespace}.${base}` : base) ?? import_luaut_parser9.unknownType)) {
|
|
1911
2082
|
add(baseToken, base.length, "class");
|
|
1912
2083
|
} else {
|
|
1913
2084
|
add(baseToken, base.length, "type", PRIMITIVES3.has(base) ? ["defaultLibrary"] : []);
|
|
@@ -1916,6 +2087,10 @@ function classify(analysis, spanned, ancestors, identifiers, add) {
|
|
|
1916
2087
|
}
|
|
1917
2088
|
}
|
|
1918
2089
|
}
|
|
2090
|
+
function wordOf(token) {
|
|
2091
|
+
const value = token.value;
|
|
2092
|
+
return typeof value === "string" ? value : void 0;
|
|
2093
|
+
}
|
|
1919
2094
|
function identifier(analysis, node, parent, add) {
|
|
1920
2095
|
const name = node.name;
|
|
1921
2096
|
const as = (type, modifiers = []) => add(node, name.length, type, modifiers);
|
|
@@ -1939,6 +2114,19 @@ function identifier(analysis, node, parent, add) {
|
|
|
1939
2114
|
case "DeclareClassStatement":
|
|
1940
2115
|
if (parent.name === node) return as("class", ["declaration"]);
|
|
1941
2116
|
break;
|
|
2117
|
+
case "ClassDeclaration":
|
|
2118
|
+
if (parent.name === node) return as("class", ["declaration"]);
|
|
2119
|
+
if (parent.superclass === node) return as("class");
|
|
2120
|
+
break;
|
|
2121
|
+
case "ClassMethod":
|
|
2122
|
+
if (parent.name === node) return as("method", ["declaration"]);
|
|
2123
|
+
break;
|
|
2124
|
+
case "ClassField":
|
|
2125
|
+
if (parent.name === node) return as("property", ["declaration"]);
|
|
2126
|
+
break;
|
|
2127
|
+
case "ClassAccessor":
|
|
2128
|
+
if (parent.name === node) return as("property", ["declaration"]);
|
|
2129
|
+
break;
|
|
1942
2130
|
case "DeclareStatement":
|
|
1943
2131
|
if (parent.id === node) return as(isFunction(typeOfNode(parent.valueType)) ? "function" : "variable", ["declaration"]);
|
|
1944
2132
|
break;
|
|
@@ -2159,6 +2347,11 @@ function createServer(connection, options = {}) {
|
|
|
2159
2347
|
(d) => hover(analyzer.get(d), p.position),
|
|
2160
2348
|
null
|
|
2161
2349
|
));
|
|
2350
|
+
connection.onRequest("luaut/hover", (p) => withDocument(
|
|
2351
|
+
p.textDocument.uri,
|
|
2352
|
+
(d) => hover(analyzer.get(d), p.position, Math.max(0, p.depth ?? 0)),
|
|
2353
|
+
null
|
|
2354
|
+
));
|
|
2162
2355
|
connection.onDefinition((p) => withDocument(
|
|
2163
2356
|
p.textDocument.uri,
|
|
2164
2357
|
(d) => {
|