@bhsd/codemirror-mediawiki 2.1.7 → 2.1.9
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 +76 -59
- package/package.json +2 -3
- package/src/codemirror.ts +71 -25
- package/src/mediawiki.ts +2 -2
package/mw/dist/base.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
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.9/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.9/mediawiki.min.css', 'text/css');
|
|
5
|
+
mw.loader.addStyleTag('.wikiEditor-ui-toolbar{z-index:7}');
|
|
5
6
|
const instances = new WeakMap();
|
|
7
|
+
const getInstance = ($ele) => instances.get($ele[0]);
|
|
6
8
|
$.valHooks['textarea'] = {
|
|
7
9
|
get(elem) {
|
|
8
10
|
const cm = instances.get(elem);
|
|
@@ -11,9 +13,7 @@ import { CodeMirror6 } from 'https://testingcf.jsdelivr.net/npm/@bhsd/codemirror
|
|
|
11
13
|
set(elem, value) {
|
|
12
14
|
const cm = instances.get(elem);
|
|
13
15
|
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
|
-
});
|
|
16
|
+
cm.setContent(value);
|
|
17
17
|
}
|
|
18
18
|
else {
|
|
19
19
|
elem.value = value;
|
|
@@ -21,26 +21,23 @@ import { CodeMirror6 } from 'https://testingcf.jsdelivr.net/npm/@bhsd/codemirror
|
|
|
21
21
|
},
|
|
22
22
|
};
|
|
23
23
|
function getCaretPosition(option) {
|
|
24
|
-
const { view: { state: { selection: { main } } } } =
|
|
24
|
+
const { view: { state: { selection: { main } } } } = getInstance(this);
|
|
25
25
|
return (option === null || option === void 0 ? void 0 : option.startAndEnd) ? [main.from, main.to] : main.head;
|
|
26
26
|
}
|
|
27
27
|
const textSelection = {
|
|
28
28
|
getContents() {
|
|
29
|
-
return
|
|
29
|
+
return getInstance(this).view.state.doc.toString();
|
|
30
30
|
},
|
|
31
31
|
setContents(content) {
|
|
32
|
-
|
|
33
|
-
view.dispatch({
|
|
34
|
-
changes: { from: 0, to: view.state.doc.length, insert: content },
|
|
35
|
-
});
|
|
32
|
+
getInstance(this).setContent(content);
|
|
36
33
|
return this;
|
|
37
34
|
},
|
|
38
35
|
getSelection() {
|
|
39
|
-
const { view: { state } } =
|
|
36
|
+
const { view: { state } } = getInstance(this);
|
|
40
37
|
return state.sliceDoc(state.selection.main.from, state.selection.main.to);
|
|
41
38
|
},
|
|
42
39
|
setSelection({ start, end }) {
|
|
43
|
-
const { view } =
|
|
40
|
+
const { view } = getInstance(this);
|
|
44
41
|
view.dispatch({
|
|
45
42
|
selection: { anchor: start, head: end !== null && end !== void 0 ? end : start },
|
|
46
43
|
});
|
|
@@ -48,36 +45,44 @@ import { CodeMirror6 } from 'https://testingcf.jsdelivr.net/npm/@bhsd/codemirror
|
|
|
48
45
|
return this;
|
|
49
46
|
},
|
|
50
47
|
replaceSelection(value) {
|
|
51
|
-
const { view } =
|
|
48
|
+
const { view } = getInstance(this);
|
|
52
49
|
view.dispatch(view.state.replaceSelection(value));
|
|
53
50
|
return this;
|
|
54
51
|
},
|
|
55
52
|
getCaretPosition,
|
|
56
53
|
scrollToCaretPosition() {
|
|
57
|
-
|
|
54
|
+
getInstance(this).view.dispatch({ scrollIntoView: true });
|
|
58
55
|
return this;
|
|
59
56
|
},
|
|
60
57
|
};
|
|
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
|
-
|
|
58
|
+
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;
|
|
59
|
+
const getConfig = (magicWords, rule, flip) => {
|
|
60
|
+
const words = magicWords.filter(rule).filter(({ 'case-sensitive': i }) => i !== flip)
|
|
61
|
+
.flatMap(({ aliases, name, 'case-sensitive': i }) => aliases.map(alias => ({
|
|
62
|
+
alias: (i ? alias : alias.toLowerCase()).replace(/:$/u, ''),
|
|
63
|
+
name,
|
|
64
|
+
}))), obj = {};
|
|
65
|
+
for (const { alias, name } of words) {
|
|
66
|
+
obj[alias] = name;
|
|
67
67
|
}
|
|
68
|
-
return
|
|
68
|
+
return obj;
|
|
69
|
+
};
|
|
70
|
+
const getConfigPair = (magicWords, rule) => [true, false]
|
|
71
|
+
.map(bool => getConfig(magicWords, rule, bool));
|
|
72
|
+
const setConfig = (config) => {
|
|
73
|
+
mw.config.set('extCodeMirrorConfig', config);
|
|
69
74
|
};
|
|
70
75
|
const getMwConfig = async () => {
|
|
71
|
-
if (USING_LOCAL &&
|
|
76
|
+
if (USING_LOCAL && !VALID) {
|
|
72
77
|
await mw.loader.using(DATA_MODULE);
|
|
73
78
|
}
|
|
74
79
|
let config = mw.config.get('extCodeMirrorConfig');
|
|
75
|
-
if (!config &&
|
|
80
|
+
if (!config && VALID) {
|
|
76
81
|
({ config } = SITE_SETTINGS);
|
|
77
|
-
|
|
82
|
+
setConfig(config);
|
|
78
83
|
}
|
|
79
84
|
const isIPE = config && Object.values(config.functionSynonyms[0]).includes(true);
|
|
80
|
-
if (config
|
|
85
|
+
if ((config === null || config === void 0 ? void 0 : config.img) && config.variants && !isIPE) {
|
|
81
86
|
return {
|
|
82
87
|
...config,
|
|
83
88
|
nsid: mw.config.get('wgNamespaceIds'),
|
|
@@ -92,20 +97,25 @@ import { CodeMirror6 } from 'https://testingcf.jsdelivr.net/npm/@bhsd/codemirror
|
|
|
92
97
|
],
|
|
93
98
|
formatversion: '2',
|
|
94
99
|
});
|
|
95
|
-
const
|
|
100
|
+
const others = new Set(['msg', 'raw', 'msgnw', 'subst', 'safesubst']);
|
|
96
101
|
if (config && !isIPE) {
|
|
97
102
|
const { functionSynonyms: [insensitive] } = config;
|
|
98
103
|
if (!('subst' in insensitive)) {
|
|
99
|
-
|
|
100
|
-
for (const { alias, name } of aliases) {
|
|
101
|
-
insensitive[alias.replace(/:$/u, '')] = name;
|
|
102
|
-
}
|
|
104
|
+
Object.assign(insensitive, getConfig(magicwords, ({ name }) => others.has(name)));
|
|
103
105
|
}
|
|
104
106
|
}
|
|
105
107
|
else {
|
|
106
108
|
config = {
|
|
107
109
|
tagModes: {
|
|
110
|
+
tab: 'text/mediawiki',
|
|
111
|
+
indicator: 'text/mediawiki',
|
|
112
|
+
poem: 'text/mediawiki',
|
|
108
113
|
ref: 'text/mediawiki',
|
|
114
|
+
option: 'text/mediawiki',
|
|
115
|
+
combooption: 'text/mediawiki',
|
|
116
|
+
tabs: 'text/mediawiki',
|
|
117
|
+
poll: 'text/mediawiki',
|
|
118
|
+
gallery: 'text/mediawiki',
|
|
109
119
|
},
|
|
110
120
|
tags: {},
|
|
111
121
|
urlProtocols: mw.config.get('wgUrlProtocols'),
|
|
@@ -113,24 +123,18 @@ import { CodeMirror6 } from 'https://testingcf.jsdelivr.net/npm/@bhsd/codemirror
|
|
|
113
123
|
for (const tag of extensiontags) {
|
|
114
124
|
config.tags[tag.slice(1, -1)] = true;
|
|
115
125
|
}
|
|
116
|
-
const
|
|
126
|
+
const functions = new Set([
|
|
117
127
|
...functionhooks,
|
|
118
128
|
...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
|
-
];
|
|
129
|
+
...others,
|
|
130
|
+
]);
|
|
131
|
+
config.functionSynonyms = getConfigPair(magicwords, ({ name }) => functions.has(name));
|
|
132
|
+
config.doubleUnderscore = getConfigPair(magicwords, ({ aliases }) => aliases.some(alias => /^__.+__$/u.test(alias)));
|
|
129
133
|
}
|
|
130
|
-
config.img = getConfig(
|
|
134
|
+
config.img = getConfig(magicwords, ({ name }) => name.startsWith('img_'));
|
|
131
135
|
config.variants = variants ? variants.map(({ code }) => code) : [];
|
|
132
136
|
config.nsid = mw.config.get('wgNamespaceIds');
|
|
133
|
-
|
|
137
|
+
setConfig(config);
|
|
134
138
|
ALL_SETTINGS_CACHE[SITE_ID] = { config: config, time: Date.now() };
|
|
135
139
|
localStorage.setItem('InPageEditMwConfig', JSON.stringify(ALL_SETTINGS_CACHE));
|
|
136
140
|
return config;
|
|
@@ -149,20 +153,40 @@ import { CodeMirror6 } from 'https://testingcf.jsdelivr.net/npm/@bhsd/codemirror
|
|
|
149
153
|
super.toggle(show);
|
|
150
154
|
$(this.textarea).data('jquery.textSelection', show && textSelection);
|
|
151
155
|
}
|
|
152
|
-
async
|
|
156
|
+
async getLinter(opt) {
|
|
157
|
+
const linter = await super.getLinter(opt);
|
|
158
|
+
linters[this.lang] = linter;
|
|
159
|
+
return linter;
|
|
160
|
+
}
|
|
161
|
+
async defaultLint(on, optOrNs) {
|
|
153
162
|
if (!on) {
|
|
154
163
|
this.lint();
|
|
155
164
|
return;
|
|
156
165
|
}
|
|
157
166
|
const { lang } = this;
|
|
167
|
+
let opt;
|
|
168
|
+
if (typeof optOrNs === 'number') {
|
|
169
|
+
if (lang === 'mediawiki' && (optOrNs === 10 || optOrNs === 828)) {
|
|
170
|
+
opt = { include: true };
|
|
171
|
+
}
|
|
172
|
+
else if (lang === 'javascript' && (optOrNs === 8 || optOrNs === 2300)) {
|
|
173
|
+
opt = {
|
|
174
|
+
env: { browser: true, es6: true },
|
|
175
|
+
parserOptions: { ecmaVersion: 6 },
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
opt = optOrNs;
|
|
181
|
+
}
|
|
158
182
|
if (!(lang in linters)) {
|
|
159
|
-
|
|
160
|
-
if (
|
|
183
|
+
await this.getLinter(opt);
|
|
184
|
+
if (lang === 'mediawiki') {
|
|
161
185
|
const mwConfig = await getMwConfig(), config = {
|
|
162
186
|
...await wikiparse.getConfig(),
|
|
163
187
|
ext: Object.keys(mwConfig.tags),
|
|
164
188
|
namespaces: mw.config.get('wgFormattedNamespaces'),
|
|
165
|
-
nsid:
|
|
189
|
+
nsid: mwConfig.nsid,
|
|
166
190
|
doubleUnderscore: mwConfig.doubleUnderscore.map(obj => Object.keys(obj).map(s => s.slice(2, -2))),
|
|
167
191
|
variants: mwConfig.variants,
|
|
168
192
|
protocol: mwConfig.urlProtocols.replace(/\\:/gu, ':'),
|
|
@@ -185,6 +209,9 @@ import { CodeMirror6 } from 'https://testingcf.jsdelivr.net/npm/@bhsd/codemirror
|
|
|
185
209
|
wikiparse.setConfig(config);
|
|
186
210
|
}
|
|
187
211
|
}
|
|
212
|
+
else if (opt) {
|
|
213
|
+
await this.getLinter(opt);
|
|
214
|
+
}
|
|
188
215
|
if (linters[lang]) {
|
|
189
216
|
this.lint(linters[lang]);
|
|
190
217
|
}
|
|
@@ -230,17 +257,7 @@ import { CodeMirror6 } from 'https://testingcf.jsdelivr.net/npm/@bhsd/codemirror
|
|
|
230
257
|
}
|
|
231
258
|
}
|
|
232
259
|
const cm = await CodeMirror.fromTextArea(e.target, lang);
|
|
233
|
-
|
|
234
|
-
if (lang === 'mediawiki' && wgNamespaceNumber === 10) {
|
|
235
|
-
opt = { include: true };
|
|
236
|
-
}
|
|
237
|
-
else if (lang === 'javascript' && (wgNamespaceNumber === 8 || wgNamespaceNumber === 2300)) {
|
|
238
|
-
opt = {
|
|
239
|
-
env: { browser: true, es6: true },
|
|
240
|
-
parserOptions: { ecmaVersion: 6 },
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
await cm.defaultLint(true, opt);
|
|
260
|
+
void cm.defaultLint(true, wgNamespaceNumber);
|
|
244
261
|
})();
|
|
245
262
|
}
|
|
246
263
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bhsd/codemirror-mediawiki",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.9",
|
|
4
4
|
"description": "Modified CodeMirror mode based on wikimedia/mediawiki-extensions-CodeMirror",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mediawiki",
|
|
@@ -35,8 +35,7 @@
|
|
|
35
35
|
"lint:ts": "tsc --noEmit && tsc --project mw/tsconfig.json --noEmit && eslint --cache .",
|
|
36
36
|
"lint:css": "stylelint *.css",
|
|
37
37
|
"lint": "npm run lint:ts && npm run lint:css",
|
|
38
|
-
"
|
|
39
|
-
"test": "npm run server; open http://127.0.0.1:8080/codemirror-mediawiki/index.html",
|
|
38
|
+
"test": "http-server .. -c-1 --cors &",
|
|
40
39
|
"test:end": "pkill -x http-server"
|
|
41
40
|
},
|
|
42
41
|
"engines": {
|
package/src/codemirror.ts
CHANGED
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
} from '@codemirror/language';
|
|
20
20
|
import {defaultKeymap, historyKeymap, history} from '@codemirror/commands';
|
|
21
21
|
import {searchKeymap} from '@codemirror/search';
|
|
22
|
-
import {linter, lintGutter, openLintPanel, closeLintPanel} from '@codemirror/lint';
|
|
22
|
+
import {linter, lintGutter, openLintPanel, closeLintPanel, lintKeymap} from '@codemirror/lint';
|
|
23
23
|
import {closeBrackets} from '@codemirror/autocomplete';
|
|
24
24
|
import {mediawiki, html} from './mediawiki';
|
|
25
25
|
import * as plugins from './plugins';
|
|
@@ -32,6 +32,8 @@ import type {Linter} from 'eslint';
|
|
|
32
32
|
export type {MwConfig} from './mediawiki';
|
|
33
33
|
export type LintSource = (doc: Text) => Diagnostic[] | Promise<Diagnostic[]>;
|
|
34
34
|
|
|
35
|
+
declare type LintExtension = [unknown, ViewPlugin<{set: boolean, force(): void}>];
|
|
36
|
+
|
|
35
37
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
36
38
|
const languages: Record<string, (config?: any) => LanguageSupport | []> = {
|
|
37
39
|
plain: () => [],
|
|
@@ -43,7 +45,7 @@ for (const [language, parser] of Object.entries(plugins)) {
|
|
|
43
45
|
}
|
|
44
46
|
const linters: Record<string, Extension> = {};
|
|
45
47
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
46
|
-
const avail: Record<string, [
|
|
48
|
+
const avail: Record<string, [(config?: any) => Extension, Record<string, unknown>]> = {
|
|
47
49
|
highlightSpecialChars: [highlightSpecialChars, {}],
|
|
48
50
|
highlightActiveLine: [highlightActiveLine, {}],
|
|
49
51
|
highlightWhitespace: [highlightWhitespace, {}],
|
|
@@ -70,6 +72,14 @@ const loadScript = (src: string, target: string): Promise<void> => new Promise(r
|
|
|
70
72
|
document.head.append(script);
|
|
71
73
|
});
|
|
72
74
|
|
|
75
|
+
/**
|
|
76
|
+
* 获取指定行列的位置
|
|
77
|
+
* @param doc 文档
|
|
78
|
+
* @param line 行号
|
|
79
|
+
* @param column 列号
|
|
80
|
+
*/
|
|
81
|
+
const pos = (doc: Text, line: number, column: number): number => doc.line(line).from + column - 1;
|
|
82
|
+
|
|
73
83
|
export class CodeMirror6 {
|
|
74
84
|
readonly #textarea;
|
|
75
85
|
readonly #language;
|
|
@@ -129,6 +139,7 @@ export class CodeMirror6 {
|
|
|
129
139
|
...defaultKeymap,
|
|
130
140
|
...historyKeymap,
|
|
131
141
|
...searchKeymap,
|
|
142
|
+
...lintKeymap,
|
|
132
143
|
]),
|
|
133
144
|
EditorView.updateListener.of(({state: {doc}, docChanged}) => {
|
|
134
145
|
if (docChanged) {
|
|
@@ -143,12 +154,12 @@ export class CodeMirror6 {
|
|
|
143
154
|
extensions,
|
|
144
155
|
doc: textarea.value,
|
|
145
156
|
});
|
|
146
|
-
const {
|
|
157
|
+
const {selectionStart, selectionEnd, scrollTop} = textarea,
|
|
147
158
|
{fontSize, lineHeight} = getComputedStyle(textarea),
|
|
148
159
|
hasFocus = document.activeElement === textarea;
|
|
149
160
|
textarea.parentNode!.insertBefore(this.#view.dom, textarea);
|
|
150
|
-
this.#
|
|
151
|
-
this.#
|
|
161
|
+
this.#minHeight();
|
|
162
|
+
this.#refresh();
|
|
152
163
|
this.#view.dom.style.fontSize = fontSize;
|
|
153
164
|
this.#view.scrollDOM.style.lineHeight = lineHeight;
|
|
154
165
|
this.#view.requestMeasure();
|
|
@@ -164,6 +175,35 @@ export class CodeMirror6 {
|
|
|
164
175
|
});
|
|
165
176
|
}
|
|
166
177
|
|
|
178
|
+
/** 刷新编辑器高度 */
|
|
179
|
+
#refresh(): void {
|
|
180
|
+
const {offsetHeight} = this.#textarea;
|
|
181
|
+
this.#view.dom.style.height = offsetHeight ? `${offsetHeight}px` : this.#textarea.style.height;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* 设置编辑器最小高度
|
|
186
|
+
* @param linting 是否启用语法检查
|
|
187
|
+
*/
|
|
188
|
+
#minHeight(linting?: boolean): void {
|
|
189
|
+
this.#view.dom.style.minHeight = linting ? 'calc(100px + 2em)' : '2em';
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* 开关语法检查面板
|
|
194
|
+
* @param show 是否显示
|
|
195
|
+
*/
|
|
196
|
+
#toggleLintPanel(show: boolean): void {
|
|
197
|
+
(show ? openLintPanel : closeLintPanel)(this.#view);
|
|
198
|
+
document.querySelector<HTMLUListElement>('.cm-panel-lint ul')?.blur();
|
|
199
|
+
this.#minHeight(show);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** 获取语法检查扩展 */
|
|
203
|
+
#getLintExtension(): LintExtension | undefined {
|
|
204
|
+
return (this.#linter.get(this.#view.state) as LintExtension[])[0];
|
|
205
|
+
}
|
|
206
|
+
|
|
167
207
|
/**
|
|
168
208
|
* 设置语言
|
|
169
209
|
* @param lang 语言
|
|
@@ -177,7 +217,7 @@ export class CodeMirror6 {
|
|
|
177
217
|
],
|
|
178
218
|
});
|
|
179
219
|
this.#lang = lang;
|
|
180
|
-
(linters[lang]
|
|
220
|
+
this.#toggleLintPanel(Boolean(linters[lang]));
|
|
181
221
|
}
|
|
182
222
|
|
|
183
223
|
/**
|
|
@@ -199,17 +239,14 @@ export class CodeMirror6 {
|
|
|
199
239
|
this.#view.dispatch({
|
|
200
240
|
effects: [this.#linter.reconfigure(linterExtension)],
|
|
201
241
|
});
|
|
202
|
-
(lintSource
|
|
242
|
+
this.#toggleLintPanel(Boolean(lintSource));
|
|
203
243
|
}
|
|
204
244
|
|
|
205
245
|
/** 立即更新语法检查 */
|
|
206
246
|
update(): void {
|
|
207
|
-
const extension = this.#
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}> ]] | [];
|
|
211
|
-
if (extension.length > 0) {
|
|
212
|
-
const plugin = this.#view.plugin(extension[0]![1])!;
|
|
247
|
+
const extension = this.#getLintExtension();
|
|
248
|
+
if (extension) {
|
|
249
|
+
const plugin = this.#view.plugin(extension[1])!;
|
|
213
250
|
plugin.set = true;
|
|
214
251
|
plugin.force();
|
|
215
252
|
}
|
|
@@ -244,8 +281,8 @@ export class CodeMirror6 {
|
|
|
244
281
|
async getLinter(opt?: Record<string, unknown>): Promise<LintSource | undefined> {
|
|
245
282
|
switch (this.#lang) {
|
|
246
283
|
case 'mediawiki': {
|
|
247
|
-
const
|
|
248
|
-
|
|
284
|
+
const CDN = 'npm/wikiparser-node@1.3.4-b/extensions/dist',
|
|
285
|
+
src = `combine/${CDN}/base.min.js,${CDN}/lint.min.js`;
|
|
249
286
|
await loadScript(src, 'wikiparse');
|
|
250
287
|
const wikiLinter = new wikiparse.Linter(opt?.['include'] as boolean);
|
|
251
288
|
return doc => wikiLinter.codemirror(doc.toString());
|
|
@@ -257,28 +294,28 @@ export class CodeMirror6 {
|
|
|
257
294
|
conf: Linter.Config = {
|
|
258
295
|
env: {
|
|
259
296
|
browser: true,
|
|
260
|
-
|
|
297
|
+
es2024: true,
|
|
261
298
|
},
|
|
262
299
|
parserOptions: {
|
|
263
|
-
ecmaVersion:
|
|
300
|
+
ecmaVersion: 15,
|
|
264
301
|
sourceType: 'module',
|
|
265
302
|
},
|
|
266
303
|
rules: {},
|
|
267
304
|
...opt,
|
|
268
305
|
};
|
|
269
306
|
for (const [name, {meta}] of esLinter.getRules()) {
|
|
270
|
-
if (meta?.docs
|
|
307
|
+
if (meta?.docs?.recommended) {
|
|
271
308
|
conf.rules![name] ??= 2;
|
|
272
309
|
}
|
|
273
310
|
}
|
|
274
311
|
return doc => esLinter.verify(doc.toString(), conf)
|
|
275
312
|
.map(({message, severity, line, column, endLine, endColumn}) => {
|
|
276
|
-
const from = doc
|
|
313
|
+
const from = pos(doc, line, column);
|
|
277
314
|
return {
|
|
278
315
|
message,
|
|
279
316
|
severity: severity === 1 ? 'warning' : 'error',
|
|
280
317
|
from,
|
|
281
|
-
to: endLine === undefined ? from + 1 : doc
|
|
318
|
+
to: endLine === undefined ? from + 1 : pos(doc, endLine, endColumn!),
|
|
282
319
|
};
|
|
283
320
|
});
|
|
284
321
|
}
|
|
@@ -339,8 +376,8 @@ export class CodeMirror6 {
|
|
|
339
376
|
.map(({text, severity, line, column, endLine, endColumn}) => ({
|
|
340
377
|
message: text,
|
|
341
378
|
severity,
|
|
342
|
-
from: doc
|
|
343
|
-
to: endLine === undefined ? doc.line(line).to : doc
|
|
379
|
+
from: pos(doc, line, column),
|
|
380
|
+
to: endLine === undefined ? doc.line(line).to : pos(doc, endLine, endColumn!),
|
|
344
381
|
}));
|
|
345
382
|
};
|
|
346
383
|
}
|
|
@@ -369,15 +406,24 @@ export class CodeMirror6 {
|
|
|
369
406
|
}
|
|
370
407
|
}
|
|
371
408
|
|
|
409
|
+
/**
|
|
410
|
+
* 重设编辑器内容
|
|
411
|
+
* @param content 新内容
|
|
412
|
+
*/
|
|
413
|
+
setContent(content: string): void {
|
|
414
|
+
this.#view.dispatch({
|
|
415
|
+
changes: {from: 0, to: this.#view.state.doc.length, insert: content},
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
|
|
372
419
|
/**
|
|
373
420
|
* 在编辑器和文本框之间切换
|
|
374
421
|
* @param show 是否显示编辑器
|
|
375
422
|
*/
|
|
376
423
|
toggle(show = !this.#visible): void {
|
|
377
424
|
if (show && !this.#visible) {
|
|
378
|
-
this.#
|
|
379
|
-
|
|
380
|
-
});
|
|
425
|
+
this.setContent(this.#textarea.value);
|
|
426
|
+
this.#refresh();
|
|
381
427
|
}
|
|
382
428
|
this.#visible = show;
|
|
383
429
|
this.#view.dom.style.setProperty('display', show ? '' : 'none', 'important');
|
package/src/mediawiki.ts
CHANGED
|
@@ -556,7 +556,7 @@ class MediaWiki {
|
|
|
556
556
|
|
|
557
557
|
inHtmlTagAttribute(name: string): Tokenizer {
|
|
558
558
|
return (stream, state) => {
|
|
559
|
-
if (stream.match(/^[^>/<{]+/u)) {
|
|
559
|
+
if (stream.match(/^(?:"[^<">]*"|'[^<'>]*'[^>/<{])+/u)) {
|
|
560
560
|
return this.makeLocalStyle(modeConfig.tags.htmlTagAttribute, state);
|
|
561
561
|
} else if (stream.match(/^\/?>/u)) {
|
|
562
562
|
if (!this.implicitlyClosedHtmlTags.has(name)) {
|
|
@@ -582,7 +582,7 @@ class MediaWiki {
|
|
|
582
582
|
|
|
583
583
|
inExtTagAttribute(name: string): Tokenizer {
|
|
584
584
|
return (stream, state) => {
|
|
585
|
-
if (stream.match(/^[^>/]+/u)) {
|
|
585
|
+
if (stream.match(/^(?:"[^">]*"|'[^'>]*'|[^>/])+/u)) {
|
|
586
586
|
return this.makeLocalStyle(modeConfig.tags.extTagAttribute, state);
|
|
587
587
|
} else if (stream.eat('>')) {
|
|
588
588
|
state.extName = name;
|