@bhsd/codemirror-mediawiki 3.7.0 → 3.8.0

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 CHANGED
@@ -53,6 +53,8 @@ Nonetheless, this repository also provides a customized version with additional
53
53
  - [setTheme](#settheme)
54
54
  - [toggle](#toggle)
55
55
  - [update](#update)
56
+ - [Static accessors](#static-accessors)
57
+ - [CDN](#cdn)
56
58
  - [Static methods](#static-methods)
57
59
  - [getMwConfig](#getmwconfig)
58
60
  - [replaceSelections](#replaceselections)
@@ -845,6 +847,24 @@ Refresh linting immediately.
845
847
 
846
848
  </details>
847
849
 
850
+ # Static accessors
851
+
852
+ ## CDN
853
+
854
+ <details>
855
+ <summary>Expand</summary>
856
+
857
+ *version added: 3.8.0*
858
+
859
+ **type**: `string`
860
+ By default, libraries such as [WikiParser-Node](https://www.npmjs.com/package/wikiparser-node) are loaded from `testingcf.jsdelivr.net`. You can change the [jsDelivr CDN](https://www.jsdelivr.com/network) by setting this property.
861
+
862
+ ```js
863
+ CodeMirror6.CDN = 'https://cdn.jsdelivr.net';
864
+ ```
865
+
866
+ </details>
867
+
848
868
  # Static methods
849
869
 
850
870
  ## getMwConfig
@@ -2,6 +2,7 @@ import { EditorView } from '@codemirror/view';
2
2
  import type { KeyBinding } from '@codemirror/view';
3
3
  import type { Extension } from '@codemirror/state';
4
4
  import type { SyntaxNode } from '@lezer/common';
5
+ import type { ConfigGetter } from '@bhsd/browser';
5
6
  import type { ConfigData } from 'wikiparser-node';
6
7
  import type { DocRange, foldHandler } from './fold';
7
8
  import type { detectIndent } from './indent';
@@ -34,9 +35,11 @@ export declare const optionalFunctions: OptionalFunctions;
34
35
  /** CodeMirror 6 editor */
35
36
  export declare class CodeMirror6 {
36
37
  #private;
38
+ static get CDN(): string | undefined;
39
+ static set CDN(url: string | undefined);
37
40
  /** only for sanitized-css */
38
41
  dialect: Dialect;
39
- getWikiConfig?: () => Promise<ConfigData>;
42
+ getWikiConfig?: ConfigGetter;
40
43
  langConfig: MwConfig | undefined;
41
44
  /** textarea element */
42
45
  get textarea(): HTMLTextAreaElement;
@@ -2,7 +2,7 @@ import { EditorView, lineNumbers, keymap, highlightActiveLineGutter, } from '@co
2
2
  import { Compartment, EditorState, EditorSelection, SelectionRange } from '@codemirror/state';
3
3
  import { syntaxHighlighting, defaultHighlightStyle, indentOnInput, indentUnit, ensureSyntaxTree, } from '@codemirror/language';
4
4
  import { defaultKeymap, historyKeymap, history, redo, indentWithTab } from '@codemirror/commands';
5
- import { searchKeymap } from '@codemirror/search';
5
+ import { search, searchKeymap } from '@codemirror/search';
6
6
  import { linter, lintGutter, lintKeymap } from '@codemirror/lint';
7
7
  import elt from 'crelt';
8
8
  import { panelSelector, panelsSelector, diagnosticSelector } from './constants';
@@ -32,6 +32,13 @@ const linters = {};
32
32
  const phrases = {};
33
33
  /** CodeMirror 6 editor */
34
34
  export class CodeMirror6 {
35
+ static #CDN;
36
+ static get CDN() {
37
+ return CodeMirror6.#CDN;
38
+ }
39
+ static set CDN(url) {
40
+ CodeMirror6.#CDN = url;
41
+ }
35
42
  #textarea;
36
43
  #language = new Compartment();
37
44
  #linter = new Compartment();
@@ -97,7 +104,7 @@ export class CodeMirror6 {
97
104
  let timer;
98
105
  const { textarea, lang } = this, { value, dir: d, accessKey, tabIndex, lang: l, readOnly } = textarea, extensions = [
99
106
  this.#language.of(this.#getLanguage(config)),
100
- this.#linter.of(linters[lang] ?? []),
107
+ this.#linter.of(linters[lang]?.(this) ?? []),
101
108
  this.#extensions.of([]),
102
109
  this.#dir.of(EditorView.editorAttributes.of({ dir: d })),
103
110
  this.#extraKeys.of([]),
@@ -112,6 +119,13 @@ export class CodeMirror6 {
112
119
  EditorView.editorAttributes.of({ lang: l }),
113
120
  lineNumbers(),
114
121
  highlightActiveLineGutter(),
122
+ search({
123
+ scrollToMatch(range, view) {
124
+ const scrollRect = view.scrollDOM.getBoundingClientRect(), startCoords = view.coordsAtPos(range.from), endCoords = view.coordsAtPos(range.to), isInViewport = startCoords && startCoords.top >= scrollRect.top
125
+ && endCoords && endCoords.bottom <= scrollRect.bottom;
126
+ return EditorView.scrollIntoView(range, { y: isInViewport ? 'nearest' : 'center' });
127
+ },
128
+ }),
115
129
  keymap.of([
116
130
  ...defaultKeymap,
117
131
  ...searchKeymap,
@@ -157,7 +171,7 @@ export class CodeMirror6 {
157
171
  ...readOnly
158
172
  ? [
159
173
  EditorState.readOnly.of(true),
160
- EditorState.transactionFilter.of(tr => tr.docChanged ? [] : tr),
174
+ EditorState.changeFilter.of(({ docChanged }) => !docChanged),
161
175
  EditorView.theme({
162
176
  'input[type="color"]': {
163
177
  pointerEvents: 'none',
@@ -219,9 +233,9 @@ export class CodeMirror6 {
219
233
  const ext = this.#getLanguage(config);
220
234
  this.#effects([
221
235
  this.#language.reconfigure(ext),
222
- this.#linter.reconfigure(linters[lang] ?? []),
236
+ this.#linter.reconfigure(linters[lang]?.(this) ?? []),
223
237
  ]);
224
- this.#minHeight(Boolean(linters[lang]));
238
+ this.#minHeight(lang in linters);
225
239
  this.prefer({});
226
240
  }
227
241
  }
@@ -231,7 +245,7 @@ export class CodeMirror6 {
231
245
  */
232
246
  lint(lintSource) {
233
247
  const lintSources = typeof lintSource === 'function' ? [lintSource] : lintSource;
234
- const linterExtension = lintSources
248
+ const linterExtension = (cm) => lintSources
235
249
  ? [
236
250
  ...lintSources.map(source => linter(async ({ state }) => {
237
251
  const diagnostics = (await source(state)).map((diagnostic) => ({
@@ -256,7 +270,7 @@ export class CodeMirror6 {
256
270
  })),
257
271
  lintGutter(),
258
272
  keymap.of(lintKeymap),
259
- optionalFunctions.statusBar(this, lintSources[0].fixer),
273
+ optionalFunctions.statusBar(cm, lintSources[0].fixer),
260
274
  ]
261
275
  : [];
262
276
  if (lintSource) {
@@ -266,7 +280,7 @@ export class CodeMirror6 {
266
280
  delete linters[this.#lang];
267
281
  }
268
282
  if (this.#view) {
269
- this.#effects(this.#linter.reconfigure(linterExtension));
283
+ this.#effects(this.#linter.reconfigure(linterExtension(this)));
270
284
  this.#minHeight(Boolean(lintSource));
271
285
  }
272
286
  }
@@ -341,7 +355,7 @@ export class CodeMirror6 {
341
355
  * @param opt linter options
342
356
  */
343
357
  async getLinter(opt) {
344
- return linterRegistry[this.#lang]?.(opt, this.#view, this.#nestedMWLanguage);
358
+ return linterRegistry[this.#lang]?.(CodeMirror6.CDN, opt, this.#view, this.#nestedMWLanguage);
345
359
  }
346
360
  /**
347
361
  * Set content
package/dist/escape.js CHANGED
@@ -33,7 +33,7 @@ export const escapeHTML = (str) => [...str].map(c => {
33
33
  return encodeURIComponent(str);
34
34
  };
35
35
  const escapeWiki = (cm) => {
36
- const view = cm.view, { state } = view, { ranges } = state.selection, lsp = getLSP(view, false, cm.getWikiConfig);
36
+ const view = cm.view, { state } = view, { ranges } = state.selection, lsp = getLSP(view, false, cm.getWikiConfig, CodeMirror6.CDN);
37
37
  if (lsp && 'provideRefactoringAction' in lsp && ranges.some(({ empty }) => !empty)) {
38
38
  (async () => {
39
39
  const replacements = new WeakMap();
@@ -79,7 +79,7 @@ menuRegistry.push({
79
79
  handlerBase(view, e);
80
80
  });
81
81
  items = [btnHTML, btnURI];
82
- const lsp = getLSP(view, false, cm.getWikiConfig);
82
+ const lsp = getLSP(view, false, cm.getWikiConfig, CodeMirror6.CDN);
83
83
  if (lsp && 'provideRefactoringAction' in lsp) {
84
84
  const btnWiki = elt('div', 'Escape with magic words');
85
85
  btnWiki.addEventListener('click', e => {
package/dist/hover.d.ts CHANGED
@@ -1,4 +1,4 @@
1
+ import { CodeMirror6 } from './codemirror';
1
2
  import type { Extension } from '@codemirror/state';
2
- import type { CodeMirror6 } from './codemirror';
3
3
  declare const _default: (cm: CodeMirror6) => Extension;
4
4
  export default _default;
package/dist/hover.js CHANGED
@@ -3,11 +3,12 @@ import { ensureSyntaxTree } from '@codemirror/language';
3
3
  import { loadScript, getLSP } from '@bhsd/browser';
4
4
  import { tokens } from './config';
5
5
  import { hoverSelector } from './constants';
6
+ import { CodeMirror6 } from './codemirror';
6
7
  import { escHTML, indexToPos, posToIndex, createTooltipView } from './util';
7
8
  export default (cm) => [
8
9
  hoverTooltip(async (view, pos, side) => {
9
10
  const { state } = view, { doc } = state, { paramSuggest, tags } = cm.langConfig;
10
- let hover = await getLSP(view, false, cm.getWikiConfig)
11
+ let hover = await getLSP(view, false, cm.getWikiConfig, CodeMirror6.CDN)
11
12
  ?.provideHover(doc.toString(), indexToPos(doc, pos));
12
13
  if (!hover && paramSuggest && 'templatedata' in tags) {
13
14
  const node = ensureSyntaxTree(state, pos + Math.max(side, 0))?.resolve(pos, side);
@@ -28,7 +29,8 @@ export default (cm) => [
28
29
  }
29
30
  }
30
31
  if (hover) {
31
- await loadScript('npm/marked/lib/marked.umd.js', 'marked', true);
32
+ const { CDN = '' } = CodeMirror6;
33
+ await loadScript(`${CDN}${CDN && '/'}npm/marked/lib/marked.umd.js`, 'marked', true);
32
34
  const { end } = hover.range;
33
35
  return {
34
36
  pos,
package/dist/index.js CHANGED
@@ -223,7 +223,11 @@ export const registerMediaWikiCore = () => {
223
223
  toolKeymap,
224
224
  ];
225
225
  registerLintSource('mediawiki', getWikiLintSource);
226
- destroyListeners.push(view => getLSP(view)?.destroy());
226
+ destroyListeners.push(view => {
227
+ if (typeof wikiparse === 'object' && wikiparse.LanguageService) {
228
+ getLSP(view)?.destroy();
229
+ }
230
+ });
227
231
  };
228
232
  /** Register mixed MediaWiki-HTML language support */
229
233
  export const registerHTML = () => {
package/dist/inlay.d.ts CHANGED
@@ -1,4 +1,4 @@
1
+ import { CodeMirror6 } from './codemirror';
1
2
  import type { Extension } from '@codemirror/state';
2
- import type { CodeMirror6 } from './codemirror';
3
3
  declare const _default: (cm: CodeMirror6) => Extension;
4
4
  export default _default;
package/dist/inlay.js CHANGED
@@ -3,6 +3,7 @@ import { Decoration, EditorView, WidgetType, ViewPlugin } from '@codemirror/view
3
3
  import { getLSP } from '@bhsd/browser';
4
4
  import elt from 'crelt';
5
5
  import { posToIndex } from './util';
6
+ import { CodeMirror6 } from './codemirror';
6
7
  const cls = 'cm-inlay-hint';
7
8
  class InlayHintWidget extends WidgetType {
8
9
  constructor(label) {
@@ -54,7 +55,7 @@ export default (cm) => [
54
55
  ViewPlugin.fromClass(class {
55
56
  constructor(view) {
56
57
  const timer = setInterval(() => {
57
- if (getLSP(view, false, cm.getWikiConfig)) {
58
+ if (getLSP(view, false, cm.getWikiConfig, CodeMirror6.CDN)) {
58
59
  clearInterval(timer);
59
60
  void updateField({ view, docChanged: true });
60
61
  }
package/dist/linter.d.ts CHANGED
@@ -26,6 +26,7 @@ declare interface JsonError {
26
26
  column: string | undefined;
27
27
  position: string | undefined;
28
28
  }
29
+ export declare const stylelintRepo = "npm/@bhsd/stylelint-browserify", eslintRepo = "npm/@bhsd/eslint-browserify", luacheckRepo = "npm/luacheck-browserify", wikilintRepo = "npm/wikiparser-node";
29
30
  /**
30
31
  * 获取 Wikitext LSP
31
32
  * @param opt 选项
package/dist/linter.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { sanitizeInlineStyle } from '@bhsd/common';
2
2
  import { loadScript, getWikiparse, getLSP } from '@bhsd/browser';
3
3
  import { styleLint } from '@bhsd/stylelint-util';
4
+ export const stylelintRepo = 'npm/@bhsd/stylelint-browserify', eslintRepo = 'npm/@bhsd/eslint-browserify', luacheckRepo = 'npm/luacheck-browserify', wikilintRepo = 'npm/wikiparser-node';
4
5
  /**
5
6
  * 计算位置
6
7
  * @param range 范围
@@ -44,8 +45,9 @@ const isStylelintConfig = (config) => Boolean(config && ('extends' in config ||
44
45
  * @param obj 对象
45
46
  */
46
47
  export const getWikiLinter = async (opt, obj) => {
47
- await getWikiparse(opt?.['getConfig'], opt?.['i18n']);
48
- const lsp = getLSP(obj, opt?.['include']), cssLint = await getCssLinter();
48
+ const cdn = opt?.['cdn'];
49
+ await getWikiparse(opt?.['getConfig'], opt?.['i18n'], cdn && `${cdn}/${wikilintRepo}`);
50
+ const lsp = getLSP(obj, opt?.['include']), cssLint = await getCssLinter(cdn && `${cdn}/${stylelintRepo}`);
49
51
  const linter = async (text, config) => {
50
52
  const defaultSeverity = config?.['defaultSeverity'] ?? 2, diagnostics = (await lsp.provideDiagnostics(text)).filter(({ code, severity }) => Number(config?.[code] ?? defaultSeverity) > Number(severity === 2)), tokens = 'findStyleTokens' in lsp && config?.['invalid-css'] !== '0' ? await lsp.findStyleTokens() : [];
51
53
  if (tokens.length === 0) {
@@ -110,7 +112,7 @@ export const jsConfig = /* #__PURE__ */ (() => ({
110
112
  * 获取 ESLint
111
113
  * @param cdn CDN 地址
112
114
  */
113
- export const getJsLinter = async (cdn = 'npm/@bhsd/eslint-browserify') => {
115
+ export const getJsLinter = async (cdn = eslintRepo) => {
114
116
  await loadScript(cdn, 'eslint');
115
117
  /** @see https://www.npmjs.com/package/@codemirror/lang-javascript */
116
118
  const esLinter = new eslint.Linter(), conf = {
@@ -140,7 +142,7 @@ export const getJsLinter = async (cdn = 'npm/@bhsd/eslint-browserify') => {
140
142
  * 获取 Stylelint
141
143
  * @param cdn CDN 地址
142
144
  */
143
- export const getCssLinter = async (cdn = 'npm/@bhsd/stylelint-browserify') => {
145
+ export const getCssLinter = async (cdn = stylelintRepo) => {
144
146
  await loadScript(cdn, 'stylelint');
145
147
  const linter = async (code, opt) => {
146
148
  const warnings = await styleLint(stylelint, code, opt);
@@ -159,7 +161,7 @@ export const getCssLinter = async (cdn = 'npm/@bhsd/stylelint-browserify') => {
159
161
  * 获取 Luacheck
160
162
  * @param cdn CDN 地址
161
163
  */
162
- export const getLuaLinter = async (cdn = 'npm/luacheck-browserify') => {
164
+ export const getLuaLinter = async (cdn = luacheckRepo) => {
163
165
  await loadScript(cdn, 'luacheck');
164
166
  // eslint-disable-next-line @typescript-eslint/await-thenable
165
167
  const luachecker = await luacheck(undefined);
@@ -7,7 +7,7 @@ export type LintSource = ((state: EditorState) => Diagnostic[] | Promise<Diagnos
7
7
  fixer?: (doc: Text, rule?: string) => string | Promise<string>;
8
8
  };
9
9
  export type LintSources = LintSource | [LintSource] | [LintSource, LintSource];
10
- export type LintSourceGetter = (opt?: Option | LiveOption, view?: EditorView, nestedMWLanguage?: Language) => LintSource | Promise<LintSource>;
10
+ export type LintSourceGetter = (cdn?: string, opt?: Option | LiveOption, view?: EditorView, nestedMWLanguage?: Language) => LintSource | Promise<LintSource>;
11
11
  export interface ExtendedAction extends Action {
12
12
  tooltip: string | undefined;
13
13
  }
@@ -2,7 +2,7 @@ import { ensureSyntaxTree } from '@codemirror/language';
2
2
  import { cssLanguage } from '@codemirror/lang-css';
3
3
  import { javascriptLanguage } from '@codemirror/lang-javascript';
4
4
  import { sanitizeInlineStyle } from '@bhsd/common';
5
- import { getWikiLinter, getJsLinter, getCssLinter, getJsonLinter, getLuaLinter } from './linter';
5
+ import { getWikiLinter, getJsLinter, getCssLinter, getJsonLinter, getLuaLinter, stylelintRepo, eslintRepo, luacheckRepo, } from './linter';
6
6
  import { posToIndex } from './util';
7
7
  /**
8
8
  * 获取Linter选项
@@ -55,8 +55,8 @@ const wikiLintSource = async (wikiLint, text, opt, doc, f = 0, t) => (await wiki
55
55
  ? getRange(doc, r.start.line + 1, r.start.character + 1, r.end.line + 1, r.end.character + 1, f, t)
56
56
  : { from: from + f, to: (to ?? from) + f },
57
57
  }));
58
- export const getWikiLintSource = async (opt, v) => {
59
- const wikiLint = await getWikiLinter(await getOpt(opt), v);
58
+ export const getWikiLintSource = async (cdn, opt, v) => {
59
+ const wikiLint = await getWikiLinter({ ...await getOpt(opt), cdn }, v);
60
60
  const lintSource = async ({ doc }) => wikiLintSource(wikiLint, doc.toString(), await getOpt(opt, true), doc);
61
61
  if (wikiLint.fixer) {
62
62
  lintSource.fixer = (_, rule) => wikiLint.fixer('', rule);
@@ -91,8 +91,8 @@ const jsLintSource = (esLint, code, opt, doc, f = 0, t) => esLint(code, opt)
91
91
  }
92
92
  return diagnostic;
93
93
  });
94
- export const getJsLintSource = async (opt) => {
95
- const esLint = await getJsLinter();
94
+ export const getJsLintSource = async (cdn, opt) => {
95
+ const esLint = await getJsLinter(cdn && `${cdn}/${eslintRepo}`);
96
96
  const lintSource = async ({ doc }) => jsLintSource(esLint, doc.toString(), await getOpt(opt), doc);
97
97
  lintSource.fixer = (doc, rule) => esLint.fixer(doc.toString(), rule);
98
98
  return lintSource;
@@ -125,14 +125,14 @@ const cssLintSource = async (styleLint, code, opt, doc, f = 0, t) => {
125
125
  return diagnostic;
126
126
  });
127
127
  };
128
- export const getCssLintSource = async (opt) => {
129
- const styleLint = await getCssLinter();
128
+ export const getCssLintSource = async (cdn, opt) => {
129
+ const styleLint = await getCssLinter(cdn && `${cdn}/${stylelintRepo}`);
130
130
  const lintSource = async ({ doc }) => cssLintSource(styleLint, doc.toString(), await getOpt(opt), doc);
131
131
  lintSource.fixer = async (doc, rule) => styleLint.fixer(doc.toString(), rule);
132
132
  return lintSource;
133
133
  };
134
- export const getVueLintSource = async (opt) => {
135
- const styleLint = await getCssLinter(), esLint = await getJsLinter();
134
+ export const getVueLintSource = async (cdn, opt) => {
135
+ const styleLint = await getCssLinter(cdn && `${cdn}/${stylelintRepo}`), esLint = await getJsLinter(cdn && `${cdn}/${eslintRepo}`);
136
136
  return async (state) => {
137
137
  const { doc } = state, option = await getOpt(opt, true) ?? {}, js = option['js'], css = option['css'];
138
138
  return [
@@ -153,8 +153,8 @@ export const getVueLintSource = async (opt) => {
153
153
  ];
154
154
  };
155
155
  };
156
- export const getHTMLLintSource = async (opt, view, language) => {
157
- const vueLintSource = await getVueLintSource(opt), wikiLint = await getWikiLinter({ include: false, ...await getOpt(opt) }, view);
156
+ export const getHTMLLintSource = async (cdn, opt, view, language) => {
157
+ const vueLintSource = await getVueLintSource(cdn, opt), wikiLint = await getWikiLinter({ include: false, ...await getOpt(opt), cdn }, view);
158
158
  return async (state) => {
159
159
  const { doc } = state, option = await getOpt(opt, true) ?? {}, wiki = option['wiki'];
160
160
  return [
@@ -182,8 +182,8 @@ export const getJsonLintSource = () => {
182
182
  return [];
183
183
  };
184
184
  };
185
- export const getLuaLintSource = async () => {
186
- const luaLint = await getLuaLinter();
185
+ export const getLuaLintSource = async (cdn) => {
186
+ const luaLint = await getLuaLinter(cdn && `${cdn}/${luacheckRepo}`);
187
187
  return async ({ doc }) => (await luaLint(doc.toString()))
188
188
  .map(({ line, column, end_column: endColumn, msg: message, severity }) => ({
189
189
  source: 'Luacheck',