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.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,100 @@ 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 ((0, import_luaut_parser5.isClassType)(type)) return void 0;
|
|
880
|
+
if (type.kind === "object" || type.kind === "intersection") return type.name;
|
|
881
|
+
if (type.kind === "genericRef") return type.typeArguments.length ? void 0 : type.name;
|
|
882
|
+
return void 0;
|
|
883
|
+
}
|
|
884
|
+
function unnamed(type) {
|
|
885
|
+
if (type.kind === "object") {
|
|
886
|
+
const out = (0, import_luaut_parser5.objectType)(type.properties, type.indexer, type.frozen);
|
|
887
|
+
return type.class ? Object.assign(out, { class: type.class, name: type.name }) : out;
|
|
888
|
+
}
|
|
889
|
+
if (type.kind === "intersection" && type.name) return { ...type, name: void 0 };
|
|
890
|
+
return type;
|
|
891
|
+
}
|
|
892
|
+
function children2(type, f) {
|
|
893
|
+
switch (type.kind) {
|
|
894
|
+
case "array":
|
|
895
|
+
return (0, import_luaut_parser5.arrayOf)(f(type.element));
|
|
896
|
+
case "tuple":
|
|
897
|
+
return (0, import_luaut_parser5.tuple)(type.elements.map(f), type.isPack);
|
|
898
|
+
case "union":
|
|
899
|
+
return (0, import_luaut_parser5.union)(type.types.map(f));
|
|
900
|
+
case "intersection": {
|
|
901
|
+
const out = (0, import_luaut_parser5.intersection)(type.types.map(f));
|
|
902
|
+
return type.name && out.kind === "intersection" ? { ...out, name: type.name } : out;
|
|
903
|
+
}
|
|
904
|
+
case "object": {
|
|
905
|
+
if ((0, import_luaut_parser5.isClassType)(type)) return type;
|
|
906
|
+
const out = (0, import_luaut_parser5.objectType)(
|
|
907
|
+
[...type.properties].map(([name, property]) => [name, { ...property, type: f(property.type) }]),
|
|
908
|
+
type.indexer && { key: type.indexer.key, value: f(type.indexer.value) },
|
|
909
|
+
type.frozen
|
|
910
|
+
);
|
|
911
|
+
if (type.name) out.name = type.name;
|
|
912
|
+
return out;
|
|
913
|
+
}
|
|
914
|
+
case "function":
|
|
915
|
+
return (0, import_luaut_parser5.fn)(
|
|
916
|
+
type.params.map((p) => ({ ...p, type: f(p.type) })),
|
|
917
|
+
f(type.returns),
|
|
918
|
+
type.varargs && f(type.varargs),
|
|
919
|
+
type.typeParams,
|
|
920
|
+
type.predicate
|
|
921
|
+
);
|
|
922
|
+
default:
|
|
923
|
+
return type;
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
// src/features/hover.ts
|
|
928
|
+
function hover(analysis, position, depth = 0) {
|
|
865
929
|
const path = pathAt(analysis.program, position, true);
|
|
866
930
|
for (let i = path.length - 1; i >= 0; i--) {
|
|
867
931
|
if (UNNAMED.has(path[i].type)) return null;
|
|
868
|
-
const text =
|
|
869
|
-
if (text)
|
|
932
|
+
const text = at(analysis, path, i, depth);
|
|
933
|
+
if (!text) continue;
|
|
934
|
+
const canExpand = at(analysis, path, i, depth + 1) !== text;
|
|
935
|
+
return {
|
|
936
|
+
contents: { kind: "markdown", value: code(text) },
|
|
937
|
+
range: toRange(path[i]),
|
|
938
|
+
depth,
|
|
939
|
+
canExpand
|
|
940
|
+
};
|
|
870
941
|
}
|
|
871
942
|
return null;
|
|
872
943
|
}
|
|
944
|
+
function at(analysis, path, index, depth) {
|
|
945
|
+
const previous = expansion;
|
|
946
|
+
expansion = { depth, aliases: analysis.types.aliases, source: analysis.source };
|
|
947
|
+
try {
|
|
948
|
+
return describe(analysis, path, index);
|
|
949
|
+
} finally {
|
|
950
|
+
expansion = previous;
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
var expansion = {
|
|
954
|
+
depth: 0,
|
|
955
|
+
aliases: /* @__PURE__ */ new Map(),
|
|
956
|
+
source: ""
|
|
957
|
+
};
|
|
873
958
|
var UNNAMED = /* @__PURE__ */ new Set([
|
|
874
959
|
"BinaryExpression",
|
|
875
960
|
"UnaryExpression",
|
|
@@ -902,8 +987,8 @@ function describe(analysis, path, index) {
|
|
|
902
987
|
case "TableExpression": {
|
|
903
988
|
const field = fieldWithKey(parent, node);
|
|
904
989
|
if (!field) break;
|
|
905
|
-
const
|
|
906
|
-
const property =
|
|
990
|
+
const objectType2 = types.typeOf.get(parent);
|
|
991
|
+
const property = objectType2?.kind === "object" ? objectType2.properties.get(name) : void 0;
|
|
907
992
|
const type2 = property?.type ?? types.typeOf.get(field.value);
|
|
908
993
|
return type2 && `(property) ${name}: ${pretty(type2)}`;
|
|
909
994
|
}
|
|
@@ -951,6 +1036,19 @@ function describe(analysis, path, index) {
|
|
|
951
1036
|
case "DeclareClassStatement":
|
|
952
1037
|
if (parent.name === node) return classText(analysis, name);
|
|
953
1038
|
break;
|
|
1039
|
+
case "ClassDeclaration":
|
|
1040
|
+
if (parent.name === node) return classText(analysis, name);
|
|
1041
|
+
break;
|
|
1042
|
+
case "ClassField":
|
|
1043
|
+
if (parent.name === node) {
|
|
1044
|
+
const type2 = typeOfNode(parent.typeAnnotation) ?? types.typeOf.get(parent.init);
|
|
1045
|
+
const prefix = parent.isStatic ? "(static) " : "(field) ";
|
|
1046
|
+
return type2 ? `${prefix}${name}: ${pretty(type2)}` : `${prefix}${name}`;
|
|
1047
|
+
}
|
|
1048
|
+
break;
|
|
1049
|
+
case "ClassAccessor":
|
|
1050
|
+
if (parent.name === node) return `(${parent.kind === "get" ? "getter" : "setter"}) ${name}`;
|
|
1051
|
+
break;
|
|
954
1052
|
case "TableTypeProperty":
|
|
955
1053
|
if (parent.key === node) {
|
|
956
1054
|
const type2 = typeOfNode(parent.valueType);
|
|
@@ -973,7 +1071,7 @@ function describe(analysis, path, index) {
|
|
|
973
1071
|
case "MappedTypeNode":
|
|
974
1072
|
if (parent.parameterId === node) {
|
|
975
1073
|
const keys = typeOfNode(parent.constraint);
|
|
976
|
-
return `(type parameter) ${name}${keys ? ` in ${(0,
|
|
1074
|
+
return `(type parameter) ${name}${keys ? ` in ${(0, import_luaut_parser6.formatType)(keys)}` : ""}`;
|
|
977
1075
|
}
|
|
978
1076
|
break;
|
|
979
1077
|
}
|
|
@@ -1001,7 +1099,7 @@ function describe(analysis, path, index) {
|
|
|
1001
1099
|
case "TypedIdentifier": {
|
|
1002
1100
|
const binding = bindingOfNode(analysis, node);
|
|
1003
1101
|
const type2 = binding && types.bindingType.get(binding.id);
|
|
1004
|
-
return type2 ? bindingText(binding, type2) : void 0;
|
|
1102
|
+
return type2 ? bindingText(binding, type2, asWritten(node.typeAnnotation)) : void 0;
|
|
1005
1103
|
}
|
|
1006
1104
|
// A type written by name: `number`, `Shape`, `Partial<User>`, or a type
|
|
1007
1105
|
// parameter in scope.
|
|
@@ -1015,7 +1113,7 @@ function describe(analysis, path, index) {
|
|
|
1015
1113
|
if (!node.typeArguments.length) {
|
|
1016
1114
|
const qualified = node.namespace ? `${node.namespace}.${base}` : base;
|
|
1017
1115
|
const alias = types.aliases.get(qualified);
|
|
1018
|
-
if (alias && (0,
|
|
1116
|
+
if (alias && (0, import_luaut_parser6.isClassType)(alias)) return classText(analysis, qualified);
|
|
1019
1117
|
if (alias) return `type ${qualified} = ${pretty(alias)}`;
|
|
1020
1118
|
}
|
|
1021
1119
|
const type2 = typeOfNode(node);
|
|
@@ -1043,20 +1141,29 @@ function declareText(analysis, statement) {
|
|
|
1043
1141
|
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
1142
|
const others = total - 1;
|
|
1045
1143
|
const overloads = others > 0 ? ` (+${others} overload${others > 1 ? "s" : ""})` : "";
|
|
1046
|
-
return `declare function ${name}${(0,
|
|
1144
|
+
return `declare function ${name}${(0, import_luaut_parser6.formatType)(own)}${overloads}`;
|
|
1047
1145
|
}
|
|
1048
1146
|
function classText(analysis, name) {
|
|
1049
1147
|
const type = analysis.types.aliases.get(name);
|
|
1050
|
-
if (!type || !(0,
|
|
1148
|
+
if (!type || !(0, import_luaut_parser6.isClassType)(type)) return void 0;
|
|
1051
1149
|
const superclass = type.class.superclass;
|
|
1052
1150
|
const inherited = superclass ? analysis.types.aliases.get(superclass) : void 0;
|
|
1053
|
-
const own = [...type.properties].filter(([key, property]) =>
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1151
|
+
const own = [...type.properties].filter(([key, property]) => (
|
|
1152
|
+
// `ClassObject` is on every instance and says nothing about this one.
|
|
1153
|
+
key !== "ClassObject" && (inherited?.kind !== "object" || inherited.properties.get(key) !== property)
|
|
1154
|
+
));
|
|
1155
|
+
const written = isRuntimeClass(analysis, name);
|
|
1156
|
+
const head = `${written ? "" : "declare "}class ${name}${superclass ? ` extends ${superclass}` : ""}`;
|
|
1157
|
+
const lines = own.map(([key, property]) => ` ${property.readonly ? "readonly " : ""}${key}${property.optional ? "?" : ""}: ${(0, import_luaut_parser6.formatType)(property.type)}${written ? "" : ","}`);
|
|
1158
|
+
return own.length ? `${head} {
|
|
1058
1159
|
${lines.join("\n")}
|
|
1059
|
-
}`;
|
|
1160
|
+
}` : `${head} {}`;
|
|
1161
|
+
}
|
|
1162
|
+
function isRuntimeClass(analysis, name) {
|
|
1163
|
+
return analysis.program.body.statements.some((statement) => {
|
|
1164
|
+
const declaration = statement.type === "ExportStatement" ? statement.declaration : statement;
|
|
1165
|
+
return declaration.type === "ClassDeclaration" && declaration.name.name === name;
|
|
1166
|
+
});
|
|
1060
1167
|
}
|
|
1061
1168
|
function typeParameterText(analysis, parameter) {
|
|
1062
1169
|
return `(type parameter) ${typeParameterSignature(analysis, parameter)}`;
|
|
@@ -1065,7 +1172,7 @@ function typeParameterSignature(analysis, parameter) {
|
|
|
1065
1172
|
const p = parameter;
|
|
1066
1173
|
if (p.infer) return `infer ${p.name}`;
|
|
1067
1174
|
const constraint = p.constraint ? analysis.types.typeOfTypeNode.get(p.constraint) : void 0;
|
|
1068
|
-
return `${p.isConst ? "const " : ""}${p.name}${constraint ? ` extends ${(0,
|
|
1175
|
+
return `${p.isConst ? "const " : ""}${p.name}${constraint ? ` extends ${(0, import_luaut_parser6.formatType)(constraint)}` : ""}`;
|
|
1069
1176
|
}
|
|
1070
1177
|
function typeParameterInScope(path, index, name) {
|
|
1071
1178
|
for (let i = index - 1; i >= 0; i--) {
|
|
@@ -1090,7 +1197,7 @@ function referenceText(analysis, reference) {
|
|
|
1090
1197
|
if (!args.length) return name;
|
|
1091
1198
|
const resolved = args.map((a) => {
|
|
1092
1199
|
const t = analysis.types.typeOfTypeNode.get(a);
|
|
1093
|
-
return t ? (0,
|
|
1200
|
+
return t ? (0, import_luaut_parser6.formatType)(t) : "?";
|
|
1094
1201
|
});
|
|
1095
1202
|
return `${name}<${resolved.join(", ")}>`;
|
|
1096
1203
|
}
|
|
@@ -1098,30 +1205,50 @@ function fieldWithKey(table, key) {
|
|
|
1098
1205
|
const fields = table.fields;
|
|
1099
1206
|
return fields.find((f) => f.type === "TableFieldNamed" && f.key === key);
|
|
1100
1207
|
}
|
|
1101
|
-
function pretty(type) {
|
|
1102
|
-
const
|
|
1208
|
+
function pretty(type, written) {
|
|
1209
|
+
const named = render(expandAliases(type, expansion.aliases, 0));
|
|
1210
|
+
const shorthand = written !== void 0 && written !== named ? written : void 0;
|
|
1211
|
+
const depth = shorthand === void 0 ? expansion.depth : expansion.depth - 1;
|
|
1212
|
+
if (depth < 0) return shorthand;
|
|
1213
|
+
return depth === 0 ? named : render(expandAliases(type, expansion.aliases, depth));
|
|
1214
|
+
}
|
|
1215
|
+
function asWritten(node) {
|
|
1216
|
+
if (!isSpanned(node) || !expansion.source) return void 0;
|
|
1217
|
+
const lines = expansion.source.split(/\r?\n/);
|
|
1218
|
+
const { line, column } = node;
|
|
1219
|
+
if (line.start < 1 || line.end > lines.length) return void 0;
|
|
1220
|
+
const text = line.start === line.end ? lines[line.start - 1].slice(column.start - 1, column.end - 1) : [
|
|
1221
|
+
lines[line.start - 1].slice(column.start - 1),
|
|
1222
|
+
...lines.slice(line.start, line.end - 1),
|
|
1223
|
+
lines[line.end - 1].slice(0, column.end - 1)
|
|
1224
|
+
].join(" ");
|
|
1225
|
+
const folded = text.trim().replace(/\s+/g, " ");
|
|
1226
|
+
return folded.length ? folded : void 0;
|
|
1227
|
+
}
|
|
1228
|
+
function render(type) {
|
|
1229
|
+
const flat = (0, import_luaut_parser6.formatType)(type);
|
|
1103
1230
|
if (flat.length <= 80) return flat;
|
|
1104
1231
|
if (type.kind === "object") {
|
|
1105
1232
|
const lines = [];
|
|
1106
|
-
if (type.indexer) lines.push(` [${(0,
|
|
1233
|
+
if (type.indexer) lines.push(` [${(0, import_luaut_parser6.formatType)(type.indexer.key)}]: ${(0, import_luaut_parser6.formatType)(type.indexer.value)},`);
|
|
1107
1234
|
for (const [name, property] of type.properties) {
|
|
1108
1235
|
const readonly = property.readonly ? "readonly " : "";
|
|
1109
|
-
lines.push(` ${readonly}${name}${property.optional ? "?" : ""}: ${(0,
|
|
1236
|
+
lines.push(` ${readonly}${name}${property.optional ? "?" : ""}: ${(0, import_luaut_parser6.formatType)(property.type)},`);
|
|
1110
1237
|
}
|
|
1111
1238
|
return `{
|
|
1112
1239
|
${lines.join("\n")}
|
|
1113
1240
|
}`;
|
|
1114
1241
|
}
|
|
1115
1242
|
if (type.kind === "intersection" && type.types.every((t) => t.kind === "function")) {
|
|
1116
|
-
return type.types.map(
|
|
1243
|
+
return type.types.map(import_luaut_parser6.formatType).join("\n& ");
|
|
1117
1244
|
}
|
|
1118
1245
|
return flat;
|
|
1119
1246
|
}
|
|
1120
|
-
function bindingText(binding, type) {
|
|
1247
|
+
function bindingText(binding, type, written) {
|
|
1121
1248
|
if (binding.declaredBy === "function" && type.kind === "function") {
|
|
1122
|
-
return `function ${binding.name}${(
|
|
1249
|
+
return `function ${binding.name}${pretty(type)}`;
|
|
1123
1250
|
}
|
|
1124
|
-
return `${keyword(binding)} ${binding.name}: ${pretty(type)}`;
|
|
1251
|
+
return `${keyword(binding)} ${binding.name}: ${pretty(type, written)}`;
|
|
1125
1252
|
}
|
|
1126
1253
|
function keyword(binding) {
|
|
1127
1254
|
if (binding.kind === "param" || binding.kind === "self") return "(parameter)";
|
|
@@ -1197,7 +1324,7 @@ function isIdentifier(name) {
|
|
|
1197
1324
|
|
|
1198
1325
|
// src/features/completion.ts
|
|
1199
1326
|
var import_vscode_languageserver5 = require("vscode-languageserver");
|
|
1200
|
-
var
|
|
1327
|
+
var import_luaut_parser7 = require("luaut-parser");
|
|
1201
1328
|
|
|
1202
1329
|
// src/features/autoImport.ts
|
|
1203
1330
|
var import_node_fs3 = require("fs");
|
|
@@ -1234,7 +1361,7 @@ function importItems(analyzer, analysis, typePosition, taken) {
|
|
|
1234
1361
|
function serviceItems(analysis, taken) {
|
|
1235
1362
|
const services = analysis.types.aliases.get("Services");
|
|
1236
1363
|
if (!services || services.kind !== "object") return [];
|
|
1237
|
-
const
|
|
1364
|
+
const at2 = serviceInsertion(analysis.program.body.statements);
|
|
1238
1365
|
const items = [];
|
|
1239
1366
|
for (const name of services.properties.keys()) {
|
|
1240
1367
|
if (taken.has(name)) continue;
|
|
@@ -1245,8 +1372,8 @@ function serviceItems(analysis, taken) {
|
|
|
1245
1372
|
labelDetails: { description: "service" },
|
|
1246
1373
|
detail: line,
|
|
1247
1374
|
sortText: `5${name}`,
|
|
1248
|
-
additionalTextEdits: [{ range: { start:
|
|
1249
|
-
${
|
|
1375
|
+
additionalTextEdits: [{ range: { start: at2.position, end: at2.position }, newText: `${line}
|
|
1376
|
+
${at2.gap}` }]
|
|
1250
1377
|
});
|
|
1251
1378
|
}
|
|
1252
1379
|
return items;
|
|
@@ -1258,24 +1385,24 @@ function importEdit(analyzer, analysis, file, name, specifier, typePosition) {
|
|
|
1258
1385
|
if (existing) {
|
|
1259
1386
|
const last = existing.specifiers[existing.specifiers.length - 1];
|
|
1260
1387
|
if (last) {
|
|
1261
|
-
const
|
|
1262
|
-
return { range: { start:
|
|
1388
|
+
const at3 = endOf(last);
|
|
1389
|
+
return { range: { start: at3, end: at3 }, newText: `, ${name}` };
|
|
1263
1390
|
}
|
|
1264
1391
|
if (existing.defaultImport) {
|
|
1265
|
-
const
|
|
1266
|
-
return { range: { start:
|
|
1392
|
+
const at3 = endOf(existing.defaultImport);
|
|
1393
|
+
return { range: { start: at3, end: at3 }, newText: `, { ${name} }` };
|
|
1267
1394
|
}
|
|
1268
1395
|
}
|
|
1269
1396
|
const line = `import { ${name} } from "${specifier}"`;
|
|
1270
1397
|
const lastImport = imports[imports.length - 1];
|
|
1271
1398
|
if (lastImport) {
|
|
1272
|
-
const
|
|
1273
|
-
return { range: { start:
|
|
1399
|
+
const at3 = { line: lastImport.line.end, character: 0 };
|
|
1400
|
+
return { range: { start: at3, end: at3 }, newText: `${line}
|
|
1274
1401
|
` };
|
|
1275
1402
|
}
|
|
1276
1403
|
const first = statements[0];
|
|
1277
|
-
const
|
|
1278
|
-
return { range: { start:
|
|
1404
|
+
const at2 = { line: first ? first.line.start - 1 : 0, character: 0 };
|
|
1405
|
+
return { range: { start: at2, end: at2 }, newText: first ? `${line}
|
|
1279
1406
|
|
|
1280
1407
|
` : `${line}
|
|
1281
1408
|
` };
|
|
@@ -1368,12 +1495,12 @@ function completion(analyzer, document, position) {
|
|
|
1368
1495
|
const operator = memberOperator(source, start);
|
|
1369
1496
|
const alreadyCalled = /^\s*\(/.test(source.slice(end));
|
|
1370
1497
|
const standIns = operator === ":" ? [alreadyCalled ? PLACEHOLDER : `${PLACEHOLDER}()`] : operator === "." && !alreadyCalled ? [PLACEHOLDER, `${PLACEHOLDER}()`] : [PLACEHOLDER];
|
|
1371
|
-
const
|
|
1498
|
+
const at2 = { line: position.line, character: position.character - (offset - start) };
|
|
1372
1499
|
let first;
|
|
1373
1500
|
for (const standIn of standIns) {
|
|
1374
1501
|
const patched = source.slice(0, start) + standIn + source.slice(end);
|
|
1375
1502
|
const analysis = analyzer.analyze(document.uri, -1, patched);
|
|
1376
|
-
const path = pathAt(analysis.program,
|
|
1503
|
+
const path = pathAt(analysis.program, at2, true);
|
|
1377
1504
|
const index = path.findLastIndex(
|
|
1378
1505
|
(n) => n.type === "Identifier" && n.name === PLACEHOLDER
|
|
1379
1506
|
);
|
|
@@ -1389,8 +1516,8 @@ function completion(analyzer, document, position) {
|
|
|
1389
1516
|
if (inTypePosition(first.path)) {
|
|
1390
1517
|
const named = [...first.analysis.types.aliases].map(([name, type]) => ({
|
|
1391
1518
|
label: name,
|
|
1392
|
-
kind: (0,
|
|
1393
|
-
detail: (0,
|
|
1519
|
+
kind: (0, import_luaut_parser7.isClassType)(type) ? import_vscode_languageserver5.CompletionItemKind.Class : import_vscode_languageserver5.CompletionItemKind.Interface,
|
|
1520
|
+
detail: (0, import_luaut_parser7.isClassType)(type) ? "class" : "type"
|
|
1394
1521
|
}));
|
|
1395
1522
|
const primitives = PRIMITIVES2.map((name) => ({
|
|
1396
1523
|
label: name,
|
|
@@ -1410,7 +1537,7 @@ function completion(analyzer, document, position) {
|
|
|
1410
1537
|
for (const binding of first.analysis.scopes.bindings.values()) taken.add(binding.name);
|
|
1411
1538
|
const current = analyzer.get(document);
|
|
1412
1539
|
return [
|
|
1413
|
-
...valueItems(first.analysis,
|
|
1540
|
+
...valueItems(first.analysis, at2, insideFunction(first.path)),
|
|
1414
1541
|
...contextKeywords(source.slice(0, start)),
|
|
1415
1542
|
...importItems(analyzer, current, false, taken),
|
|
1416
1543
|
...serviceItems(current, taken)
|
|
@@ -1478,14 +1605,14 @@ function objectKeyItems(analysis, path, after) {
|
|
|
1478
1605
|
return members.filter((member) => !written.has(member.name)).map((member) => ({
|
|
1479
1606
|
label: member.name,
|
|
1480
1607
|
kind: import_vscode_languageserver5.CompletionItemKind.Field,
|
|
1481
|
-
detail: `${member.property.optional ? "?" : ""}: ${(0,
|
|
1608
|
+
detail: `${member.property.optional ? "?" : ""}: ${(0, import_luaut_parser7.formatType)(member.property.type)}`,
|
|
1482
1609
|
insertText: colon ? member.name : `${member.name}: `
|
|
1483
1610
|
}));
|
|
1484
1611
|
}
|
|
1485
1612
|
function indexKeys(analysis, position, literal) {
|
|
1486
1613
|
const path = pathAt(analysis.program, position, false);
|
|
1487
|
-
const
|
|
1488
|
-
const parent =
|
|
1614
|
+
const at2 = path.indexOf(literal);
|
|
1615
|
+
const parent = at2 > 0 ? path[at2 - 1] : void 0;
|
|
1489
1616
|
if (!parent) return [];
|
|
1490
1617
|
let indexed;
|
|
1491
1618
|
if (parent.type === "IndexedAccessTypeNode" && parent.indexType === literal) {
|
|
@@ -1577,20 +1704,24 @@ function isMethodOnly(type) {
|
|
|
1577
1704
|
return false;
|
|
1578
1705
|
}
|
|
1579
1706
|
}
|
|
1580
|
-
function
|
|
1707
|
+
function insideFunction(path) {
|
|
1708
|
+
return path.some((n) => n.type === "FunctionExpression" || n.type === "FunctionDeclaration" || n.type === "FunctionDeclarationStatement" || n.type === "FunctionBody");
|
|
1709
|
+
}
|
|
1710
|
+
function valueItems(analysis, at2, inFunction) {
|
|
1581
1711
|
const items = [];
|
|
1582
1712
|
const seen = /* @__PURE__ */ new Set();
|
|
1583
1713
|
for (const binding of analysis.scopes.bindings.values()) {
|
|
1584
1714
|
if (binding.name === PLACEHOLDER || seen.has(binding.name)) continue;
|
|
1585
1715
|
if (binding.declaredBy === "type") continue;
|
|
1586
1716
|
const declaration = binding.declarationNode;
|
|
1587
|
-
|
|
1717
|
+
const later = declaration !== void 0 && declaration.line.start - 1 > at2.line;
|
|
1718
|
+
if (later && !inFunction && binding.declaredBy !== "function") continue;
|
|
1588
1719
|
seen.add(binding.name);
|
|
1589
1720
|
const type = analysis.types.bindingType.get(binding.id);
|
|
1590
1721
|
items.push({
|
|
1591
1722
|
label: binding.name,
|
|
1592
1723
|
kind: kindOf(type, binding.kind),
|
|
1593
|
-
detail: type ? (0,
|
|
1724
|
+
detail: type ? (0, import_luaut_parser7.formatType)(type) : void 0,
|
|
1594
1725
|
// Locals before globals, and globals before library names.
|
|
1595
1726
|
sortText: `${binding.isBuiltin ? 2 : binding.kind === "global" ? 1 : 0}${binding.name}`
|
|
1596
1727
|
});
|
|
@@ -1614,7 +1745,7 @@ function memberItem(name, type, readonly) {
|
|
|
1614
1745
|
return {
|
|
1615
1746
|
label: name,
|
|
1616
1747
|
kind: import_vscode_languageserver5.CompletionItemKind.Field,
|
|
1617
|
-
detail: `${readonly ? "readonly " : ""}${(0,
|
|
1748
|
+
detail: `${readonly ? "readonly " : ""}${(0, import_luaut_parser7.formatType)(type)}`
|
|
1618
1749
|
};
|
|
1619
1750
|
}
|
|
1620
1751
|
function kindOf(type, bindingKind) {
|
|
@@ -1722,9 +1853,9 @@ function helpAt(analysis, position) {
|
|
|
1722
1853
|
function methodType(analysis, call) {
|
|
1723
1854
|
const object = call.object;
|
|
1724
1855
|
const method = call.method;
|
|
1725
|
-
const
|
|
1726
|
-
if (!
|
|
1727
|
-
return memberType(
|
|
1856
|
+
const objectType2 = analysis.types.typeOf.get(object);
|
|
1857
|
+
if (!objectType2) return void 0;
|
|
1858
|
+
return memberType(objectType2, method.name, analysis);
|
|
1728
1859
|
}
|
|
1729
1860
|
function memberType(type, name, analysis) {
|
|
1730
1861
|
if (type.kind === "object") return type.properties.get(name)?.type;
|
|
@@ -1755,7 +1886,7 @@ function activeArgument(call, position) {
|
|
|
1755
1886
|
|
|
1756
1887
|
// src/features/symbols.ts
|
|
1757
1888
|
var import_vscode_languageserver6 = require("vscode-languageserver");
|
|
1758
|
-
var
|
|
1889
|
+
var import_luaut_parser8 = require("luaut-parser");
|
|
1759
1890
|
function documentSymbols(analysis) {
|
|
1760
1891
|
const out = [];
|
|
1761
1892
|
walk(analysis.program, (node) => {
|
|
@@ -1772,10 +1903,19 @@ function documentSymbols(analysis) {
|
|
|
1772
1903
|
const name = typeof named === "string" ? named : named?.name;
|
|
1773
1904
|
if (name) {
|
|
1774
1905
|
const alias = analysis.types.aliases.get(name);
|
|
1775
|
-
out.push(symbol(name, import_vscode_languageserver6.SymbolKind.Interface, node, alias ? (0,
|
|
1906
|
+
out.push(symbol(name, import_vscode_languageserver6.SymbolKind.Interface, node, alias ? (0, import_luaut_parser8.formatType)(alias) : void 0));
|
|
1776
1907
|
}
|
|
1777
1908
|
break;
|
|
1778
1909
|
}
|
|
1910
|
+
case "ClassDeclaration": {
|
|
1911
|
+
const declaration = node;
|
|
1912
|
+
const superclass = declaration.superclass?.name;
|
|
1913
|
+
out.push({
|
|
1914
|
+
...symbol(declaration.name.name, import_vscode_languageserver6.SymbolKind.Class, node, superclass && `extends ${superclass}`),
|
|
1915
|
+
children: declaration.members.map((member) => classMember(analysis, member))
|
|
1916
|
+
});
|
|
1917
|
+
break;
|
|
1918
|
+
}
|
|
1779
1919
|
case "DeclareClassStatement": {
|
|
1780
1920
|
const name = node.name.name;
|
|
1781
1921
|
const superclass = node.superclass?.base;
|
|
@@ -1793,6 +1933,23 @@ function documentSymbols(analysis) {
|
|
|
1793
1933
|
});
|
|
1794
1934
|
return out;
|
|
1795
1935
|
}
|
|
1936
|
+
function classMember(analysis, member) {
|
|
1937
|
+
switch (member.type) {
|
|
1938
|
+
case "ClassConstructor":
|
|
1939
|
+
return symbol("constructor", import_vscode_languageserver6.SymbolKind.Constructor, member);
|
|
1940
|
+
case "ClassField":
|
|
1941
|
+
return symbol(
|
|
1942
|
+
member.name.name,
|
|
1943
|
+
member.isStatic ? import_vscode_languageserver6.SymbolKind.Constant : import_vscode_languageserver6.SymbolKind.Field,
|
|
1944
|
+
member,
|
|
1945
|
+
member.typeAnnotation ? void 0 : detailOf(analysis, member)
|
|
1946
|
+
);
|
|
1947
|
+
case "ClassAccessor":
|
|
1948
|
+
return symbol(member.name.name, import_vscode_languageserver6.SymbolKind.Property, member, member.kind);
|
|
1949
|
+
case "ClassMethod":
|
|
1950
|
+
return symbol(member.name.name, import_vscode_languageserver6.SymbolKind.Method, member, member.isStatic ? "static" : void 0);
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1796
1953
|
function functionName(node) {
|
|
1797
1954
|
const named = node;
|
|
1798
1955
|
if (typeof named.name === "string") return named.name;
|
|
@@ -1809,7 +1966,7 @@ function detailOf(analysis, node) {
|
|
|
1809
1966
|
if (name && typeof name === "object") {
|
|
1810
1967
|
const binding = bindingOfNode(analysis, name);
|
|
1811
1968
|
const type = binding && analysis.types.bindingType.get(binding.id);
|
|
1812
|
-
if (type) return (0,
|
|
1969
|
+
if (type) return (0, import_luaut_parser8.formatType)(type);
|
|
1813
1970
|
}
|
|
1814
1971
|
return void 0;
|
|
1815
1972
|
}
|
|
@@ -1819,7 +1976,7 @@ function symbol(name, kind, node, detail) {
|
|
|
1819
1976
|
}
|
|
1820
1977
|
|
|
1821
1978
|
// src/features/semanticTokens.ts
|
|
1822
|
-
var
|
|
1979
|
+
var import_luaut_parser9 = require("luaut-parser");
|
|
1823
1980
|
var TOKEN_TYPES = [
|
|
1824
1981
|
"namespace",
|
|
1825
1982
|
"type",
|
|
@@ -1849,21 +2006,23 @@ var SOFT_KEYWORDS = /* @__PURE__ */ new Set([
|
|
|
1849
2006
|
"asserts",
|
|
1850
2007
|
"satisfies",
|
|
1851
2008
|
"typeof",
|
|
1852
|
-
"default"
|
|
2009
|
+
"default",
|
|
2010
|
+
"new",
|
|
2011
|
+
"super"
|
|
1853
2012
|
]);
|
|
1854
2013
|
var CONTROL_KEYWORDS = /* @__PURE__ */ new Set(["default"]);
|
|
1855
2014
|
var PRIMITIVES3 = /* @__PURE__ */ new Set(["any", "unknown", "never", "nil", "boolean", "number", "string", "thread", "buffer"]);
|
|
1856
2015
|
function semanticTokens(analysis) {
|
|
1857
2016
|
const entries = /* @__PURE__ */ new Map();
|
|
1858
|
-
const add = (
|
|
1859
|
-
const line =
|
|
1860
|
-
const character =
|
|
2017
|
+
const add = (at2, length, type, modifiers = []) => {
|
|
2018
|
+
const line = at2.line.start - 1;
|
|
2019
|
+
const character = at2.column.start - 1;
|
|
1861
2020
|
const key = `${line}:${character}`;
|
|
1862
2021
|
if (!entries.has(key)) entries.set(key, { line, character, length, type, modifiers });
|
|
1863
2022
|
};
|
|
1864
2023
|
let tokens = [];
|
|
1865
2024
|
try {
|
|
1866
|
-
tokens = (0,
|
|
2025
|
+
tokens = (0, import_luaut_parser9.tokenize)(analysis.source);
|
|
1867
2026
|
} catch {
|
|
1868
2027
|
}
|
|
1869
2028
|
const identifiers = tokens.filter((t) => t.type === "Identifier");
|
|
@@ -1898,6 +2057,25 @@ function classify(analysis, spanned, ancestors, identifiers, add) {
|
|
|
1898
2057
|
add(node, name.length, valueKind(analysis, binding), modifiersOf(binding, true));
|
|
1899
2058
|
return;
|
|
1900
2059
|
}
|
|
2060
|
+
// A class body's own words. `get`, `set`, `static` and `constructor`
|
|
2061
|
+
// are ordinary names anywhere else, so they are coloured from the
|
|
2062
|
+
// member they open rather than wherever they are written.
|
|
2063
|
+
case "ClassMethod":
|
|
2064
|
+
case "ClassField":
|
|
2065
|
+
case "ClassAccessor":
|
|
2066
|
+
case "ClassConstructor": {
|
|
2067
|
+
const opener = node.type === "ClassConstructor" ? "constructor" : node.type === "ClassAccessor" ? node.kind : void 0;
|
|
2068
|
+
const words = firstTokensWithin(identifiers, node, 2);
|
|
2069
|
+
let index = 0;
|
|
2070
|
+
if (node.isStatic === true && words[index] && wordOf(words[index]) === "static") {
|
|
2071
|
+
add(words[index], "static".length, "keyword");
|
|
2072
|
+
index++;
|
|
2073
|
+
}
|
|
2074
|
+
if (opener && words[index] && wordOf(words[index]) === opener) {
|
|
2075
|
+
add(words[index], opener.length, "keyword");
|
|
2076
|
+
}
|
|
2077
|
+
return;
|
|
2078
|
+
}
|
|
1901
2079
|
case "TypeReference": {
|
|
1902
2080
|
const base = node.base;
|
|
1903
2081
|
const namespace = node.namespace;
|
|
@@ -1907,7 +2085,7 @@ function classify(analysis, spanned, ancestors, identifiers, add) {
|
|
|
1907
2085
|
if (!baseToken) return;
|
|
1908
2086
|
if (!namespace && typeParameterInScope2(ancestors, base)) {
|
|
1909
2087
|
add(baseToken, base.length, "typeParameter");
|
|
1910
|
-
} else if ((0,
|
|
2088
|
+
} else if ((0, import_luaut_parser9.isClassType)(analysis.types.aliases.get(namespace ? `${namespace}.${base}` : base) ?? import_luaut_parser9.unknownType)) {
|
|
1911
2089
|
add(baseToken, base.length, "class");
|
|
1912
2090
|
} else {
|
|
1913
2091
|
add(baseToken, base.length, "type", PRIMITIVES3.has(base) ? ["defaultLibrary"] : []);
|
|
@@ -1916,6 +2094,10 @@ function classify(analysis, spanned, ancestors, identifiers, add) {
|
|
|
1916
2094
|
}
|
|
1917
2095
|
}
|
|
1918
2096
|
}
|
|
2097
|
+
function wordOf(token) {
|
|
2098
|
+
const value = token.value;
|
|
2099
|
+
return typeof value === "string" ? value : void 0;
|
|
2100
|
+
}
|
|
1919
2101
|
function identifier(analysis, node, parent, add) {
|
|
1920
2102
|
const name = node.name;
|
|
1921
2103
|
const as = (type, modifiers = []) => add(node, name.length, type, modifiers);
|
|
@@ -1939,6 +2121,19 @@ function identifier(analysis, node, parent, add) {
|
|
|
1939
2121
|
case "DeclareClassStatement":
|
|
1940
2122
|
if (parent.name === node) return as("class", ["declaration"]);
|
|
1941
2123
|
break;
|
|
2124
|
+
case "ClassDeclaration":
|
|
2125
|
+
if (parent.name === node) return as("class", ["declaration"]);
|
|
2126
|
+
if (parent.superclass === node) return as("class");
|
|
2127
|
+
break;
|
|
2128
|
+
case "ClassMethod":
|
|
2129
|
+
if (parent.name === node) return as("method", ["declaration"]);
|
|
2130
|
+
break;
|
|
2131
|
+
case "ClassField":
|
|
2132
|
+
if (parent.name === node) return as("property", ["declaration"]);
|
|
2133
|
+
break;
|
|
2134
|
+
case "ClassAccessor":
|
|
2135
|
+
if (parent.name === node) return as("property", ["declaration"]);
|
|
2136
|
+
break;
|
|
1942
2137
|
case "DeclareStatement":
|
|
1943
2138
|
if (parent.id === node) return as(isFunction(typeOfNode(parent.valueType)) ? "function" : "variable", ["declaration"]);
|
|
1944
2139
|
break;
|
|
@@ -2159,6 +2354,11 @@ function createServer(connection, options = {}) {
|
|
|
2159
2354
|
(d) => hover(analyzer.get(d), p.position),
|
|
2160
2355
|
null
|
|
2161
2356
|
));
|
|
2357
|
+
connection.onRequest("luaut/hover", (p) => withDocument(
|
|
2358
|
+
p.textDocument.uri,
|
|
2359
|
+
(d) => hover(analyzer.get(d), p.position, Math.max(0, p.depth ?? 0)),
|
|
2360
|
+
null
|
|
2361
|
+
));
|
|
2162
2362
|
connection.onDefinition((p) => withDocument(
|
|
2163
2363
|
p.textDocument.uri,
|
|
2164
2364
|
(d) => {
|