wikilint 2.16.1 → 2.16.3
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/data/signatures.json +82 -4
- package/dist/base.d.mts +7 -2
- package/dist/base.d.ts +7 -2
- package/dist/bin/coverage.js +2 -1
- package/dist/lib/element.js +3 -1
- package/dist/lib/lsp.d.ts +16 -2
- package/dist/lib/lsp.js +156 -148
- package/dist/lib/node.d.ts +2 -0
- package/dist/lib/node.js +68 -33
- package/dist/lib/text.js +4 -2
- package/dist/parser/braces.js +2 -1
- package/dist/parser/commentAndExt.js +2 -3
- package/dist/parser/selector.js +11 -4
- package/dist/src/arg.d.ts +1 -0
- package/dist/src/html.js +6 -4
- package/dist/src/imageParameter.js +6 -6
- package/dist/src/imagemap.js +3 -3
- package/dist/src/index.d.ts +21 -0
- package/dist/src/index.js +64 -12
- package/dist/src/magicLink.js +2 -1
- package/dist/src/nested.js +2 -1
- package/dist/src/nowiki/index.js +2 -1
- package/dist/src/table/index.js +7 -1
- package/dist/src/table/td.js +20 -23
- package/dist/src/transclude.d.ts +1 -0
- package/dist/src/transclude.js +3 -3
- package/dist/util/debug.js +10 -11
- package/dist/util/lint.js +20 -4
- package/dist/util/string.js +9 -5
- package/package.json +3 -2
package/dist/lib/lsp.js
CHANGED
|
@@ -3,12 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.LanguageService = exports.tasks = void 0;
|
|
4
4
|
const path = require("path");
|
|
5
5
|
const common_1 = require("@bhsd/common");
|
|
6
|
-
const debug_1 = require("../util/debug");
|
|
7
6
|
const sharable_1 = require("../util/sharable");
|
|
8
7
|
const lint_1 = require("../util/lint");
|
|
9
8
|
const index_1 = require("../index");
|
|
10
9
|
const element_1 = require("../lib/element");
|
|
11
10
|
exports.tasks = new WeakMap();
|
|
11
|
+
const refTags = new Set(['ref']), referencesTags = new Set(['ref', 'references']), nameAttrs = new Set(['name', 'extends', 'follow']), groupAttrs = new Set(['group']);
|
|
12
12
|
/**
|
|
13
13
|
* Check if all child nodes are plain text or comments.
|
|
14
14
|
* @param childNodes child nodes
|
|
@@ -41,7 +41,7 @@ const createNodeRange = (token) => {
|
|
|
41
41
|
const { top, left, height, width } = token.getBoundingClientRect();
|
|
42
42
|
return {
|
|
43
43
|
start: { line: top, character: left },
|
|
44
|
-
end: (0, lint_1.getEndPos)(top, left,
|
|
44
|
+
end: (0, lint_1.getEndPos)(top, left, height, width),
|
|
45
45
|
};
|
|
46
46
|
};
|
|
47
47
|
/**
|
|
@@ -53,7 +53,7 @@ const createNodeRange = (token) => {
|
|
|
53
53
|
* @param pos.line line number
|
|
54
54
|
* @param pos.character character number
|
|
55
55
|
*/
|
|
56
|
-
const getCompletion = (words, kind, mt, { line, character }) => [...new Set(words)].map(w => ({
|
|
56
|
+
const getCompletion = (words, kind, mt, { line, character }) => [...new Set(words)].map((w) => ({
|
|
57
57
|
label: w,
|
|
58
58
|
kind,
|
|
59
59
|
textEdit: {
|
|
@@ -86,59 +86,34 @@ const getUrl = (page, ns) => {
|
|
|
86
86
|
: articlePath + (articlePath.endsWith('/') ? '' : '/') + encoded;
|
|
87
87
|
};
|
|
88
88
|
/**
|
|
89
|
-
* Get the token at the position.
|
|
89
|
+
* Get the token at the position from a word.
|
|
90
90
|
* @param root root token
|
|
91
91
|
* @param pos position
|
|
92
92
|
*/
|
|
93
|
-
const
|
|
94
|
-
const { line, character } = pos;
|
|
95
|
-
|
|
96
|
-
while (true) { // eslint-disable-line no-constant-condition
|
|
97
|
-
// eslint-disable-next-line @typescript-eslint/no-loop-func
|
|
98
|
-
const child = node.childNodes.find(ch => {
|
|
99
|
-
const i = ch.getRelativeIndex();
|
|
100
|
-
if (i < offset && i + ch.toString().length >= offset) {
|
|
101
|
-
offset -= i;
|
|
102
|
-
return true;
|
|
103
|
-
}
|
|
104
|
-
return false;
|
|
105
|
-
});
|
|
106
|
-
if (!child || child.type === 'text') {
|
|
107
|
-
break;
|
|
108
|
-
}
|
|
109
|
-
node = child;
|
|
110
|
-
}
|
|
111
|
-
return node;
|
|
93
|
+
const elementFromWord = (root, pos) => {
|
|
94
|
+
const { line, character } = pos, index = root.indexFromPos(line, character), offset = Number(/[\w!#]/u.test(root.toString().charAt(index)));
|
|
95
|
+
return root.elementFromIndex(index + offset);
|
|
112
96
|
};
|
|
113
97
|
/**
|
|
114
|
-
* Get the
|
|
115
|
-
* @param
|
|
116
|
-
* @param
|
|
98
|
+
* Get the attribute of a `<ref>` tag.
|
|
99
|
+
* @param token attribute token
|
|
100
|
+
* @param tags tag names
|
|
101
|
+
* @param names attribute names
|
|
117
102
|
*/
|
|
118
|
-
const
|
|
119
|
-
const {
|
|
120
|
-
return
|
|
103
|
+
const getRefAttr = (token, tags, names) => {
|
|
104
|
+
const { type, parentNode = {} } = token, { name, tag } = parentNode;
|
|
105
|
+
return type === 'attr-value' && tags.has(tag) && names.has(name) ? token.toString().trim() : NaN;
|
|
121
106
|
};
|
|
122
107
|
/**
|
|
123
108
|
* Get the `name` attribute of a `<ref>` tag.
|
|
124
109
|
* @param token `name` attribute token
|
|
125
110
|
*/
|
|
126
|
-
const getRefName = (token) =>
|
|
127
|
-
const { type, parentNode = {} } = token, { name, tag } = parentNode;
|
|
128
|
-
return type === 'attr-value' && tag === 'ref' && ['name', 'extends', 'follow'].includes(name)
|
|
129
|
-
? token.toString().trim()
|
|
130
|
-
: NaN;
|
|
131
|
-
};
|
|
111
|
+
const getRefName = (token) => getRefAttr(token, refTags, nameAttrs);
|
|
132
112
|
/**
|
|
133
113
|
* Get the `group` attribute of a `<ref>` or `<references>` tag.
|
|
134
114
|
* @param token `group` attribute token
|
|
135
115
|
*/
|
|
136
|
-
const getRefGroup = (token) =>
|
|
137
|
-
const { type, parentNode = {} } = token, { name, tag } = parentNode;
|
|
138
|
-
return type === 'attr-value' && name === 'group' && (tag === 'ref' || tag === 'references')
|
|
139
|
-
? token.toString().trim()
|
|
140
|
-
: NaN;
|
|
141
|
-
};
|
|
116
|
+
const getRefGroup = (token) => getRefAttr(token, referencesTags, groupAttrs);
|
|
142
117
|
/**
|
|
143
118
|
* Get the effective name of a token.
|
|
144
119
|
* @param token
|
|
@@ -169,7 +144,8 @@ const getName = (token) => {
|
|
|
169
144
|
*/
|
|
170
145
|
const getSectionEnd = (section, lines, line) => {
|
|
171
146
|
if (section) {
|
|
172
|
-
|
|
147
|
+
const [, start, end] = lines[line];
|
|
148
|
+
section.range.end = { line, character: end - start };
|
|
173
149
|
}
|
|
174
150
|
};
|
|
175
151
|
/* NOT FOR BROWSER ONLY END */
|
|
@@ -178,6 +154,7 @@ class LanguageService {
|
|
|
178
154
|
#text;
|
|
179
155
|
#running;
|
|
180
156
|
#done;
|
|
157
|
+
#config;
|
|
181
158
|
#completionConfig;
|
|
182
159
|
#signature;
|
|
183
160
|
/** @private */
|
|
@@ -201,14 +178,11 @@ class LanguageService {
|
|
|
201
178
|
* - 否则开始新的解析
|
|
202
179
|
*/
|
|
203
180
|
async #queue(text) {
|
|
204
|
-
|
|
205
|
-
if (
|
|
206
|
-
return (0, debug_1.typeError)(this.constructor, 'queue', 'String');
|
|
207
|
-
}
|
|
208
|
-
else if (this.#text === text && !this.#running && this.#done) {
|
|
181
|
+
text = text.replace(/\r$/gmu, '');
|
|
182
|
+
if (this.#text === text && this.#config === index_1.default.config && !this.#running) {
|
|
209
183
|
return this.#done;
|
|
210
184
|
}
|
|
211
|
-
this.#text = text
|
|
185
|
+
this.#text = text;
|
|
212
186
|
this.#running ??= this.#parse(); // 不要提交多个解析任务
|
|
213
187
|
return this.#running;
|
|
214
188
|
}
|
|
@@ -221,8 +195,10 @@ class LanguageService {
|
|
|
221
195
|
async #parse() {
|
|
222
196
|
return new Promise(resolve => {
|
|
223
197
|
(typeof setImmediate === 'function' ? setImmediate : /* istanbul ignore next */ setTimeout)(() => {
|
|
224
|
-
const
|
|
225
|
-
|
|
198
|
+
const config = index_1.default.getConfig();
|
|
199
|
+
this.#config = index_1.default.config;
|
|
200
|
+
const text = this.#text, root = index_1.default.parse(text, true, undefined, config);
|
|
201
|
+
if (this.#text === text && this.#config === index_1.default.config) {
|
|
226
202
|
this.#done = root;
|
|
227
203
|
this.#running = undefined;
|
|
228
204
|
resolve(root);
|
|
@@ -235,6 +211,16 @@ class LanguageService {
|
|
|
235
211
|
}, 0);
|
|
236
212
|
});
|
|
237
213
|
}
|
|
214
|
+
/**
|
|
215
|
+
* 检查是否为签名语言服务器
|
|
216
|
+
* @throws `Error` 是签名语言服务器
|
|
217
|
+
*/
|
|
218
|
+
#checkSignature() {
|
|
219
|
+
/* istanbul ignore if */
|
|
220
|
+
if (this.#signature) {
|
|
221
|
+
throw new Error('This is a signature language server!');
|
|
222
|
+
}
|
|
223
|
+
}
|
|
238
224
|
/**
|
|
239
225
|
* 提供颜色指示
|
|
240
226
|
* @param rgba 颜色解析函数
|
|
@@ -242,16 +228,15 @@ class LanguageService {
|
|
|
242
228
|
* @param hsl 是否允许HSL颜色
|
|
243
229
|
*/
|
|
244
230
|
async provideDocumentColors(rgba, text, hsl = true) {
|
|
245
|
-
|
|
246
|
-
if (this.#signature) {
|
|
247
|
-
throw new Error('This is a signature language server!');
|
|
248
|
-
}
|
|
231
|
+
this.#checkSignature();
|
|
249
232
|
const root = await this.#queue(text);
|
|
250
|
-
return root.querySelectorAll('attr-value,parameter-value,arg-default')
|
|
233
|
+
return root.querySelectorAll('attr-value,parameter-value,arg-default')
|
|
234
|
+
.flatMap(({ type, childNodes }) => {
|
|
251
235
|
if (type !== 'attr-value' && !isPlain(childNodes)) {
|
|
252
236
|
return [];
|
|
253
237
|
}
|
|
254
|
-
return childNodes.filter((child) => child.type === 'text')
|
|
238
|
+
return childNodes.filter((child) => child.type === 'text')
|
|
239
|
+
.flatMap(child => {
|
|
255
240
|
const parts = (0, common_1.splitColors)(child.data, hsl).filter(([, , , isColor]) => isColor);
|
|
256
241
|
if (parts.length === 0) {
|
|
257
242
|
return [];
|
|
@@ -287,21 +272,22 @@ class LanguageService {
|
|
|
287
272
|
#prepareCompletionConfig() {
|
|
288
273
|
if (!this.#completionConfig) {
|
|
289
274
|
const { nsid, ext, html, parserFunction: [insensitive, sensitive, ...other], doubleUnderscore, protocol, img, } = index_1.default.getConfig(), tags = new Set([ext, html].flat(2));
|
|
275
|
+
const re = new RegExp('(?:' // eslint-disable-line prefer-template
|
|
276
|
+
+ String.raw `<\/?(\w*)` // tag
|
|
277
|
+
+ '|'
|
|
278
|
+
+ String.raw `(\{{2,4}|\[\[)\s*([^|{}<>[\]\s][^|{}<>[\]#]*)?` // braces and brackets
|
|
279
|
+
+ '|'
|
|
280
|
+
+ String.raw `(__(?:(?!__)[\p{L}\d_])*)` // behavior switch
|
|
281
|
+
+ '|'
|
|
282
|
+
+ String.raw `(?<!\[)\[([a-z:/]*)` // protocol
|
|
283
|
+
+ '|'
|
|
284
|
+
+ String.raw `\[\[\s*(?:${Object.entries(nsid).filter(([, v]) => v === 6).map(([k]) => k).join('|')})\s*:[^[\]{}<>]+\|([^[\]{}<>|=]*)` // image parameter
|
|
285
|
+
+ '|'
|
|
286
|
+
// attribute key
|
|
287
|
+
+ String.raw `<(\w+)(?:\s(?:[^<>{}|=\s]+(?:\s*=\s*(?:[^\s"']\S*|(["']).*?\8))?(?=\s))*)?\s(\w*)`
|
|
288
|
+
+ ')$', 'iu');
|
|
290
289
|
this.#completionConfig = {
|
|
291
|
-
re
|
|
292
|
-
+ String.raw `<\/?(\w+)` // tag
|
|
293
|
-
+ '|'
|
|
294
|
-
+ String.raw `(\{{2,4}|\[\[)\s*([^|{}<>[\]\s][^|{}<>[\]#]*)` // braces and brackets
|
|
295
|
-
+ '|'
|
|
296
|
-
+ String.raw `(__(?:(?!__)[\p{L}\d_])+)` // behavior switch
|
|
297
|
-
+ '|'
|
|
298
|
-
+ String.raw `(?<!\[)\[([a-z:/]+)` // protocol
|
|
299
|
-
+ '|'
|
|
300
|
-
+ String.raw `\[\[\s*(?:${Object.entries(nsid).filter(([, v]) => v === 6).map(([k]) => k).join('|')})\s*:[^[\]{}<>]+\|([^[\]{}<>|=]+)` // image parameter
|
|
301
|
-
+ '|'
|
|
302
|
-
// attribute key
|
|
303
|
-
+ String.raw `<(\w+)(?:\s(?:[^<>{}|=\s]+(?:\s*=\s*(?:[^\s"']\S*|(["']).*?\8))?(?=\s))*)?\s(\w+)`
|
|
304
|
-
+ ')$', 'iu'),
|
|
290
|
+
re,
|
|
305
291
|
ext,
|
|
306
292
|
tags,
|
|
307
293
|
allTags: [...tags, 'onlyinclude', 'includeonly', 'noinclude'],
|
|
@@ -324,39 +310,37 @@ class LanguageService {
|
|
|
324
310
|
* @param position 位置
|
|
325
311
|
*/
|
|
326
312
|
async provideCompletionItems(text, position) {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
}
|
|
331
|
-
const { re, allTags, functions, switches, protocols, params, tags, ext } = this.#prepareCompletionConfig(), { line, character } = position, mt = re.exec(text.split(/\r?\n/u)[line]?.slice(0, character) ?? '');
|
|
332
|
-
if (mt?.[1]) { // tag
|
|
313
|
+
this.#checkSignature();
|
|
314
|
+
const { re, allTags, functions, switches, protocols, params, tags, ext } = this.#prepareCompletionConfig(), { line, character } = position, mt = re.exec(text.split(/\r?\n/u, line + 1)[line]?.slice(0, character) ?? '');
|
|
315
|
+
if (mt?.[1] !== undefined) { // tag
|
|
333
316
|
return getCompletion(allTags, 'Class', mt[1], position);
|
|
334
317
|
}
|
|
335
318
|
else if (mt?.[4]) { // behavior switch
|
|
336
319
|
return getCompletion(switches, 'Constant', mt[4], position);
|
|
337
320
|
}
|
|
338
|
-
else if (mt?.[5]) { // protocol
|
|
321
|
+
else if (mt?.[5] !== undefined) { // protocol
|
|
339
322
|
return getCompletion(protocols, 'Reference', mt[5], position);
|
|
340
323
|
}
|
|
341
324
|
const root = await this.#queue(text);
|
|
342
|
-
if (mt?.[2]
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
const colon = mt[3].startsWith(':'), str = colon ? mt[3].slice(1).trimStart() : mt[3];
|
|
347
|
-
if (mt[2] === '[[') {
|
|
348
|
-
return getCompletion(root.querySelectorAll('link,file,category').map(({ name }) => name), 'Folder', str, position);
|
|
325
|
+
if (mt?.[2]) {
|
|
326
|
+
const match = mt[3] ?? '';
|
|
327
|
+
if (mt[2] === '{{{') { // argument
|
|
328
|
+
return getCompletion(root.querySelectorAll('arg').map(({ name }) => name).filter(Boolean), 'Variable', match, position);
|
|
349
329
|
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
330
|
+
const colon = match.startsWith(':'), str = colon ? match.slice(1).trimStart() : match;
|
|
331
|
+
return mt[2] === '[['
|
|
332
|
+
? getCompletion(// link
|
|
333
|
+
root.querySelectorAll('link,file,category,redirect-target').map(({ name }) => name), 'Folder', str, position)
|
|
334
|
+
: [
|
|
335
|
+
...getCompletion(functions, 'Function', match, position),
|
|
336
|
+
...match.startsWith('#')
|
|
337
|
+
? []
|
|
338
|
+
: getCompletion(root.querySelectorAll('template')
|
|
339
|
+
.map(({ name }) => colon ? name : name.replace(/^Template:/u, '')), 'Folder', str, position),
|
|
340
|
+
];
|
|
357
341
|
}
|
|
358
|
-
const token = elementFromPoint(
|
|
359
|
-
if (mt?.[6]
|
|
342
|
+
const token = root.elementFromPoint(character, line), { type, parentNode } = token;
|
|
343
|
+
if (mt?.[6] !== undefined || type === 'image-parameter') { // image parameter
|
|
360
344
|
const match = mt?.[6]?.trimStart()
|
|
361
345
|
?? this.#text.slice(token.getAbsoluteIndex(), root.indexFromPos(position.line, position.character)).trimStart();
|
|
362
346
|
return [
|
|
@@ -364,12 +348,12 @@ class LanguageService {
|
|
|
364
348
|
...getCompletion(root.querySelectorAll('image-parameter#width').map(width => width.text()), 'Unit', match, position),
|
|
365
349
|
];
|
|
366
350
|
}
|
|
367
|
-
else if (mt?.[7] || type === 'attr-key') { // attribute key
|
|
368
|
-
const tag = mt?.[7]?.toLowerCase() ?? parentNode.tag;
|
|
351
|
+
else if (mt?.[7] !== undefined || type === 'attr-key') { // attribute key
|
|
352
|
+
const tag = mt?.[7]?.toLowerCase() ?? parentNode.tag, key = mt?.[9] ?? token.toString();
|
|
369
353
|
if (!tags.has(tag)) {
|
|
370
354
|
return undefined;
|
|
371
355
|
}
|
|
372
|
-
const
|
|
356
|
+
const thisHtmlAttrs = sharable_1.htmlAttrs[tag], thisExtAttrs = sharable_1.extAttrs[tag], extCompletion = thisExtAttrs && getCompletion(thisExtAttrs, 'Field', key, position);
|
|
373
357
|
return ext.includes(tag) && !thisHtmlAttrs
|
|
374
358
|
? extCompletion
|
|
375
359
|
: [
|
|
@@ -386,22 +370,23 @@ class LanguageService {
|
|
|
386
370
|
}
|
|
387
371
|
else if ((type === 'parameter-key' || type === 'parameter-value' && parentNode.anon)
|
|
388
372
|
&& parentNode.parentNode.type === 'template') { // parameter key
|
|
389
|
-
|
|
390
|
-
|
|
373
|
+
const key = token.toString().trimStart();
|
|
374
|
+
return key
|
|
375
|
+
? getCompletion(root.querySelectorAll('parameter').filter(({ anon, parentNode: parent }) => !anon && parent.type === 'template'
|
|
376
|
+
&& parent.name === parentNode.parentNode.name).map(({ name }) => name), 'Variable', key, position)
|
|
377
|
+
: undefined;
|
|
391
378
|
}
|
|
392
379
|
return undefined;
|
|
393
380
|
}
|
|
394
381
|
/**
|
|
395
382
|
* 提供语法诊断
|
|
396
383
|
* @param wikitext 源代码
|
|
384
|
+
* @param warning 是否提供警告
|
|
397
385
|
*/
|
|
398
|
-
async provideDiagnostics(wikitext) {
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
}
|
|
403
|
-
const root = await this.#queue(wikitext);
|
|
404
|
-
return root.lint().filter(({ severity }) => severity === 'error')
|
|
386
|
+
async provideDiagnostics(wikitext, warning = true) {
|
|
387
|
+
this.#checkSignature();
|
|
388
|
+
const root = await this.#queue(wikitext), errors = root.lint();
|
|
389
|
+
return (warning ? errors : errors.filter(({ severity }) => severity === 'error'))
|
|
405
390
|
.map(({ startLine, startCol, endLine, endCol, severity, rule, message, fix, suggestions }) => ({
|
|
406
391
|
range: {
|
|
407
392
|
start: { line: startLine, character: startCol },
|
|
@@ -434,15 +419,12 @@ class LanguageService {
|
|
|
434
419
|
}));
|
|
435
420
|
}
|
|
436
421
|
async #provideFoldingRangesOrDocumentSymbols(text, fold = true) {
|
|
437
|
-
|
|
438
|
-
if (this.#signature) {
|
|
439
|
-
throw new Error('This is a signature language server!');
|
|
440
|
-
}
|
|
422
|
+
this.#checkSignature();
|
|
441
423
|
const ranges = [], symbols = [],
|
|
442
424
|
/* NOT FOR BROWSER ONLY */
|
|
443
425
|
names = new Set(), sections = new Array(6),
|
|
444
426
|
/* NOT FOR BROWSER ONLY END */
|
|
445
|
-
root = await this.#queue(text), lines =
|
|
427
|
+
root = await this.#queue(text), lines = root.getLines(), { length } = lines, levels = new Array(6), tokens = root.querySelectorAll(fold ? 'heading-title,table,template,magic-word' : 'heading-title');
|
|
446
428
|
for (const token of tokens) {
|
|
447
429
|
const { top, height,
|
|
448
430
|
/* NOT FOR BROWSER ONLY */
|
|
@@ -474,7 +456,7 @@ class LanguageService {
|
|
|
474
456
|
.find(s => !names.has(s))
|
|
475
457
|
: section, container = sections.slice(0, level - 1).reverse().find(Boolean), selectionRange = {
|
|
476
458
|
start: { line: top, character: left - level },
|
|
477
|
-
end: (0, lint_1.getEndPos)(top, left, width + level
|
|
459
|
+
end: (0, lint_1.getEndPos)(top, left, height, width + level),
|
|
478
460
|
}, info = {
|
|
479
461
|
name,
|
|
480
462
|
kind: 15,
|
|
@@ -533,13 +515,10 @@ class LanguageService {
|
|
|
533
515
|
* @param text 源代码
|
|
534
516
|
*/
|
|
535
517
|
async provideLinks(text) {
|
|
536
|
-
|
|
537
|
-
if (this.#signature) {
|
|
538
|
-
throw new Error('This is a signature language server!');
|
|
539
|
-
}
|
|
518
|
+
this.#checkSignature();
|
|
540
519
|
const protocolRegex = new RegExp(`^(?:${index_1.default.getConfig().protocol}|//)`, 'iu'), selector = 'link-target,template-name,invoke-module,magic-link,ext-link-url,free-ext-link,attr-value,'
|
|
541
520
|
+ 'image-parameter#link';
|
|
542
|
-
return (await this.#queue(text)).querySelectorAll(selector).flatMap(token => {
|
|
521
|
+
return (await this.#queue(text)).querySelectorAll(selector).flatMap((token) => {
|
|
543
522
|
const { type, parentNode, firstChild, lastChild, childNodes } = token, { name, tag } = parentNode;
|
|
544
523
|
if (!(type !== 'attr-value'
|
|
545
524
|
|| name === 'src' && ['templatestyles', 'img'].includes(tag)
|
|
@@ -586,7 +565,7 @@ class LanguageService {
|
|
|
586
565
|
{
|
|
587
566
|
range: {
|
|
588
567
|
start: { line: rect.top, character: rect.left },
|
|
589
|
-
end: (0, lint_1.getEndPos)(top, left,
|
|
568
|
+
end: (0, lint_1.getEndPos)(top, left, height, width),
|
|
590
569
|
},
|
|
591
570
|
target,
|
|
592
571
|
},
|
|
@@ -599,11 +578,8 @@ class LanguageService {
|
|
|
599
578
|
}
|
|
600
579
|
});
|
|
601
580
|
}
|
|
602
|
-
async #provideReferencesOrDefinition(text, position, usage
|
|
603
|
-
|
|
604
|
-
if (this.#signature) {
|
|
605
|
-
throw new Error('This is a signature language server!');
|
|
606
|
-
}
|
|
581
|
+
async #provideReferencesOrDefinition(text, position, usage) {
|
|
582
|
+
this.#checkSignature();
|
|
607
583
|
const renameTypes = [
|
|
608
584
|
'arg-name',
|
|
609
585
|
'template-name',
|
|
@@ -619,40 +595,30 @@ class LanguageService {
|
|
|
619
595
|
'heading',
|
|
620
596
|
...renameTypes,
|
|
621
597
|
], root = await this.#queue(text), node = elementFromWord(root, position), { type } = node, refName = getRefName(node), refGroup = getRefGroup(node);
|
|
622
|
-
if (usage
|
|
598
|
+
if (usage === 2 && type === 'parameter-key' && /^[1-9]\d*$/u.test(node.parentNode.name)
|
|
623
599
|
|| !refName && (usage === 1
|
|
624
600
|
|| !refGroup && (usage === 0
|
|
625
601
|
? !types.includes(type)
|
|
626
602
|
: !renameTypes.includes(type)
|
|
627
|
-
|| type === 'link-target' && ['link', 'redirect-target'].includes(node.parentNode.type)))) {
|
|
603
|
+
|| type === 'link-target' && !['link', 'redirect-target'].includes(node.parentNode.type)))) {
|
|
628
604
|
return undefined;
|
|
629
605
|
}
|
|
630
606
|
else if (usage === 2) {
|
|
631
607
|
return createNodeRange(node);
|
|
632
608
|
}
|
|
633
609
|
const name = getName(node), refs = root.querySelectorAll(type === 'heading-title' ? 'heading' : type).filter(token => {
|
|
610
|
+
const { name: n, parentNode } = token.parentNode;
|
|
634
611
|
if (usage === 1) {
|
|
635
|
-
const { name: n, parentNode } = token.parentNode;
|
|
636
612
|
return getRefName(token) === refName
|
|
637
613
|
&& n === 'name' && parentNode.parentNode.innerText;
|
|
638
614
|
}
|
|
639
615
|
return type === 'attr-value'
|
|
640
616
|
? getRefName(token) === refName || getRefGroup(token) === refGroup
|
|
641
617
|
: getName(token) === name;
|
|
642
|
-
}).map(token =>
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
return usage === 3
|
|
647
|
-
? {
|
|
648
|
-
changes: {
|
|
649
|
-
'': refs.map(ref => ({
|
|
650
|
-
range: createNodeRange(ref),
|
|
651
|
-
newText: newName,
|
|
652
|
-
})),
|
|
653
|
-
},
|
|
654
|
-
}
|
|
655
|
-
: refs.map((ref) => ({ range: createNodeRange(ref) }));
|
|
618
|
+
}).map((token) => ({
|
|
619
|
+
range: createNodeRange(token.type === 'parameter-key' ? token.parentNode : token),
|
|
620
|
+
}));
|
|
621
|
+
return refs.length === 0 ? undefined : refs;
|
|
656
622
|
}
|
|
657
623
|
/**
|
|
658
624
|
* 提供引用
|
|
@@ -685,7 +651,28 @@ class LanguageService {
|
|
|
685
651
|
* @param newName 新名称
|
|
686
652
|
*/
|
|
687
653
|
async provideRenameEdits(text, position, newName) {
|
|
688
|
-
|
|
654
|
+
this.#checkSignature();
|
|
655
|
+
const root = await this.#queue(text), node = elementFromWord(root, position), { type } = node, refName = getRefName(node), refGroup = getRefGroup(node);
|
|
656
|
+
const name = getName(node), refs = root.querySelectorAll(type).filter(token => {
|
|
657
|
+
const { type: t } = token.parentNode;
|
|
658
|
+
if (type === 'link-target' && t !== 'link' && t !== 'redirect-target') {
|
|
659
|
+
return false;
|
|
660
|
+
}
|
|
661
|
+
return type === 'attr-value'
|
|
662
|
+
? getRefName(token) === refName || getRefGroup(token) === refGroup
|
|
663
|
+
: getName(token) === name;
|
|
664
|
+
});
|
|
665
|
+
if (refs.length === 0) {
|
|
666
|
+
return undefined;
|
|
667
|
+
}
|
|
668
|
+
return {
|
|
669
|
+
changes: {
|
|
670
|
+
'': refs.map((ref) => ({
|
|
671
|
+
range: createNodeRange(ref),
|
|
672
|
+
newText: newName,
|
|
673
|
+
})),
|
|
674
|
+
},
|
|
675
|
+
};
|
|
689
676
|
}
|
|
690
677
|
/**
|
|
691
678
|
* 检索解析器函数
|
|
@@ -704,12 +691,10 @@ class LanguageService {
|
|
|
704
691
|
if (!this.data) {
|
|
705
692
|
return undefined;
|
|
706
693
|
}
|
|
707
|
-
|
|
708
|
-
throw new Error('This is a signature language server!');
|
|
709
|
-
}
|
|
694
|
+
this.#checkSignature();
|
|
710
695
|
const token = elementFromWord(await this.#queue(text), position);
|
|
711
696
|
let info, f;
|
|
712
|
-
if (token.
|
|
697
|
+
if (token.is('double-underscore')) {
|
|
713
698
|
info = this.data.behaviorSwitches.find(({ aliases }) => aliases.includes(token.innerText.toLowerCase()));
|
|
714
699
|
}
|
|
715
700
|
else if (token.type === 'magic-word-name') {
|
|
@@ -727,7 +712,6 @@ class LanguageService {
|
|
|
727
712
|
range: createNodeRange(token),
|
|
728
713
|
};
|
|
729
714
|
}
|
|
730
|
-
/* NOT FOR BROWSER ONLY */
|
|
731
715
|
/**
|
|
732
716
|
* 提供魔术字帮助
|
|
733
717
|
* @param text 源代码
|
|
@@ -743,7 +727,7 @@ class LanguageService {
|
|
|
743
727
|
throw new Error('This is a regular language server!');
|
|
744
728
|
}
|
|
745
729
|
this.#signature = true;
|
|
746
|
-
const { line, character } = position, curLine = text.split(/\r?\n/u)[line], { lastChild } = await this.#queue(`${curLine.slice(0, character + /^[^{}<]*/u.exec(curLine.slice(character))[0].length)}}}`), { type, name, childNodes, firstChild } = lastChild;
|
|
730
|
+
const { line, character } = position, curLine = text.split(/\r?\n/u, line + 1)[line], { lastChild } = await this.#queue(`${curLine.slice(0, character + /^[^{}<]*/u.exec(curLine.slice(character))[0].length)}}}`), { type, name, childNodes, firstChild } = lastChild;
|
|
747
731
|
if (type !== 'magic-word') {
|
|
748
732
|
return undefined;
|
|
749
733
|
}
|
|
@@ -751,7 +735,12 @@ class LanguageService {
|
|
|
751
735
|
if (!info?.signatures) {
|
|
752
736
|
return undefined;
|
|
753
737
|
}
|
|
754
|
-
const f = firstChild.text().trim(), n = childNodes.length - 1, start = lastChild.getAbsoluteIndex()
|
|
738
|
+
const f = firstChild.text().trim(), n = childNodes.length - 1, start = lastChild.getAbsoluteIndex();
|
|
739
|
+
let activeParameter = childNodes.findIndex(child => child.getRelativeIndex() > character - start) - 2;
|
|
740
|
+
if (activeParameter === -3) {
|
|
741
|
+
activeParameter = n - 1;
|
|
742
|
+
}
|
|
743
|
+
const signatures = info.signatures.filter(params => (params.length >= n || params[params.length - 1]?.rest)
|
|
755
744
|
&& params.every(({ label, const: c }, i) => {
|
|
756
745
|
const p = c && i < n && childNodes[i + 1]?.text().trim();
|
|
757
746
|
return !p || label.startsWith(p) || label.startsWith(p.toLowerCase());
|
|
@@ -765,10 +754,29 @@ class LanguageService {
|
|
|
765
754
|
}));
|
|
766
755
|
return { signatures, activeParameter };
|
|
767
756
|
}
|
|
757
|
+
/**
|
|
758
|
+
* 提供 CodeLens
|
|
759
|
+
* @param text 源代码
|
|
760
|
+
*/
|
|
761
|
+
async provideInlayHints(text) {
|
|
762
|
+
this.#checkSignature();
|
|
763
|
+
const hints = [], root = await this.#queue(text);
|
|
764
|
+
for (const template of root.querySelectorAll('template,magic-word#invoke')) {
|
|
765
|
+
const { type, childNodes } = template;
|
|
766
|
+
hints.push(...childNodes.slice(type === 'template' ? 1 : 3).filter(({ anon }) => anon)
|
|
767
|
+
.map((parameter) => ({
|
|
768
|
+
position: positionAt(root, parameter.getAbsoluteIndex()),
|
|
769
|
+
label: `${parameter.name}=`,
|
|
770
|
+
kind: 2,
|
|
771
|
+
})));
|
|
772
|
+
}
|
|
773
|
+
return hints;
|
|
774
|
+
}
|
|
775
|
+
/* NOT FOR BROWSER ONLY */
|
|
768
776
|
/** @implements */
|
|
769
777
|
// eslint-disable-next-line @typescript-eslint/class-methods-use-this
|
|
770
778
|
provideCodeAction(diagnostics) {
|
|
771
|
-
return diagnostics.
|
|
779
|
+
return diagnostics.flatMap(diagnostic => diagnostic.data.map((data) => ({
|
|
772
780
|
title: data.title,
|
|
773
781
|
kind: 'quickfix',
|
|
774
782
|
diagnostics: [diagnostic],
|