wikiparser-node 1.16.1 → 1.16.2
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/bundle/bundle.es7.js +26 -29
- package/data/signatures.json +82 -4
- package/dist/addon/table.js +9 -3
- package/dist/addon/token.js +73 -90
- package/dist/addon/transclude.js +3 -3
- package/dist/base.d.mts +10 -2
- package/dist/base.d.ts +10 -2
- package/dist/index.d.ts +0 -1
- package/dist/index.js +6 -6
- package/dist/lib/attributes.d.ts +24 -0
- package/dist/lib/attributes.js +67 -0
- package/dist/lib/element.js +9 -9
- package/dist/lib/lsp.d.ts +16 -2
- package/dist/lib/lsp.js +159 -148
- package/dist/lib/node.d.ts +2 -0
- package/dist/lib/node.js +93 -50
- package/dist/lib/ranges.d.ts +4 -8
- package/dist/lib/ranges.js +12 -19
- package/dist/lib/text.js +6 -4
- package/dist/lib/title.js +3 -1
- package/dist/parser/braces.js +3 -3
- package/dist/parser/commentAndExt.js +2 -3
- package/dist/parser/hrAndDoubleUnderscore.js +1 -2
- package/dist/parser/links.js +1 -2
- package/dist/parser/magicLinks.js +4 -2
- package/dist/parser/selector.js +37 -30
- package/dist/src/html.js +6 -4
- package/dist/src/imageParameter.js +12 -13
- package/dist/src/imagemap.js +3 -3
- package/dist/src/index.d.ts +23 -19
- package/dist/src/index.js +74 -48
- package/dist/src/link/file.js +1 -2
- package/dist/src/magicLink.js +8 -6
- package/dist/src/nested.js +4 -1
- package/dist/src/nowiki/index.js +3 -3
- package/dist/src/nowiki/list.js +1 -1
- package/dist/src/redirect.js +1 -2
- package/dist/src/table/index.js +10 -4
- package/dist/src/table/td.js +47 -55
- package/dist/src/transclude.js +5 -5
- package/dist/util/constants.js +2 -0
- package/dist/util/debug.js +14 -18
- package/dist/util/html.js +2 -0
- package/dist/util/lint.js +24 -4
- package/dist/util/string.js +9 -5
- package/extensions/es7/base.js +22 -4
- package/extensions/es7/lint.js +20 -11
- package/extensions/typings.d.ts +1 -2
- package/package.json +4 -3
- package/bundle/bundle.lsp.js +0 -42
- package/bundle/bundle.min.js +0 -38
- package/extensions/dist/base.js +0 -268
- package/extensions/dist/codejar.js +0 -56
- package/extensions/dist/editor.js +0 -159
- package/extensions/dist/highlight.js +0 -30
- package/extensions/dist/lint.js +0 -72
- package/extensions/dist/lsp.js +0 -67
- package/extensions/dist/test-page.js +0 -89
- package/extensions/editor.css +0 -59
- package/extensions/ui.css +0 -162
package/dist/lib/lsp.d.ts
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import Parser from '../index';
|
|
2
|
-
import type { Range, Position, ColorInformation, ColorPresentation, FoldingRange, DocumentSymbol, DocumentLink, Location, WorkspaceEdit, Diagnostic,
|
|
2
|
+
import type { Range, Position, ColorInformation, ColorPresentation, FoldingRange, DocumentSymbol, DocumentLink, Location, WorkspaceEdit, Diagnostic as DiagnosticBase, TextEdit, Hover, SignatureHelp, InlayHint, CodeAction } from 'vscode-languageserver-types';
|
|
3
3
|
import type { LanguageService as LanguageServiceBase, CompletionItem, SignatureData } from '../base';
|
|
4
|
+
declare interface QuickFixData extends TextEdit {
|
|
5
|
+
title: string;
|
|
6
|
+
fix: boolean;
|
|
7
|
+
}
|
|
8
|
+
declare interface Diagnostic extends DiagnosticBase {
|
|
9
|
+
data: QuickFixData[];
|
|
10
|
+
}
|
|
4
11
|
export declare const tasks: WeakMap<object, Parser.LanguageService>;
|
|
5
12
|
/** VSCode-style language service */
|
|
6
13
|
export declare class LanguageService implements LanguageServiceBase {
|
|
@@ -28,8 +35,9 @@ export declare class LanguageService implements LanguageServiceBase {
|
|
|
28
35
|
/**
|
|
29
36
|
* 提供语法诊断
|
|
30
37
|
* @param wikitext 源代码
|
|
38
|
+
* @param warning 是否提供警告
|
|
31
39
|
*/
|
|
32
|
-
provideDiagnostics(wikitext: string): Promise<Diagnostic[]>;
|
|
40
|
+
provideDiagnostics(wikitext: string, warning?: boolean): Promise<Diagnostic[]>;
|
|
33
41
|
/**
|
|
34
42
|
* 提供折叠范围
|
|
35
43
|
* @param text 源代码
|
|
@@ -77,6 +85,11 @@ export declare class LanguageService implements LanguageServiceBase {
|
|
|
77
85
|
* @param position 位置
|
|
78
86
|
*/
|
|
79
87
|
provideSignatureHelp(text: string, position: Position): Promise<SignatureHelp | undefined>;
|
|
88
|
+
/**
|
|
89
|
+
* 提供 CodeLens
|
|
90
|
+
* @param text 源代码
|
|
91
|
+
*/
|
|
92
|
+
provideInlayHints(text: string): Promise<InlayHint[]>;
|
|
80
93
|
/** @implements */
|
|
81
94
|
provideCodeAction(diagnostics: Diagnostic[]): CodeAction[];
|
|
82
95
|
/**
|
|
@@ -85,3 +98,4 @@ export declare class LanguageService implements LanguageServiceBase {
|
|
|
85
98
|
*/
|
|
86
99
|
provideDocumentSymbols(text: string): Promise<DocumentSymbol[]>;
|
|
87
100
|
}
|
|
101
|
+
export {};
|
package/dist/lib/lsp.js
CHANGED
|
@@ -3,7 +3,6 @@ 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");
|
|
@@ -11,6 +10,7 @@ const element_1 = require("../lib/element");
|
|
|
11
10
|
/* NOT FOR BROWSER */
|
|
12
11
|
const constants_1 = require("../util/constants");
|
|
13
12
|
exports.tasks = new WeakMap();
|
|
13
|
+
const refTags = new Set(['ref']), referencesTags = new Set(['ref', 'references']), nameAttrs = new Set(['name', 'extends', 'follow']), groupAttrs = new Set(['group']);
|
|
14
14
|
/**
|
|
15
15
|
* Check if all child nodes are plain text or comments.
|
|
16
16
|
* @param childNodes child nodes
|
|
@@ -43,7 +43,7 @@ const createNodeRange = (token) => {
|
|
|
43
43
|
const { top, left, height, width } = token.getBoundingClientRect();
|
|
44
44
|
return {
|
|
45
45
|
start: { line: top, character: left },
|
|
46
|
-
end: (0, lint_1.getEndPos)(top, left,
|
|
46
|
+
end: (0, lint_1.getEndPos)(top, left, height, width),
|
|
47
47
|
};
|
|
48
48
|
};
|
|
49
49
|
/**
|
|
@@ -55,7 +55,7 @@ const createNodeRange = (token) => {
|
|
|
55
55
|
* @param pos.line line number
|
|
56
56
|
* @param pos.character character number
|
|
57
57
|
*/
|
|
58
|
-
const getCompletion = (words, kind, mt, { line, character }) => [...new Set(words)].map(w => ({
|
|
58
|
+
const getCompletion = (words, kind, mt, { line, character }) => [...new Set(words)].map((w) => ({
|
|
59
59
|
label: w,
|
|
60
60
|
kind,
|
|
61
61
|
textEdit: {
|
|
@@ -88,59 +88,34 @@ const getUrl = (page, ns) => {
|
|
|
88
88
|
: articlePath + (articlePath.endsWith('/') ? '' : '/') + encoded;
|
|
89
89
|
};
|
|
90
90
|
/**
|
|
91
|
-
* Get the token at the position.
|
|
91
|
+
* Get the token at the position from a word.
|
|
92
92
|
* @param root root token
|
|
93
93
|
* @param pos position
|
|
94
94
|
*/
|
|
95
|
-
const
|
|
96
|
-
const { line, character } = pos;
|
|
97
|
-
|
|
98
|
-
while (true) { // eslint-disable-line no-constant-condition
|
|
99
|
-
// eslint-disable-next-line @typescript-eslint/no-loop-func
|
|
100
|
-
const child = node.childNodes.find(ch => {
|
|
101
|
-
const i = ch.getRelativeIndex();
|
|
102
|
-
if (i < offset && i + ch.toString().length >= offset) {
|
|
103
|
-
offset -= i;
|
|
104
|
-
return true;
|
|
105
|
-
}
|
|
106
|
-
return false;
|
|
107
|
-
});
|
|
108
|
-
if (!child || child.type === 'text') {
|
|
109
|
-
break;
|
|
110
|
-
}
|
|
111
|
-
node = child;
|
|
112
|
-
}
|
|
113
|
-
return node;
|
|
95
|
+
const elementFromWord = (root, pos) => {
|
|
96
|
+
const { line, character } = pos, index = root.indexFromPos(line, character), offset = Number(/[\w!#]/u.test(root.toString().charAt(index)));
|
|
97
|
+
return root.elementFromIndex(index + offset);
|
|
114
98
|
};
|
|
115
99
|
/**
|
|
116
|
-
* Get the
|
|
117
|
-
* @param
|
|
118
|
-
* @param
|
|
100
|
+
* Get the attribute of a `<ref>` tag.
|
|
101
|
+
* @param token attribute token
|
|
102
|
+
* @param tags tag names
|
|
103
|
+
* @param names attribute names
|
|
119
104
|
*/
|
|
120
|
-
const
|
|
121
|
-
const {
|
|
122
|
-
return
|
|
105
|
+
const getRefAttr = (token, tags, names) => {
|
|
106
|
+
const { type, parentNode = {} } = token, { name, tag } = parentNode;
|
|
107
|
+
return type === 'attr-value' && tags.has(tag) && names.has(name) ? token.toString().trim() : NaN;
|
|
123
108
|
};
|
|
124
109
|
/**
|
|
125
110
|
* Get the `name` attribute of a `<ref>` tag.
|
|
126
111
|
* @param token `name` attribute token
|
|
127
112
|
*/
|
|
128
|
-
const getRefName = (token) =>
|
|
129
|
-
const { type, parentNode = {} } = token, { name, tag } = parentNode;
|
|
130
|
-
return type === 'attr-value' && tag === 'ref' && ['name', 'extends', 'follow'].includes(name)
|
|
131
|
-
? token.toString().trim()
|
|
132
|
-
: NaN;
|
|
133
|
-
};
|
|
113
|
+
const getRefName = (token) => getRefAttr(token, refTags, nameAttrs);
|
|
134
114
|
/**
|
|
135
115
|
* Get the `group` attribute of a `<ref>` or `<references>` tag.
|
|
136
116
|
* @param token `group` attribute token
|
|
137
117
|
*/
|
|
138
|
-
const getRefGroup = (token) =>
|
|
139
|
-
const { type, parentNode = {} } = token, { name, tag } = parentNode;
|
|
140
|
-
return type === 'attr-value' && name === 'group' && (tag === 'ref' || tag === 'references')
|
|
141
|
-
? token.toString().trim()
|
|
142
|
-
: NaN;
|
|
143
|
-
};
|
|
118
|
+
const getRefGroup = (token) => getRefAttr(token, referencesTags, groupAttrs);
|
|
144
119
|
/**
|
|
145
120
|
* Get the effective name of a token.
|
|
146
121
|
* @param token
|
|
@@ -171,7 +146,8 @@ const getName = (token) => {
|
|
|
171
146
|
*/
|
|
172
147
|
const getSectionEnd = (section, lines, line) => {
|
|
173
148
|
if (section) {
|
|
174
|
-
|
|
149
|
+
const [, start, end] = lines[line];
|
|
150
|
+
section.range.end = { line, character: end - start };
|
|
175
151
|
}
|
|
176
152
|
};
|
|
177
153
|
/* NOT FOR BROWSER ONLY END */
|
|
@@ -180,6 +156,7 @@ class LanguageService {
|
|
|
180
156
|
#text;
|
|
181
157
|
#running;
|
|
182
158
|
#done;
|
|
159
|
+
#config;
|
|
183
160
|
#completionConfig;
|
|
184
161
|
#signature;
|
|
185
162
|
/** @private */
|
|
@@ -203,14 +180,11 @@ class LanguageService {
|
|
|
203
180
|
* - 否则开始新的解析
|
|
204
181
|
*/
|
|
205
182
|
async #queue(text) {
|
|
206
|
-
|
|
207
|
-
if (
|
|
208
|
-
return (0, debug_1.typeError)(this.constructor, 'queue', 'String');
|
|
209
|
-
}
|
|
210
|
-
else if (this.#text === text && !this.#running && this.#done) {
|
|
183
|
+
text = text.replace(/\r$/gmu, '');
|
|
184
|
+
if (this.#text === text && this.#config === index_1.default.config && !this.#running) {
|
|
211
185
|
return this.#done;
|
|
212
186
|
}
|
|
213
|
-
this.#text = text
|
|
187
|
+
this.#text = text;
|
|
214
188
|
this.#running ??= this.#parse(); // 不要提交多个解析任务
|
|
215
189
|
return this.#running;
|
|
216
190
|
}
|
|
@@ -223,8 +197,10 @@ class LanguageService {
|
|
|
223
197
|
async #parse() {
|
|
224
198
|
return new Promise(resolve => {
|
|
225
199
|
(typeof setImmediate === 'function' ? setImmediate : /* istanbul ignore next */ setTimeout)(() => {
|
|
226
|
-
const
|
|
227
|
-
|
|
200
|
+
const config = index_1.default.getConfig();
|
|
201
|
+
this.#config = index_1.default.config;
|
|
202
|
+
const text = this.#text, root = index_1.default.parse(text, true, undefined, config);
|
|
203
|
+
if (this.#text === text && this.#config === index_1.default.config) {
|
|
228
204
|
this.#done = root;
|
|
229
205
|
this.#running = undefined;
|
|
230
206
|
resolve(root);
|
|
@@ -237,6 +213,16 @@ class LanguageService {
|
|
|
237
213
|
}, 0);
|
|
238
214
|
});
|
|
239
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* 检查是否为签名语言服务器
|
|
218
|
+
* @throws `Error` 是签名语言服务器
|
|
219
|
+
*/
|
|
220
|
+
#checkSignature() {
|
|
221
|
+
/* istanbul ignore if */
|
|
222
|
+
if (this.#signature) {
|
|
223
|
+
throw new Error('This is a signature language server!');
|
|
224
|
+
}
|
|
225
|
+
}
|
|
240
226
|
/**
|
|
241
227
|
* 提供颜色指示
|
|
242
228
|
* @param rgba 颜色解析函数
|
|
@@ -244,16 +230,15 @@ class LanguageService {
|
|
|
244
230
|
* @param hsl 是否允许HSL颜色
|
|
245
231
|
*/
|
|
246
232
|
async provideDocumentColors(rgba, text, hsl = true) {
|
|
247
|
-
|
|
248
|
-
if (this.#signature) {
|
|
249
|
-
throw new Error('This is a signature language server!');
|
|
250
|
-
}
|
|
233
|
+
this.#checkSignature();
|
|
251
234
|
const root = await this.#queue(text);
|
|
252
|
-
return root.querySelectorAll('attr-value,parameter-value,arg-default')
|
|
235
|
+
return root.querySelectorAll('attr-value,parameter-value,arg-default')
|
|
236
|
+
.flatMap(({ type, childNodes }) => {
|
|
253
237
|
if (type !== 'attr-value' && !isPlain(childNodes)) {
|
|
254
238
|
return [];
|
|
255
239
|
}
|
|
256
|
-
return childNodes.filter((child) => child.type === 'text')
|
|
240
|
+
return childNodes.filter((child) => child.type === 'text')
|
|
241
|
+
.flatMap(child => {
|
|
257
242
|
const parts = (0, common_1.splitColors)(child.data, hsl).filter(([, , , isColor]) => isColor);
|
|
258
243
|
if (parts.length === 0) {
|
|
259
244
|
return [];
|
|
@@ -289,21 +274,24 @@ class LanguageService {
|
|
|
289
274
|
#prepareCompletionConfig() {
|
|
290
275
|
if (!this.#completionConfig) {
|
|
291
276
|
const { nsid, ext, html, parserFunction: [insensitive, sensitive, ...other], doubleUnderscore, protocol, img, } = index_1.default.getConfig(), tags = new Set([ext, html].flat(2));
|
|
277
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions, es-x/no-regexp-unicode-property-escapes
|
|
278
|
+
/(?:<\/?(\w*)|(\{{2,4}|\[\[)\s*([^|{}<>[\]\s][^|{}<>[\]#]*)?|(__(?:(?!__)[\p{L}\d_])*)|(?<!\[)\[([a-z:/]*)|\[\[\s*(?:file|image)\s*:[^[\]{}<>]+\|([^[\]{}<>|=]*)|<(\w+)(?:\s(?:[^<>{}|=\s]+(?:\s*=\s*(?:[^\s"']\S*|(["']).*?\8))?(?=\s))*)?\s(\w*))$/iu;
|
|
279
|
+
const re = new RegExp('(?:' // eslint-disable-line prefer-template
|
|
280
|
+
+ String.raw `<\/?(\w*)` // tag
|
|
281
|
+
+ '|'
|
|
282
|
+
+ String.raw `(\{{2,4}|\[\[)\s*([^|{}<>[\]\s][^|{}<>[\]#]*)?` // braces and brackets
|
|
283
|
+
+ '|'
|
|
284
|
+
+ String.raw `(__(?:(?!__)[\p{L}\d_])*)` // behavior switch
|
|
285
|
+
+ '|'
|
|
286
|
+
+ String.raw `(?<!\[)\[([a-z:/]*)` // protocol
|
|
287
|
+
+ '|'
|
|
288
|
+
+ String.raw `\[\[\s*(?:${Object.entries(nsid).filter(([, v]) => v === 6).map(([k]) => k).join('|')})\s*:[^[\]{}<>]+\|([^[\]{}<>|=]*)` // image parameter
|
|
289
|
+
+ '|'
|
|
290
|
+
// attribute key
|
|
291
|
+
+ String.raw `<(\w+)(?:\s(?:[^<>{}|=\s]+(?:\s*=\s*(?:[^\s"']\S*|(["']).*?\8))?(?=\s))*)?\s(\w*)`
|
|
292
|
+
+ ')$', 'iu');
|
|
292
293
|
this.#completionConfig = {
|
|
293
|
-
re
|
|
294
|
-
+ String.raw `<\/?(\w+)` // tag
|
|
295
|
-
+ '|'
|
|
296
|
-
+ String.raw `(\{{2,4}|\[\[)\s*([^|{}<>[\]\s][^|{}<>[\]#]*)` // braces and brackets
|
|
297
|
-
+ '|'
|
|
298
|
-
+ String.raw `(__(?:(?!__)[\p{L}\d_])+)` // behavior switch
|
|
299
|
-
+ '|'
|
|
300
|
-
+ String.raw `(?<!\[)\[([a-z:/]+)` // protocol
|
|
301
|
-
+ '|'
|
|
302
|
-
+ String.raw `\[\[\s*(?:${Object.entries(nsid).filter(([, v]) => v === 6).map(([k]) => k).join('|')})\s*:[^[\]{}<>]+\|([^[\]{}<>|=]+)` // image parameter
|
|
303
|
-
+ '|'
|
|
304
|
-
// attribute key
|
|
305
|
-
+ String.raw `<(\w+)(?:\s(?:[^<>{}|=\s]+(?:\s*=\s*(?:[^\s"']\S*|(["']).*?\8))?(?=\s))*)?\s(\w+)`
|
|
306
|
-
+ ')$', 'iu'),
|
|
294
|
+
re,
|
|
307
295
|
ext,
|
|
308
296
|
tags,
|
|
309
297
|
allTags: [...tags, 'onlyinclude', 'includeonly', 'noinclude'],
|
|
@@ -326,39 +314,37 @@ class LanguageService {
|
|
|
326
314
|
* @param position 位置
|
|
327
315
|
*/
|
|
328
316
|
async provideCompletionItems(text, position) {
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
}
|
|
333
|
-
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) ?? '');
|
|
334
|
-
if (mt?.[1]) { // tag
|
|
317
|
+
this.#checkSignature();
|
|
318
|
+
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) ?? '');
|
|
319
|
+
if (mt?.[1] !== undefined) { // tag
|
|
335
320
|
return getCompletion(allTags, 'Class', mt[1], position);
|
|
336
321
|
}
|
|
337
322
|
else if (mt?.[4]) { // behavior switch
|
|
338
323
|
return getCompletion(switches, 'Constant', mt[4], position);
|
|
339
324
|
}
|
|
340
|
-
else if (mt?.[5]) { // protocol
|
|
325
|
+
else if (mt?.[5] !== undefined) { // protocol
|
|
341
326
|
return getCompletion(protocols, 'Reference', mt[5], position);
|
|
342
327
|
}
|
|
343
328
|
const root = await this.#queue(text);
|
|
344
|
-
if (mt?.[2]
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
const colon = mt[3].startsWith(':'), str = colon ? mt[3].slice(1).trimStart() : mt[3];
|
|
349
|
-
if (mt[2] === '[[') {
|
|
350
|
-
return getCompletion(root.querySelectorAll('link,file,category').map(({ name }) => name), 'Folder', str, position);
|
|
329
|
+
if (mt?.[2]) {
|
|
330
|
+
const match = mt[3] ?? '';
|
|
331
|
+
if (mt[2] === '{{{') { // argument
|
|
332
|
+
return getCompletion(root.querySelectorAll('arg').map(({ name }) => name).filter(Boolean), 'Variable', match, position);
|
|
351
333
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
334
|
+
const colon = match.startsWith(':'), str = colon ? match.slice(1).trimStart() : match;
|
|
335
|
+
return mt[2] === '[['
|
|
336
|
+
? getCompletion(// link
|
|
337
|
+
root.querySelectorAll('link,file,category,redirect-target').map(({ name }) => name), 'Folder', str, position)
|
|
338
|
+
: [
|
|
339
|
+
...getCompletion(functions, 'Function', match, position),
|
|
340
|
+
...match.startsWith('#')
|
|
341
|
+
? []
|
|
342
|
+
: getCompletion(root.querySelectorAll('template')
|
|
343
|
+
.map(({ name }) => colon ? name : name.replace(/^Template:/u, '')), 'Folder', str, position),
|
|
344
|
+
];
|
|
359
345
|
}
|
|
360
|
-
const token = elementFromPoint(
|
|
361
|
-
if (mt?.[6]
|
|
346
|
+
const token = root.elementFromPoint(character, line), { type, parentNode } = token;
|
|
347
|
+
if (mt?.[6] !== undefined || type === 'image-parameter') { // image parameter
|
|
362
348
|
const match = mt?.[6]?.trimStart()
|
|
363
349
|
?? this.#text.slice(token.getAbsoluteIndex(), root.indexFromPos(position.line, position.character)).trimStart();
|
|
364
350
|
return [
|
|
@@ -366,12 +352,12 @@ class LanguageService {
|
|
|
366
352
|
...getCompletion(root.querySelectorAll('image-parameter#width').map(width => width.text()), 'Unit', match, position),
|
|
367
353
|
];
|
|
368
354
|
}
|
|
369
|
-
else if (mt?.[7] || type === 'attr-key') { // attribute key
|
|
370
|
-
const tag = mt?.[7]?.toLowerCase() ?? parentNode.tag;
|
|
355
|
+
else if (mt?.[7] !== undefined || type === 'attr-key') { // attribute key
|
|
356
|
+
const tag = mt?.[7]?.toLowerCase() ?? parentNode.tag, key = mt?.[9] ?? token.toString();
|
|
371
357
|
if (!tags.has(tag)) {
|
|
372
358
|
return undefined;
|
|
373
359
|
}
|
|
374
|
-
const
|
|
360
|
+
const thisHtmlAttrs = sharable_1.htmlAttrs[tag], thisExtAttrs = sharable_1.extAttrs[tag], extCompletion = thisExtAttrs && getCompletion(thisExtAttrs, 'Field', key, position);
|
|
375
361
|
return ext.includes(tag) && !thisHtmlAttrs
|
|
376
362
|
? extCompletion
|
|
377
363
|
: [
|
|
@@ -388,22 +374,23 @@ class LanguageService {
|
|
|
388
374
|
}
|
|
389
375
|
else if ((type === 'parameter-key' || type === 'parameter-value' && parentNode.anon)
|
|
390
376
|
&& parentNode.parentNode.type === 'template') { // parameter key
|
|
391
|
-
|
|
392
|
-
|
|
377
|
+
const key = token.toString().trimStart();
|
|
378
|
+
return key
|
|
379
|
+
? getCompletion(root.querySelectorAll('parameter').filter(({ anon, parentNode: parent }) => !anon && parent.type === 'template'
|
|
380
|
+
&& parent.name === parentNode.parentNode.name).map(({ name }) => name), 'Variable', key, position)
|
|
381
|
+
: undefined;
|
|
393
382
|
}
|
|
394
383
|
return undefined;
|
|
395
384
|
}
|
|
396
385
|
/**
|
|
397
386
|
* 提供语法诊断
|
|
398
387
|
* @param wikitext 源代码
|
|
388
|
+
* @param warning 是否提供警告
|
|
399
389
|
*/
|
|
400
|
-
async provideDiagnostics(wikitext) {
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
}
|
|
405
|
-
const root = await this.#queue(wikitext);
|
|
406
|
-
return root.lint().filter(({ severity }) => severity === 'error')
|
|
390
|
+
async provideDiagnostics(wikitext, warning = true) {
|
|
391
|
+
this.#checkSignature();
|
|
392
|
+
const root = await this.#queue(wikitext), errors = root.lint();
|
|
393
|
+
return (warning ? errors : errors.filter(({ severity }) => severity === 'error'))
|
|
407
394
|
.map(({ startLine, startCol, endLine, endCol, severity, rule, message, fix, suggestions }) => ({
|
|
408
395
|
range: {
|
|
409
396
|
start: { line: startLine, character: startCol },
|
|
@@ -436,15 +423,12 @@ class LanguageService {
|
|
|
436
423
|
}));
|
|
437
424
|
}
|
|
438
425
|
async #provideFoldingRangesOrDocumentSymbols(text, fold = true) {
|
|
439
|
-
|
|
440
|
-
if (this.#signature) {
|
|
441
|
-
throw new Error('This is a signature language server!');
|
|
442
|
-
}
|
|
426
|
+
this.#checkSignature();
|
|
443
427
|
const ranges = [], symbols = [],
|
|
444
428
|
/* NOT FOR BROWSER ONLY */
|
|
445
429
|
names = new Set(), sections = new Array(6),
|
|
446
430
|
/* NOT FOR BROWSER ONLY END */
|
|
447
|
-
root = await this.#queue(text), lines =
|
|
431
|
+
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');
|
|
448
432
|
for (const token of tokens) {
|
|
449
433
|
const { top, height,
|
|
450
434
|
/* NOT FOR BROWSER ONLY */
|
|
@@ -476,7 +460,7 @@ class LanguageService {
|
|
|
476
460
|
.find(s => !names.has(s))
|
|
477
461
|
: section, container = sections.slice(0, level - 1).reverse().find(Boolean), selectionRange = {
|
|
478
462
|
start: { line: top, character: left - level },
|
|
479
|
-
end: (0, lint_1.getEndPos)(top, left, width + level
|
|
463
|
+
end: (0, lint_1.getEndPos)(top, left, height, width + level),
|
|
480
464
|
}, info = {
|
|
481
465
|
name,
|
|
482
466
|
kind: 15,
|
|
@@ -535,13 +519,11 @@ class LanguageService {
|
|
|
535
519
|
* @param text 源代码
|
|
536
520
|
*/
|
|
537
521
|
async provideLinks(text) {
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
throw new Error('This is a signature language server!');
|
|
541
|
-
}
|
|
522
|
+
this.#checkSignature();
|
|
523
|
+
/^(?:http:\/\/|\/\/)/iu; // eslint-disable-line @typescript-eslint/no-unused-expressions
|
|
542
524
|
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,'
|
|
543
525
|
+ 'image-parameter#link';
|
|
544
|
-
return (await this.#queue(text)).querySelectorAll(selector).flatMap(token => {
|
|
526
|
+
return (await this.#queue(text)).querySelectorAll(selector).flatMap((token) => {
|
|
545
527
|
const { type, parentNode, firstChild, lastChild, childNodes } = token, { name, tag } = parentNode;
|
|
546
528
|
if (!(type !== 'attr-value'
|
|
547
529
|
|| name === 'src' && ['templatestyles', 'img'].includes(tag)
|
|
@@ -588,7 +570,7 @@ class LanguageService {
|
|
|
588
570
|
{
|
|
589
571
|
range: {
|
|
590
572
|
start: { line: rect.top, character: rect.left },
|
|
591
|
-
end: (0, lint_1.getEndPos)(top, left,
|
|
573
|
+
end: (0, lint_1.getEndPos)(top, left, height, width),
|
|
592
574
|
},
|
|
593
575
|
target,
|
|
594
576
|
},
|
|
@@ -601,11 +583,8 @@ class LanguageService {
|
|
|
601
583
|
}
|
|
602
584
|
});
|
|
603
585
|
}
|
|
604
|
-
async #provideReferencesOrDefinition(text, position, usage
|
|
605
|
-
|
|
606
|
-
if (this.#signature) {
|
|
607
|
-
throw new Error('This is a signature language server!');
|
|
608
|
-
}
|
|
586
|
+
async #provideReferencesOrDefinition(text, position, usage) {
|
|
587
|
+
this.#checkSignature();
|
|
609
588
|
const renameTypes = [
|
|
610
589
|
'arg-name',
|
|
611
590
|
'template-name',
|
|
@@ -621,40 +600,30 @@ class LanguageService {
|
|
|
621
600
|
'heading',
|
|
622
601
|
...renameTypes,
|
|
623
602
|
], root = await this.#queue(text), node = elementFromWord(root, position), { type } = node, refName = getRefName(node), refGroup = getRefGroup(node);
|
|
624
|
-
if (usage
|
|
603
|
+
if (usage === 2 && type === 'parameter-key' && /^[1-9]\d*$/u.test(node.parentNode.name)
|
|
625
604
|
|| !refName && (usage === 1
|
|
626
605
|
|| !refGroup && (usage === 0
|
|
627
606
|
? !types.includes(type)
|
|
628
607
|
: !renameTypes.includes(type)
|
|
629
|
-
|| type === 'link-target' && ['link', 'redirect-target'].includes(node.parentNode.type)))) {
|
|
608
|
+
|| type === 'link-target' && !['link', 'redirect-target'].includes(node.parentNode.type)))) {
|
|
630
609
|
return undefined;
|
|
631
610
|
}
|
|
632
611
|
else if (usage === 2) {
|
|
633
612
|
return createNodeRange(node);
|
|
634
613
|
}
|
|
635
614
|
const name = getName(node), refs = root.querySelectorAll(type === 'heading-title' ? 'heading' : type).filter(token => {
|
|
615
|
+
const { name: n, parentNode } = token.parentNode;
|
|
636
616
|
if (usage === 1) {
|
|
637
|
-
const { name: n, parentNode } = token.parentNode;
|
|
638
617
|
return getRefName(token) === refName
|
|
639
618
|
&& n === 'name' && parentNode.parentNode.innerText;
|
|
640
619
|
}
|
|
641
620
|
return type === 'attr-value'
|
|
642
621
|
? getRefName(token) === refName || getRefGroup(token) === refGroup
|
|
643
622
|
: getName(token) === name;
|
|
644
|
-
}).map(token =>
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
return usage === 3
|
|
649
|
-
? {
|
|
650
|
-
changes: {
|
|
651
|
-
'': refs.map(ref => ({
|
|
652
|
-
range: createNodeRange(ref),
|
|
653
|
-
newText: newName,
|
|
654
|
-
})),
|
|
655
|
-
},
|
|
656
|
-
}
|
|
657
|
-
: refs.map((ref) => ({ range: createNodeRange(ref) }));
|
|
623
|
+
}).map((token) => ({
|
|
624
|
+
range: createNodeRange(token.type === 'parameter-key' ? token.parentNode : token),
|
|
625
|
+
}));
|
|
626
|
+
return refs.length === 0 ? undefined : refs;
|
|
658
627
|
}
|
|
659
628
|
/**
|
|
660
629
|
* 提供引用
|
|
@@ -687,7 +656,28 @@ class LanguageService {
|
|
|
687
656
|
* @param newName 新名称
|
|
688
657
|
*/
|
|
689
658
|
async provideRenameEdits(text, position, newName) {
|
|
690
|
-
|
|
659
|
+
this.#checkSignature();
|
|
660
|
+
const root = await this.#queue(text), node = elementFromWord(root, position), { type } = node, refName = getRefName(node), refGroup = getRefGroup(node);
|
|
661
|
+
const name = getName(node), refs = root.querySelectorAll(type).filter(token => {
|
|
662
|
+
const { type: t } = token.parentNode;
|
|
663
|
+
if (type === 'link-target' && t !== 'link' && t !== 'redirect-target') {
|
|
664
|
+
return false;
|
|
665
|
+
}
|
|
666
|
+
return type === 'attr-value'
|
|
667
|
+
? getRefName(token) === refName || getRefGroup(token) === refGroup
|
|
668
|
+
: getName(token) === name;
|
|
669
|
+
});
|
|
670
|
+
if (refs.length === 0) {
|
|
671
|
+
return undefined;
|
|
672
|
+
}
|
|
673
|
+
return {
|
|
674
|
+
changes: {
|
|
675
|
+
'': refs.map((ref) => ({
|
|
676
|
+
range: createNodeRange(ref),
|
|
677
|
+
newText: newName,
|
|
678
|
+
})),
|
|
679
|
+
},
|
|
680
|
+
};
|
|
691
681
|
}
|
|
692
682
|
/**
|
|
693
683
|
* 检索解析器函数
|
|
@@ -706,12 +696,10 @@ class LanguageService {
|
|
|
706
696
|
if (!this.data) {
|
|
707
697
|
return undefined;
|
|
708
698
|
}
|
|
709
|
-
|
|
710
|
-
throw new Error('This is a signature language server!');
|
|
711
|
-
}
|
|
699
|
+
this.#checkSignature();
|
|
712
700
|
const token = elementFromWord(await this.#queue(text), position);
|
|
713
701
|
let info, f;
|
|
714
|
-
if (token.
|
|
702
|
+
if (token.is('double-underscore')) {
|
|
715
703
|
info = this.data.behaviorSwitches.find(({ aliases }) => aliases.includes(token.innerText.toLowerCase()));
|
|
716
704
|
}
|
|
717
705
|
else if (token.type === 'magic-word-name') {
|
|
@@ -729,7 +717,6 @@ class LanguageService {
|
|
|
729
717
|
range: createNodeRange(token),
|
|
730
718
|
};
|
|
731
719
|
}
|
|
732
|
-
/* NOT FOR BROWSER ONLY */
|
|
733
720
|
/**
|
|
734
721
|
* 提供魔术字帮助
|
|
735
722
|
* @param text 源代码
|
|
@@ -745,7 +732,7 @@ class LanguageService {
|
|
|
745
732
|
throw new Error('This is a regular language server!');
|
|
746
733
|
}
|
|
747
734
|
this.#signature = true;
|
|
748
|
-
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;
|
|
735
|
+
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;
|
|
749
736
|
if (type !== 'magic-word') {
|
|
750
737
|
return undefined;
|
|
751
738
|
}
|
|
@@ -753,7 +740,12 @@ class LanguageService {
|
|
|
753
740
|
if (!info?.signatures) {
|
|
754
741
|
return undefined;
|
|
755
742
|
}
|
|
756
|
-
const f = firstChild.text().trim(), n = childNodes.length - 1, start = lastChild.getAbsoluteIndex()
|
|
743
|
+
const f = firstChild.text().trim(), n = childNodes.length - 1, start = lastChild.getAbsoluteIndex();
|
|
744
|
+
let activeParameter = childNodes.findIndex(child => child.getRelativeIndex() > character - start) - 2;
|
|
745
|
+
if (activeParameter === -3) {
|
|
746
|
+
activeParameter = n - 1;
|
|
747
|
+
}
|
|
748
|
+
const signatures = info.signatures.filter(params => (params.length >= n || params[params.length - 1]?.rest)
|
|
757
749
|
&& params.every(({ label, const: c }, i) => {
|
|
758
750
|
const p = c && i < n && childNodes[i + 1]?.text().trim();
|
|
759
751
|
return !p || label.startsWith(p) || label.startsWith(p.toLowerCase());
|
|
@@ -767,10 +759,29 @@ class LanguageService {
|
|
|
767
759
|
}));
|
|
768
760
|
return { signatures, activeParameter };
|
|
769
761
|
}
|
|
762
|
+
/**
|
|
763
|
+
* 提供 CodeLens
|
|
764
|
+
* @param text 源代码
|
|
765
|
+
*/
|
|
766
|
+
async provideInlayHints(text) {
|
|
767
|
+
this.#checkSignature();
|
|
768
|
+
const hints = [], root = await this.#queue(text);
|
|
769
|
+
for (const template of root.querySelectorAll('template,magic-word#invoke')) {
|
|
770
|
+
const { type, childNodes } = template;
|
|
771
|
+
hints.push(...childNodes.slice(type === 'template' ? 1 : 3).filter(({ anon }) => anon)
|
|
772
|
+
.map((parameter) => ({
|
|
773
|
+
position: positionAt(root, parameter.getAbsoluteIndex()),
|
|
774
|
+
label: `${parameter.name}=`,
|
|
775
|
+
kind: 2,
|
|
776
|
+
})));
|
|
777
|
+
}
|
|
778
|
+
return hints;
|
|
779
|
+
}
|
|
780
|
+
/* NOT FOR BROWSER ONLY */
|
|
770
781
|
/** @implements */
|
|
771
782
|
// eslint-disable-next-line @typescript-eslint/class-methods-use-this
|
|
772
783
|
provideCodeAction(diagnostics) {
|
|
773
|
-
return diagnostics.
|
|
784
|
+
return diagnostics.flatMap(diagnostic => diagnostic.data.map((data) => ({
|
|
774
785
|
title: data.title,
|
|
775
786
|
kind: 'quickfix',
|
|
776
787
|
diagnostics: [diagnostic],
|
package/dist/lib/node.d.ts
CHANGED
|
@@ -95,6 +95,8 @@ export declare abstract class AstNode implements AstNodeBase {
|
|
|
95
95
|
* @param type 节点类型
|
|
96
96
|
*/
|
|
97
97
|
is<T extends Token>(type: string): this is T;
|
|
98
|
+
/** 获取所有行的wikitext和起止位置 */
|
|
99
|
+
getLines(): [string, number, number][];
|
|
98
100
|
/**
|
|
99
101
|
* 是否是全同节点
|
|
100
102
|
* @param node 待比较的节点
|