wikiparser-node 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/default.json +129 -66
- package/config/zhwiki.json +4 -4
- package/index.js +74 -65
- package/lib/element.js +125 -152
- package/lib/node.js +251 -223
- package/lib/ranges.js +2 -2
- package/lib/text.js +64 -64
- package/lib/title.js +8 -7
- package/mixin/hidden.js +2 -0
- package/mixin/sol.js +1 -2
- package/package.json +4 -3
- package/parser/brackets.js +8 -2
- package/parser/externalLinks.js +1 -1
- package/parser/hrAndDoubleUnderscore.js +4 -4
- package/parser/links.js +7 -7
- package/parser/table.js +12 -10
- package/src/arg.js +53 -48
- package/src/atom/index.js +7 -5
- package/src/attribute.js +91 -80
- package/src/charinsert.js +91 -0
- package/src/converter.js +22 -11
- package/src/converterFlags.js +72 -62
- package/src/converterRule.js +49 -49
- package/src/extLink.js +30 -28
- package/src/gallery.js +56 -32
- package/src/hasNowiki/index.js +42 -0
- package/src/hasNowiki/pre.js +40 -0
- package/src/heading.js +15 -11
- package/src/html.js +38 -38
- package/src/imageParameter.js +64 -48
- package/src/imagemap.js +205 -0
- package/src/imagemapLink.js +43 -0
- package/src/index.js +222 -124
- package/src/link/category.js +4 -8
- package/src/link/file.js +95 -59
- package/src/link/galleryImage.js +74 -10
- package/src/link/index.js +61 -39
- package/src/magicLink.js +21 -22
- package/src/nested/choose.js +24 -0
- package/src/nested/combobox.js +23 -0
- package/src/nested/index.js +88 -0
- package/src/nested/references.js +23 -0
- package/src/nowiki/comment.js +17 -17
- package/src/nowiki/dd.js +2 -2
- package/src/nowiki/doubleUnderscore.js +14 -14
- package/src/nowiki/index.js +12 -0
- package/src/onlyinclude.js +10 -8
- package/src/paramTag/index.js +83 -0
- package/src/paramTag/inputbox.js +42 -0
- package/src/parameter.js +32 -18
- package/src/syntax.js +9 -1
- package/src/table/index.js +33 -32
- package/src/table/td.js +51 -57
- package/src/table/tr.js +6 -6
- package/src/tagPair/ext.js +58 -40
- package/src/tagPair/include.js +1 -1
- package/src/tagPair/index.js +21 -20
- package/src/transclude.js +158 -143
- package/tool/index.js +720 -439
- package/util/base.js +17 -0
- package/util/debug.js +1 -1
- package/util/diff.js +1 -1
- package/util/string.js +20 -20
package/lib/text.js
CHANGED
|
@@ -23,6 +23,68 @@ class AstText extends AstNode {
|
|
|
23
23
|
});
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/** 输出字符串 */
|
|
27
|
+
toString() {
|
|
28
|
+
return this.data;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 修改内容
|
|
33
|
+
* @param {string} text 新内容
|
|
34
|
+
*/
|
|
35
|
+
#setData(text) {
|
|
36
|
+
text = String(text);
|
|
37
|
+
const {data} = this,
|
|
38
|
+
e = new Event('text', {bubbles: true});
|
|
39
|
+
this.setAttribute('data', text);
|
|
40
|
+
if (data !== text) {
|
|
41
|
+
this.dispatchEvent(e, {oldText: data, newText: text});
|
|
42
|
+
}
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 替换字符串
|
|
48
|
+
* @param {string} text 替换的字符串
|
|
49
|
+
*/
|
|
50
|
+
replaceData(text = '') {
|
|
51
|
+
this.#setData(text);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
static errorSyntax = /[{}]+|\[{2,}|\[(?!(?:(?!https?\b)[^[])*\])|(?<=^|\])([^[]*?)\]+|<(?=\s*\/?\w+[\s/>])/giu;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Linter
|
|
58
|
+
* @param {number} start 起始位置
|
|
59
|
+
* @returns {LintError[]}
|
|
60
|
+
*/
|
|
61
|
+
lint(start = 0) {
|
|
62
|
+
const {data} = this,
|
|
63
|
+
errors = [...data.matchAll(AstText.errorSyntax)];
|
|
64
|
+
if (errors.length > 0) {
|
|
65
|
+
const {top, left} = this.getRootNode().posFromIndex(start);
|
|
66
|
+
return errors.map(({0: error, 1: prefix, index}) => {
|
|
67
|
+
if (prefix) {
|
|
68
|
+
index += prefix.length;
|
|
69
|
+
error = error.slice(prefix.length);
|
|
70
|
+
}
|
|
71
|
+
const lines = data.slice(0, index).split('\n'),
|
|
72
|
+
startLine = lines.length + top - 1,
|
|
73
|
+
{length} = lines.at(-1),
|
|
74
|
+
startCol = lines.length > 1 ? length : left + length;
|
|
75
|
+
return {
|
|
76
|
+
message: `孤立的"${error[0]}"`,
|
|
77
|
+
severity: error[0] === '{' || error[0] === '}' ? 'error' : 'warning',
|
|
78
|
+
startLine,
|
|
79
|
+
endLine: startLine,
|
|
80
|
+
startCol,
|
|
81
|
+
endCol: startCol + error.length,
|
|
82
|
+
};
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
|
|
26
88
|
/** 复制 */
|
|
27
89
|
cloneNode() {
|
|
28
90
|
return new AstText(this.data);
|
|
@@ -43,31 +105,11 @@ class AstText extends AstNode {
|
|
|
43
105
|
: super.getAttribute(key);
|
|
44
106
|
}
|
|
45
107
|
|
|
46
|
-
/** 输出字符串 */
|
|
47
|
-
toString() {
|
|
48
|
-
return this.data;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
108
|
/** @override */
|
|
52
109
|
text() {
|
|
53
110
|
return this.data;
|
|
54
111
|
}
|
|
55
112
|
|
|
56
|
-
/**
|
|
57
|
-
* 修改内容
|
|
58
|
-
* @param {string} text 新内容
|
|
59
|
-
*/
|
|
60
|
-
#setData(text) {
|
|
61
|
-
text = String(text);
|
|
62
|
-
const {data} = this,
|
|
63
|
-
e = new Event('text', {bubbles: true});
|
|
64
|
-
this.setAttribute('data', text);
|
|
65
|
-
if (data !== text) {
|
|
66
|
-
this.dispatchEvent(e, {oldText: data, newText: text});
|
|
67
|
-
}
|
|
68
|
-
return this;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
113
|
/**
|
|
72
114
|
* 在后方添加字符串
|
|
73
115
|
* @param {string} text 添加的字符串
|
|
@@ -94,14 +136,6 @@ class AstText extends AstNode {
|
|
|
94
136
|
this.#setData(this.data.slice(0, offset) + text + this.data.slice(offset));
|
|
95
137
|
}
|
|
96
138
|
|
|
97
|
-
/**
|
|
98
|
-
* 替换字符串
|
|
99
|
-
* @param {string} text 替换的字符串
|
|
100
|
-
*/
|
|
101
|
-
replaceData(text = '') {
|
|
102
|
-
this.#setData(text);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
139
|
/**
|
|
106
140
|
* 提取子串
|
|
107
141
|
* @param {number} offset 起始位置
|
|
@@ -118,9 +152,9 @@ class AstText extends AstNode {
|
|
|
118
152
|
* @throws `Error` 没有父节点
|
|
119
153
|
*/
|
|
120
154
|
splitText(offset) {
|
|
121
|
-
if (
|
|
155
|
+
if (!Number.isInteger(offset)) {
|
|
122
156
|
this.typeError('splitText', 'Number');
|
|
123
|
-
} else if (offset > this.length || offset < -this.length
|
|
157
|
+
} else if (offset > this.length || offset < -this.length) {
|
|
124
158
|
throw new RangeError(`错误的断开位置!${offset}`);
|
|
125
159
|
}
|
|
126
160
|
const {parentNode, data} = this;
|
|
@@ -135,40 +169,6 @@ class AstText extends AstNode {
|
|
|
135
169
|
parentNode.setAttribute('childNodes', childNodes);
|
|
136
170
|
return newText;
|
|
137
171
|
}
|
|
138
|
-
|
|
139
|
-
static errorSyntax = /[{}]+|\[{2,}|\[(?!(?:(?!https?\b)[^[])*\])|(?<=^|\])([^[]*?)\]+|<(?=\s*\/?\w+[\s/>])/giu;
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Linter
|
|
143
|
-
* @param {number} start 起始位置
|
|
144
|
-
* @returns {LintError[]}
|
|
145
|
-
*/
|
|
146
|
-
lint(start = 0) {
|
|
147
|
-
const {data} = this,
|
|
148
|
-
errors = [...data.matchAll(AstText.errorSyntax)];
|
|
149
|
-
if (errors.length > 0) {
|
|
150
|
-
const {top, left} = this.getRootNode().posFromIndex(start);
|
|
151
|
-
return errors.map(({0: error, 1: prefix, index}) => {
|
|
152
|
-
if (prefix) {
|
|
153
|
-
index += prefix.length;
|
|
154
|
-
error = error.slice(prefix.length);
|
|
155
|
-
}
|
|
156
|
-
const lines = data.slice(0, index).split('\n'),
|
|
157
|
-
startLine = lines.length + top - 1,
|
|
158
|
-
{length} = lines.at(-1),
|
|
159
|
-
startCol = lines.length > 1 ? length : left + length;
|
|
160
|
-
return {
|
|
161
|
-
message: `孤立的"${error[0]}"`,
|
|
162
|
-
severity: error[0] === '{' || error[0] === '}' ? 'error' : 'warning',
|
|
163
|
-
startLine,
|
|
164
|
-
endLine: startLine,
|
|
165
|
-
startCol,
|
|
166
|
-
endCol: startCol + error.length,
|
|
167
|
-
};
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
return [];
|
|
171
|
-
}
|
|
172
172
|
}
|
|
173
173
|
|
|
174
174
|
Parser.classes.AstText = __filename;
|
package/lib/title.js
CHANGED
|
@@ -4,13 +4,13 @@ const Parser = require('..');
|
|
|
4
4
|
|
|
5
5
|
/** MediaWiki页面标题对象 */
|
|
6
6
|
class Title {
|
|
7
|
+
valid = true;
|
|
8
|
+
ns = 0;
|
|
7
9
|
title = '';
|
|
8
10
|
main = '';
|
|
9
11
|
prefix = '';
|
|
10
|
-
ns = 0;
|
|
11
12
|
interwiki = '';
|
|
12
13
|
fragment = '';
|
|
13
|
-
valid = true;
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* @param {string} title 标题(含或不含命名空间前缀)
|
|
@@ -39,24 +39,25 @@ class Title {
|
|
|
39
39
|
}
|
|
40
40
|
this.ns = nsid[namespace.toLowerCase()];
|
|
41
41
|
const i = title.indexOf('#');
|
|
42
|
+
let fragment = '';
|
|
42
43
|
if (i !== -1) {
|
|
43
|
-
|
|
44
|
+
fragment = title.slice(i + 1).trimEnd();
|
|
44
45
|
if (fragment.includes('%')) {
|
|
45
46
|
try {
|
|
46
|
-
|
|
47
|
+
fragment = decodeURIComponent(fragment);
|
|
47
48
|
} catch {}
|
|
48
49
|
} else if (fragment.includes('.')) {
|
|
49
50
|
try {
|
|
50
|
-
|
|
51
|
+
fragment = decodeURIComponent(fragment.replaceAll('.', '%'));
|
|
51
52
|
} catch {}
|
|
52
53
|
}
|
|
53
|
-
this.fragment ||= fragment;
|
|
54
54
|
title = title.slice(0, i).trim();
|
|
55
55
|
}
|
|
56
|
+
this.valid = Boolean(title || fragment) && !/\0\d+[eh!+-]\x7F|[<>[\]{}|]/u.test(title);
|
|
56
57
|
this.main = title && `${title[0].toUpperCase()}${title.slice(1)}`;
|
|
57
58
|
this.prefix = `${namespace}${namespace && ':'}`;
|
|
58
59
|
this.title = `${iw ? `${this.interwiki}:` : ''}${this.prefix}${this.main.replaceAll(' ', '_')}`;
|
|
59
|
-
this.
|
|
60
|
+
this.fragment = fragment;
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
/** @override */
|
package/mixin/hidden.js
CHANGED
package/mixin/sol.js
CHANGED
|
@@ -18,8 +18,7 @@ const sol = Constructor => class SolToken extends Constructor {
|
|
|
18
18
|
#isRoot(includeHeading) {
|
|
19
19
|
const {parentNode, type} = this;
|
|
20
20
|
return parentNode?.type === 'root'
|
|
21
|
-
|| parentNode?.type === 'ext-inner' && parentNode
|
|
22
|
-
&& (includeHeading || type !== 'heading');
|
|
21
|
+
|| parentNode?.type === 'ext-inner' && (includeHeading || type !== 'heading' && parentNode.name === 'poem');
|
|
23
22
|
}
|
|
24
23
|
|
|
25
24
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wikiparser-node",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "A Node.js parser for MediaWiki markup with AST",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mediawiki",
|
|
@@ -28,8 +28,9 @@
|
|
|
28
28
|
"url": "git+https://github.com/bhsd-harry/wikiparser-node.git"
|
|
29
29
|
},
|
|
30
30
|
"scripts": {
|
|
31
|
-
"test": "node test/test.js",
|
|
32
|
-
"real": "node test/real.js"
|
|
31
|
+
"test": "eslint . && node test/test.js",
|
|
32
|
+
"real": "node test/real.js",
|
|
33
|
+
"single": "node --prof test/single.js && node --prof-process isolate-0x*-v8.log > test/processed.txt && rm isolate-0x*-v8.log"
|
|
33
34
|
},
|
|
34
35
|
"devDependencies": {
|
|
35
36
|
"@types/node": "^17.0.23",
|
package/parser/brackets.js
CHANGED
|
@@ -57,12 +57,18 @@ const parseBrackets = (text, config = Parser.getConfig(), accum = []) => {
|
|
|
57
57
|
{length} = accum;
|
|
58
58
|
lastIndex = curIndex + close.length; // 这不是最终的lastIndex
|
|
59
59
|
parts.at(-1).push(text.slice(topPos, curIndex));
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
let skip = false,
|
|
61
|
+
ch = 't';
|
|
62
62
|
if (close.length === 3) {
|
|
63
63
|
const ArgToken = require('../src/arg');
|
|
64
64
|
new ArgToken(parts.map(part => part.join('=')), config, accum);
|
|
65
65
|
} else {
|
|
66
|
+
const name = removeComment(parts[0][0]);
|
|
67
|
+
if (name in marks) {
|
|
68
|
+
ch = marks[name]; // 标记{{!}}等
|
|
69
|
+
} else if (/^(?:fullurl|canonicalurl|filepath):./iu.test(name)) {
|
|
70
|
+
ch = 'm';
|
|
71
|
+
}
|
|
66
72
|
try {
|
|
67
73
|
const TranscludeToken = require('../src/transclude');
|
|
68
74
|
new TranscludeToken(parts[0][0], parts.slice(1), config, accum);
|
package/parser/externalLinks.js
CHANGED
|
@@ -11,7 +11,7 @@ const {extUrlChar} = require('../util/string'),
|
|
|
11
11
|
const parseExternalLinks = (wikitext, config = Parser.getConfig(), accum = []) => {
|
|
12
12
|
const ExtLinkToken = require('../src/extLink');
|
|
13
13
|
const regex = new RegExp(
|
|
14
|
-
`\\[((?:${config.protocol}|//)${extUrlChar})(\\p{Zs}*)([^\\]\x01-\x08\x0A-\x1F\uFFFD]*)\\]`,
|
|
14
|
+
`\\[((?:${config.protocol}|//)${extUrlChar}|\0\\d+m\x7F)(\\p{Zs}*)([^\\]\x01-\x08\x0A-\x1F\uFFFD]*)\\]`,
|
|
15
15
|
'giu',
|
|
16
16
|
);
|
|
17
17
|
return wikitext.replaceAll(regex, /** @type {function(...string): string} */ (_, url, space, text) => {
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const Parser = require('..')
|
|
4
|
-
AstText = require('../lib/text'),
|
|
5
|
-
Token = require('../src');
|
|
3
|
+
const Parser = require('..');
|
|
6
4
|
|
|
7
5
|
/**
|
|
8
6
|
* 解析\<hr\>和状态开关
|
|
@@ -10,7 +8,9 @@ const Parser = require('..'),
|
|
|
10
8
|
* @param {accum} accum
|
|
11
9
|
*/
|
|
12
10
|
const parseHrAndDoubleUnderscore = ({firstChild: {data}, type, name}, config = Parser.getConfig(), accum = []) => {
|
|
13
|
-
const
|
|
11
|
+
const AstText = require('../lib/text'),
|
|
12
|
+
Token = require('../src'),
|
|
13
|
+
HrToken = require('../src/nowiki/hr'),
|
|
14
14
|
DoubleUnderscoreToken = require('../src/nowiki/doubleUnderscore');
|
|
15
15
|
const {doubleUnderscore} = config;
|
|
16
16
|
if (type !== 'root' && (type !== 'ext-inner' || name !== 'poem')) {
|
package/parser/links.js
CHANGED
|
@@ -9,17 +9,17 @@ const Parser = require('..');
|
|
|
9
9
|
*/
|
|
10
10
|
const parseLinks = (wikitext, config = Parser.getConfig(), accum = []) => {
|
|
11
11
|
const parseQuotes = require('./quotes.js');
|
|
12
|
-
const regex = /^([^\n<>[\]{}|]+)(
|
|
13
|
-
regexImg = /^([^\n<>[\]{}|]+)
|
|
12
|
+
const regex = /^((?:(?!\0\d+!\x7F)[^\n<>[\]{}|])+)(?:(\||\0\d+!\x7F)(.*?[^\]]))?\]\](.*)$/su,
|
|
13
|
+
regexImg = /^((?:(?!\0\d+!\x7F)[^\n<>[\]{}|])+)(\||\0\d+!\x7F)(.*)$/su,
|
|
14
14
|
regexExt = new RegExp(`^\\s*(?:${config.protocol})`, 'iu'),
|
|
15
15
|
bits = wikitext.split('[[');
|
|
16
16
|
let s = bits.shift();
|
|
17
17
|
for (let i = 0; i < bits.length; i++) {
|
|
18
|
-
let mightBeImg, link, text, after;
|
|
18
|
+
let mightBeImg, link, delimiter, text, after;
|
|
19
19
|
const x = bits[i],
|
|
20
20
|
m = regex.exec(x);
|
|
21
21
|
if (m) {
|
|
22
|
-
[, link, text, after] = m;
|
|
22
|
+
[, link, delimiter, text, after] = m;
|
|
23
23
|
if (after[0] === ']' && text?.includes('[')) {
|
|
24
24
|
text += ']';
|
|
25
25
|
after = after.slice(1);
|
|
@@ -28,7 +28,7 @@ const parseLinks = (wikitext, config = Parser.getConfig(), accum = []) => {
|
|
|
28
28
|
const m2 = regexImg.exec(x);
|
|
29
29
|
if (m2) {
|
|
30
30
|
mightBeImg = true;
|
|
31
|
-
[, link, text] = m2;
|
|
31
|
+
[, link, delimiter, text] = m2;
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
if (link === undefined || regexExt.test(link) || /\0\d+[exhbru]\x7F/u.test(link)) {
|
|
@@ -74,7 +74,7 @@ const parseLinks = (wikitext, config = Parser.getConfig(), accum = []) => {
|
|
|
74
74
|
}
|
|
75
75
|
text = parseLinks(text, config, accum);
|
|
76
76
|
if (!found) {
|
|
77
|
-
s += `[[${link}
|
|
77
|
+
s += `[[${link}${delimiter}${text}`;
|
|
78
78
|
continue;
|
|
79
79
|
}
|
|
80
80
|
}
|
|
@@ -88,7 +88,7 @@ const parseLinks = (wikitext, config = Parser.getConfig(), accum = []) => {
|
|
|
88
88
|
LinkToken = require('../src/link/category');
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
|
-
new LinkToken(link, text, title, config, accum);
|
|
91
|
+
new LinkToken(link, text, title, config, accum, delimiter);
|
|
92
92
|
}
|
|
93
93
|
return s;
|
|
94
94
|
};
|
package/parser/table.js
CHANGED
|
@@ -5,8 +5,7 @@ const Parser = require('..'),
|
|
|
5
5
|
Token = require('../src'),
|
|
6
6
|
TableToken = require('../src/table'),
|
|
7
7
|
TrToken = require('../src/table/tr'),
|
|
8
|
-
TdToken = require('../src/table/td')
|
|
9
|
-
DdToken = require('../src/nowiki/dd');
|
|
8
|
+
TdToken = require('../src/table/td');
|
|
10
9
|
|
|
11
10
|
/**
|
|
12
11
|
* 解析表格,注意`tr`和`td`包含开头的换行
|
|
@@ -16,7 +15,9 @@ const Parser = require('..'),
|
|
|
16
15
|
const parseTable = ({firstChild: {data}, type, name}, config = Parser.getConfig(), accum = []) => {
|
|
17
16
|
const /** @type {TrToken[]} */ stack = [],
|
|
18
17
|
lines = data.split('\n');
|
|
19
|
-
let out = type === 'root' || type === 'ext-inner' && name === 'poem'
|
|
18
|
+
let out = type === 'root' || type === 'parameter-value' || type === 'ext-inner' && name === 'poem'
|
|
19
|
+
? ''
|
|
20
|
+
: `\n${lines.shift()}`;
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
23
|
* 向表格中插入纯文本
|
|
@@ -28,13 +29,13 @@ const parseTable = ({firstChild: {data}, type, name}, config = Parser.getConfig(
|
|
|
28
29
|
out += str;
|
|
29
30
|
return;
|
|
30
31
|
}
|
|
31
|
-
const /** @type {Token
|
|
32
|
-
if (lastChild.
|
|
33
|
-
lastChild.
|
|
32
|
+
const /** @type {Token}} */ {lastChild} = top;
|
|
33
|
+
if (lastChild.constructor === Token) {
|
|
34
|
+
lastChild.setText(String(lastChild) + str);
|
|
34
35
|
} else {
|
|
35
36
|
const token = new Token(str, config, true, accum);
|
|
36
37
|
token.type = 'table-inter';
|
|
37
|
-
top.
|
|
38
|
+
top.insertAt(token.setAttribute('stage', 3));
|
|
38
39
|
}
|
|
39
40
|
};
|
|
40
41
|
for (const outLine of lines) {
|
|
@@ -48,6 +49,7 @@ const parseTable = ({firstChild: {data}, type, name}, config = Parser.getConfig(
|
|
|
48
49
|
}
|
|
49
50
|
const [, indent, moreSpaces, tableSyntax, attr] = matchesStart;
|
|
50
51
|
if (indent) {
|
|
52
|
+
const DdToken = require('../src/nowiki/dd');
|
|
51
53
|
new DdToken(indent, config, accum);
|
|
52
54
|
}
|
|
53
55
|
push(`\n${spaces}${indent && `\0${accum.length - 1}d\x7F`}${moreSpaces}\0${accum.length}b\x7F`, top);
|
|
@@ -82,7 +84,7 @@ const parseTable = ({firstChild: {data}, type, name}, config = Parser.getConfig(
|
|
|
82
84
|
}
|
|
83
85
|
const tr = new TrToken(`\n${spaces}${row}`, attr, config, accum);
|
|
84
86
|
stack.push(top, tr);
|
|
85
|
-
top.
|
|
87
|
+
top.insertAt(tr);
|
|
86
88
|
} else {
|
|
87
89
|
if (top.type === 'td') {
|
|
88
90
|
top = stack.pop();
|
|
@@ -95,14 +97,14 @@ const parseTable = ({firstChild: {data}, type, name}, config = Parser.getConfig(
|
|
|
95
97
|
lastSyntax = `\n${spaces}${cell}`;
|
|
96
98
|
while (mt) {
|
|
97
99
|
const td = new TdToken(lastSyntax, attr.slice(lastIndex, mt.index), config, accum);
|
|
98
|
-
top.
|
|
100
|
+
top.insertAt(td);
|
|
99
101
|
({lastIndex} = regex);
|
|
100
102
|
[lastSyntax] = mt;
|
|
101
103
|
mt = regex.exec(attr);
|
|
102
104
|
}
|
|
103
105
|
const td = new TdToken(lastSyntax, attr.slice(lastIndex), config, accum);
|
|
104
106
|
stack.push(top, td);
|
|
105
|
-
top.
|
|
107
|
+
top.insertAt(td);
|
|
106
108
|
}
|
|
107
109
|
}
|
|
108
110
|
return out.slice(1);
|
package/src/arg.js
CHANGED
|
@@ -7,11 +7,16 @@ const {text, noWrap} = require('../util/string'),
|
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* `{{{}}}`包裹的参数
|
|
10
|
-
* @classdesc `{childNodes: [AtomToken, Token, ...HiddenToken]}`
|
|
10
|
+
* @classdesc `{childNodes: [AtomToken, ?Token, ...HiddenToken]}`
|
|
11
11
|
*/
|
|
12
12
|
class ArgToken extends Token {
|
|
13
13
|
type = 'arg';
|
|
14
14
|
|
|
15
|
+
/** default */
|
|
16
|
+
get default() {
|
|
17
|
+
return this.childNodes[1]?.text() ?? false;
|
|
18
|
+
}
|
|
19
|
+
|
|
15
20
|
/**
|
|
16
21
|
* @param {string[]} parts 以'|'分隔的各部分
|
|
17
22
|
* @param {accum} accum
|
|
@@ -25,39 +30,16 @@ class ArgToken extends Token {
|
|
|
25
30
|
const token = new AtomToken(parts[i], i === 0 ? 'arg-name' : undefined, config, accum, {
|
|
26
31
|
'Stage-2': ':', '!HeadingToken': '',
|
|
27
32
|
});
|
|
28
|
-
|
|
33
|
+
super.insertAt(token);
|
|
29
34
|
} else {
|
|
30
35
|
const token = new Token(parts[i], config, true, accum);
|
|
31
36
|
token.type = 'arg-default';
|
|
32
|
-
|
|
37
|
+
super.insertAt(token.setAttribute('stage', 2));
|
|
33
38
|
}
|
|
34
39
|
}
|
|
35
40
|
this.getAttribute('protectChildren')(0);
|
|
36
41
|
}
|
|
37
42
|
|
|
38
|
-
/** @override */
|
|
39
|
-
cloneNode() {
|
|
40
|
-
const [name, ...cloned] = this.cloneChildNodes();
|
|
41
|
-
return Parser.run(() => {
|
|
42
|
-
const token = new ArgToken([''], this.getAttribute('config'));
|
|
43
|
-
token.firstChild.safeReplaceWith(name);
|
|
44
|
-
token.append(...cloned);
|
|
45
|
-
return token.afterBuild();
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/** @override */
|
|
50
|
-
afterBuild() {
|
|
51
|
-
this.setAttribute('name', this.firstChild.text().trim());
|
|
52
|
-
const /** @type {AstListener} */ argListener = ({prevTarget}) => {
|
|
53
|
-
if (prevTarget === this.firstChild) {
|
|
54
|
-
this.setAttribute('name', prevTarget.text().trim());
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
this.addEventListener(['remove', 'insert', 'replace', 'text'], argListener);
|
|
58
|
-
return this;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
43
|
/**
|
|
62
44
|
* @override
|
|
63
45
|
* @param {string} selector
|
|
@@ -81,6 +63,47 @@ class ArgToken extends Token {
|
|
|
81
63
|
return super.print({pre: '{{{', post: '}}}', sep: '|'});
|
|
82
64
|
}
|
|
83
65
|
|
|
66
|
+
/**
|
|
67
|
+
* @override
|
|
68
|
+
* @param {number} start 起始位置
|
|
69
|
+
* @returns {LintError[]}
|
|
70
|
+
*/
|
|
71
|
+
lint(start = 0) {
|
|
72
|
+
const {childNodes: [argName, argDefault, ...rest]} = this,
|
|
73
|
+
errors = argName.lint(start + 3);
|
|
74
|
+
if (argDefault) {
|
|
75
|
+
errors.push(...argDefault.lint(start + 4 + String(argName).length));
|
|
76
|
+
}
|
|
77
|
+
if (rest.length > 0) {
|
|
78
|
+
const rect = this.getRootNode().posFromIndex(start);
|
|
79
|
+
errors.push(...rest.map(child => generateForChild(child, rect, '三重括号内的不可见部分')));
|
|
80
|
+
}
|
|
81
|
+
return errors;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** @override */
|
|
85
|
+
cloneNode() {
|
|
86
|
+
const [name, ...cloned] = this.cloneChildNodes();
|
|
87
|
+
return Parser.run(() => {
|
|
88
|
+
const token = new ArgToken([''], this.getAttribute('config'));
|
|
89
|
+
token.firstChild.safeReplaceWith(name);
|
|
90
|
+
token.append(...cloned);
|
|
91
|
+
return token.afterBuild();
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** @override */
|
|
96
|
+
afterBuild() {
|
|
97
|
+
this.setAttribute('name', this.firstChild.text().trim());
|
|
98
|
+
const /** @type {AstListener} */ argListener = ({prevTarget}) => {
|
|
99
|
+
if (prevTarget === this.firstChild) {
|
|
100
|
+
this.setAttribute('name', prevTarget.text().trim());
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
this.addEventListener(['remove', 'insert', 'replace', 'text'], argListener);
|
|
104
|
+
return this;
|
|
105
|
+
}
|
|
106
|
+
|
|
84
107
|
/**
|
|
85
108
|
* @override
|
|
86
109
|
* @returns {string}
|
|
@@ -101,24 +124,6 @@ class ArgToken extends Token {
|
|
|
101
124
|
});
|
|
102
125
|
}
|
|
103
126
|
|
|
104
|
-
/**
|
|
105
|
-
* @override
|
|
106
|
-
* @param {number} start 起始位置
|
|
107
|
-
* @returns {LintError[]}
|
|
108
|
-
*/
|
|
109
|
-
lint(start = 0) {
|
|
110
|
-
const {childNodes: [argName, argDefault, ...rest]} = this,
|
|
111
|
-
errors = argName.lint(start + 3);
|
|
112
|
-
if (argDefault) {
|
|
113
|
-
errors.push(...argDefault.lint(start + 4 + String(argName).length));
|
|
114
|
-
}
|
|
115
|
-
if (rest.length > 0) {
|
|
116
|
-
const rect = this.getRootNode().posFromIndex(start);
|
|
117
|
-
errors.push(...rest.map(child => generateForChild(child, rect, '三重括号内的不可见部分')));
|
|
118
|
-
}
|
|
119
|
-
return errors;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
127
|
/**
|
|
123
128
|
* 移除子节点,且在移除`arg-default`子节点时自动移除全部多余子节点
|
|
124
129
|
* @param {number} i 移除位置
|
|
@@ -139,7 +144,7 @@ class ArgToken extends Token {
|
|
|
139
144
|
*/
|
|
140
145
|
insertAt(token, i = this.childNodes.length) {
|
|
141
146
|
const j = i < 0 ? i + this.childNodes.length : i;
|
|
142
|
-
if (j > 1
|
|
147
|
+
if (j > 1) {
|
|
143
148
|
throw new RangeError(`${this.constructor.name} 不可插入多余的子节点!`);
|
|
144
149
|
}
|
|
145
150
|
super.insertAt(token, i);
|
|
@@ -157,7 +162,7 @@ class ArgToken extends Token {
|
|
|
157
162
|
setName(name) {
|
|
158
163
|
name = String(name);
|
|
159
164
|
const root = Parser.parse(`{{{${name}}}}`, this.getAttribute('include'), 2, this.getAttribute('config')),
|
|
160
|
-
{
|
|
165
|
+
{length, firstChild: arg} = root;
|
|
161
166
|
if (length !== 1 || arg.type !== 'arg' || arg.childNodes.length !== 1) {
|
|
162
167
|
throw new SyntaxError(`非法的参数名称:${noWrap(name)}`);
|
|
163
168
|
}
|
|
@@ -174,7 +179,7 @@ class ArgToken extends Token {
|
|
|
174
179
|
setDefault(value) {
|
|
175
180
|
value = String(value);
|
|
176
181
|
const root = Parser.parse(`{{{|${value}}}}`, this.getAttribute('include'), 2, this.getAttribute('config')),
|
|
177
|
-
{
|
|
182
|
+
{length, firstChild: arg} = root;
|
|
178
183
|
if (length !== 1 || arg.type !== 'arg' || arg.childNodes.length !== 2) {
|
|
179
184
|
throw new SyntaxError(`非法的参数预设值:${noWrap(value)}`);
|
|
180
185
|
}
|
|
@@ -184,7 +189,7 @@ class ArgToken extends Token {
|
|
|
184
189
|
if (oldDefault) {
|
|
185
190
|
oldDefault.safeReplaceWith(lastChild);
|
|
186
191
|
} else {
|
|
187
|
-
this.
|
|
192
|
+
this.insertAt(lastChild);
|
|
188
193
|
}
|
|
189
194
|
}
|
|
190
195
|
}
|
package/src/atom/index.js
CHANGED
|
@@ -5,7 +5,7 @@ const Parser = require('../..'),
|
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* 不会被继续解析的plain Token
|
|
8
|
-
* @classdesc `{childNodes:
|
|
8
|
+
* @classdesc `{childNodes: ...AstText|Token}`
|
|
9
9
|
*/
|
|
10
10
|
class AtomToken extends Token {
|
|
11
11
|
type = 'plain';
|
|
@@ -30,10 +30,12 @@ class AtomToken extends Token {
|
|
|
30
30
|
cloneNode() {
|
|
31
31
|
const cloned = this.cloneChildNodes(),
|
|
32
32
|
config = this.getAttribute('config'),
|
|
33
|
-
acceptable = this.getAttribute('acceptable')
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
acceptable = this.getAttribute('acceptable');
|
|
34
|
+
return Parser.run(() => {
|
|
35
|
+
const token = new this.constructor(undefined, this.type, config, [], acceptable);
|
|
36
|
+
token.append(...cloned);
|
|
37
|
+
return token;
|
|
38
|
+
});
|
|
37
39
|
}
|
|
38
40
|
}
|
|
39
41
|
|