wikiplus-highlight 2.21.0 → 2.22.5
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/.eslintrc.json +466 -22
- package/README.md +2 -2
- package/dist/main.min.js +1 -1
- package/dist/main.min.js.map +1 -1
- package/fold.js +91 -64
- package/i18n/en.json +1 -1
- package/i18n/ka.json +1 -1
- package/i18n/zh-hans.json +1 -1
- package/i18n/zh-hant.json +1 -1
- package/lint.js +92 -0
- package/main.js +220 -149
- package/matchtags.js +66 -38
- package/package.json +5 -1
- package/search.js +25 -15
package/matchtags.js
CHANGED
|
@@ -7,16 +7,17 @@
|
|
|
7
7
|
(() => {
|
|
8
8
|
'use strict';
|
|
9
9
|
|
|
10
|
-
const {Pos, cmpPos} = CodeMirror;
|
|
10
|
+
const {Pos, cmpPos, Init} = CodeMirror;
|
|
11
11
|
|
|
12
|
-
const tagStart = /<(\/?)([
|
|
12
|
+
const tagStart = /<(\/?)([_a-z]\w*)/giu,
|
|
13
13
|
voidTags = ['br', 'wbr', 'hr', 'img'],
|
|
14
14
|
maxScanLines = 1000;
|
|
15
15
|
|
|
16
|
+
/** @ignore */
|
|
16
17
|
class Iter {
|
|
17
18
|
/**
|
|
18
19
|
* @param {CodeMirror.Editor} cm
|
|
19
|
-
* @param {CodeMirror.Position} pos
|
|
20
|
+
* @param {CodeMirror.Position} pos 当前位置
|
|
20
21
|
*/
|
|
21
22
|
constructor(cm, pos) {
|
|
22
23
|
const {line, ch} = pos;
|
|
@@ -28,21 +29,25 @@
|
|
|
28
29
|
this.max = Math.min(line + maxScanLines - 1, cm.lastLine());
|
|
29
30
|
}
|
|
30
31
|
|
|
32
|
+
/** 是否是标签 */
|
|
31
33
|
isTag() {
|
|
32
34
|
const type = this.cm.getTokenTypeAt(Pos(this.line, this.ch));
|
|
33
|
-
return /\b(?:mw-(?:html|ext)tag|tag\b)
|
|
35
|
+
return /\b(?:mw-(?:html|ext)tag|tag\b)/u.test(type);
|
|
34
36
|
}
|
|
35
37
|
|
|
36
|
-
/**
|
|
38
|
+
/**
|
|
39
|
+
* 判断是否是括号
|
|
40
|
+
* @param {number} ch 列号
|
|
41
|
+
*/
|
|
37
42
|
bracketAt(ch) {
|
|
38
43
|
const type = this.cm.getTokenTypeAt(Pos(this.line, ch + 1));
|
|
39
|
-
return /\b(?:mw-(?:html|ext)tag-)?bracket\b
|
|
44
|
+
return /\b(?:mw-(?:html|ext)tag-)?bracket\b/u.test(type);
|
|
40
45
|
}
|
|
41
46
|
|
|
42
47
|
/** Jump to the start of the next line */
|
|
43
48
|
nextLine() {
|
|
44
49
|
if (this.line >= this.max) {
|
|
45
|
-
return;
|
|
50
|
+
return false;
|
|
46
51
|
}
|
|
47
52
|
this.ch = 0;
|
|
48
53
|
this.text = this.cm.getLine(++this.line);
|
|
@@ -52,7 +57,7 @@
|
|
|
52
57
|
/** Jump to the end of the previous line */
|
|
53
58
|
prevLine() {
|
|
54
59
|
if (this.line <= this.min) {
|
|
55
|
-
return;
|
|
60
|
+
return false;
|
|
56
61
|
}
|
|
57
62
|
this.text = this.cm.getLine(--this.line);
|
|
58
63
|
this.ch = this.text.length;
|
|
@@ -64,7 +69,7 @@
|
|
|
64
69
|
for (;;) {
|
|
65
70
|
const gt = this.text.indexOf('>', this.ch);
|
|
66
71
|
if (gt === -1) {
|
|
67
|
-
return;
|
|
72
|
+
return undefined;
|
|
68
73
|
}
|
|
69
74
|
this.ch = gt + 1;
|
|
70
75
|
if (!this.bracketAt(gt)) {
|
|
@@ -79,7 +84,7 @@
|
|
|
79
84
|
for (;;) {
|
|
80
85
|
const lt = this.ch ? this.text.lastIndexOf('<', this.ch - 1) : -1;
|
|
81
86
|
if (lt === -1) {
|
|
82
|
-
return;
|
|
87
|
+
return undefined;
|
|
83
88
|
}
|
|
84
89
|
if (!this.bracketAt(lt)) {
|
|
85
90
|
this.ch = lt;
|
|
@@ -103,7 +108,7 @@
|
|
|
103
108
|
if (this.nextLine()) {
|
|
104
109
|
continue;
|
|
105
110
|
} else {
|
|
106
|
-
return;
|
|
111
|
+
return undefined;
|
|
107
112
|
}
|
|
108
113
|
}
|
|
109
114
|
if (!this.bracketAt(found.index)) {
|
|
@@ -123,7 +128,7 @@
|
|
|
123
128
|
if (this.prevLine()) {
|
|
124
129
|
continue;
|
|
125
130
|
} else {
|
|
126
|
-
return;
|
|
131
|
+
return undefined;
|
|
127
132
|
}
|
|
128
133
|
}
|
|
129
134
|
if (!this.bracketAt(gt)) {
|
|
@@ -131,14 +136,16 @@
|
|
|
131
136
|
continue;
|
|
132
137
|
}
|
|
133
138
|
const lastSlash = this.text.lastIndexOf('/', gt);
|
|
134
|
-
const selfClose = lastSlash > -1 && !/\S
|
|
139
|
+
const selfClose = lastSlash > -1 && !/\S/u.test(this.text.slice(lastSlash + 1, gt));
|
|
135
140
|
this.ch = gt + 1;
|
|
136
141
|
return selfClose ? 'selfClose' : 'regular';
|
|
137
142
|
}
|
|
138
143
|
}
|
|
139
144
|
|
|
145
|
+
// eslint-disable-next-line jsdoc/require-returns-check
|
|
140
146
|
/**
|
|
141
|
-
*
|
|
147
|
+
* 搜索匹配的闭合标签
|
|
148
|
+
* @param {string} tag 标签名
|
|
142
149
|
* @returns {CodeMirror.MatchingTag}
|
|
143
150
|
*/
|
|
144
151
|
findMatchingClose(tag) {
|
|
@@ -146,13 +153,13 @@
|
|
|
146
153
|
for (;;) {
|
|
147
154
|
const next = this.toNextTag();
|
|
148
155
|
if (!next) {
|
|
149
|
-
return;
|
|
156
|
+
return undefined;
|
|
150
157
|
}
|
|
151
158
|
const start = this.ch - next[0].length,
|
|
152
159
|
end = this.toTagEnd(),
|
|
153
160
|
tagName = next[2].toLowerCase();
|
|
154
161
|
if (!end) {
|
|
155
|
-
return;
|
|
162
|
+
return undefined;
|
|
156
163
|
}
|
|
157
164
|
if (end === 'selfClose' || voidTags.includes(tagName)) {
|
|
158
165
|
continue;
|
|
@@ -174,8 +181,10 @@
|
|
|
174
181
|
}
|
|
175
182
|
}
|
|
176
183
|
|
|
184
|
+
// eslint-disable-next-line jsdoc/require-returns-check
|
|
177
185
|
/**
|
|
178
|
-
*
|
|
186
|
+
* 搜索匹配的开启标签
|
|
187
|
+
* @param {string|undefined} tag 标签名
|
|
179
188
|
* @returns {CodeMirror.MatchingTag}
|
|
180
189
|
*/
|
|
181
190
|
findMatchingOpen(tag) {
|
|
@@ -183,12 +192,12 @@
|
|
|
183
192
|
for (;;) {
|
|
184
193
|
const prev = this.toPrevTag();
|
|
185
194
|
if (!prev) {
|
|
186
|
-
return;
|
|
195
|
+
return undefined;
|
|
187
196
|
}
|
|
188
|
-
const end = this
|
|
197
|
+
const {ch: end} = this,
|
|
189
198
|
start = this.toTagStart();
|
|
190
199
|
if (!start) {
|
|
191
|
-
return;
|
|
200
|
+
return undefined;
|
|
192
201
|
}
|
|
193
202
|
const tagName = start[2].toLowerCase();
|
|
194
203
|
if (prev === 'selfClose' || voidTags.includes(tagName)) {
|
|
@@ -205,6 +214,7 @@
|
|
|
205
214
|
}
|
|
206
215
|
}
|
|
207
216
|
if (i < 0 && (!tag || tag === tagName)) {
|
|
217
|
+
// eslint-disable-next-line unicorn/consistent-destructuring
|
|
208
218
|
return {tag: tagName, from: Pos(this.line, this.ch), to: Pos(this.line, end)};
|
|
209
219
|
}
|
|
210
220
|
}
|
|
@@ -214,17 +224,22 @@
|
|
|
214
224
|
|
|
215
225
|
CodeMirror.defineExtension(
|
|
216
226
|
'findMatchingTag',
|
|
217
|
-
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* @this {CodeMirror.Editor}
|
|
230
|
+
* @param {CodeMirror.Position} pos 当前位置
|
|
231
|
+
* @returns {CodeMirror.MatchingTagPair}
|
|
232
|
+
*/
|
|
218
233
|
function(pos) {
|
|
219
234
|
let iter = new Iter(this, pos);
|
|
220
235
|
if (!iter.isTag()) {
|
|
221
|
-
return;
|
|
236
|
+
return undefined;
|
|
222
237
|
}
|
|
223
238
|
const end = iter.toTagEnd(),
|
|
224
239
|
to = end && Pos(iter.line, iter.ch);
|
|
225
240
|
const start = end && iter.toTagStart();
|
|
226
241
|
if (!start || cmpPos(iter, pos) > 0) {
|
|
227
|
-
return;
|
|
242
|
+
return undefined;
|
|
228
243
|
}
|
|
229
244
|
const tag = start[2].toLowerCase(),
|
|
230
245
|
here = {from: Pos(iter.line, iter.ch), to, tag};
|
|
@@ -242,29 +257,36 @@
|
|
|
242
257
|
|
|
243
258
|
CodeMirror.defineExtension(
|
|
244
259
|
'findEnclosingTag',
|
|
245
|
-
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* @this {CodeMirror.Editor}
|
|
263
|
+
* @param {CodeMirror.Position} pos
|
|
264
|
+
* @param {string} tag
|
|
265
|
+
* @returns {CodeMirror.MatchingTagPair}
|
|
266
|
+
*/
|
|
246
267
|
function(pos, tag) {
|
|
247
|
-
const iter = new Iter(this, pos)
|
|
248
|
-
|
|
268
|
+
const iter = new Iter(this, pos),
|
|
269
|
+
open = iter.findMatchingOpen(tag);
|
|
249
270
|
if (!open) {
|
|
250
|
-
return;
|
|
271
|
+
return undefined;
|
|
251
272
|
}
|
|
252
|
-
const forward = new Iter(this, pos)
|
|
253
|
-
|
|
273
|
+
const forward = new Iter(this, pos),
|
|
274
|
+
close = forward.findMatchingClose(open.tag);
|
|
254
275
|
if (close) {
|
|
255
276
|
return {open, close};
|
|
256
277
|
}
|
|
278
|
+
return undefined;
|
|
257
279
|
},
|
|
258
280
|
);
|
|
259
281
|
|
|
260
282
|
/** Used by addon/edit/closetag.js */
|
|
261
|
-
CodeMirror.scanForClosingTag =
|
|
283
|
+
CodeMirror.scanForClosingTag = (cm, pos, tagName) => {
|
|
262
284
|
const iter = new Iter(cm, pos);
|
|
263
|
-
return iter.findMatchingClose(
|
|
285
|
+
return iter.findMatchingClose(tagName);
|
|
264
286
|
};
|
|
265
287
|
|
|
266
288
|
CodeMirror.defineOption('matchTags', false, (cm, val, old) => {
|
|
267
|
-
if (old && old !==
|
|
289
|
+
if (old && old !== Init) {
|
|
268
290
|
cm.off('cursorActivity', doMatchTags);
|
|
269
291
|
clear(cm);
|
|
270
292
|
}
|
|
@@ -274,8 +296,11 @@
|
|
|
274
296
|
}
|
|
275
297
|
});
|
|
276
298
|
|
|
277
|
-
/**
|
|
278
|
-
|
|
299
|
+
/**
|
|
300
|
+
* 清除高亮
|
|
301
|
+
* @param {CodeMirror.EditorWithMatchingTags} cm
|
|
302
|
+
*/
|
|
303
|
+
const clear = cm => {
|
|
279
304
|
if (cm.state.tagHit) {
|
|
280
305
|
cm.state.tagHit.clear();
|
|
281
306
|
}
|
|
@@ -284,10 +309,13 @@
|
|
|
284
309
|
}
|
|
285
310
|
cm.state.tagHit = null;
|
|
286
311
|
cm.state.tagOther = null;
|
|
287
|
-
}
|
|
312
|
+
};
|
|
288
313
|
|
|
289
|
-
/**
|
|
290
|
-
|
|
314
|
+
/**
|
|
315
|
+
* 搜索并高亮匹配的标签
|
|
316
|
+
* @param {CodeMirror.EditorWithMatchingTags} cm
|
|
317
|
+
*/
|
|
318
|
+
const doMatchTags = cm => {
|
|
291
319
|
cm.operation(() => {
|
|
292
320
|
clear(cm);
|
|
293
321
|
if (cm.somethingSelected()) {
|
|
@@ -310,7 +338,7 @@
|
|
|
310
338
|
cm.state.tagOther = cm.markText(other.from, other.to, {className: 'cm-matchingtag'});
|
|
311
339
|
}
|
|
312
340
|
});
|
|
313
|
-
}
|
|
341
|
+
};
|
|
314
342
|
|
|
315
343
|
mw.loader.addStyleTag(
|
|
316
344
|
'.cm-matchingtag{background-color:#c9ffc8}'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wikiplus-highlight",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.22.5",
|
|
4
4
|
"description": "A plugin for the MediaWiki front-end add-on \"Wikiplus\"",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mediawiki",
|
|
@@ -24,6 +24,10 @@
|
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"eslint": "^8.8.0",
|
|
27
|
+
"eslint-plugin-promise": "^6.1.1",
|
|
28
|
+
"eslint-plugin-es-x": "^5.4.0",
|
|
29
|
+
"eslint-plugin-regexp": "^1.11.0",
|
|
30
|
+
"eslint-plugin-jsdoc": "^1.0.0",
|
|
27
31
|
"http-server": "^14.1.0",
|
|
28
32
|
"types-mediawiki": "^1.2.0",
|
|
29
33
|
"uglify-js": "^3.15.5"
|
package/search.js
CHANGED
|
@@ -6,7 +6,10 @@
|
|
|
6
6
|
(() => {
|
|
7
7
|
'use strict';
|
|
8
8
|
|
|
9
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* I18N消息
|
|
11
|
+
* @param {string} key 消息键
|
|
12
|
+
*/
|
|
10
13
|
const msg = key => mw.msg(`wphl-${key}`);
|
|
11
14
|
|
|
12
15
|
// Prepare elements
|
|
@@ -21,15 +24,20 @@
|
|
|
21
24
|
$searchBtn = $('<span>', {class: 'Wikiplus-Btn', html: msg('addon-search')});
|
|
22
25
|
|
|
23
26
|
const escapeRegExp = mw.util.escapeRegExp || mw.RegExp.escape;
|
|
24
|
-
const /** @type {CodeMirror.Mode<undefined>} */ overlay = {token: () => {}};
|
|
27
|
+
const /** @type {CodeMirror.Mode<undefined>} */ overlay = {token: /** @override */ () => {}};
|
|
25
28
|
|
|
26
29
|
/**
|
|
27
30
|
* 根据搜索字符串生成高亮
|
|
28
|
-
* @param {string|RegExp} str
|
|
31
|
+
* @param {string|RegExp} str 搜索字符串
|
|
29
32
|
*/
|
|
30
33
|
const token = str => {
|
|
31
|
-
const initial = typeof str === 'string' ? RegExp(`[^${escapeRegExp(str[0])}]`, '
|
|
32
|
-
|
|
34
|
+
const initial = typeof str === 'string' ? new RegExp(`[^${escapeRegExp(str[0])}]`, 'iu') : null;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @override
|
|
38
|
+
* @param {CodeMirror.StringStream} stream
|
|
39
|
+
*/
|
|
40
|
+
return stream => {
|
|
33
41
|
if (stream.match(str, true, true)) {
|
|
34
42
|
return 'search';
|
|
35
43
|
}
|
|
@@ -37,6 +45,7 @@
|
|
|
37
45
|
if (initial) {
|
|
38
46
|
stream.eatWhile(initial);
|
|
39
47
|
}
|
|
48
|
+
return undefined;
|
|
40
49
|
};
|
|
41
50
|
};
|
|
42
51
|
|
|
@@ -50,7 +59,7 @@
|
|
|
50
59
|
/**
|
|
51
60
|
* keyboard event handler of `$search`
|
|
52
61
|
* @param {CodeMirror.Editor} cm
|
|
53
|
-
* @param {boolean} dir
|
|
62
|
+
* @param {boolean} dir 搜索方向
|
|
54
63
|
*/
|
|
55
64
|
const findNext = (cm, dir) => {
|
|
56
65
|
let /** @type {string|RegExp} */ ptn = $search.val();
|
|
@@ -58,10 +67,10 @@
|
|
|
58
67
|
return;
|
|
59
68
|
}
|
|
60
69
|
|
|
61
|
-
if (typeof ptn === 'string' && /^\/.+\/i
|
|
70
|
+
if (typeof ptn === 'string' && /^\/.+\/i?$/u.test(ptn)) {
|
|
62
71
|
ptn = ptn.endsWith('i')
|
|
63
|
-
? RegExp(ptn.slice(1, -2), '
|
|
64
|
-
: RegExp(ptn.slice(1, -1));
|
|
72
|
+
? new RegExp(ptn.slice(1, -2), 'iu')
|
|
73
|
+
: new RegExp(ptn.slice(1, -1), 'u');
|
|
65
74
|
}
|
|
66
75
|
if (ptn !== lastPtn) {
|
|
67
76
|
cm.removeOverlay(overlay);
|
|
@@ -76,7 +85,7 @@
|
|
|
76
85
|
cursor = cm.getSearchCursor(ptn, {line: 0, ch: 0}, {caseFold: true});
|
|
77
86
|
} else {
|
|
78
87
|
const lastLine = cm.lastLine(),
|
|
79
|
-
lastCh = cm.getLine(lastLine)
|
|
88
|
+
{length: lastCh} = cm.getLine(lastLine);
|
|
80
89
|
cursor = cm.getSearchCursor(ptn, {line: lastLine, ch: lastCh}, {caseFold: true});
|
|
81
90
|
}
|
|
82
91
|
result = dir ? cursor.findNext() : cursor.findPrevious();
|
|
@@ -109,10 +118,10 @@
|
|
|
109
118
|
lastPtn = '';
|
|
110
119
|
};
|
|
111
120
|
|
|
112
|
-
CodeMirror.commands.findForward = doc => {
|
|
121
|
+
CodeMirror.commands.findForward = /** 向后搜索 */ doc => {
|
|
113
122
|
findNext(doc, true);
|
|
114
123
|
};
|
|
115
|
-
CodeMirror.commands.findBackward = doc => {
|
|
124
|
+
CodeMirror.commands.findBackward = /** 向前搜索 */ doc => {
|
|
116
125
|
findNext(doc, false);
|
|
117
126
|
};
|
|
118
127
|
|
|
@@ -144,9 +153,10 @@
|
|
|
144
153
|
reset(cm);
|
|
145
154
|
}
|
|
146
155
|
});
|
|
147
|
-
cm.addKeyMap(
|
|
148
|
-
|
|
149
|
-
|
|
156
|
+
cm.addKeyMap(
|
|
157
|
+
CodeMirror.keyMap.default === CodeMirror.keyMap.pcDefault
|
|
158
|
+
? {'Ctrl-F': findNew, 'Ctrl-G': 'findForward', 'Shift-Ctrl-G': 'findBackward'}
|
|
159
|
+
: {'Cmd-F': findNew, 'Cmd-G': 'findForward', 'Shift-Cmd-G': 'findBackward'},
|
|
150
160
|
);
|
|
151
161
|
});
|
|
152
162
|
|