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.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,93 @@ 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 (type.kind === "genericRef") return type.typeArguments.length ? void 0 : type.name;
|
|
814
|
+
return (0, import_luaut_parser5.aliasNameOf)(type);
|
|
815
|
+
}
|
|
816
|
+
function unnamed(type) {
|
|
817
|
+
return (0, import_luaut_parser5.withoutAliasName)(type);
|
|
818
|
+
}
|
|
819
|
+
function children2(type, f) {
|
|
820
|
+
switch (type.kind) {
|
|
821
|
+
case "array":
|
|
822
|
+
return (0, import_luaut_parser5.arrayOf)(f(type.element));
|
|
823
|
+
case "tuple":
|
|
824
|
+
return (0, import_luaut_parser5.tuple)(type.elements.map(f), type.isPack);
|
|
825
|
+
case "union":
|
|
826
|
+
return (0, import_luaut_parser5.union)(type.types.map(f));
|
|
827
|
+
case "intersection": {
|
|
828
|
+
const out = (0, import_luaut_parser5.intersection)(type.types.map(f));
|
|
829
|
+
return type.name && out.kind === "intersection" ? { ...out, name: type.name } : out;
|
|
830
|
+
}
|
|
831
|
+
case "object": {
|
|
832
|
+
if ((0, import_luaut_parser5.isClassType)(type)) return type;
|
|
833
|
+
const out = (0, import_luaut_parser5.objectType)(
|
|
834
|
+
[...type.properties].map(([name, property]) => [name, { ...property, type: f(property.type) }]),
|
|
835
|
+
type.indexer && { key: type.indexer.key, value: f(type.indexer.value) },
|
|
836
|
+
type.frozen
|
|
837
|
+
);
|
|
838
|
+
if (type.name) out.name = type.name;
|
|
839
|
+
return out;
|
|
840
|
+
}
|
|
841
|
+
case "function":
|
|
842
|
+
return (0, import_luaut_parser5.fn)(
|
|
843
|
+
type.params.map((p) => ({ ...p, type: f(p.type) })),
|
|
844
|
+
f(type.returns),
|
|
845
|
+
type.varargs && f(type.varargs),
|
|
846
|
+
type.typeParams,
|
|
847
|
+
type.predicate
|
|
848
|
+
);
|
|
849
|
+
default:
|
|
850
|
+
return type;
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
// src/features/hover.ts
|
|
855
|
+
function hover(analysis, position, depth = 0) {
|
|
799
856
|
const path = pathAt(analysis.program, position, true);
|
|
800
857
|
for (let i = path.length - 1; i >= 0; i--) {
|
|
801
858
|
if (UNNAMED.has(path[i].type)) return null;
|
|
802
|
-
const text =
|
|
803
|
-
if (text)
|
|
859
|
+
const text = at(analysis, path, i, depth);
|
|
860
|
+
if (!text) continue;
|
|
861
|
+
const canExpand = at(analysis, path, i, depth + 1) !== text;
|
|
862
|
+
return {
|
|
863
|
+
contents: { kind: "markdown", value: code(text) },
|
|
864
|
+
range: toRange(path[i]),
|
|
865
|
+
depth,
|
|
866
|
+
canExpand
|
|
867
|
+
};
|
|
804
868
|
}
|
|
805
869
|
return null;
|
|
806
870
|
}
|
|
871
|
+
function at(analysis, path, index, depth) {
|
|
872
|
+
const previous = expansion;
|
|
873
|
+
expansion = { depth, aliases: analysis.types.aliases, source: analysis.source };
|
|
874
|
+
try {
|
|
875
|
+
return describe(analysis, path, index);
|
|
876
|
+
} finally {
|
|
877
|
+
expansion = previous;
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
var expansion = {
|
|
881
|
+
depth: 0,
|
|
882
|
+
aliases: /* @__PURE__ */ new Map(),
|
|
883
|
+
source: ""
|
|
884
|
+
};
|
|
807
885
|
var UNNAMED = /* @__PURE__ */ new Set([
|
|
808
886
|
"BinaryExpression",
|
|
809
887
|
"UnaryExpression",
|
|
@@ -836,8 +914,8 @@ function describe(analysis, path, index) {
|
|
|
836
914
|
case "TableExpression": {
|
|
837
915
|
const field = fieldWithKey(parent, node);
|
|
838
916
|
if (!field) break;
|
|
839
|
-
const
|
|
840
|
-
const property =
|
|
917
|
+
const objectType2 = types.typeOf.get(parent);
|
|
918
|
+
const property = objectType2?.kind === "object" ? objectType2.properties.get(name) : void 0;
|
|
841
919
|
const type2 = property?.type ?? types.typeOf.get(field.value);
|
|
842
920
|
return type2 && `(property) ${name}: ${pretty(type2)}`;
|
|
843
921
|
}
|
|
@@ -885,6 +963,19 @@ function describe(analysis, path, index) {
|
|
|
885
963
|
case "DeclareClassStatement":
|
|
886
964
|
if (parent.name === node) return classText(analysis, name);
|
|
887
965
|
break;
|
|
966
|
+
case "ClassDeclaration":
|
|
967
|
+
if (parent.name === node) return classText(analysis, name);
|
|
968
|
+
break;
|
|
969
|
+
case "ClassField":
|
|
970
|
+
if (parent.name === node) {
|
|
971
|
+
const type2 = typeOfNode(parent.typeAnnotation) ?? types.typeOf.get(parent.init);
|
|
972
|
+
const prefix = parent.isStatic ? "(static) " : "(field) ";
|
|
973
|
+
return type2 ? `${prefix}${name}: ${pretty(type2)}` : `${prefix}${name}`;
|
|
974
|
+
}
|
|
975
|
+
break;
|
|
976
|
+
case "ClassAccessor":
|
|
977
|
+
if (parent.name === node) return `(${parent.kind === "get" ? "getter" : "setter"}) ${name}`;
|
|
978
|
+
break;
|
|
888
979
|
case "TableTypeProperty":
|
|
889
980
|
if (parent.key === node) {
|
|
890
981
|
const type2 = typeOfNode(parent.valueType);
|
|
@@ -907,7 +998,7 @@ function describe(analysis, path, index) {
|
|
|
907
998
|
case "MappedTypeNode":
|
|
908
999
|
if (parent.parameterId === node) {
|
|
909
1000
|
const keys = typeOfNode(parent.constraint);
|
|
910
|
-
return `(type parameter) ${name}${keys ? ` in ${(0,
|
|
1001
|
+
return `(type parameter) ${name}${keys ? ` in ${(0, import_luaut_parser6.formatType)(keys)}` : ""}`;
|
|
911
1002
|
}
|
|
912
1003
|
break;
|
|
913
1004
|
}
|
|
@@ -935,7 +1026,7 @@ function describe(analysis, path, index) {
|
|
|
935
1026
|
case "TypedIdentifier": {
|
|
936
1027
|
const binding = bindingOfNode(analysis, node);
|
|
937
1028
|
const type2 = binding && types.bindingType.get(binding.id);
|
|
938
|
-
return type2 ? bindingText(binding, type2) : void 0;
|
|
1029
|
+
return type2 ? bindingText(binding, type2, asWritten(node.typeAnnotation)) : void 0;
|
|
939
1030
|
}
|
|
940
1031
|
// A type written by name: `number`, `Shape`, `Partial<User>`, or a type
|
|
941
1032
|
// parameter in scope.
|
|
@@ -949,7 +1040,7 @@ function describe(analysis, path, index) {
|
|
|
949
1040
|
if (!node.typeArguments.length) {
|
|
950
1041
|
const qualified = node.namespace ? `${node.namespace}.${base}` : base;
|
|
951
1042
|
const alias = types.aliases.get(qualified);
|
|
952
|
-
if (alias && (0,
|
|
1043
|
+
if (alias && (0, import_luaut_parser6.isClassType)(alias)) return classText(analysis, qualified);
|
|
953
1044
|
if (alias) return `type ${qualified} = ${pretty(alias)}`;
|
|
954
1045
|
}
|
|
955
1046
|
const type2 = typeOfNode(node);
|
|
@@ -977,20 +1068,29 @@ function declareText(analysis, statement) {
|
|
|
977
1068
|
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
1069
|
const others = total - 1;
|
|
979
1070
|
const overloads = others > 0 ? ` (+${others} overload${others > 1 ? "s" : ""})` : "";
|
|
980
|
-
return `declare function ${name}${(0,
|
|
1071
|
+
return `declare function ${name}${(0, import_luaut_parser6.formatType)(own)}${overloads}`;
|
|
981
1072
|
}
|
|
982
1073
|
function classText(analysis, name) {
|
|
983
1074
|
const type = analysis.types.aliases.get(name);
|
|
984
|
-
if (!type || !(0,
|
|
1075
|
+
if (!type || !(0, import_luaut_parser6.isClassType)(type)) return void 0;
|
|
985
1076
|
const superclass = type.class.superclass;
|
|
986
1077
|
const inherited = superclass ? analysis.types.aliases.get(superclass) : void 0;
|
|
987
|
-
const own = [...type.properties].filter(([key, property]) =>
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
1078
|
+
const own = [...type.properties].filter(([key, property]) => (
|
|
1079
|
+
// `ClassObject` is on every instance and says nothing about this one.
|
|
1080
|
+
key !== "ClassObject" && (inherited?.kind !== "object" || inherited.properties.get(key) !== property)
|
|
1081
|
+
));
|
|
1082
|
+
const written = isRuntimeClass(analysis, name);
|
|
1083
|
+
const head = `${written ? "" : "declare "}class ${name}${superclass ? ` extends ${superclass}` : ""}`;
|
|
1084
|
+
const lines = own.map(([key, property]) => ` ${property.readonly ? "readonly " : ""}${key}${property.optional ? "?" : ""}: ${(0, import_luaut_parser6.formatType)(property.type)}${written ? "" : ","}`);
|
|
1085
|
+
return own.length ? `${head} {
|
|
992
1086
|
${lines.join("\n")}
|
|
993
|
-
}`;
|
|
1087
|
+
}` : `${head} {}`;
|
|
1088
|
+
}
|
|
1089
|
+
function isRuntimeClass(analysis, name) {
|
|
1090
|
+
return analysis.program.body.statements.some((statement) => {
|
|
1091
|
+
const declaration = statement.type === "ExportStatement" ? statement.declaration : statement;
|
|
1092
|
+
return declaration.type === "ClassDeclaration" && declaration.name.name === name;
|
|
1093
|
+
});
|
|
994
1094
|
}
|
|
995
1095
|
function typeParameterText(analysis, parameter) {
|
|
996
1096
|
return `(type parameter) ${typeParameterSignature(analysis, parameter)}`;
|
|
@@ -999,7 +1099,7 @@ function typeParameterSignature(analysis, parameter) {
|
|
|
999
1099
|
const p = parameter;
|
|
1000
1100
|
if (p.infer) return `infer ${p.name}`;
|
|
1001
1101
|
const constraint = p.constraint ? analysis.types.typeOfTypeNode.get(p.constraint) : void 0;
|
|
1002
|
-
return `${p.isConst ? "const " : ""}${p.name}${constraint ? ` extends ${(0,
|
|
1102
|
+
return `${p.isConst ? "const " : ""}${p.name}${constraint ? ` extends ${(0, import_luaut_parser6.formatType)(constraint)}` : ""}`;
|
|
1003
1103
|
}
|
|
1004
1104
|
function typeParameterInScope(path, index, name) {
|
|
1005
1105
|
for (let i = index - 1; i >= 0; i--) {
|
|
@@ -1024,7 +1124,7 @@ function referenceText(analysis, reference) {
|
|
|
1024
1124
|
if (!args.length) return name;
|
|
1025
1125
|
const resolved = args.map((a) => {
|
|
1026
1126
|
const t = analysis.types.typeOfTypeNode.get(a);
|
|
1027
|
-
return t ? (0,
|
|
1127
|
+
return t ? (0, import_luaut_parser6.formatType)(t) : "?";
|
|
1028
1128
|
});
|
|
1029
1129
|
return `${name}<${resolved.join(", ")}>`;
|
|
1030
1130
|
}
|
|
@@ -1032,30 +1132,50 @@ function fieldWithKey(table, key) {
|
|
|
1032
1132
|
const fields = table.fields;
|
|
1033
1133
|
return fields.find((f) => f.type === "TableFieldNamed" && f.key === key);
|
|
1034
1134
|
}
|
|
1035
|
-
function pretty(type) {
|
|
1036
|
-
const
|
|
1135
|
+
function pretty(type, written) {
|
|
1136
|
+
const named = render(expandAliases(type, expansion.aliases, 0));
|
|
1137
|
+
const shorthand = written !== void 0 && written !== named ? written : void 0;
|
|
1138
|
+
const depth = shorthand === void 0 ? expansion.depth : expansion.depth - 1;
|
|
1139
|
+
if (depth < 0) return shorthand;
|
|
1140
|
+
return depth === 0 ? named : render(expandAliases(type, expansion.aliases, depth));
|
|
1141
|
+
}
|
|
1142
|
+
function asWritten(node) {
|
|
1143
|
+
if (!isSpanned(node) || !expansion.source) return void 0;
|
|
1144
|
+
const lines = expansion.source.split(/\r?\n/);
|
|
1145
|
+
const { line, column } = node;
|
|
1146
|
+
if (line.start < 1 || line.end > lines.length) return void 0;
|
|
1147
|
+
const text = line.start === line.end ? lines[line.start - 1].slice(column.start - 1, column.end - 1) : [
|
|
1148
|
+
lines[line.start - 1].slice(column.start - 1),
|
|
1149
|
+
...lines.slice(line.start, line.end - 1),
|
|
1150
|
+
lines[line.end - 1].slice(0, column.end - 1)
|
|
1151
|
+
].join(" ");
|
|
1152
|
+
const folded = text.trim().replace(/\s+/g, " ");
|
|
1153
|
+
return folded.length ? folded : void 0;
|
|
1154
|
+
}
|
|
1155
|
+
function render(type) {
|
|
1156
|
+
const flat = (0, import_luaut_parser6.formatType)(type);
|
|
1037
1157
|
if (flat.length <= 80) return flat;
|
|
1038
1158
|
if (type.kind === "object") {
|
|
1039
1159
|
const lines = [];
|
|
1040
|
-
if (type.indexer) lines.push(` [${(0,
|
|
1160
|
+
if (type.indexer) lines.push(` [${(0, import_luaut_parser6.formatType)(type.indexer.key)}]: ${(0, import_luaut_parser6.formatType)(type.indexer.value)},`);
|
|
1041
1161
|
for (const [name, property] of type.properties) {
|
|
1042
1162
|
const readonly = property.readonly ? "readonly " : "";
|
|
1043
|
-
lines.push(` ${readonly}${name}${property.optional ? "?" : ""}: ${(0,
|
|
1163
|
+
lines.push(` ${readonly}${name}${property.optional ? "?" : ""}: ${(0, import_luaut_parser6.formatType)(property.type)},`);
|
|
1044
1164
|
}
|
|
1045
1165
|
return `{
|
|
1046
1166
|
${lines.join("\n")}
|
|
1047
1167
|
}`;
|
|
1048
1168
|
}
|
|
1049
1169
|
if (type.kind === "intersection" && type.types.every((t) => t.kind === "function")) {
|
|
1050
|
-
return type.types.map(
|
|
1170
|
+
return type.types.map(import_luaut_parser6.formatType).join("\n& ");
|
|
1051
1171
|
}
|
|
1052
1172
|
return flat;
|
|
1053
1173
|
}
|
|
1054
|
-
function bindingText(binding, type) {
|
|
1174
|
+
function bindingText(binding, type, written) {
|
|
1055
1175
|
if (binding.declaredBy === "function" && type.kind === "function") {
|
|
1056
|
-
return `function ${binding.name}${(
|
|
1176
|
+
return `function ${binding.name}${pretty(type)}`;
|
|
1057
1177
|
}
|
|
1058
|
-
return `${keyword(binding)} ${binding.name}: ${pretty(type)}`;
|
|
1178
|
+
return `${keyword(binding)} ${binding.name}: ${pretty(type, written)}`;
|
|
1059
1179
|
}
|
|
1060
1180
|
function keyword(binding) {
|
|
1061
1181
|
if (binding.kind === "param" || binding.kind === "self") return "(parameter)";
|
|
@@ -1131,7 +1251,7 @@ function isIdentifier(name) {
|
|
|
1131
1251
|
|
|
1132
1252
|
// src/features/completion.ts
|
|
1133
1253
|
var import_vscode_languageserver5 = require("vscode-languageserver");
|
|
1134
|
-
var
|
|
1254
|
+
var import_luaut_parser7 = require("luaut-parser");
|
|
1135
1255
|
|
|
1136
1256
|
// src/features/autoImport.ts
|
|
1137
1257
|
var import_node_fs3 = require("fs");
|
|
@@ -1168,7 +1288,7 @@ function importItems(analyzer, analysis, typePosition, taken) {
|
|
|
1168
1288
|
function serviceItems(analysis, taken) {
|
|
1169
1289
|
const services = analysis.types.aliases.get("Services");
|
|
1170
1290
|
if (!services || services.kind !== "object") return [];
|
|
1171
|
-
const
|
|
1291
|
+
const at2 = serviceInsertion(analysis.program.body.statements);
|
|
1172
1292
|
const items = [];
|
|
1173
1293
|
for (const name of services.properties.keys()) {
|
|
1174
1294
|
if (taken.has(name)) continue;
|
|
@@ -1179,8 +1299,8 @@ function serviceItems(analysis, taken) {
|
|
|
1179
1299
|
labelDetails: { description: "service" },
|
|
1180
1300
|
detail: line,
|
|
1181
1301
|
sortText: `5${name}`,
|
|
1182
|
-
additionalTextEdits: [{ range: { start:
|
|
1183
|
-
${
|
|
1302
|
+
additionalTextEdits: [{ range: { start: at2.position, end: at2.position }, newText: `${line}
|
|
1303
|
+
${at2.gap}` }]
|
|
1184
1304
|
});
|
|
1185
1305
|
}
|
|
1186
1306
|
return items;
|
|
@@ -1192,24 +1312,24 @@ function importEdit(analyzer, analysis, file, name, specifier, typePosition) {
|
|
|
1192
1312
|
if (existing) {
|
|
1193
1313
|
const last = existing.specifiers[existing.specifiers.length - 1];
|
|
1194
1314
|
if (last) {
|
|
1195
|
-
const
|
|
1196
|
-
return { range: { start:
|
|
1315
|
+
const at3 = endOf(last);
|
|
1316
|
+
return { range: { start: at3, end: at3 }, newText: `, ${name}` };
|
|
1197
1317
|
}
|
|
1198
1318
|
if (existing.defaultImport) {
|
|
1199
|
-
const
|
|
1200
|
-
return { range: { start:
|
|
1319
|
+
const at3 = endOf(existing.defaultImport);
|
|
1320
|
+
return { range: { start: at3, end: at3 }, newText: `, { ${name} }` };
|
|
1201
1321
|
}
|
|
1202
1322
|
}
|
|
1203
1323
|
const line = `import { ${name} } from "${specifier}"`;
|
|
1204
1324
|
const lastImport = imports[imports.length - 1];
|
|
1205
1325
|
if (lastImport) {
|
|
1206
|
-
const
|
|
1207
|
-
return { range: { start:
|
|
1326
|
+
const at3 = { line: lastImport.line.end, character: 0 };
|
|
1327
|
+
return { range: { start: at3, end: at3 }, newText: `${line}
|
|
1208
1328
|
` };
|
|
1209
1329
|
}
|
|
1210
1330
|
const first = statements[0];
|
|
1211
|
-
const
|
|
1212
|
-
return { range: { start:
|
|
1331
|
+
const at2 = { line: first ? first.line.start - 1 : 0, character: 0 };
|
|
1332
|
+
return { range: { start: at2, end: at2 }, newText: first ? `${line}
|
|
1213
1333
|
|
|
1214
1334
|
` : `${line}
|
|
1215
1335
|
` };
|
|
@@ -1302,12 +1422,12 @@ function completion(analyzer, document, position) {
|
|
|
1302
1422
|
const operator = memberOperator(source, start);
|
|
1303
1423
|
const alreadyCalled = /^\s*\(/.test(source.slice(end));
|
|
1304
1424
|
const standIns = operator === ":" ? [alreadyCalled ? PLACEHOLDER : `${PLACEHOLDER}()`] : operator === "." && !alreadyCalled ? [PLACEHOLDER, `${PLACEHOLDER}()`] : [PLACEHOLDER];
|
|
1305
|
-
const
|
|
1425
|
+
const at2 = { line: position.line, character: position.character - (offset - start) };
|
|
1306
1426
|
let first;
|
|
1307
1427
|
for (const standIn of standIns) {
|
|
1308
1428
|
const patched = source.slice(0, start) + standIn + source.slice(end);
|
|
1309
1429
|
const analysis = analyzer.analyze(document.uri, -1, patched);
|
|
1310
|
-
const path = pathAt(analysis.program,
|
|
1430
|
+
const path = pathAt(analysis.program, at2, true);
|
|
1311
1431
|
const index = path.findLastIndex(
|
|
1312
1432
|
(n) => n.type === "Identifier" && n.name === PLACEHOLDER
|
|
1313
1433
|
);
|
|
@@ -1323,8 +1443,8 @@ function completion(analyzer, document, position) {
|
|
|
1323
1443
|
if (inTypePosition(first.path)) {
|
|
1324
1444
|
const named = [...first.analysis.types.aliases].map(([name, type]) => ({
|
|
1325
1445
|
label: name,
|
|
1326
|
-
kind: (0,
|
|
1327
|
-
detail: (0,
|
|
1446
|
+
kind: (0, import_luaut_parser7.isClassType)(type) ? import_vscode_languageserver5.CompletionItemKind.Class : import_vscode_languageserver5.CompletionItemKind.Interface,
|
|
1447
|
+
detail: (0, import_luaut_parser7.isClassType)(type) ? "class" : "type"
|
|
1328
1448
|
}));
|
|
1329
1449
|
const primitives = PRIMITIVES2.map((name) => ({
|
|
1330
1450
|
label: name,
|
|
@@ -1344,7 +1464,7 @@ function completion(analyzer, document, position) {
|
|
|
1344
1464
|
for (const binding of first.analysis.scopes.bindings.values()) taken.add(binding.name);
|
|
1345
1465
|
const current = analyzer.get(document);
|
|
1346
1466
|
return [
|
|
1347
|
-
...valueItems(first.analysis,
|
|
1467
|
+
...valueItems(first.analysis, at2, insideFunction(first.path)),
|
|
1348
1468
|
...contextKeywords(source.slice(0, start)),
|
|
1349
1469
|
...importItems(analyzer, current, false, taken),
|
|
1350
1470
|
...serviceItems(current, taken)
|
|
@@ -1412,14 +1532,14 @@ function objectKeyItems(analysis, path, after) {
|
|
|
1412
1532
|
return members.filter((member) => !written.has(member.name)).map((member) => ({
|
|
1413
1533
|
label: member.name,
|
|
1414
1534
|
kind: import_vscode_languageserver5.CompletionItemKind.Field,
|
|
1415
|
-
detail: `${member.property.optional ? "?" : ""}: ${(0,
|
|
1535
|
+
detail: `${member.property.optional ? "?" : ""}: ${(0, import_luaut_parser7.formatType)(member.property.type)}`,
|
|
1416
1536
|
insertText: colon ? member.name : `${member.name}: `
|
|
1417
1537
|
}));
|
|
1418
1538
|
}
|
|
1419
1539
|
function indexKeys(analysis, position, literal) {
|
|
1420
1540
|
const path = pathAt(analysis.program, position, false);
|
|
1421
|
-
const
|
|
1422
|
-
const parent =
|
|
1541
|
+
const at2 = path.indexOf(literal);
|
|
1542
|
+
const parent = at2 > 0 ? path[at2 - 1] : void 0;
|
|
1423
1543
|
if (!parent) return [];
|
|
1424
1544
|
let indexed;
|
|
1425
1545
|
if (parent.type === "IndexedAccessTypeNode" && parent.indexType === literal) {
|
|
@@ -1511,20 +1631,24 @@ function isMethodOnly(type) {
|
|
|
1511
1631
|
return false;
|
|
1512
1632
|
}
|
|
1513
1633
|
}
|
|
1514
|
-
function
|
|
1634
|
+
function insideFunction(path) {
|
|
1635
|
+
return path.some((n) => n.type === "FunctionExpression" || n.type === "FunctionDeclaration" || n.type === "FunctionDeclarationStatement" || n.type === "FunctionBody");
|
|
1636
|
+
}
|
|
1637
|
+
function valueItems(analysis, at2, inFunction) {
|
|
1515
1638
|
const items = [];
|
|
1516
1639
|
const seen = /* @__PURE__ */ new Set();
|
|
1517
1640
|
for (const binding of analysis.scopes.bindings.values()) {
|
|
1518
1641
|
if (binding.name === PLACEHOLDER || seen.has(binding.name)) continue;
|
|
1519
1642
|
if (binding.declaredBy === "type") continue;
|
|
1520
1643
|
const declaration = binding.declarationNode;
|
|
1521
|
-
|
|
1644
|
+
const later = declaration !== void 0 && declaration.line.start - 1 > at2.line;
|
|
1645
|
+
if (later && !inFunction && binding.declaredBy !== "function") continue;
|
|
1522
1646
|
seen.add(binding.name);
|
|
1523
1647
|
const type = analysis.types.bindingType.get(binding.id);
|
|
1524
1648
|
items.push({
|
|
1525
1649
|
label: binding.name,
|
|
1526
1650
|
kind: kindOf(type, binding.kind),
|
|
1527
|
-
detail: type ? (0,
|
|
1651
|
+
detail: type ? (0, import_luaut_parser7.formatType)(type) : void 0,
|
|
1528
1652
|
// Locals before globals, and globals before library names.
|
|
1529
1653
|
sortText: `${binding.isBuiltin ? 2 : binding.kind === "global" ? 1 : 0}${binding.name}`
|
|
1530
1654
|
});
|
|
@@ -1548,7 +1672,7 @@ function memberItem(name, type, readonly) {
|
|
|
1548
1672
|
return {
|
|
1549
1673
|
label: name,
|
|
1550
1674
|
kind: import_vscode_languageserver5.CompletionItemKind.Field,
|
|
1551
|
-
detail: `${readonly ? "readonly " : ""}${(0,
|
|
1675
|
+
detail: `${readonly ? "readonly " : ""}${(0, import_luaut_parser7.formatType)(type)}`
|
|
1552
1676
|
};
|
|
1553
1677
|
}
|
|
1554
1678
|
function kindOf(type, bindingKind) {
|
|
@@ -1656,9 +1780,9 @@ function helpAt(analysis, position) {
|
|
|
1656
1780
|
function methodType(analysis, call) {
|
|
1657
1781
|
const object = call.object;
|
|
1658
1782
|
const method = call.method;
|
|
1659
|
-
const
|
|
1660
|
-
if (!
|
|
1661
|
-
return memberType(
|
|
1783
|
+
const objectType2 = analysis.types.typeOf.get(object);
|
|
1784
|
+
if (!objectType2) return void 0;
|
|
1785
|
+
return memberType(objectType2, method.name, analysis);
|
|
1662
1786
|
}
|
|
1663
1787
|
function memberType(type, name, analysis) {
|
|
1664
1788
|
if (type.kind === "object") return type.properties.get(name)?.type;
|
|
@@ -1689,7 +1813,7 @@ function activeArgument(call, position) {
|
|
|
1689
1813
|
|
|
1690
1814
|
// src/features/symbols.ts
|
|
1691
1815
|
var import_vscode_languageserver6 = require("vscode-languageserver");
|
|
1692
|
-
var
|
|
1816
|
+
var import_luaut_parser8 = require("luaut-parser");
|
|
1693
1817
|
function documentSymbols(analysis) {
|
|
1694
1818
|
const out = [];
|
|
1695
1819
|
walk(analysis.program, (node) => {
|
|
@@ -1706,10 +1830,19 @@ function documentSymbols(analysis) {
|
|
|
1706
1830
|
const name = typeof named === "string" ? named : named?.name;
|
|
1707
1831
|
if (name) {
|
|
1708
1832
|
const alias = analysis.types.aliases.get(name);
|
|
1709
|
-
out.push(symbol(name, import_vscode_languageserver6.SymbolKind.Interface, node, alias ? (0,
|
|
1833
|
+
out.push(symbol(name, import_vscode_languageserver6.SymbolKind.Interface, node, alias ? (0, import_luaut_parser8.formatType)(alias) : void 0));
|
|
1710
1834
|
}
|
|
1711
1835
|
break;
|
|
1712
1836
|
}
|
|
1837
|
+
case "ClassDeclaration": {
|
|
1838
|
+
const declaration = node;
|
|
1839
|
+
const superclass = declaration.superclass?.name;
|
|
1840
|
+
out.push({
|
|
1841
|
+
...symbol(declaration.name.name, import_vscode_languageserver6.SymbolKind.Class, node, superclass && `extends ${superclass}`),
|
|
1842
|
+
children: declaration.members.map((member) => classMember(analysis, member))
|
|
1843
|
+
});
|
|
1844
|
+
break;
|
|
1845
|
+
}
|
|
1713
1846
|
case "DeclareClassStatement": {
|
|
1714
1847
|
const name = node.name.name;
|
|
1715
1848
|
const superclass = node.superclass?.base;
|
|
@@ -1727,6 +1860,23 @@ function documentSymbols(analysis) {
|
|
|
1727
1860
|
});
|
|
1728
1861
|
return out;
|
|
1729
1862
|
}
|
|
1863
|
+
function classMember(analysis, member) {
|
|
1864
|
+
switch (member.type) {
|
|
1865
|
+
case "ClassConstructor":
|
|
1866
|
+
return symbol("constructor", import_vscode_languageserver6.SymbolKind.Constructor, member);
|
|
1867
|
+
case "ClassField":
|
|
1868
|
+
return symbol(
|
|
1869
|
+
member.name.name,
|
|
1870
|
+
member.isStatic ? import_vscode_languageserver6.SymbolKind.Constant : import_vscode_languageserver6.SymbolKind.Field,
|
|
1871
|
+
member,
|
|
1872
|
+
member.typeAnnotation ? void 0 : detailOf(analysis, member)
|
|
1873
|
+
);
|
|
1874
|
+
case "ClassAccessor":
|
|
1875
|
+
return symbol(member.name.name, import_vscode_languageserver6.SymbolKind.Property, member, member.kind);
|
|
1876
|
+
case "ClassMethod":
|
|
1877
|
+
return symbol(member.name.name, import_vscode_languageserver6.SymbolKind.Method, member, member.isStatic ? "static" : void 0);
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1730
1880
|
function functionName(node) {
|
|
1731
1881
|
const named = node;
|
|
1732
1882
|
if (typeof named.name === "string") return named.name;
|
|
@@ -1743,7 +1893,7 @@ function detailOf(analysis, node) {
|
|
|
1743
1893
|
if (name && typeof name === "object") {
|
|
1744
1894
|
const binding = bindingOfNode(analysis, name);
|
|
1745
1895
|
const type = binding && analysis.types.bindingType.get(binding.id);
|
|
1746
|
-
if (type) return (0,
|
|
1896
|
+
if (type) return (0, import_luaut_parser8.formatType)(type);
|
|
1747
1897
|
}
|
|
1748
1898
|
return void 0;
|
|
1749
1899
|
}
|
|
@@ -1753,7 +1903,7 @@ function symbol(name, kind, node, detail) {
|
|
|
1753
1903
|
}
|
|
1754
1904
|
|
|
1755
1905
|
// src/features/semanticTokens.ts
|
|
1756
|
-
var
|
|
1906
|
+
var import_luaut_parser9 = require("luaut-parser");
|
|
1757
1907
|
var TOKEN_TYPES = [
|
|
1758
1908
|
"namespace",
|
|
1759
1909
|
"type",
|
|
@@ -1783,21 +1933,23 @@ var SOFT_KEYWORDS = /* @__PURE__ */ new Set([
|
|
|
1783
1933
|
"asserts",
|
|
1784
1934
|
"satisfies",
|
|
1785
1935
|
"typeof",
|
|
1786
|
-
"default"
|
|
1936
|
+
"default",
|
|
1937
|
+
"new",
|
|
1938
|
+
"super"
|
|
1787
1939
|
]);
|
|
1788
1940
|
var CONTROL_KEYWORDS = /* @__PURE__ */ new Set(["default"]);
|
|
1789
1941
|
var PRIMITIVES3 = /* @__PURE__ */ new Set(["any", "unknown", "never", "nil", "boolean", "number", "string", "thread", "buffer"]);
|
|
1790
1942
|
function semanticTokens(analysis) {
|
|
1791
1943
|
const entries = /* @__PURE__ */ new Map();
|
|
1792
|
-
const add = (
|
|
1793
|
-
const line =
|
|
1794
|
-
const character =
|
|
1944
|
+
const add = (at2, length, type, modifiers = []) => {
|
|
1945
|
+
const line = at2.line.start - 1;
|
|
1946
|
+
const character = at2.column.start - 1;
|
|
1795
1947
|
const key = `${line}:${character}`;
|
|
1796
1948
|
if (!entries.has(key)) entries.set(key, { line, character, length, type, modifiers });
|
|
1797
1949
|
};
|
|
1798
1950
|
let tokens = [];
|
|
1799
1951
|
try {
|
|
1800
|
-
tokens = (0,
|
|
1952
|
+
tokens = (0, import_luaut_parser9.tokenize)(analysis.source);
|
|
1801
1953
|
} catch {
|
|
1802
1954
|
}
|
|
1803
1955
|
const identifiers = tokens.filter((t) => t.type === "Identifier");
|
|
@@ -1832,6 +1984,25 @@ function classify(analysis, spanned, ancestors, identifiers, add) {
|
|
|
1832
1984
|
add(node, name.length, valueKind(analysis, binding), modifiersOf(binding, true));
|
|
1833
1985
|
return;
|
|
1834
1986
|
}
|
|
1987
|
+
// A class body's own words. `get`, `set`, `static` and `constructor`
|
|
1988
|
+
// are ordinary names anywhere else, so they are coloured from the
|
|
1989
|
+
// member they open rather than wherever they are written.
|
|
1990
|
+
case "ClassMethod":
|
|
1991
|
+
case "ClassField":
|
|
1992
|
+
case "ClassAccessor":
|
|
1993
|
+
case "ClassConstructor": {
|
|
1994
|
+
const opener = node.type === "ClassConstructor" ? "constructor" : node.type === "ClassAccessor" ? node.kind : void 0;
|
|
1995
|
+
const words = firstTokensWithin(identifiers, node, 2);
|
|
1996
|
+
let index = 0;
|
|
1997
|
+
if (node.isStatic === true && words[index] && wordOf(words[index]) === "static") {
|
|
1998
|
+
add(words[index], "static".length, "keyword");
|
|
1999
|
+
index++;
|
|
2000
|
+
}
|
|
2001
|
+
if (opener && words[index] && wordOf(words[index]) === opener) {
|
|
2002
|
+
add(words[index], opener.length, "keyword");
|
|
2003
|
+
}
|
|
2004
|
+
return;
|
|
2005
|
+
}
|
|
1835
2006
|
case "TypeReference": {
|
|
1836
2007
|
const base = node.base;
|
|
1837
2008
|
const namespace = node.namespace;
|
|
@@ -1841,7 +2012,7 @@ function classify(analysis, spanned, ancestors, identifiers, add) {
|
|
|
1841
2012
|
if (!baseToken) return;
|
|
1842
2013
|
if (!namespace && typeParameterInScope2(ancestors, base)) {
|
|
1843
2014
|
add(baseToken, base.length, "typeParameter");
|
|
1844
|
-
} else if ((0,
|
|
2015
|
+
} else if ((0, import_luaut_parser9.isClassType)(analysis.types.aliases.get(namespace ? `${namespace}.${base}` : base) ?? import_luaut_parser9.unknownType)) {
|
|
1845
2016
|
add(baseToken, base.length, "class");
|
|
1846
2017
|
} else {
|
|
1847
2018
|
add(baseToken, base.length, "type", PRIMITIVES3.has(base) ? ["defaultLibrary"] : []);
|
|
@@ -1850,6 +2021,10 @@ function classify(analysis, spanned, ancestors, identifiers, add) {
|
|
|
1850
2021
|
}
|
|
1851
2022
|
}
|
|
1852
2023
|
}
|
|
2024
|
+
function wordOf(token) {
|
|
2025
|
+
const value = token.value;
|
|
2026
|
+
return typeof value === "string" ? value : void 0;
|
|
2027
|
+
}
|
|
1853
2028
|
function identifier(analysis, node, parent, add) {
|
|
1854
2029
|
const name = node.name;
|
|
1855
2030
|
const as = (type, modifiers = []) => add(node, name.length, type, modifiers);
|
|
@@ -1873,6 +2048,19 @@ function identifier(analysis, node, parent, add) {
|
|
|
1873
2048
|
case "DeclareClassStatement":
|
|
1874
2049
|
if (parent.name === node) return as("class", ["declaration"]);
|
|
1875
2050
|
break;
|
|
2051
|
+
case "ClassDeclaration":
|
|
2052
|
+
if (parent.name === node) return as("class", ["declaration"]);
|
|
2053
|
+
if (parent.superclass === node) return as("class");
|
|
2054
|
+
break;
|
|
2055
|
+
case "ClassMethod":
|
|
2056
|
+
if (parent.name === node) return as("method", ["declaration"]);
|
|
2057
|
+
break;
|
|
2058
|
+
case "ClassField":
|
|
2059
|
+
if (parent.name === node) return as("property", ["declaration"]);
|
|
2060
|
+
break;
|
|
2061
|
+
case "ClassAccessor":
|
|
2062
|
+
if (parent.name === node) return as("property", ["declaration"]);
|
|
2063
|
+
break;
|
|
1876
2064
|
case "DeclareStatement":
|
|
1877
2065
|
if (parent.id === node) return as(isFunction(typeOfNode(parent.valueType)) ? "function" : "variable", ["declaration"]);
|
|
1878
2066
|
break;
|
|
@@ -2093,6 +2281,11 @@ function createServer(connection, options = {}) {
|
|
|
2093
2281
|
(d) => hover(analyzer.get(d), p.position),
|
|
2094
2282
|
null
|
|
2095
2283
|
));
|
|
2284
|
+
connection.onRequest("luaut/hover", (p) => withDocument(
|
|
2285
|
+
p.textDocument.uri,
|
|
2286
|
+
(d) => hover(analyzer.get(d), p.position, Math.max(0, p.depth ?? 0)),
|
|
2287
|
+
null
|
|
2288
|
+
));
|
|
2096
2289
|
connection.onDefinition((p) => withDocument(
|
|
2097
2290
|
p.textDocument.uri,
|
|
2098
2291
|
(d) => {
|