@mirascript/monaco 0.1.87 → 0.1.88
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/highlighter-manager.d.ts +7 -0
- package/dist/basic/highlighter-manager.d.ts.map +1 -0
- package/dist/basic/index.js +2 -2
- package/dist/basic/language-configuration.d.ts.map +1 -1
- package/dist/basic/textmate-to-monaco.d.ts +3 -0
- package/dist/basic/textmate-to-monaco.d.ts.map +1 -0
- package/dist/basic/tokens-provider.d.ts.map +1 -1
- package/dist/{chunk-JOHUNWDB.js → chunk-HULIQQS3.js} +6 -2
- package/dist/{chunk-JOHUNWDB.js.map → chunk-HULIQQS3.js.map} +1 -1
- package/dist/{chunk-6QSF4YTT.js → chunk-NW3ZHO3C.js} +70 -91
- package/dist/chunk-NW3ZHO3C.js.map +6 -0
- package/dist/contribute.d.ts +5 -1
- package/dist/contribute.d.ts.map +1 -1
- package/dist/index.js +7 -6
- package/dist/index.js.map +1 -1
- package/dist/lsp/index.d.ts +1 -1
- package/dist/lsp/index.d.ts.map +1 -1
- package/dist/lsp/index.js +252 -241
- package/dist/lsp/index.js.map +3 -3
- package/dist/lsp/providers/completion-item-provider/common-global-suggestions.d.ts +5 -0
- package/dist/lsp/providers/completion-item-provider/common-global-suggestions.d.ts.map +1 -0
- package/dist/lsp/providers/completion-item-provider/complete-fields.d.ts +5 -0
- package/dist/lsp/providers/completion-item-provider/complete-fields.d.ts.map +1 -0
- package/dist/lsp/providers/completion-item-provider/complete-global.d.ts +5 -0
- package/dist/lsp/providers/completion-item-provider/complete-global.d.ts.map +1 -0
- package/dist/lsp/providers/completion-item-provider/complete-local.d.ts +5 -0
- package/dist/lsp/providers/completion-item-provider/complete-local.d.ts.map +1 -0
- package/dist/lsp/providers/{completion-item-provider.d.ts → completion-item-provider/index.d.ts} +3 -11
- package/dist/lsp/providers/completion-item-provider/index.d.ts.map +1 -0
- package/dist/lsp/providers/completion-item-provider/interface.d.ts +16 -0
- package/dist/lsp/providers/completion-item-provider/interface.d.ts.map +1 -0
- package/dist/lsp/providers/completion-item-provider/utils.d.ts +11 -0
- package/dist/lsp/providers/completion-item-provider/utils.d.ts.map +1 -0
- package/dist/lsp/utils.d.ts +0 -2
- package/dist/lsp/utils.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/basic/highlighter-manager.ts +38 -0
- package/src/basic/language-configuration.ts +16 -13
- package/src/basic/textmate-to-monaco.ts +49 -0
- package/src/basic/tokens-provider.ts +18 -127
- package/src/contribute.ts +5 -1
- package/src/index.ts +6 -6
- package/src/lsp/index.ts +1 -1
- package/src/lsp/providers/completion-item-provider/common-global-suggestions.ts +155 -0
- package/src/lsp/providers/completion-item-provider/complete-fields.ts +64 -0
- package/src/lsp/providers/completion-item-provider/complete-global.ts +82 -0
- package/src/lsp/providers/completion-item-provider/complete-local.ts +57 -0
- package/src/lsp/providers/completion-item-provider/index.ts +141 -0
- package/src/lsp/providers/completion-item-provider/interface.ts +17 -0
- package/src/lsp/providers/completion-item-provider/utils.ts +61 -0
- package/src/lsp/utils.ts +0 -13
- package/dist/chunk-6QSF4YTT.js.map +0 -6
- package/dist/lsp/providers/completion-item-provider.d.ts.map +0 -1
- package/src/lsp/providers/completion-item-provider.ts +0 -536
|
@@ -1,536 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getVmFunctionInfo,
|
|
3
|
-
type VmValue,
|
|
4
|
-
isVmExtern,
|
|
5
|
-
isVmModule,
|
|
6
|
-
type VmFunctionInfo,
|
|
7
|
-
type VmExtern,
|
|
8
|
-
type VmModule,
|
|
9
|
-
isVmWrapper,
|
|
10
|
-
serialize,
|
|
11
|
-
} from '@mirascript/mirascript';
|
|
12
|
-
import { DiagnosticCode } from '@mirascript/mirascript/subtle';
|
|
13
|
-
import { KEYWORDS as HELP_KEYWORDS } from '@mirascript/help';
|
|
14
|
-
import {
|
|
15
|
-
type editor,
|
|
16
|
-
languages,
|
|
17
|
-
type CancellationToken,
|
|
18
|
-
type IPosition,
|
|
19
|
-
type IRange,
|
|
20
|
-
type Position,
|
|
21
|
-
Range,
|
|
22
|
-
} from '../../monaco-api.js';
|
|
23
|
-
import { Provider, type MonacoContext } from './base.js';
|
|
24
|
-
import { strictContainsPosition, wordAt } from '../monaco-utils.js';
|
|
25
|
-
import { codeblock, getDeep, valueDoc, paramsList, getField, listFields, isDeprecatedGlobal } from '../utils.js';
|
|
26
|
-
import { KEYWORDS, RESERVED_KEYWORDS, REG_IDENTIFIER_FULL, REG_ORDINAL_FULL, isKeyword } from '../../constants.js';
|
|
27
|
-
import type { LocalDefinition } from '../compile-result.js';
|
|
28
|
-
|
|
29
|
-
const DESC_GLOBAL = '(global)';
|
|
30
|
-
const DESC_LOCAL = '(local)';
|
|
31
|
-
const DESC_FIELD = '(field)';
|
|
32
|
-
|
|
33
|
-
const SUGGEST_KEYWORDS: string[] = [];
|
|
34
|
-
|
|
35
|
-
const loadSuggestKeywords = () => {
|
|
36
|
-
if (SUGGEST_KEYWORDS.length > 0) return SUGGEST_KEYWORDS; // 已加载过
|
|
37
|
-
for (const kw of KEYWORDS) {
|
|
38
|
-
if (RESERVED_KEYWORDS.includes(kw as never)) continue; // 跳过保留关键字
|
|
39
|
-
SUGGEST_KEYWORDS.push(kw);
|
|
40
|
-
}
|
|
41
|
-
return SUGGEST_KEYWORDS;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const COMMON_GLOBAL_SUGGESTIONS = (
|
|
45
|
-
range: languages.CompletionItemRanges,
|
|
46
|
-
extension: boolean,
|
|
47
|
-
): languages.CompletionItem[] => {
|
|
48
|
-
const suggestions: languages.CompletionItem[] = [
|
|
49
|
-
{
|
|
50
|
-
label: 'type',
|
|
51
|
-
kind: languages.CompletionItemKind.Keyword,
|
|
52
|
-
insertText: 'type',
|
|
53
|
-
commitCharacters: ['('],
|
|
54
|
-
documentation: { value: HELP_KEYWORDS.type },
|
|
55
|
-
range,
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
label: 'global',
|
|
59
|
-
kind: languages.CompletionItemKind.Keyword,
|
|
60
|
-
insertText: 'global',
|
|
61
|
-
commitCharacters: ['.', '['],
|
|
62
|
-
documentation: { value: HELP_KEYWORDS.global },
|
|
63
|
-
range,
|
|
64
|
-
},
|
|
65
|
-
];
|
|
66
|
-
if (!extension) {
|
|
67
|
-
suggestions.push(
|
|
68
|
-
{
|
|
69
|
-
label: { label: 'if', description: 'If 表达式' },
|
|
70
|
-
kind: languages.CompletionItemKind.Snippet,
|
|
71
|
-
insertText: 'if ${1:condition} {\n\t$0\n}',
|
|
72
|
-
insertTextRules: languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
|
73
|
-
documentation: {
|
|
74
|
-
value: `使用 \`if\` 表达式进行条件判断。${codeblock('if condition {\n\t// code\n}')}`,
|
|
75
|
-
},
|
|
76
|
-
range,
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
label: { label: 'ifelse', description: 'If-Else 表达式' },
|
|
80
|
-
kind: languages.CompletionItemKind.Snippet,
|
|
81
|
-
insertText: 'if ${1:condition} {\n\t$0\n} else {\n\t\n}',
|
|
82
|
-
insertTextRules: languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
|
83
|
-
documentation: {
|
|
84
|
-
value: `使用 \`ifelse\` 表达式进行条件判断。${codeblock('if condition {\n\t// code\n} else {\n\t// code\n}')}`,
|
|
85
|
-
},
|
|
86
|
-
range,
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
label: { label: 'match', description: 'Match 表达式' },
|
|
90
|
-
kind: languages.CompletionItemKind.Snippet,
|
|
91
|
-
insertText:
|
|
92
|
-
'match ${1:value} {\n\tcase ${2:case1} {\n\t\t$0\n\t}\n\tcase ${3:case2} {\n\t\t\n\t}\n\tcase _ {\n\t\t\n\t}\n}',
|
|
93
|
-
insertTextRules: languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
|
94
|
-
documentation: {
|
|
95
|
-
value: `使用 \`match\` 表达式进行模式匹配。${codeblock('match value {\n\tcase case1 {\n\t\t// code\n\t}\n\tcase case2 {\n\t\t// code\n\t}\n\tcase _ {\n\t\t// code\n\t}\n}')}`,
|
|
96
|
-
},
|
|
97
|
-
range,
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
label: { label: 'loop', description: 'Loop 表达式' },
|
|
101
|
-
kind: languages.CompletionItemKind.Snippet,
|
|
102
|
-
insertText: 'loop {\n\t$0\n}',
|
|
103
|
-
insertTextRules: languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
|
104
|
-
documentation: {
|
|
105
|
-
value: `使用 \`loop\` 表达式进行无限循环。${codeblock('loop {\n\t// code\n}')}`,
|
|
106
|
-
},
|
|
107
|
-
range,
|
|
108
|
-
},
|
|
109
|
-
{
|
|
110
|
-
label: { label: 'while', description: 'While 表达式' },
|
|
111
|
-
kind: languages.CompletionItemKind.Snippet,
|
|
112
|
-
insertText: 'while ${1:condition} {\n\t$0\n}',
|
|
113
|
-
insertTextRules: languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
|
114
|
-
documentation: {
|
|
115
|
-
value: `使用 \`while\` 表达式进行条件循环。${codeblock('while condition {\n\t// code\n}')}`,
|
|
116
|
-
},
|
|
117
|
-
range,
|
|
118
|
-
},
|
|
119
|
-
{
|
|
120
|
-
label: { label: 'whileelse', description: 'While-Else 表达式' },
|
|
121
|
-
kind: languages.CompletionItemKind.Snippet,
|
|
122
|
-
insertText: 'while ${1:condition} {\n\t$0\n} else {\n\t\n}',
|
|
123
|
-
insertTextRules: languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
|
124
|
-
documentation: {
|
|
125
|
-
value: `使用 \`whileelse\` 表达式进行条件循环。${codeblock('while condition {\n\t// code\n} else {\n\t// code\n}')}`,
|
|
126
|
-
},
|
|
127
|
-
range,
|
|
128
|
-
},
|
|
129
|
-
{
|
|
130
|
-
label: { label: 'for', description: 'For 表达式' },
|
|
131
|
-
kind: languages.CompletionItemKind.Snippet,
|
|
132
|
-
insertText: 'for ${1:item} in ${2:collection} {\n\t$0\n}',
|
|
133
|
-
insertTextRules: languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
|
134
|
-
documentation: {
|
|
135
|
-
value: `使用 \`for\` 表达式进行迭代循环。${codeblock('for item in collection {\n\t// code\n}')}`,
|
|
136
|
-
},
|
|
137
|
-
range,
|
|
138
|
-
},
|
|
139
|
-
{
|
|
140
|
-
label: { label: 'forelse', description: 'For-Else 表达式' },
|
|
141
|
-
kind: languages.CompletionItemKind.Snippet,
|
|
142
|
-
insertText: 'for ${1:item} in ${2:collection} {\n\t$0\n} else {\n\t\n}',
|
|
143
|
-
insertTextRules: languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
|
144
|
-
documentation: {
|
|
145
|
-
value: `使用 \`forelse\` 表达式进行迭代循环。${codeblock('for item in collection {\n\t// code\n} else {\n\t// code\n}')}`,
|
|
146
|
-
},
|
|
147
|
-
range,
|
|
148
|
-
},
|
|
149
|
-
{
|
|
150
|
-
label: { label: 'fn', description: 'Fn 语句' },
|
|
151
|
-
kind: languages.CompletionItemKind.Snippet,
|
|
152
|
-
insertText: 'fn ${1:name}(${2:params}) {\n\t$0\n}',
|
|
153
|
-
insertTextRules: languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
|
154
|
-
documentation: {
|
|
155
|
-
value: `使用 \`fn\` 语句进行函数声明。${codeblock('fn name(params) {\n\t// code\n}')}`,
|
|
156
|
-
},
|
|
157
|
-
range,
|
|
158
|
-
},
|
|
159
|
-
);
|
|
160
|
-
|
|
161
|
-
for (const kw of loadSuggestKeywords()) {
|
|
162
|
-
const exist = suggestions.some(
|
|
163
|
-
(item) => item.label === kw && item.kind === languages.CompletionItemKind.Keyword,
|
|
164
|
-
);
|
|
165
|
-
if (exist) continue;
|
|
166
|
-
suggestions.push(kwSuggestion(kw, range));
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
return suggestions;
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
/** 构造关键字选项 */
|
|
173
|
-
function kwSuggestion(kw: string, range: languages.CompletionItemRanges): languages.CompletionItem {
|
|
174
|
-
const doc = (HELP_KEYWORDS as Record<string, string | undefined>)[kw];
|
|
175
|
-
return {
|
|
176
|
-
label: kw,
|
|
177
|
-
kind: languages.CompletionItemKind.Keyword,
|
|
178
|
-
insertText: kw,
|
|
179
|
-
documentation: doc ? { value: doc } : undefined,
|
|
180
|
-
range,
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/** 扩展完成项 */
|
|
185
|
-
interface CustomCompletionItem extends languages.CompletionItem {
|
|
186
|
-
/** 是否为字段 */
|
|
187
|
-
isField: boolean;
|
|
188
|
-
/** 对应的父值 */
|
|
189
|
-
vmParent?: MonacoContext | VmExtern | VmModule;
|
|
190
|
-
/** 对应的变量值 */
|
|
191
|
-
vmValue?: VmValue;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/** 构造 filterText */
|
|
195
|
-
function filterText(key: string, char: string | undefined): string {
|
|
196
|
-
if (char == null || key.startsWith(char)) return key;
|
|
197
|
-
if (key.startsWith('@')) return key.replace(/^@+/, '');
|
|
198
|
-
if (key.startsWith('$')) return key.replace(/^\$+/, '');
|
|
199
|
-
return key;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/** 构造选项 */
|
|
203
|
-
function completion(
|
|
204
|
-
model: editor.ITextModel,
|
|
205
|
-
description: string,
|
|
206
|
-
key: string,
|
|
207
|
-
value: VmValue | undefined,
|
|
208
|
-
fn: VmFunctionInfo | LocalDefinition['fn'] | undefined,
|
|
209
|
-
field: boolean,
|
|
210
|
-
): Pick<CustomCompletionItem, 'label' | 'kind' | 'commitCharacters' | 'vmValue' | 'isField'> {
|
|
211
|
-
let detail = '';
|
|
212
|
-
let kind: languages.CompletionItemKind;
|
|
213
|
-
if (fn == null && typeof value == 'function') {
|
|
214
|
-
fn = getVmFunctionInfo(value);
|
|
215
|
-
}
|
|
216
|
-
if (fn != null) {
|
|
217
|
-
detail = paramsList(model, fn);
|
|
218
|
-
kind = field ? languages.CompletionItemKind.Function : languages.CompletionItemKind.Method;
|
|
219
|
-
} else if (isVmModule(value)) {
|
|
220
|
-
kind = languages.CompletionItemKind.Module;
|
|
221
|
-
} else if (isVmExtern(value) && typeof value.value == 'function') {
|
|
222
|
-
if (value.value.prototype != null && (key[0] ?? '').toUpperCase() === key[0]) {
|
|
223
|
-
kind = languages.CompletionItemKind.Class;
|
|
224
|
-
} else {
|
|
225
|
-
detail = '(..)';
|
|
226
|
-
kind = value.thisArg ? languages.CompletionItemKind.Method : languages.CompletionItemKind.Function;
|
|
227
|
-
}
|
|
228
|
-
} else if (!field && key.startsWith('@')) {
|
|
229
|
-
kind = languages.CompletionItemKind.Constant;
|
|
230
|
-
} else {
|
|
231
|
-
kind = field ? languages.CompletionItemKind.Field : languages.CompletionItemKind.Variable;
|
|
232
|
-
}
|
|
233
|
-
return {
|
|
234
|
-
label: { label: key, description, detail },
|
|
235
|
-
kind,
|
|
236
|
-
commitCharacters: fn ? ['!', '('] : ['!', '.', '[', '('],
|
|
237
|
-
vmValue: value,
|
|
238
|
-
isField: field,
|
|
239
|
-
};
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* 自动完成
|
|
244
|
-
*/
|
|
245
|
-
export class CompletionItemProvider extends Provider implements languages.CompletionItemProvider {
|
|
246
|
-
readonly triggerCharacters: string[] = ['.', ':'];
|
|
247
|
-
/** 查找全局变量 */
|
|
248
|
-
private async completeGlobal(
|
|
249
|
-
model: editor.ITextModel,
|
|
250
|
-
char: string | undefined,
|
|
251
|
-
locals: readonly CustomCompletionItem[],
|
|
252
|
-
range: languages.CompletionItemRanges,
|
|
253
|
-
hasGlobalPrefix: boolean,
|
|
254
|
-
): Promise<CustomCompletionItem[]> {
|
|
255
|
-
const global = await this.getContext(model);
|
|
256
|
-
const suggestions: CustomCompletionItem[] = [];
|
|
257
|
-
const localKeys = new Set(locals.map((item) => item.insertText));
|
|
258
|
-
for (const key of new Set(global.keys())) {
|
|
259
|
-
if (!global.has(key)) continue;
|
|
260
|
-
const element = global.get(key);
|
|
261
|
-
|
|
262
|
-
let prefix = key;
|
|
263
|
-
const edits: editor.ISingleEditOperation[] = [];
|
|
264
|
-
if (!REG_IDENTIFIER_FULL.test(key)) {
|
|
265
|
-
const kStr = serialize(key);
|
|
266
|
-
if (hasGlobalPrefix) {
|
|
267
|
-
prefix = `[${kStr}]`;
|
|
268
|
-
// 删除多余的 . 前缀
|
|
269
|
-
edits.push({
|
|
270
|
-
range: {
|
|
271
|
-
startLineNumber: range.replace.startLineNumber,
|
|
272
|
-
startColumn: range.replace.startColumn - 1,
|
|
273
|
-
endLineNumber: range.replace.endLineNumber,
|
|
274
|
-
endColumn: range.replace.endColumn,
|
|
275
|
-
},
|
|
276
|
-
text: '',
|
|
277
|
-
});
|
|
278
|
-
} else {
|
|
279
|
-
prefix = `global[${serialize(key)}]`;
|
|
280
|
-
}
|
|
281
|
-
} else if (!hasGlobalPrefix && (localKeys.has(key) || isKeyword(key))) {
|
|
282
|
-
prefix = `global.${key}`;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
if (isVmModule(element)) {
|
|
286
|
-
for (const f of element.keys()) {
|
|
287
|
-
if (char && !f.toLowerCase().includes(char)) {
|
|
288
|
-
continue;
|
|
289
|
-
}
|
|
290
|
-
const field = element.get(f);
|
|
291
|
-
if (field === undefined) continue;
|
|
292
|
-
|
|
293
|
-
suggestions.push({
|
|
294
|
-
insertText: `${prefix}.${f}`,
|
|
295
|
-
filterText: filterText(f, char),
|
|
296
|
-
range,
|
|
297
|
-
additionalTextEdits: edits,
|
|
298
|
-
vmParent: element,
|
|
299
|
-
...completion(model, DESC_GLOBAL, `${key}.${f}`, field, undefined, true),
|
|
300
|
-
});
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
if (char && !key.toLowerCase().includes(char)) {
|
|
305
|
-
continue;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
if (isDeprecatedGlobal(global, key)) {
|
|
309
|
-
// skip deprecated globals in completion
|
|
310
|
-
continue;
|
|
311
|
-
}
|
|
312
|
-
suggestions.push({
|
|
313
|
-
insertText: prefix,
|
|
314
|
-
filterText: filterText(key, char),
|
|
315
|
-
range,
|
|
316
|
-
additionalTextEdits: edits,
|
|
317
|
-
vmParent: global,
|
|
318
|
-
...completion(model, DESC_GLOBAL, key, element, undefined, false),
|
|
319
|
-
});
|
|
320
|
-
}
|
|
321
|
-
return suggestions;
|
|
322
|
-
}
|
|
323
|
-
/** 查找局部变量 */
|
|
324
|
-
private async completeLocal(
|
|
325
|
-
model: editor.ITextModel,
|
|
326
|
-
position: IPosition,
|
|
327
|
-
char: string | undefined,
|
|
328
|
-
range: languages.CompletionItemRanges,
|
|
329
|
-
): Promise<CustomCompletionItem[]> {
|
|
330
|
-
const compiled = await this.getCompileResult(model);
|
|
331
|
-
if (!compiled) return [];
|
|
332
|
-
const suggestions: CustomCompletionItem[] = [];
|
|
333
|
-
|
|
334
|
-
let scope = compiled.scopeAt(model, position);
|
|
335
|
-
const locals = new Set<string>();
|
|
336
|
-
while (scope) {
|
|
337
|
-
for (const { definition, fn } of scope.locals) {
|
|
338
|
-
const name =
|
|
339
|
-
definition.code === DiagnosticCode.ParameterIt ? 'it' : model.getValueInRange(definition.range);
|
|
340
|
-
if (char && !name.toLowerCase().includes(char)) continue;
|
|
341
|
-
if (locals.has(name)) continue; // 子作用域可能会覆盖父作用域的变量
|
|
342
|
-
|
|
343
|
-
locals.add(name);
|
|
344
|
-
const suggestion = {
|
|
345
|
-
insertText: name,
|
|
346
|
-
filterText: filterText(name, char),
|
|
347
|
-
range,
|
|
348
|
-
...completion(model, DESC_LOCAL, name, undefined, fn, false),
|
|
349
|
-
};
|
|
350
|
-
if (definition.code === DiagnosticCode.LocalModule) {
|
|
351
|
-
suggestion.kind = languages.CompletionItemKind.Module;
|
|
352
|
-
} else if (definition.code === DiagnosticCode.LocalFunction) {
|
|
353
|
-
suggestion.kind = languages.CompletionItemKind.Function;
|
|
354
|
-
}
|
|
355
|
-
suggestions.push(suggestion);
|
|
356
|
-
}
|
|
357
|
-
if (!scope.parent) break;
|
|
358
|
-
scope = scope.parent;
|
|
359
|
-
}
|
|
360
|
-
return suggestions;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
/** 查找变量字段 */
|
|
364
|
-
private async completeFields(
|
|
365
|
-
model: editor.ITextModel,
|
|
366
|
-
position: Position,
|
|
367
|
-
char: string | undefined,
|
|
368
|
-
range: languages.CompletionItemRanges,
|
|
369
|
-
): Promise<CustomCompletionItem[]> {
|
|
370
|
-
const compiled = await this.getCompileResult(model);
|
|
371
|
-
if (!compiled) return [];
|
|
372
|
-
const access = compiled.fieldAccessAt(model, position);
|
|
373
|
-
if (!access || access.fields.length === 0) return [];
|
|
374
|
-
const { def, fields } = access;
|
|
375
|
-
if ('definition' in def.def) {
|
|
376
|
-
// TODO: suggests local item fields
|
|
377
|
-
return [];
|
|
378
|
-
}
|
|
379
|
-
const vmGlobal = await this.getContext(model);
|
|
380
|
-
fields.pop(); // 移除最后一个部分,因为它是当前输入位置的字段名
|
|
381
|
-
const [, value] = getDeep(vmGlobal, def.def.name, fields);
|
|
382
|
-
if (value == null || typeof value != 'object') {
|
|
383
|
-
return [];
|
|
384
|
-
}
|
|
385
|
-
const keys = listFields(value, true);
|
|
386
|
-
const result: CustomCompletionItem[] = [];
|
|
387
|
-
for (const k of keys) {
|
|
388
|
-
const key = String(k);
|
|
389
|
-
if (char && !String(key).toLowerCase().includes(char)) {
|
|
390
|
-
continue;
|
|
391
|
-
}
|
|
392
|
-
if (!REG_IDENTIFIER_FULL.test(key) && !REG_ORDINAL_FULL.test(key)) {
|
|
393
|
-
continue;
|
|
394
|
-
}
|
|
395
|
-
const field = getField(value, key);
|
|
396
|
-
result.push({
|
|
397
|
-
insertText: key,
|
|
398
|
-
range,
|
|
399
|
-
vmParent: isVmWrapper(value) ? value : undefined,
|
|
400
|
-
...completion(model, DESC_FIELD, key, field, undefined, true),
|
|
401
|
-
});
|
|
402
|
-
}
|
|
403
|
-
return result;
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
/** 获取完成范围 */
|
|
407
|
-
private toCompletionItemRanges(position: IPosition, range: IRange): languages.CompletionItemRanges {
|
|
408
|
-
return {
|
|
409
|
-
replace: range,
|
|
410
|
-
insert: Range.fromPositions(Range.getStartPosition(range), position),
|
|
411
|
-
};
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
/** @inheritdoc */
|
|
415
|
-
async provideCompletionItems(
|
|
416
|
-
model: editor.ITextModel,
|
|
417
|
-
position: Position,
|
|
418
|
-
context: languages.CompletionContext,
|
|
419
|
-
token: CancellationToken,
|
|
420
|
-
): Promise<languages.CompletionList | undefined> {
|
|
421
|
-
const compiled = await this.getCompileResult(model);
|
|
422
|
-
if (!compiled) return undefined;
|
|
423
|
-
|
|
424
|
-
if (context.triggerCharacter === '.') {
|
|
425
|
-
const prevWord = model.getWordAtPosition({
|
|
426
|
-
lineNumber: position.lineNumber,
|
|
427
|
-
column: position.column - 1,
|
|
428
|
-
});
|
|
429
|
-
if (prevWord?.word === 'global') {
|
|
430
|
-
const globals = await this.completeGlobal(
|
|
431
|
-
model,
|
|
432
|
-
undefined,
|
|
433
|
-
[],
|
|
434
|
-
this.toCompletionItemRanges(position, {
|
|
435
|
-
startLineNumber: position.lineNumber,
|
|
436
|
-
startColumn: position.column,
|
|
437
|
-
endLineNumber: position.lineNumber,
|
|
438
|
-
endColumn: position.column,
|
|
439
|
-
}),
|
|
440
|
-
true,
|
|
441
|
-
);
|
|
442
|
-
return { suggestions: globals };
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
const word = wordAt(model, position);
|
|
447
|
-
const prev = model.getValueInRange({
|
|
448
|
-
startLineNumber: position.lineNumber,
|
|
449
|
-
startColumn: (word?.range.startColumn ?? position.column) - 2,
|
|
450
|
-
endLineNumber: position.lineNumber,
|
|
451
|
-
endColumn: word?.range.startColumn ?? position.column,
|
|
452
|
-
});
|
|
453
|
-
|
|
454
|
-
if (prev !== '::' && context.triggerCharacter === ':') {
|
|
455
|
-
return undefined; // 不是 :: 触发的
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
// suggest variables
|
|
459
|
-
let char: string | undefined;
|
|
460
|
-
let range: IRange;
|
|
461
|
-
const def = compiled.variableAccessAt(model, position);
|
|
462
|
-
if (def) {
|
|
463
|
-
if (def.ref == null) {
|
|
464
|
-
// 输入位置是变量定义
|
|
465
|
-
const suggestions: languages.CompletionItem[] = [];
|
|
466
|
-
if (
|
|
467
|
-
word &&
|
|
468
|
-
compiled.tags.some(
|
|
469
|
-
(t) => strictContainsPosition(t.range, position) && t.code === DiagnosticCode.MatchExpression,
|
|
470
|
-
)
|
|
471
|
-
) {
|
|
472
|
-
suggestions.push(
|
|
473
|
-
kwSuggestion('case', this.toCompletionItemRanges(position, word.range)),
|
|
474
|
-
kwSuggestion('if', this.toCompletionItemRanges(position, word.range)),
|
|
475
|
-
);
|
|
476
|
-
}
|
|
477
|
-
return { suggestions };
|
|
478
|
-
}
|
|
479
|
-
const d = def.def;
|
|
480
|
-
range = d.references[def.ref]!.range;
|
|
481
|
-
char = model.getValueInRange({
|
|
482
|
-
startLineNumber: range.startLineNumber,
|
|
483
|
-
startColumn: range.startColumn,
|
|
484
|
-
endLineNumber: range.startLineNumber,
|
|
485
|
-
endColumn: range.startColumn + 1,
|
|
486
|
-
});
|
|
487
|
-
} else if (word) {
|
|
488
|
-
range = word.range;
|
|
489
|
-
char = word.word[0];
|
|
490
|
-
} else {
|
|
491
|
-
range = {
|
|
492
|
-
startLineNumber: position.lineNumber,
|
|
493
|
-
startColumn: position.column,
|
|
494
|
-
endLineNumber: position.lineNumber,
|
|
495
|
-
endColumn: position.column,
|
|
496
|
-
};
|
|
497
|
-
}
|
|
498
|
-
char = char?.toLowerCase();
|
|
499
|
-
|
|
500
|
-
const completionRange = this.toCompletionItemRanges(position, range);
|
|
501
|
-
|
|
502
|
-
if (/[^.]\.$/u.test(prev)) {
|
|
503
|
-
const suggestions = await this.completeFields(model, position, char, completionRange);
|
|
504
|
-
return { suggestions };
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
const suggestions = COMMON_GLOBAL_SUGGESTIONS(completionRange, prev === '::');
|
|
508
|
-
const locals = await this.completeLocal(model, position, char, completionRange);
|
|
509
|
-
const globals = await this.completeGlobal(model, char, locals, completionRange, false);
|
|
510
|
-
suggestions.push(...locals, ...globals);
|
|
511
|
-
|
|
512
|
-
return { suggestions };
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
/** @inheritdoc */
|
|
516
|
-
resolveCompletionItem(
|
|
517
|
-
item: languages.CompletionItem,
|
|
518
|
-
token: CancellationToken,
|
|
519
|
-
): languages.CompletionItem | undefined {
|
|
520
|
-
if (typeof item.label == 'string') {
|
|
521
|
-
// not a dynamic completion item
|
|
522
|
-
return item;
|
|
523
|
-
}
|
|
524
|
-
const { vmValue, isField, vmParent } = item as CustomCompletionItem;
|
|
525
|
-
const { label } = item.label;
|
|
526
|
-
if (vmValue !== undefined || vmParent) {
|
|
527
|
-
if (item.documentation) return item;
|
|
528
|
-
const last = label.split('.').pop()!;
|
|
529
|
-
const def = valueDoc(last, vmValue, isField ? 'field' : 'hint', vmParent ?? null);
|
|
530
|
-
item.documentation = {
|
|
531
|
-
value: `${codeblock('\0' + def.script)}\n${def.doc.join('\n')}`,
|
|
532
|
-
};
|
|
533
|
-
}
|
|
534
|
-
return item;
|
|
535
|
-
}
|
|
536
|
-
}
|