luaut-language-server 3.0.0 → 4.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 +2 -2
- package/dist/{chunk-X2GHHRWT.js → chunk-XK3KS3T4.js} +381 -42
- package/dist/chunk-XK3KS3T4.js.map +1 -0
- package/dist/cli.cjs +406 -67
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/index.cjs +406 -67
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -1
- package/package.json +4 -4
- package/dist/chunk-X2GHHRWT.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -68,6 +68,19 @@ var import_luaut_parser2 = require("luaut-parser");
|
|
|
68
68
|
|
|
69
69
|
// src/features/members.ts
|
|
70
70
|
var import_luaut_parser = require("luaut-parser");
|
|
71
|
+
function literalKeys(key, aliases) {
|
|
72
|
+
if (!key) return [];
|
|
73
|
+
const resolved = key.kind === "genericRef" ? aliases.get(key.name) : key;
|
|
74
|
+
if (!resolved) return [];
|
|
75
|
+
const parts = resolved.kind === "union" ? resolved.types : [resolved];
|
|
76
|
+
const out = [];
|
|
77
|
+
for (const part of parts) {
|
|
78
|
+
const member = part.kind === "genericRef" ? aliases.get(part.name) ?? part : part;
|
|
79
|
+
if (member.kind !== "literal" || typeof member.value !== "string") return [];
|
|
80
|
+
out.push(member.value);
|
|
81
|
+
}
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
71
84
|
function membersOf(type, aliases, seen = /* @__PURE__ */ new Set()) {
|
|
72
85
|
if (!type || seen.has(type)) return [];
|
|
73
86
|
seen.add(type);
|
|
@@ -77,6 +90,11 @@ function membersOf(type, aliases, seen = /* @__PURE__ */ new Set()) {
|
|
|
77
90
|
for (const [name, property] of type.properties) {
|
|
78
91
|
out.push({ name, property, isMethod: takesSelf(property.type) });
|
|
79
92
|
}
|
|
93
|
+
for (const name of literalKeys(type.indexer?.key, aliases)) {
|
|
94
|
+
if (type.properties.has(name)) continue;
|
|
95
|
+
const property = { type: type.indexer.value, optional: true };
|
|
96
|
+
out.push({ name, property, isMethod: takesSelf(property.type) });
|
|
97
|
+
}
|
|
80
98
|
return out;
|
|
81
99
|
}
|
|
82
100
|
case "intersection": {
|
|
@@ -98,6 +116,20 @@ function membersOf(type, aliases, seen = /* @__PURE__ */ new Set()) {
|
|
|
98
116
|
}
|
|
99
117
|
case "typeParam":
|
|
100
118
|
return membersOf(type.constraint, aliases, seen);
|
|
119
|
+
// An array and a string answer to the methods the language gives them
|
|
120
|
+
// — `names:filter(f)`, `text:trim()`. They are written in the parser's
|
|
121
|
+
// prelude as `ArrayMethods<T>` and `StringMethods`, so the element
|
|
122
|
+
// type goes in where `T` stands.
|
|
123
|
+
case "array":
|
|
124
|
+
case "tuple": {
|
|
125
|
+
const element = type.kind === "array" ? type.element : (0, import_luaut_parser.union)(type.elements);
|
|
126
|
+
const methods = aliases.get("ArrayMethods");
|
|
127
|
+
return methods ? membersOf((0, import_luaut_parser.substitute)(methods, /* @__PURE__ */ new Map([["T", element]])), aliases, seen) : [];
|
|
128
|
+
}
|
|
129
|
+
case "primitive":
|
|
130
|
+
return type.name === "string" ? membersOf(aliases.get("StringMethods"), aliases, seen) : [];
|
|
131
|
+
case "literal":
|
|
132
|
+
return type.base === "string" ? membersOf(aliases.get("StringMethods"), aliases, seen) : [];
|
|
101
133
|
default:
|
|
102
134
|
return [];
|
|
103
135
|
}
|
|
@@ -396,11 +428,13 @@ var Analyzer = class {
|
|
|
396
428
|
const script = path ? context.sourceMap?.scriptFor(path) : void 0;
|
|
397
429
|
const libs = script ? [...context.libs, script] : context.libs;
|
|
398
430
|
const globals = script ? [...context.globals, "script"] : context.globals;
|
|
399
|
-
const { program, errors } = (0, import_luaut_parser2.parseWithRecovery)(source);
|
|
400
|
-
const
|
|
431
|
+
const { program, errors, directives } = (0, import_luaut_parser2.parseWithRecovery)(source);
|
|
432
|
+
const reportUndeclared = context.libs.length > 0;
|
|
433
|
+
const scopes = (0, import_luaut_parser2.analyzeScopes)(program, { builtinGlobals: [...globals], reportUndeclared });
|
|
401
434
|
const dependencies = new Map(context.reads);
|
|
402
435
|
const types = (0, import_luaut_parser2.analyzeTypes)(program, scopes, {
|
|
403
436
|
libs,
|
|
437
|
+
reportUnknownTypes: reportUndeclared,
|
|
404
438
|
resolveModule: (specifier) => {
|
|
405
439
|
if (!path) return void 0;
|
|
406
440
|
const candidates = this.candidatesFor(path, specifier);
|
|
@@ -414,7 +448,7 @@ var Analyzer = class {
|
|
|
414
448
|
return exports2;
|
|
415
449
|
}
|
|
416
450
|
});
|
|
417
|
-
return { uri, version, source, program, parseErrors: errors, scopes, types, dependencies, project: context.project };
|
|
451
|
+
return { uri, version, source, program, parseErrors: errors, directives, scopes, types, dependencies, project: context.project };
|
|
418
452
|
}
|
|
419
453
|
exportsOf(path, importing) {
|
|
420
454
|
const key = pathKey(path);
|
|
@@ -509,7 +543,7 @@ function collect(container, out) {
|
|
|
509
543
|
}
|
|
510
544
|
}
|
|
511
545
|
function isSpanlessNode(v) {
|
|
512
|
-
return !!v && typeof v === "object" &&
|
|
546
|
+
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
513
547
|
}
|
|
514
548
|
function pathAt(root, pos, inclusive = false) {
|
|
515
549
|
let best;
|
|
@@ -780,6 +814,7 @@ function patternNamed(target, name) {
|
|
|
780
814
|
|
|
781
815
|
// src/features/diagnostics.ts
|
|
782
816
|
var import_vscode_languageserver2 = require("vscode-languageserver");
|
|
817
|
+
var import_luaut_parser4 = require("luaut-parser");
|
|
783
818
|
function diagnostics(analysis) {
|
|
784
819
|
const out = [];
|
|
785
820
|
for (const error of analysis.parseErrors) {
|
|
@@ -793,29 +828,39 @@ function diagnostics(analysis) {
|
|
|
793
828
|
message: error.message.replace(/\s*\(\d+:\d+\)$/, "")
|
|
794
829
|
});
|
|
795
830
|
}
|
|
796
|
-
|
|
797
|
-
|
|
831
|
+
const semantic = [
|
|
832
|
+
...analysis.scopes.diagnostics.map((d) => ({
|
|
798
833
|
range: toRange(d.node),
|
|
799
834
|
severity: import_vscode_languageserver2.DiagnosticSeverity.Error,
|
|
800
835
|
source: "luaut",
|
|
801
836
|
code: d.kind,
|
|
802
837
|
message: d.message
|
|
803
|
-
})
|
|
804
|
-
|
|
805
|
-
for (const d of analysis.types.diagnostics) {
|
|
806
|
-
out.push({
|
|
838
|
+
})),
|
|
839
|
+
...analysis.types.diagnostics.map((d) => ({
|
|
807
840
|
range: toRange(d.node),
|
|
808
841
|
severity: import_vscode_languageserver2.DiagnosticSeverity.Error,
|
|
809
842
|
source: "luaut",
|
|
810
843
|
code: "type",
|
|
811
844
|
message: d.message
|
|
845
|
+
}))
|
|
846
|
+
];
|
|
847
|
+
const { kept, unusedExpectErrors } = (0, import_luaut_parser4.applyDirectives)(analysis.directives, semantic, (d) => d.range.start.line + 1);
|
|
848
|
+
out.push(...kept);
|
|
849
|
+
for (const directive of unusedExpectErrors) {
|
|
850
|
+
const start = toPosition(directive.line, directive.column);
|
|
851
|
+
out.push({
|
|
852
|
+
range: { start, end: { line: start.line, character: start.character + "--@luaut-expect-error".length } },
|
|
853
|
+
severity: import_vscode_languageserver2.DiagnosticSeverity.Error,
|
|
854
|
+
source: "luaut",
|
|
855
|
+
code: "directive",
|
|
856
|
+
message: import_luaut_parser4.UNUSED_EXPECT_ERROR
|
|
812
857
|
});
|
|
813
858
|
}
|
|
814
859
|
return out;
|
|
815
860
|
}
|
|
816
861
|
|
|
817
862
|
// src/features/hover.ts
|
|
818
|
-
var
|
|
863
|
+
var import_luaut_parser5 = require("luaut-parser");
|
|
819
864
|
function hover(analysis, position) {
|
|
820
865
|
const path = pathAt(analysis.program, position, true);
|
|
821
866
|
for (let i = path.length - 1; i >= 0; i--) {
|
|
@@ -862,6 +907,27 @@ function describe(analysis, path, index) {
|
|
|
862
907
|
const type2 = property?.type ?? types.typeOf.get(field.value);
|
|
863
908
|
return type2 && `(property) ${name}: ${pretty(type2)}`;
|
|
864
909
|
}
|
|
910
|
+
// `const { name } = t`: a shorthand key *is* the binding it
|
|
911
|
+
// declares, and has the same span, so the cursor can land on
|
|
912
|
+
// either. A renamed key (`{ name: other }`) names the property
|
|
913
|
+
// the value is read from.
|
|
914
|
+
case "ObjectPatternProperty": {
|
|
915
|
+
if (parent.key !== node || parent.computed) break;
|
|
916
|
+
const value = parent.value;
|
|
917
|
+
if (parent.shorthand) return describe(analysis, [...path.slice(0, index), value], index);
|
|
918
|
+
const binding2 = value.type === "IdentifierPattern" ? bindingOfNode(analysis, value) : void 0;
|
|
919
|
+
const type2 = binding2 && types.bindingType.get(binding2.id);
|
|
920
|
+
return type2 && `(property) ${name}: ${pretty(type2)}`;
|
|
921
|
+
}
|
|
922
|
+
// One line of an overload set reads as its own signature.
|
|
923
|
+
// The line the body is on reads as the whole set, which is
|
|
924
|
+
// what the binding says and what the default path gives.
|
|
925
|
+
case "FunctionSignature": {
|
|
926
|
+
if (parent.name !== node) break;
|
|
927
|
+
const own = types.typeOfTypeNode.get(parent);
|
|
928
|
+
if (own) return `function ${name}${pretty(own)}`;
|
|
929
|
+
break;
|
|
930
|
+
}
|
|
865
931
|
case "ImportSpecifier": {
|
|
866
932
|
const alias = types.aliases.get(name);
|
|
867
933
|
const binding2 = bindingOfNode(analysis, identifier2);
|
|
@@ -907,7 +973,7 @@ function describe(analysis, path, index) {
|
|
|
907
973
|
case "MappedTypeNode":
|
|
908
974
|
if (parent.parameterId === node) {
|
|
909
975
|
const keys = typeOfNode(parent.constraint);
|
|
910
|
-
return `(type parameter) ${name}${keys ? ` in ${(0,
|
|
976
|
+
return `(type parameter) ${name}${keys ? ` in ${(0, import_luaut_parser5.formatType)(keys)}` : ""}`;
|
|
911
977
|
}
|
|
912
978
|
break;
|
|
913
979
|
}
|
|
@@ -924,6 +990,11 @@ function describe(analysis, path, index) {
|
|
|
924
990
|
}
|
|
925
991
|
return void 0;
|
|
926
992
|
}
|
|
993
|
+
// `...` — what this function's extra arguments are.
|
|
994
|
+
case "VarargExpression": {
|
|
995
|
+
const type2 = types.typeOf.get(node);
|
|
996
|
+
return type2 ? `(vararg) ...: ${pretty(type2)}` : void 0;
|
|
997
|
+
}
|
|
927
998
|
// Declarations: `const x`, a parameter.
|
|
928
999
|
case "IdentifierPattern":
|
|
929
1000
|
case "FunctionParameter":
|
|
@@ -944,7 +1015,7 @@ function describe(analysis, path, index) {
|
|
|
944
1015
|
if (!node.typeArguments.length) {
|
|
945
1016
|
const qualified = node.namespace ? `${node.namespace}.${base}` : base;
|
|
946
1017
|
const alias = types.aliases.get(qualified);
|
|
947
|
-
if (alias && (0,
|
|
1018
|
+
if (alias && (0, import_luaut_parser5.isClassType)(alias)) return classText(analysis, qualified);
|
|
948
1019
|
if (alias) return `type ${qualified} = ${pretty(alias)}`;
|
|
949
1020
|
}
|
|
950
1021
|
const type2 = typeOfNode(node);
|
|
@@ -972,17 +1043,17 @@ function declareText(analysis, statement) {
|
|
|
972
1043
|
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);
|
|
973
1044
|
const others = total - 1;
|
|
974
1045
|
const overloads = others > 0 ? ` (+${others} overload${others > 1 ? "s" : ""})` : "";
|
|
975
|
-
return `declare function ${name}${(0,
|
|
1046
|
+
return `declare function ${name}${(0, import_luaut_parser5.formatType)(own)}${overloads}`;
|
|
976
1047
|
}
|
|
977
1048
|
function classText(analysis, name) {
|
|
978
1049
|
const type = analysis.types.aliases.get(name);
|
|
979
|
-
if (!type || !(0,
|
|
1050
|
+
if (!type || !(0, import_luaut_parser5.isClassType)(type)) return void 0;
|
|
980
1051
|
const superclass = type.class.superclass;
|
|
981
1052
|
const inherited = superclass ? analysis.types.aliases.get(superclass) : void 0;
|
|
982
1053
|
const own = [...type.properties].filter(([key, property]) => inherited?.kind !== "object" || inherited.properties.get(key) !== property);
|
|
983
1054
|
const head = `declare class ${name}${superclass ? ` extends ${superclass}` : ""}`;
|
|
984
1055
|
if (!own.length) return `${head} {}`;
|
|
985
|
-
const lines = own.map(([key, property]) => ` ${property.readonly ? "readonly " : ""}${key}${property.optional ? "?" : ""}: ${(0,
|
|
1056
|
+
const lines = own.map(([key, property]) => ` ${property.readonly ? "readonly " : ""}${key}${property.optional ? "?" : ""}: ${(0, import_luaut_parser5.formatType)(property.type)},`);
|
|
986
1057
|
return `${head} {
|
|
987
1058
|
${lines.join("\n")}
|
|
988
1059
|
}`;
|
|
@@ -994,7 +1065,7 @@ function typeParameterSignature(analysis, parameter) {
|
|
|
994
1065
|
const p = parameter;
|
|
995
1066
|
if (p.infer) return `infer ${p.name}`;
|
|
996
1067
|
const constraint = p.constraint ? analysis.types.typeOfTypeNode.get(p.constraint) : void 0;
|
|
997
|
-
return `${p.isConst ? "const " : ""}${p.name}${constraint ? ` extends ${(0,
|
|
1068
|
+
return `${p.isConst ? "const " : ""}${p.name}${constraint ? ` extends ${(0, import_luaut_parser5.formatType)(constraint)}` : ""}`;
|
|
998
1069
|
}
|
|
999
1070
|
function typeParameterInScope(path, index, name) {
|
|
1000
1071
|
for (let i = index - 1; i >= 0; i--) {
|
|
@@ -1019,7 +1090,7 @@ function referenceText(analysis, reference) {
|
|
|
1019
1090
|
if (!args.length) return name;
|
|
1020
1091
|
const resolved = args.map((a) => {
|
|
1021
1092
|
const t = analysis.types.typeOfTypeNode.get(a);
|
|
1022
|
-
return t ? (0,
|
|
1093
|
+
return t ? (0, import_luaut_parser5.formatType)(t) : "?";
|
|
1023
1094
|
});
|
|
1024
1095
|
return `${name}<${resolved.join(", ")}>`;
|
|
1025
1096
|
}
|
|
@@ -1028,27 +1099,27 @@ function fieldWithKey(table, key) {
|
|
|
1028
1099
|
return fields.find((f) => f.type === "TableFieldNamed" && f.key === key);
|
|
1029
1100
|
}
|
|
1030
1101
|
function pretty(type) {
|
|
1031
|
-
const flat = (0,
|
|
1102
|
+
const flat = (0, import_luaut_parser5.formatType)(type);
|
|
1032
1103
|
if (flat.length <= 80) return flat;
|
|
1033
1104
|
if (type.kind === "object") {
|
|
1034
1105
|
const lines = [];
|
|
1035
|
-
if (type.indexer) lines.push(` [${(0,
|
|
1106
|
+
if (type.indexer) lines.push(` [${(0, import_luaut_parser5.formatType)(type.indexer.key)}]: ${(0, import_luaut_parser5.formatType)(type.indexer.value)},`);
|
|
1036
1107
|
for (const [name, property] of type.properties) {
|
|
1037
1108
|
const readonly = property.readonly ? "readonly " : "";
|
|
1038
|
-
lines.push(` ${readonly}${name}${property.optional ? "?" : ""}: ${(0,
|
|
1109
|
+
lines.push(` ${readonly}${name}${property.optional ? "?" : ""}: ${(0, import_luaut_parser5.formatType)(property.type)},`);
|
|
1039
1110
|
}
|
|
1040
1111
|
return `{
|
|
1041
1112
|
${lines.join("\n")}
|
|
1042
1113
|
}`;
|
|
1043
1114
|
}
|
|
1044
1115
|
if (type.kind === "intersection" && type.types.every((t) => t.kind === "function")) {
|
|
1045
|
-
return type.types.map(
|
|
1116
|
+
return type.types.map(import_luaut_parser5.formatType).join("\n& ");
|
|
1046
1117
|
}
|
|
1047
1118
|
return flat;
|
|
1048
1119
|
}
|
|
1049
1120
|
function bindingText(binding, type) {
|
|
1050
1121
|
if (binding.declaredBy === "function" && type.kind === "function") {
|
|
1051
|
-
return `function ${binding.name}${(0,
|
|
1122
|
+
return `function ${binding.name}${(0, import_luaut_parser5.formatType)(type)}`;
|
|
1052
1123
|
}
|
|
1053
1124
|
return `${keyword(binding)} ${binding.name}: ${pretty(type)}`;
|
|
1054
1125
|
}
|
|
@@ -1125,8 +1196,162 @@ function isIdentifier(name) {
|
|
|
1125
1196
|
}
|
|
1126
1197
|
|
|
1127
1198
|
// src/features/completion.ts
|
|
1199
|
+
var import_vscode_languageserver5 = require("vscode-languageserver");
|
|
1200
|
+
var import_luaut_parser6 = require("luaut-parser");
|
|
1201
|
+
|
|
1202
|
+
// src/features/autoImport.ts
|
|
1203
|
+
var import_node_fs3 = require("fs");
|
|
1204
|
+
var import_node_path3 = require("path");
|
|
1128
1205
|
var import_vscode_languageserver4 = require("vscode-languageserver");
|
|
1129
|
-
|
|
1206
|
+
function importItems(analyzer, analysis, typePosition, taken) {
|
|
1207
|
+
const from = pathOfUri(analysis.uri);
|
|
1208
|
+
if (!from) return [];
|
|
1209
|
+
const config = analysis.project.config;
|
|
1210
|
+
const items = [];
|
|
1211
|
+
const offered = /* @__PURE__ */ new Set();
|
|
1212
|
+
for (const file of projectFiles(config?.directory ?? (0, import_node_path3.dirname)(from))) {
|
|
1213
|
+
if (samePath(file, from)) continue;
|
|
1214
|
+
const exports2 = analyzer.exportsAt(file);
|
|
1215
|
+
if (!exports2 || exports2.partial) continue;
|
|
1216
|
+
const names = typePosition ? [...exports2.types.keys()] : [...exports2.values.keys()];
|
|
1217
|
+
const specifier = specifierFor(from, file, config);
|
|
1218
|
+
for (const name of names) {
|
|
1219
|
+
if (taken.has(name) || offered.has(name)) continue;
|
|
1220
|
+
offered.add(name);
|
|
1221
|
+
const type = typePosition ? exports2.types.get(name)?.type : exports2.values.get(name);
|
|
1222
|
+
items.push({
|
|
1223
|
+
label: name,
|
|
1224
|
+
kind: typePosition ? import_vscode_languageserver4.CompletionItemKind.Interface : type && signaturesOf(type).length ? import_vscode_languageserver4.CompletionItemKind.Function : import_vscode_languageserver4.CompletionItemKind.Variable,
|
|
1225
|
+
labelDetails: { description: specifier },
|
|
1226
|
+
detail: `import { ${name} } from "${specifier}"`,
|
|
1227
|
+
sortText: `4${name}`,
|
|
1228
|
+
additionalTextEdits: [importEdit(analyzer, analysis, file, name, specifier, typePosition)]
|
|
1229
|
+
});
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
return items;
|
|
1233
|
+
}
|
|
1234
|
+
function serviceItems(analysis, taken) {
|
|
1235
|
+
const services = analysis.types.aliases.get("Services");
|
|
1236
|
+
if (!services || services.kind !== "object") return [];
|
|
1237
|
+
const at = serviceInsertion(analysis.program.body.statements);
|
|
1238
|
+
const items = [];
|
|
1239
|
+
for (const name of services.properties.keys()) {
|
|
1240
|
+
if (taken.has(name)) continue;
|
|
1241
|
+
const line = `const ${name} = game:GetService("${name}")`;
|
|
1242
|
+
items.push({
|
|
1243
|
+
label: name,
|
|
1244
|
+
kind: import_vscode_languageserver4.CompletionItemKind.Module,
|
|
1245
|
+
labelDetails: { description: "service" },
|
|
1246
|
+
detail: line,
|
|
1247
|
+
sortText: `5${name}`,
|
|
1248
|
+
additionalTextEdits: [{ range: { start: at.position, end: at.position }, newText: `${line}
|
|
1249
|
+
${at.gap}` }]
|
|
1250
|
+
});
|
|
1251
|
+
}
|
|
1252
|
+
return items;
|
|
1253
|
+
}
|
|
1254
|
+
function importEdit(analyzer, analysis, file, name, specifier, typePosition) {
|
|
1255
|
+
const statements = analysis.program.body.statements;
|
|
1256
|
+
const imports = statements.filter((s) => s.type === "ImportStatement");
|
|
1257
|
+
const existing = imports.find((s) => !s.namespaceImport && (typePosition || !s.isTypeOnly) && samePathOrUndefined(analyzer.resolveModulePath(analysis.uri, s.source.value), file));
|
|
1258
|
+
if (existing) {
|
|
1259
|
+
const last = existing.specifiers[existing.specifiers.length - 1];
|
|
1260
|
+
if (last) {
|
|
1261
|
+
const at2 = endOf(last);
|
|
1262
|
+
return { range: { start: at2, end: at2 }, newText: `, ${name}` };
|
|
1263
|
+
}
|
|
1264
|
+
if (existing.defaultImport) {
|
|
1265
|
+
const at2 = endOf(existing.defaultImport);
|
|
1266
|
+
return { range: { start: at2, end: at2 }, newText: `, { ${name} }` };
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
const line = `import { ${name} } from "${specifier}"`;
|
|
1270
|
+
const lastImport = imports[imports.length - 1];
|
|
1271
|
+
if (lastImport) {
|
|
1272
|
+
const at2 = { line: lastImport.line.end, character: 0 };
|
|
1273
|
+
return { range: { start: at2, end: at2 }, newText: `${line}
|
|
1274
|
+
` };
|
|
1275
|
+
}
|
|
1276
|
+
const first = statements[0];
|
|
1277
|
+
const at = { line: first ? first.line.start - 1 : 0, character: 0 };
|
|
1278
|
+
return { range: { start: at, end: at }, newText: first ? `${line}
|
|
1279
|
+
|
|
1280
|
+
` : `${line}
|
|
1281
|
+
` };
|
|
1282
|
+
}
|
|
1283
|
+
function serviceInsertion(statements) {
|
|
1284
|
+
let last;
|
|
1285
|
+
for (const statement of statements) {
|
|
1286
|
+
if (statement.type !== "ImportStatement" && !isServiceDeclaration(statement)) break;
|
|
1287
|
+
last = statement;
|
|
1288
|
+
}
|
|
1289
|
+
if (last) return { position: { line: last.line.end, character: 0 }, gap: "" };
|
|
1290
|
+
const first = statements[0];
|
|
1291
|
+
return first ? { position: { line: first.line.start - 1, character: 0 }, gap: "\n" } : { position: { line: 0, character: 0 }, gap: "" };
|
|
1292
|
+
}
|
|
1293
|
+
function isServiceDeclaration(statement) {
|
|
1294
|
+
if (statement.type !== "VariableDeclaration") return false;
|
|
1295
|
+
const init = statement.init[0];
|
|
1296
|
+
return init?.type === "MethodCallExpression" && init.method.name === "GetService" && init.object.type === "Identifier" && init.object.name === "game";
|
|
1297
|
+
}
|
|
1298
|
+
function endOf(node) {
|
|
1299
|
+
return { line: node.line.end - 1, character: node.column.end - 1 };
|
|
1300
|
+
}
|
|
1301
|
+
function samePathOrUndefined(a, b) {
|
|
1302
|
+
return a !== void 0 && samePath(a, b);
|
|
1303
|
+
}
|
|
1304
|
+
function specifierFor(from, target, config) {
|
|
1305
|
+
const withoutExtension = (path) => {
|
|
1306
|
+
const bare = path.replace(/\\/g, "/").replace(/\.luaut$/, "");
|
|
1307
|
+
return bare.endsWith("/index") ? bare.slice(0, -"/index".length) : bare;
|
|
1308
|
+
};
|
|
1309
|
+
let relativePath = withoutExtension((0, import_node_path3.relative)((0, import_node_path3.dirname)(from), target));
|
|
1310
|
+
if (!relativePath.startsWith(".")) relativePath = `./${relativePath}`;
|
|
1311
|
+
if (!relativePath.startsWith("../") || !config) return relativePath;
|
|
1312
|
+
for (const [pattern, targets] of Object.entries(config.paths)) {
|
|
1313
|
+
const star = pattern.indexOf("*");
|
|
1314
|
+
if (star < 0) continue;
|
|
1315
|
+
for (const targetPattern of targets) {
|
|
1316
|
+
const cut = targetPattern.indexOf("*");
|
|
1317
|
+
if (cut < 0) continue;
|
|
1318
|
+
const head = (0, import_node_path3.resolve)(config.baseUrl, targetPattern.slice(0, cut));
|
|
1319
|
+
const rest = (0, import_node_path3.relative)(head, target);
|
|
1320
|
+
if (rest.startsWith("..") || (0, import_node_path3.resolve)(head, rest) !== (0, import_node_path3.resolve)(target)) continue;
|
|
1321
|
+
return `${pattern.slice(0, star)}${withoutExtension(rest)}${pattern.slice(star + 1)}`;
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
return relativePath;
|
|
1325
|
+
}
|
|
1326
|
+
var FILE_LIMIT = 2e3;
|
|
1327
|
+
var LISTING_TTL = 3e3;
|
|
1328
|
+
var listings = /* @__PURE__ */ new Map();
|
|
1329
|
+
function projectFiles(root) {
|
|
1330
|
+
const cached = listings.get(root);
|
|
1331
|
+
if (cached && Date.now() - cached.at < LISTING_TTL) return cached.files;
|
|
1332
|
+
const files = [];
|
|
1333
|
+
const walk2 = (directory, depth) => {
|
|
1334
|
+
if (files.length >= FILE_LIMIT || depth > 12) return;
|
|
1335
|
+
let entries;
|
|
1336
|
+
try {
|
|
1337
|
+
entries = (0, import_node_fs3.readdirSync)(directory, { withFileTypes: true });
|
|
1338
|
+
} catch {
|
|
1339
|
+
return;
|
|
1340
|
+
}
|
|
1341
|
+
for (const entry of entries) {
|
|
1342
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
1343
|
+
const path = (0, import_node_path3.join)(directory, entry.name);
|
|
1344
|
+
if (entry.isDirectory()) walk2(path, depth + 1);
|
|
1345
|
+
else if (entry.name.endsWith(".luaut") && !entry.name.endsWith(".d.luaut")) files.push(path);
|
|
1346
|
+
if (files.length >= FILE_LIMIT) return;
|
|
1347
|
+
}
|
|
1348
|
+
};
|
|
1349
|
+
walk2(root, 0);
|
|
1350
|
+
listings.set(root, { at: Date.now(), files });
|
|
1351
|
+
return files;
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
// src/features/completion.ts
|
|
1130
1355
|
var PLACEHOLDER = "__luautCompletion__";
|
|
1131
1356
|
var IDENTIFIER_CHAR = /[A-Za-z0-9_]/;
|
|
1132
1357
|
function completion(analyzer, document, position) {
|
|
@@ -1159,28 +1384,58 @@ function completion(analyzer, document, position) {
|
|
|
1159
1384
|
first ??= { analysis, path };
|
|
1160
1385
|
}
|
|
1161
1386
|
if (operator || !first) return [];
|
|
1387
|
+
const keys = objectKeyItems(first.analysis, first.path, source.slice(end));
|
|
1388
|
+
if (keys) return keys;
|
|
1162
1389
|
if (inTypePosition(first.path)) {
|
|
1163
1390
|
const named = [...first.analysis.types.aliases].map(([name, type]) => ({
|
|
1164
1391
|
label: name,
|
|
1165
|
-
kind: (0,
|
|
1166
|
-
detail: (0,
|
|
1392
|
+
kind: (0, import_luaut_parser6.isClassType)(type) ? import_vscode_languageserver5.CompletionItemKind.Class : import_vscode_languageserver5.CompletionItemKind.Interface,
|
|
1393
|
+
detail: (0, import_luaut_parser6.isClassType)(type) ? "class" : "type"
|
|
1167
1394
|
}));
|
|
1168
1395
|
const primitives = PRIMITIVES2.map((name) => ({
|
|
1169
1396
|
label: name,
|
|
1170
|
-
kind:
|
|
1397
|
+
kind: import_vscode_languageserver5.CompletionItemKind.Keyword,
|
|
1171
1398
|
detail: "type"
|
|
1172
1399
|
}));
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1400
|
+
const keywords = TYPE_KEYWORDS.map((name) => ({
|
|
1401
|
+
label: name,
|
|
1402
|
+
kind: import_vscode_languageserver5.CompletionItemKind.Keyword,
|
|
1403
|
+
sortText: `3${name}`
|
|
1404
|
+
}));
|
|
1405
|
+
const typeNames = new Set(first.analysis.types.aliases.keys());
|
|
1406
|
+
const imported = importItems(analyzer, analyzer.get(document), true, typeNames);
|
|
1407
|
+
return [...named, ...primitives, ...keywords, ...imported];
|
|
1408
|
+
}
|
|
1409
|
+
const taken = /* @__PURE__ */ new Set();
|
|
1410
|
+
for (const binding of first.analysis.scopes.bindings.values()) taken.add(binding.name);
|
|
1411
|
+
const current = analyzer.get(document);
|
|
1412
|
+
return [
|
|
1413
|
+
...valueItems(first.analysis, at),
|
|
1414
|
+
...contextKeywords(source.slice(0, start)),
|
|
1415
|
+
...importItems(analyzer, current, false, taken),
|
|
1416
|
+
...serviceItems(current, taken)
|
|
1417
|
+
];
|
|
1176
1418
|
}
|
|
1177
1419
|
function stringCompletion(analyzer, document, position) {
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1420
|
+
let analysis = analyzer.get(document);
|
|
1421
|
+
let literal = stringAt(analysis, position);
|
|
1422
|
+
if (!literal) {
|
|
1423
|
+
const repaired = repairedStrings(document, position);
|
|
1424
|
+
for (const text of repaired) {
|
|
1425
|
+
const candidate = analyzer.analyze(document.uri, -1, text);
|
|
1426
|
+
literal = stringAt(candidate, position);
|
|
1427
|
+
if (literal) {
|
|
1428
|
+
analysis = candidate;
|
|
1429
|
+
break;
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
if (!literal) return repaired.length ? [] : void 0;
|
|
1433
|
+
}
|
|
1182
1434
|
const expected = analysis.types.expectedTypeOf.get(literal);
|
|
1183
|
-
const values =
|
|
1435
|
+
const values = [.../* @__PURE__ */ new Set([
|
|
1436
|
+
...stringLiterals(expected, analysis.types.aliases),
|
|
1437
|
+
...indexKeys(analysis, position, literal)
|
|
1438
|
+
])];
|
|
1184
1439
|
if (!values.length) return [];
|
|
1185
1440
|
const line = literal.line.start - 1;
|
|
1186
1441
|
const range = literal.line.start === literal.line.end ? {
|
|
@@ -1189,10 +1444,81 @@ function stringCompletion(analyzer, document, position) {
|
|
|
1189
1444
|
} : void 0;
|
|
1190
1445
|
return values.map((value) => ({
|
|
1191
1446
|
label: value,
|
|
1192
|
-
kind:
|
|
1447
|
+
kind: import_vscode_languageserver5.CompletionItemKind.Constant,
|
|
1193
1448
|
...range ? { textEdit: { range, newText: value } } : {}
|
|
1194
1449
|
}));
|
|
1195
1450
|
}
|
|
1451
|
+
function stringAt(analysis, position) {
|
|
1452
|
+
const path = pathAt(analysis.program, position, false);
|
|
1453
|
+
return [...path].reverse().find((n) => n.type === "StringLiteral" || n.type === "TypeLiteralString");
|
|
1454
|
+
}
|
|
1455
|
+
function objectKeyItems(analysis, path, after) {
|
|
1456
|
+
const index = path.findLastIndex(
|
|
1457
|
+
(n) => n.type === "Identifier" && n.name === PLACEHOLDER
|
|
1458
|
+
);
|
|
1459
|
+
const literal = index > 0 ? path[index - 1] : void 0;
|
|
1460
|
+
if (literal?.type !== "TableExpression") return void 0;
|
|
1461
|
+
const fields = literal.fields;
|
|
1462
|
+
const atKey = fields.some((f) => f.type === "TableFieldShorthand" && f.name?.name === PLACEHOLDER);
|
|
1463
|
+
if (!atKey) return void 0;
|
|
1464
|
+
let expected = analysis.types.expectedTypeOf.get(literal);
|
|
1465
|
+
for (let up = index - 2; expected === void 0 && up >= 0; up--) {
|
|
1466
|
+
const outer = path[up];
|
|
1467
|
+
if (outer.type !== "AsConstExpression" && outer.type !== "ParenthesizedExpression") break;
|
|
1468
|
+
expected = analysis.types.expectedTypeOf.get(outer);
|
|
1469
|
+
}
|
|
1470
|
+
const members = membersOf(expected, analysis.types.aliases);
|
|
1471
|
+
if (!members.length) return void 0;
|
|
1472
|
+
const written = /* @__PURE__ */ new Set();
|
|
1473
|
+
for (const field of fields) {
|
|
1474
|
+
if (field.type === "TableFieldNamed") written.add(field.key?.name ?? field.key?.value ?? "");
|
|
1475
|
+
else if (field.type === "TableFieldShorthand" && field.name?.name !== PLACEHOLDER) written.add(field.name.name);
|
|
1476
|
+
}
|
|
1477
|
+
const colon = /^\s*:/.test(after);
|
|
1478
|
+
return members.filter((member) => !written.has(member.name)).map((member) => ({
|
|
1479
|
+
label: member.name,
|
|
1480
|
+
kind: import_vscode_languageserver5.CompletionItemKind.Field,
|
|
1481
|
+
detail: `${member.property.optional ? "?" : ""}: ${(0, import_luaut_parser6.formatType)(member.property.type)}`,
|
|
1482
|
+
insertText: colon ? member.name : `${member.name}: `
|
|
1483
|
+
}));
|
|
1484
|
+
}
|
|
1485
|
+
function indexKeys(analysis, position, literal) {
|
|
1486
|
+
const path = pathAt(analysis.program, position, false);
|
|
1487
|
+
const at = path.indexOf(literal);
|
|
1488
|
+
const parent = at > 0 ? path[at - 1] : void 0;
|
|
1489
|
+
if (!parent) return [];
|
|
1490
|
+
let indexed;
|
|
1491
|
+
if (parent.type === "IndexedAccessTypeNode" && parent.indexType === literal) {
|
|
1492
|
+
indexed = analysis.types.typeOfTypeNode.get(parent.objectType);
|
|
1493
|
+
} else if (parent.type === "IndexExpression" && parent.index === literal) {
|
|
1494
|
+
indexed = withoutNil(analysis.types.typeOf.get(parent.object));
|
|
1495
|
+
}
|
|
1496
|
+
return membersOf(indexed, analysis.types.aliases).map((member) => member.name);
|
|
1497
|
+
}
|
|
1498
|
+
function repairedStrings(document, position) {
|
|
1499
|
+
const source = document.getText();
|
|
1500
|
+
const offset = document.offsetAt(position);
|
|
1501
|
+
const lineStart = offset - position.character;
|
|
1502
|
+
const lineEndIndex = source.indexOf("\n", offset);
|
|
1503
|
+
const lineEnd = lineEndIndex < 0 ? source.length : lineEndIndex;
|
|
1504
|
+
const before = source.slice(lineStart, offset);
|
|
1505
|
+
let quote;
|
|
1506
|
+
for (let i = 0; i < before.length; i++) {
|
|
1507
|
+
const ch = before[i];
|
|
1508
|
+
if (quote) {
|
|
1509
|
+
if (ch === "\\") i++;
|
|
1510
|
+
else if (ch === quote) quote = void 0;
|
|
1511
|
+
} else if (ch === '"' || ch === "'") {
|
|
1512
|
+
quote = ch;
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
if (!quote) return [];
|
|
1516
|
+
let rest = source.slice(offset, lineEnd).replace(/\r$/, "");
|
|
1517
|
+
if (!rest.includes(quote)) rest += quote;
|
|
1518
|
+
const line = before + rest;
|
|
1519
|
+
const endings = ["", " then end", " do end", ")", ") then end", "]"];
|
|
1520
|
+
return endings.map((ending) => source.slice(0, lineStart) + line + ending + source.slice(lineEnd));
|
|
1521
|
+
}
|
|
1196
1522
|
function stringLiterals(type, aliases, seen = /* @__PURE__ */ new Set()) {
|
|
1197
1523
|
if (!type || seen.has(type)) return [];
|
|
1198
1524
|
seen.add(type);
|
|
@@ -1224,27 +1550,29 @@ function memberOperator(source, wordStart) {
|
|
|
1224
1550
|
}
|
|
1225
1551
|
function memberItems(analysis, access) {
|
|
1226
1552
|
const object = access.object;
|
|
1227
|
-
const type = analysis.types.typeOf.get(object);
|
|
1553
|
+
const type = withoutNil(analysis.types.typeOf.get(object));
|
|
1228
1554
|
const colon = access.type === "MethodCallExpression";
|
|
1229
|
-
if (
|
|
1230
|
-
if (!colon) return [];
|
|
1231
|
-
const id = analysis.scopes.globalsByName.get("string");
|
|
1232
|
-
const library = id === void 0 ? void 0 : analysis.types.bindingType.get(id);
|
|
1233
|
-
return membersOf(library, analysis.types.aliases).filter((member) => signaturesOf(member.property.type).length > 0).map((member) => memberItem(member.name, member.property.type, member.property.readonly));
|
|
1234
|
-
}
|
|
1555
|
+
if (isMethodOnly(type) && !colon) return [];
|
|
1235
1556
|
return membersOf(type, analysis.types.aliases).filter((member) => colon ? member.isMethod : true).map((member) => memberItem(member.name, member.property.type, member.property.readonly));
|
|
1236
1557
|
}
|
|
1237
|
-
function
|
|
1558
|
+
function withoutNil(type) {
|
|
1559
|
+
if (type?.kind !== "union") return type;
|
|
1560
|
+
const kept = type.types.filter((t) => !(t.kind === "primitive" && t.name === "nil"));
|
|
1561
|
+
return kept.length === 1 ? kept[0] : { ...type, types: kept };
|
|
1562
|
+
}
|
|
1563
|
+
function isMethodOnly(type) {
|
|
1238
1564
|
if (!type) return false;
|
|
1239
1565
|
switch (type.kind) {
|
|
1566
|
+
case "array":
|
|
1567
|
+
case "tuple":
|
|
1568
|
+
case "templateLiteral":
|
|
1569
|
+
return true;
|
|
1240
1570
|
case "primitive":
|
|
1241
1571
|
return type.name === "string";
|
|
1242
1572
|
case "literal":
|
|
1243
1573
|
return typeof type.value === "string";
|
|
1244
|
-
case "templateLiteral":
|
|
1245
|
-
return true;
|
|
1246
1574
|
case "union":
|
|
1247
|
-
return type.types.length > 0 && type.types.every(
|
|
1575
|
+
return type.types.length > 0 && type.types.every(isMethodOnly);
|
|
1248
1576
|
default:
|
|
1249
1577
|
return false;
|
|
1250
1578
|
}
|
|
@@ -1262,13 +1590,13 @@ function valueItems(analysis, at) {
|
|
|
1262
1590
|
items.push({
|
|
1263
1591
|
label: binding.name,
|
|
1264
1592
|
kind: kindOf(type, binding.kind),
|
|
1265
|
-
detail: type ? (0,
|
|
1593
|
+
detail: type ? (0, import_luaut_parser6.formatType)(type) : void 0,
|
|
1266
1594
|
// Locals before globals, and globals before library names.
|
|
1267
1595
|
sortText: `${binding.isBuiltin ? 2 : binding.kind === "global" ? 1 : 0}${binding.name}`
|
|
1268
1596
|
});
|
|
1269
1597
|
}
|
|
1270
1598
|
for (const keyword2 of KEYWORDS) {
|
|
1271
|
-
items.push({ label: keyword2, kind:
|
|
1599
|
+
items.push({ label: keyword2, kind: import_vscode_languageserver5.CompletionItemKind.Keyword, sortText: `3${keyword2}` });
|
|
1272
1600
|
}
|
|
1273
1601
|
return items;
|
|
1274
1602
|
}
|
|
@@ -1277,22 +1605,22 @@ function memberItem(name, type, readonly) {
|
|
|
1277
1605
|
if (signatures.length) {
|
|
1278
1606
|
return {
|
|
1279
1607
|
label: name,
|
|
1280
|
-
kind:
|
|
1608
|
+
kind: import_vscode_languageserver5.CompletionItemKind.Method,
|
|
1281
1609
|
detail: signatureLabel(signatures[0]).label,
|
|
1282
1610
|
insertText: `${name}($0)`,
|
|
1283
|
-
insertTextFormat:
|
|
1611
|
+
insertTextFormat: import_vscode_languageserver5.InsertTextFormat.Snippet
|
|
1284
1612
|
};
|
|
1285
1613
|
}
|
|
1286
1614
|
return {
|
|
1287
1615
|
label: name,
|
|
1288
|
-
kind:
|
|
1289
|
-
detail: `${readonly ? "readonly " : ""}${(0,
|
|
1616
|
+
kind: import_vscode_languageserver5.CompletionItemKind.Field,
|
|
1617
|
+
detail: `${readonly ? "readonly " : ""}${(0, import_luaut_parser6.formatType)(type)}`
|
|
1290
1618
|
};
|
|
1291
1619
|
}
|
|
1292
1620
|
function kindOf(type, bindingKind) {
|
|
1293
|
-
if (type && signaturesOf(type).length) return
|
|
1294
|
-
if (bindingKind === "param" || bindingKind === "self") return
|
|
1295
|
-
return
|
|
1621
|
+
if (type && signaturesOf(type).length) return import_vscode_languageserver5.CompletionItemKind.Function;
|
|
1622
|
+
if (bindingKind === "param" || bindingKind === "self") return import_vscode_languageserver5.CompletionItemKind.Variable;
|
|
1623
|
+
return import_vscode_languageserver5.CompletionItemKind.Variable;
|
|
1296
1624
|
}
|
|
1297
1625
|
function inTypePosition(path) {
|
|
1298
1626
|
return path.some(
|
|
@@ -1310,6 +1638,17 @@ var PRIMITIVES2 = [
|
|
|
1310
1638
|
"thread",
|
|
1311
1639
|
"buffer"
|
|
1312
1640
|
];
|
|
1641
|
+
var TYPE_KEYWORDS = ["keyof", "typeof", "infer", "extends"];
|
|
1642
|
+
function contextKeywords(before) {
|
|
1643
|
+
const keyword2 = (name) => ({
|
|
1644
|
+
label: name,
|
|
1645
|
+
kind: import_vscode_languageserver5.CompletionItemKind.Keyword,
|
|
1646
|
+
sortText: `3${name}`
|
|
1647
|
+
});
|
|
1648
|
+
if (/<\s*[A-Za-z_]\w*(?:\s*,\s*[A-Za-z_]\w*)*\s+$/.test(before)) return [keyword2("extends")];
|
|
1649
|
+
if (/[)\]}"'`\w][^\S\n]+$/.test(before)) return [keyword2("as"), keyword2("satisfies")];
|
|
1650
|
+
return [];
|
|
1651
|
+
}
|
|
1313
1652
|
var KEYWORDS = [
|
|
1314
1653
|
"const",
|
|
1315
1654
|
"let",
|
|
@@ -1415,8 +1754,8 @@ function activeArgument(call, position) {
|
|
|
1415
1754
|
}
|
|
1416
1755
|
|
|
1417
1756
|
// src/features/symbols.ts
|
|
1418
|
-
var
|
|
1419
|
-
var
|
|
1757
|
+
var import_vscode_languageserver6 = require("vscode-languageserver");
|
|
1758
|
+
var import_luaut_parser7 = require("luaut-parser");
|
|
1420
1759
|
function documentSymbols(analysis) {
|
|
1421
1760
|
const out = [];
|
|
1422
1761
|
walk(analysis.program, (node) => {
|
|
@@ -1424,7 +1763,7 @@ function documentSymbols(analysis) {
|
|
|
1424
1763
|
case "FunctionDeclaration":
|
|
1425
1764
|
case "FunctionDeclarationStatement": {
|
|
1426
1765
|
const name = functionName(node);
|
|
1427
|
-
if (name) out.push(symbol(name,
|
|
1766
|
+
if (name) out.push(symbol(name, import_vscode_languageserver6.SymbolKind.Function, node, detailOf(analysis, node)));
|
|
1428
1767
|
break;
|
|
1429
1768
|
}
|
|
1430
1769
|
case "TypeAliasStatement":
|
|
@@ -1433,20 +1772,20 @@ function documentSymbols(analysis) {
|
|
|
1433
1772
|
const name = typeof named === "string" ? named : named?.name;
|
|
1434
1773
|
if (name) {
|
|
1435
1774
|
const alias = analysis.types.aliases.get(name);
|
|
1436
|
-
out.push(symbol(name,
|
|
1775
|
+
out.push(symbol(name, import_vscode_languageserver6.SymbolKind.Interface, node, alias ? (0, import_luaut_parser7.formatType)(alias) : void 0));
|
|
1437
1776
|
}
|
|
1438
1777
|
break;
|
|
1439
1778
|
}
|
|
1440
1779
|
case "DeclareClassStatement": {
|
|
1441
1780
|
const name = node.name.name;
|
|
1442
1781
|
const superclass = node.superclass?.base;
|
|
1443
|
-
out.push(symbol(name,
|
|
1782
|
+
out.push(symbol(name, import_vscode_languageserver6.SymbolKind.Class, node, superclass && `extends ${superclass}`));
|
|
1444
1783
|
break;
|
|
1445
1784
|
}
|
|
1446
1785
|
case "VariableDeclaration": {
|
|
1447
1786
|
for (const target of node.names ?? []) {
|
|
1448
1787
|
const name = target.name;
|
|
1449
|
-
if (name) out.push(symbol(name,
|
|
1788
|
+
if (name) out.push(symbol(name, import_vscode_languageserver6.SymbolKind.Variable, target));
|
|
1450
1789
|
}
|
|
1451
1790
|
break;
|
|
1452
1791
|
}
|
|
@@ -1470,7 +1809,7 @@ function detailOf(analysis, node) {
|
|
|
1470
1809
|
if (name && typeof name === "object") {
|
|
1471
1810
|
const binding = bindingOfNode(analysis, name);
|
|
1472
1811
|
const type = binding && analysis.types.bindingType.get(binding.id);
|
|
1473
|
-
if (type) return (0,
|
|
1812
|
+
if (type) return (0, import_luaut_parser7.formatType)(type);
|
|
1474
1813
|
}
|
|
1475
1814
|
return void 0;
|
|
1476
1815
|
}
|
|
@@ -1480,7 +1819,7 @@ function symbol(name, kind, node, detail) {
|
|
|
1480
1819
|
}
|
|
1481
1820
|
|
|
1482
1821
|
// src/features/semanticTokens.ts
|
|
1483
|
-
var
|
|
1822
|
+
var import_luaut_parser8 = require("luaut-parser");
|
|
1484
1823
|
var TOKEN_TYPES = [
|
|
1485
1824
|
"namespace",
|
|
1486
1825
|
"type",
|
|
@@ -1524,7 +1863,7 @@ function semanticTokens(analysis) {
|
|
|
1524
1863
|
};
|
|
1525
1864
|
let tokens = [];
|
|
1526
1865
|
try {
|
|
1527
|
-
tokens = (0,
|
|
1866
|
+
tokens = (0, import_luaut_parser8.tokenize)(analysis.source);
|
|
1528
1867
|
} catch {
|
|
1529
1868
|
}
|
|
1530
1869
|
const identifiers = tokens.filter((t) => t.type === "Identifier");
|
|
@@ -1568,7 +1907,7 @@ function classify(analysis, spanned, ancestors, identifiers, add) {
|
|
|
1568
1907
|
if (!baseToken) return;
|
|
1569
1908
|
if (!namespace && typeParameterInScope2(ancestors, base)) {
|
|
1570
1909
|
add(baseToken, base.length, "typeParameter");
|
|
1571
|
-
} else if ((0,
|
|
1910
|
+
} else if ((0, import_luaut_parser8.isClassType)(analysis.types.aliases.get(namespace ? `${namespace}.${base}` : base) ?? import_luaut_parser8.unknownType)) {
|
|
1572
1911
|
add(baseToken, base.length, "class");
|
|
1573
1912
|
} else {
|
|
1574
1913
|
add(baseToken, base.length, "type", PRIMITIVES3.has(base) ? ["defaultLibrary"] : []);
|
|
@@ -1801,7 +2140,7 @@ function createServer(connection, options = {}) {
|
|
|
1801
2140
|
severity: import_node.DiagnosticSeverity.Information,
|
|
1802
2141
|
source: "luaut",
|
|
1803
2142
|
code: "no-config",
|
|
1804
|
-
message: 'No luaut.config.json applies to this file, so no types are loaded \u2014 not even `print`. Add one to this folder or a folder above
|
|
2143
|
+
message: 'No luaut.config.json applies to this file, so no types are loaded \u2014 not even `print`. Add one to this folder or a folder above: { "types": [], "paths": {}, "sourceMap": null }, listing in `types` the type libraries the project has installed.'
|
|
1805
2144
|
}];
|
|
1806
2145
|
};
|
|
1807
2146
|
documents.onDidOpen(publishAll);
|