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/cli.cjs
CHANGED
|
@@ -13,6 +13,19 @@ var import_luaut_parser2 = require("luaut-parser");
|
|
|
13
13
|
|
|
14
14
|
// src/features/members.ts
|
|
15
15
|
var import_luaut_parser = require("luaut-parser");
|
|
16
|
+
function literalKeys(key, aliases) {
|
|
17
|
+
if (!key) return [];
|
|
18
|
+
const resolved = key.kind === "genericRef" ? aliases.get(key.name) : key;
|
|
19
|
+
if (!resolved) return [];
|
|
20
|
+
const parts = resolved.kind === "union" ? resolved.types : [resolved];
|
|
21
|
+
const out = [];
|
|
22
|
+
for (const part of parts) {
|
|
23
|
+
const member = part.kind === "genericRef" ? aliases.get(part.name) ?? part : part;
|
|
24
|
+
if (member.kind !== "literal" || typeof member.value !== "string") return [];
|
|
25
|
+
out.push(member.value);
|
|
26
|
+
}
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
16
29
|
function membersOf(type, aliases, seen = /* @__PURE__ */ new Set()) {
|
|
17
30
|
if (!type || seen.has(type)) return [];
|
|
18
31
|
seen.add(type);
|
|
@@ -22,6 +35,11 @@ function membersOf(type, aliases, seen = /* @__PURE__ */ new Set()) {
|
|
|
22
35
|
for (const [name, property] of type.properties) {
|
|
23
36
|
out.push({ name, property, isMethod: takesSelf(property.type) });
|
|
24
37
|
}
|
|
38
|
+
for (const name of literalKeys(type.indexer?.key, aliases)) {
|
|
39
|
+
if (type.properties.has(name)) continue;
|
|
40
|
+
const property = { type: type.indexer.value, optional: true };
|
|
41
|
+
out.push({ name, property, isMethod: takesSelf(property.type) });
|
|
42
|
+
}
|
|
25
43
|
return out;
|
|
26
44
|
}
|
|
27
45
|
case "intersection": {
|
|
@@ -43,6 +61,20 @@ function membersOf(type, aliases, seen = /* @__PURE__ */ new Set()) {
|
|
|
43
61
|
}
|
|
44
62
|
case "typeParam":
|
|
45
63
|
return membersOf(type.constraint, aliases, seen);
|
|
64
|
+
// An array and a string answer to the methods the language gives them
|
|
65
|
+
// — `names:filter(f)`, `text:trim()`. They are written in the parser's
|
|
66
|
+
// prelude as `ArrayMethods<T>` and `StringMethods`, so the element
|
|
67
|
+
// type goes in where `T` stands.
|
|
68
|
+
case "array":
|
|
69
|
+
case "tuple": {
|
|
70
|
+
const element = type.kind === "array" ? type.element : (0, import_luaut_parser.union)(type.elements);
|
|
71
|
+
const methods = aliases.get("ArrayMethods");
|
|
72
|
+
return methods ? membersOf((0, import_luaut_parser.substitute)(methods, /* @__PURE__ */ new Map([["T", element]])), aliases, seen) : [];
|
|
73
|
+
}
|
|
74
|
+
case "primitive":
|
|
75
|
+
return type.name === "string" ? membersOf(aliases.get("StringMethods"), aliases, seen) : [];
|
|
76
|
+
case "literal":
|
|
77
|
+
return type.base === "string" ? membersOf(aliases.get("StringMethods"), aliases, seen) : [];
|
|
46
78
|
default:
|
|
47
79
|
return [];
|
|
48
80
|
}
|
|
@@ -341,11 +373,13 @@ var Analyzer = class {
|
|
|
341
373
|
const script = path ? context.sourceMap?.scriptFor(path) : void 0;
|
|
342
374
|
const libs = script ? [...context.libs, script] : context.libs;
|
|
343
375
|
const globals = script ? [...context.globals, "script"] : context.globals;
|
|
344
|
-
const { program, errors } = (0, import_luaut_parser2.parseWithRecovery)(source);
|
|
345
|
-
const
|
|
376
|
+
const { program, errors, directives } = (0, import_luaut_parser2.parseWithRecovery)(source);
|
|
377
|
+
const reportUndeclared = context.libs.length > 0;
|
|
378
|
+
const scopes = (0, import_luaut_parser2.analyzeScopes)(program, { builtinGlobals: [...globals], reportUndeclared });
|
|
346
379
|
const dependencies = new Map(context.reads);
|
|
347
380
|
const types = (0, import_luaut_parser2.analyzeTypes)(program, scopes, {
|
|
348
381
|
libs,
|
|
382
|
+
reportUnknownTypes: reportUndeclared,
|
|
349
383
|
resolveModule: (specifier) => {
|
|
350
384
|
if (!path) return void 0;
|
|
351
385
|
const candidates = this.candidatesFor(path, specifier);
|
|
@@ -359,7 +393,7 @@ var Analyzer = class {
|
|
|
359
393
|
return exports2;
|
|
360
394
|
}
|
|
361
395
|
});
|
|
362
|
-
return { uri, version, source, program, parseErrors: errors, scopes, types, dependencies, project: context.project };
|
|
396
|
+
return { uri, version, source, program, parseErrors: errors, directives, scopes, types, dependencies, project: context.project };
|
|
363
397
|
}
|
|
364
398
|
exportsOf(path, importing) {
|
|
365
399
|
const key = pathKey(path);
|
|
@@ -454,7 +488,7 @@ function collect(container, out) {
|
|
|
454
488
|
}
|
|
455
489
|
}
|
|
456
490
|
function isSpanlessNode(v) {
|
|
457
|
-
return !!v && typeof v === "object" &&
|
|
491
|
+
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
458
492
|
}
|
|
459
493
|
function pathAt(root, pos, inclusive = false) {
|
|
460
494
|
let best;
|
|
@@ -714,6 +748,7 @@ function patternNamed(target, name) {
|
|
|
714
748
|
|
|
715
749
|
// src/features/diagnostics.ts
|
|
716
750
|
var import_vscode_languageserver2 = require("vscode-languageserver");
|
|
751
|
+
var import_luaut_parser4 = require("luaut-parser");
|
|
717
752
|
function diagnostics(analysis) {
|
|
718
753
|
const out = [];
|
|
719
754
|
for (const error of analysis.parseErrors) {
|
|
@@ -727,29 +762,39 @@ function diagnostics(analysis) {
|
|
|
727
762
|
message: error.message.replace(/\s*\(\d+:\d+\)$/, "")
|
|
728
763
|
});
|
|
729
764
|
}
|
|
730
|
-
|
|
731
|
-
|
|
765
|
+
const semantic = [
|
|
766
|
+
...analysis.scopes.diagnostics.map((d) => ({
|
|
732
767
|
range: toRange(d.node),
|
|
733
768
|
severity: import_vscode_languageserver2.DiagnosticSeverity.Error,
|
|
734
769
|
source: "luaut",
|
|
735
770
|
code: d.kind,
|
|
736
771
|
message: d.message
|
|
737
|
-
})
|
|
738
|
-
|
|
739
|
-
for (const d of analysis.types.diagnostics) {
|
|
740
|
-
out.push({
|
|
772
|
+
})),
|
|
773
|
+
...analysis.types.diagnostics.map((d) => ({
|
|
741
774
|
range: toRange(d.node),
|
|
742
775
|
severity: import_vscode_languageserver2.DiagnosticSeverity.Error,
|
|
743
776
|
source: "luaut",
|
|
744
777
|
code: "type",
|
|
745
778
|
message: d.message
|
|
779
|
+
}))
|
|
780
|
+
];
|
|
781
|
+
const { kept, unusedExpectErrors } = (0, import_luaut_parser4.applyDirectives)(analysis.directives, semantic, (d) => d.range.start.line + 1);
|
|
782
|
+
out.push(...kept);
|
|
783
|
+
for (const directive of unusedExpectErrors) {
|
|
784
|
+
const start = toPosition(directive.line, directive.column);
|
|
785
|
+
out.push({
|
|
786
|
+
range: { start, end: { line: start.line, character: start.character + "--@luaut-expect-error".length } },
|
|
787
|
+
severity: import_vscode_languageserver2.DiagnosticSeverity.Error,
|
|
788
|
+
source: "luaut",
|
|
789
|
+
code: "directive",
|
|
790
|
+
message: import_luaut_parser4.UNUSED_EXPECT_ERROR
|
|
746
791
|
});
|
|
747
792
|
}
|
|
748
793
|
return out;
|
|
749
794
|
}
|
|
750
795
|
|
|
751
796
|
// src/features/hover.ts
|
|
752
|
-
var
|
|
797
|
+
var import_luaut_parser5 = require("luaut-parser");
|
|
753
798
|
function hover(analysis, position) {
|
|
754
799
|
const path = pathAt(analysis.program, position, true);
|
|
755
800
|
for (let i = path.length - 1; i >= 0; i--) {
|
|
@@ -796,6 +841,27 @@ function describe(analysis, path, index) {
|
|
|
796
841
|
const type2 = property?.type ?? types.typeOf.get(field.value);
|
|
797
842
|
return type2 && `(property) ${name}: ${pretty(type2)}`;
|
|
798
843
|
}
|
|
844
|
+
// `const { name } = t`: a shorthand key *is* the binding it
|
|
845
|
+
// declares, and has the same span, so the cursor can land on
|
|
846
|
+
// either. A renamed key (`{ name: other }`) names the property
|
|
847
|
+
// the value is read from.
|
|
848
|
+
case "ObjectPatternProperty": {
|
|
849
|
+
if (parent.key !== node || parent.computed) break;
|
|
850
|
+
const value = parent.value;
|
|
851
|
+
if (parent.shorthand) return describe(analysis, [...path.slice(0, index), value], index);
|
|
852
|
+
const binding2 = value.type === "IdentifierPattern" ? bindingOfNode(analysis, value) : void 0;
|
|
853
|
+
const type2 = binding2 && types.bindingType.get(binding2.id);
|
|
854
|
+
return type2 && `(property) ${name}: ${pretty(type2)}`;
|
|
855
|
+
}
|
|
856
|
+
// One line of an overload set reads as its own signature.
|
|
857
|
+
// The line the body is on reads as the whole set, which is
|
|
858
|
+
// what the binding says and what the default path gives.
|
|
859
|
+
case "FunctionSignature": {
|
|
860
|
+
if (parent.name !== node) break;
|
|
861
|
+
const own = types.typeOfTypeNode.get(parent);
|
|
862
|
+
if (own) return `function ${name}${pretty(own)}`;
|
|
863
|
+
break;
|
|
864
|
+
}
|
|
799
865
|
case "ImportSpecifier": {
|
|
800
866
|
const alias = types.aliases.get(name);
|
|
801
867
|
const binding2 = bindingOfNode(analysis, identifier2);
|
|
@@ -841,7 +907,7 @@ function describe(analysis, path, index) {
|
|
|
841
907
|
case "MappedTypeNode":
|
|
842
908
|
if (parent.parameterId === node) {
|
|
843
909
|
const keys = typeOfNode(parent.constraint);
|
|
844
|
-
return `(type parameter) ${name}${keys ? ` in ${(0,
|
|
910
|
+
return `(type parameter) ${name}${keys ? ` in ${(0, import_luaut_parser5.formatType)(keys)}` : ""}`;
|
|
845
911
|
}
|
|
846
912
|
break;
|
|
847
913
|
}
|
|
@@ -858,6 +924,11 @@ function describe(analysis, path, index) {
|
|
|
858
924
|
}
|
|
859
925
|
return void 0;
|
|
860
926
|
}
|
|
927
|
+
// `...` — what this function's extra arguments are.
|
|
928
|
+
case "VarargExpression": {
|
|
929
|
+
const type2 = types.typeOf.get(node);
|
|
930
|
+
return type2 ? `(vararg) ...: ${pretty(type2)}` : void 0;
|
|
931
|
+
}
|
|
861
932
|
// Declarations: `const x`, a parameter.
|
|
862
933
|
case "IdentifierPattern":
|
|
863
934
|
case "FunctionParameter":
|
|
@@ -878,7 +949,7 @@ function describe(analysis, path, index) {
|
|
|
878
949
|
if (!node.typeArguments.length) {
|
|
879
950
|
const qualified = node.namespace ? `${node.namespace}.${base}` : base;
|
|
880
951
|
const alias = types.aliases.get(qualified);
|
|
881
|
-
if (alias && (0,
|
|
952
|
+
if (alias && (0, import_luaut_parser5.isClassType)(alias)) return classText(analysis, qualified);
|
|
882
953
|
if (alias) return `type ${qualified} = ${pretty(alias)}`;
|
|
883
954
|
}
|
|
884
955
|
const type2 = typeOfNode(node);
|
|
@@ -906,17 +977,17 @@ function declareText(analysis, statement) {
|
|
|
906
977
|
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);
|
|
907
978
|
const others = total - 1;
|
|
908
979
|
const overloads = others > 0 ? ` (+${others} overload${others > 1 ? "s" : ""})` : "";
|
|
909
|
-
return `declare function ${name}${(0,
|
|
980
|
+
return `declare function ${name}${(0, import_luaut_parser5.formatType)(own)}${overloads}`;
|
|
910
981
|
}
|
|
911
982
|
function classText(analysis, name) {
|
|
912
983
|
const type = analysis.types.aliases.get(name);
|
|
913
|
-
if (!type || !(0,
|
|
984
|
+
if (!type || !(0, import_luaut_parser5.isClassType)(type)) return void 0;
|
|
914
985
|
const superclass = type.class.superclass;
|
|
915
986
|
const inherited = superclass ? analysis.types.aliases.get(superclass) : void 0;
|
|
916
987
|
const own = [...type.properties].filter(([key, property]) => inherited?.kind !== "object" || inherited.properties.get(key) !== property);
|
|
917
988
|
const head = `declare class ${name}${superclass ? ` extends ${superclass}` : ""}`;
|
|
918
989
|
if (!own.length) return `${head} {}`;
|
|
919
|
-
const lines = own.map(([key, property]) => ` ${property.readonly ? "readonly " : ""}${key}${property.optional ? "?" : ""}: ${(0,
|
|
990
|
+
const lines = own.map(([key, property]) => ` ${property.readonly ? "readonly " : ""}${key}${property.optional ? "?" : ""}: ${(0, import_luaut_parser5.formatType)(property.type)},`);
|
|
920
991
|
return `${head} {
|
|
921
992
|
${lines.join("\n")}
|
|
922
993
|
}`;
|
|
@@ -928,7 +999,7 @@ function typeParameterSignature(analysis, parameter) {
|
|
|
928
999
|
const p = parameter;
|
|
929
1000
|
if (p.infer) return `infer ${p.name}`;
|
|
930
1001
|
const constraint = p.constraint ? analysis.types.typeOfTypeNode.get(p.constraint) : void 0;
|
|
931
|
-
return `${p.isConst ? "const " : ""}${p.name}${constraint ? ` extends ${(0,
|
|
1002
|
+
return `${p.isConst ? "const " : ""}${p.name}${constraint ? ` extends ${(0, import_luaut_parser5.formatType)(constraint)}` : ""}`;
|
|
932
1003
|
}
|
|
933
1004
|
function typeParameterInScope(path, index, name) {
|
|
934
1005
|
for (let i = index - 1; i >= 0; i--) {
|
|
@@ -953,7 +1024,7 @@ function referenceText(analysis, reference) {
|
|
|
953
1024
|
if (!args.length) return name;
|
|
954
1025
|
const resolved = args.map((a) => {
|
|
955
1026
|
const t = analysis.types.typeOfTypeNode.get(a);
|
|
956
|
-
return t ? (0,
|
|
1027
|
+
return t ? (0, import_luaut_parser5.formatType)(t) : "?";
|
|
957
1028
|
});
|
|
958
1029
|
return `${name}<${resolved.join(", ")}>`;
|
|
959
1030
|
}
|
|
@@ -962,27 +1033,27 @@ function fieldWithKey(table, key) {
|
|
|
962
1033
|
return fields.find((f) => f.type === "TableFieldNamed" && f.key === key);
|
|
963
1034
|
}
|
|
964
1035
|
function pretty(type) {
|
|
965
|
-
const flat = (0,
|
|
1036
|
+
const flat = (0, import_luaut_parser5.formatType)(type);
|
|
966
1037
|
if (flat.length <= 80) return flat;
|
|
967
1038
|
if (type.kind === "object") {
|
|
968
1039
|
const lines = [];
|
|
969
|
-
if (type.indexer) lines.push(` [${(0,
|
|
1040
|
+
if (type.indexer) lines.push(` [${(0, import_luaut_parser5.formatType)(type.indexer.key)}]: ${(0, import_luaut_parser5.formatType)(type.indexer.value)},`);
|
|
970
1041
|
for (const [name, property] of type.properties) {
|
|
971
1042
|
const readonly = property.readonly ? "readonly " : "";
|
|
972
|
-
lines.push(` ${readonly}${name}${property.optional ? "?" : ""}: ${(0,
|
|
1043
|
+
lines.push(` ${readonly}${name}${property.optional ? "?" : ""}: ${(0, import_luaut_parser5.formatType)(property.type)},`);
|
|
973
1044
|
}
|
|
974
1045
|
return `{
|
|
975
1046
|
${lines.join("\n")}
|
|
976
1047
|
}`;
|
|
977
1048
|
}
|
|
978
1049
|
if (type.kind === "intersection" && type.types.every((t) => t.kind === "function")) {
|
|
979
|
-
return type.types.map(
|
|
1050
|
+
return type.types.map(import_luaut_parser5.formatType).join("\n& ");
|
|
980
1051
|
}
|
|
981
1052
|
return flat;
|
|
982
1053
|
}
|
|
983
1054
|
function bindingText(binding, type) {
|
|
984
1055
|
if (binding.declaredBy === "function" && type.kind === "function") {
|
|
985
|
-
return `function ${binding.name}${(0,
|
|
1056
|
+
return `function ${binding.name}${(0, import_luaut_parser5.formatType)(type)}`;
|
|
986
1057
|
}
|
|
987
1058
|
return `${keyword(binding)} ${binding.name}: ${pretty(type)}`;
|
|
988
1059
|
}
|
|
@@ -1059,8 +1130,162 @@ function isIdentifier(name) {
|
|
|
1059
1130
|
}
|
|
1060
1131
|
|
|
1061
1132
|
// src/features/completion.ts
|
|
1133
|
+
var import_vscode_languageserver5 = require("vscode-languageserver");
|
|
1134
|
+
var import_luaut_parser6 = require("luaut-parser");
|
|
1135
|
+
|
|
1136
|
+
// src/features/autoImport.ts
|
|
1137
|
+
var import_node_fs3 = require("fs");
|
|
1138
|
+
var import_node_path3 = require("path");
|
|
1062
1139
|
var import_vscode_languageserver4 = require("vscode-languageserver");
|
|
1063
|
-
|
|
1140
|
+
function importItems(analyzer, analysis, typePosition, taken) {
|
|
1141
|
+
const from = pathOfUri(analysis.uri);
|
|
1142
|
+
if (!from) return [];
|
|
1143
|
+
const config = analysis.project.config;
|
|
1144
|
+
const items = [];
|
|
1145
|
+
const offered = /* @__PURE__ */ new Set();
|
|
1146
|
+
for (const file of projectFiles(config?.directory ?? (0, import_node_path3.dirname)(from))) {
|
|
1147
|
+
if (samePath(file, from)) continue;
|
|
1148
|
+
const exports2 = analyzer.exportsAt(file);
|
|
1149
|
+
if (!exports2 || exports2.partial) continue;
|
|
1150
|
+
const names = typePosition ? [...exports2.types.keys()] : [...exports2.values.keys()];
|
|
1151
|
+
const specifier = specifierFor(from, file, config);
|
|
1152
|
+
for (const name of names) {
|
|
1153
|
+
if (taken.has(name) || offered.has(name)) continue;
|
|
1154
|
+
offered.add(name);
|
|
1155
|
+
const type = typePosition ? exports2.types.get(name)?.type : exports2.values.get(name);
|
|
1156
|
+
items.push({
|
|
1157
|
+
label: name,
|
|
1158
|
+
kind: typePosition ? import_vscode_languageserver4.CompletionItemKind.Interface : type && signaturesOf(type).length ? import_vscode_languageserver4.CompletionItemKind.Function : import_vscode_languageserver4.CompletionItemKind.Variable,
|
|
1159
|
+
labelDetails: { description: specifier },
|
|
1160
|
+
detail: `import { ${name} } from "${specifier}"`,
|
|
1161
|
+
sortText: `4${name}`,
|
|
1162
|
+
additionalTextEdits: [importEdit(analyzer, analysis, file, name, specifier, typePosition)]
|
|
1163
|
+
});
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
return items;
|
|
1167
|
+
}
|
|
1168
|
+
function serviceItems(analysis, taken) {
|
|
1169
|
+
const services = analysis.types.aliases.get("Services");
|
|
1170
|
+
if (!services || services.kind !== "object") return [];
|
|
1171
|
+
const at = serviceInsertion(analysis.program.body.statements);
|
|
1172
|
+
const items = [];
|
|
1173
|
+
for (const name of services.properties.keys()) {
|
|
1174
|
+
if (taken.has(name)) continue;
|
|
1175
|
+
const line = `const ${name} = game:GetService("${name}")`;
|
|
1176
|
+
items.push({
|
|
1177
|
+
label: name,
|
|
1178
|
+
kind: import_vscode_languageserver4.CompletionItemKind.Module,
|
|
1179
|
+
labelDetails: { description: "service" },
|
|
1180
|
+
detail: line,
|
|
1181
|
+
sortText: `5${name}`,
|
|
1182
|
+
additionalTextEdits: [{ range: { start: at.position, end: at.position }, newText: `${line}
|
|
1183
|
+
${at.gap}` }]
|
|
1184
|
+
});
|
|
1185
|
+
}
|
|
1186
|
+
return items;
|
|
1187
|
+
}
|
|
1188
|
+
function importEdit(analyzer, analysis, file, name, specifier, typePosition) {
|
|
1189
|
+
const statements = analysis.program.body.statements;
|
|
1190
|
+
const imports = statements.filter((s) => s.type === "ImportStatement");
|
|
1191
|
+
const existing = imports.find((s) => !s.namespaceImport && (typePosition || !s.isTypeOnly) && samePathOrUndefined(analyzer.resolveModulePath(analysis.uri, s.source.value), file));
|
|
1192
|
+
if (existing) {
|
|
1193
|
+
const last = existing.specifiers[existing.specifiers.length - 1];
|
|
1194
|
+
if (last) {
|
|
1195
|
+
const at2 = endOf(last);
|
|
1196
|
+
return { range: { start: at2, end: at2 }, newText: `, ${name}` };
|
|
1197
|
+
}
|
|
1198
|
+
if (existing.defaultImport) {
|
|
1199
|
+
const at2 = endOf(existing.defaultImport);
|
|
1200
|
+
return { range: { start: at2, end: at2 }, newText: `, { ${name} }` };
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
const line = `import { ${name} } from "${specifier}"`;
|
|
1204
|
+
const lastImport = imports[imports.length - 1];
|
|
1205
|
+
if (lastImport) {
|
|
1206
|
+
const at2 = { line: lastImport.line.end, character: 0 };
|
|
1207
|
+
return { range: { start: at2, end: at2 }, newText: `${line}
|
|
1208
|
+
` };
|
|
1209
|
+
}
|
|
1210
|
+
const first = statements[0];
|
|
1211
|
+
const at = { line: first ? first.line.start - 1 : 0, character: 0 };
|
|
1212
|
+
return { range: { start: at, end: at }, newText: first ? `${line}
|
|
1213
|
+
|
|
1214
|
+
` : `${line}
|
|
1215
|
+
` };
|
|
1216
|
+
}
|
|
1217
|
+
function serviceInsertion(statements) {
|
|
1218
|
+
let last;
|
|
1219
|
+
for (const statement of statements) {
|
|
1220
|
+
if (statement.type !== "ImportStatement" && !isServiceDeclaration(statement)) break;
|
|
1221
|
+
last = statement;
|
|
1222
|
+
}
|
|
1223
|
+
if (last) return { position: { line: last.line.end, character: 0 }, gap: "" };
|
|
1224
|
+
const first = statements[0];
|
|
1225
|
+
return first ? { position: { line: first.line.start - 1, character: 0 }, gap: "\n" } : { position: { line: 0, character: 0 }, gap: "" };
|
|
1226
|
+
}
|
|
1227
|
+
function isServiceDeclaration(statement) {
|
|
1228
|
+
if (statement.type !== "VariableDeclaration") return false;
|
|
1229
|
+
const init = statement.init[0];
|
|
1230
|
+
return init?.type === "MethodCallExpression" && init.method.name === "GetService" && init.object.type === "Identifier" && init.object.name === "game";
|
|
1231
|
+
}
|
|
1232
|
+
function endOf(node) {
|
|
1233
|
+
return { line: node.line.end - 1, character: node.column.end - 1 };
|
|
1234
|
+
}
|
|
1235
|
+
function samePathOrUndefined(a, b) {
|
|
1236
|
+
return a !== void 0 && samePath(a, b);
|
|
1237
|
+
}
|
|
1238
|
+
function specifierFor(from, target, config) {
|
|
1239
|
+
const withoutExtension = (path) => {
|
|
1240
|
+
const bare = path.replace(/\\/g, "/").replace(/\.luaut$/, "");
|
|
1241
|
+
return bare.endsWith("/index") ? bare.slice(0, -"/index".length) : bare;
|
|
1242
|
+
};
|
|
1243
|
+
let relativePath = withoutExtension((0, import_node_path3.relative)((0, import_node_path3.dirname)(from), target));
|
|
1244
|
+
if (!relativePath.startsWith(".")) relativePath = `./${relativePath}`;
|
|
1245
|
+
if (!relativePath.startsWith("../") || !config) return relativePath;
|
|
1246
|
+
for (const [pattern, targets] of Object.entries(config.paths)) {
|
|
1247
|
+
const star = pattern.indexOf("*");
|
|
1248
|
+
if (star < 0) continue;
|
|
1249
|
+
for (const targetPattern of targets) {
|
|
1250
|
+
const cut = targetPattern.indexOf("*");
|
|
1251
|
+
if (cut < 0) continue;
|
|
1252
|
+
const head = (0, import_node_path3.resolve)(config.baseUrl, targetPattern.slice(0, cut));
|
|
1253
|
+
const rest = (0, import_node_path3.relative)(head, target);
|
|
1254
|
+
if (rest.startsWith("..") || (0, import_node_path3.resolve)(head, rest) !== (0, import_node_path3.resolve)(target)) continue;
|
|
1255
|
+
return `${pattern.slice(0, star)}${withoutExtension(rest)}${pattern.slice(star + 1)}`;
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
return relativePath;
|
|
1259
|
+
}
|
|
1260
|
+
var FILE_LIMIT = 2e3;
|
|
1261
|
+
var LISTING_TTL = 3e3;
|
|
1262
|
+
var listings = /* @__PURE__ */ new Map();
|
|
1263
|
+
function projectFiles(root) {
|
|
1264
|
+
const cached = listings.get(root);
|
|
1265
|
+
if (cached && Date.now() - cached.at < LISTING_TTL) return cached.files;
|
|
1266
|
+
const files = [];
|
|
1267
|
+
const walk2 = (directory, depth) => {
|
|
1268
|
+
if (files.length >= FILE_LIMIT || depth > 12) return;
|
|
1269
|
+
let entries;
|
|
1270
|
+
try {
|
|
1271
|
+
entries = (0, import_node_fs3.readdirSync)(directory, { withFileTypes: true });
|
|
1272
|
+
} catch {
|
|
1273
|
+
return;
|
|
1274
|
+
}
|
|
1275
|
+
for (const entry of entries) {
|
|
1276
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
1277
|
+
const path = (0, import_node_path3.join)(directory, entry.name);
|
|
1278
|
+
if (entry.isDirectory()) walk2(path, depth + 1);
|
|
1279
|
+
else if (entry.name.endsWith(".luaut") && !entry.name.endsWith(".d.luaut")) files.push(path);
|
|
1280
|
+
if (files.length >= FILE_LIMIT) return;
|
|
1281
|
+
}
|
|
1282
|
+
};
|
|
1283
|
+
walk2(root, 0);
|
|
1284
|
+
listings.set(root, { at: Date.now(), files });
|
|
1285
|
+
return files;
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
// src/features/completion.ts
|
|
1064
1289
|
var PLACEHOLDER = "__luautCompletion__";
|
|
1065
1290
|
var IDENTIFIER_CHAR = /[A-Za-z0-9_]/;
|
|
1066
1291
|
function completion(analyzer, document, position) {
|
|
@@ -1093,28 +1318,58 @@ function completion(analyzer, document, position) {
|
|
|
1093
1318
|
first ??= { analysis, path };
|
|
1094
1319
|
}
|
|
1095
1320
|
if (operator || !first) return [];
|
|
1321
|
+
const keys = objectKeyItems(first.analysis, first.path, source.slice(end));
|
|
1322
|
+
if (keys) return keys;
|
|
1096
1323
|
if (inTypePosition(first.path)) {
|
|
1097
1324
|
const named = [...first.analysis.types.aliases].map(([name, type]) => ({
|
|
1098
1325
|
label: name,
|
|
1099
|
-
kind: (0,
|
|
1100
|
-
detail: (0,
|
|
1326
|
+
kind: (0, import_luaut_parser6.isClassType)(type) ? import_vscode_languageserver5.CompletionItemKind.Class : import_vscode_languageserver5.CompletionItemKind.Interface,
|
|
1327
|
+
detail: (0, import_luaut_parser6.isClassType)(type) ? "class" : "type"
|
|
1101
1328
|
}));
|
|
1102
1329
|
const primitives = PRIMITIVES2.map((name) => ({
|
|
1103
1330
|
label: name,
|
|
1104
|
-
kind:
|
|
1331
|
+
kind: import_vscode_languageserver5.CompletionItemKind.Keyword,
|
|
1105
1332
|
detail: "type"
|
|
1106
1333
|
}));
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1334
|
+
const keywords = TYPE_KEYWORDS.map((name) => ({
|
|
1335
|
+
label: name,
|
|
1336
|
+
kind: import_vscode_languageserver5.CompletionItemKind.Keyword,
|
|
1337
|
+
sortText: `3${name}`
|
|
1338
|
+
}));
|
|
1339
|
+
const typeNames = new Set(first.analysis.types.aliases.keys());
|
|
1340
|
+
const imported = importItems(analyzer, analyzer.get(document), true, typeNames);
|
|
1341
|
+
return [...named, ...primitives, ...keywords, ...imported];
|
|
1342
|
+
}
|
|
1343
|
+
const taken = /* @__PURE__ */ new Set();
|
|
1344
|
+
for (const binding of first.analysis.scopes.bindings.values()) taken.add(binding.name);
|
|
1345
|
+
const current = analyzer.get(document);
|
|
1346
|
+
return [
|
|
1347
|
+
...valueItems(first.analysis, at),
|
|
1348
|
+
...contextKeywords(source.slice(0, start)),
|
|
1349
|
+
...importItems(analyzer, current, false, taken),
|
|
1350
|
+
...serviceItems(current, taken)
|
|
1351
|
+
];
|
|
1110
1352
|
}
|
|
1111
1353
|
function stringCompletion(analyzer, document, position) {
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1354
|
+
let analysis = analyzer.get(document);
|
|
1355
|
+
let literal = stringAt(analysis, position);
|
|
1356
|
+
if (!literal) {
|
|
1357
|
+
const repaired = repairedStrings(document, position);
|
|
1358
|
+
for (const text of repaired) {
|
|
1359
|
+
const candidate = analyzer.analyze(document.uri, -1, text);
|
|
1360
|
+
literal = stringAt(candidate, position);
|
|
1361
|
+
if (literal) {
|
|
1362
|
+
analysis = candidate;
|
|
1363
|
+
break;
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
if (!literal) return repaired.length ? [] : void 0;
|
|
1367
|
+
}
|
|
1116
1368
|
const expected = analysis.types.expectedTypeOf.get(literal);
|
|
1117
|
-
const values =
|
|
1369
|
+
const values = [.../* @__PURE__ */ new Set([
|
|
1370
|
+
...stringLiterals(expected, analysis.types.aliases),
|
|
1371
|
+
...indexKeys(analysis, position, literal)
|
|
1372
|
+
])];
|
|
1118
1373
|
if (!values.length) return [];
|
|
1119
1374
|
const line = literal.line.start - 1;
|
|
1120
1375
|
const range = literal.line.start === literal.line.end ? {
|
|
@@ -1123,10 +1378,81 @@ function stringCompletion(analyzer, document, position) {
|
|
|
1123
1378
|
} : void 0;
|
|
1124
1379
|
return values.map((value) => ({
|
|
1125
1380
|
label: value,
|
|
1126
|
-
kind:
|
|
1381
|
+
kind: import_vscode_languageserver5.CompletionItemKind.Constant,
|
|
1127
1382
|
...range ? { textEdit: { range, newText: value } } : {}
|
|
1128
1383
|
}));
|
|
1129
1384
|
}
|
|
1385
|
+
function stringAt(analysis, position) {
|
|
1386
|
+
const path = pathAt(analysis.program, position, false);
|
|
1387
|
+
return [...path].reverse().find((n) => n.type === "StringLiteral" || n.type === "TypeLiteralString");
|
|
1388
|
+
}
|
|
1389
|
+
function objectKeyItems(analysis, path, after) {
|
|
1390
|
+
const index = path.findLastIndex(
|
|
1391
|
+
(n) => n.type === "Identifier" && n.name === PLACEHOLDER
|
|
1392
|
+
);
|
|
1393
|
+
const literal = index > 0 ? path[index - 1] : void 0;
|
|
1394
|
+
if (literal?.type !== "TableExpression") return void 0;
|
|
1395
|
+
const fields = literal.fields;
|
|
1396
|
+
const atKey = fields.some((f) => f.type === "TableFieldShorthand" && f.name?.name === PLACEHOLDER);
|
|
1397
|
+
if (!atKey) return void 0;
|
|
1398
|
+
let expected = analysis.types.expectedTypeOf.get(literal);
|
|
1399
|
+
for (let up = index - 2; expected === void 0 && up >= 0; up--) {
|
|
1400
|
+
const outer = path[up];
|
|
1401
|
+
if (outer.type !== "AsConstExpression" && outer.type !== "ParenthesizedExpression") break;
|
|
1402
|
+
expected = analysis.types.expectedTypeOf.get(outer);
|
|
1403
|
+
}
|
|
1404
|
+
const members = membersOf(expected, analysis.types.aliases);
|
|
1405
|
+
if (!members.length) return void 0;
|
|
1406
|
+
const written = /* @__PURE__ */ new Set();
|
|
1407
|
+
for (const field of fields) {
|
|
1408
|
+
if (field.type === "TableFieldNamed") written.add(field.key?.name ?? field.key?.value ?? "");
|
|
1409
|
+
else if (field.type === "TableFieldShorthand" && field.name?.name !== PLACEHOLDER) written.add(field.name.name);
|
|
1410
|
+
}
|
|
1411
|
+
const colon = /^\s*:/.test(after);
|
|
1412
|
+
return members.filter((member) => !written.has(member.name)).map((member) => ({
|
|
1413
|
+
label: member.name,
|
|
1414
|
+
kind: import_vscode_languageserver5.CompletionItemKind.Field,
|
|
1415
|
+
detail: `${member.property.optional ? "?" : ""}: ${(0, import_luaut_parser6.formatType)(member.property.type)}`,
|
|
1416
|
+
insertText: colon ? member.name : `${member.name}: `
|
|
1417
|
+
}));
|
|
1418
|
+
}
|
|
1419
|
+
function indexKeys(analysis, position, literal) {
|
|
1420
|
+
const path = pathAt(analysis.program, position, false);
|
|
1421
|
+
const at = path.indexOf(literal);
|
|
1422
|
+
const parent = at > 0 ? path[at - 1] : void 0;
|
|
1423
|
+
if (!parent) return [];
|
|
1424
|
+
let indexed;
|
|
1425
|
+
if (parent.type === "IndexedAccessTypeNode" && parent.indexType === literal) {
|
|
1426
|
+
indexed = analysis.types.typeOfTypeNode.get(parent.objectType);
|
|
1427
|
+
} else if (parent.type === "IndexExpression" && parent.index === literal) {
|
|
1428
|
+
indexed = withoutNil(analysis.types.typeOf.get(parent.object));
|
|
1429
|
+
}
|
|
1430
|
+
return membersOf(indexed, analysis.types.aliases).map((member) => member.name);
|
|
1431
|
+
}
|
|
1432
|
+
function repairedStrings(document, position) {
|
|
1433
|
+
const source = document.getText();
|
|
1434
|
+
const offset = document.offsetAt(position);
|
|
1435
|
+
const lineStart = offset - position.character;
|
|
1436
|
+
const lineEndIndex = source.indexOf("\n", offset);
|
|
1437
|
+
const lineEnd = lineEndIndex < 0 ? source.length : lineEndIndex;
|
|
1438
|
+
const before = source.slice(lineStart, offset);
|
|
1439
|
+
let quote;
|
|
1440
|
+
for (let i = 0; i < before.length; i++) {
|
|
1441
|
+
const ch = before[i];
|
|
1442
|
+
if (quote) {
|
|
1443
|
+
if (ch === "\\") i++;
|
|
1444
|
+
else if (ch === quote) quote = void 0;
|
|
1445
|
+
} else if (ch === '"' || ch === "'") {
|
|
1446
|
+
quote = ch;
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
if (!quote) return [];
|
|
1450
|
+
let rest = source.slice(offset, lineEnd).replace(/\r$/, "");
|
|
1451
|
+
if (!rest.includes(quote)) rest += quote;
|
|
1452
|
+
const line = before + rest;
|
|
1453
|
+
const endings = ["", " then end", " do end", ")", ") then end", "]"];
|
|
1454
|
+
return endings.map((ending) => source.slice(0, lineStart) + line + ending + source.slice(lineEnd));
|
|
1455
|
+
}
|
|
1130
1456
|
function stringLiterals(type, aliases, seen = /* @__PURE__ */ new Set()) {
|
|
1131
1457
|
if (!type || seen.has(type)) return [];
|
|
1132
1458
|
seen.add(type);
|
|
@@ -1158,27 +1484,29 @@ function memberOperator(source, wordStart) {
|
|
|
1158
1484
|
}
|
|
1159
1485
|
function memberItems(analysis, access) {
|
|
1160
1486
|
const object = access.object;
|
|
1161
|
-
const type = analysis.types.typeOf.get(object);
|
|
1487
|
+
const type = withoutNil(analysis.types.typeOf.get(object));
|
|
1162
1488
|
const colon = access.type === "MethodCallExpression";
|
|
1163
|
-
if (
|
|
1164
|
-
if (!colon) return [];
|
|
1165
|
-
const id = analysis.scopes.globalsByName.get("string");
|
|
1166
|
-
const library = id === void 0 ? void 0 : analysis.types.bindingType.get(id);
|
|
1167
|
-
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));
|
|
1168
|
-
}
|
|
1489
|
+
if (isMethodOnly(type) && !colon) return [];
|
|
1169
1490
|
return membersOf(type, analysis.types.aliases).filter((member) => colon ? member.isMethod : true).map((member) => memberItem(member.name, member.property.type, member.property.readonly));
|
|
1170
1491
|
}
|
|
1171
|
-
function
|
|
1492
|
+
function withoutNil(type) {
|
|
1493
|
+
if (type?.kind !== "union") return type;
|
|
1494
|
+
const kept = type.types.filter((t) => !(t.kind === "primitive" && t.name === "nil"));
|
|
1495
|
+
return kept.length === 1 ? kept[0] : { ...type, types: kept };
|
|
1496
|
+
}
|
|
1497
|
+
function isMethodOnly(type) {
|
|
1172
1498
|
if (!type) return false;
|
|
1173
1499
|
switch (type.kind) {
|
|
1500
|
+
case "array":
|
|
1501
|
+
case "tuple":
|
|
1502
|
+
case "templateLiteral":
|
|
1503
|
+
return true;
|
|
1174
1504
|
case "primitive":
|
|
1175
1505
|
return type.name === "string";
|
|
1176
1506
|
case "literal":
|
|
1177
1507
|
return typeof type.value === "string";
|
|
1178
|
-
case "templateLiteral":
|
|
1179
|
-
return true;
|
|
1180
1508
|
case "union":
|
|
1181
|
-
return type.types.length > 0 && type.types.every(
|
|
1509
|
+
return type.types.length > 0 && type.types.every(isMethodOnly);
|
|
1182
1510
|
default:
|
|
1183
1511
|
return false;
|
|
1184
1512
|
}
|
|
@@ -1196,13 +1524,13 @@ function valueItems(analysis, at) {
|
|
|
1196
1524
|
items.push({
|
|
1197
1525
|
label: binding.name,
|
|
1198
1526
|
kind: kindOf(type, binding.kind),
|
|
1199
|
-
detail: type ? (0,
|
|
1527
|
+
detail: type ? (0, import_luaut_parser6.formatType)(type) : void 0,
|
|
1200
1528
|
// Locals before globals, and globals before library names.
|
|
1201
1529
|
sortText: `${binding.isBuiltin ? 2 : binding.kind === "global" ? 1 : 0}${binding.name}`
|
|
1202
1530
|
});
|
|
1203
1531
|
}
|
|
1204
1532
|
for (const keyword2 of KEYWORDS) {
|
|
1205
|
-
items.push({ label: keyword2, kind:
|
|
1533
|
+
items.push({ label: keyword2, kind: import_vscode_languageserver5.CompletionItemKind.Keyword, sortText: `3${keyword2}` });
|
|
1206
1534
|
}
|
|
1207
1535
|
return items;
|
|
1208
1536
|
}
|
|
@@ -1211,22 +1539,22 @@ function memberItem(name, type, readonly) {
|
|
|
1211
1539
|
if (signatures.length) {
|
|
1212
1540
|
return {
|
|
1213
1541
|
label: name,
|
|
1214
|
-
kind:
|
|
1542
|
+
kind: import_vscode_languageserver5.CompletionItemKind.Method,
|
|
1215
1543
|
detail: signatureLabel(signatures[0]).label,
|
|
1216
1544
|
insertText: `${name}($0)`,
|
|
1217
|
-
insertTextFormat:
|
|
1545
|
+
insertTextFormat: import_vscode_languageserver5.InsertTextFormat.Snippet
|
|
1218
1546
|
};
|
|
1219
1547
|
}
|
|
1220
1548
|
return {
|
|
1221
1549
|
label: name,
|
|
1222
|
-
kind:
|
|
1223
|
-
detail: `${readonly ? "readonly " : ""}${(0,
|
|
1550
|
+
kind: import_vscode_languageserver5.CompletionItemKind.Field,
|
|
1551
|
+
detail: `${readonly ? "readonly " : ""}${(0, import_luaut_parser6.formatType)(type)}`
|
|
1224
1552
|
};
|
|
1225
1553
|
}
|
|
1226
1554
|
function kindOf(type, bindingKind) {
|
|
1227
|
-
if (type && signaturesOf(type).length) return
|
|
1228
|
-
if (bindingKind === "param" || bindingKind === "self") return
|
|
1229
|
-
return
|
|
1555
|
+
if (type && signaturesOf(type).length) return import_vscode_languageserver5.CompletionItemKind.Function;
|
|
1556
|
+
if (bindingKind === "param" || bindingKind === "self") return import_vscode_languageserver5.CompletionItemKind.Variable;
|
|
1557
|
+
return import_vscode_languageserver5.CompletionItemKind.Variable;
|
|
1230
1558
|
}
|
|
1231
1559
|
function inTypePosition(path) {
|
|
1232
1560
|
return path.some(
|
|
@@ -1244,6 +1572,17 @@ var PRIMITIVES2 = [
|
|
|
1244
1572
|
"thread",
|
|
1245
1573
|
"buffer"
|
|
1246
1574
|
];
|
|
1575
|
+
var TYPE_KEYWORDS = ["keyof", "typeof", "infer", "extends"];
|
|
1576
|
+
function contextKeywords(before) {
|
|
1577
|
+
const keyword2 = (name) => ({
|
|
1578
|
+
label: name,
|
|
1579
|
+
kind: import_vscode_languageserver5.CompletionItemKind.Keyword,
|
|
1580
|
+
sortText: `3${name}`
|
|
1581
|
+
});
|
|
1582
|
+
if (/<\s*[A-Za-z_]\w*(?:\s*,\s*[A-Za-z_]\w*)*\s+$/.test(before)) return [keyword2("extends")];
|
|
1583
|
+
if (/[)\]}"'`\w][^\S\n]+$/.test(before)) return [keyword2("as"), keyword2("satisfies")];
|
|
1584
|
+
return [];
|
|
1585
|
+
}
|
|
1247
1586
|
var KEYWORDS = [
|
|
1248
1587
|
"const",
|
|
1249
1588
|
"let",
|
|
@@ -1349,8 +1688,8 @@ function activeArgument(call, position) {
|
|
|
1349
1688
|
}
|
|
1350
1689
|
|
|
1351
1690
|
// src/features/symbols.ts
|
|
1352
|
-
var
|
|
1353
|
-
var
|
|
1691
|
+
var import_vscode_languageserver6 = require("vscode-languageserver");
|
|
1692
|
+
var import_luaut_parser7 = require("luaut-parser");
|
|
1354
1693
|
function documentSymbols(analysis) {
|
|
1355
1694
|
const out = [];
|
|
1356
1695
|
walk(analysis.program, (node) => {
|
|
@@ -1358,7 +1697,7 @@ function documentSymbols(analysis) {
|
|
|
1358
1697
|
case "FunctionDeclaration":
|
|
1359
1698
|
case "FunctionDeclarationStatement": {
|
|
1360
1699
|
const name = functionName(node);
|
|
1361
|
-
if (name) out.push(symbol(name,
|
|
1700
|
+
if (name) out.push(symbol(name, import_vscode_languageserver6.SymbolKind.Function, node, detailOf(analysis, node)));
|
|
1362
1701
|
break;
|
|
1363
1702
|
}
|
|
1364
1703
|
case "TypeAliasStatement":
|
|
@@ -1367,20 +1706,20 @@ function documentSymbols(analysis) {
|
|
|
1367
1706
|
const name = typeof named === "string" ? named : named?.name;
|
|
1368
1707
|
if (name) {
|
|
1369
1708
|
const alias = analysis.types.aliases.get(name);
|
|
1370
|
-
out.push(symbol(name,
|
|
1709
|
+
out.push(symbol(name, import_vscode_languageserver6.SymbolKind.Interface, node, alias ? (0, import_luaut_parser7.formatType)(alias) : void 0));
|
|
1371
1710
|
}
|
|
1372
1711
|
break;
|
|
1373
1712
|
}
|
|
1374
1713
|
case "DeclareClassStatement": {
|
|
1375
1714
|
const name = node.name.name;
|
|
1376
1715
|
const superclass = node.superclass?.base;
|
|
1377
|
-
out.push(symbol(name,
|
|
1716
|
+
out.push(symbol(name, import_vscode_languageserver6.SymbolKind.Class, node, superclass && `extends ${superclass}`));
|
|
1378
1717
|
break;
|
|
1379
1718
|
}
|
|
1380
1719
|
case "VariableDeclaration": {
|
|
1381
1720
|
for (const target of node.names ?? []) {
|
|
1382
1721
|
const name = target.name;
|
|
1383
|
-
if (name) out.push(symbol(name,
|
|
1722
|
+
if (name) out.push(symbol(name, import_vscode_languageserver6.SymbolKind.Variable, target));
|
|
1384
1723
|
}
|
|
1385
1724
|
break;
|
|
1386
1725
|
}
|
|
@@ -1404,7 +1743,7 @@ function detailOf(analysis, node) {
|
|
|
1404
1743
|
if (name && typeof name === "object") {
|
|
1405
1744
|
const binding = bindingOfNode(analysis, name);
|
|
1406
1745
|
const type = binding && analysis.types.bindingType.get(binding.id);
|
|
1407
|
-
if (type) return (0,
|
|
1746
|
+
if (type) return (0, import_luaut_parser7.formatType)(type);
|
|
1408
1747
|
}
|
|
1409
1748
|
return void 0;
|
|
1410
1749
|
}
|
|
@@ -1414,7 +1753,7 @@ function symbol(name, kind, node, detail) {
|
|
|
1414
1753
|
}
|
|
1415
1754
|
|
|
1416
1755
|
// src/features/semanticTokens.ts
|
|
1417
|
-
var
|
|
1756
|
+
var import_luaut_parser8 = require("luaut-parser");
|
|
1418
1757
|
var TOKEN_TYPES = [
|
|
1419
1758
|
"namespace",
|
|
1420
1759
|
"type",
|
|
@@ -1458,7 +1797,7 @@ function semanticTokens(analysis) {
|
|
|
1458
1797
|
};
|
|
1459
1798
|
let tokens = [];
|
|
1460
1799
|
try {
|
|
1461
|
-
tokens = (0,
|
|
1800
|
+
tokens = (0, import_luaut_parser8.tokenize)(analysis.source);
|
|
1462
1801
|
} catch {
|
|
1463
1802
|
}
|
|
1464
1803
|
const identifiers = tokens.filter((t) => t.type === "Identifier");
|
|
@@ -1502,7 +1841,7 @@ function classify(analysis, spanned, ancestors, identifiers, add) {
|
|
|
1502
1841
|
if (!baseToken) return;
|
|
1503
1842
|
if (!namespace && typeParameterInScope2(ancestors, base)) {
|
|
1504
1843
|
add(baseToken, base.length, "typeParameter");
|
|
1505
|
-
} else if ((0,
|
|
1844
|
+
} else if ((0, import_luaut_parser8.isClassType)(analysis.types.aliases.get(namespace ? `${namespace}.${base}` : base) ?? import_luaut_parser8.unknownType)) {
|
|
1506
1845
|
add(baseToken, base.length, "class");
|
|
1507
1846
|
} else {
|
|
1508
1847
|
add(baseToken, base.length, "type", PRIMITIVES3.has(base) ? ["defaultLibrary"] : []);
|
|
@@ -1735,7 +2074,7 @@ function createServer(connection, options = {}) {
|
|
|
1735
2074
|
severity: import_node.DiagnosticSeverity.Information,
|
|
1736
2075
|
source: "luaut",
|
|
1737
2076
|
code: "no-config",
|
|
1738
|
-
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
|
|
2077
|
+
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.'
|
|
1739
2078
|
}];
|
|
1740
2079
|
};
|
|
1741
2080
|
documents.onDidOpen(publishAll);
|