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.
Files changed (60) hide show
  1. package/bundle/bundle.es7.js +26 -29
  2. package/data/signatures.json +82 -4
  3. package/dist/addon/table.js +9 -3
  4. package/dist/addon/token.js +73 -90
  5. package/dist/addon/transclude.js +3 -3
  6. package/dist/base.d.mts +10 -2
  7. package/dist/base.d.ts +10 -2
  8. package/dist/index.d.ts +0 -1
  9. package/dist/index.js +6 -6
  10. package/dist/lib/attributes.d.ts +24 -0
  11. package/dist/lib/attributes.js +67 -0
  12. package/dist/lib/element.js +9 -9
  13. package/dist/lib/lsp.d.ts +16 -2
  14. package/dist/lib/lsp.js +159 -148
  15. package/dist/lib/node.d.ts +2 -0
  16. package/dist/lib/node.js +93 -50
  17. package/dist/lib/ranges.d.ts +4 -8
  18. package/dist/lib/ranges.js +12 -19
  19. package/dist/lib/text.js +6 -4
  20. package/dist/lib/title.js +3 -1
  21. package/dist/parser/braces.js +3 -3
  22. package/dist/parser/commentAndExt.js +2 -3
  23. package/dist/parser/hrAndDoubleUnderscore.js +1 -2
  24. package/dist/parser/links.js +1 -2
  25. package/dist/parser/magicLinks.js +4 -2
  26. package/dist/parser/selector.js +37 -30
  27. package/dist/src/html.js +6 -4
  28. package/dist/src/imageParameter.js +12 -13
  29. package/dist/src/imagemap.js +3 -3
  30. package/dist/src/index.d.ts +23 -19
  31. package/dist/src/index.js +74 -48
  32. package/dist/src/link/file.js +1 -2
  33. package/dist/src/magicLink.js +8 -6
  34. package/dist/src/nested.js +4 -1
  35. package/dist/src/nowiki/index.js +3 -3
  36. package/dist/src/nowiki/list.js +1 -1
  37. package/dist/src/redirect.js +1 -2
  38. package/dist/src/table/index.js +10 -4
  39. package/dist/src/table/td.js +47 -55
  40. package/dist/src/transclude.js +5 -5
  41. package/dist/util/constants.js +2 -0
  42. package/dist/util/debug.js +14 -18
  43. package/dist/util/html.js +2 -0
  44. package/dist/util/lint.js +24 -4
  45. package/dist/util/string.js +9 -5
  46. package/extensions/es7/base.js +22 -4
  47. package/extensions/es7/lint.js +20 -11
  48. package/extensions/typings.d.ts +1 -2
  49. package/package.json +4 -3
  50. package/bundle/bundle.lsp.js +0 -42
  51. package/bundle/bundle.min.js +0 -38
  52. package/extensions/dist/base.js +0 -268
  53. package/extensions/dist/codejar.js +0 -56
  54. package/extensions/dist/editor.js +0 -159
  55. package/extensions/dist/highlight.js +0 -30
  56. package/extensions/dist/lint.js +0 -72
  57. package/extensions/dist/lsp.js +0 -67
  58. package/extensions/dist/test-page.js +0 -89
  59. package/extensions/editor.css +0 -59
  60. 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, Hover, CodeAction, SignatureHelp } from 'vscode-languageserver-types';
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, width, height),
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 elementFromPoint = (root, pos) => {
96
- const { line, character } = pos;
97
- let offset = root.indexFromPos(line, character), node = root;
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 token at the position from a word.
117
- * @param root root token
118
- * @param pos position
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 elementFromWord = (root, pos) => {
121
- const { line, character } = pos, offset = Number(/\w/u.test(root.toString().charAt(root.indexFromPos(line, character))));
122
- return elementFromPoint(root, { line, character: character + offset });
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
- section.range.end = { line, character: lines[line].length };
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
- /* istanbul ignore if */
207
- if (typeof text !== 'string') {
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.replace(/\r$/gmu, '');
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 text = this.#text, root = index_1.default.parse(text, true);
227
- if (this.#text === text) {
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
- /* istanbul ignore if */
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').flatMap(({ type, childNodes }) => {
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').flatMap(child => {
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: new RegExp('(?:' // eslint-disable-line prefer-template
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
- /* istanbul ignore if */
330
- if (this.#signature) {
331
- throw new Error('This is a signature language server!');
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] === '{{{') { // argument
345
- return getCompletion(root.querySelectorAll('arg').map(({ name }) => name), 'Variable', mt[3], position);
346
- }
347
- else if (mt?.[3]) { // parser function, template or link
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
- return [
353
- ...getCompletion(functions, 'Function', mt[3], position),
354
- ...mt[3].startsWith('#')
355
- ? []
356
- : getCompletion(root.querySelectorAll('template')
357
- .map(({ name }) => colon ? name : name.replace(/^Template:/u, '')), 'Folder', str, position),
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(root, position), { type, parentNode } = token;
361
- if (mt?.[6]?.trim() || type === 'image-parameter') { // image parameter
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 key = mt?.[9] ?? token.toString(), thisHtmlAttrs = sharable_1.htmlAttrs[tag], thisExtAttrs = sharable_1.extAttrs[tag], extCompletion = thisExtAttrs && getCompletion(thisExtAttrs, 'Field', key, position);
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
- return getCompletion(root.querySelectorAll('parameter').filter(({ anon, parentNode: parent }) => !anon && parent.type === 'template'
392
- && parent.name === parentNode.parentNode.name).map(({ name }) => name), 'Variable', token.toString().trimStart(), position);
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
- /* istanbul ignore if */
402
- if (this.#signature) {
403
- throw new Error('This is a signature language server!');
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
- /* istanbul ignore if */
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 = this.#text.split('\n'), { length } = lines, levels = new Array(6), tokens = root.querySelectorAll(fold ? 'heading-title,table,template,magic-word' : 'heading-title');
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, height),
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
- /* istanbul ignore if */
539
- if (this.#signature) {
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, width, height),
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, newName) {
605
- /* istanbul ignore if */
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 > 1 && type === 'parameter-key' && /^[1-9]\d*$/u.test(node.parentNode.name)
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 => usage !== 3 && token.type === 'parameter-key' ? token.parentNode : token);
645
- if (refs.length === 0) {
646
- return undefined;
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
- return this.#provideReferencesOrDefinition(text, position, 3, newName);
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
- else if (this.#signature) {
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.type === 'double-underscore') {
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(), activeParameter = childNodes.findLastIndex(child => child.getRelativeIndex() <= character - start) - 1, signatures = info.signatures.filter(params => (params.length >= n || params.at(-1)?.rest)
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.filter(({ data }) => data).flatMap(diagnostic => diagnostic.data.map((data) => ({
784
+ return diagnostics.flatMap(diagnostic => diagnostic.data.map((data) => ({
774
785
  title: data.title,
775
786
  kind: 'quickfix',
776
787
  diagnostics: [diagnostic],
@@ -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 待比较的节点