@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/mw/dist/base.js CHANGED
@@ -1,8 +1,10 @@
1
- import { CodeMirror6 } from 'https://testingcf.jsdelivr.net/npm/@bhsd/codemirror-mediawiki@2.1.7/dist/main.min.js';
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.7/mediawiki.min.css', 'text/css');
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.view.dispatch({
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 } } } } = instances.get(this[0]);
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 instances.get(this[0]).view.state.doc.toString();
29
+ return getInstance(this).view.state.doc.toString();
30
30
  },
31
31
  setContents(content) {
32
- const { view } = instances.get(this[0]);
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 } } = instances.get(this[0]);
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 } = instances.get(this[0]);
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 } = instances.get(this[0]);
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
- instances.get(this[0]).view.dispatch({ scrollIntoView: true });
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], EXPIRED = !(SITE_SETTINGS && SITE_SETTINGS.time > Date.now() - 86400 * 1000 * 30);
62
- const getAliases = (words) => words.flatMap(({ aliases, name }) => aliases.map(alias => ({ alias, name })));
63
- const getConfig = (aliases) => {
64
- const config = {};
65
- for (const { alias, name } of aliases) {
66
- config[alias.replace(/:$/u, '')] = name;
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 config;
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 && EXPIRED) {
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 && !EXPIRED) {
80
+ if (!config && VALID) {
76
81
  ({ config } = SITE_SETTINGS);
77
- mw.config.set('extCodeMirrorConfig', config);
82
+ setConfig(config);
78
83
  }
79
84
  const isIPE = config && Object.values(config.functionSynonyms[0]).includes(true);
80
- if (config && config.img && config.variants && !isIPE) {
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 otherMagicwords = new Set(['msg', 'raw', 'msgnw', 'subst', 'safesubst']);
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
- const aliases = getAliases(magicwords.filter(({ name }) => otherMagicwords.has(name)));
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 realMagicwords = new Set([
126
+ const functions = new Set([
117
127
  ...functionhooks,
118
128
  ...variables,
119
- ...otherMagicwords,
120
- ]), allMagicwords = magicwords.filter(({ name, aliases }) => aliases.some(alias => /^__.+__$/u.test(alias)) || realMagicwords.has(name)), sensitive = getAliases(allMagicwords.filter(word => word['case-sensitive'])), insensitive = getAliases(allMagicwords.filter(word => !word['case-sensitive'])).map(({ alias, name }) => ({ alias: alias.toLowerCase(), name }));
121
- config.doubleUnderscore = [
122
- getConfig(insensitive.filter(({ alias }) => /^__.+__$/u.test(alias))),
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(getAliases(magicwords.filter(({ name }) => name.startsWith('img_'))));
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
- mw.config.set('extCodeMirrorConfig', config);
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 defaultLint(on, opt) {
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
- linters[lang] = await this.getLinter(opt);
160
- if (this.lang === 'mediawiki') {
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: mw.config.get('wgNamespaceIds'),
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
- let opt;
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.7",
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
- "server": "http-server .. -c-1 --cors &",
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, [ (config?: any) => Extension, Record<string, unknown> ]> = {
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 {offsetHeight, selectionStart, selectionEnd, scrollTop} = textarea,
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.#view.dom.style.minHeight = '2em';
151
- this.#view.dom.style.height = `${offsetHeight}px`;
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] ? openLintPanel : closeLintPanel)(this.#view);
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 ? openLintPanel : closeLintPanel)(this.#view);
242
+ this.#toggleLintPanel(Boolean(lintSource));
203
243
  }
204
244
 
205
245
  /** 立即更新语法检查 */
206
246
  update(): void {
207
- const extension = this.#linter.get(this.#view.state) as [[ unknown, ViewPlugin<{
208
- set: boolean;
209
- force(): void;
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 src = 'combine/npm/wikiparser-node@1.3.2-b/extensions/dist/base.min.js,'
248
- + 'npm/wikiparser-node@1.3.2-b/extensions/dist/lint.min.js';
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
- es2018: true,
297
+ es2024: true,
261
298
  },
262
299
  parserOptions: {
263
- ecmaVersion: 9,
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!.recommended) {
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.line(line).from + column - 1;
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.line(endLine).from + endColumn! - 1,
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.line(line).from + column - 1,
343
- to: endLine === undefined ? doc.line(line).to : doc.line(endLine).from + endColumn! - 1,
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.#view.dispatch({
379
- changes: {from: 0, to: this.#view.state.doc.length, insert: this.#textarea.value},
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;