@bhsd/codemirror-mediawiki 2.29.2 → 2.30.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
@@ -1,5 +1,4 @@
1
1
  [![npm version](https://badge.fury.io/js/@bhsd%2Fcodemirror-mediawiki.svg)](https://www.npmjs.com/package/@bhsd/codemirror-mediawiki)
2
- [![CodeQL](https://github.com/bhsd-harry/codemirror-mediawiki/actions/workflows/codeql.yml/badge.svg)](https://github.com/bhsd-harry/codemirror-mediawiki/actions/workflows/codeql.yml)
3
2
  [![jsDelivr hits (npm scoped)](https://img.shields.io/jsdelivr/npm/hm/%40bhsd/codemirror-mediawiki)](https://www.npmjs.com/package/@bhsd/codemirror-mediawiki)
4
3
  [![Codacy Badge](https://app.codacy.com/project/badge/Grade/972fd5f6684c4fd8ac2f26e01d349948)](https://app.codacy.com/gh/bhsd-harry/codemirror-mediawiki/dashboard)
5
4
 
@@ -89,7 +89,7 @@ const avail = {
89
89
  hover: mediawikiOnly(magicWordHover),
90
90
  signatureHelp: mediawikiOnly(signatureHelp),
91
91
  inlayHints: mediawikiOnly(inlayHints),
92
- };
92
+ }, editExtensions = new Set(['closeBrackets', 'autocompletion', 'signatureHelp']);
93
93
  const linters = {};
94
94
  const phrases = {};
95
95
  /**
@@ -194,7 +194,13 @@ export class CodeMirror6 {
194
194
  }
195
195
  }),
196
196
  ...readOnly
197
- ? [EditorState.readOnly.of(true)]
197
+ ? [
198
+ EditorState.readOnly.of(true),
199
+ EditorState.transactionFilter.of(tr => tr.docChanged ? [] : tr),
200
+ EditorView.theme({
201
+ 'input[type="color"]': { pointerEvents: 'none' },
202
+ }),
203
+ ]
198
204
  : [
199
205
  history(),
200
206
  indentOnInput(),
@@ -261,7 +267,15 @@ export class CodeMirror6 {
261
267
  lint(lintSource) {
262
268
  const linterExtension = lintSource
263
269
  ? [
264
- linter(view => lintSource(view.state.doc)),
270
+ linter(async ({ state: { doc, readOnly } }) => {
271
+ const diagnostics = await lintSource(doc);
272
+ if (readOnly) {
273
+ for (const diagnostic of diagnostics) {
274
+ delete diagnostic.actions;
275
+ }
276
+ }
277
+ return diagnostics;
278
+ }),
265
279
  lintGutter(),
266
280
  keymap.of(lintKeymap),
267
281
  statusBar(lintSource.fixer),
@@ -308,7 +322,8 @@ export class CodeMirror6 {
308
322
  }
309
323
  }
310
324
  if (this.#view) {
311
- this.#effects(this.#extensions.reconfigure([...this.#preferred].map(name => {
325
+ const { readOnly } = this.#view.state;
326
+ this.#effects(this.#extensions.reconfigure([...this.#preferred].filter(name => !readOnly || !editExtensions.has(name)).map(name => {
312
327
  const [extension, configs] = avail[name];
313
328
  return extension(configs[this.#lang], this);
314
329
  })));
package/dist/config.d.ts CHANGED
@@ -53,6 +53,7 @@ tokens: {
53
53
  htmlTagAttributeValue: string;
54
54
  htmlTagBracket: string;
55
55
  htmlTagName: string;
56
+ ignored: string;
56
57
  imageParameter: string;
57
58
  linkBracket: string;
58
59
  linkDelimiter: string;
package/dist/config.js CHANGED
@@ -112,6 +112,7 @@ var tokens = {
112
112
  htmlTagAttributeValue: "mw-htmltag-attribute-value",
113
113
  htmlTagBracket: "mw-htmltag-bracket",
114
114
  htmlTagName: "mw-htmltag-name",
115
+ ignored: "mw-ignored",
115
116
  imageParameter: "mw-image-parameter",
116
117
  linkBracket: "mw-link-bracket",
117
118
  linkDelimiter: "mw-link-delimiter",
package/dist/fold.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { StateField } from '@codemirror/state';
2
- import type { EditorView, Tooltip } from '@codemirror/view';
2
+ import type { EditorView, Tooltip, Command } from '@codemirror/view';
3
3
  import type { EditorState, Extension } from '@codemirror/state';
4
4
  import type { SyntaxNode, Tree } from '@lezer/common';
5
5
  export interface DocRange {
@@ -17,9 +17,11 @@ export declare const braceStackUpdate: (state: EditorState, node: SyntaxNode) =>
17
17
  * @param state
18
18
  * @param posOrNode 字符位置或语法树节点
19
19
  * @param tree 语法树
20
+ * @param refOnly 是否仅检查`<ref>`标签
20
21
  */
21
- export declare const foldable: (state: EditorState, posOrNode: number | SyntaxNode, tree?: Tree | null) => DocRange | false;
22
+ export declare const foldable: (state: EditorState, posOrNode: number | SyntaxNode, tree?: Tree | null, refOnly?: boolean) => DocRange | false;
22
23
  export declare const foldableLine: ({ state, viewport: { to: end }, viewportLineBlocks }: EditorView, { from: f, to: t }: DocRange) => DocRange | false;
24
+ export declare const foldRef: Command;
23
25
  declare const _default: [(e?: Extension | undefined) => Extension, {
24
26
  mediawiki: (Extension | StateField<Tooltip | null>)[];
25
27
  }];
package/dist/fold.js CHANGED
@@ -3,7 +3,7 @@ import { StateField, RangeSetBuilder, RangeSet } from '@codemirror/state';
3
3
  import { syntaxTree, ensureSyntaxTree, foldEffect, unfoldEffect, foldedRanges, unfoldAll, codeFolding, foldGutter, foldKeymap, foldState, language, } from '@codemirror/language';
4
4
  import { getRegex } from '@bhsd/common';
5
5
  import { tokens } from './config';
6
- import { matchTag } from './matchTag';
6
+ import { matchTag, getTag } from './matchTag';
7
7
  const getExtRegex = getRegex(tag => new RegExp(`mw-tag-${tag}(?![a-z])`, 'u'));
8
8
  const updateSelection = (pos, { to }) => Math.max(pos, to), updateAll = (pos, { from, to }) => from <= pos && to > pos ? to : pos;
9
9
  /**
@@ -25,8 +25,9 @@ isExtBracket = isComponent(['extTagBracket']),
25
25
  /**
26
26
  * Check if a SyntaxNode is part of a extension tag
27
27
  * @param node 语法树节点
28
+ * @param refOnly 是否仅检查`<ref>`标签
28
29
  */
29
- isExt = (node) => node.name.includes('mw-tag-');
30
+ isExt = (node, refOnly) => node.name.includes(`mw-tag-${refOnly ? 'ref' : ''}`);
30
31
  /**
31
32
  * Update the stack of opening (+) or closing (-) brackets
32
33
  * @param state
@@ -36,13 +37,15 @@ export const braceStackUpdate = (state, node) => {
36
37
  const brackets = state.sliceDoc(node.from, node.to);
37
38
  return [brackets.split('{{').length - 1, 1 - brackets.split('}}').length];
38
39
  };
40
+ const refNames = new Set(['ref', 'references']);
39
41
  /**
40
42
  * 寻找可折叠的范围
41
43
  * @param state
42
44
  * @param posOrNode 字符位置或语法树节点
43
45
  * @param tree 语法树
46
+ * @param refOnly 是否仅检查`<ref>`标签
44
47
  */
45
- export const foldable = (state, posOrNode, tree) => {
48
+ export const foldable = (state, posOrNode, tree, refOnly = false) => {
46
49
  if (typeof posOrNode === 'number') {
47
50
  tree = ensureSyntaxTree(state, posOrNode); // eslint-disable-line no-param-reassign
48
51
  }
@@ -53,12 +56,12 @@ export const foldable = (state, posOrNode, tree) => {
53
56
  if (typeof posOrNode === 'number') {
54
57
  // Find the initial template node on both sides of the position
55
58
  const left = tree.resolve(posOrNode, -1);
56
- if (isTemplate(left)) {
59
+ if (!refOnly && isTemplate(left)) {
57
60
  node = left;
58
61
  }
59
62
  else {
60
63
  const right = tree.resolve(posOrNode, 1);
61
- node = isExt(left)
64
+ node = isExt(left, refOnly)
62
65
  && left.name.split('mw-tag-').length > right.name.split('mw-tag-').length
63
66
  ? left
64
67
  : right;
@@ -67,15 +70,17 @@ export const foldable = (state, posOrNode, tree) => {
67
70
  else {
68
71
  node = posOrNode;
69
72
  }
70
- if (!isTemplate(node)) {
73
+ if (refOnly || !isTemplate(node)) {
71
74
  // Not a template
72
- if (isExt(node)) {
75
+ if (isExt(node, refOnly)) {
73
76
  const { name } = node, [tag] = /^[a-z]+/u.exec(name.slice(name.lastIndexOf('mw-tag-') + 7)), regex = getExtRegex(tag);
74
77
  let { nextSibling } = node;
75
78
  while (nextSibling && !(isExtBracket(nextSibling) && !regex.test(nextSibling.name))) {
76
79
  ({ nextSibling } = nextSibling);
77
80
  }
78
- if (nextSibling) { // The closing bracket of the current extension tag
81
+ const next = nextSibling?.nextSibling;
82
+ // The closing bracket of the current extension tag
83
+ if (nextSibling && (!refOnly || next && refNames.has(getTag(state, next)?.name))) {
79
84
  return { from: matchTag(state, nextSibling.to).end.to, to: nextSibling.from };
80
85
  }
81
86
  }
@@ -189,11 +194,12 @@ const getAnchor = (state) => Math.max(...state.selection.ranges.map(({ to }) =>
189
194
  * @param end 终止位置
190
195
  * @param anchor 光标位置
191
196
  * @param update 更新光标位置
197
+ * @param refOnly 是否仅检查`<ref>`标签
192
198
  */
193
- const traverse = (state, tree, effects, node, end, anchor, update) => {
199
+ const traverse = (state, tree, effects, node, end, anchor, update, refOnly) => {
194
200
  while (node && node.from <= end) {
195
201
  /* eslint-disable no-param-reassign */
196
- const range = foldable(state, node, tree);
202
+ const range = foldable(state, node, tree, refOnly);
197
203
  if (range) {
198
204
  effects.push(foldEffect.of(range));
199
205
  node = tree.resolve(range.to, 1);
@@ -311,6 +317,15 @@ const markers = ViewPlugin.fromClass(class {
311
317
  }
312
318
  });
313
319
  const defaultFoldExtension = [foldGutter(), keymap.of(foldKeymap)];
320
+ /**
321
+ * 生成折叠命令
322
+ * @param refOnly 是否仅检查`<ref>`标签
323
+ */
324
+ const foldCommand = (refOnly) => view => {
325
+ const { state } = view, tree = syntaxTree(state), effects = [], anchor = traverse(state, tree, effects, tree.topNode.firstChild, Infinity, getAnchor(state), updateAll, refOnly);
326
+ return execute(view, effects, anchor);
327
+ };
328
+ export const foldRef = foldCommand(true);
314
329
  export default [
315
330
  (e = defaultFoldExtension) => e,
316
331
  {
@@ -372,10 +387,12 @@ export default [
372
387
  {
373
388
  // Fold all templates in the document
374
389
  key: 'Ctrl-Alt-[',
375
- run(view) {
376
- const { state } = view, tree = syntaxTree(state), effects = [], anchor = traverse(state, tree, effects, tree.topNode.firstChild, Infinity, getAnchor(state), updateAll);
377
- return execute(view, effects, anchor);
378
- },
390
+ run: foldCommand(),
391
+ },
392
+ {
393
+ // Fold all `<ref>` tags in the document
394
+ key: 'Mod-Alt-,',
395
+ run: foldRef,
379
396
  },
380
397
  {
381
398
  // Unfold the template at the selection/cursor
package/dist/linter.d.ts CHANGED
@@ -33,12 +33,21 @@ declare interface JsonError {
33
33
  */
34
34
  export declare const getWikiLinter: getAsyncLinter<Promise<MixedDiagnostic[]>, Option, object>;
35
35
  export declare const jsConfig: Linter.Config<Linter.RulesRecord, Linter.RulesRecord>;
36
- /** 获取 ESLint */
37
- export declare const getJsLinter: getAsyncLinter<Linter.LintMessage[]>;
38
- /** 获取 Stylelint */
39
- export declare const getCssLinter: getAsyncLinter<Promise<Warning[]>>;
40
- /** 获取 Luacheck */
41
- export declare const getLuaLinter: getAsyncLinter<Promise<Diagnostic[]>>;
36
+ /**
37
+ * 获取 ESLint
38
+ * @param cdn CDN 地址
39
+ */
40
+ export declare const getJsLinter: getAsyncLinter<Linter.LintMessage[], string>;
41
+ /**
42
+ * 获取 Stylelint
43
+ * @param cdn CDN 地址
44
+ */
45
+ export declare const getCssLinter: getAsyncLinter<Promise<Warning[]>, string>;
46
+ /**
47
+ * 获取 Luacheck
48
+ * @param cdn CDN 地址
49
+ */
50
+ export declare const getLuaLinter: getAsyncLinter<Promise<Diagnostic[]>, string>;
42
51
  /** JSON.parse */
43
52
  export declare const getJsonLinter: getLinter<JsonError[]>;
44
53
  export {};
package/dist/linter.js CHANGED
@@ -57,9 +57,12 @@ export const jsConfig = /* #__PURE__ */ (() => ({
57
57
  importStylesheetURI: 'readonly',
58
58
  },
59
59
  }))();
60
- /** 获取 ESLint */
61
- export const getJsLinter = async () => {
62
- await loadScript('npm/@bhsd/eslint-browserify', 'eslint');
60
+ /**
61
+ * 获取 ESLint
62
+ * @param cdn CDN 地址
63
+ */
64
+ export const getJsLinter = async (cdn = 'npm/@bhsd/eslint-browserify') => {
65
+ await loadScript(cdn, 'eslint');
63
66
  /** @see https://www.npmjs.com/package/@codemirror/lang-javascript */
64
67
  const esLinter = new eslint.Linter(), conf = {
65
68
  env: { browser: true, es2024: true },
@@ -84,9 +87,12 @@ export const getJsLinter = async () => {
84
87
  linter.fixer = (code, rule) => esLinter.verifyAndFix(code, rule ? { ...linter.config, rules: { [rule]: linter.config.rules?.[rule] ?? 2 } } : linter.config).output;
85
88
  return linter;
86
89
  };
87
- /** 获取 Stylelint */
88
- export const getCssLinter = async () => {
89
- await loadScript('npm/@bhsd/stylelint-browserify', 'stylelint');
90
+ /**
91
+ * 获取 Stylelint
92
+ * @param cdn CDN 地址
93
+ */
94
+ export const getCssLinter = async (cdn = 'npm/@bhsd/stylelint-browserify') => {
95
+ await loadScript(cdn, 'stylelint');
90
96
  const linter = async (code, opt) => {
91
97
  const warnings = await styleLint(stylelint, code, opt);
92
98
  if (opt && 'rules' in opt) {
@@ -102,9 +108,12 @@ export const getCssLinter = async () => {
102
108
  };
103
109
  return linter;
104
110
  };
105
- /** 获取 Luacheck */
106
- export const getLuaLinter = async () => {
107
- await loadScript('npm/luacheck-browserify', 'luacheck');
111
+ /**
112
+ * 获取 Luacheck
113
+ * @param cdn CDN 地址
114
+ */
115
+ export const getLuaLinter = async (cdn = 'npm/luacheck-browserify') => {
116
+ await loadScript(cdn, 'luacheck');
108
117
  // eslint-disable-next-line @typescript-eslint/await-thenable
109
118
  const luachecker = await luacheck(undefined);
110
119
  return async (text) => (await luachecker.queue(text)).filter(({ severity }) => severity);