wikilint 2.44.0 → 2.46.0

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 (80) hide show
  1. package/config/default.json +1 -2
  2. package/config/enwiki.json +1 -1
  3. package/config/jawiki.json +1 -1
  4. package/config/minimum.json +1 -1
  5. package/config/zhwiki.json +1 -1
  6. package/data/ext/templatedata.json +13 -5
  7. package/data/signatures.json +38 -32
  8. package/dist/base.d.mts +10 -2
  9. package/dist/base.d.ts +10 -2
  10. package/dist/bin/config.js +1 -1
  11. package/dist/index.js +1 -1
  12. package/dist/internal.d.ts +6 -1
  13. package/dist/lib/element.js +5 -5
  14. package/dist/lib/lintConfig.js +7 -0
  15. package/dist/lib/lsp.d.ts +11 -3
  16. package/dist/lib/lsp.js +91 -59
  17. package/dist/lib/node.js +2 -2
  18. package/dist/lib/text.js +2 -2
  19. package/dist/map.d.ts +4 -1
  20. package/dist/mixin/elementLike.js +29 -31
  21. package/dist/mixin/hidden.js +0 -1
  22. package/dist/mixin/paramLineParent.d.ts +29 -0
  23. package/dist/mixin/paramLineParent.js +51 -0
  24. package/dist/src/arg.js +17 -35
  25. package/dist/src/atom.d.ts +1 -1
  26. package/dist/src/atom.js +2 -0
  27. package/dist/src/attribute.js +10 -5
  28. package/dist/src/attributes.js +18 -15
  29. package/dist/src/commented.d.ts +1 -0
  30. package/dist/src/commented.js +1 -3
  31. package/dist/src/converterFlags.js +18 -21
  32. package/dist/src/converterRule.js +2 -2
  33. package/dist/src/funcTag.d.ts +23 -0
  34. package/dist/src/funcTag.js +85 -0
  35. package/dist/src/heading.js +8 -3
  36. package/dist/src/hidden.d.ts +3 -2
  37. package/dist/src/hidden.js +29 -5
  38. package/dist/src/imageParameter.js +16 -4
  39. package/dist/src/index.js +2 -2
  40. package/dist/src/link/base.js +14 -2
  41. package/dist/src/link/categorytree.d.ts +2 -0
  42. package/dist/src/link/categorytree.js +8 -5
  43. package/dist/src/link/file.js +21 -14
  44. package/dist/src/multiLine/charinsert.d.ts +16 -0
  45. package/dist/src/multiLine/charinsert.js +84 -0
  46. package/dist/src/multiLine/gallery.js +4 -4
  47. package/dist/src/multiLine/imagemap.d.ts +1 -1
  48. package/dist/src/multiLine/imagemap.js +13 -12
  49. package/dist/src/multiLine/index.d.ts +3 -0
  50. package/dist/src/multiLine/index.js +79 -18
  51. package/dist/src/multiLine/inputbox.d.ts +1 -0
  52. package/dist/src/multiLine/paramTag.d.ts +3 -0
  53. package/dist/src/multiLine/paramTag.js +60 -13
  54. package/dist/src/nested.js +23 -25
  55. package/dist/src/nowiki/quote.js +6 -5
  56. package/dist/src/paramLine.d.ts +23 -3
  57. package/dist/src/paramLine.js +129 -31
  58. package/dist/src/parameter.d.ts +6 -0
  59. package/dist/src/parameter.js +15 -1
  60. package/dist/src/singleLine/charinsertLine.d.ts +21 -0
  61. package/dist/src/singleLine/charinsertLine.js +69 -0
  62. package/dist/src/{imagemapLink.d.ts → singleLine/imagemapLink.d.ts} +8 -7
  63. package/dist/src/singleLine/imagemapLink.js +95 -0
  64. package/dist/src/singleLine/index.d.ts +10 -0
  65. package/dist/src/singleLine/index.js +16 -0
  66. package/dist/src/table/td.js +5 -4
  67. package/dist/src/tag/html.js +26 -8
  68. package/dist/src/tag/index.js +30 -17
  69. package/dist/src/tagPair/ext.js +23 -2
  70. package/dist/src/tagPair/index.js +1 -10
  71. package/dist/src/transclude.js +15 -15
  72. package/dist/util/sharable.js +22 -0
  73. package/dist/util/sharable.mjs +22 -0
  74. package/dist/util/string.js +27 -2
  75. package/i18n/en.json +4 -0
  76. package/i18n/zh-hans.json +4 -0
  77. package/i18n/zh-hant.json +4 -0
  78. package/package.json +17 -25
  79. package/dist/src/imagemapLink.js +0 -34
  80. /package/dist/{util → vendor}/search.js +0 -0
package/dist/lib/lsp.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import Parser from '../index';
2
2
  import { Token } from '../src/index';
3
- import type { Range, Position, ColorInformation, ColorPresentation, FoldingRange, DocumentLink, Location, WorkspaceEdit, Diagnostic as DiagnosticBase, TextEdit, Hover, SignatureHelp, InlayHint, CodeAction, DocumentSymbol } from 'vscode-languageserver-types';
3
+ import type { Range, Position, ColorInformation, ColorPresentation, FoldingRange, DocumentLink, Location, DocumentHighlight, WorkspaceEdit, Diagnostic as DiagnosticBase, TextEdit, Hover, SignatureHelp, InlayHint, CodeAction, DocumentSymbol } from 'vscode-languageserver-types';
4
4
  import type { Config, LanguageService as LanguageServiceBase, CompletionItem, SignatureData } from '../base';
5
5
  import type { AttributeToken } from '../internal';
6
6
  import type { TokenTypeMap, SelectedTokenTypes } from '../map';
@@ -24,9 +24,8 @@ export declare class LanguageService implements LanguageServiceBase {
24
24
  * 提供颜色指示
25
25
  * @param rgba color parser / 颜色解析函数
26
26
  * @param text source Wikitext / 源代码
27
- * @param hsl whether HSL colors are treated / 是否允许HSL颜色
28
27
  */
29
- provideDocumentColors(rgba: (s: string) => [number, number, number, number] | [], text: string, hsl?: boolean): Promise<ColorInformation[]>;
28
+ provideDocumentColors(rgba: (s: string) => [number, number, number, number] | [], text: string): Promise<ColorInformation[]>;
30
29
  /**
31
30
  * Provide color pickers
32
31
  *
@@ -72,6 +71,15 @@ export declare class LanguageService implements LanguageServiceBase {
72
71
  * @param text source Wikitext / 源代码
73
72
  */
74
73
  provideLinks(text: string): Promise<DocumentLink[]>;
74
+ /**
75
+ * Provide document highlights
76
+ *
77
+ * 提供文档高亮
78
+ * @param text source Wikitext / 源代码
79
+ * @param position position / 位置
80
+ * @since v1.46.0
81
+ */
82
+ provideDocumentHighlights(text: string, position: Position): Promise<DocumentHighlight[] | undefined>;
75
83
  /**
76
84
  * Provide references
77
85
  *
package/dist/lib/lsp.js CHANGED
@@ -21,7 +21,8 @@ const util_1 = __importDefault(require("util"));
21
21
  const child_process_1 = require("child_process");
22
22
  const crypto_1 = require("crypto");
23
23
  const stylelint_util_1 = require("@bhsd/stylelint-util");
24
- const search_1 = __importDefault(require("../util/search"));
24
+ const color_1 = require("@bhsd/common/color");
25
+ const search_1 = __importDefault(require("../vendor/search"));
25
26
  const constants_2 = require("../util/constants");
26
27
  const document_1 = require("./document");
27
28
  exports.tasks = new WeakMap();
@@ -30,15 +31,17 @@ const refTags = new Set(['ref']), referencesTags = new Set(['ref', 'references']
30
31
  'template-name',
31
32
  'link-target',
32
33
  'parameter-key',
33
- ]), referenceTypes = new Set([
34
+ ]), highlightTypes = new Set([
34
35
  'ext',
35
36
  'html',
36
37
  'attr-key',
38
+ 'magic-word-name',
39
+ ...renameTypes,
40
+ ]), referenceTypes = new Set([
37
41
  'image-parameter',
38
42
  'heading-title',
39
43
  'heading',
40
- 'magic-word-name',
41
- ...renameTypes,
44
+ ...highlightTypes,
42
45
  ]), plainTypes = new Set(['text', 'comment', 'noinclude', 'include']), rawLilyPondCommands = new Set(['paper', 'book', 'bookpart', 'score', 'markuplist'].map(c => `\\${c}`)), cssSelector = ['ext', 'html', 'table'].map(s => `${s}-attr#style`).join();
43
46
  const getLinkRegex = (0, common_1.getRegex)(protocol => new RegExp(`^(?:${protocol}|//)`, 'iu'));
44
47
  /**
@@ -105,7 +108,7 @@ const createNodeRange = (token) => {
105
108
  * @param extra extra text
106
109
  * @param getDoc documentation method
107
110
  */
108
- const getCompletion = (words, kind, mt, { line, character }, extra, getDoc) => [...new Set(words)].map((w) => {
111
+ const getCompletion = (words, kind, mt, { line, character }, extra = '', getDoc) => [...new Set(words)].map((w) => {
109
112
  const doc = getDoc?.(w)?.description;
110
113
  return {
111
114
  label: w,
@@ -115,7 +118,7 @@ const getCompletion = (words, kind, mt, { line, character }, extra, getDoc) => [
115
118
  start: { line, character: character - mt.length },
116
119
  end: { line, character },
117
120
  },
118
- newText: w + (extra ?? ''),
121
+ newText: w + extra,
119
122
  },
120
123
  ...doc && {
121
124
  documentation: {
@@ -168,8 +171,10 @@ const getName = (token) => {
168
171
  return token.level;
169
172
  case 'heading-title':
170
173
  return parentNode.level;
171
- case 'parameter-key':
172
- return `${parentNode.parentNode.name}|${parentNode.name}`;
174
+ case 'parameter-key': {
175
+ const { type: t, name: n } = parentNode.parentNode;
176
+ return n + (t === 'template' ? '|' : '#') + parentNode.name;
177
+ }
173
178
  case 'ext':
174
179
  case 'html':
175
180
  case 'image-parameter':
@@ -178,6 +183,22 @@ const getName = (token) => {
178
183
  return parentNode.name;
179
184
  }
180
185
  };
186
+ /**
187
+ * Get the tokens that match the given token.
188
+ * @param root
189
+ * @param node the token to match
190
+ * @param types accepted token types
191
+ */
192
+ const getMatchingTokens = (root, node, types) => {
193
+ const { type } = node, refName = getRefName(node), refGroup = getRefGroup(node);
194
+ if (!refName && !refGroup && !types.has(type)) {
195
+ return undefined;
196
+ }
197
+ const name = getName(node), refs = root.querySelectorAll(type === 'heading-title' ? 'heading' : type).filter(token => type === 'attr-value'
198
+ ? getRefName(token) === refName || getRefGroup(token) === refGroup
199
+ : getName(token) === name);
200
+ return refs.length === 0 ? undefined : refs.reverse();
201
+ };
181
202
  /**
182
203
  * Get the quick fix data.
183
204
  * @param root root token
@@ -272,8 +293,7 @@ const partialParse = async (wikitext, watch, include, config = index_1.default.g
272
293
  };
273
294
  /* NOT FOR BROWSER ONLY */
274
295
  /** @see https://www.npmjs.com/package/stylelint-config-recommended */
275
- const cssRules = { 'block-no-empty': null }, sources = { 'invalid-css': 'css', 'invalid-math': 'texvc' }, jsonSelector = constants_2.jsonTags.map(s => `ext#${s}`).join(), scores = new Map();
276
- let colors;
296
+ const cssRules = { 'block-no-empty': null }, colors = Object.keys(color_1.colorsNamed), sources = { 'invalid-css': 'css', 'invalid-math': 'texvc' }, jsonSelector = constants_2.jsonTags.map(s => `ext#${s}`).join(), scores = new Map();
277
297
  /**
278
298
  * Correct the position of an error.
279
299
  * @param height
@@ -479,22 +499,9 @@ class LanguageService {
479
499
  * 提供颜色指示
480
500
  * @param rgba color parser / 颜色解析函数
481
501
  * @param text source Wikitext / 源代码
482
- * @param hsl whether HSL colors are treated / 是否允许HSL颜色
483
502
  */
484
- async provideDocumentColors(rgba, text, hsl = true) {
503
+ async provideDocumentColors(rgba, text) {
485
504
  const root = await this.#queue(text);
486
- /* NOT FOR BROWSER ONLY */
487
- if (colors === undefined) {
488
- try {
489
- const { default: colorName } = require('color-name');
490
- colors = new RegExp(String.raw `\b${Object.keys(colorName).join('|')}\b`, 'giu');
491
- }
492
- catch {
493
- /* c8 ignore next */
494
- colors = false;
495
- }
496
- }
497
- /* NOT FOR BROWSER ONLY END */
498
505
  return root.querySelectorAll('attr-value,parameter-value,arg-default').reverse().flatMap(token => {
499
506
  const { type, childNodes,
500
507
  /* NOT FOR BROWSER ONLY */
@@ -509,15 +516,10 @@ class LanguageService {
509
516
  /* NOT FOR BROWSER ONLY END */
510
517
  }
511
518
  /* NOT FOR BROWSER ONLY */
512
- const isStyle = colors && type === 'attr-value' && parentNode.name === 'style';
519
+ const isStyle = type === 'attr-value' && parentNode.name === 'style';
513
520
  /* NOT FOR BROWSER ONLY END */
514
521
  return childNodes.filter((child) => child.type === 'text').reverse().flatMap(child => {
515
- const { data } = child, parts = (0, common_1.splitColors)(data, hsl).filter(([, , , isColor]) => isColor);
516
- /* NOT FOR BROWSER ONLY */
517
- if (isStyle) {
518
- parts.push(...[...data.matchAll(colors)].map(({ index, 0: s }) => [s, index, index + s.length, true]));
519
- }
520
- /* NOT FOR BROWSER ONLY END */
522
+ const { data } = child, parts = (0, common_1.splitColors)(data, isStyle && colors).filter(([, , , isColor]) => isColor);
521
523
  if (parts.length === 0) {
522
524
  return [];
523
525
  }
@@ -688,13 +690,8 @@ class LanguageService {
688
690
  if (mt?.[7] !== undefined || type === 'image-parameter') { // image parameter
689
691
  const index = root.indexFromPos(line, character), match = mt?.[7]?.trimStart()
690
692
  ?? this.#text.slice(cur.getAbsoluteIndex(), index).trimStart(), equal = this.#text[index] === '=';
691
- return [
692
- ...getCompletion(params, 'Property', match, position)
693
- .filter(({ label }) => !equal || !/[= ]$/u.test(label)),
694
- ...getCompletion(root.querySelectorAll('image-parameter#width')
695
- .filter(token => token !== cur)
696
- .map(width => width.text()), 'Unit', match, position),
697
- ];
693
+ return getCompletion(params, 'Property', match, position)
694
+ .filter(({ label }) => !equal || !/[= ]$/u.test(label));
698
695
  }
699
696
  else if (mt?.[8] !== undefined || type === 'attr-key') { // attribute key
700
697
  const tag = mt?.[8]?.toLowerCase() ?? parentNode.tag, key = mt?.[10]
@@ -739,11 +736,11 @@ class LanguageService {
739
736
  }).map(({ name }) => name), 'Variable', key, position, type === 'parameter-value' ? '=' : '')
740
737
  : undefined;
741
738
  }
742
- else if (type === 'param-line') {
743
- // parameter line of `<dynamicpagelist>` or `<inputbox>`
739
+ else if (type === 'param-line-key') {
740
+ // parameter line of `<dynamicpagelist>`, `<inputbox>` or `<seo>`
744
741
  const key = this.#text.slice(cur.getAbsoluteIndex(), root.indexFromPos(line, character)).trimStart();
745
742
  return /^[a-z]+$/iu.test(key)
746
- ? getCompletion(sharable_1.extParams[cur.name], 'Property', key, position)
743
+ ? getCompletion(sharable_1.extParams[parentNode.name], 'Property', key, position)
747
744
  : undefined;
748
745
  /* NOT FOR BROWSER ONLY */
749
746
  }
@@ -935,7 +932,8 @@ class LanguageService {
935
932
  catch (e) {
936
933
  const { stderr } = e;
937
934
  if (stderr) {
938
- const re = new RegExp(String.raw `^${file}:(\d+):(\d+): error: (.+)$`, 'gmu'), lilypondErrors = [...stderr.matchAll(re)].map(([, line, col, msg]) => {
935
+ const re = new RegExp(String.raw `^${file}:(\d+):(\d+): error: (.+)$`, 'gmu'), lilypondErrors = [...stderr.matchAll(re)]
936
+ .map(([, line, col, msg]) => {
939
937
  const { offsetHeight, offsetWidth } = token.lastChild, pos = adjustPos(offsetHeight, offsetWidth, Number(line) - 1, Number(col) - 1);
940
938
  return {
941
939
  line: pos[0],
@@ -989,8 +987,8 @@ class LanguageService {
989
987
  */
990
988
  async provideFoldingRanges(text) {
991
989
  const root = await this.#queue(text), { length } = root.getLines(), ranges = [], levels = Array.from({ length: 6 }), tokens = root.querySelectorAll('heading-title,table,template,magic-word');
992
- for (const token of [...tokens].reverse()) { // 提高 getBoundingClientRect 的性能
993
- token.getRelativeIndex();
990
+ for (let i = tokens.length - 1; i >= 0; i--) { // 提高 getBoundingClientRect 的性能
991
+ tokens[i].getRelativeIndex();
994
992
  }
995
993
  for (const token of tokens) {
996
994
  const { offsetHeight } = token;
@@ -1146,6 +1144,37 @@ class LanguageService {
1146
1144
  })
1147
1145
  .filter(link => link !== false);
1148
1146
  }
1147
+ /**
1148
+ * Provide document highlights
1149
+ *
1150
+ * 提供文档高亮
1151
+ * @param text source Wikitext / 源代码
1152
+ * @param position position / 位置
1153
+ * @since v1.46.0
1154
+ */
1155
+ async provideDocumentHighlights(text, position) {
1156
+ const root = await this.#queue(text), index = root.indexFromPos(position.line, position.character), node = root.elementFromIndex(index), type = node?.type;
1157
+ if (!node || (type === 'ext' || type === 'html') && !/\w/u.test(text.charAt(index))) {
1158
+ return undefined;
1159
+ }
1160
+ return getMatchingTokens(root, node, highlightTypes)?.map((token) => {
1161
+ const range = createNodeRange(token);
1162
+ if (type === 'ext' || type === 'html') {
1163
+ const { start } = range;
1164
+ start.character += token.closing ? 2 : 1;
1165
+ return {
1166
+ range: {
1167
+ start,
1168
+ end: {
1169
+ line: start.line,
1170
+ character: start.character + token.name.length,
1171
+ },
1172
+ },
1173
+ };
1174
+ }
1175
+ return { range };
1176
+ });
1177
+ }
1149
1178
  /**
1150
1179
  * Provide references
1151
1180
  *
@@ -1156,16 +1185,19 @@ class LanguageService {
1156
1185
  async provideReferences(text, position) {
1157
1186
  const root = await this.#queue(text), { offsetNode, offset } = caretPositionFromWord(root, this.#text, position), element = offsetNode.type === 'text' ? offsetNode.parentNode : offsetNode, node = offset === 0 && (element.is('ext-attr-dirty') || element.is('html-attr-dirty'))
1158
1187
  ? element.parentNode.parentNode
1159
- : element, { type } = node, refName = getRefName(node), refGroup = getRefGroup(node);
1160
- if (!refName && !refGroup && !referenceTypes.has(type)) {
1161
- return undefined;
1162
- }
1163
- const name = getName(node), refs = root.querySelectorAll(type === 'heading-title' ? 'heading' : type).filter(token => type === 'attr-value'
1164
- ? getRefName(token) === refName || getRefGroup(token) === refGroup
1165
- : getName(token) === name).reverse().map((token) => ({
1188
+ : element;
1189
+ return getMatchingTokens(root, node, referenceTypes)?.map((token) => ({
1166
1190
  range: createNodeRange(token.type === 'parameter-key' ? token.parentNode : token),
1167
1191
  }));
1168
- return refs.length === 0 ? undefined : refs;
1192
+ }
1193
+ /**
1194
+ * 获取根节点和当前节点
1195
+ * @param text 源代码
1196
+ * @param position 位置
1197
+ */
1198
+ async #getRootAndNode(text, position) {
1199
+ const root = await this.#queue(text), node = root.elementFromPoint(position.character, position.line);
1200
+ return [root, node];
1169
1201
  }
1170
1202
  /**
1171
1203
  * Provide definitions
@@ -1175,7 +1207,7 @@ class LanguageService {
1175
1207
  * @param position position / 位置
1176
1208
  */
1177
1209
  async provideDefinition(text, position) {
1178
- const root = await this.#queue(text), node = root.elementFromPoint(position.character, position.line);
1210
+ const [root, node] = await this.#getRootAndNode(text, position);
1179
1211
  if (!node) {
1180
1212
  return undefined;
1181
1213
  }
@@ -1200,7 +1232,7 @@ class LanguageService {
1200
1232
  * @param position position / 位置
1201
1233
  */
1202
1234
  async resolveRenameLocation(text, position) {
1203
- const root = await this.#queue(text), node = root.elementFromPoint(position.character, position.line);
1235
+ const [, node] = await this.#getRootAndNode(text, position);
1204
1236
  if (!node) {
1205
1237
  return undefined;
1206
1238
  }
@@ -1220,12 +1252,12 @@ class LanguageService {
1220
1252
  * @param newName new name / 新名称
1221
1253
  */
1222
1254
  async provideRenameEdits(text, position, newName) {
1223
- const root = await this.#queue(text), node = root.elementFromPoint(position.character, position.line);
1255
+ const [root, node] = await this.#getRootAndNode(text, position);
1224
1256
  if (!node) {
1225
1257
  return undefined;
1226
1258
  }
1227
1259
  const { type } = node, refName = getRefName(node), refNameGroup = refName && node.parentNode.parentNode.getAttr('group'), refGroup = getRefGroup(node), name = getName(node), refs = root.querySelectorAll(type).filter(token => {
1228
- const { type: t } = token.parentNode;
1260
+ const t = token.parentNode.type;
1229
1261
  if (type === 'link-target' && t !== 'link' && t !== 'redirect-target') {
1230
1262
  return false;
1231
1263
  }
@@ -1415,10 +1447,10 @@ class LanguageService {
1415
1447
  * @since v1.16.3
1416
1448
  */
1417
1449
  async provideInlayHints(text) {
1418
- const root = await this.#queue(text);
1450
+ const root = await this.#queue(text), templates = root.querySelectorAll('template,magic-word#invoke');
1419
1451
  let hints = [];
1420
- for (const token of root.querySelectorAll('template,magic-word#invoke').reverse()) {
1421
- const { type, childNodes } = token;
1452
+ for (let i = templates.length - 1; i >= 0; i--) {
1453
+ const { type, childNodes } = templates[i];
1422
1454
  hints = [
1423
1455
  ...hints,
1424
1456
  ...childNodes.slice(type === 'template' ? 1 : 3).filter(({ anon }) => anon)
package/dist/lib/node.js CHANGED
@@ -40,7 +40,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
40
40
  exports.AstNode = void 0;
41
41
  /* eslint-disable @typescript-eslint/no-base-to-string */
42
42
  const common_1 = require("@bhsd/common");
43
- const search_1 = __importDefault(require("../util/search"));
43
+ const search_1 = __importDefault(require("../vendor/search"));
44
44
  const lint_1 = require("../util/lint");
45
45
  const debug_1 = require("../util/debug");
46
46
  const cached_1 = require("../mixin/cached");
@@ -145,7 +145,7 @@ let AstNode = (() => {
145
145
  * @param left column number / 列号
146
146
  */
147
147
  indexFromPos(top, left) {
148
- LINT: {
148
+ LSP: {
149
149
  if (top < 0 || left < 0) {
150
150
  return undefined;
151
151
  }
package/dist/lib/text.js CHANGED
@@ -16,7 +16,7 @@ const errorSyntaxUrl = /* #__PURE__ */ new RegExp(source, 'giu'), noLinkTypes =
16
16
  '{': /[{}]/u,
17
17
  ']': /[[\]](?=[^[\]]*$)/u,
18
18
  '}': /[{}](?=[^{}]*$)/u,
19
- }, disallowedTags = new Set([
19
+ }, magic = new Set(['rfc', 'pmid', 'isbn']), disallowedTags = new Set([
20
20
  'html',
21
21
  'head',
22
22
  'style',
@@ -136,7 +136,7 @@ class AstText extends node_1.AstNode {
136
136
  error = error.slice(1);
137
137
  }
138
138
  error = error.toLowerCase();
139
- const [char] = error, magicLink = error === 'rfc' || error === 'pmid' || error === 'isbn', lbrace = char === '{', rbrace = char === '}', lbrack = char === '[', rbrack = char === ']';
139
+ const [char] = error, magicLink = magic.has(error), lbrace = char === '{', rbrace = char === '}', lbrack = char === '[', rbrack = char === ']';
140
140
  let { length } = error;
141
141
  if (rbrack && (index || length > 1)) {
142
142
  errorRegex.lastIndex--;
package/dist/map.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { RedirectToken, RedirectTargetToken, OnlyincludeToken, IncludeToken, CommentToken, ExtToken, AttributesToken, AttributeToken, AtomToken, ArgToken, TranscludeToken, SyntaxToken, ParameterToken, HeadingToken, HtmlToken, TableToken, TrToken, TdToken, DoubleUnderscoreToken, HrToken, LinkToken, CategoryToken, FileToken, ImageParameterToken, QuoteToken, ExtLinkToken, MagicLinkToken, ListToken, DdToken, ConverterToken, ConverterFlagsToken, ConverterRuleToken, TranslateToken, TvarToken, HiddenToken, GalleryImageToken, ParamLineToken, ImagemapLinkToken } from './internal';
1
+ import type { RedirectToken, RedirectTargetToken, OnlyincludeToken, IncludeToken, CommentToken, ExtToken, AttributesToken, AttributeToken, AtomToken, ArgToken, TranscludeToken, SyntaxToken, ParameterToken, HeadingToken, HtmlToken, TableToken, TrToken, TdToken, DoubleUnderscoreToken, HrToken, LinkToken, CategoryToken, FileToken, ImageParameterToken, QuoteToken, ExtLinkToken, MagicLinkToken, ListToken, DdToken, ConverterToken, ConverterFlagsToken, ConverterRuleToken, TranslateToken, TvarToken, HiddenToken, GalleryImageToken, ParamLineToken, ImagemapLinkToken, CharinsertLineToken } from './internal';
2
2
  export interface TokenTypeMap {
3
3
  redirect: RedirectToken;
4
4
  'redirect-syntax': SyntaxToken;
@@ -60,6 +60,9 @@ export interface TokenTypeMap {
60
60
  'converter-rule': ConverterRuleToken;
61
61
  'converter-rule-variant': AtomToken;
62
62
  'param-line': ParamLineToken;
63
+ 'param-line-key': AtomToken;
64
+ 'param-line-value': AtomToken;
63
65
  'imagemap-link': ImagemapLinkToken;
66
+ 'charinsert-line': CharinsertLineToken;
64
67
  }
65
68
  export type SelectedTokenTypes = keyof TokenTypeMap;
@@ -5,23 +5,23 @@ const debug_1 = require("../util/debug");
5
5
  const selector_1 = require("../util/selector");
6
6
  /** @ignore */
7
7
  const elementLike = (constructor) => {
8
- class ElementLike extends constructor {
9
- #getCondition(selector) {
10
- return (0, selector_1.getCondition)(selector, this);
11
- }
12
- closest(selector) {
13
- const condition = this.#getCondition(selector);
14
- let { parentNode } = this;
15
- while (parentNode) {
16
- if (condition(parentNode)) {
17
- return parentNode;
8
+ LINT: {
9
+ class ElementLike extends constructor {
10
+ #getCondition(selector) {
11
+ return (0, selector_1.getCondition)(selector, this);
12
+ }
13
+ closest(selector) {
14
+ const condition = this.#getCondition(selector);
15
+ let { parentNode } = this;
16
+ while (parentNode) {
17
+ if (condition(parentNode)) {
18
+ return parentNode;
19
+ }
20
+ ({ parentNode } = parentNode);
18
21
  }
19
- ({ parentNode } = parentNode);
22
+ return undefined;
20
23
  }
21
- return undefined;
22
- }
23
- getElementBy(condition) {
24
- LINT: {
24
+ getElementBy(condition) {
25
25
  const stack = [...this.childNodes].reverse();
26
26
  while (stack.length > 0) {
27
27
  const child = stack.pop(), { type, childNodes } = child;
@@ -37,12 +37,10 @@ const elementLike = (constructor) => {
37
37
  }
38
38
  return undefined;
39
39
  }
40
- }
41
- querySelector(selector) {
42
- LINT: return this.getElementBy(this.#getCondition(selector));
43
- }
44
- getElementsBy(condition) {
45
- LINT: {
40
+ querySelector(selector) {
41
+ return this.getElementBy(this.#getCondition(selector));
42
+ }
43
+ getElementsBy(condition) {
46
44
  const stack = [...this.childNodes].reverse(), descendants = [];
47
45
  while (stack.length > 0) {
48
46
  const child = stack.pop(), { type, childNodes } = child;
@@ -58,19 +56,19 @@ const elementLike = (constructor) => {
58
56
  }
59
57
  return descendants;
60
58
  }
61
- }
62
- querySelectorAll(selector) {
63
- LINT: return this.getElementsBy(this.#getCondition(selector));
64
- }
65
- escape() {
66
- LSP: {
67
- for (const child of this.childNodes) {
68
- child.escape();
59
+ querySelectorAll(selector) {
60
+ return this.getElementsBy(this.#getCondition(selector));
61
+ }
62
+ escape() {
63
+ LSP: {
64
+ for (const child of this.childNodes) {
65
+ child.escape();
66
+ }
69
67
  }
70
68
  }
71
69
  }
70
+ (0, debug_1.mixin)(ElementLike, constructor);
71
+ return ElementLike;
72
72
  }
73
- (0, debug_1.mixin)(ElementLike, constructor);
74
- return ElementLike;
75
73
  };
76
74
  exports.elementLike = elementLike;
@@ -13,7 +13,6 @@ const hiddenToken = (linter = true, html = true) => (constructor) => {
13
13
  return '';
14
14
  }
15
15
  lint(start) {
16
- // @ts-expect-error private argument
17
16
  LINT: return linter ? [] : super.lint(start);
18
17
  }
19
18
  }
@@ -0,0 +1,29 @@
1
+ import { ParamLineToken } from '../src/paramLine';
2
+ export interface ParamLineParentBase {
3
+ /**
4
+ * Get all parameter keys
5
+ *
6
+ * 获取所有参数键
7
+ * @since v1.45.0
8
+ */
9
+ getKeys(): Set<string>;
10
+ /**
11
+ * Get parameters with the specified key
12
+ *
13
+ * 获取指定参数
14
+ * @param key parameter key / 参数键
15
+ * @since v1.45.0
16
+ */
17
+ getParams(key: string): ParamLineToken[];
18
+ /**
19
+ * Get duplicated parameters
20
+ *
21
+ * 获取重复参数
22
+ * @since v1.45.0
23
+ */
24
+ getDuplicatedParams(): [string, ParamLineToken[]][];
25
+ }
26
+ /**
27
+ * 子节点为ParamLineToken的类
28
+ * @ignore
29
+ */
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.paramLineParent = void 0;
7
+ const debug_1 = require("../util/debug");
8
+ const lint_1 = require("../util/lint");
9
+ const rect_1 = require("../lib/rect");
10
+ const index_1 = __importDefault(require("../index"));
11
+ /**
12
+ * 子节点为ParamLineToken的类
13
+ * @ignore
14
+ */
15
+ const paramLineParent = (constructor) => {
16
+ LINT: {
17
+ class ParamLineParent extends constructor {
18
+ getKeys() {
19
+ return new Set(this.childNodes.map(({ key }) => key).filter((key) => key !== undefined));
20
+ }
21
+ getParams(key) {
22
+ return this.childNodes.filter(({ key: k }) => k === key);
23
+ }
24
+ getDuplicatedParams() {
25
+ return [...this.getKeys()].map((key) => [key, this.getParams(key)])
26
+ .filter(([, { length }]) => length > 1);
27
+ }
28
+ lint(start = this.getAbsoluteIndex()) {
29
+ const errors = super.lint(start), { name, firstChild } = this, rule = 'no-duplicate', { lintConfig } = index_1.default, s = lintConfig.getSeverity(rule, name);
30
+ if (s) {
31
+ const msg = index_1.default.msg('duplicate-ext-parameter', name), rect = new rect_1.BoundingRect(this, start), { computeEditInfo } = lintConfig;
32
+ for (const [, params] of this.getDuplicatedParams()) {
33
+ const { length } = params, duplication = Array.from({ length });
34
+ for (let i = length - 1; i >= 0; i--) {
35
+ const param = params[i], e = (0, lint_1.generateForChild)(param, rect, rule, msg, s);
36
+ if (computeEditInfo) {
37
+ e.suggestions = [(0, lint_1.fixByRemove)(e, param === firstChild ? 0 : -1)];
38
+ }
39
+ duplication[i] = e;
40
+ }
41
+ Array.prototype.push.apply(errors, duplication);
42
+ }
43
+ }
44
+ return errors;
45
+ }
46
+ }
47
+ (0, debug_1.mixin)(ParamLineParent, constructor);
48
+ return ParamLineParent;
49
+ }
50
+ };
51
+ exports.paramLineParent = paramLineParent;
package/dist/src/arg.js CHANGED
@@ -38,7 +38,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
38
38
  };
39
39
  Object.defineProperty(exports, "__esModule", { value: true });
40
40
  exports.ArgToken = void 0;
41
- const string_1 = require("../util/string");
42
41
  const lint_1 = require("../util/lint");
43
42
  const rect_1 = require("../lib/rect");
44
43
  const padded_1 = require("../mixin/padded");
@@ -99,11 +98,17 @@ let ArgToken = (() => {
99
98
  }
100
99
  /** @private */
101
100
  text() {
102
- return `{{{${(0, string_1.text)(this.childNodes.slice(0, 2), '|')}}}}`;
101
+ // eslint-disable-next-line @typescript-eslint/no-useless-default-assignment
102
+ const { length, childNodes, name = this.#getName() } = this;
103
+ return length === 1 ? `{{{${name}}}}` : `{{{${name}|${childNodes[1].text()}}}}`;
104
+ }
105
+ /** 获取name */
106
+ #getName() {
107
+ return this.firstChild.text().trim();
103
108
  }
104
109
  /** 更新name */
105
110
  #setName() {
106
- LSP: this.setAttribute('name', this.firstChild.text().trim());
111
+ LSP: this.setAttribute('name', this.#getName());
107
112
  }
108
113
  /** @private */
109
114
  afterBuild() {
@@ -113,44 +118,21 @@ let ArgToken = (() => {
113
118
  /** @private */
114
119
  lint(start = this.getAbsoluteIndex(), re) {
115
120
  LINT: {
116
- const [argName, argDefault, ...rest] = this.childNodes;
117
- argName.setAttribute('aIndex', start + 3);
118
- const errors = argName.lint(start + 3, re);
119
- if (argDefault) {
120
- const index = start + 4 + argName.toString().length;
121
- argDefault.setAttribute('aIndex', index);
122
- const childErrors = argDefault.lint(index, re);
123
- if (childErrors.length > 0) {
124
- Array.prototype.push.apply(errors, childErrors);
125
- }
126
- }
127
- const rules = ['no-ignored', 'no-arg'], { lintConfig } = index_1.default, { computeEditInfo } = lintConfig, rect = new rect_1.BoundingRect(this, start), s = rules.map(rule => lintConfig.getSeverity(rule, 'arg'));
128
- if (s[0] && rest.length > 0) {
129
- Array.prototype.push.apply(errors, rest.map(child => {
130
- const e = (0, lint_1.generateForChild)(child, rect, rules[0], 'invisible-triple-braces', s[0]);
131
- e.startIndex--;
132
- e.startCol--;
133
- if (computeEditInfo) {
134
- e.suggestions = [
135
- (0, lint_1.fixByRemove)(e),
136
- (0, lint_1.fixByEscape)(e.startIndex, '{{!}}'),
137
- ];
138
- }
139
- return e;
140
- }));
141
- }
142
- if (s[1] && !this.getAttribute('include')) {
143
- const e = (0, lint_1.generateForSelf)(this, rect, rules[1], 'unexpected-argument', s[1]);
144
- if (computeEditInfo && argDefault) {
121
+ const errors = super.lint(start, re), { lintConfig } = index_1.default, rect = new rect_1.BoundingRect(this, start);
122
+ let rule = 'no-arg', s = lintConfig.getSeverity(rule);
123
+ if (s && !this.getAttribute('include')) {
124
+ const e = (0, lint_1.generateForSelf)(this, rect, rule, 'unexpected-argument', s), [, argDefault] = this.childNodes;
125
+ if (lintConfig.computeEditInfo && argDefault) {
145
126
  e.suggestions = [(0, lint_1.fixBy)(e, 'expand', argDefault.text())];
146
127
  }
147
128
  errors.push(e);
148
129
  }
149
130
  const ext = this.closest('ext');
150
131
  if (ext) {
151
- const rule = 'arg-in-ext', severity = lintConfig.getSeverity(rule, ext.name);
152
- if (severity) {
153
- errors.push((0, lint_1.generateForSelf)(this, rect, rule, 'argument-in-ext', severity));
132
+ rule = 'arg-in-ext';
133
+ s = lintConfig.getSeverity(rule, ext.name);
134
+ if (s) {
135
+ errors.push((0, lint_1.generateForSelf)(this, rect, rule, 'argument-in-ext', s));
154
136
  }
155
137
  }
156
138
  return errors;
@@ -1,6 +1,6 @@
1
1
  import { Token } from './index';
2
2
  import type { Config } from '../base';
3
- declare const atomTypes: readonly ["arg-name", "attr-key", "attr-value", "ext-attr-dirty", "html-attr-dirty", "table-attr-dirty", "converter-flag", "converter-rule-variant", "invoke-function", "invoke-module", "template-name", "link-target"];
3
+ declare const atomTypes: readonly ["arg-name", "attr-key", "attr-value", "ext-attr-dirty", "html-attr-dirty", "table-attr-dirty", "converter-flag", "converter-rule-variant", "invoke-function", "invoke-module", "template-name", "link-target", "param-line-key", "param-line-value"];
4
4
  declare type AtomTypes = typeof atomTypes[number];
5
5
  /**
6
6
  * plain Token that will not be parsed further