@mirascript/monaco 0.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/dist/basic/index.d.ts +6 -0
- package/dist/basic/index.d.ts.map +1 -0
- package/dist/basic/index.js +10 -0
- package/dist/basic/index.js.map +6 -0
- package/dist/basic/language-configuration.d.ts +5 -0
- package/dist/basic/language-configuration.d.ts.map +1 -0
- package/dist/basic/tokens-provider.d.ts +4 -0
- package/dist/basic/tokens-provider.d.ts.map +1 -0
- package/dist/chunk-CEFEXBF7.js +65 -0
- package/dist/chunk-CEFEXBF7.js.map +6 -0
- package/dist/chunk-PTNWRTNM.js +474 -0
- package/dist/chunk-PTNWRTNM.js.map +6 -0
- package/dist/constants.d.ts +20 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/contribute.d.ts +3 -0
- package/dist/contribute.d.ts.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +83 -0
- package/dist/index.js.map +6 -0
- package/dist/lsp/compile-result.d.ts +150 -0
- package/dist/lsp/compile-result.d.ts.map +1 -0
- package/dist/lsp/diagnostics.d.ts +5 -0
- package/dist/lsp/diagnostics.d.ts.map +1 -0
- package/dist/lsp/index.d.ts +21 -0
- package/dist/lsp/index.d.ts.map +1 -0
- package/dist/lsp/index.js +2285 -0
- package/dist/lsp/index.js.map +6 -0
- package/dist/lsp/providers/base.d.ts +21 -0
- package/dist/lsp/providers/base.d.ts.map +1 -0
- package/dist/lsp/providers/code-action-provider.d.ts +12 -0
- package/dist/lsp/providers/code-action-provider.d.ts.map +1 -0
- package/dist/lsp/providers/color-provider.d.ts +10 -0
- package/dist/lsp/providers/color-provider.d.ts.map +1 -0
- package/dist/lsp/providers/completion-item-provider.d.ts +21 -0
- package/dist/lsp/providers/completion-item-provider.d.ts.map +1 -0
- package/dist/lsp/providers/definition-reference-provider.d.ts +16 -0
- package/dist/lsp/providers/definition-reference-provider.d.ts.map +1 -0
- package/dist/lsp/providers/document-highlight-provider.d.ts +12 -0
- package/dist/lsp/providers/document-highlight-provider.d.ts.map +1 -0
- package/dist/lsp/providers/document-symbol-provider.d.ts +10 -0
- package/dist/lsp/providers/document-symbol-provider.d.ts.map +1 -0
- package/dist/lsp/providers/formatter-provider.d.ts +18 -0
- package/dist/lsp/providers/formatter-provider.d.ts.map +1 -0
- package/dist/lsp/providers/hover-provider.d.ts +12 -0
- package/dist/lsp/providers/hover-provider.d.ts.map +1 -0
- package/dist/lsp/providers/inlay-hints-provider.d.ts +10 -0
- package/dist/lsp/providers/inlay-hints-provider.d.ts.map +1 -0
- package/dist/lsp/providers/range-provider.d.ts +10 -0
- package/dist/lsp/providers/range-provider.d.ts.map +1 -0
- package/dist/lsp/providers/rename-provider.d.ts +12 -0
- package/dist/lsp/providers/rename-provider.d.ts.map +1 -0
- package/dist/lsp/providers/semantic-tokens-provider.d.ts +12 -0
- package/dist/lsp/providers/semantic-tokens-provider.d.ts.map +1 -0
- package/dist/lsp/providers/signature-help-provider.d.ts +12 -0
- package/dist/lsp/providers/signature-help-provider.d.ts.map +1 -0
- package/dist/lsp/utils.d.ts +37 -0
- package/dist/lsp/utils.d.ts.map +1 -0
- package/dist/lsp/worker-helper.d.ts +5 -0
- package/dist/lsp/worker-helper.d.ts.map +1 -0
- package/dist/lsp/worker.d.ts +22 -0
- package/dist/lsp/worker.d.ts.map +1 -0
- package/dist/lsp/worker.js +62 -0
- package/dist/lsp/worker.js.map +6 -0
- package/dist/monaco-api.d.ts +16 -0
- package/dist/monaco-api.d.ts.map +1 -0
- package/package.json +39 -0
- package/src/basic/index.ts +11 -0
- package/src/basic/language-configuration.ts +90 -0
- package/src/basic/tokens-provider.ts +358 -0
- package/src/constants.ts +56 -0
- package/src/contribute.ts +18 -0
- package/src/index.ts +71 -0
- package/src/lsp/compile-result.ts +518 -0
- package/src/lsp/diagnostics.ts +83 -0
- package/src/lsp/index.ts +84 -0
- package/src/lsp/providers/base.ts +40 -0
- package/src/lsp/providers/code-action-provider.ts +28 -0
- package/src/lsp/providers/color-provider.ts +129 -0
- package/src/lsp/providers/completion-item-provider.ts +497 -0
- package/src/lsp/providers/definition-reference-provider.ts +107 -0
- package/src/lsp/providers/document-highlight-provider.ts +71 -0
- package/src/lsp/providers/document-symbol-provider.ts +65 -0
- package/src/lsp/providers/formatter-provider.ts +81 -0
- package/src/lsp/providers/hover-provider.ts +150 -0
- package/src/lsp/providers/inlay-hints-provider.ts +121 -0
- package/src/lsp/providers/range-provider.ts +37 -0
- package/src/lsp/providers/rename-provider.ts +144 -0
- package/src/lsp/providers/semantic-tokens-provider.ts +166 -0
- package/src/lsp/providers/signature-help-provider.ts +119 -0
- package/src/lsp/utils.ts +322 -0
- package/src/lsp/worker-helper.ts +119 -0
- package/src/lsp/worker.ts +83 -0
- package/src/monaco-api.js +66 -0
- package/src/monaco-api.ts +18 -0
|
@@ -0,0 +1,2285 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DOC_HEADER,
|
|
3
|
+
REG_IDENTIFIER,
|
|
4
|
+
REG_ORDINAL,
|
|
5
|
+
keywords,
|
|
6
|
+
reservedKeywords
|
|
7
|
+
} from "../chunk-PTNWRTNM.js";
|
|
8
|
+
import {
|
|
9
|
+
Emitter,
|
|
10
|
+
MarkerSeverity,
|
|
11
|
+
MarkerTag,
|
|
12
|
+
Position,
|
|
13
|
+
Range,
|
|
14
|
+
Uri,
|
|
15
|
+
editor,
|
|
16
|
+
languages
|
|
17
|
+
} from "../chunk-CEFEXBF7.js";
|
|
18
|
+
|
|
19
|
+
// src/lsp/providers/base.ts
|
|
20
|
+
import { DefaultVmContext } from "@mirascript/mirascript/subtle";
|
|
21
|
+
|
|
22
|
+
// src/lsp/utils.ts
|
|
23
|
+
import { DiagnosticCode } from "@mirascript/wasm";
|
|
24
|
+
import {
|
|
25
|
+
getVmFunctionInfo,
|
|
26
|
+
isVmArray,
|
|
27
|
+
isVmExtern,
|
|
28
|
+
isVmFunction,
|
|
29
|
+
isVmModule,
|
|
30
|
+
isVmPrimitive,
|
|
31
|
+
isVmRecord,
|
|
32
|
+
serialize
|
|
33
|
+
} from "@mirascript/mirascript";
|
|
34
|
+
import { operations, serializePropName, serializeString } from "@mirascript/mirascript/subtle";
|
|
35
|
+
function globalParamsSignature(info) {
|
|
36
|
+
if (info == null || !info.params && !info.paramsType) return [["..", "..", ""]];
|
|
37
|
+
const paramItems = [];
|
|
38
|
+
const params = Object.keys(info.paramsType ?? {});
|
|
39
|
+
for (const key of Object.keys(info.params ?? {})) {
|
|
40
|
+
if (params.includes(key)) continue;
|
|
41
|
+
params.push(key);
|
|
42
|
+
}
|
|
43
|
+
for (const key of params) {
|
|
44
|
+
const type = info.paramsType?.[key] ?? "";
|
|
45
|
+
const doc = info.params?.[key] ?? "";
|
|
46
|
+
paramItems.push([key, type ? `${key}: ${type}` : key, doc ? `\`${key}\`: ${doc}` : ""]);
|
|
47
|
+
}
|
|
48
|
+
return paramItems;
|
|
49
|
+
}
|
|
50
|
+
var SIG_WIDTH = 60;
|
|
51
|
+
function fnSignature(id, info) {
|
|
52
|
+
const prefix = id ? `fn ${id}` : "fn";
|
|
53
|
+
const params = globalParamsSignature(info);
|
|
54
|
+
const returns = info.returnsType ? ` -> ${info.returnsType}` : "";
|
|
55
|
+
return {
|
|
56
|
+
params,
|
|
57
|
+
returns,
|
|
58
|
+
toString() {
|
|
59
|
+
let p;
|
|
60
|
+
if (this.params.length >= 1 && (prefix.length + this.returns.length > SIG_WIDTH || prefix.length + this.returns.length + params.reduce((a, b) => a + b[1].length, 0) > SIG_WIDTH)) {
|
|
61
|
+
p = `(
|
|
62
|
+
${params.map((item) => ` ${item[1]},`).join("\n")}
|
|
63
|
+
)`;
|
|
64
|
+
} else {
|
|
65
|
+
p = `(${params.map((item) => item[1]).join(", ")})`;
|
|
66
|
+
}
|
|
67
|
+
return `${prefix}${p}${this.returns}`;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function localParamSignature(model, info) {
|
|
72
|
+
const {
|
|
73
|
+
args,
|
|
74
|
+
scope: { params }
|
|
75
|
+
} = info;
|
|
76
|
+
if (params[0]?.code === DiagnosticCode.ParameterIt) {
|
|
77
|
+
return params[0].references.length ? [["it", "it", ""]] : [];
|
|
78
|
+
}
|
|
79
|
+
return params.map((a, i) => {
|
|
80
|
+
const rest = a.code === DiagnosticCode.ParameterRestPattern || a.code === DiagnosticCode.ParameterMutableRest || a.code === DiagnosticCode.ParameterImmutableRest;
|
|
81
|
+
const argsInParam = args.filter((arg) => Range.containsRange(a.range, arg.definition.range));
|
|
82
|
+
const argName = argsInParam.length === 0 ? `arg_${i}` : argsInParam.map((arg) => model.getValueInRange(arg.definition.range)).join("_");
|
|
83
|
+
if (rest) return [`..${argName}`, `..${argName}`, ""];
|
|
84
|
+
return [argName, argName, ""];
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
function paramsList(model, info) {
|
|
88
|
+
if (!info) return "(..)";
|
|
89
|
+
if ("scope" in info) {
|
|
90
|
+
return `(${localParamSignature(model, info).map((p) => p[1]).join(", ")})`;
|
|
91
|
+
} else {
|
|
92
|
+
if (!info.params) return "(..)";
|
|
93
|
+
const paramItems = Object.keys(info.params).join(", ");
|
|
94
|
+
return `(${paramItems})`;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function globalFnDoc(info) {
|
|
98
|
+
const doc = [];
|
|
99
|
+
if (info.summary) {
|
|
100
|
+
doc.push(info.summary);
|
|
101
|
+
}
|
|
102
|
+
const paramDoc = [];
|
|
103
|
+
if (info.params) {
|
|
104
|
+
for (const [key, value] of Object.entries(info.params)) {
|
|
105
|
+
if (!value) continue;
|
|
106
|
+
paramDoc.push(`- \`${key}\`: ${value}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (info.returns) {
|
|
110
|
+
paramDoc.push(`- **返回值**: ${info.returns}`);
|
|
111
|
+
}
|
|
112
|
+
if (paramDoc.length) {
|
|
113
|
+
doc.push(paramDoc.join("\n"));
|
|
114
|
+
}
|
|
115
|
+
if (info.examples?.length) {
|
|
116
|
+
let exp = `### 示例`;
|
|
117
|
+
for (const example of info.examples) {
|
|
118
|
+
exp += codeblock(example);
|
|
119
|
+
}
|
|
120
|
+
doc.push(exp);
|
|
121
|
+
}
|
|
122
|
+
return doc;
|
|
123
|
+
}
|
|
124
|
+
function codeblock(value) {
|
|
125
|
+
const includeFences = /`{3,}/.exec(value);
|
|
126
|
+
const CODEBLOCK_FENCE = includeFences ? "`".repeat(includeFences[0].length + 1) : "```";
|
|
127
|
+
return `
|
|
128
|
+
${CODEBLOCK_FENCE}mirascript
|
|
129
|
+
${value}
|
|
130
|
+
${CODEBLOCK_FENCE}
|
|
131
|
+
`;
|
|
132
|
+
}
|
|
133
|
+
function strictContainsPosition(range, position) {
|
|
134
|
+
return !Range.isEmpty(range) && Range.containsPosition(range, position);
|
|
135
|
+
}
|
|
136
|
+
function wordAt(model, position) {
|
|
137
|
+
const word = model.getWordAtPosition(position);
|
|
138
|
+
if (!word) return void 0;
|
|
139
|
+
const range = new Range(position.lineNumber, word.startColumn, position.lineNumber, word.endColumn);
|
|
140
|
+
return { word: word.word, range };
|
|
141
|
+
}
|
|
142
|
+
function serializeForDisplayInner(value, maxWidth) {
|
|
143
|
+
if (maxWidth < 10) maxWidth = 10;
|
|
144
|
+
if (typeof value === "string") {
|
|
145
|
+
if (value.length < maxWidth) {
|
|
146
|
+
return serializeString(value);
|
|
147
|
+
}
|
|
148
|
+
return `${serializeString(value.slice(0, maxWidth))}..`;
|
|
149
|
+
}
|
|
150
|
+
if (isVmPrimitive(value)) {
|
|
151
|
+
return serialize(value);
|
|
152
|
+
}
|
|
153
|
+
if (isVmArray(value)) {
|
|
154
|
+
const len = value.length;
|
|
155
|
+
if (!len) return "[]";
|
|
156
|
+
return `[../* x${len} */]`;
|
|
157
|
+
}
|
|
158
|
+
if (isVmRecord(value)) {
|
|
159
|
+
const len = Object.keys(value).length;
|
|
160
|
+
if (!len) return "()";
|
|
161
|
+
return `(../* x${len} */)`;
|
|
162
|
+
}
|
|
163
|
+
return `/* ${operations.$ToString(value)} */`;
|
|
164
|
+
}
|
|
165
|
+
function serializeForDisplay(value, maxEntries = 100, maxWidth = 40) {
|
|
166
|
+
if (isVmPrimitive(value) || isVmFunction(value)) {
|
|
167
|
+
return serializeForDisplayInner(value, maxWidth);
|
|
168
|
+
}
|
|
169
|
+
let begin, end;
|
|
170
|
+
const entries = [];
|
|
171
|
+
let resultLength = 0;
|
|
172
|
+
if (isVmArray(value)) {
|
|
173
|
+
begin = "[";
|
|
174
|
+
end = "]";
|
|
175
|
+
for (const v of value) {
|
|
176
|
+
if (entries.length > maxEntries) {
|
|
177
|
+
entries.push(`../* x${value.length - entries.length} */`);
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
const entry = serializeForDisplayInner(v ?? null, maxWidth - 2);
|
|
181
|
+
entries.push(entry);
|
|
182
|
+
resultLength += entry.length;
|
|
183
|
+
}
|
|
184
|
+
} else if (isVmRecord(value)) {
|
|
185
|
+
begin = "(";
|
|
186
|
+
end = ")";
|
|
187
|
+
const e = Object.entries(value);
|
|
188
|
+
for (const [key, value2] of e) {
|
|
189
|
+
if (entries.length > maxEntries) {
|
|
190
|
+
entries.push(`../* x${e.length - entries.length} */`);
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
const sk = serializePropName(key);
|
|
194
|
+
const entry = `${sk}: ${serializeForDisplayInner(value2 ?? null, maxWidth - sk.length - 4)}`;
|
|
195
|
+
entries.push(entry);
|
|
196
|
+
resultLength += entry.length;
|
|
197
|
+
}
|
|
198
|
+
} else {
|
|
199
|
+
const hint = serializeForDisplayInner(value, 100);
|
|
200
|
+
const isArray = isVmExtern(value) && Array.isArray(value.value);
|
|
201
|
+
begin = `${hint} ${isArray ? "[" : "("}`;
|
|
202
|
+
end = isArray ? "]" : ")";
|
|
203
|
+
const keys = value.keys();
|
|
204
|
+
for (const [index, key] of keys.entries()) {
|
|
205
|
+
if (entries.length > maxEntries) {
|
|
206
|
+
entries.push(`../* x${keys.length - entries.length} */`);
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
let entry;
|
|
210
|
+
if (isArray && String(index) === key) {
|
|
211
|
+
entry = serializeForDisplayInner(value.get(key) ?? null, maxWidth - 2);
|
|
212
|
+
} else {
|
|
213
|
+
const sk = serializePropName(key);
|
|
214
|
+
entry = `${sk}: ${serializeForDisplayInner(value.get(key) ?? null, maxWidth - sk.length - 4)}`;
|
|
215
|
+
}
|
|
216
|
+
entries.push(entry);
|
|
217
|
+
resultLength += entry.length;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (resultLength >= maxWidth) {
|
|
221
|
+
return `${begin}
|
|
222
|
+
${entries.join(",\n ")}
|
|
223
|
+
${end}`;
|
|
224
|
+
}
|
|
225
|
+
return `${begin}${entries.join(", ")}${end}`;
|
|
226
|
+
}
|
|
227
|
+
function valueDoc(name, value, type) {
|
|
228
|
+
const info = getVmFunctionInfo(value);
|
|
229
|
+
if (info) {
|
|
230
|
+
return {
|
|
231
|
+
script: fnSignature(name, info).toString() + (type === "declare" ? ";" : ""),
|
|
232
|
+
doc: globalFnDoc(info)
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
let prefix;
|
|
236
|
+
let suffix = "";
|
|
237
|
+
if (type === "hint") {
|
|
238
|
+
prefix = `${name} = `;
|
|
239
|
+
} else if (type === "declare") {
|
|
240
|
+
if (name.startsWith("@")) {
|
|
241
|
+
prefix = `const ${name} = `;
|
|
242
|
+
} else {
|
|
243
|
+
prefix = `let ${name} = `;
|
|
244
|
+
}
|
|
245
|
+
suffix = ";";
|
|
246
|
+
} else if (/^\d/.test(name)) {
|
|
247
|
+
prefix = `[${name}]: `;
|
|
248
|
+
} else {
|
|
249
|
+
prefix = `${name}: `;
|
|
250
|
+
}
|
|
251
|
+
if (isVmModule(value)) {
|
|
252
|
+
const doc = `模块 \`${value.name}\``;
|
|
253
|
+
let script;
|
|
254
|
+
if (type === "declare") {
|
|
255
|
+
const exports = value.keys();
|
|
256
|
+
script = "\n";
|
|
257
|
+
for (const k of exports) {
|
|
258
|
+
const v = value.get(k);
|
|
259
|
+
const vDoc = valueDoc(k, v, isVmModule(v) ? "field" : "declare");
|
|
260
|
+
const code = [
|
|
261
|
+
`/**`,
|
|
262
|
+
...vDoc.doc.flatMap((sec) => sec.split("\n")).map((line) => ` * ${line}`),
|
|
263
|
+
` */`,
|
|
264
|
+
"export " + vDoc.script,
|
|
265
|
+
"",
|
|
266
|
+
""
|
|
267
|
+
];
|
|
268
|
+
script += code.join("\n");
|
|
269
|
+
}
|
|
270
|
+
script = script.trimEnd();
|
|
271
|
+
} else {
|
|
272
|
+
script = `(module) ${value.name}`;
|
|
273
|
+
if (value.name !== name) {
|
|
274
|
+
script = `${prefix}${script}`;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return { script, doc: doc ? [doc] : [] };
|
|
278
|
+
}
|
|
279
|
+
let valueStr;
|
|
280
|
+
if (value === void 0) {
|
|
281
|
+
valueStr = "/* ... */";
|
|
282
|
+
} else {
|
|
283
|
+
valueStr = serializeForDisplay(value, type === "declare" ? 1e3 : 100, type === "declare" ? 80 : 40);
|
|
284
|
+
}
|
|
285
|
+
return { script: `${prefix}${valueStr}${suffix}`, doc: [] };
|
|
286
|
+
}
|
|
287
|
+
function getDeep(value, path) {
|
|
288
|
+
let current = value;
|
|
289
|
+
for (const key of path) {
|
|
290
|
+
if (current == null) return null;
|
|
291
|
+
current = operations.$Get(current, key);
|
|
292
|
+
}
|
|
293
|
+
return current ?? null;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// src/lsp/compile-result.ts
|
|
297
|
+
import {
|
|
298
|
+
parseDiagnostics,
|
|
299
|
+
DiagnosticCode as DiagnosticCode2
|
|
300
|
+
} from "@mirascript/mirascript/subtle";
|
|
301
|
+
var LocalFunctionType = [DiagnosticCode2.LocalFunction];
|
|
302
|
+
var LocalVariableType = [
|
|
303
|
+
DiagnosticCode2.LocalMutable,
|
|
304
|
+
DiagnosticCode2.LocalImmutable,
|
|
305
|
+
DiagnosticCode2.LocalConst
|
|
306
|
+
];
|
|
307
|
+
var ParameterExplicitType = [
|
|
308
|
+
DiagnosticCode2.ParameterMutable,
|
|
309
|
+
DiagnosticCode2.ParameterImmutable,
|
|
310
|
+
DiagnosticCode2.ParameterMutableRest,
|
|
311
|
+
DiagnosticCode2.ParameterImmutableRest
|
|
312
|
+
];
|
|
313
|
+
var ParameterSubPatternType = [
|
|
314
|
+
DiagnosticCode2.ParameterSubPatternImmutable,
|
|
315
|
+
DiagnosticCode2.ParameterSubPatternMutable
|
|
316
|
+
];
|
|
317
|
+
var ParameterPatternType = [DiagnosticCode2.ParameterPattern, DiagnosticCode2.ParameterRestPattern];
|
|
318
|
+
var ParameterItType = [DiagnosticCode2.ParameterIt];
|
|
319
|
+
var ParameterDefinitionType = [
|
|
320
|
+
...ParameterExplicitType,
|
|
321
|
+
...ParameterSubPatternType,
|
|
322
|
+
...ParameterItType
|
|
323
|
+
];
|
|
324
|
+
var ParameterPlaceholderType = [
|
|
325
|
+
...ParameterExplicitType,
|
|
326
|
+
...ParameterPatternType,
|
|
327
|
+
...ParameterItType
|
|
328
|
+
];
|
|
329
|
+
var LocalDefinitionType = [...LocalVariableType, ...LocalFunctionType, ...ParameterDefinitionType];
|
|
330
|
+
var CompileResult = class {
|
|
331
|
+
constructor(cacheKey, version, source, result) {
|
|
332
|
+
this.cacheKey = cacheKey;
|
|
333
|
+
this.version = version;
|
|
334
|
+
this.source = source;
|
|
335
|
+
this.result = result;
|
|
336
|
+
this.diagnosticsReady = false;
|
|
337
|
+
this._errors = [];
|
|
338
|
+
this._warnings = [];
|
|
339
|
+
this._infos = [];
|
|
340
|
+
this._hints = [];
|
|
341
|
+
this._references = [];
|
|
342
|
+
this._tags = [];
|
|
343
|
+
this._tagsReferences = [];
|
|
344
|
+
this.diagnostics = result.diagnostics;
|
|
345
|
+
this.chunk = result.chunk;
|
|
346
|
+
}
|
|
347
|
+
/** 源代码诊断信息 */
|
|
348
|
+
get errors() {
|
|
349
|
+
if (!this.diagnosticsReady) {
|
|
350
|
+
this.readDiagnostics();
|
|
351
|
+
}
|
|
352
|
+
return this._errors;
|
|
353
|
+
}
|
|
354
|
+
/** 源代码诊断信息 */
|
|
355
|
+
get warnings() {
|
|
356
|
+
if (!this.diagnosticsReady) {
|
|
357
|
+
this.readDiagnostics();
|
|
358
|
+
}
|
|
359
|
+
return this._warnings;
|
|
360
|
+
}
|
|
361
|
+
/** 源代码诊断信息 */
|
|
362
|
+
get infos() {
|
|
363
|
+
if (!this.diagnosticsReady) {
|
|
364
|
+
this.readDiagnostics();
|
|
365
|
+
}
|
|
366
|
+
return this._infos;
|
|
367
|
+
}
|
|
368
|
+
/** 源代码诊断信息 */
|
|
369
|
+
get hints() {
|
|
370
|
+
if (!this.diagnosticsReady) {
|
|
371
|
+
this.readDiagnostics();
|
|
372
|
+
}
|
|
373
|
+
return this._hints;
|
|
374
|
+
}
|
|
375
|
+
/** 源代码诊断信息 */
|
|
376
|
+
get references() {
|
|
377
|
+
if (!this.diagnosticsReady) {
|
|
378
|
+
this.readDiagnostics();
|
|
379
|
+
}
|
|
380
|
+
return this._references;
|
|
381
|
+
}
|
|
382
|
+
/** 源代码诊断信息 */
|
|
383
|
+
get tags() {
|
|
384
|
+
if (!this.diagnosticsReady) {
|
|
385
|
+
this.readDiagnostics();
|
|
386
|
+
}
|
|
387
|
+
return this._tags;
|
|
388
|
+
}
|
|
389
|
+
/** 源代码诊断信息 */
|
|
390
|
+
get tagsReferences() {
|
|
391
|
+
if (!this.diagnosticsReady) {
|
|
392
|
+
this.readDiagnostics();
|
|
393
|
+
}
|
|
394
|
+
return this._tagsReferences;
|
|
395
|
+
}
|
|
396
|
+
/** 分析诊断信息 */
|
|
397
|
+
readDiagnostics() {
|
|
398
|
+
const parsed = parseDiagnostics(this.source, this.diagnostics);
|
|
399
|
+
this._errors = parsed.errors;
|
|
400
|
+
this._warnings = parsed.warnings;
|
|
401
|
+
this._infos = parsed.infos;
|
|
402
|
+
this._hints = parsed.hints;
|
|
403
|
+
this._tags = parsed.tags;
|
|
404
|
+
this._references = parsed.references;
|
|
405
|
+
this._tagsReferences = parsed.tagsReferences;
|
|
406
|
+
this.diagnosticsReady = true;
|
|
407
|
+
}
|
|
408
|
+
/** 获取源代码定义 */
|
|
409
|
+
groupedTags(model) {
|
|
410
|
+
if (this._groupedTags) {
|
|
411
|
+
return this._groupedTags;
|
|
412
|
+
}
|
|
413
|
+
const getText = (range) => {
|
|
414
|
+
if (model.getVersionId() !== this.version) {
|
|
415
|
+
return void 0;
|
|
416
|
+
}
|
|
417
|
+
return model.getValueInRange(range);
|
|
418
|
+
};
|
|
419
|
+
const locals = [];
|
|
420
|
+
const params = [];
|
|
421
|
+
const globals = [];
|
|
422
|
+
const ranges = [];
|
|
423
|
+
const omitNameFields = [];
|
|
424
|
+
for (const tag of this.tags) {
|
|
425
|
+
if (ParameterPlaceholderType.includes(tag.code)) {
|
|
426
|
+
params.push(tag);
|
|
427
|
+
}
|
|
428
|
+
if (LocalDefinitionType.includes(tag.code)) {
|
|
429
|
+
locals.push({
|
|
430
|
+
definition: tag,
|
|
431
|
+
references: tag.references
|
|
432
|
+
});
|
|
433
|
+
} else if (tag.code === DiagnosticCode2.GlobalVariable) {
|
|
434
|
+
const name = getText(tag.range);
|
|
435
|
+
let def = globals.find((def2) => name === def2.name);
|
|
436
|
+
if (!def) {
|
|
437
|
+
def = {
|
|
438
|
+
name: name ?? "",
|
|
439
|
+
references: []
|
|
440
|
+
};
|
|
441
|
+
globals.push(def);
|
|
442
|
+
}
|
|
443
|
+
def.references.push(tag);
|
|
444
|
+
} else if (tag.code === DiagnosticCode2.Scope || tag.code === DiagnosticCode2.String || tag.code === DiagnosticCode2.Interpolation || tag.code === DiagnosticCode2.FunctionCall || tag.code === DiagnosticCode2.ExtensionCall) {
|
|
445
|
+
ranges.push(tag);
|
|
446
|
+
} else if (tag.code === DiagnosticCode2.OmitNamedRecordField) {
|
|
447
|
+
omitNameFields.push(tag);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
locals.sort(
|
|
451
|
+
(a, b) => Range.compareRangesUsingStarts(a.definition.range, b.definition.range)
|
|
452
|
+
);
|
|
453
|
+
params.sort((a, b) => Range.compareRangesUsingStarts(a.range, b.range));
|
|
454
|
+
globals.sort((a, b) => a.name.localeCompare(b.name));
|
|
455
|
+
ranges.sort((a, b) => Range.compareRangesUsingStarts(a.range, b.range));
|
|
456
|
+
this._groupedTags = {
|
|
457
|
+
locals,
|
|
458
|
+
params,
|
|
459
|
+
globals,
|
|
460
|
+
ranges,
|
|
461
|
+
omitNameFields
|
|
462
|
+
};
|
|
463
|
+
return this._groupedTags;
|
|
464
|
+
}
|
|
465
|
+
/** 获取指定位置的变量访问信息 */
|
|
466
|
+
variableAccessAt(model, position) {
|
|
467
|
+
const { globals } = this.groupedTags(model);
|
|
468
|
+
for (const d of globals) {
|
|
469
|
+
const refIndex = d.references.findIndex((u) => strictContainsPosition(u.range, position));
|
|
470
|
+
if (refIndex >= 0) {
|
|
471
|
+
return { def: d, ref: refIndex, range: d.references[refIndex].range };
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
this.scopes(model);
|
|
475
|
+
const { locals } = this.groupedTags(model);
|
|
476
|
+
for (const d of locals) {
|
|
477
|
+
if (strictContainsPosition(d.definition.range, position)) {
|
|
478
|
+
return { def: d, ref: void 0, range: d.definition.range };
|
|
479
|
+
}
|
|
480
|
+
const refIndex = d.references.findIndex((u) => strictContainsPosition(u.range, position));
|
|
481
|
+
if (refIndex >= 0) {
|
|
482
|
+
return { def: d, ref: refIndex, range: d.references[refIndex].range };
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
return void 0;
|
|
486
|
+
}
|
|
487
|
+
/** 获取作用域信息 */
|
|
488
|
+
scopes(model) {
|
|
489
|
+
if (this._scopes) {
|
|
490
|
+
return this._scopes;
|
|
491
|
+
}
|
|
492
|
+
const { locals, params, ranges } = this.groupedTags(model);
|
|
493
|
+
const scopes = ranges.filter((r) => r.code === DiagnosticCode2.Scope).map((r) => {
|
|
494
|
+
return {
|
|
495
|
+
range: r.range,
|
|
496
|
+
locals: [],
|
|
497
|
+
params: [],
|
|
498
|
+
parent: void 0,
|
|
499
|
+
children: []
|
|
500
|
+
};
|
|
501
|
+
});
|
|
502
|
+
for (let i = 0; i < scopes.length; i++) {
|
|
503
|
+
const scopeA = scopes[i];
|
|
504
|
+
let parent;
|
|
505
|
+
for (let j = 0; j < scopes.length; j++) {
|
|
506
|
+
if (i === j) continue;
|
|
507
|
+
const scopeB = scopes[j];
|
|
508
|
+
const aRange = scopeA.range;
|
|
509
|
+
const bRange = scopeB.range;
|
|
510
|
+
const isContained = Range.containsRange(bRange, aRange);
|
|
511
|
+
if (isContained) {
|
|
512
|
+
if (!parent || Range.containsRange(parent.range, bRange)) {
|
|
513
|
+
parent = scopeB;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
if (parent) {
|
|
518
|
+
if (parent.parent === scopeA) {
|
|
519
|
+
continue;
|
|
520
|
+
}
|
|
521
|
+
scopeA.parent = parent;
|
|
522
|
+
parent.children.push(scopeA);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
const root = scopes.find((s) => !s.parent);
|
|
526
|
+
if (!root) {
|
|
527
|
+
this._scopes = [
|
|
528
|
+
{
|
|
529
|
+
range: model.getFullModelRange(),
|
|
530
|
+
locals: [],
|
|
531
|
+
params: [],
|
|
532
|
+
parent: void 0,
|
|
533
|
+
children: []
|
|
534
|
+
}
|
|
535
|
+
];
|
|
536
|
+
return this._scopes;
|
|
537
|
+
}
|
|
538
|
+
const queue = [root];
|
|
539
|
+
const sortedScopes = [];
|
|
540
|
+
while (queue.length > 0) {
|
|
541
|
+
const scope = queue.shift();
|
|
542
|
+
sortedScopes.push(scope);
|
|
543
|
+
scope.children.sort(
|
|
544
|
+
(a, b) => Range.compareRangesUsingStarts(a.range, b.range)
|
|
545
|
+
);
|
|
546
|
+
for (const child of scope.children) {
|
|
547
|
+
queue.push(child);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
for (const local of locals) {
|
|
551
|
+
const { range } = local.definition;
|
|
552
|
+
const scope = sortedScopes.findLast((s) => Range.containsRange(s.range, range));
|
|
553
|
+
if (scope) {
|
|
554
|
+
scope.locals.push(local);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
for (const param of params) {
|
|
558
|
+
const { range } = param;
|
|
559
|
+
const scope = sortedScopes.findLast((s) => Range.containsRange(s.range, range));
|
|
560
|
+
if (scope) {
|
|
561
|
+
scope.params.push(param);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
const scopeMap = /* @__PURE__ */ new Map();
|
|
565
|
+
for (const scope of sortedScopes) {
|
|
566
|
+
scope.locals.sort(
|
|
567
|
+
(a, b) => Range.compareRangesUsingStarts(a.definition.range, b.definition.range)
|
|
568
|
+
);
|
|
569
|
+
scope.params.sort(
|
|
570
|
+
(a, b) => Range.compareRangesUsingStarts(a.range, b.range)
|
|
571
|
+
);
|
|
572
|
+
for (const local of scope.locals) {
|
|
573
|
+
scopeMap.set(local, scope);
|
|
574
|
+
if (local.definition.code === DiagnosticCode2.LocalFunction) {
|
|
575
|
+
const funcScope = scope.children.find(
|
|
576
|
+
(s) => Range.compareRangesUsingStarts(s.range, local.definition.range) > 0
|
|
577
|
+
);
|
|
578
|
+
if (funcScope) {
|
|
579
|
+
const args = funcScope.locals.filter(
|
|
580
|
+
(l) => ParameterDefinitionType.includes(l.definition.code)
|
|
581
|
+
);
|
|
582
|
+
local.fn = {
|
|
583
|
+
scope: funcScope,
|
|
584
|
+
args
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
this._scopeMap = scopeMap;
|
|
591
|
+
this._scopes = sortedScopes;
|
|
592
|
+
return this._scopes;
|
|
593
|
+
}
|
|
594
|
+
/** 获取定义所在作用域 */
|
|
595
|
+
scopeOf(model, def) {
|
|
596
|
+
if (!this._scopeMap) {
|
|
597
|
+
this.scopes(model);
|
|
598
|
+
}
|
|
599
|
+
return this._scopeMap.get(def);
|
|
600
|
+
}
|
|
601
|
+
/** 获取位置所在作用域 */
|
|
602
|
+
scopeAt(model, position) {
|
|
603
|
+
const scopes = this.scopes(model);
|
|
604
|
+
let scope = scopes.findLast((s) => Range.containsPosition(s.range, position)) ?? scopes[0];
|
|
605
|
+
while (scope.children.length > 0) {
|
|
606
|
+
const inner = scope.children.find((s) => Range.containsPosition(s.range, position));
|
|
607
|
+
if (!inner) break;
|
|
608
|
+
scope = inner;
|
|
609
|
+
}
|
|
610
|
+
return scope;
|
|
611
|
+
}
|
|
612
|
+
/** 获取指定位置的字段访问信息 */
|
|
613
|
+
fieldAccessAt(model, position) {
|
|
614
|
+
let prevDef;
|
|
615
|
+
const { globals } = this.groupedTags(model);
|
|
616
|
+
for (const d of globals) {
|
|
617
|
+
for (const [refIndex, ref] of d.references.entries()) {
|
|
618
|
+
if (!Position.isBefore(Range.getEndPosition(ref.range), position)) continue;
|
|
619
|
+
if (prevDef && Position.isBefore(Range.getEndPosition(ref.range), Range.getEndPosition(prevDef.range)))
|
|
620
|
+
continue;
|
|
621
|
+
prevDef = { def: d, ref: refIndex, range: ref.range };
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
this.scopes(model);
|
|
625
|
+
const { locals } = this.groupedTags(model);
|
|
626
|
+
for (const d of locals) {
|
|
627
|
+
for (const [refIndex, ref] of d.references.entries()) {
|
|
628
|
+
if (!Position.isBefore(Range.getEndPosition(ref.range), position)) continue;
|
|
629
|
+
if (prevDef && Position.isBefore(Range.getEndPosition(ref.range), Range.getEndPosition(prevDef.range)))
|
|
630
|
+
continue;
|
|
631
|
+
prevDef = { def: d, ref: refIndex, range: ref.range };
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
if (!prevDef) return void 0;
|
|
635
|
+
const chain = model.getValueInRange(Range.fromPositions(Range.getStartPosition(prevDef.range), position));
|
|
636
|
+
const chainParts = chain.split(/\s*(?:!\.|\.)\s*/);
|
|
637
|
+
if (
|
|
638
|
+
// 至少包含变量名和当前位置的字段名
|
|
639
|
+
chainParts.length < 2 || !chainParts.every(
|
|
640
|
+
(part, index) => (
|
|
641
|
+
// 如果是最后一个部分,则可以为空(表示当前位置的字段名),否则必须是合法的标识符
|
|
642
|
+
(index === chainParts.length - 1 ? !part : false) || REG_IDENTIFIER.test(part) || REG_ORDINAL.test(part)
|
|
643
|
+
)
|
|
644
|
+
)
|
|
645
|
+
) {
|
|
646
|
+
return void 0;
|
|
647
|
+
}
|
|
648
|
+
return { def: prevDef, fields: chainParts.slice(1) };
|
|
649
|
+
}
|
|
650
|
+
/** 获取指定位置的字段访问信息 */
|
|
651
|
+
accessAt(model, position) {
|
|
652
|
+
const v = this.variableAccessAt(model, position);
|
|
653
|
+
if (v) return { def: v, fields: [] };
|
|
654
|
+
return this.fieldAccessAt(model, position);
|
|
655
|
+
}
|
|
656
|
+
};
|
|
657
|
+
|
|
658
|
+
// src/lsp/diagnostics.ts
|
|
659
|
+
import { DiagnosticCode as DiagnosticCode3, getDiagnosticMessage } from "@mirascript/mirascript/subtle";
|
|
660
|
+
var makeMarker = (model, modelVersionId, diagnostic, severity) => {
|
|
661
|
+
const { range, code } = diagnostic;
|
|
662
|
+
const { startLineNumber, startColumn, endLineNumber, endColumn } = range;
|
|
663
|
+
let unnecessary = false;
|
|
664
|
+
const deprecated = false;
|
|
665
|
+
if (code === DiagnosticCode3.UnusedLocalVariable || code === DiagnosticCode3.UnusedLocalFunction) {
|
|
666
|
+
unnecessary = true;
|
|
667
|
+
severity = MarkerSeverity.Hint;
|
|
668
|
+
}
|
|
669
|
+
let message = getDiagnosticMessage(code) ?? "Unknown error";
|
|
670
|
+
if (message.includes(`$0`)) {
|
|
671
|
+
message = message.replaceAll(`$0`, model.getValueInRange(range));
|
|
672
|
+
}
|
|
673
|
+
const marker = {
|
|
674
|
+
startLineNumber,
|
|
675
|
+
startColumn,
|
|
676
|
+
endLineNumber,
|
|
677
|
+
endColumn,
|
|
678
|
+
message,
|
|
679
|
+
severity,
|
|
680
|
+
modelVersionId,
|
|
681
|
+
source: "MiraScript"
|
|
682
|
+
};
|
|
683
|
+
const codeName = DiagnosticCode3[code];
|
|
684
|
+
if (codeName) {
|
|
685
|
+
marker.code = {
|
|
686
|
+
value: codeName,
|
|
687
|
+
target: Uri.parse(`https://mira.cloudpss.net/code/${codeName}`)
|
|
688
|
+
};
|
|
689
|
+
} else {
|
|
690
|
+
marker.code = `${code}`;
|
|
691
|
+
}
|
|
692
|
+
if (unnecessary) {
|
|
693
|
+
marker.tags ??= [];
|
|
694
|
+
marker.tags.push(MarkerTag.Unnecessary);
|
|
695
|
+
}
|
|
696
|
+
if (deprecated) {
|
|
697
|
+
marker.tags ??= [];
|
|
698
|
+
marker.tags.push(MarkerTag.Deprecated);
|
|
699
|
+
}
|
|
700
|
+
if (diagnostic.references.length) {
|
|
701
|
+
marker.relatedInformation = [];
|
|
702
|
+
for (const ref of diagnostic.references) {
|
|
703
|
+
const { range: range2, code: code2 } = ref;
|
|
704
|
+
const { startLineNumber: startLineNumber2, startColumn: startColumn2, endLineNumber: endLineNumber2, endColumn: endColumn2 } = range2;
|
|
705
|
+
const message2 = getDiagnosticMessage(code2) ?? "...here";
|
|
706
|
+
marker.relatedInformation.push({
|
|
707
|
+
message: message2,
|
|
708
|
+
resource: model.uri,
|
|
709
|
+
startLineNumber: startLineNumber2,
|
|
710
|
+
startColumn: startColumn2,
|
|
711
|
+
endLineNumber: endLineNumber2,
|
|
712
|
+
endColumn: endColumn2
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
return marker;
|
|
717
|
+
};
|
|
718
|
+
function setMarkers(model, result) {
|
|
719
|
+
const setModelMarkers = editor?.setModelMarkers;
|
|
720
|
+
if (typeof setModelMarkers != "function") return;
|
|
721
|
+
const { version } = result;
|
|
722
|
+
if (version !== model.getVersionId()) return;
|
|
723
|
+
const errors = result.errors.map((d) => makeMarker(model, version, d, MarkerSeverity.Error));
|
|
724
|
+
const warnings = result.warnings.map((d) => makeMarker(model, version, d, MarkerSeverity.Warning));
|
|
725
|
+
const infos = result.infos.map((d) => makeMarker(model, version, d, MarkerSeverity.Info));
|
|
726
|
+
const hints = result.hints.map((d) => makeMarker(model, version, d, MarkerSeverity.Hint));
|
|
727
|
+
const markers = [...errors, ...warnings, ...infos, ...hints];
|
|
728
|
+
setModelMarkers(model, "mirascript", markers);
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// src/lsp/worker-helper.ts
|
|
732
|
+
var cache = /* @__PURE__ */ new Map();
|
|
733
|
+
var worker = void 0;
|
|
734
|
+
var CACHE_MAX_AGE = 3e4;
|
|
735
|
+
setInterval(() => {
|
|
736
|
+
const now = Date.now();
|
|
737
|
+
for (const [key, { lastAccess }] of cache) {
|
|
738
|
+
if (now - lastAccess > CACHE_MAX_AGE) {
|
|
739
|
+
cache.delete(key);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
}, CACHE_MAX_AGE);
|
|
743
|
+
async function compileWorker(req) {
|
|
744
|
+
if (!worker) {
|
|
745
|
+
const w = new Worker(new URL("#lsp/worker", import.meta.url), {
|
|
746
|
+
type: "module",
|
|
747
|
+
name: "@mirascript/lsp-server"
|
|
748
|
+
});
|
|
749
|
+
worker = new Promise((resolve, reject) => {
|
|
750
|
+
const onError = (e) => {
|
|
751
|
+
cleanUp();
|
|
752
|
+
reject(new Error(`Worker failed to start: ${e.message}`));
|
|
753
|
+
};
|
|
754
|
+
const onMessage = (e) => {
|
|
755
|
+
if (e.data === "mirascript lsp ready") {
|
|
756
|
+
cleanUp();
|
|
757
|
+
resolve(w);
|
|
758
|
+
} else if (e.data instanceof Error) {
|
|
759
|
+
cleanUp();
|
|
760
|
+
reject(e.data);
|
|
761
|
+
}
|
|
762
|
+
};
|
|
763
|
+
w.addEventListener("error", onError);
|
|
764
|
+
w.addEventListener("message", onMessage);
|
|
765
|
+
const cleanUp = () => {
|
|
766
|
+
w.removeEventListener("error", onError);
|
|
767
|
+
w.removeEventListener("message", onMessage);
|
|
768
|
+
};
|
|
769
|
+
setTimeout(() => {
|
|
770
|
+
onError(new ErrorEvent("error", { message: "Worker did not respond in time" }));
|
|
771
|
+
}, 3e4);
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
const instance = await worker;
|
|
775
|
+
instance.postMessage(req);
|
|
776
|
+
const [key, version, source] = req;
|
|
777
|
+
const [_key, _version, result] = await new Promise((resolve, reject) => {
|
|
778
|
+
const onMessage = (e) => {
|
|
779
|
+
if (e.data[0] === key && e.data[1] === version) {
|
|
780
|
+
instance.removeEventListener("message", onMessage);
|
|
781
|
+
if (e.data[2] instanceof Error) {
|
|
782
|
+
reject(e.data[2]);
|
|
783
|
+
} else {
|
|
784
|
+
resolve(e.data);
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
};
|
|
788
|
+
instance.addEventListener("message", onMessage);
|
|
789
|
+
});
|
|
790
|
+
return new CompileResult(key, version, source, result);
|
|
791
|
+
}
|
|
792
|
+
async function compileSync(req) {
|
|
793
|
+
const [key, version, script, mode] = req;
|
|
794
|
+
const { compile: compile2 } = await import("./worker.js");
|
|
795
|
+
const result = compile2(script, mode);
|
|
796
|
+
return new CompileResult(key, version, script, result);
|
|
797
|
+
}
|
|
798
|
+
var USE_WORKER = typeof Worker === "function";
|
|
799
|
+
async function compile(model) {
|
|
800
|
+
const uri = model.uri.toString();
|
|
801
|
+
const version = model.getVersionId();
|
|
802
|
+
const mode = model.getLanguageId() === "mirascript-template" ? "Template" : "Script";
|
|
803
|
+
const cacheKey = `${model.id}\0${uri}\0${mode}`;
|
|
804
|
+
const cached = cache.get(cacheKey);
|
|
805
|
+
if (cached?.version === version) {
|
|
806
|
+
cached.lastAccess = Date.now();
|
|
807
|
+
return cached.result;
|
|
808
|
+
}
|
|
809
|
+
const value = model.getValue();
|
|
810
|
+
const req = [cacheKey, version, value, mode];
|
|
811
|
+
const res = USE_WORKER ? compileWorker(req) : compileSync(req);
|
|
812
|
+
void res.then((result) => {
|
|
813
|
+
setMarkers(model, result);
|
|
814
|
+
});
|
|
815
|
+
const item = {
|
|
816
|
+
version,
|
|
817
|
+
lastAccess: Date.now(),
|
|
818
|
+
result: res
|
|
819
|
+
};
|
|
820
|
+
cache.set(cacheKey, item);
|
|
821
|
+
res.catch(() => {
|
|
822
|
+
const current = cache.get(cacheKey);
|
|
823
|
+
if (current === item) {
|
|
824
|
+
cache.delete(cacheKey);
|
|
825
|
+
}
|
|
826
|
+
});
|
|
827
|
+
return res;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// src/lsp/providers/base.ts
|
|
831
|
+
var contextProvider;
|
|
832
|
+
function setContextProvider(provider) {
|
|
833
|
+
contextProvider = provider;
|
|
834
|
+
}
|
|
835
|
+
var Provider = class {
|
|
836
|
+
constructor() {
|
|
837
|
+
this.displayName = "MiraScript LSP";
|
|
838
|
+
this._debugDisplayName = "MiraScript LSP";
|
|
839
|
+
this._onDidChange = null;
|
|
840
|
+
}
|
|
841
|
+
/** 获取编译结果 */
|
|
842
|
+
async getCompileResult(model) {
|
|
843
|
+
if (model.uri.scheme === "mirascript") {
|
|
844
|
+
return void 0;
|
|
845
|
+
}
|
|
846
|
+
return await compile(model);
|
|
847
|
+
}
|
|
848
|
+
/** 获取执行上下文(全局变量) */
|
|
849
|
+
async getContext(model) {
|
|
850
|
+
return await contextProvider?.(model) ?? DefaultVmContext;
|
|
851
|
+
}
|
|
852
|
+
/** @inheritdoc */
|
|
853
|
+
get onDidChange() {
|
|
854
|
+
this._onDidChange ??= new Emitter();
|
|
855
|
+
return this._onDidChange.event;
|
|
856
|
+
}
|
|
857
|
+
/** 触发 onDidChange */
|
|
858
|
+
emitDidChange() {
|
|
859
|
+
this._onDidChange?.fire(this);
|
|
860
|
+
}
|
|
861
|
+
};
|
|
862
|
+
|
|
863
|
+
// src/lsp/providers/code-action-provider.ts
|
|
864
|
+
var CodeActionProvider = class extends Provider {
|
|
865
|
+
/** @inheritdoc */
|
|
866
|
+
provideCodeActions(model, range, context, token) {
|
|
867
|
+
return {
|
|
868
|
+
actions: [],
|
|
869
|
+
dispose: () => void 0
|
|
870
|
+
};
|
|
871
|
+
}
|
|
872
|
+
/** @inheritdoc */
|
|
873
|
+
resolveCodeAction(codeAction, token) {
|
|
874
|
+
throw new Error("Method not implemented.");
|
|
875
|
+
}
|
|
876
|
+
};
|
|
877
|
+
|
|
878
|
+
// src/lsp/providers/color-provider.ts
|
|
879
|
+
import { DiagnosticCode as DiagnosticCode4 } from "@mirascript/wasm";
|
|
880
|
+
var REG_COLOR_STR = /^(@*)(['"`])(#(?:[0-9a-f]{6}|[0-9a-f]{3}|[0-9a-f]{8}|[0-9a-f]{4}))\2\1$/iu;
|
|
881
|
+
function parseColorString(text) {
|
|
882
|
+
const colorMatch = REG_COLOR_STR.exec(text);
|
|
883
|
+
if (!colorMatch) {
|
|
884
|
+
return void 0;
|
|
885
|
+
}
|
|
886
|
+
const ats = colorMatch[1] || "";
|
|
887
|
+
const quote = colorMatch[2] || "";
|
|
888
|
+
const colorString = colorMatch[3];
|
|
889
|
+
let color;
|
|
890
|
+
if (colorString.startsWith("#")) {
|
|
891
|
+
const colorCode = colorString.slice(1);
|
|
892
|
+
if (colorCode.length === 3) {
|
|
893
|
+
color = {
|
|
894
|
+
red: Number.parseInt(colorCode[0] + colorCode[0], 16) / 255,
|
|
895
|
+
green: Number.parseInt(colorCode[1] + colorCode[1], 16) / 255,
|
|
896
|
+
blue: Number.parseInt(colorCode[2] + colorCode[2], 16) / 255,
|
|
897
|
+
alpha: 1
|
|
898
|
+
};
|
|
899
|
+
} else if (colorCode.length === 6) {
|
|
900
|
+
color = {
|
|
901
|
+
red: Number.parseInt(colorCode.slice(0, 2), 16) / 255,
|
|
902
|
+
green: Number.parseInt(colorCode.slice(2, 4), 16) / 255,
|
|
903
|
+
blue: Number.parseInt(colorCode.slice(4, 6), 16) / 255,
|
|
904
|
+
alpha: 1
|
|
905
|
+
// 假设不透明
|
|
906
|
+
};
|
|
907
|
+
} else if (colorCode.length === 8) {
|
|
908
|
+
color = {
|
|
909
|
+
red: Number.parseInt(colorCode.slice(0, 2), 16) / 255,
|
|
910
|
+
green: Number.parseInt(colorCode.slice(2, 4), 16) / 255,
|
|
911
|
+
blue: Number.parseInt(colorCode.slice(4, 6), 16) / 255,
|
|
912
|
+
alpha: Number.parseInt(colorCode.slice(6, 8), 16) / 255
|
|
913
|
+
};
|
|
914
|
+
} else if (colorCode.length === 4) {
|
|
915
|
+
color = {
|
|
916
|
+
red: Number.parseInt(colorCode[0] + colorCode[0], 16) / 255,
|
|
917
|
+
green: Number.parseInt(colorCode[1] + colorCode[1], 16) / 255,
|
|
918
|
+
blue: Number.parseInt(colorCode[2] + colorCode[2], 16) / 255,
|
|
919
|
+
alpha: Number.parseInt(colorCode[3] + colorCode[3], 16) / 255
|
|
920
|
+
};
|
|
921
|
+
} else {
|
|
922
|
+
return void 0;
|
|
923
|
+
}
|
|
924
|
+
} else {
|
|
925
|
+
return void 0;
|
|
926
|
+
}
|
|
927
|
+
return {
|
|
928
|
+
ats,
|
|
929
|
+
quote,
|
|
930
|
+
colorString,
|
|
931
|
+
color
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
var ColorProvider = class extends Provider {
|
|
935
|
+
/** @inheritdoc */
|
|
936
|
+
async provideDocumentColors(model, token) {
|
|
937
|
+
const compiled = await this.getCompileResult(model);
|
|
938
|
+
if (!compiled) return void 0;
|
|
939
|
+
const info = [];
|
|
940
|
+
for (const { range, code } of compiled.groupedTags(model).ranges) {
|
|
941
|
+
if (code !== DiagnosticCode4.String) continue;
|
|
942
|
+
if (range.startLineNumber !== range.endLineNumber) {
|
|
943
|
+
continue;
|
|
944
|
+
}
|
|
945
|
+
const text = model.getValueInRange(range);
|
|
946
|
+
const parsed = parseColorString(text);
|
|
947
|
+
if (!parsed) continue;
|
|
948
|
+
info.push({
|
|
949
|
+
range: {
|
|
950
|
+
startLineNumber: range.startLineNumber,
|
|
951
|
+
startColumn: range.startColumn + parsed.ats.length + parsed.quote.length,
|
|
952
|
+
endLineNumber: range.endLineNumber,
|
|
953
|
+
endColumn: range.endColumn - parsed.ats.length - parsed.quote.length
|
|
954
|
+
},
|
|
955
|
+
color: parsed.color
|
|
956
|
+
});
|
|
957
|
+
}
|
|
958
|
+
return info;
|
|
959
|
+
}
|
|
960
|
+
/** @inheritdoc */
|
|
961
|
+
provideColorPresentations(model, colorInfo, token) {
|
|
962
|
+
const { color } = colorInfo;
|
|
963
|
+
return [
|
|
964
|
+
{
|
|
965
|
+
label: `#${Math.round(color.red * 255).toString(16).padStart(2, "0")}${Math.round(color.green * 255).toString(16).padStart(2, "0")}${Math.round(color.blue * 255).toString(16).padStart(2, "0")}${color.alpha >= 1 ? "" : Math.round(color.alpha * 255).toString(16).padStart(2, "0")}`
|
|
966
|
+
}
|
|
967
|
+
];
|
|
968
|
+
}
|
|
969
|
+
};
|
|
970
|
+
|
|
971
|
+
// src/lsp/providers/completion-item-provider.ts
|
|
972
|
+
import {
|
|
973
|
+
getVmFunctionInfo as getVmFunctionInfo2,
|
|
974
|
+
isVmExtern as isVmExtern2,
|
|
975
|
+
isVmModule as isVmModule2
|
|
976
|
+
} from "@mirascript/mirascript";
|
|
977
|
+
import { DiagnosticCode as DiagnosticCode5, lib, operations as operations2 } from "@mirascript/mirascript/subtle";
|
|
978
|
+
var DESC_GLOBAL = "(global)";
|
|
979
|
+
var DESC_LOCAL = "(local)";
|
|
980
|
+
var DESC_FIELD = "(field)";
|
|
981
|
+
var SUGGEST_KEYWORDS = [];
|
|
982
|
+
var loadSuggestKeywords = () => {
|
|
983
|
+
if (SUGGEST_KEYWORDS.length > 0) return SUGGEST_KEYWORDS;
|
|
984
|
+
const reserved = reservedKeywords();
|
|
985
|
+
for (const kw of keywords()) {
|
|
986
|
+
if (reserved.includes(kw)) continue;
|
|
987
|
+
SUGGEST_KEYWORDS.push(kw);
|
|
988
|
+
}
|
|
989
|
+
return SUGGEST_KEYWORDS;
|
|
990
|
+
};
|
|
991
|
+
var COMMON_GLOBAL_SUGGESTIONS = (range, extension) => {
|
|
992
|
+
const suggestions = [
|
|
993
|
+
{
|
|
994
|
+
label: "type",
|
|
995
|
+
kind: languages.CompletionItemKind.Keyword,
|
|
996
|
+
insertText: "type",
|
|
997
|
+
commitCharacters: ["("],
|
|
998
|
+
documentation: {
|
|
999
|
+
value: `使用 \`type()\` 调用获取表达式的类型。${codeblock("type(expression);\nexpression::type();")}`
|
|
1000
|
+
},
|
|
1001
|
+
range
|
|
1002
|
+
},
|
|
1003
|
+
{
|
|
1004
|
+
label: "global",
|
|
1005
|
+
kind: languages.CompletionItemKind.Keyword,
|
|
1006
|
+
insertText: "global",
|
|
1007
|
+
commitCharacters: [".", "["],
|
|
1008
|
+
documentation: {
|
|
1009
|
+
value: `使用 \`global\` 获取全局变量。${codeblock('global.variableName;\nglobal["variableName"];\n"variableName" in global;')}`
|
|
1010
|
+
},
|
|
1011
|
+
range
|
|
1012
|
+
}
|
|
1013
|
+
];
|
|
1014
|
+
if (!extension) {
|
|
1015
|
+
suggestions.push(
|
|
1016
|
+
{
|
|
1017
|
+
label: { label: "if", description: "If 表达式" },
|
|
1018
|
+
kind: languages.CompletionItemKind.Snippet,
|
|
1019
|
+
insertText: "if ${1:condition} {\n $0\n}",
|
|
1020
|
+
insertTextRules: languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
|
1021
|
+
documentation: {
|
|
1022
|
+
value: `使用 \`if\` 表达式进行条件判断。${codeblock("if condition {\n // code\n}")}`
|
|
1023
|
+
},
|
|
1024
|
+
range
|
|
1025
|
+
},
|
|
1026
|
+
{
|
|
1027
|
+
label: { label: "ifelse", description: "If-Else 表达式" },
|
|
1028
|
+
kind: languages.CompletionItemKind.Snippet,
|
|
1029
|
+
insertText: "if ${1:condition} {\n $0\n} else {\n \n}",
|
|
1030
|
+
insertTextRules: languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
|
1031
|
+
documentation: {
|
|
1032
|
+
value: `使用 \`ifelse\` 表达式进行条件判断。${codeblock("if condition {\n // code\n} else {\n // code\n}")}`
|
|
1033
|
+
},
|
|
1034
|
+
range
|
|
1035
|
+
},
|
|
1036
|
+
{
|
|
1037
|
+
label: { label: "match", description: "Match 表达式" },
|
|
1038
|
+
kind: languages.CompletionItemKind.Snippet,
|
|
1039
|
+
insertText: "match ${1:value} {\n case ${2:case1} {\n $0\n }\n case ${3:case2} {\n \n }\n case _ {\n \n }\n}",
|
|
1040
|
+
insertTextRules: languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
|
1041
|
+
documentation: {
|
|
1042
|
+
value: `使用 \`match\` 表达式进行模式匹配。${codeblock("match value {\n case case1 {\n // code\n }\n case case2 {\n // code\n }\n case _ {\n // code\n }\n}")}`
|
|
1043
|
+
},
|
|
1044
|
+
range
|
|
1045
|
+
},
|
|
1046
|
+
{
|
|
1047
|
+
label: { label: "loop", description: "Loop 表达式" },
|
|
1048
|
+
kind: languages.CompletionItemKind.Snippet,
|
|
1049
|
+
insertText: "loop {\n $0\n}",
|
|
1050
|
+
insertTextRules: languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
|
1051
|
+
documentation: {
|
|
1052
|
+
value: `使用 \`loop\` 表达式进行无限循环。${codeblock("loop {\n // code\n}")}`
|
|
1053
|
+
},
|
|
1054
|
+
range
|
|
1055
|
+
},
|
|
1056
|
+
{
|
|
1057
|
+
label: { label: "while", description: "While 表达式" },
|
|
1058
|
+
kind: languages.CompletionItemKind.Snippet,
|
|
1059
|
+
insertText: "while ${1:condition} {\n $0\n}",
|
|
1060
|
+
insertTextRules: languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
|
1061
|
+
documentation: {
|
|
1062
|
+
value: `使用 \`while\` 表达式进行条件循环。${codeblock("while condition {\n // code\n}")}`
|
|
1063
|
+
},
|
|
1064
|
+
range
|
|
1065
|
+
},
|
|
1066
|
+
{
|
|
1067
|
+
label: { label: "whileelse", description: "While-Else 表达式" },
|
|
1068
|
+
kind: languages.CompletionItemKind.Snippet,
|
|
1069
|
+
insertText: "while ${1:condition} {\n $0\n} else {\n \n}",
|
|
1070
|
+
insertTextRules: languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
|
1071
|
+
documentation: {
|
|
1072
|
+
value: `使用 \`whileelse\` 表达式进行条件循环。${codeblock("while condition {\n // code\n} else {\n // code\n}")}`
|
|
1073
|
+
},
|
|
1074
|
+
range
|
|
1075
|
+
},
|
|
1076
|
+
{
|
|
1077
|
+
label: { label: "for", description: "For 表达式" },
|
|
1078
|
+
kind: languages.CompletionItemKind.Snippet,
|
|
1079
|
+
insertText: "for ${1:item} in ${2:collection} {\n $0\n}",
|
|
1080
|
+
insertTextRules: languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
|
1081
|
+
documentation: {
|
|
1082
|
+
value: `使用 \`for\` 表达式进行迭代循环。${codeblock("for item in collection {\n // code\n}")}`
|
|
1083
|
+
},
|
|
1084
|
+
range
|
|
1085
|
+
},
|
|
1086
|
+
{
|
|
1087
|
+
label: { label: "forelse", description: "For-Else 表达式" },
|
|
1088
|
+
kind: languages.CompletionItemKind.Snippet,
|
|
1089
|
+
insertText: "for ${1:item} in ${2:collection} {\n $0\n} else {\n \n}",
|
|
1090
|
+
insertTextRules: languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
|
1091
|
+
documentation: {
|
|
1092
|
+
value: `使用 \`forelse\` 表达式进行迭代循环。${codeblock("for item in collection {\n // code\n} else {\n // code\n}")}`
|
|
1093
|
+
},
|
|
1094
|
+
range
|
|
1095
|
+
},
|
|
1096
|
+
{
|
|
1097
|
+
label: { label: "fn", description: "Fn 语句" },
|
|
1098
|
+
kind: languages.CompletionItemKind.Snippet,
|
|
1099
|
+
insertText: "fn ${1:name}(${2:params}) {\n $0\n}",
|
|
1100
|
+
insertTextRules: languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
|
1101
|
+
documentation: {
|
|
1102
|
+
value: `使用 \`fn\` 语句进行函数声明。${codeblock("fn name(params) {\n // code\n}")}`
|
|
1103
|
+
},
|
|
1104
|
+
range
|
|
1105
|
+
}
|
|
1106
|
+
);
|
|
1107
|
+
for (const kw of loadSuggestKeywords()) {
|
|
1108
|
+
const exist = suggestions.find(
|
|
1109
|
+
(item) => item.label === kw && item.kind === languages.CompletionItemKind.Keyword
|
|
1110
|
+
);
|
|
1111
|
+
if (exist) continue;
|
|
1112
|
+
suggestions.push(kwSuggestion(kw, range));
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
return suggestions;
|
|
1116
|
+
};
|
|
1117
|
+
function kwSuggestion(kw, range) {
|
|
1118
|
+
return {
|
|
1119
|
+
label: kw,
|
|
1120
|
+
kind: languages.CompletionItemKind.Keyword,
|
|
1121
|
+
insertText: kw,
|
|
1122
|
+
range
|
|
1123
|
+
};
|
|
1124
|
+
}
|
|
1125
|
+
function filterText(key, char) {
|
|
1126
|
+
if (char == null || key.startsWith(char)) return key;
|
|
1127
|
+
return key.startsWith("@") || key.startsWith("$") ? key.slice(1) : key;
|
|
1128
|
+
}
|
|
1129
|
+
function completion(model, description, key, value, fn, field) {
|
|
1130
|
+
let detail = "";
|
|
1131
|
+
let kind;
|
|
1132
|
+
if (fn == null && typeof value == "function") {
|
|
1133
|
+
fn = getVmFunctionInfo2(value);
|
|
1134
|
+
}
|
|
1135
|
+
if (fn != null) {
|
|
1136
|
+
detail = paramsList(model, fn);
|
|
1137
|
+
kind = field ? languages.CompletionItemKind.Function : languages.CompletionItemKind.Method;
|
|
1138
|
+
} else if (isVmModule2(value)) {
|
|
1139
|
+
kind = languages.CompletionItemKind.Module;
|
|
1140
|
+
} else if (isVmExtern2(value) && typeof value.value == "function") {
|
|
1141
|
+
if (value.value.prototype != null && (key[0] ?? "").toUpperCase() === key[0]) {
|
|
1142
|
+
kind = languages.CompletionItemKind.Class;
|
|
1143
|
+
} else {
|
|
1144
|
+
detail = "(..)";
|
|
1145
|
+
kind = value.caller ? languages.CompletionItemKind.Method : languages.CompletionItemKind.Function;
|
|
1146
|
+
}
|
|
1147
|
+
} else if (!field && key.startsWith("@")) {
|
|
1148
|
+
kind = languages.CompletionItemKind.Constant;
|
|
1149
|
+
} else {
|
|
1150
|
+
kind = field ? languages.CompletionItemKind.Field : languages.CompletionItemKind.Variable;
|
|
1151
|
+
}
|
|
1152
|
+
return {
|
|
1153
|
+
label: { label: key, description, detail },
|
|
1154
|
+
kind,
|
|
1155
|
+
commitCharacters: fn ? ["!", "("] : ["!", ".", "[", "("],
|
|
1156
|
+
vmValue: value,
|
|
1157
|
+
isField: field
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1160
|
+
function externKeys(value) {
|
|
1161
|
+
const keys = /* @__PURE__ */ new Set();
|
|
1162
|
+
let e = value.value;
|
|
1163
|
+
while (e && (typeof e == "object" || typeof e == "function")) {
|
|
1164
|
+
for (const key of Object.getOwnPropertyNames(e)) {
|
|
1165
|
+
if (value.has(key)) {
|
|
1166
|
+
keys.add(key);
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
e = Object.getPrototypeOf(e);
|
|
1170
|
+
}
|
|
1171
|
+
return Array.from(keys);
|
|
1172
|
+
}
|
|
1173
|
+
var CompletionItemProvider = class extends Provider {
|
|
1174
|
+
constructor() {
|
|
1175
|
+
super(...arguments);
|
|
1176
|
+
this.triggerCharacters = [".", ":"];
|
|
1177
|
+
}
|
|
1178
|
+
/** 查找全局变量 */
|
|
1179
|
+
async completeGlobal(model, char, locals, range) {
|
|
1180
|
+
const global = await this.getContext(model);
|
|
1181
|
+
const suggestions = [];
|
|
1182
|
+
const localKeys = new Set(locals.map((item) => item.insertText));
|
|
1183
|
+
for (const key of new Set(global.keys())) {
|
|
1184
|
+
const element = global.get(key);
|
|
1185
|
+
if (element === void 0) continue;
|
|
1186
|
+
if (isVmModule2(element)) {
|
|
1187
|
+
for (const f of element.keys()) {
|
|
1188
|
+
if (char && !f.toLowerCase().includes(char)) {
|
|
1189
|
+
continue;
|
|
1190
|
+
}
|
|
1191
|
+
const field = element.get(f);
|
|
1192
|
+
if (field === void 0) continue;
|
|
1193
|
+
suggestions.push({
|
|
1194
|
+
insertText: localKeys.has(key) ? `global.${key}.${f}` : `${key}.${f}`,
|
|
1195
|
+
filterText: filterText(f, char),
|
|
1196
|
+
range,
|
|
1197
|
+
...completion(model, DESC_GLOBAL, `${key}.${f}`, field, void 0, true)
|
|
1198
|
+
});
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
if (char && !key.toLowerCase().includes(char)) {
|
|
1202
|
+
continue;
|
|
1203
|
+
}
|
|
1204
|
+
suggestions.push({
|
|
1205
|
+
insertText: localKeys.has(key) ? `global.${key}` : key,
|
|
1206
|
+
// 如果有同名局部变量,使用 global. 前缀
|
|
1207
|
+
filterText: filterText(key, char),
|
|
1208
|
+
range,
|
|
1209
|
+
...completion(model, DESC_GLOBAL, key, element, void 0, false)
|
|
1210
|
+
});
|
|
1211
|
+
}
|
|
1212
|
+
return suggestions;
|
|
1213
|
+
}
|
|
1214
|
+
/** 查找局部变量 */
|
|
1215
|
+
async completeLocal(model, position, char, range) {
|
|
1216
|
+
const compiled = await this.getCompileResult(model);
|
|
1217
|
+
if (!compiled) return [];
|
|
1218
|
+
const suggestions = [];
|
|
1219
|
+
let scope = compiled.scopeAt(model, position);
|
|
1220
|
+
const locals = /* @__PURE__ */ new Set();
|
|
1221
|
+
while (scope) {
|
|
1222
|
+
for (const { definition, fn } of scope.locals) {
|
|
1223
|
+
const name = model.getValueInRange(definition.range);
|
|
1224
|
+
if (locals.has(name)) continue;
|
|
1225
|
+
if (char && !name.toLowerCase().includes(char)) {
|
|
1226
|
+
continue;
|
|
1227
|
+
}
|
|
1228
|
+
locals.add(name);
|
|
1229
|
+
suggestions.push({
|
|
1230
|
+
insertText: name,
|
|
1231
|
+
filterText: filterText(name, char),
|
|
1232
|
+
range,
|
|
1233
|
+
...completion(model, DESC_LOCAL, name, void 0, fn, false)
|
|
1234
|
+
});
|
|
1235
|
+
}
|
|
1236
|
+
if (!scope.parent) break;
|
|
1237
|
+
scope = scope.parent;
|
|
1238
|
+
}
|
|
1239
|
+
return suggestions;
|
|
1240
|
+
}
|
|
1241
|
+
/** 查找变量字段 */
|
|
1242
|
+
async completeFields(model, position, char, range) {
|
|
1243
|
+
const compiled = await this.getCompileResult(model);
|
|
1244
|
+
if (!compiled) return [];
|
|
1245
|
+
const access = compiled.fieldAccessAt(model, position);
|
|
1246
|
+
if (!access || access.fields.length === 0) return [];
|
|
1247
|
+
const { def, fields } = access;
|
|
1248
|
+
if ("definition" in def.def) {
|
|
1249
|
+
return [];
|
|
1250
|
+
}
|
|
1251
|
+
const vmGlobal = await this.getContext(model);
|
|
1252
|
+
fields.pop();
|
|
1253
|
+
const value = getDeep(vmGlobal.get(def.def.name), fields);
|
|
1254
|
+
if (value == null || typeof value != "object") {
|
|
1255
|
+
return [];
|
|
1256
|
+
}
|
|
1257
|
+
const keys = isVmExtern2(value) ? externKeys(value) : lib.keys(value);
|
|
1258
|
+
const result = [];
|
|
1259
|
+
for (const k of keys) {
|
|
1260
|
+
const key = String(k);
|
|
1261
|
+
if (char && !String(key).toLowerCase().includes(char)) {
|
|
1262
|
+
continue;
|
|
1263
|
+
}
|
|
1264
|
+
const field = operations2.$Get(value, key);
|
|
1265
|
+
result.push({
|
|
1266
|
+
insertText: key,
|
|
1267
|
+
range,
|
|
1268
|
+
...completion(model, DESC_FIELD, key, field, void 0, true)
|
|
1269
|
+
});
|
|
1270
|
+
}
|
|
1271
|
+
return result;
|
|
1272
|
+
}
|
|
1273
|
+
/** 获取完成范围 */
|
|
1274
|
+
toCompletionItemRanges(position, range) {
|
|
1275
|
+
return {
|
|
1276
|
+
replace: range,
|
|
1277
|
+
insert: Range.fromPositions(Range.getStartPosition(range), position)
|
|
1278
|
+
};
|
|
1279
|
+
}
|
|
1280
|
+
/** @inheritdoc */
|
|
1281
|
+
async provideCompletionItems(model, position, context, token) {
|
|
1282
|
+
const compiled = await this.getCompileResult(model);
|
|
1283
|
+
if (!compiled) return void 0;
|
|
1284
|
+
if (context.triggerCharacter === ".") {
|
|
1285
|
+
const prevWord = model.getWordAtPosition({
|
|
1286
|
+
lineNumber: position.lineNumber,
|
|
1287
|
+
column: position.column - 1
|
|
1288
|
+
});
|
|
1289
|
+
if (prevWord?.word === "global") {
|
|
1290
|
+
const globals2 = await this.completeGlobal(
|
|
1291
|
+
model,
|
|
1292
|
+
void 0,
|
|
1293
|
+
[],
|
|
1294
|
+
void 0
|
|
1295
|
+
);
|
|
1296
|
+
return { suggestions: globals2 };
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
const word = wordAt(model, position);
|
|
1300
|
+
const prev = model.getValueInRange({
|
|
1301
|
+
startLineNumber: position.lineNumber,
|
|
1302
|
+
startColumn: (word?.range.startColumn ?? position.column) - 2,
|
|
1303
|
+
endLineNumber: position.lineNumber,
|
|
1304
|
+
endColumn: word?.range.startColumn ?? position.column
|
|
1305
|
+
});
|
|
1306
|
+
if (context.triggerCharacter === ":" && prev !== "::") {
|
|
1307
|
+
return void 0;
|
|
1308
|
+
}
|
|
1309
|
+
let char;
|
|
1310
|
+
let range;
|
|
1311
|
+
const def = compiled.variableAccessAt(model, position);
|
|
1312
|
+
if (def) {
|
|
1313
|
+
if (def.ref == null) {
|
|
1314
|
+
const suggestions2 = [];
|
|
1315
|
+
if (word && compiled.tags.some(
|
|
1316
|
+
(t) => strictContainsPosition(t.range, position) && t.code === DiagnosticCode5.MatchExpression
|
|
1317
|
+
)) {
|
|
1318
|
+
suggestions2.push(
|
|
1319
|
+
kwSuggestion("case", this.toCompletionItemRanges(position, word.range)),
|
|
1320
|
+
kwSuggestion("if", this.toCompletionItemRanges(position, word.range))
|
|
1321
|
+
);
|
|
1322
|
+
}
|
|
1323
|
+
return { suggestions: suggestions2 };
|
|
1324
|
+
}
|
|
1325
|
+
const d = def.def;
|
|
1326
|
+
range = d.references[def.ref].range;
|
|
1327
|
+
char = model.getValueInRange({
|
|
1328
|
+
startLineNumber: range.startLineNumber,
|
|
1329
|
+
startColumn: range.startColumn,
|
|
1330
|
+
endLineNumber: range.startLineNumber,
|
|
1331
|
+
endColumn: range.startColumn + 1
|
|
1332
|
+
});
|
|
1333
|
+
} else if (word) {
|
|
1334
|
+
range = word.range;
|
|
1335
|
+
char = word.word[0];
|
|
1336
|
+
} else {
|
|
1337
|
+
range = {
|
|
1338
|
+
startLineNumber: position.lineNumber,
|
|
1339
|
+
startColumn: position.column,
|
|
1340
|
+
endLineNumber: position.lineNumber,
|
|
1341
|
+
endColumn: position.column
|
|
1342
|
+
};
|
|
1343
|
+
}
|
|
1344
|
+
char = char?.toLowerCase();
|
|
1345
|
+
const completionRange = this.toCompletionItemRanges(position, range);
|
|
1346
|
+
if (/[^.]\.$/u.test(prev)) {
|
|
1347
|
+
const suggestions2 = await this.completeFields(model, position, char, completionRange);
|
|
1348
|
+
return { suggestions: suggestions2 };
|
|
1349
|
+
}
|
|
1350
|
+
const suggestions = COMMON_GLOBAL_SUGGESTIONS(completionRange, prev === "::");
|
|
1351
|
+
const locals = await this.completeLocal(model, position, char, completionRange);
|
|
1352
|
+
const globals = await this.completeGlobal(model, char, locals, completionRange);
|
|
1353
|
+
suggestions.push(...locals, ...globals);
|
|
1354
|
+
return { suggestions };
|
|
1355
|
+
}
|
|
1356
|
+
/** @inheritdoc */
|
|
1357
|
+
resolveCompletionItem(item, token) {
|
|
1358
|
+
if (typeof item.label == "string") {
|
|
1359
|
+
return item;
|
|
1360
|
+
}
|
|
1361
|
+
const { vmValue, isField } = item;
|
|
1362
|
+
const { label } = item.label;
|
|
1363
|
+
if (vmValue != null) {
|
|
1364
|
+
if (item.documentation) return item;
|
|
1365
|
+
const last = label.split(".").pop();
|
|
1366
|
+
const def = valueDoc(last, vmValue, isField ? "field" : "hint");
|
|
1367
|
+
item.documentation = {
|
|
1368
|
+
value: `${codeblock("\0" + def.script)}
|
|
1369
|
+
${def.doc.join("\n")}`
|
|
1370
|
+
};
|
|
1371
|
+
}
|
|
1372
|
+
return item;
|
|
1373
|
+
}
|
|
1374
|
+
};
|
|
1375
|
+
|
|
1376
|
+
// src/lsp/providers/definition-reference-provider.ts
|
|
1377
|
+
var DefinitionReferenceProvider = class extends Provider {
|
|
1378
|
+
constructor(globalModel = editor.createModel(``, "mirascript", Uri.parse("mirascript:///lib/global.mira"))) {
|
|
1379
|
+
super();
|
|
1380
|
+
this.globalModel = globalModel;
|
|
1381
|
+
}
|
|
1382
|
+
/** 准备要显示的定义 */
|
|
1383
|
+
prepareGlobal(name, value) {
|
|
1384
|
+
const { globalModel } = this;
|
|
1385
|
+
const { script, doc } = valueDoc(name, value, "declare");
|
|
1386
|
+
const code = [
|
|
1387
|
+
`/**${DOC_HEADER}**/`,
|
|
1388
|
+
"",
|
|
1389
|
+
`/**`,
|
|
1390
|
+
...doc.flatMap((sec) => sec.split("\n")).map((line) => ` * ${line}`),
|
|
1391
|
+
` */`,
|
|
1392
|
+
script,
|
|
1393
|
+
""
|
|
1394
|
+
];
|
|
1395
|
+
globalModel.setValue(code.join("\n"));
|
|
1396
|
+
return {
|
|
1397
|
+
uri: globalModel.uri,
|
|
1398
|
+
range: {
|
|
1399
|
+
startColumn: 1,
|
|
1400
|
+
startLineNumber: code.length - 1,
|
|
1401
|
+
endColumn: 1,
|
|
1402
|
+
endLineNumber: code.length - 1
|
|
1403
|
+
}
|
|
1404
|
+
};
|
|
1405
|
+
}
|
|
1406
|
+
/** @inheritdoc */
|
|
1407
|
+
async provideDefinition(model, position, token) {
|
|
1408
|
+
const compiled = await this.getCompileResult(model);
|
|
1409
|
+
if (!compiled) return void 0;
|
|
1410
|
+
const globals = await this.getContext(model);
|
|
1411
|
+
const d = compiled.variableAccessAt(model, position);
|
|
1412
|
+
if (!d) return [];
|
|
1413
|
+
const { def, ref } = d;
|
|
1414
|
+
let originSelectionRange;
|
|
1415
|
+
if (ref != null) {
|
|
1416
|
+
originSelectionRange = def.references[ref]?.range;
|
|
1417
|
+
} else if ("definition" in def) {
|
|
1418
|
+
originSelectionRange = def.definition.range;
|
|
1419
|
+
}
|
|
1420
|
+
let link;
|
|
1421
|
+
if ("name" in def) {
|
|
1422
|
+
link = this.prepareGlobal(def.name, globals.get(def.name));
|
|
1423
|
+
} else {
|
|
1424
|
+
link = { uri: model.uri, range: def.definition.range };
|
|
1425
|
+
}
|
|
1426
|
+
link.originSelectionRange = originSelectionRange;
|
|
1427
|
+
return [link];
|
|
1428
|
+
}
|
|
1429
|
+
/** @inheritdoc */
|
|
1430
|
+
async provideReferences(model, position, context, token) {
|
|
1431
|
+
const compiled = await this.getCompileResult(model);
|
|
1432
|
+
if (!compiled) return void 0;
|
|
1433
|
+
const globals = await this.getContext(model);
|
|
1434
|
+
const d = compiled.variableAccessAt(model, position);
|
|
1435
|
+
if (!d) return [];
|
|
1436
|
+
const { def } = d;
|
|
1437
|
+
const links = def.references.map((u) => ({
|
|
1438
|
+
uri: model.uri,
|
|
1439
|
+
range: u.range
|
|
1440
|
+
}));
|
|
1441
|
+
if (context.includeDeclaration) {
|
|
1442
|
+
if ("name" in def) {
|
|
1443
|
+
links.push(this.prepareGlobal(def.name, globals.get(def.name)));
|
|
1444
|
+
} else if (!Range.isEmpty(def.definition.range)) {
|
|
1445
|
+
links.push({
|
|
1446
|
+
uri: model.uri,
|
|
1447
|
+
range: def.definition.range
|
|
1448
|
+
});
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
return links;
|
|
1452
|
+
}
|
|
1453
|
+
};
|
|
1454
|
+
|
|
1455
|
+
// src/lsp/providers/document-highlight-provider.ts
|
|
1456
|
+
import { DiagnosticCode as DiagnosticCode6 } from "@mirascript/mirascript/subtle";
|
|
1457
|
+
var DocumentHighlightProvider = class extends Provider {
|
|
1458
|
+
/** @inheritdoc */
|
|
1459
|
+
async provideDocumentHighlights(model, position, token) {
|
|
1460
|
+
const word = model.getWordAtPosition(position);
|
|
1461
|
+
if (word && keywords().includes(word.word)) {
|
|
1462
|
+
const result = await this.highlightKeyword(model, position, word.word);
|
|
1463
|
+
if (result) return result;
|
|
1464
|
+
}
|
|
1465
|
+
return this.getDocumentHighlightsFromDefinition(model, position);
|
|
1466
|
+
}
|
|
1467
|
+
/** 从 definition 生成 DocumentHighlight 列表 */
|
|
1468
|
+
async getDocumentHighlightsFromDefinition(model, position) {
|
|
1469
|
+
const compiled = await this.getCompileResult(model);
|
|
1470
|
+
if (!compiled) return void 0;
|
|
1471
|
+
const def = compiled.variableAccessAt(model, position)?.def;
|
|
1472
|
+
if (!def) return void 0;
|
|
1473
|
+
const links = def.references.map((u) => {
|
|
1474
|
+
const { code, range } = u;
|
|
1475
|
+
let kind = languages.DocumentHighlightKind.Read;
|
|
1476
|
+
if (code === DiagnosticCode6.WriteLocal || code === DiagnosticCode6.ReadWriteLocal || code === DiagnosticCode6.RedeclareLocal) {
|
|
1477
|
+
kind = languages.DocumentHighlightKind.Write;
|
|
1478
|
+
}
|
|
1479
|
+
return {
|
|
1480
|
+
kind,
|
|
1481
|
+
range
|
|
1482
|
+
};
|
|
1483
|
+
});
|
|
1484
|
+
if ("definition" in def && !Range.isEmpty(def.definition.range)) {
|
|
1485
|
+
links.push({
|
|
1486
|
+
kind: languages.DocumentHighlightKind.Write,
|
|
1487
|
+
range: def.definition.range
|
|
1488
|
+
});
|
|
1489
|
+
}
|
|
1490
|
+
return links;
|
|
1491
|
+
}
|
|
1492
|
+
/** 高亮关键字 */
|
|
1493
|
+
async highlightKeyword(model, position, word) {
|
|
1494
|
+
const compiled = await this.getCompileResult(model);
|
|
1495
|
+
if (!compiled) return void 0;
|
|
1496
|
+
const kw = compiled.tagsReferences.find((kw2) => Range.containsPosition(kw2.range, position));
|
|
1497
|
+
if (!kw) return void 0;
|
|
1498
|
+
return kw.diagnostic.references.map((r) => ({
|
|
1499
|
+
kind: languages.DocumentHighlightKind.Text,
|
|
1500
|
+
range: r.range
|
|
1501
|
+
}));
|
|
1502
|
+
}
|
|
1503
|
+
};
|
|
1504
|
+
|
|
1505
|
+
// src/lsp/providers/document-symbol-provider.ts
|
|
1506
|
+
import { DiagnosticCode as DiagnosticCode7 } from "@mirascript/wasm";
|
|
1507
|
+
var DocumentSymbolProvider = class extends Provider {
|
|
1508
|
+
/** 构建树 */
|
|
1509
|
+
handleScope(model, scope) {
|
|
1510
|
+
const symbols = [];
|
|
1511
|
+
const unhandledChildren = new Set(scope.children);
|
|
1512
|
+
for (const { definition } of scope.locals) {
|
|
1513
|
+
const { range } = definition;
|
|
1514
|
+
let kind = languages.SymbolKind.Variable;
|
|
1515
|
+
let name;
|
|
1516
|
+
let children = [];
|
|
1517
|
+
let allRange = range;
|
|
1518
|
+
switch (definition.code) {
|
|
1519
|
+
case DiagnosticCode7.ParameterIt:
|
|
1520
|
+
if (definition.references.length === 0) {
|
|
1521
|
+
continue;
|
|
1522
|
+
}
|
|
1523
|
+
name = `it`;
|
|
1524
|
+
break;
|
|
1525
|
+
case DiagnosticCode7.LocalFunction: {
|
|
1526
|
+
kind = languages.SymbolKind.Function;
|
|
1527
|
+
const funcScope = scope.children.find((s) => Range.compareRangesUsingStarts(s.range, range) > 0);
|
|
1528
|
+
if (funcScope) {
|
|
1529
|
+
allRange = Range.plusRange(range, funcScope.range);
|
|
1530
|
+
children = this.handleScope(model, funcScope);
|
|
1531
|
+
unhandledChildren.delete(funcScope);
|
|
1532
|
+
}
|
|
1533
|
+
break;
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
symbols.push({
|
|
1537
|
+
name: name ?? model.getValueInRange(definition.range),
|
|
1538
|
+
detail: "",
|
|
1539
|
+
kind,
|
|
1540
|
+
range: allRange,
|
|
1541
|
+
tags: [],
|
|
1542
|
+
selectionRange: range,
|
|
1543
|
+
children
|
|
1544
|
+
});
|
|
1545
|
+
}
|
|
1546
|
+
for (const child of unhandledChildren) {
|
|
1547
|
+
symbols.push(...this.handleScope(model, child));
|
|
1548
|
+
}
|
|
1549
|
+
return symbols;
|
|
1550
|
+
}
|
|
1551
|
+
/** @inheritdoc */
|
|
1552
|
+
async provideDocumentSymbols(model, token) {
|
|
1553
|
+
const compiled = await this.getCompileResult(model);
|
|
1554
|
+
if (!compiled) return void 0;
|
|
1555
|
+
const root = compiled.scopes(model)[0];
|
|
1556
|
+
if (!root) return void 0;
|
|
1557
|
+
return this.handleScope(model, root);
|
|
1558
|
+
}
|
|
1559
|
+
};
|
|
1560
|
+
|
|
1561
|
+
// src/lsp/providers/formatter-provider.ts
|
|
1562
|
+
var FormatterProvider = class extends Provider {
|
|
1563
|
+
constructor() {
|
|
1564
|
+
super(...arguments);
|
|
1565
|
+
/** @inheritdoc */
|
|
1566
|
+
this.autoFormatTriggerCharacters = [";", "}", "]", ")"];
|
|
1567
|
+
}
|
|
1568
|
+
/** @inheritdoc */
|
|
1569
|
+
async format(model, ranges, options, hint, token) {
|
|
1570
|
+
if (hint !== "full") return [];
|
|
1571
|
+
const compiled = await this.getCompileResult(model);
|
|
1572
|
+
if (compiled?.result.formatted == null) return [];
|
|
1573
|
+
return [
|
|
1574
|
+
{
|
|
1575
|
+
range: model.getFullModelRange(),
|
|
1576
|
+
text: compiled.result.formatted
|
|
1577
|
+
}
|
|
1578
|
+
];
|
|
1579
|
+
}
|
|
1580
|
+
/** @inheritdoc */
|
|
1581
|
+
provideDocumentFormattingEdits(model, options, token) {
|
|
1582
|
+
return this.format(model, [], options, "full", token);
|
|
1583
|
+
}
|
|
1584
|
+
/** @inheritdoc */
|
|
1585
|
+
provideDocumentRangeFormattingEdits(model, range, options, token) {
|
|
1586
|
+
return this.format(model, [range], options, "range", token);
|
|
1587
|
+
}
|
|
1588
|
+
/** @inheritdoc */
|
|
1589
|
+
provideDocumentRangesFormattingEdits(model, ranges, options, token) {
|
|
1590
|
+
return this.format(model, ranges, options, "range", token);
|
|
1591
|
+
}
|
|
1592
|
+
/** @inheritdoc */
|
|
1593
|
+
provideOnTypeFormattingEdits(model, position, ch, options, token) {
|
|
1594
|
+
return this.format(
|
|
1595
|
+
model,
|
|
1596
|
+
[Range.fromPositions(position, position)],
|
|
1597
|
+
options,
|
|
1598
|
+
ch === ";" ? "statement" : "expression",
|
|
1599
|
+
token
|
|
1600
|
+
);
|
|
1601
|
+
}
|
|
1602
|
+
};
|
|
1603
|
+
|
|
1604
|
+
// src/lsp/providers/hover-provider.ts
|
|
1605
|
+
import { DiagnosticCode as DiagnosticCode8 } from "@mirascript/wasm";
|
|
1606
|
+
var HoverProvider = class extends Provider {
|
|
1607
|
+
/** 变量提示 */
|
|
1608
|
+
async provideVariableHover(model, { def, ref }) {
|
|
1609
|
+
const contents = [];
|
|
1610
|
+
let range;
|
|
1611
|
+
if ("name" in def) {
|
|
1612
|
+
const globals = await this.getContext(model);
|
|
1613
|
+
const value = globals.has(def.name) ? globals.get(def.name) : void 0;
|
|
1614
|
+
const { script, doc } = valueDoc(def.name, value, "hint");
|
|
1615
|
+
contents.push({ value: codeblock(`\0(global) ${script}`) });
|
|
1616
|
+
for (const d of doc) {
|
|
1617
|
+
contents.push({ value: d });
|
|
1618
|
+
}
|
|
1619
|
+
range = def.references[ref]?.range;
|
|
1620
|
+
} else {
|
|
1621
|
+
let content;
|
|
1622
|
+
const tag = def.definition;
|
|
1623
|
+
switch (tag.code) {
|
|
1624
|
+
case DiagnosticCode8.ParameterSubPatternImmutable:
|
|
1625
|
+
content = {
|
|
1626
|
+
value: codeblock(`\0(parameter pattern) ${model.getValueInRange(tag.range)}`)
|
|
1627
|
+
};
|
|
1628
|
+
break;
|
|
1629
|
+
case DiagnosticCode8.ParameterSubPatternMutable:
|
|
1630
|
+
content = {
|
|
1631
|
+
value: codeblock(`\0(parameter pattern) mut ${model.getValueInRange(tag.range)}`)
|
|
1632
|
+
};
|
|
1633
|
+
break;
|
|
1634
|
+
case DiagnosticCode8.ParameterImmutable:
|
|
1635
|
+
content = {
|
|
1636
|
+
value: codeblock(`\0(parameter) ${model.getValueInRange(tag.range)}`)
|
|
1637
|
+
};
|
|
1638
|
+
break;
|
|
1639
|
+
case DiagnosticCode8.ParameterMutable:
|
|
1640
|
+
content = {
|
|
1641
|
+
value: codeblock(`\0(parameter) mut ${model.getValueInRange(tag.range)}`)
|
|
1642
|
+
};
|
|
1643
|
+
break;
|
|
1644
|
+
case DiagnosticCode8.ParameterIt:
|
|
1645
|
+
content = {
|
|
1646
|
+
value: codeblock(`\0(parameter) it`)
|
|
1647
|
+
};
|
|
1648
|
+
break;
|
|
1649
|
+
case DiagnosticCode8.ParameterImmutableRest:
|
|
1650
|
+
content = {
|
|
1651
|
+
value: codeblock(`\0(parameter) ..${model.getValueInRange(tag.range)}`)
|
|
1652
|
+
};
|
|
1653
|
+
break;
|
|
1654
|
+
case DiagnosticCode8.ParameterMutableRest:
|
|
1655
|
+
content = {
|
|
1656
|
+
value: codeblock(`\0(parameter) ..mut ${model.getValueInRange(tag.range)}`)
|
|
1657
|
+
};
|
|
1658
|
+
break;
|
|
1659
|
+
case DiagnosticCode8.LocalFunction: {
|
|
1660
|
+
const params = paramsList(model, def.fn);
|
|
1661
|
+
content = {
|
|
1662
|
+
value: codeblock(`\0fn ${model.getValueInRange(tag.range)}${params}`)
|
|
1663
|
+
};
|
|
1664
|
+
break;
|
|
1665
|
+
}
|
|
1666
|
+
case DiagnosticCode8.LocalImmutable:
|
|
1667
|
+
content = {
|
|
1668
|
+
value: codeblock(`\0let ${model.getValueInRange(tag.range)}`)
|
|
1669
|
+
};
|
|
1670
|
+
break;
|
|
1671
|
+
case DiagnosticCode8.LocalConst:
|
|
1672
|
+
content = {
|
|
1673
|
+
value: codeblock(`\0const ${model.getValueInRange(tag.range)}`)
|
|
1674
|
+
};
|
|
1675
|
+
break;
|
|
1676
|
+
case DiagnosticCode8.LocalMutable:
|
|
1677
|
+
content = {
|
|
1678
|
+
value: codeblock(`\0let mut ${model.getValueInRange(tag.range)}`)
|
|
1679
|
+
};
|
|
1680
|
+
break;
|
|
1681
|
+
}
|
|
1682
|
+
if (ref == null) {
|
|
1683
|
+
range = tag.range;
|
|
1684
|
+
} else {
|
|
1685
|
+
range = def.references[ref]?.range;
|
|
1686
|
+
}
|
|
1687
|
+
if (content) {
|
|
1688
|
+
contents.push(content);
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
if (!contents.length) return void 0;
|
|
1692
|
+
return {
|
|
1693
|
+
contents,
|
|
1694
|
+
range
|
|
1695
|
+
};
|
|
1696
|
+
}
|
|
1697
|
+
/** 字段提示 */
|
|
1698
|
+
async provideFieldHover(model, range, { def: { def, ref }, fields }) {
|
|
1699
|
+
if ("definition" in def) {
|
|
1700
|
+
return void 0;
|
|
1701
|
+
}
|
|
1702
|
+
const vmGlobal = await this.getContext(model);
|
|
1703
|
+
const value = getDeep(vmGlobal.get(def.name), fields);
|
|
1704
|
+
if (value == null) return void 0;
|
|
1705
|
+
const lastField = fields.pop();
|
|
1706
|
+
const { script, doc } = valueDoc(lastField, value, "field");
|
|
1707
|
+
return {
|
|
1708
|
+
contents: [{ value: codeblock(`\0(field) ${script}`) }, ...doc.map((d) => ({ value: d }))],
|
|
1709
|
+
range
|
|
1710
|
+
};
|
|
1711
|
+
}
|
|
1712
|
+
/** @inheritdoc */
|
|
1713
|
+
async provideHover(model, position, token, context) {
|
|
1714
|
+
const compiled = await this.getCompileResult(model);
|
|
1715
|
+
if (!compiled) {
|
|
1716
|
+
return void 0;
|
|
1717
|
+
}
|
|
1718
|
+
const d = compiled.variableAccessAt(model, position);
|
|
1719
|
+
if (d) {
|
|
1720
|
+
return this.provideVariableHover(model, d);
|
|
1721
|
+
}
|
|
1722
|
+
const word = wordAt(model, position);
|
|
1723
|
+
if (word) {
|
|
1724
|
+
const a = compiled.fieldAccessAt(model, {
|
|
1725
|
+
lineNumber: position.lineNumber,
|
|
1726
|
+
column: word.range.endColumn
|
|
1727
|
+
});
|
|
1728
|
+
if (a) {
|
|
1729
|
+
return this.provideFieldHover(model, word.range, a);
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
return void 0;
|
|
1733
|
+
}
|
|
1734
|
+
};
|
|
1735
|
+
|
|
1736
|
+
// src/lsp/providers/inlay-hints-provider.ts
|
|
1737
|
+
import { DiagnosticCode as DiagnosticCode9 } from "@mirascript/wasm";
|
|
1738
|
+
var InlayHintsProvider = class extends Provider {
|
|
1739
|
+
/** @inheritdoc */
|
|
1740
|
+
get onDidChangeInlayHints() {
|
|
1741
|
+
return this.onDidChange;
|
|
1742
|
+
}
|
|
1743
|
+
/** @inheritdoc */
|
|
1744
|
+
async provideInlayHints(model, range, token) {
|
|
1745
|
+
const compiled = await this.getCompileResult(model);
|
|
1746
|
+
if (!compiled) {
|
|
1747
|
+
return void 0;
|
|
1748
|
+
}
|
|
1749
|
+
const hints = [];
|
|
1750
|
+
for (const tag of compiled.tags) {
|
|
1751
|
+
if (!range.containsRange(tag.range)) {
|
|
1752
|
+
continue;
|
|
1753
|
+
}
|
|
1754
|
+
let lineNumber = 0;
|
|
1755
|
+
let column = 0;
|
|
1756
|
+
let label = "";
|
|
1757
|
+
const kind = languages.InlayHintKind.Parameter;
|
|
1758
|
+
let paddingLeft = true;
|
|
1759
|
+
let paddingRight = false;
|
|
1760
|
+
const edits = [];
|
|
1761
|
+
switch (tag.code) {
|
|
1762
|
+
case DiagnosticCode9.ParameterIt: {
|
|
1763
|
+
if (!tag.references.length) {
|
|
1764
|
+
continue;
|
|
1765
|
+
}
|
|
1766
|
+
lineNumber = tag.range.endLineNumber;
|
|
1767
|
+
column = tag.range.endColumn;
|
|
1768
|
+
label = "(it)";
|
|
1769
|
+
paddingLeft = /\bfn$/u.test(
|
|
1770
|
+
model.getValueInRange(new Range(lineNumber, column - 3, lineNumber, column))
|
|
1771
|
+
);
|
|
1772
|
+
paddingRight = model.getValueInRange(new Range(lineNumber, column, lineNumber, column + 1)) === "{";
|
|
1773
|
+
edits.push({
|
|
1774
|
+
range: new Range(lineNumber, column, lineNumber, column),
|
|
1775
|
+
text: `${paddingLeft ? " " : ""}${label}${paddingRight ? " " : ""}`
|
|
1776
|
+
});
|
|
1777
|
+
break;
|
|
1778
|
+
}
|
|
1779
|
+
case DiagnosticCode9.UnnamedRecordField0:
|
|
1780
|
+
case DiagnosticCode9.UnnamedRecordField1:
|
|
1781
|
+
case DiagnosticCode9.UnnamedRecordField2:
|
|
1782
|
+
case DiagnosticCode9.UnnamedRecordField3:
|
|
1783
|
+
case DiagnosticCode9.UnnamedRecordField4:
|
|
1784
|
+
case DiagnosticCode9.UnnamedRecordField5:
|
|
1785
|
+
case DiagnosticCode9.UnnamedRecordField6:
|
|
1786
|
+
case DiagnosticCode9.UnnamedRecordField7:
|
|
1787
|
+
case DiagnosticCode9.UnnamedRecordField8:
|
|
1788
|
+
case DiagnosticCode9.UnnamedRecordField9:
|
|
1789
|
+
case DiagnosticCode9.UnnamedRecordFieldN: {
|
|
1790
|
+
const index = tag.code - DiagnosticCode9.UnnamedRecordField0;
|
|
1791
|
+
if (index > 9) break;
|
|
1792
|
+
lineNumber = tag.range.startLineNumber;
|
|
1793
|
+
column = tag.range.startColumn;
|
|
1794
|
+
label = `${index}:`;
|
|
1795
|
+
paddingLeft = /[^\s(]/u.test(
|
|
1796
|
+
model.getValueInRange(new Range(lineNumber, column - 1, lineNumber, column))
|
|
1797
|
+
);
|
|
1798
|
+
paddingRight = true;
|
|
1799
|
+
break;
|
|
1800
|
+
}
|
|
1801
|
+
case DiagnosticCode9.OmitNamedRecordField: {
|
|
1802
|
+
const ref = tag.references[0];
|
|
1803
|
+
if (ref?.code !== DiagnosticCode9.OmitNamedRecordFieldName) continue;
|
|
1804
|
+
lineNumber = tag.range.startLineNumber;
|
|
1805
|
+
column = tag.range.startColumn;
|
|
1806
|
+
label = model.getValueInRange(ref.range);
|
|
1807
|
+
paddingLeft = /[^\s(]/u.test(
|
|
1808
|
+
model.getValueInRange(new Range(lineNumber, column - 1, lineNumber, column))
|
|
1809
|
+
);
|
|
1810
|
+
paddingRight = false;
|
|
1811
|
+
const insertRight = !/\s/u.test(
|
|
1812
|
+
model.getValueInRange(
|
|
1813
|
+
new Range(
|
|
1814
|
+
tag.range.endLineNumber,
|
|
1815
|
+
tag.range.endColumn,
|
|
1816
|
+
tag.range.endLineNumber,
|
|
1817
|
+
tag.range.endColumn + 1
|
|
1818
|
+
)
|
|
1819
|
+
)
|
|
1820
|
+
);
|
|
1821
|
+
edits.push({
|
|
1822
|
+
range: tag.range,
|
|
1823
|
+
text: `${paddingLeft ? " " : ""}${label}${model.getValueInRange(tag.range)}${insertRight ? " " : ""}`
|
|
1824
|
+
});
|
|
1825
|
+
break;
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
if (!label) continue;
|
|
1829
|
+
const hint = {
|
|
1830
|
+
position: { lineNumber, column },
|
|
1831
|
+
label,
|
|
1832
|
+
kind,
|
|
1833
|
+
paddingLeft,
|
|
1834
|
+
paddingRight,
|
|
1835
|
+
textEdits: edits
|
|
1836
|
+
};
|
|
1837
|
+
hints.push(hint);
|
|
1838
|
+
}
|
|
1839
|
+
return {
|
|
1840
|
+
hints,
|
|
1841
|
+
dispose: () => {
|
|
1842
|
+
}
|
|
1843
|
+
};
|
|
1844
|
+
}
|
|
1845
|
+
};
|
|
1846
|
+
|
|
1847
|
+
// src/lsp/providers/range-provider.ts
|
|
1848
|
+
var RangeProvider = class extends Provider {
|
|
1849
|
+
/** @inheritdoc */
|
|
1850
|
+
async provideFoldingRanges(model, context, token) {
|
|
1851
|
+
const compiled = await this.getCompileResult(model);
|
|
1852
|
+
if (!compiled) return void 0;
|
|
1853
|
+
return void 0;
|
|
1854
|
+
}
|
|
1855
|
+
/** @inheritdoc */
|
|
1856
|
+
async provideSelectionRanges(model, positions, token) {
|
|
1857
|
+
const compiled = await this.getCompileResult(model);
|
|
1858
|
+
if (!compiled) return void 0;
|
|
1859
|
+
const { ranges } = compiled.groupedTags(model);
|
|
1860
|
+
return positions.map((pos) => {
|
|
1861
|
+
return ranges.filter((r) => Range.containsPosition(r.range, pos)).map((t) => ({
|
|
1862
|
+
range: t.range
|
|
1863
|
+
}));
|
|
1864
|
+
});
|
|
1865
|
+
}
|
|
1866
|
+
};
|
|
1867
|
+
|
|
1868
|
+
// src/lsp/providers/rename-provider.ts
|
|
1869
|
+
import { DiagnosticCode as DiagnosticCode10 } from "@mirascript/wasm";
|
|
1870
|
+
var RenameProvider = class extends Provider {
|
|
1871
|
+
/** 重命名推断字段 */
|
|
1872
|
+
provideRenameEditsOmitNameFields(model, compiled, edits, ref, oldName) {
|
|
1873
|
+
const { omitNameFields } = compiled.groupedTags(model);
|
|
1874
|
+
for (const tag of omitNameFields) {
|
|
1875
|
+
if (Range.equalsRange(tag.references[0]?.range, ref.range)) {
|
|
1876
|
+
const current = model.getValueInRange(tag.range);
|
|
1877
|
+
const paddingLeft = /[^\s(]/u.test(
|
|
1878
|
+
model.getValueInRange({
|
|
1879
|
+
startLineNumber: tag.range.startLineNumber,
|
|
1880
|
+
startColumn: tag.range.startColumn - 1,
|
|
1881
|
+
endLineNumber: tag.range.startLineNumber,
|
|
1882
|
+
endColumn: tag.range.startColumn
|
|
1883
|
+
})
|
|
1884
|
+
);
|
|
1885
|
+
const paddingRight = !/\s/u.test(
|
|
1886
|
+
model.getValueInRange({
|
|
1887
|
+
startLineNumber: tag.range.endLineNumber,
|
|
1888
|
+
startColumn: tag.range.endColumn,
|
|
1889
|
+
endLineNumber: tag.range.endLineNumber,
|
|
1890
|
+
endColumn: tag.range.endColumn + 1
|
|
1891
|
+
})
|
|
1892
|
+
);
|
|
1893
|
+
edits.push({
|
|
1894
|
+
resource: model.uri,
|
|
1895
|
+
versionId: compiled.version,
|
|
1896
|
+
textEdit: {
|
|
1897
|
+
range: tag.range,
|
|
1898
|
+
text: `${paddingLeft ? " " : ""}${oldName}${current}${paddingRight ? " " : ""}`
|
|
1899
|
+
}
|
|
1900
|
+
});
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
/** @inheritdoc */
|
|
1905
|
+
async provideRenameEdits(model, position, newName, token) {
|
|
1906
|
+
newName = newName.trim();
|
|
1907
|
+
const compiled = await this.getCompileResult(model);
|
|
1908
|
+
if (!compiled) return void 0;
|
|
1909
|
+
const d = compiled.variableAccessAt(model, position);
|
|
1910
|
+
if (!d) return void 0;
|
|
1911
|
+
const edits = [];
|
|
1912
|
+
const { references } = d.def;
|
|
1913
|
+
let oldName;
|
|
1914
|
+
if ("definition" in d.def) {
|
|
1915
|
+
edits.push({
|
|
1916
|
+
resource: model.uri,
|
|
1917
|
+
versionId: compiled.version,
|
|
1918
|
+
textEdit: {
|
|
1919
|
+
range: d.def.definition.range,
|
|
1920
|
+
text: d.def.definition.code === DiagnosticCode10.ParameterIt ? `(${newName})` : newName
|
|
1921
|
+
}
|
|
1922
|
+
});
|
|
1923
|
+
oldName = model.getValueInRange(d.def.definition.range);
|
|
1924
|
+
this.provideRenameEditsOmitNameFields(model, compiled, edits, d.def.definition, oldName);
|
|
1925
|
+
} else {
|
|
1926
|
+
oldName = d.def.name;
|
|
1927
|
+
}
|
|
1928
|
+
for (const ref of references) {
|
|
1929
|
+
edits.push({
|
|
1930
|
+
resource: model.uri,
|
|
1931
|
+
versionId: compiled.version,
|
|
1932
|
+
textEdit: {
|
|
1933
|
+
range: ref.range,
|
|
1934
|
+
text: newName
|
|
1935
|
+
}
|
|
1936
|
+
});
|
|
1937
|
+
this.provideRenameEditsOmitNameFields(model, compiled, edits, ref, oldName);
|
|
1938
|
+
}
|
|
1939
|
+
const { globals } = compiled.groupedTags(model);
|
|
1940
|
+
const globalWithSameName = globals.find((g) => g.name === newName);
|
|
1941
|
+
if (globalWithSameName) {
|
|
1942
|
+
for (const ref of globalWithSameName.references) {
|
|
1943
|
+
edits.push({
|
|
1944
|
+
resource: model.uri,
|
|
1945
|
+
versionId: compiled.version,
|
|
1946
|
+
textEdit: {
|
|
1947
|
+
range: ref.range,
|
|
1948
|
+
text: `global.${newName}`
|
|
1949
|
+
}
|
|
1950
|
+
});
|
|
1951
|
+
this.provideRenameEditsOmitNameFields(model, compiled, edits, ref, oldName);
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
return { edits };
|
|
1955
|
+
}
|
|
1956
|
+
/** @inheritdoc */
|
|
1957
|
+
async resolveRenameLocation(model, position, token) {
|
|
1958
|
+
const compiled = await this.getCompileResult(model);
|
|
1959
|
+
if (!compiled) return void 0;
|
|
1960
|
+
const d = compiled.variableAccessAt(model, position);
|
|
1961
|
+
if (!d) {
|
|
1962
|
+
return {
|
|
1963
|
+
range: Range.fromPositions(position),
|
|
1964
|
+
text: "",
|
|
1965
|
+
rejectReason: "Cannot rename this element"
|
|
1966
|
+
};
|
|
1967
|
+
}
|
|
1968
|
+
const { def, ref } = d;
|
|
1969
|
+
if ("name" in def) {
|
|
1970
|
+
return {
|
|
1971
|
+
range: def.references[ref]?.range ?? Range.fromPositions(position),
|
|
1972
|
+
text: def.name,
|
|
1973
|
+
rejectReason: "Cannot rename global variables"
|
|
1974
|
+
};
|
|
1975
|
+
}
|
|
1976
|
+
const tag = ref == null ? def.definition : def.references[ref];
|
|
1977
|
+
return {
|
|
1978
|
+
range: tag.range,
|
|
1979
|
+
text: model.getValueInRange(tag.range) ?? ""
|
|
1980
|
+
};
|
|
1981
|
+
}
|
|
1982
|
+
};
|
|
1983
|
+
|
|
1984
|
+
// src/lsp/providers/semantic-tokens-provider.ts
|
|
1985
|
+
import { isVmFunction as isVmFunction2, isVmModule as isVmModule3 } from "@mirascript/mirascript";
|
|
1986
|
+
import { DiagnosticCode as DiagnosticCode11 } from "@mirascript/wasm";
|
|
1987
|
+
var TOKEN_TYPES = {
|
|
1988
|
+
[0 /* VARIABLE */]: "variable.other.constant",
|
|
1989
|
+
[1 /* VARIABLE_MUTABLE */]: "variable",
|
|
1990
|
+
[2 /* CONSTANT */]: "variable.other.constant",
|
|
1991
|
+
[3 /* GLOBAL */]: "variable",
|
|
1992
|
+
[4 /* FUNCTION */]: "entity.name.function",
|
|
1993
|
+
[5 /* MODULE */]: "entity.name.namespace",
|
|
1994
|
+
[6 /* PROPERTY */]: "support.type.property-name",
|
|
1995
|
+
[7 /* KEYWORD_CONTROL */]: "keyword.control",
|
|
1996
|
+
[8 /* PARAM */]: "variable.other.constant.emphasis",
|
|
1997
|
+
[9 /* PARAM_MUTABLE */]: "variable.emphasis"
|
|
1998
|
+
};
|
|
1999
|
+
var DocumentSemanticTokensProvider = class extends Provider {
|
|
2000
|
+
/** @inheritdoc */
|
|
2001
|
+
getLegend() {
|
|
2002
|
+
const tokenTypes = Object.values(TOKEN_TYPES);
|
|
2003
|
+
return {
|
|
2004
|
+
tokenTypes,
|
|
2005
|
+
tokenModifiers: tokenTypes.map((_) => "")
|
|
2006
|
+
};
|
|
2007
|
+
}
|
|
2008
|
+
/** @inheritdoc */
|
|
2009
|
+
async provideDocumentSemanticTokens(model, lastResultId, token) {
|
|
2010
|
+
const resultId = `${model.uri.toString()}?${model.getVersionId()}`;
|
|
2011
|
+
const compiled = await this.getCompileResult(model);
|
|
2012
|
+
if (!compiled) {
|
|
2013
|
+
return void 0;
|
|
2014
|
+
}
|
|
2015
|
+
const globals = await this.getContext(model);
|
|
2016
|
+
const data = [];
|
|
2017
|
+
for (const { code, range, references } of compiled.tags) {
|
|
2018
|
+
if (Range.isEmpty(range)) continue;
|
|
2019
|
+
let tokenType = -1;
|
|
2020
|
+
let onlyReferences = false;
|
|
2021
|
+
switch (code) {
|
|
2022
|
+
case DiagnosticCode11.GlobalVariable: {
|
|
2023
|
+
const id = model.getValueInRange(range);
|
|
2024
|
+
if (id.startsWith("@")) {
|
|
2025
|
+
tokenType = 2 /* CONSTANT */;
|
|
2026
|
+
} else if (isVmFunction2(globals.get(id))) {
|
|
2027
|
+
tokenType = 4 /* FUNCTION */;
|
|
2028
|
+
} else if (isVmModule3(globals.get(id))) {
|
|
2029
|
+
tokenType = 5 /* MODULE */;
|
|
2030
|
+
} else {
|
|
2031
|
+
tokenType = 3 /* GLOBAL */;
|
|
2032
|
+
}
|
|
2033
|
+
break;
|
|
2034
|
+
}
|
|
2035
|
+
case DiagnosticCode11.LocalFunction: {
|
|
2036
|
+
tokenType = 4 /* FUNCTION */;
|
|
2037
|
+
break;
|
|
2038
|
+
}
|
|
2039
|
+
case DiagnosticCode11.ParameterMutable:
|
|
2040
|
+
case DiagnosticCode11.ParameterSubPatternMutable:
|
|
2041
|
+
case DiagnosticCode11.ParameterMutableRest: {
|
|
2042
|
+
tokenType = 9 /* PARAM_MUTABLE */;
|
|
2043
|
+
break;
|
|
2044
|
+
}
|
|
2045
|
+
case DiagnosticCode11.LocalMutable: {
|
|
2046
|
+
tokenType = 1 /* VARIABLE_MUTABLE */;
|
|
2047
|
+
break;
|
|
2048
|
+
}
|
|
2049
|
+
case DiagnosticCode11.ParameterImmutable:
|
|
2050
|
+
case DiagnosticCode11.ParameterSubPatternImmutable:
|
|
2051
|
+
case DiagnosticCode11.ParameterImmutableRest: {
|
|
2052
|
+
tokenType = 8 /* PARAM */;
|
|
2053
|
+
break;
|
|
2054
|
+
}
|
|
2055
|
+
case DiagnosticCode11.LocalImmutable: {
|
|
2056
|
+
tokenType = 0 /* VARIABLE */;
|
|
2057
|
+
break;
|
|
2058
|
+
}
|
|
2059
|
+
case DiagnosticCode11.LocalConst: {
|
|
2060
|
+
tokenType = 2 /* CONSTANT */;
|
|
2061
|
+
break;
|
|
2062
|
+
}
|
|
2063
|
+
case DiagnosticCode11.RecordFieldIdName: {
|
|
2064
|
+
tokenType = 6 /* PROPERTY */;
|
|
2065
|
+
break;
|
|
2066
|
+
}
|
|
2067
|
+
case DiagnosticCode11.ForExpression: {
|
|
2068
|
+
tokenType = 7 /* KEYWORD_CONTROL */;
|
|
2069
|
+
onlyReferences = true;
|
|
2070
|
+
break;
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
if (tokenType < 0) continue;
|
|
2074
|
+
const { startLineNumber, startColumn, endColumn } = range;
|
|
2075
|
+
const length = endColumn - startColumn;
|
|
2076
|
+
if (!onlyReferences) {
|
|
2077
|
+
data.push({
|
|
2078
|
+
row: startLineNumber - 1,
|
|
2079
|
+
// 从 0 开始
|
|
2080
|
+
col: startColumn - 1,
|
|
2081
|
+
length,
|
|
2082
|
+
tokenType,
|
|
2083
|
+
tokenModifiers: 1 << tokenType
|
|
2084
|
+
});
|
|
2085
|
+
}
|
|
2086
|
+
for (const ref of references) {
|
|
2087
|
+
data.push({
|
|
2088
|
+
row: ref.range.startLineNumber - 1,
|
|
2089
|
+
col: ref.range.startColumn - 1,
|
|
2090
|
+
length: ref.range.endColumn - ref.range.startColumn,
|
|
2091
|
+
tokenType,
|
|
2092
|
+
tokenModifiers: 1 << tokenType
|
|
2093
|
+
});
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
data.sort((a, b) => {
|
|
2097
|
+
if (a.row !== b.row) {
|
|
2098
|
+
return a.row - b.row;
|
|
2099
|
+
}
|
|
2100
|
+
return a.col - b.col;
|
|
2101
|
+
});
|
|
2102
|
+
const bin = new Uint32Array(data.length * 5);
|
|
2103
|
+
for (let i = 0; i < data.length; i++) {
|
|
2104
|
+
const current = data[i];
|
|
2105
|
+
let { row, col } = current;
|
|
2106
|
+
if (i > 0) {
|
|
2107
|
+
const prev = data[i - 1];
|
|
2108
|
+
row -= prev.row;
|
|
2109
|
+
if (row === 0) {
|
|
2110
|
+
col -= prev.col;
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
bin.set([row, col, current.length, current.tokenType, current.tokenModifiers], i * 5);
|
|
2114
|
+
}
|
|
2115
|
+
return {
|
|
2116
|
+
resultId,
|
|
2117
|
+
data: bin
|
|
2118
|
+
};
|
|
2119
|
+
}
|
|
2120
|
+
/** @inheritdoc */
|
|
2121
|
+
releaseDocumentSemanticTokens(resultId) {
|
|
2122
|
+
}
|
|
2123
|
+
};
|
|
2124
|
+
|
|
2125
|
+
// src/lsp/providers/signature-help-provider.ts
|
|
2126
|
+
import { DiagnosticCode as DiagnosticCode12 } from "@mirascript/wasm";
|
|
2127
|
+
import { getVmFunctionInfo as getVmFunctionInfo3 } from "@mirascript/mirascript";
|
|
2128
|
+
var SignatureHelpProvider = class extends Provider {
|
|
2129
|
+
constructor() {
|
|
2130
|
+
super(...arguments);
|
|
2131
|
+
/** @inheritdoc */
|
|
2132
|
+
this.signatureHelpTriggerCharacters = ["(", ","];
|
|
2133
|
+
/** @inheritdoc */
|
|
2134
|
+
this.signatureHelpRetriggerCharacters = [")"];
|
|
2135
|
+
}
|
|
2136
|
+
/** @inheritdoc */
|
|
2137
|
+
async provideSignatureHelp(model, position, token, context) {
|
|
2138
|
+
const compiled = await this.getCompileResult(model);
|
|
2139
|
+
if (!compiled) return void 0;
|
|
2140
|
+
const invokes = compiled.groupedTags(model).ranges.filter((r) => {
|
|
2141
|
+
if (r.code !== DiagnosticCode12.FunctionCall && r.code !== DiagnosticCode12.ExtensionCall) {
|
|
2142
|
+
return false;
|
|
2143
|
+
}
|
|
2144
|
+
if (!strictContainsPosition(r.range, position)) {
|
|
2145
|
+
return false;
|
|
2146
|
+
}
|
|
2147
|
+
const argStart = r.references.find((ref) => ref.code === DiagnosticCode12.ArgumentStart);
|
|
2148
|
+
const argEnd = r.references.find((ref) => ref.code === DiagnosticCode12.ArgumentEnd);
|
|
2149
|
+
if (!argStart || !argEnd) {
|
|
2150
|
+
return false;
|
|
2151
|
+
}
|
|
2152
|
+
return Range.containsPosition(
|
|
2153
|
+
Range.fromPositions(Range.getEndPosition(argStart.range), Range.getStartPosition(argEnd.range)),
|
|
2154
|
+
position
|
|
2155
|
+
);
|
|
2156
|
+
});
|
|
2157
|
+
if (!invokes.length) return void 0;
|
|
2158
|
+
invokes.sort((a, b) => Range.strictContainsRange(a.range, b.range) ? 1 : -1);
|
|
2159
|
+
const invoke = invokes[0];
|
|
2160
|
+
const callableRef = invoke.references.find((ref) => ref.code === DiagnosticCode12.Callable);
|
|
2161
|
+
if (!callableRef) return void 0;
|
|
2162
|
+
const callableInfo = compiled.accessAt(model, Range.getEndPosition(callableRef.range));
|
|
2163
|
+
if (!callableInfo) return void 0;
|
|
2164
|
+
let sig;
|
|
2165
|
+
if ("name" in callableInfo.def.def) {
|
|
2166
|
+
const { name } = callableInfo.def.def;
|
|
2167
|
+
const globals = await this.getContext(model);
|
|
2168
|
+
const callable = getDeep(globals.get(name), callableInfo.fields);
|
|
2169
|
+
const info = getVmFunctionInfo3(callable);
|
|
2170
|
+
if (!info) return void 0;
|
|
2171
|
+
sig = { ...fnSignature(name, info), name, summary: info.summary };
|
|
2172
|
+
} else if (callableInfo.def.def.fn) {
|
|
2173
|
+
const { fn, definition } = callableInfo.def.def;
|
|
2174
|
+
const params = localParamSignature(model, fn);
|
|
2175
|
+
const name = model.getValueInRange(definition.range);
|
|
2176
|
+
sig = { params, returns: "", name, summary: "" };
|
|
2177
|
+
}
|
|
2178
|
+
if (!sig) return void 0;
|
|
2179
|
+
const signature = {
|
|
2180
|
+
label: "",
|
|
2181
|
+
parameters: []
|
|
2182
|
+
};
|
|
2183
|
+
if (invoke.code === DiagnosticCode12.ExtensionCall) {
|
|
2184
|
+
const thisArg = sig.params[0];
|
|
2185
|
+
if (thisArg && !thisArg[0].startsWith("..")) {
|
|
2186
|
+
sig.params.shift();
|
|
2187
|
+
const s = thisArg[1].includes(" ") ? `(${thisArg[1]})` : thisArg[1];
|
|
2188
|
+
signature.label = `fn ${s}::${sig.name}(`;
|
|
2189
|
+
} else {
|
|
2190
|
+
signature.label = `fn ()::${sig.name}(`;
|
|
2191
|
+
}
|
|
2192
|
+
} else {
|
|
2193
|
+
signature.label = `fn ${sig.name}(`;
|
|
2194
|
+
}
|
|
2195
|
+
signature.documentation = { value: sig.summary ?? "" };
|
|
2196
|
+
for (let i = 0; i < sig.params.length; i++) {
|
|
2197
|
+
const [_, p, doc] = sig.params[i];
|
|
2198
|
+
const start = signature.label.length;
|
|
2199
|
+
signature.parameters.push({
|
|
2200
|
+
label: [start, start + p.length],
|
|
2201
|
+
documentation: { value: doc }
|
|
2202
|
+
});
|
|
2203
|
+
if (i === sig.params.length - 1) {
|
|
2204
|
+
signature.label += p;
|
|
2205
|
+
} else {
|
|
2206
|
+
signature.label += p + ", ";
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
signature.label += ")" + sig.returns;
|
|
2210
|
+
let pos = 0;
|
|
2211
|
+
for (const ref of invoke.references) {
|
|
2212
|
+
if (ref.code === DiagnosticCode12.ArgumentSpread) pos = Number.NaN;
|
|
2213
|
+
if (ref.code !== DiagnosticCode12.ArgumentComma || Range.isEmpty(ref.range)) continue;
|
|
2214
|
+
if (Position.isBeforeOrEqual(Range.getEndPosition(ref.range), position) && !sig.params[pos]?.[0].startsWith("..")) {
|
|
2215
|
+
pos++;
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
return {
|
|
2219
|
+
dispose: () => void 0,
|
|
2220
|
+
value: {
|
|
2221
|
+
signatures: [signature],
|
|
2222
|
+
activeSignature: 0,
|
|
2223
|
+
activeParameter: pos
|
|
2224
|
+
}
|
|
2225
|
+
};
|
|
2226
|
+
}
|
|
2227
|
+
};
|
|
2228
|
+
|
|
2229
|
+
// src/lsp/index.ts
|
|
2230
|
+
import { ready } from "@mirascript/wasm";
|
|
2231
|
+
async function registerLSP(contextProvider2) {
|
|
2232
|
+
setContextProvider(contextProvider2);
|
|
2233
|
+
await ready;
|
|
2234
|
+
const codeActionProvider = new CodeActionProvider();
|
|
2235
|
+
const colorProvider = new ColorProvider();
|
|
2236
|
+
const completionItemProvider = new CompletionItemProvider();
|
|
2237
|
+
const definitionReferenceProvider = new DefinitionReferenceProvider();
|
|
2238
|
+
const documentHighlightProvider = new DocumentHighlightProvider();
|
|
2239
|
+
const documentSymbolProvider = new DocumentSymbolProvider();
|
|
2240
|
+
const formatterProvider = new FormatterProvider();
|
|
2241
|
+
const hoverProvider = new HoverProvider();
|
|
2242
|
+
const inlayHintsProvider = new InlayHintsProvider();
|
|
2243
|
+
const rangeProvider = new RangeProvider();
|
|
2244
|
+
const renameProvider = new RenameProvider();
|
|
2245
|
+
const documentSemanticTokensProvider = new DocumentSemanticTokensProvider();
|
|
2246
|
+
const signatureHelpProvider = new SignatureHelpProvider();
|
|
2247
|
+
const language = ["mirascript", "mirascript-template"];
|
|
2248
|
+
return [
|
|
2249
|
+
languages.registerCodeActionProvider(language, codeActionProvider),
|
|
2250
|
+
languages.registerColorProvider(language, colorProvider),
|
|
2251
|
+
languages.registerDefinitionProvider(language, definitionReferenceProvider),
|
|
2252
|
+
languages.registerReferenceProvider(language, definitionReferenceProvider),
|
|
2253
|
+
languages.registerDocumentHighlightProvider(language, documentHighlightProvider),
|
|
2254
|
+
languages.registerDocumentSymbolProvider(language, documentSymbolProvider),
|
|
2255
|
+
languages.registerDocumentFormattingEditProvider(language, formatterProvider),
|
|
2256
|
+
// languages.registerDocumentRangeFormattingEditProvider(language, formatterProvider),
|
|
2257
|
+
// languages.registerOnTypeFormattingEditProvider(language, formatterProvider),
|
|
2258
|
+
languages.registerHoverProvider(language, hoverProvider),
|
|
2259
|
+
languages.registerInlayHintsProvider(language, inlayHintsProvider),
|
|
2260
|
+
languages.registerFoldingRangeProvider(language, rangeProvider),
|
|
2261
|
+
languages.registerSelectionRangeProvider(language, rangeProvider),
|
|
2262
|
+
languages.registerDocumentSemanticTokensProvider(language, documentSemanticTokensProvider),
|
|
2263
|
+
languages.registerRenameProvider(language, renameProvider),
|
|
2264
|
+
languages.registerCompletionItemProvider(language, completionItemProvider),
|
|
2265
|
+
languages.registerSignatureHelpProvider(language, signatureHelpProvider)
|
|
2266
|
+
];
|
|
2267
|
+
}
|
|
2268
|
+
export {
|
|
2269
|
+
CodeActionProvider,
|
|
2270
|
+
ColorProvider,
|
|
2271
|
+
CompletionItemProvider,
|
|
2272
|
+
DefinitionReferenceProvider,
|
|
2273
|
+
DocumentHighlightProvider,
|
|
2274
|
+
DocumentSemanticTokensProvider,
|
|
2275
|
+
DocumentSymbolProvider,
|
|
2276
|
+
FormatterProvider,
|
|
2277
|
+
HoverProvider,
|
|
2278
|
+
InlayHintsProvider,
|
|
2279
|
+
RangeProvider,
|
|
2280
|
+
RenameProvider,
|
|
2281
|
+
SignatureHelpProvider,
|
|
2282
|
+
registerLSP,
|
|
2283
|
+
setContextProvider
|
|
2284
|
+
};
|
|
2285
|
+
//# sourceMappingURL=index.js.map
|