@bhsd/codemirror-mediawiki 2.1.15 → 2.2.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/src/codemirror.ts CHANGED
@@ -1,4 +1,4 @@
1
- import {Compartment, EditorState} from '@codemirror/state';
1
+ import {Compartment, EditorState, EditorSelection} from '@codemirror/state';
2
2
  import {
3
3
  EditorView,
4
4
  lineNumbers,
@@ -23,9 +23,10 @@ import {searchKeymap} from '@codemirror/search';
23
23
  import {linter, lintGutter, openLintPanel, closeLintPanel, lintKeymap} from '@codemirror/lint';
24
24
  import {closeBrackets} from '@codemirror/autocomplete';
25
25
  import {mediawiki, html} from './mediawiki';
26
+ import {keyMap} from './escape';
26
27
  import * as plugins from './plugins';
27
- import type {ViewPlugin} from '@codemirror/view';
28
- import type {Extension, Text} from '@codemirror/state';
28
+ import type {ViewPlugin, KeyBinding} from '@codemirror/view';
29
+ import type {Extension, Text, StateEffect} from '@codemirror/state';
29
30
  import type {Diagnostic} from '@codemirror/lint';
30
31
  import type {Highlighter} from '@lezer/highlight';
31
32
  import type {Linter} from 'eslint';
@@ -60,8 +61,13 @@ const avail: Record<string, [(config?: any) => Extension, Record<string, unknown
60
61
  ],
61
62
  {},
62
63
  ],
64
+ escape: [
65
+ (keys: KeyBinding[] = []): Extension => keymap.of(keys),
66
+ {mediawiki: keyMap},
67
+ ],
63
68
  };
64
- const CDN = 'https://testingcf.jsdelivr.net';
69
+
70
+ export const CDN = 'https://testingcf.jsdelivr.net';
65
71
 
66
72
  /**
67
73
  * 使用传统方法加载脚本
@@ -96,6 +102,7 @@ export class CodeMirror6 {
96
102
  readonly #linter = new Compartment();
97
103
  readonly #extensions = new Compartment();
98
104
  readonly #indent = new Compartment();
105
+ readonly #extraKeys = new Compartment();
99
106
  #lang;
100
107
  #visible = false;
101
108
  #preferred = new Set<string>();
@@ -130,6 +137,7 @@ export class CodeMirror6 {
130
137
  this.#linter.of([]),
131
138
  this.#extensions.of([]),
132
139
  this.#indent.of(indentUnit.of('\t')),
140
+ this.#extraKeys.of([]),
133
141
  syntaxHighlighting(defaultHighlightStyle as Highlighter),
134
142
  EditorView.contentAttributes.of({
135
143
  accesskey: textarea.accessKey,
@@ -168,6 +176,14 @@ export class CodeMirror6 {
168
176
  this.toggle(true);
169
177
  }
170
178
 
179
+ /**
180
+ * 修改扩展
181
+ * @param effects 扩展变动
182
+ */
183
+ #effects(effects: StateEffect<unknown> | StateEffect<unknown>[]): void {
184
+ this.#view.dispatch({effects});
185
+ }
186
+
171
187
  /** 刷新编辑器高度 */
172
188
  #refresh(): void {
173
189
  const {offsetHeight} = this.#textarea;
@@ -203,14 +219,13 @@ export class CodeMirror6 {
203
219
  * @param config 语言设置
204
220
  */
205
221
  setLanguage(lang = 'plain', config?: unknown): void {
206
- this.#view.dispatch({
207
- effects: [
208
- this.#language.reconfigure(languages[lang]!(config)),
209
- this.#linter.reconfigure(linters[lang] || []),
210
- ],
211
- });
222
+ this.#effects([
223
+ this.#language.reconfigure(languages[lang]!(config)),
224
+ this.#linter.reconfigure(linters[lang] || []),
225
+ ]);
212
226
  this.#lang = lang;
213
227
  this.#toggleLintPanel(Boolean(linters[lang]));
228
+ this.prefer({});
214
229
  }
215
230
 
216
231
  /**
@@ -229,9 +244,7 @@ export class CodeMirror6 {
229
244
  } else {
230
245
  delete linters[this.#lang];
231
246
  }
232
- this.#view.dispatch({
233
- effects: [this.#linter.reconfigure(linterExtension)],
234
- });
247
+ this.#effects(this.#linter.reconfigure(linterExtension));
235
248
  this.#toggleLintPanel(Boolean(lintSource));
236
249
  }
237
250
 
@@ -261,14 +274,12 @@ export class CodeMirror6 {
261
274
  }
262
275
  }
263
276
  }
264
- this.#view.dispatch({
265
- effects: [
266
- this.#extensions.reconfigure([...this.#preferred].map(name => {
267
- const [extension, configs] = avail[name]!;
268
- return extension(configs[this.#lang]);
269
- })),
270
- ],
271
- });
277
+ this.#effects(
278
+ this.#extensions.reconfigure([...this.#preferred].map(name => {
279
+ const [extension, configs] = avail[name]!;
280
+ return extension(configs[this.#lang]);
281
+ })),
282
+ );
272
283
  }
273
284
 
274
285
  /**
@@ -276,16 +287,14 @@ export class CodeMirror6 {
276
287
  * @param indent 缩进字符串
277
288
  */
278
289
  setIndent(indent: string): void {
279
- this.#view.dispatch({
280
- effects: [this.#indent.reconfigure(indentUnit.of(indent))],
281
- });
290
+ this.#effects(this.#indent.reconfigure(indentUnit.of(indent)));
282
291
  }
283
292
 
284
293
  /** 获取默认linter */
285
294
  async getLinter(opt?: Record<string, unknown>): Promise<LintSource | undefined> {
286
295
  switch (this.#lang) {
287
296
  case 'mediawiki': {
288
- const REPO = 'npm/wikiparser-node@1.4.1-b',
297
+ const REPO = 'npm/wikiparser-node@1.4.3-b',
289
298
  DIR = `${REPO}/extensions/dist`,
290
299
  src = `combine/${DIR}/base.min.js,${DIR}/lint.min.js`,
291
300
  lang = opt?.['i18n'];
@@ -471,4 +480,28 @@ export class CodeMirror6 {
471
480
  }
472
481
  this.#visible = show;
473
482
  }
483
+
484
+ /**
485
+ * 添加额外快捷键
486
+ * @param keys 快捷键
487
+ */
488
+ extraKeys(keys: KeyBinding[]): void {
489
+ this.#effects(this.#extraKeys.reconfigure(keymap.of(keys)));
490
+ }
491
+
492
+ /**
493
+ * 替换选中内容
494
+ * @param view EditorView
495
+ * @param func 替换函数
496
+ */
497
+ static replaceSelections(view: EditorView, func: (str: string) => string): void {
498
+ const {state} = view;
499
+ view.dispatch(state.update(state.changeByRange(({from, to}) => {
500
+ const insert = func(state.sliceDoc(from, to));
501
+ return {
502
+ range: EditorSelection.cursor(from + insert.length),
503
+ changes: {from, to, insert},
504
+ };
505
+ })));
506
+ }
474
507
  }
package/src/escape.ts ADDED
@@ -0,0 +1,28 @@
1
+ import {CodeMirror6} from './codemirror';
2
+ import type {KeyBinding, Command} from '@codemirror/view';
3
+
4
+ const entity = {'"': 'quot', "'": 'apos', '<': 'lt', '>': 'gt', '&': 'amp', ' ': 'nbsp'};
5
+ const convert = (func: (str: string) => string): Command => (view): true => {
6
+ CodeMirror6.replaceSelections(view, func);
7
+ return true;
8
+ },
9
+ escapeHTML = convert(str => [...str].map(c => {
10
+ if (c in entity) {
11
+ return `&${entity[c as keyof typeof entity]};`;
12
+ }
13
+ const code = c.codePointAt(0)!;
14
+ return code < 256 ? `&#${code};` : `&#x${code.toString(16)};`;
15
+ }).join('')),
16
+ escapeURI = convert(str => {
17
+ if (str.includes('%')) {
18
+ try {
19
+ return decodeURIComponent(str);
20
+ } catch {}
21
+ }
22
+ return encodeURIComponent(str);
23
+ });
24
+
25
+ export const keyMap: KeyBinding[] = [
26
+ {key: 'Mod-[', run: escapeHTML},
27
+ {key: 'Mod-]', run: escapeURI},
28
+ ];