@bhsd/codemirror-mediawiki 2.1.7 → 2.1.8
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/README.md +17 -0
- package/dist/codemirror.d.ts +5 -0
- package/dist/main.min.js +12 -12
- package/dist/main.min.js.map +3 -3
- package/mw/dist/base.js +58 -47
- package/package.json +1 -1
- package/src/codemirror.ts +46 -13
package/mw/dist/base.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { CodeMirror6 } from 'https://testingcf.jsdelivr.net/npm/@bhsd/codemirror-mediawiki@2.1.
|
|
1
|
+
import { CodeMirror6 } from 'https://testingcf.jsdelivr.net/npm/@bhsd/codemirror-mediawiki@2.1.8/dist/main.min.js';
|
|
2
2
|
(() => {
|
|
3
3
|
var _a;
|
|
4
|
-
mw.loader.load('https://testingcf.jsdelivr.net/npm/@bhsd/codemirror-mediawiki@2.1.
|
|
4
|
+
mw.loader.load('https://testingcf.jsdelivr.net/npm/@bhsd/codemirror-mediawiki@2.1.8/mediawiki.min.css', 'text/css');
|
|
5
5
|
const instances = new WeakMap();
|
|
6
|
+
const getInstance = ($ele) => instances.get($ele[0]);
|
|
6
7
|
$.valHooks['textarea'] = {
|
|
7
8
|
get(elem) {
|
|
8
9
|
const cm = instances.get(elem);
|
|
@@ -11,9 +12,7 @@ import { CodeMirror6 } from 'https://testingcf.jsdelivr.net/npm/@bhsd/codemirror
|
|
|
11
12
|
set(elem, value) {
|
|
12
13
|
const cm = instances.get(elem);
|
|
13
14
|
if (cm === null || cm === void 0 ? void 0 : cm.visible) {
|
|
14
|
-
cm.
|
|
15
|
-
changes: { from: 0, to: cm.view.state.doc.length, insert: value },
|
|
16
|
-
});
|
|
15
|
+
cm.setContent(value);
|
|
17
16
|
}
|
|
18
17
|
else {
|
|
19
18
|
elem.value = value;
|
|
@@ -21,26 +20,23 @@ import { CodeMirror6 } from 'https://testingcf.jsdelivr.net/npm/@bhsd/codemirror
|
|
|
21
20
|
},
|
|
22
21
|
};
|
|
23
22
|
function getCaretPosition(option) {
|
|
24
|
-
const { view: { state: { selection: { main } } } } =
|
|
23
|
+
const { view: { state: { selection: { main } } } } = getInstance(this);
|
|
25
24
|
return (option === null || option === void 0 ? void 0 : option.startAndEnd) ? [main.from, main.to] : main.head;
|
|
26
25
|
}
|
|
27
26
|
const textSelection = {
|
|
28
27
|
getContents() {
|
|
29
|
-
return
|
|
28
|
+
return getInstance(this).view.state.doc.toString();
|
|
30
29
|
},
|
|
31
30
|
setContents(content) {
|
|
32
|
-
|
|
33
|
-
view.dispatch({
|
|
34
|
-
changes: { from: 0, to: view.state.doc.length, insert: content },
|
|
35
|
-
});
|
|
31
|
+
getInstance(this).setContent(content);
|
|
36
32
|
return this;
|
|
37
33
|
},
|
|
38
34
|
getSelection() {
|
|
39
|
-
const { view: { state } } =
|
|
35
|
+
const { view: { state } } = getInstance(this);
|
|
40
36
|
return state.sliceDoc(state.selection.main.from, state.selection.main.to);
|
|
41
37
|
},
|
|
42
38
|
setSelection({ start, end }) {
|
|
43
|
-
const { view } =
|
|
39
|
+
const { view } = getInstance(this);
|
|
44
40
|
view.dispatch({
|
|
45
41
|
selection: { anchor: start, head: end !== null && end !== void 0 ? end : start },
|
|
46
42
|
});
|
|
@@ -48,36 +44,44 @@ import { CodeMirror6 } from 'https://testingcf.jsdelivr.net/npm/@bhsd/codemirror
|
|
|
48
44
|
return this;
|
|
49
45
|
},
|
|
50
46
|
replaceSelection(value) {
|
|
51
|
-
const { view } =
|
|
47
|
+
const { view } = getInstance(this);
|
|
52
48
|
view.dispatch(view.state.replaceSelection(value));
|
|
53
49
|
return this;
|
|
54
50
|
},
|
|
55
51
|
getCaretPosition,
|
|
56
52
|
scrollToCaretPosition() {
|
|
57
|
-
|
|
53
|
+
getInstance(this).view.dispatch({ scrollIntoView: true });
|
|
58
54
|
return this;
|
|
59
55
|
},
|
|
60
56
|
};
|
|
61
|
-
const USING_LOCAL = mw.loader.getState('ext.CodeMirror') !== null, DATA_MODULE = mw.loader.getState('ext.CodeMirror.data') ? 'ext.CodeMirror.data' : 'ext.CodeMirror', ALL_SETTINGS_CACHE = (_a = JSON.parse(localStorage.getItem('InPageEditMwConfig'))) !== null && _a !== void 0 ? _a : {}, SITE_ID = `${mw.config.get('wgServerName')}${mw.config.get('wgScriptPath')}`, SITE_SETTINGS = ALL_SETTINGS_CACHE[SITE_ID],
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
57
|
+
const USING_LOCAL = mw.loader.getState('ext.CodeMirror') !== null, DATA_MODULE = mw.loader.getState('ext.CodeMirror.data') ? 'ext.CodeMirror.data' : 'ext.CodeMirror', ALL_SETTINGS_CACHE = (_a = JSON.parse(localStorage.getItem('InPageEditMwConfig'))) !== null && _a !== void 0 ? _a : {}, SITE_ID = `${mw.config.get('wgServerName')}${mw.config.get('wgScriptPath')}`, SITE_SETTINGS = ALL_SETTINGS_CACHE[SITE_ID], VALID = (SITE_SETTINGS === null || SITE_SETTINGS === void 0 ? void 0 : SITE_SETTINGS.time) > Date.now() - 86400 * 1000 * 30;
|
|
58
|
+
const getConfig = (magicWords, rule, flip) => {
|
|
59
|
+
const words = magicWords.filter(rule).filter(({ 'case-sensitive': i }) => i !== flip)
|
|
60
|
+
.flatMap(({ aliases, name, 'case-sensitive': i }) => aliases.map(alias => ({
|
|
61
|
+
alias: (i ? alias : alias.toLowerCase()).replace(/:$/u, ''),
|
|
62
|
+
name,
|
|
63
|
+
}))), obj = {};
|
|
64
|
+
for (const { alias, name } of words) {
|
|
65
|
+
obj[alias] = name;
|
|
67
66
|
}
|
|
68
|
-
return
|
|
67
|
+
return obj;
|
|
68
|
+
};
|
|
69
|
+
const getConfigPair = (magicWords, rule) => [true, false]
|
|
70
|
+
.map(bool => getConfig(magicWords, rule, bool));
|
|
71
|
+
const setConfig = (config) => {
|
|
72
|
+
mw.config.set('extCodeMirrorConfig', config);
|
|
69
73
|
};
|
|
70
74
|
const getMwConfig = async () => {
|
|
71
|
-
if (USING_LOCAL &&
|
|
75
|
+
if (USING_LOCAL && !VALID) {
|
|
72
76
|
await mw.loader.using(DATA_MODULE);
|
|
73
77
|
}
|
|
74
78
|
let config = mw.config.get('extCodeMirrorConfig');
|
|
75
|
-
if (!config &&
|
|
79
|
+
if (!config && VALID) {
|
|
76
80
|
({ config } = SITE_SETTINGS);
|
|
77
|
-
|
|
81
|
+
setConfig(config);
|
|
78
82
|
}
|
|
79
83
|
const isIPE = config && Object.values(config.functionSynonyms[0]).includes(true);
|
|
80
|
-
if (config
|
|
84
|
+
if ((config === null || config === void 0 ? void 0 : config.img) && config.variants && !isIPE) {
|
|
81
85
|
return {
|
|
82
86
|
...config,
|
|
83
87
|
nsid: mw.config.get('wgNamespaceIds'),
|
|
@@ -92,20 +96,25 @@ import { CodeMirror6 } from 'https://testingcf.jsdelivr.net/npm/@bhsd/codemirror
|
|
|
92
96
|
],
|
|
93
97
|
formatversion: '2',
|
|
94
98
|
});
|
|
95
|
-
const
|
|
99
|
+
const others = new Set(['msg', 'raw', 'msgnw', 'subst', 'safesubst']);
|
|
96
100
|
if (config && !isIPE) {
|
|
97
101
|
const { functionSynonyms: [insensitive] } = config;
|
|
98
102
|
if (!('subst' in insensitive)) {
|
|
99
|
-
|
|
100
|
-
for (const { alias, name } of aliases) {
|
|
101
|
-
insensitive[alias.replace(/:$/u, '')] = name;
|
|
102
|
-
}
|
|
103
|
+
Object.assign(insensitive, getConfig(magicwords, ({ name }) => others.has(name)));
|
|
103
104
|
}
|
|
104
105
|
}
|
|
105
106
|
else {
|
|
106
107
|
config = {
|
|
107
108
|
tagModes: {
|
|
109
|
+
tab: 'text/mediawiki',
|
|
110
|
+
indicator: 'text/mediawiki',
|
|
111
|
+
poem: 'text/mediawiki',
|
|
108
112
|
ref: 'text/mediawiki',
|
|
113
|
+
option: 'text/mediawiki',
|
|
114
|
+
combooption: 'text/mediawiki',
|
|
115
|
+
tabs: 'text/mediawiki',
|
|
116
|
+
poll: 'text/mediawiki',
|
|
117
|
+
gallery: 'text/mediawiki',
|
|
109
118
|
},
|
|
110
119
|
tags: {},
|
|
111
120
|
urlProtocols: mw.config.get('wgUrlProtocols'),
|
|
@@ -113,24 +122,18 @@ import { CodeMirror6 } from 'https://testingcf.jsdelivr.net/npm/@bhsd/codemirror
|
|
|
113
122
|
for (const tag of extensiontags) {
|
|
114
123
|
config.tags[tag.slice(1, -1)] = true;
|
|
115
124
|
}
|
|
116
|
-
const
|
|
125
|
+
const functions = new Set([
|
|
117
126
|
...functionhooks,
|
|
118
127
|
...variables,
|
|
119
|
-
...
|
|
120
|
-
])
|
|
121
|
-
config.
|
|
122
|
-
|
|
123
|
-
getConfig(sensitive.filter(({ alias }) => /^__.+__$/u.test(alias))),
|
|
124
|
-
];
|
|
125
|
-
config.functionSynonyms = [
|
|
126
|
-
getConfig(insensitive.filter(({ alias }) => !/^__.+__|^#$/u.test(alias))),
|
|
127
|
-
getConfig(sensitive.filter(({ alias }) => !/^__.+__|^#$/u.test(alias))),
|
|
128
|
-
];
|
|
128
|
+
...others,
|
|
129
|
+
]);
|
|
130
|
+
config.functionSynonyms = getConfigPair(magicwords, ({ name }) => functions.has(name));
|
|
131
|
+
config.doubleUnderscore = getConfigPair(magicwords, ({ aliases }) => aliases.some(alias => /^__.+__$/u.test(alias)));
|
|
129
132
|
}
|
|
130
|
-
config.img = getConfig(
|
|
133
|
+
config.img = getConfig(magicwords, ({ name }) => name.startsWith('img_'));
|
|
131
134
|
config.variants = variants ? variants.map(({ code }) => code) : [];
|
|
132
135
|
config.nsid = mw.config.get('wgNamespaceIds');
|
|
133
|
-
|
|
136
|
+
setConfig(config);
|
|
134
137
|
ALL_SETTINGS_CACHE[SITE_ID] = { config: config, time: Date.now() };
|
|
135
138
|
localStorage.setItem('InPageEditMwConfig', JSON.stringify(ALL_SETTINGS_CACHE));
|
|
136
139
|
return config;
|
|
@@ -149,6 +152,11 @@ import { CodeMirror6 } from 'https://testingcf.jsdelivr.net/npm/@bhsd/codemirror
|
|
|
149
152
|
super.toggle(show);
|
|
150
153
|
$(this.textarea).data('jquery.textSelection', show && textSelection);
|
|
151
154
|
}
|
|
155
|
+
async getLinter(opt) {
|
|
156
|
+
const linter = await super.getLinter(opt);
|
|
157
|
+
linters[this.lang] = linter;
|
|
158
|
+
return linter;
|
|
159
|
+
}
|
|
152
160
|
async defaultLint(on, opt) {
|
|
153
161
|
if (!on) {
|
|
154
162
|
this.lint();
|
|
@@ -156,13 +164,13 @@ import { CodeMirror6 } from 'https://testingcf.jsdelivr.net/npm/@bhsd/codemirror
|
|
|
156
164
|
}
|
|
157
165
|
const { lang } = this;
|
|
158
166
|
if (!(lang in linters)) {
|
|
159
|
-
|
|
160
|
-
if (
|
|
167
|
+
await this.getLinter(opt);
|
|
168
|
+
if (lang === 'mediawiki') {
|
|
161
169
|
const mwConfig = await getMwConfig(), config = {
|
|
162
170
|
...await wikiparse.getConfig(),
|
|
163
171
|
ext: Object.keys(mwConfig.tags),
|
|
164
172
|
namespaces: mw.config.get('wgFormattedNamespaces'),
|
|
165
|
-
nsid:
|
|
173
|
+
nsid: mwConfig.nsid,
|
|
166
174
|
doubleUnderscore: mwConfig.doubleUnderscore.map(obj => Object.keys(obj).map(s => s.slice(2, -2))),
|
|
167
175
|
variants: mwConfig.variants,
|
|
168
176
|
protocol: mwConfig.urlProtocols.replace(/\\:/gu, ':'),
|
|
@@ -185,6 +193,9 @@ import { CodeMirror6 } from 'https://testingcf.jsdelivr.net/npm/@bhsd/codemirror
|
|
|
185
193
|
wikiparse.setConfig(config);
|
|
186
194
|
}
|
|
187
195
|
}
|
|
196
|
+
else if (opt) {
|
|
197
|
+
await this.getLinter(opt);
|
|
198
|
+
}
|
|
188
199
|
if (linters[lang]) {
|
|
189
200
|
this.lint(linters[lang]);
|
|
190
201
|
}
|
package/package.json
CHANGED
package/src/codemirror.ts
CHANGED
|
@@ -70,6 +70,14 @@ const loadScript = (src: string, target: string): Promise<void> => new Promise(r
|
|
|
70
70
|
document.head.append(script);
|
|
71
71
|
});
|
|
72
72
|
|
|
73
|
+
/**
|
|
74
|
+
* 获取指定行列的位置
|
|
75
|
+
* @param doc 文档
|
|
76
|
+
* @param line 行号
|
|
77
|
+
* @param column 列号
|
|
78
|
+
*/
|
|
79
|
+
const pos = (doc: Text, line: number, column: number): number => doc.line(line).from + column - 1;
|
|
80
|
+
|
|
73
81
|
export class CodeMirror6 {
|
|
74
82
|
readonly #textarea;
|
|
75
83
|
readonly #language;
|
|
@@ -143,12 +151,12 @@ export class CodeMirror6 {
|
|
|
143
151
|
extensions,
|
|
144
152
|
doc: textarea.value,
|
|
145
153
|
});
|
|
146
|
-
const {
|
|
154
|
+
const {selectionStart, selectionEnd, scrollTop} = textarea,
|
|
147
155
|
{fontSize, lineHeight} = getComputedStyle(textarea),
|
|
148
156
|
hasFocus = document.activeElement === textarea;
|
|
149
157
|
textarea.parentNode!.insertBefore(this.#view.dom, textarea);
|
|
150
|
-
this.#
|
|
151
|
-
this.#
|
|
158
|
+
this.#minHeight();
|
|
159
|
+
this.#refresh();
|
|
152
160
|
this.#view.dom.style.fontSize = fontSize;
|
|
153
161
|
this.#view.scrollDOM.style.lineHeight = lineHeight;
|
|
154
162
|
this.#view.requestMeasure();
|
|
@@ -164,6 +172,20 @@ export class CodeMirror6 {
|
|
|
164
172
|
});
|
|
165
173
|
}
|
|
166
174
|
|
|
175
|
+
/** 刷新编辑器高度 */
|
|
176
|
+
#refresh(): void {
|
|
177
|
+
const {offsetHeight} = this.#textarea;
|
|
178
|
+
this.#view.dom.style.height = offsetHeight ? `${offsetHeight}px` : this.#textarea.style.height;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* 设置编辑器最小高度
|
|
183
|
+
* @param linting 是否启用语法检查
|
|
184
|
+
*/
|
|
185
|
+
#minHeight(linting?: boolean): void {
|
|
186
|
+
this.#view.dom.style.minHeight = linting ? 'calc(100px + 2em)' : '2em';
|
|
187
|
+
}
|
|
188
|
+
|
|
167
189
|
/**
|
|
168
190
|
* 设置语言
|
|
169
191
|
* @param lang 语言
|
|
@@ -193,8 +215,10 @@ export class CodeMirror6 {
|
|
|
193
215
|
: [];
|
|
194
216
|
if (lintSource) {
|
|
195
217
|
linters[this.#lang] = linterExtension;
|
|
218
|
+
this.#minHeight(true);
|
|
196
219
|
} else {
|
|
197
220
|
delete linters[this.#lang];
|
|
221
|
+
this.#minHeight();
|
|
198
222
|
}
|
|
199
223
|
this.#view.dispatch({
|
|
200
224
|
effects: [this.#linter.reconfigure(linterExtension)],
|
|
@@ -244,8 +268,8 @@ export class CodeMirror6 {
|
|
|
244
268
|
async getLinter(opt?: Record<string, unknown>): Promise<LintSource | undefined> {
|
|
245
269
|
switch (this.#lang) {
|
|
246
270
|
case 'mediawiki': {
|
|
247
|
-
const
|
|
248
|
-
|
|
271
|
+
const CDN = 'npm/wikiparser-node@1.3.4-b/extensions/dist',
|
|
272
|
+
src = `combine/${CDN}/base.min.js,${CDN}/lint.min.js`;
|
|
249
273
|
await loadScript(src, 'wikiparse');
|
|
250
274
|
const wikiLinter = new wikiparse.Linter(opt?.['include'] as boolean);
|
|
251
275
|
return doc => wikiLinter.codemirror(doc.toString());
|
|
@@ -267,18 +291,18 @@ export class CodeMirror6 {
|
|
|
267
291
|
...opt,
|
|
268
292
|
};
|
|
269
293
|
for (const [name, {meta}] of esLinter.getRules()) {
|
|
270
|
-
if (meta?.docs
|
|
294
|
+
if (meta?.docs?.recommended) {
|
|
271
295
|
conf.rules![name] ??= 2;
|
|
272
296
|
}
|
|
273
297
|
}
|
|
274
298
|
return doc => esLinter.verify(doc.toString(), conf)
|
|
275
299
|
.map(({message, severity, line, column, endLine, endColumn}) => {
|
|
276
|
-
const from = doc
|
|
300
|
+
const from = pos(doc, line, column);
|
|
277
301
|
return {
|
|
278
302
|
message,
|
|
279
303
|
severity: severity === 1 ? 'warning' : 'error',
|
|
280
304
|
from,
|
|
281
|
-
to: endLine === undefined ? from + 1 : doc
|
|
305
|
+
to: endLine === undefined ? from + 1 : pos(doc, endLine, endColumn!),
|
|
282
306
|
};
|
|
283
307
|
});
|
|
284
308
|
}
|
|
@@ -339,8 +363,8 @@ export class CodeMirror6 {
|
|
|
339
363
|
.map(({text, severity, line, column, endLine, endColumn}) => ({
|
|
340
364
|
message: text,
|
|
341
365
|
severity,
|
|
342
|
-
from: doc
|
|
343
|
-
to: endLine === undefined ? doc.line(line).to : doc
|
|
366
|
+
from: pos(doc, line, column),
|
|
367
|
+
to: endLine === undefined ? doc.line(line).to : pos(doc, endLine, endColumn!),
|
|
344
368
|
}));
|
|
345
369
|
};
|
|
346
370
|
}
|
|
@@ -369,15 +393,24 @@ export class CodeMirror6 {
|
|
|
369
393
|
}
|
|
370
394
|
}
|
|
371
395
|
|
|
396
|
+
/**
|
|
397
|
+
* 重设编辑器内容
|
|
398
|
+
* @param content 新内容
|
|
399
|
+
*/
|
|
400
|
+
setContent(content: string): void {
|
|
401
|
+
this.#view.dispatch({
|
|
402
|
+
changes: {from: 0, to: this.#view.state.doc.length, insert: content},
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
|
|
372
406
|
/**
|
|
373
407
|
* 在编辑器和文本框之间切换
|
|
374
408
|
* @param show 是否显示编辑器
|
|
375
409
|
*/
|
|
376
410
|
toggle(show = !this.#visible): void {
|
|
377
411
|
if (show && !this.#visible) {
|
|
378
|
-
this.#
|
|
379
|
-
|
|
380
|
-
});
|
|
412
|
+
this.setContent(this.#textarea.value);
|
|
413
|
+
this.#refresh();
|
|
381
414
|
}
|
|
382
415
|
this.#visible = show;
|
|
383
416
|
this.#view.dom.style.setProperty('display', show ? '' : 'none', 'important');
|