wikilint 2.16.4 → 2.16.6

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 (121) hide show
  1. package/dist/base.d.mts +84 -27
  2. package/dist/base.d.ts +84 -27
  3. package/dist/bin/cli.js +2 -1
  4. package/dist/index.d.ts +8 -4
  5. package/dist/index.js +55 -11
  6. package/dist/lib/element.d.ts +42 -16
  7. package/dist/lib/element.js +45 -17
  8. package/dist/lib/lsp.d.ts +64 -35
  9. package/dist/lib/lsp.js +208 -159
  10. package/dist/lib/node.d.ts +50 -18
  11. package/dist/lib/node.js +66 -27
  12. package/dist/lib/text.d.ts +8 -2
  13. package/dist/lib/text.js +10 -3
  14. package/dist/lib/title.d.ts +19 -10
  15. package/dist/lib/title.js +28 -22
  16. package/dist/mixin/attributesParent.d.ts +4 -4
  17. package/dist/mixin/attributesParent.js +0 -2
  18. package/dist/mixin/hidden.d.ts +0 -2
  19. package/dist/mixin/hidden.js +0 -2
  20. package/dist/parser/braces.js +52 -20
  21. package/dist/parser/commentAndExt.js +17 -4
  22. package/dist/parser/links.js +8 -7
  23. package/dist/parser/list.js +3 -1
  24. package/dist/parser/quotes.js +2 -1
  25. package/dist/parser/redirect.js +4 -1
  26. package/dist/src/arg.d.ts +4 -2
  27. package/dist/src/arg.js +8 -4
  28. package/dist/src/atom.d.ts +5 -1
  29. package/dist/src/atom.js +5 -1
  30. package/dist/src/attribute.d.ts +9 -3
  31. package/dist/src/attribute.js +9 -3
  32. package/dist/src/attributes.d.ts +12 -4
  33. package/dist/src/attributes.js +12 -4
  34. package/dist/src/converter.d.ts +3 -1
  35. package/dist/src/converter.js +8 -5
  36. package/dist/src/converterFlags.d.ts +13 -3
  37. package/dist/src/converterFlags.js +13 -3
  38. package/dist/src/converterRule.d.ts +3 -1
  39. package/dist/src/converterRule.js +3 -1
  40. package/dist/src/extLink.d.ts +2 -0
  41. package/dist/src/extLink.js +2 -0
  42. package/dist/src/gallery.d.ts +3 -1
  43. package/dist/src/gallery.js +7 -5
  44. package/dist/src/heading.d.ts +3 -1
  45. package/dist/src/heading.js +4 -2
  46. package/dist/src/hidden.d.ts +5 -1
  47. package/dist/src/hidden.js +5 -1
  48. package/dist/src/html.d.ts +6 -2
  49. package/dist/src/html.js +6 -2
  50. package/dist/src/imageParameter.d.ts +11 -3
  51. package/dist/src/imageParameter.js +12 -4
  52. package/dist/src/imagemap.d.ts +1 -1
  53. package/dist/src/imagemap.js +5 -4
  54. package/dist/src/imagemapLink.d.ts +2 -0
  55. package/dist/src/imagemapLink.js +2 -0
  56. package/dist/src/index.d.ts +5 -3
  57. package/dist/src/index.js +28 -18
  58. package/dist/src/link/base.d.ts +4 -2
  59. package/dist/src/link/base.js +12 -6
  60. package/dist/src/link/category.d.ts +2 -0
  61. package/dist/src/link/category.js +2 -0
  62. package/dist/src/link/file.d.ts +17 -7
  63. package/dist/src/link/file.js +28 -14
  64. package/dist/src/link/galleryImage.d.ts +5 -1
  65. package/dist/src/link/galleryImage.js +9 -5
  66. package/dist/src/link/index.d.ts +2 -0
  67. package/dist/src/link/index.js +2 -0
  68. package/dist/src/link/redirectTarget.d.ts +2 -1
  69. package/dist/src/link/redirectTarget.js +4 -2
  70. package/dist/src/magicLink.d.ts +8 -4
  71. package/dist/src/magicLink.js +16 -9
  72. package/dist/src/nested.d.ts +3 -1
  73. package/dist/src/nested.js +7 -4
  74. package/dist/src/nowiki/base.d.ts +3 -1
  75. package/dist/src/nowiki/base.js +3 -1
  76. package/dist/src/nowiki/comment.d.ts +5 -1
  77. package/dist/src/nowiki/comment.js +5 -1
  78. package/dist/src/nowiki/doubleUnderscore.d.ts +5 -1
  79. package/dist/src/nowiki/doubleUnderscore.js +5 -1
  80. package/dist/src/nowiki/index.d.ts +5 -1
  81. package/dist/src/nowiki/index.js +5 -1
  82. package/dist/src/nowiki/list.d.ts +5 -1
  83. package/dist/src/nowiki/list.js +5 -1
  84. package/dist/src/nowiki/noinclude.d.ts +5 -1
  85. package/dist/src/nowiki/noinclude.js +5 -1
  86. package/dist/src/nowiki/quote.d.ts +5 -1
  87. package/dist/src/nowiki/quote.js +5 -1
  88. package/dist/src/onlyinclude.d.ts +3 -1
  89. package/dist/src/onlyinclude.js +3 -1
  90. package/dist/src/paramTag/index.d.ts +1 -1
  91. package/dist/src/paramTag/index.js +1 -1
  92. package/dist/src/parameter.d.ts +3 -1
  93. package/dist/src/parameter.js +3 -1
  94. package/dist/src/pre.d.ts +1 -1
  95. package/dist/src/pre.js +1 -1
  96. package/dist/src/redirect.d.ts +2 -0
  97. package/dist/src/redirect.js +2 -0
  98. package/dist/src/syntax.d.ts +5 -1
  99. package/dist/src/syntax.js +5 -1
  100. package/dist/src/table/base.js +3 -1
  101. package/dist/src/table/index.d.ts +20 -15
  102. package/dist/src/table/index.js +22 -23
  103. package/dist/src/table/td.d.ts +9 -3
  104. package/dist/src/table/td.js +9 -3
  105. package/dist/src/table/tr.d.ts +3 -1
  106. package/dist/src/table/tr.js +4 -2
  107. package/dist/src/table/trBase.d.ts +9 -9
  108. package/dist/src/table/trBase.js +13 -18
  109. package/dist/src/tagPair/ext.d.ts +3 -1
  110. package/dist/src/tagPair/ext.js +3 -1
  111. package/dist/src/tagPair/include.d.ts +2 -0
  112. package/dist/src/tagPair/include.js +2 -0
  113. package/dist/src/tagPair/index.d.ts +6 -2
  114. package/dist/src/tagPair/index.js +6 -2
  115. package/dist/src/transclude.d.ts +34 -10
  116. package/dist/src/transclude.js +47 -17
  117. package/dist/util/debug.js +2 -16
  118. package/dist/util/string.js +2 -0
  119. package/i18n/zh-hans.json +1 -1
  120. package/i18n/zh-hant.json +1 -1
  121. package/package.json +2 -2
package/dist/lib/lsp.js CHANGED
@@ -170,18 +170,24 @@ const getSectionEnd = (section, lines, line) => {
170
170
  /** VSCode-style language service */
171
171
  class LanguageService {
172
172
  #text;
173
+ #text2;
173
174
  #running;
175
+ #running2;
174
176
  #done;
177
+ #done2;
175
178
  #config;
179
+ #config2;
176
180
  #completionConfig;
177
- #signature;
178
181
  /** @private */
179
182
  data;
180
183
  /** @param uri 任务标识 */
181
184
  constructor(uri) {
182
185
  exports.tasks.set(uri, this);
183
186
  /* NOT FOR BROWSER ONLY */
184
- this.data = require(path.join('..', '..', 'data', 'signatures'));
187
+ Object.defineProperty(this, 'data', {
188
+ value: require(path.join('..', '..', 'data', 'signatures')),
189
+ enumerable: false,
190
+ });
185
191
  }
186
192
  /** @implements */
187
193
  destroy() {
@@ -191,7 +197,7 @@ class LanguageService {
191
197
  * 提交解析任务
192
198
  * @param text 源代码
193
199
  * @description
194
- * - 总是更新`text`以便`parse`完成时可以判断是否需要重新解析
200
+ * - 总是更新`#text`以便`#parse`完成时可以判断是否需要重新解析
195
201
  * - 如果已有进行中或已完成的解析,则返回该解析的结果
196
202
  * - 否则开始新的解析
197
203
  */
@@ -207,48 +213,71 @@ class LanguageService {
207
213
  /**
208
214
  * 执行解析
209
215
  * @description
210
- * - 完成后会检查`text`是否已更新,如果是则重新解析
216
+ * - 完成后会检查`#text`是否已更新,如果是则重新解析
211
217
  * - 总是返回最新的解析结果
212
218
  */
213
219
  async #parse() {
214
- return new Promise(resolve => {
215
- (typeof setImmediate === 'function' ? setImmediate : /* istanbul ignore next */ setTimeout)(() => {
216
- const config = index_1.default.getConfig();
217
- this.#config = index_1.default.config;
218
- const text = this.#text, root = index_1.default.parse(text, true, undefined, config);
219
- if (this.#text === text && this.#config === index_1.default.config) {
220
- this.#done = root;
221
- this.#running = undefined;
222
- resolve(root);
223
- return;
224
- }
225
- /* istanbul ignore next */
226
- this.#running = this.#parse();
227
- /* istanbul ignore next */
228
- resolve(this.#running);
229
- }, 0);
230
- });
220
+ const config = index_1.default.getConfig();
221
+ this.#config = index_1.default.config;
222
+ const text = this.#text, root = await index_1.default.partialParse(text, () => this.#text, true, config);
223
+ if (this.#text === text && this.#config === index_1.default.config) {
224
+ this.#done = root;
225
+ this.#running = undefined;
226
+ return root;
227
+ }
228
+ /* istanbul ignore next */
229
+ this.#running = this.#parse();
230
+ /* istanbul ignore next */
231
+ return this.#running;
232
+ }
233
+ /**
234
+ * 提交签名解析任务
235
+ * @param text 源代码
236
+ * @description
237
+ * - 总是更新`#text2`以便`#parseSignature`完成时可以判断是否需要重新解析
238
+ * - 如果已有进行中或已完成的解析,则返回该解析的结果
239
+ * - 否则开始新的解析
240
+ */
241
+ async #queueSignature(text) {
242
+ text = (0, string_1.tidy)(text);
243
+ if (this.#text2 === text && this.#config2 === index_1.default.config && !this.#running2) {
244
+ return this.#done2;
245
+ }
246
+ this.#text2 = text;
247
+ this.#running2 ??= this.#parseSignature(); // 不要提交多个解析任务
248
+ return this.#running2;
231
249
  }
232
250
  /**
233
- * 检查是否为签名语言服务器
234
- * @throws `Error` 是签名语言服务器
251
+ * 执行签名解析
252
+ * @description
253
+ * - 完成后会检查`#text2`是否已更新,如果是则重新解析
254
+ * - 总是返回最新的解析结果
235
255
  */
236
- #checkSignature() {
237
- /* istanbul ignore if */
238
- if (this.#signature) {
239
- throw new Error('This is a signature language server!');
256
+ async #parseSignature() {
257
+ const config = index_1.default.getConfig();
258
+ this.#config2 = index_1.default.config;
259
+ const text = this.#text2, root = await index_1.default.partialParse(text, () => this.#text2, true, config);
260
+ if (this.#text2 === text && this.#config2 === index_1.default.config) {
261
+ this.#done2 = root;
262
+ this.#running2 = undefined;
263
+ return root;
240
264
  }
265
+ /* istanbul ignore next */
266
+ this.#running2 = this.#parseSignature();
267
+ /* istanbul ignore next */
268
+ return this.#running2;
241
269
  }
242
270
  /**
271
+ * Provide color decorators
272
+ *
243
273
  * 提供颜色指示
244
- * @param rgba 颜色解析函数
245
- * @param text 源代码
246
- * @param hsl 是否允许HSL颜色
274
+ * @param rgba color parser / 颜色解析函数
275
+ * @param text source Wikitext / 源代码
276
+ * @param hsl whether HSL colors are treated / 是否允许HSL颜色
247
277
  */
248
278
  async provideDocumentColors(rgba, text, hsl = true) {
249
- this.#checkSignature();
250
279
  const root = await this.#queue(text);
251
- return root.querySelectorAll('attr-value,parameter-value,arg-default')
280
+ return root.querySelectorAll('attr-value,parameter-value,arg-default').reverse()
252
281
  .flatMap(({ type, childNodes }) => {
253
282
  if (type !== 'attr-value' && !isPlain(childNodes)) {
254
283
  return [];
@@ -275,10 +304,15 @@ class LanguageService {
275
304
  });
276
305
  });
277
306
  }
278
- /** @implements */
279
- provideColorPresentations(// eslint-disable-line @typescript-eslint/class-methods-use-this
280
- { color: { red, green, blue, alpha }, range }) {
281
- const newText = `#${(0, common_1.numToHex)(red)}${(0, common_1.numToHex)(green)}${(0, common_1.numToHex)(blue)}${alpha < 1 ? (0, common_1.numToHex)(alpha) : ''}`;
307
+ /**
308
+ * Provide color pickers
309
+ *
310
+ * 颜色选择器
311
+ * @param color color information / 颜色信息
312
+ */
313
+ // eslint-disable-next-line @typescript-eslint/class-methods-use-this
314
+ provideColorPresentations(color) {
315
+ const { color: { red, green, blue, alpha }, range } = color, newText = `#${(0, common_1.numToHex)(red)}${(0, common_1.numToHex)(green)}${(0, common_1.numToHex)(blue)}${alpha < 1 ? (0, common_1.numToHex)(alpha) : ''}`;
282
316
  return [
283
317
  {
284
318
  label: newText,
@@ -323,12 +357,13 @@ class LanguageService {
323
357
  return this.#completionConfig;
324
358
  }
325
359
  /**
360
+ * Provide auto-completion
361
+ *
326
362
  * 提供自动补全
327
- * @param text 源代码
363
+ * @param text source Wikitext / 源代码
328
364
  * @param position 位置
329
365
  */
330
366
  async provideCompletionItems(text, position) {
331
- this.#checkSignature();
332
367
  const { re, allTags, functions, switches, protocols, params, tags, ext } = this.#prepareCompletionConfig(), { line, character } = position, curLine = text.split(/\r?\n/u, line + 1)[line], mt = re.exec(curLine?.slice(0, character) ?? '');
333
368
  if (mt?.[1] !== undefined) { // tag
334
369
  const closing = mt[1].startsWith('/');
@@ -382,7 +417,8 @@ class LanguageService {
382
417
  return [
383
418
  ...getCompletion(params, 'Property', match, position)
384
419
  .filter(({ label }) => !equal || !/[= ]$/u.test(label)),
385
- ...getCompletion(root.querySelectorAll('image-parameter#width').filter(token => token !== cur)
420
+ ...getCompletion(root.querySelectorAll('image-parameter#width')
421
+ .filter(token => token !== cur)
386
422
  .map(width => width.text()), 'Unit', match, position),
387
423
  ];
388
424
  }
@@ -408,20 +444,20 @@ class LanguageService {
408
444
  }
409
445
  else if (type === 'parameter-key' || type === 'parameter-value' && parentNode.anon) {
410
446
  // parameter key
411
- const transclusion = parentNode.parentNode;
412
- if (transclusion.type === 'magic-word' && transclusion.name !== 'invoke') {
447
+ const transclusion = parentNode.parentNode, { type: t, name: n } = transclusion;
448
+ if (t === 'magic-word' && n !== 'invoke') {
413
449
  return undefined;
414
450
  }
415
- const key = cur.toString().trimStart(), [module, func] = transclusion.type === 'magic-word' ? transclusion.getModule() : [];
451
+ const key = cur.toString().trimStart(), [module, func] = t === 'magic-word' ? transclusion.getModule() : [];
416
452
  return key
417
453
  ? getCompletion(root.querySelectorAll('parameter').filter(token => {
418
454
  if (token === parentNode
419
455
  || token.anon
420
- || token.parentNode.type !== transclusion.type
421
- || token.parentNode.name !== transclusion.name) {
456
+ || token.parentNode.type !== t
457
+ || token.parentNode.name !== n) {
422
458
  return false;
423
459
  }
424
- else if (transclusion.type === 'template') {
460
+ else if (t === 'template') {
425
461
  return true;
426
462
  }
427
463
  const [m, f] = token.parentNode.getModule();
@@ -432,13 +468,14 @@ class LanguageService {
432
468
  return undefined;
433
469
  }
434
470
  /**
435
- * 提供语法诊断
436
- * @param wikitext 源代码
437
- * @param warning 是否提供警告
471
+ * Provide grammar check
472
+ *
473
+ * 提供语法检查
474
+ * @param text source Wikitext / 源代码
475
+ * @param warning whether to include warnings / 是否包含警告
438
476
  */
439
- async provideDiagnostics(wikitext, warning = true) {
440
- this.#checkSignature();
441
- const root = await this.#queue(wikitext), errors = root.lint();
477
+ async provideDiagnostics(text, warning = true) {
478
+ const root = await this.#queue(text), errors = root.lint();
442
479
  return (warning ? errors : errors.filter(({ severity }) => severity === 'error'))
443
480
  .map(({ startLine, startCol, endLine, endCol, severity, rule, message, fix, suggestions }) => ({
444
481
  range: {
@@ -457,38 +494,42 @@ class LanguageService {
457
494
  }));
458
495
  }
459
496
  /**
497
+ * Provide folding ranges
498
+ *
460
499
  * 提供折叠范围
461
- * @param text 源代码
500
+ * @param text source Wikitext / 源代码
462
501
  */
463
502
  async provideFoldingRanges(text) {
464
- this.#checkSignature();
465
503
  const root = await this.#queue(text), { length } = root.getLines(), ranges = [], levels = new Array(6), tokens = root.querySelectorAll('heading-title,table,template,magic-word');
466
504
  for (const token of [...tokens].reverse()) { // 提高 getBoundingClientRect 的性能
467
505
  token.getRelativeIndex();
468
506
  }
469
507
  for (const token of tokens) {
470
- const { top, height } = token.getBoundingClientRect();
471
- if (token.type === 'heading-title') {
472
- const { level } = token.parentNode;
473
- for (let i = level - 1; i < 6; i++) {
474
- const startLine = levels[i];
475
- if (startLine !== undefined && startLine < top - 1) {
476
- ranges.push({
477
- startLine,
478
- endLine: top - 1,
479
- kind: 'region',
480
- });
508
+ const { offsetHeight } = token;
509
+ if (token.type === 'heading-title' || offsetHeight > 2) {
510
+ const { top } = token.getBoundingClientRect();
511
+ if (token.type === 'heading-title') {
512
+ const { level } = token.parentNode;
513
+ for (let i = level - 1; i < 6; i++) {
514
+ const startLine = levels[i];
515
+ if (startLine !== undefined && startLine < top - 1) {
516
+ ranges.push({
517
+ startLine,
518
+ endLine: top - 1,
519
+ kind: 'region',
520
+ });
521
+ }
522
+ levels[i] = undefined;
481
523
  }
482
- levels[i] = undefined;
524
+ levels[level - 1] = top + offsetHeight - 1; // 从标题的最后一行开始折叠
525
+ }
526
+ else {
527
+ ranges.push({
528
+ startLine: top, // 从表格或模板的第一行开始折叠
529
+ endLine: top + offsetHeight - 2,
530
+ kind: 'region',
531
+ });
483
532
  }
484
- levels[level - 1] = top + height - 1; // 从标题的最后一行开始折叠
485
- }
486
- else if (height > 2) {
487
- ranges.push({
488
- startLine: top, // 从表格或模板的第一行开始折叠
489
- endLine: top + height - 2,
490
- kind: 'region',
491
- });
492
533
  }
493
534
  }
494
535
  for (const startLine of levels) {
@@ -503,16 +544,18 @@ class LanguageService {
503
544
  return ranges;
504
545
  }
505
546
  /**
547
+ * Provide links
548
+ *
506
549
  * 提供链接
507
- * @param text 源代码
550
+ * @param text source Wikitext / 源代码
508
551
  */
509
552
  async provideLinks(text) {
510
- this.#checkSignature();
511
553
  const { articlePath, protocol } = index_1.default.getConfig(), absolute = articlePath?.includes('//'), protocolRegex = new RegExp(`^(?:${protocol}|//)`, 'iu');
512
554
  return (await this.#queue(text))
513
- .querySelectorAll(`magic-link,ext-link-url,free-ext-link,attr-value,image-parameter#link${absolute ? ',link-target,template-name,invoke-module' : ''}`).reverse()
555
+ .querySelectorAll(`magic-link,ext-link-url,free-ext-link,attr-value,image-parameter#link${absolute ? ',link-target,template-name,invoke-module' : ''}`)
556
+ .reverse()
514
557
  .map((token) => {
515
- const { type, parentNode, firstChild, lastChild, childNodes } = token, { name, tag } = parentNode;
558
+ const { type, parentNode, firstChild, lastChild, childNodes, length } = token, { name, tag } = parentNode;
516
559
  if (!(type !== 'attr-value'
517
560
  || name === 'src' && ['templatestyles', 'img'].includes(tag)
518
561
  || name === 'cite' && ['blockquote', 'del', 'ins', 'q'].includes(tag))
@@ -520,7 +563,9 @@ class LanguageService {
520
563
  return false;
521
564
  }
522
565
  let target = childNodes.filter((node) => node.type === 'text')
523
- .map(({ data }) => data).join('').trim();
566
+ .map(({ data }) => data)
567
+ .join('')
568
+ .trim();
524
569
  if (!target) {
525
570
  return false;
526
571
  }
@@ -554,7 +599,8 @@ class LanguageService {
554
599
  else if (type === 'invoke-module') {
555
600
  ns = 828;
556
601
  }
557
- const title = index_1.default.normalizeTitle(target, ns);
602
+ const title = index_1.default
603
+ .normalizeTitle(target, ns, false, undefined, true);
558
604
  /* istanbul ignore if */
559
605
  if (!title.valid) {
560
606
  return false;
@@ -566,7 +612,7 @@ class LanguageService {
566
612
  }
567
613
  target = new URL(target).href;
568
614
  if (type === 'image-parameter') {
569
- const { top, left, height, width } = lastChild.getBoundingClientRect(), rect = firstChild.getBoundingClientRect();
615
+ const rect = firstChild.getBoundingClientRect(), { top, left, height, width } = length === 1 ? rect : lastChild.getBoundingClientRect();
570
616
  return {
571
617
  range: {
572
618
  start: { line: rect.top, character: rect.left },
@@ -580,15 +626,17 @@ class LanguageService {
580
626
  catch {
581
627
  return false;
582
628
  }
583
- }).filter(Boolean);
629
+ })
630
+ .filter(Boolean);
584
631
  }
585
632
  /**
633
+ * Provide references
634
+ *
586
635
  * 提供引用
587
- * @param text 源代码
636
+ * @param text source Wikitext / 源代码
588
637
  * @param position 位置
589
638
  */
590
639
  async provideReferences(text, position) {
591
- this.#checkSignature();
592
640
  const root = await this.#queue(text), { offsetNode, offset } = caretPositionFromWord(root, position), element = offsetNode.type === 'text' ? offsetNode.parentNode : offsetNode, node = offset === 0 && (element.type === 'ext-attr-dirty' || element.type === 'html-attr-dirty')
593
641
  ? element.parentNode.parentNode
594
642
  : element, { type } = node, refName = getRefName(node), refGroup = getRefGroup(node);
@@ -597,41 +645,41 @@ class LanguageService {
597
645
  }
598
646
  const name = getName(node), refs = root.querySelectorAll(type === 'heading-title' ? 'heading' : type).filter(token => type === 'attr-value'
599
647
  ? getRefName(token) === refName || getRefGroup(token) === refGroup
600
- : getName(token) === name).map((token) => ({
648
+ : getName(token) === name).reverse().map((token) => ({
601
649
  range: createNodeRange(token.type === 'parameter-key' ? token.parentNode : token),
602
650
  }));
603
651
  return refs.length === 0 ? undefined : refs;
604
652
  }
605
653
  /**
654
+ * Provide definitions
655
+ *
606
656
  * 提供定义
607
- * @param text 源代码
608
- * @param pos 位置
609
- * @param pos.line 行号
610
- * @param pos.character 列号
657
+ * @param text source Wikitext / 源代码
658
+ * @param position 位置
611
659
  */
612
- async provideDefinition(text, { line, character }) {
613
- this.#checkSignature();
614
- const root = await this.#queue(text), node = root.elementFromPoint(character, line), ext = node.is('ext') && node.name === 'ref' ? node : node.closest('ext#ref'), refName = getRefTagAttr(ext, 'name');
660
+ async provideDefinition(text, position) {
661
+ const root = await this.#queue(text), node = root.elementFromPoint(position.character, position.line), ext = node.is('ext') && node.name === 'ref'
662
+ ? node
663
+ : node.closest('ext#ref'), refName = getRefTagAttr(ext, 'name');
615
664
  if (!refName) {
616
665
  return undefined;
617
666
  }
618
667
  const refGroup = getRefTagAttr(ext, 'group'), refs = root.querySelectorAll('ext#ref').filter(token => token.innerText
619
668
  && getRefTagAttr(token, 'name') === refName
620
- && getRefTagAttr(token, 'group') === refGroup).map(({ lastChild }) => ({
669
+ && getRefTagAttr(token, 'group') === refGroup).reverse().map(({ lastChild }) => ({
621
670
  range: createNodeRange(lastChild),
622
671
  }));
623
672
  return refs.length === 0 ? undefined : refs;
624
673
  }
625
674
  /**
675
+ * Provide locations for renaming
676
+ *
626
677
  * 提供变量更名准备
627
- * @param text 源代码
628
- * @param pos 位置
629
- * @param pos.line 行号
630
- * @param pos.character 列号
678
+ * @param text source Wikitext / 源代码
679
+ * @param position 位置
631
680
  */
632
- async resolveRenameLocation(text, { line, character }) {
633
- this.#checkSignature();
634
- const root = await this.#queue(text), node = root.elementFromPoint(character, line), { type } = node, refName = getRefName(node), refGroup = getRefGroup(node);
681
+ async resolveRenameLocation(text, position) {
682
+ const root = await this.#queue(text), node = root.elementFromPoint(position.character, position.line), { type } = node, refName = getRefName(node), refGroup = getRefGroup(node);
635
683
  return !refName && !refGroup && (!renameTypes.has(type)
636
684
  || type === 'parameter-key' && /^[1-9]\d*$/u.test(node.parentNode.name)
637
685
  || type === 'link-target' && !['link', 'redirect-target'].includes(node.parentNode.type))
@@ -639,16 +687,15 @@ class LanguageService {
639
687
  : createNodeRange(node);
640
688
  }
641
689
  /**
690
+ * Provide rename edits
691
+ *
642
692
  * 变量更名
643
- * @param text 源代码
644
- * @param pos 位置
645
- * @param pos.line 行号
646
- * @param pos.character 列号
647
- * @param newName 新名称
693
+ * @param text source Wikitext / 源代码
694
+ * @param position 位置
695
+ * @param newName new name / 新名称
648
696
  */
649
- async provideRenameEdits(text, { line, character }, newName) {
650
- this.#checkSignature();
651
- const root = await this.#queue(text), node = root.elementFromPoint(character, line), { type } = node, refName = getRefName(node), refNameGroup = refName && getRefTagAttr(node.parentNode.parentNode, 'group'), refGroup = getRefGroup(node), name = getName(node), refs = root.querySelectorAll(type).filter(token => {
697
+ async provideRenameEdits(text, position, newName) {
698
+ const root = await this.#queue(text), node = root.elementFromPoint(position.character, position.line), { type } = node, refName = getRefName(node), refNameGroup = refName && getRefTagAttr(node.parentNode.parentNode, 'group'), refGroup = getRefGroup(node), name = getName(node), refs = root.querySelectorAll(type).filter(token => {
652
699
  const { type: t } = token.parentNode;
653
700
  if (type === 'link-target' && t !== 'link' && t !== 'redirect-target') {
654
701
  return false;
@@ -663,7 +710,7 @@ class LanguageService {
663
710
  ? undefined
664
711
  : {
665
712
  changes: {
666
- '': refs.map((ref) => ({
713
+ '': refs.reverse().map((ref) => ({
667
714
  range: createNodeRange(ref),
668
715
  newText: newName,
669
716
  })),
@@ -675,11 +722,14 @@ class LanguageService {
675
722
  * @param name 函数名
676
723
  */
677
724
  #getParserFunction(name) {
678
- return this.data.parserFunctions.find(({ aliases }) => aliases.some(alias => alias.replace(/^#/u, '') === name));
725
+ return this.data.parserFunctions
726
+ .find(({ aliases }) => aliases.some(alias => alias.replace(/^#/u, '') === name));
679
727
  }
680
728
  /**
729
+ * Provide hover information
730
+ *
681
731
  * 提供悬停信息
682
- * @param text 源代码
732
+ * @param text source Wikitext / 源代码
683
733
  * @param position 位置
684
734
  */
685
735
  async provideHover(text, position) {
@@ -687,20 +737,17 @@ class LanguageService {
687
737
  if (!this.data) {
688
738
  return undefined;
689
739
  }
690
- this.#checkSignature();
691
740
  const root = await this.#queue(text), { offsetNode, offset } = caretPositionFromWord(root, position), token = offsetNode.type === 'text' ? offsetNode.parentNode : offsetNode, { type, parentNode, length, name } = token;
692
741
  let info, f, range;
693
- if (token.is('double-underscore')) {
694
- if (offset === 0 && token.getBoundingClientRect().left > position.character) {
695
- return undefined;
696
- }
742
+ if (token.is('double-underscore') && offset > 0) {
697
743
  info = this.data.behaviorSwitches.find(({ aliases }) => aliases.includes(token.innerText.toLowerCase()));
698
744
  }
699
745
  else if (type === 'magic-word-name') {
700
746
  info = this.#getParserFunction(parentNode.name);
701
747
  f = token.toString(true).trim();
702
748
  }
703
- else if (token.is('magic-word') && length === 1 && !token.modifier) {
749
+ else if (token.is('magic-word') && !token.modifier && length === 1
750
+ && (offset > 0 || token.getBoundingClientRect().left === position.character)) {
704
751
  info = this.#getParserFunction(name);
705
752
  f = token.firstChild.toString(true).trim();
706
753
  }
@@ -728,21 +775,18 @@ class LanguageService {
728
775
  };
729
776
  }
730
777
  /**
778
+ * Provide signature help for magic words
779
+ *
731
780
  * 提供魔术字帮助
732
- * @param text 源代码
781
+ * @param text source Wikitext / 源代码
733
782
  * @param position 位置
734
783
  */
735
784
  async provideSignatureHelp(text, position) {
736
785
  /* istanbul ignore next */
737
786
  if (!this.data) {
738
787
  return undefined;
739
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
740
788
  }
741
- else if (this.#text !== undefined && !this.#signature) {
742
- throw new Error('This is a regular language server!');
743
- }
744
- this.#signature = true;
745
- 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)}}}`);
789
+ const { line, character } = position, curLine = text.split(/\r?\n/u, line + 1)[line], { lastChild } = await this.#queueSignature(`${curLine.slice(0, character + /^[^{}<]*/u.exec(curLine.slice(character))[0].length)}}}`);
746
790
  if (!lastChild.is('magic-word') || lastChild.length === 1) {
747
791
  return undefined;
748
792
  }
@@ -779,16 +823,18 @@ class LanguageService {
779
823
  };
780
824
  }
781
825
  /**
826
+ * Provide CodeLens
827
+ *
782
828
  * 提供 CodeLens
783
- * @param text 源代码
829
+ * @param text source Wikitext / 源代码
784
830
  */
785
831
  async provideInlayHints(text) {
786
- this.#checkSignature();
787
832
  const hints = [], root = await this.#queue(text);
788
- for (const template of root.querySelectorAll('template,magic-word#invoke')) {
789
- const { type, childNodes } = template;
833
+ for (const token of root.querySelectorAll('template,magic-word#invoke').reverse()) {
834
+ const { type, childNodes } = token;
790
835
  hints.push(...childNodes.slice(type === 'template' ? 1 : 3).filter(({ anon }) => anon)
791
- .reverse().map((parameter) => ({
836
+ .reverse()
837
+ .map((parameter) => ({
792
838
  position: positionAt(root, parameter.getAbsoluteIndex()),
793
839
  label: `${parameter.name}=`,
794
840
  kind: 2,
@@ -797,7 +843,12 @@ class LanguageService {
797
843
  return hints;
798
844
  }
799
845
  /* NOT FOR BROWSER ONLY */
800
- /** @implements */
846
+ /**
847
+ * Provide quick fixes
848
+ *
849
+ * 提供快速修复建议
850
+ * @param diagnostics grammar diagnostics / 语法诊断信息
851
+ */
801
852
  // eslint-disable-next-line @typescript-eslint/class-methods-use-this
802
853
  provideCodeAction(diagnostics) {
803
854
  return diagnostics.flatMap(diagnostic => diagnostic.data.map((data) => ({
@@ -811,44 +862,42 @@ class LanguageService {
811
862
  })));
812
863
  }
813
864
  /**
865
+ * Provide document sections
866
+ *
814
867
  * 提供章节
815
- * @param text 源代码
868
+ * @param text source Wikitext / 源代码
816
869
  */
817
870
  async provideDocumentSymbols(text) {
818
- this.#checkSignature();
819
871
  const root = await this.#queue(text), lines = root.getLines(), { length } = lines, symbols = [], names = new Set(), sections = new Array(6), tokens = root.querySelectorAll('heading-title');
820
872
  for (const token of [...tokens].reverse()) { // 提高 getBoundingClientRect 的性能
821
873
  token.getRelativeIndex();
822
874
  }
823
875
  for (const token of tokens) {
824
- const { top, height, left, width } = token.getBoundingClientRect();
825
- if (token.type === 'heading-title') {
826
- const { level } = token.parentNode;
827
- for (let i = level - 1; i < 6; i++) {
828
- getSectionEnd(sections[i], lines, top - 1);
829
- sections[i] = undefined;
830
- }
831
- const section = token.text().trim() || ' ', name = names.has(section)
832
- ? new Array(names.size).fill('').map((_, i) => `${section.trim()}_${i + 2}`)
833
- .find(s => !names.has(s))
834
- : section, container = sections.slice(0, level - 1).reverse().find(Boolean), selectionRange = {
835
- start: { line: top, character: left - level },
836
- end: (0, lint_1.getEndPos)(top, left, height, width + level),
837
- }, info = {
838
- name,
839
- kind: 15,
840
- range: { start: selectionRange.start },
841
- selectionRange,
842
- };
843
- names.add(name);
844
- sections[level - 1] = info;
845
- if (container) {
846
- container.children ??= [];
847
- container.children.push(info);
848
- }
849
- else {
850
- symbols.push(info);
851
- }
876
+ const { top, height, left, width } = token.getBoundingClientRect(), { level } = token.parentNode;
877
+ for (let i = level - 1; i < 6; i++) {
878
+ getSectionEnd(sections[i], lines, top - 1);
879
+ sections[i] = undefined;
880
+ }
881
+ const section = token.text().trim() || ' ', name = names.has(section)
882
+ ? new Array(names.size).fill('').map((_, i) => `${section.trim()}_${i + 2}`)
883
+ .find(s => !names.has(s))
884
+ : section, container = sections.slice(0, level - 1).reverse().find(Boolean), selectionRange = {
885
+ start: { line: top, character: left - level },
886
+ end: (0, lint_1.getEndPos)(top, left, height, width + level),
887
+ }, info = {
888
+ name,
889
+ kind: 15,
890
+ range: { start: selectionRange.start },
891
+ selectionRange,
892
+ };
893
+ names.add(name);
894
+ sections[level - 1] = info;
895
+ if (container) {
896
+ container.children ??= [];
897
+ container.children.push(info);
898
+ }
899
+ else {
900
+ symbols.push(info);
852
901
  }
853
902
  }
854
903
  for (const section of sections) {