wikilint 2.13.8 → 2.14.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.
@@ -99,8 +99,11 @@ class AttributesToken extends index_2.Token {
99
99
  lint(start = this.getAbsoluteIndex(), re) {
100
100
  const errors = super.lint(start, re), { parentNode, childNodes } = this, attrs = new Map(), duplicated = new Set(), rect = new rect_1.BoundingRect(this, start);
101
101
  if (parentNode?.type === 'html' && parentNode.closing && this.text().trim()) {
102
- const e = (0, lint_1.generateForSelf)(this, rect, 'no-ignored', 'attributes of a closing tag');
103
- e.fix = { range: [start, e.endIndex], text: '' };
102
+ const e = (0, lint_1.generateForSelf)(this, rect, 'no-ignored', 'attributes of a closing tag'), index = parentNode.getAbsoluteIndex();
103
+ e.suggestions = [
104
+ { desc: 'remove', range: [start, e.endIndex], text: '' },
105
+ { desc: 'open', range: [index + 1, index + 2], text: '' },
106
+ ];
104
107
  errors.push(e);
105
108
  }
106
109
  for (const attr of childNodes) {
@@ -118,20 +121,27 @@ class AttributesToken extends index_2.Token {
118
121
  const str = attr.text().trim();
119
122
  if (str) {
120
123
  const e = (0, lint_1.generateForChild)(attr, rect, 'no-ignored', 'containing invalid attribute', /[\p{L}\d]/u.test(str) ? 'error' : 'warning');
121
- e.suggestions = [
122
- {
123
- desc: 'remove',
124
- range: [e.startIndex, e.endIndex],
125
- text: ' ',
126
- },
127
- ];
124
+ e.suggestions = [{ desc: 'remove', range: [e.startIndex, e.endIndex], text: ' ' }];
128
125
  errors.push(e);
129
126
  }
130
127
  }
131
128
  }
132
129
  if (duplicated.size > 0) {
133
130
  for (const key of duplicated) {
134
- errors.push(...attrs.get(key).map(attr => (0, lint_1.generateForChild)(attr, rect, 'no-duplicate', index_1.default.msg('duplicated $1 attribute', key))));
131
+ const pairs = attrs.get(key).map(attr => {
132
+ const value = attr.getValue();
133
+ return [attr, value === true ? '' : value];
134
+ });
135
+ errors.push(...pairs.map(([attr, value], i) => {
136
+ const e = (0, lint_1.generateForChild)(attr, rect, 'no-duplicate', index_1.default.msg('duplicated $1 attribute', key)), remove = { desc: 'remove', range: [e.startIndex, e.endIndex], text: '' };
137
+ if (!value || pairs.slice(0, i).some(([, v]) => v === value)) {
138
+ e.fix = remove;
139
+ }
140
+ else {
141
+ e.suggestions = [remove];
142
+ }
143
+ return e;
144
+ }));
135
145
  }
136
146
  }
137
147
  return errors;
@@ -62,16 +62,10 @@ class ConverterFlagsToken extends index_2.Token {
62
62
  && (variantFlags.size > 0 || !validFlags.has(flag))) {
63
63
  const e = (0, lint_1.generateForChild)(child, rect, 'no-ignored', 'invalid conversion flag');
64
64
  if (variantFlags.size === 0 && definedFlags.has(flag.toUpperCase())) {
65
- e.fix = { range: [e.startIndex, e.endIndex], text: flag.toUpperCase() };
65
+ e.fix = { range: [e.startIndex, e.endIndex], text: flag.toUpperCase(), desc: 'uppercase' };
66
66
  }
67
67
  else {
68
- e.suggestions = [
69
- {
70
- desc: 'remove',
71
- range: [e.startIndex - (i && 1), e.endIndex],
72
- text: '',
73
- },
74
- ];
68
+ e.suggestions = [{ desc: 'remove', range: [e.startIndex - (i && 1), e.endIndex], text: '' }];
75
69
  }
76
70
  errors.push(e);
77
71
  }
@@ -71,16 +71,8 @@ class GalleryToken extends index_2.Token {
71
71
  startCol,
72
72
  endCol: startCol + length,
73
73
  suggestions: [
74
- {
75
- desc: 'remove',
76
- range: [start, endIndex],
77
- text: '',
78
- },
79
- {
80
- desc: 'comment',
81
- range: [start, endIndex],
82
- text: `<!--${str}-->`,
83
- },
74
+ { desc: 'remove', range: [start, endIndex], text: '' },
75
+ { desc: 'comment', range: [start, endIndex], text: `<!--${str}-->` },
84
76
  ],
85
77
  });
86
78
  }
@@ -57,21 +57,55 @@ class HeadingToken extends index_2.Token {
57
57
  }
58
58
  /** @private */
59
59
  lint(start = this.getAbsoluteIndex(), re) {
60
- const errors = super.lint(start, re), { firstChild, level } = this, innerStr = firstChild.toString(), quotes = firstChild.childNodes.filter((0, debug_1.isToken)('quote')), boldQuotes = quotes.filter(({ bold }) => bold), italicQuotes = quotes.filter(({ italic }) => italic), rect = new rect_1.BoundingRect(this, start);
60
+ const errors = super.lint(start, re), { firstChild, level } = this, innerStr = firstChild.toString(), unbalancedStart = innerStr.startsWith('='), unbalanced = unbalancedStart || innerStr.endsWith('='), quotes = firstChild.childNodes.filter((0, debug_1.isToken)('quote')), boldQuotes = quotes.filter(({ bold }) => bold), italicQuotes = quotes.filter(({ italic }) => italic), rect = new rect_1.BoundingRect(this, start);
61
61
  if (this.level === 1) {
62
- errors.push((0, lint_1.generateForChild)(firstChild, rect, 'h1', '<h1>'));
62
+ const e = (0, lint_1.generateForChild)(firstChild, rect, 'h1', '<h1>');
63
+ if (!unbalanced) {
64
+ e.suggestions = [{ desc: 'h2', range: [e.startIndex, e.endIndex], text: `=${innerStr}=` }];
65
+ }
66
+ errors.push(e);
63
67
  }
64
- if (innerStr.startsWith('=') || innerStr.endsWith('=')) {
65
- errors.push((0, lint_1.generateForChild)(firstChild, rect, 'unbalanced-header', index_1.default.msg('unbalanced $1 in a section header', '"="')));
68
+ if (unbalanced) {
69
+ const e = (0, lint_1.generateForChild)(firstChild, rect, 'unbalanced-header', index_1.default.msg('unbalanced $1 in a section header', '"="'));
70
+ if (innerStr === '=') {
71
+ //
72
+ }
73
+ else if (unbalancedStart) {
74
+ const [extra] = /^=+/u.exec(innerStr);
75
+ e.suggestions = [
76
+ { desc: `h${level}`, range: [e.startIndex, e.startIndex + extra.length], text: '' },
77
+ { desc: `h${level + extra.length}`, range: [e.endIndex, e.endIndex], text: extra },
78
+ ];
79
+ }
80
+ else {
81
+ const extra = /[^=](=+)$/u.exec(innerStr)[1];
82
+ e.suggestions = [
83
+ { desc: `h${level}`, range: [e.endIndex - extra.length, e.endIndex], text: '' },
84
+ { desc: `h${level + extra.length}`, range: [e.startIndex, e.startIndex], text: extra },
85
+ ];
86
+ }
87
+ errors.push(e);
66
88
  }
67
89
  if (this.closest('html-attrs,table-attrs')) {
68
90
  errors.push((0, lint_1.generateForSelf)(this, rect, 'parsing-order', 'section header in a HTML tag'));
69
91
  }
92
+ const rootStr = this.getRootNode().toString();
70
93
  if (boldQuotes.length % 2) {
71
- errors.push((0, lint_1.generateForChild)(boldQuotes[boldQuotes.length - 1], { ...rect, start: start + level, left: rect.left + level }, 'format-leakage', index_1.default.msg('unbalanced $1 in a section header', 'bold apostrophes')));
94
+ const e = (0, lint_1.generateForChild)(boldQuotes[boldQuotes.length - 1], { ...rect, start: start + level, left: rect.left + level }, 'format-leakage', index_1.default.msg('unbalanced $1 in a section header', 'bold apostrophes')), end = start + level + innerStr.length;
95
+ if (rootStr.slice(e.endIndex, end).trim()) {
96
+ e.suggestions = [{ desc: 'close', range: [end, end], text: "'''" }];
97
+ }
98
+ else {
99
+ e.fix = { desc: 'remove', range: [e.startIndex, e.endIndex], text: '' };
100
+ }
101
+ errors.push(e);
72
102
  }
73
103
  if (italicQuotes.length % 2) {
74
- errors.push((0, lint_1.generateForChild)(italicQuotes[italicQuotes.length - 1], { start: start + level }, 'format-leakage', index_1.default.msg('unbalanced $1 in a section header', 'italic apostrophes')));
104
+ const e = (0, lint_1.generateForChild)(italicQuotes[italicQuotes.length - 1], { start: start + level }, 'format-leakage', index_1.default.msg('unbalanced $1 in a section header', 'italic apostrophes')), end = start + level + innerStr.length;
105
+ e.fix = rootStr.slice(e.endIndex, end).trim()
106
+ ? { desc: 'close', range: [end, end], text: "''" }
107
+ : { desc: 'remove', range: [e.startIndex, e.endIndex], text: '' };
108
+ errors.push(e);
75
109
  }
76
110
  return errors;
77
111
  }
package/dist/src/html.js CHANGED
@@ -84,10 +84,15 @@ class HtmlToken extends index_1.Token {
84
84
  lint(start = this.getAbsoluteIndex(), re) {
85
85
  const errors = super.lint(start, re), rect = new rect_1.BoundingRect(this, start);
86
86
  if (this.name === 'h1' && !this.closing) {
87
- errors.push((0, lint_1.generateForSelf)(this, rect, 'h1', '<h1>'));
87
+ errors.push({
88
+ ...(0, lint_1.generateForSelf)(this, rect, 'h1', '<h1>'),
89
+ suggestions: [{ desc: 'h2', range: [start + 2, start + 3], text: '2' }],
90
+ });
88
91
  }
89
92
  if (this.closest('table-attrs')) {
90
- errors.push((0, lint_1.generateForSelf)(this, rect, 'parsing-order', 'HTML tag in table attributes'));
93
+ const e = (0, lint_1.generateForSelf)(this, rect, 'parsing-order', 'HTML tag in table attributes');
94
+ e.fix = { desc: 'remove', range: [start, e.endIndex], text: '' };
95
+ errors.push(e);
91
96
  }
92
97
  try {
93
98
  this.findMatchingTag();
@@ -95,38 +100,53 @@ class HtmlToken extends index_1.Token {
95
100
  catch (e) {
96
101
  if (e instanceof SyntaxError) {
97
102
  const { message } = e;
98
- const msg = message.split(':')[0].toLowerCase(), error = (0, lint_1.generateForSelf)(this, rect, 'unmatched-tag', msg);
99
- if (msg === 'unclosed tag' && !this.closest('heading-title')) {
100
- if (formattingTags.has(this.name)) {
101
- const childNodes = this.parentNode?.childNodes, i = childNodes?.indexOf(this);
102
- if (!childNodes?.slice(0, i).some(({ type, name }) => type === 'html' && name === this.name)) {
103
+ const msg = message.split(':')[0].toLowerCase(), error = (0, lint_1.generateForSelf)(this, rect, 'unmatched-tag', msg), noSelfClosing = {
104
+ desc: 'no self-closing',
105
+ range: [error.endIndex - 2, error.endIndex - 1],
106
+ text: '',
107
+ };
108
+ switch (msg) {
109
+ case 'unclosed tag': {
110
+ const childNodes = this.parentNode?.childNodes;
111
+ if (formattingTags.has(this.name)
112
+ && childNodes?.slice(0, childNodes.indexOf(this))
113
+ .some(({ type, name }) => type === 'html' && name === this.name)) {
114
+ error.suggestions = [{ desc: 'close', range: [start + 1, start + 1], text: '/' }];
115
+ }
116
+ else if (!this.closest('heading-title')) {
103
117
  error.severity = 'warning';
104
118
  }
119
+ break;
105
120
  }
106
- else {
107
- error.severity = 'warning';
121
+ case 'unmatched closing tag': {
122
+ const ancestor = this.closest('magic-word');
123
+ if (ancestor && magicWords.has(ancestor.name)) {
124
+ error.severity = 'warning';
125
+ }
126
+ else {
127
+ error.suggestions = [{ desc: 'remove', range: [start, error.endIndex], text: '' }];
128
+ }
129
+ break;
108
130
  }
109
- }
110
- else if (msg === 'unmatched closing tag') {
111
- const ancestor = this.closest('magic-word');
112
- if (ancestor && magicWords.has(ancestor.name)) {
113
- error.severity = 'warning';
131
+ case 'tag that is both closing and self-closing': {
132
+ const { html: [normalTags, , voidTags] } = this.getAttribute('config'), open = { desc: 'open', range: [start + 1, start + 2], text: '' };
133
+ if (voidTags.includes(this.name)) {
134
+ error.fix = open;
135
+ }
136
+ else if (normalTags.includes(this.name)) {
137
+ error.fix = noSelfClosing;
138
+ }
139
+ else {
140
+ error.suggestions = [open, noSelfClosing];
141
+ }
142
+ break;
114
143
  }
115
- else {
144
+ case 'invalid self-closing tag':
116
145
  error.suggestions = [
117
- {
118
- desc: 'remove',
119
- range: [start, error.endIndex],
120
- text: '',
121
- },
146
+ noSelfClosing,
147
+ { desc: 'close', range: [error.endIndex - 2, error.endIndex], text: `></${this.name}>` },
122
148
  ];
123
- }
124
- }
125
- else if (msg === 'tag that is both closing and self-closing') {
126
- const { html: [, , voidTags] } = this.getAttribute('config');
127
- if (voidTags.includes(this.name)) {
128
- error.fix = { range: [start + 1, start + 2], text: '' };
129
- }
149
+ // no default
130
150
  }
131
151
  errors.push(error);
132
152
  }
@@ -107,11 +107,13 @@ class ImageParameterToken extends index_2.Token {
107
107
  const errors = super.lint(start, re), { link, name } = this;
108
108
  if (name === 'invalid') {
109
109
  const e = (0, lint_1.generateForSelf)(this, { start }, 'invalid-gallery', 'invalid image parameter');
110
- e.fix = { range: [start - 1, e.endIndex], text: '' };
110
+ e.fix = { range: [start - 1, e.endIndex], text: '', desc: 'remove' };
111
111
  errors.push(e);
112
112
  }
113
113
  else if (typeof link === 'object' && link.encoded) {
114
- errors.push((0, lint_1.generateForSelf)(this, { start }, 'url-encoding', 'unnecessary URL encoding in an internal link'));
114
+ const e = (0, lint_1.generateForSelf)(this, { start }, 'url-encoding', 'unnecessary URL encoding in an internal link');
115
+ e.suggestions = [{ desc: 'decode', range: [start, e.endIndex], text: (0, string_1.rawurldecode)(this.text()) }];
116
+ errors.push(e);
115
117
  }
116
118
  return errors;
117
119
  }
@@ -95,7 +95,14 @@ class ImagemapToken extends index_2.Token {
95
95
  errors.push(...this.childNodes.filter(child => {
96
96
  const str = child.toString().trim();
97
97
  return child.type === 'noinclude' && str && !str.startsWith('#');
98
- }).map(child => (0, lint_1.generateForChild)(child, rect, 'invalid-imagemap', 'invalid link in <imagemap>')));
98
+ }).map(child => {
99
+ const e = (0, lint_1.generateForChild)(child, rect, 'invalid-imagemap', 'invalid link in <imagemap>');
100
+ e.suggestions = [
101
+ { desc: 'remove', range: [e.startIndex - 1, e.endIndex], text: '' },
102
+ { desc: 'comment', range: [e.startIndex, e.startIndex], text: '# ' },
103
+ ];
104
+ return e;
105
+ }));
99
106
  }
100
107
  else {
101
108
  errors.push((0, lint_1.generateForSelf)(this, rect, 'invalid-imagemap', '<imagemap> without an image'));
package/dist/src/index.js CHANGED
@@ -337,47 +337,35 @@ class Token extends element_1.AstElement {
337
337
  lint(start = this.getAbsoluteIndex(), re) {
338
338
  let errors = super.lint(start, re);
339
339
  if (this.type === 'root') {
340
- const record = new Map(), selector = 'category,html-attr#id,ext-attr#id,table-attr#id,ext-attr#name';
340
+ const record = new Map(), selector = 'category,html-attr#id,ext-attr#id,table-attr#id';
341
341
  for (const cat of this.querySelectorAll(selector)) {
342
342
  let key;
343
343
  if (cat.type === 'category') {
344
344
  key = cat.name;
345
345
  }
346
346
  else {
347
- const value = cat.getValue(), attrs = cat.parentNode;
348
- if (cat.name === 'id') {
349
- key = `#${value === true ? '' : value}`;
347
+ const value = cat.getValue();
348
+ if (value && value !== true) {
349
+ key = `#${value}`;
350
350
  }
351
- else if (cat.tag === 'ref' && value !== true && value
352
- && attrs.parentNode.innerText) {
353
- const group = attrs.getAttr('group');
354
- key = `${typeof group === 'string' && group || ' '}#${value}`;
351
+ }
352
+ if (key) {
353
+ const thisCat = record.get(key);
354
+ if (thisCat) {
355
+ thisCat.add(cat);
355
356
  }
356
357
  else {
357
- continue;
358
+ record.set(key, new Set([cat]));
358
359
  }
359
360
  }
360
- const thisCat = record.get(key);
361
- if (thisCat) {
362
- thisCat.add(cat);
363
- }
364
- else {
365
- record.set(key, new Set([cat]));
366
- }
367
361
  }
368
362
  for (const [key, value] of record) {
369
363
  if (value.size > 1 && !key.startsWith('#mw-customcollapsible-')) {
370
- const isCat = !key.includes('#'), msg = `duplicated ${isCat ? 'category' : 'id/name'}`, severity = key.startsWith('#') ? 'warning' : 'error';
364
+ const isCat = !key.startsWith('#'), msg = `duplicated ${isCat ? 'category' : 'id'}`, severity = isCat ? 'error' : 'warning';
371
365
  errors.push(...[...value].map(cat => {
372
366
  const e = (0, lint_1.generateForSelf)(cat, { start: cat.getAbsoluteIndex() }, 'no-duplicate', msg, severity);
373
367
  if (isCat) {
374
- e.suggestions = [
375
- {
376
- desc: 'remove',
377
- range: [e.startIndex, e.endIndex],
378
- text: '',
379
- },
380
- ];
368
+ e.suggestions = [{ desc: 'remove', range: [e.startIndex, e.endIndex], text: '' }];
381
369
  }
382
370
  return e;
383
371
  }));
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LinkBaseToken = void 0;
4
4
  const lint_1 = require("../../util/lint");
5
5
  const constants_1 = require("../../util/constants");
6
+ const string_1 = require("../../util/string");
6
7
  const rect_1 = require("../../lib/rect");
7
8
  const index_1 = require("../../index");
8
9
  const index_2 = require("../index");
@@ -85,7 +86,9 @@ class LinkBaseToken extends index_2.Token {
85
86
  errors.push((0, lint_1.generateForChild)(target, rect, 'unknown-page', 'template in an internal link target', 'warning'));
86
87
  }
87
88
  if (encoded) {
88
- errors.push((0, lint_1.generateForChild)(target, rect, 'url-encoding', 'unnecessary URL encoding in an internal link'));
89
+ const e = (0, lint_1.generateForChild)(target, rect, 'url-encoding', 'unnecessary URL encoding in an internal link');
90
+ e.suggestions = [{ desc: 'decode', range: [e.startIndex, e.endIndex], text: (0, string_1.rawurldecode)(target.text()) }];
91
+ errors.push(e);
89
92
  }
90
93
  if (type === 'link' || type === 'category') {
91
94
  const textNode = linkText?.childNodes.find((c) => c.type === 'text' && c.data.includes('|'));
@@ -107,6 +110,7 @@ class LinkBaseToken extends index_2.Token {
107
110
  e.fix = {
108
111
  range: [e.startIndex + textNode.getRelativeIndex() + textNode.data.indexOf('#'), e.endIndex],
109
112
  text: '',
113
+ desc: 'remove',
110
114
  };
111
115
  }
112
116
  errors.push(e);
@@ -76,7 +76,7 @@ class FileToken extends base_1.LinkBaseToken {
76
76
  if (unscaled) {
77
77
  for (const arg of args.filter(({ name }) => name === 'width')) {
78
78
  const e = (0, lint_1.generateForChild)(arg, rect, 'invalid-gallery', 'invalid image parameter');
79
- e.fix = { range: [e.startIndex - 1, e.endIndex], text: '' };
79
+ e.fix = { range: [e.startIndex - 1, e.endIndex], text: '', desc: 'remove' };
80
80
  errors.push(e);
81
81
  }
82
82
  }
@@ -91,7 +91,11 @@ class FileToken extends base_1.LinkBaseToken {
91
91
  * @param msg 消息键
92
92
  * @param p1 替换$1
93
93
  */
94
- const generate = (msg, p1) => (arg) => (0, lint_1.generateForChild)(arg, rect, 'no-duplicate', index_1.default.msg(`${msg} image $1 parameter`, p1));
94
+ const generate = (msg, p1) => (arg) => {
95
+ const e = (0, lint_1.generateForChild)(arg, rect, 'no-duplicate', index_1.default.msg(`${msg} image $1 parameter`, p1));
96
+ e.suggestions = [{ desc: 'remove', range: [e.startIndex - 1, e.endIndex], text: '' }];
97
+ return e;
98
+ };
95
99
  for (const key of keys) {
96
100
  if (key === 'invalid' || key === 'width' && unscaled) {
97
101
  continue;
@@ -46,7 +46,9 @@ class GalleryImageToken extends file_1.FileToken {
46
46
  lint(start = this.getAbsoluteIndex(), re) {
47
47
  const errors = super.lint(start, re), { ns, interwiki } = this.getAttribute('title');
48
48
  if (interwiki || ns !== 6) {
49
- errors.push((0, lint_1.generateForSelf)(this, { start }, 'invalid-gallery', 'invalid gallery image'));
49
+ const e = (0, lint_1.generateForSelf)(this, { start }, 'invalid-gallery', 'invalid gallery image');
50
+ e.suggestions = [{ desc: 'prefix', range: [start, start], text: 'File:' }];
51
+ errors.push(e);
50
52
  }
51
53
  return errors;
52
54
  }
@@ -36,7 +36,7 @@ class RedirectTargetToken extends base_1.LinkBaseToken {
36
36
  const e = (0, lint_1.generateForChild)(this.lastChild, { start }, 'no-ignored', 'useless link text');
37
37
  e.startIndex--;
38
38
  e.startCol--;
39
- e.fix = { range: [e.startIndex, e.endIndex], text: '' };
39
+ e.fix = { range: [e.startIndex, e.endIndex], text: '', desc: 'remove' };
40
40
  errors.push(e);
41
41
  }
42
42
  return errors;
@@ -66,24 +66,10 @@ class MagicLinkToken extends index_2.Token {
66
66
  if (child) {
67
67
  const { data } = child, e = (0, lint_1.generateForChild)(child, rect, 'unterminated-url', index_1.default.msg('$1 in URL', pipe ? '"|"' : 'full-width punctuation'), 'warning'), { index, 0: s } = regex.exec(data), i = e.startIndex + index;
68
68
  e.suggestions = pipe
69
- ? [
70
- {
71
- desc: 'whitespace',
72
- range: [i, i + 1],
73
- text: ' ',
74
- },
75
- ]
69
+ ? [{ desc: 'whitespace', range: [i, i + 1], text: ' ' }]
76
70
  : [
77
- {
78
- desc: 'whitespace',
79
- range: [i, i],
80
- text: ' ',
81
- },
82
- {
83
- desc: 'escape',
84
- range: [i, i + s.length],
85
- text: encodeURI(s),
86
- },
71
+ { desc: 'whitespace', range: [i, i], text: ' ' },
72
+ { desc: 'escape', range: [i, i + s.length], text: encodeURI(s) },
87
73
  ];
88
74
  errors.push(e);
89
75
  }
@@ -68,16 +68,8 @@ class NestedToken extends index_2.Token {
68
68
  }).map(child => {
69
69
  const e = (0, lint_1.generateForChild)(child, rect, 'no-ignored', index_1.default.msg('invalid content in <$1>', this.name));
70
70
  e.suggestions = [
71
- {
72
- desc: 'remove',
73
- range: [e.startIndex, e.endIndex],
74
- text: '',
75
- },
76
- {
77
- desc: 'comment',
78
- range: [e.startIndex, e.endIndex],
79
- text: `<!--${child.toString()}-->`,
80
- },
71
+ { desc: 'remove', range: [e.startIndex, e.endIndex], text: '' },
72
+ { desc: 'comment', range: [e.startIndex, e.endIndex], text: `<!--${child.toString()}-->` },
81
73
  ];
82
74
  return e;
83
75
  }),
@@ -74,7 +74,7 @@ let CommentToken = (() => {
74
74
  return [];
75
75
  }
76
76
  const e = (0, lint_1.generateForSelf)(this, { start }, 'unclosed-comment', index_1.default.msg('unclosed $1', 'HTML comment'));
77
- e.fix = { range: [e.endIndex, e.endIndex], text: '-->' };
77
+ e.suggestions = [{ range: [e.endIndex, e.endIndex], text: '-->', desc: 'close' }];
78
78
  return [e];
79
79
  }
80
80
  /** @private */
@@ -14,7 +14,7 @@ class NowikiToken extends base_1.NowikiBaseToken {
14
14
  const { name, firstChild: { data } } = this;
15
15
  if ((name === 'templatestyles' || name === 'section') && data) {
16
16
  const e = (0, lint_1.generateForSelf)(this, { start }, 'void-ext', index_1.default.msg('nothing should be in <$1>', name));
17
- e.fix = { range: [start, e.endIndex], text: '' };
17
+ e.fix = { range: [start, e.endIndex], text: '', desc: 'empty' };
18
18
  return [e];
19
19
  }
20
20
  return super.lint(start, new RegExp(String.raw `<\s*(?:/\s*)${name === 'nowiki' ? '' : '?'}(${name})\b`, 'giu'));
@@ -33,11 +33,10 @@ class QuoteToken extends base_1.NowikiBaseToken {
33
33
  * @param endIndex 终点
34
34
  * @param length 长度
35
35
  */
36
- const getSuggestion = (startIndex, endIndex, length) => ({
37
- desc: 'escape',
38
- range: [startIndex, endIndex],
39
- text: '&apos;'.repeat(length),
40
- });
36
+ const getSuggestion = (startIndex, endIndex, length) => [
37
+ { desc: 'escape', range: [startIndex, endIndex], text: '&apos;'.repeat(length) },
38
+ { desc: 'remove', range: [startIndex, endIndex], text: '' },
39
+ ];
41
40
  if (previousSibling?.type === 'text' && previousSibling.data.endsWith(`'`)) {
42
41
  refError = (0, lint_1.generateForSelf)(this, rect, 'lonely-apos', message);
43
42
  const { startIndex: endIndex, startLine: endLine, startCol: endCol } = refError, [, { length }] = /(?:^|[^'])('+)$/u.exec(previousSibling.data), startIndex = start - length;
@@ -48,7 +47,7 @@ class QuoteToken extends base_1.NowikiBaseToken {
48
47
  startCol: endCol - length,
49
48
  endLine,
50
49
  endCol,
51
- suggestions: [getSuggestion(startIndex, endIndex, length)],
50
+ suggestions: getSuggestion(startIndex, endIndex, length),
52
51
  });
53
52
  }
54
53
  if (nextSibling?.type === 'text' && nextSibling.data.startsWith(`'`)) {
@@ -61,11 +60,14 @@ class QuoteToken extends base_1.NowikiBaseToken {
61
60
  startLine,
62
61
  startCol,
63
62
  endCol: startCol + length,
64
- suggestions: [getSuggestion(startIndex, endIndex, length)],
63
+ suggestions: getSuggestion(startIndex, endIndex, length),
65
64
  });
66
65
  }
67
66
  if (bold && this.closest('heading-title')) {
68
- errors.push((0, lint_1.generateForSelf)(this, rect, 'bold-header', 'bold in section header', 'warning'));
67
+ errors.push({
68
+ ...(0, lint_1.generateForSelf)(this, rect, 'bold-header', 'bold in section header', 'warning'),
69
+ suggestions: [{ desc: 'remove', range: [start, start + 3], text: '' }],
70
+ });
69
71
  }
70
72
  return errors;
71
73
  }
@@ -52,13 +52,7 @@ class ParamTagToken extends index_2.Token {
52
52
  const i = grandChildren.findIndex(({ type }) => type !== 'text'), str = grandChildren.slice(0, i === -1 ? undefined : i).map(String).join('');
53
53
  if (str && !(i === -1 ? /^[a-z]+(?:\[\])?\s*=/iu : /^[a-z]+(?:\[\])?\s*(?:=|$)/iu).test(str)) {
54
54
  const e = (0, lint_1.generateForChild)(child, rect, 'no-ignored', msg);
55
- e.suggestions = [
56
- {
57
- desc: 'remove',
58
- range: [e.startIndex, e.endIndex],
59
- text: '',
60
- },
61
- ];
55
+ e.suggestions = [{ desc: 'remove', range: [e.startIndex, e.endIndex], text: '' }];
62
56
  errors.push(e);
63
57
  }
64
58
  else {
@@ -69,7 +69,7 @@ class ParameterToken extends index_2.Token {
69
69
  e.startCol = e.endCol;
70
70
  e.endIndex++;
71
71
  e.endCol++;
72
- e.fix = { range: [e.startIndex, e.endIndex], text: '{{=}}' };
72
+ e.fix = { range: [e.startIndex, e.endIndex], text: '{{=}}', desc: 'escape' };
73
73
  errors.push(e);
74
74
  }
75
75
  return errors;
@@ -111,6 +111,7 @@ class TdToken extends base_1.TableBaseToken {
111
111
  e.fix = {
112
112
  range: [e.startIndex, e.endIndex],
113
113
  text: data.replace(/\|\|/gu, `\n${syntax}`),
114
+ desc: 'newline',
114
115
  };
115
116
  }
116
117
  else {
@@ -80,24 +80,12 @@ let IncludeToken = (() => {
80
80
  const errors = [], { firstChild, closed, name } = this, rect = new rect_1.BoundingRect(this, start);
81
81
  if (firstChild.data.trim()) {
82
82
  const e = (0, lint_1.generateForChild)(firstChild, rect, 'no-ignored', 'useless attribute', 'warning');
83
- e.suggestions = [
84
- {
85
- desc: 'remove',
86
- range: [e.startIndex, e.endIndex],
87
- text: '',
88
- },
89
- ];
83
+ e.suggestions = [{ desc: 'remove', range: [e.startIndex, e.endIndex], text: '' }];
90
84
  errors.push(e);
91
85
  }
92
86
  if (!closed) {
93
87
  const e = (0, lint_1.generateForSelf)(this, rect, 'unclosed-comment', index_1.default.msg('unclosed $1', `<${name}>`));
94
- e.suggestions = [
95
- {
96
- desc: 'close',
97
- range: [e.endIndex, e.endIndex],
98
- text: `</${name}>`,
99
- },
100
- ];
88
+ e.suggestions = [{ desc: 'close', range: [e.endIndex, e.endIndex], text: `</${name}>` }];
101
89
  errors.push(e);
102
90
  }
103
91
  return errors;
@@ -193,6 +193,7 @@ class TranscludeToken extends index_2.Token {
193
193
  e.fix = {
194
194
  range: [e.startIndex + textNode.getRelativeIndex() + textNode.data.indexOf('#'), e.endIndex],
195
195
  text: '',
196
+ desc: 'remove',
196
197
  };
197
198
  errors.push(e);
198
199
  }
@@ -203,7 +204,11 @@ class TranscludeToken extends index_2.Token {
203
204
  }
204
205
  const duplicatedArgs = this.getDuplicatedArgs().filter(([, parameter]) => !parameter[0].querySelector('ext'));
205
206
  if (duplicatedArgs.length > 0) {
206
- errors.push(...duplicatedArgs.flatMap(([, args]) => args).map(arg => (0, lint_1.generateForChild)(arg, rect, 'no-duplicate', 'duplicated parameter')));
207
+ errors.push(...duplicatedArgs.flatMap(([, args]) => args).map(arg => {
208
+ const e = (0, lint_1.generateForChild)(arg, rect, 'no-duplicate', 'duplicated parameter');
209
+ e.suggestions = [{ desc: 'remove', range: [e.startIndex - 1, e.endIndex], text: '' }];
210
+ return e;
211
+ }));
207
212
  }
208
213
  return errors;
209
214
  }
package/i18n/zh-hans.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "content to be moved out from the table": "将被移出表格的内容",
12
12
  "duplicated $1 attribute": "重复的$1属性",
13
13
  "duplicated category": "重复的分类",
14
- "duplicated id/name": "重复的id/name",
14
+ "duplicated id": "重复的id",
15
15
  "duplicated image $1 parameter": "重复的图片$1参数",
16
16
  "duplicated parameter": "重复参数",
17
17
  "extension tag in HTML tag attributes": "HTML标签属性中的扩展标签",