@bhsd/codemirror-mediawiki 3.11.4 → 3.12.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
@@ -68,6 +68,7 @@ If you are just looking for a CodeMirror 6 language mode and language support ex
68
68
  - [bidiIsolates](#bidiisolates)
69
69
  - [bracketMatching](#bracketmatching)
70
70
  - [closeBrackets](#closebrackets)
71
+ - [closeTags](#closetags)
71
72
  - [codeFolding](#codefolding)
72
73
  - [colorPicker](#colorpicker)
73
74
  - [escape](#escape)
@@ -259,11 +260,11 @@ In addition to the common [extensions](#extensions), here are some HTML-specific
259
260
  ```js
260
261
  import {
261
262
  registerBracketMatchingForHTML,
262
- registerCloseBracketsForHTML,
263
+ registerCloseTagsForHTML,
263
264
  registerColorPickerForHTML,
264
265
  } from '@bhsd/codemirror-mediawiki';
265
266
  registerBracketMatchingForHTML();
266
- registerCloseBracketsForHTML();
267
+ registerCloseTagsForHTML();
267
268
  registerColorPickerForHTML();
268
269
  ```
269
270
 
@@ -408,10 +409,10 @@ In addition to the common [extensions](#extensions), here are some Vue-specific
408
409
 
409
410
  ```js
410
411
  import {
411
- registerCloseBracketsForVue,
412
+ registerCloseTagsForVue,
412
413
  registerColorPickerForVue,
413
414
  } from '@bhsd/codemirror-mediawiki';
414
- registerCloseBracketsForVue();
415
+ registerCloseTagsForVue();
415
416
  registerColorPickerForVue();
416
417
  ```
417
418
 
@@ -722,6 +723,7 @@ cm.prefer([
722
723
  'autocompletion',
723
724
  'bracketMatching',
724
725
  'closeBrackets',
726
+ 'closeTags',
725
727
  'codeFolding',
726
728
  'highlightActiveLine',
727
729
  'highlightSelectionMatches',
@@ -746,6 +748,7 @@ cm.prefer({
746
748
  autocompletion: false,
747
749
  bracketMatching: false,
748
750
  closeBrackets: false,
751
+ closeTags: false,
749
752
  codeFolding: false,
750
753
  highlightActiveLine: false,
751
754
  highlightSelectionMatches: false,
@@ -1070,6 +1073,30 @@ registerCloseBrackets();
1070
1073
 
1071
1074
  </details>
1072
1075
 
1076
+ ### closeTags
1077
+
1078
+ <details>
1079
+ <summary>Expand</summary>
1080
+
1081
+ *version added: 3.12.0*
1082
+
1083
+ Automatically close HTML/XML tags.
1084
+
1085
+ For granular control over the bundled extensions, you need to register this extension for specific languages([HTML](#html), [MediaWiki](#mediawiki) or [Vue](#vue)):
1086
+
1087
+ ```js
1088
+ import {
1089
+ registerCloseTagsForHTML,
1090
+ registerCloseTagsForMediaWiki,
1091
+ registerCloseTagsForVue,
1092
+ } from '@bhsd/codemirror-mediawiki';
1093
+ registerCloseTagsForHTML();
1094
+ registerCloseTagsForMediaWiki();
1095
+ registerCloseTagsForVue();
1096
+ ```
1097
+
1098
+ </details>
1099
+
1073
1100
  ### codeFolding
1074
1101
 
1075
1102
  <details>
@@ -1105,11 +1132,19 @@ registerCodeFolding();
1105
1132
 
1106
1133
  Provide color pickers for CSS and MediaWiki modes.
1107
1134
 
1108
- For granular control over the bundled extensions, you can import the `registerColorPicker` functions. Note that you also need to register this extension for specific languages([CSS](#css), [MediaWiki](#mediawiki) or [Vue](#vue)):
1135
+ For granular control over the bundled extensions, you need to register this extension for specific languages([CSS](#css), [HTML](#html), [MediaWiki](#mediawiki) or [Vue](#vue)):
1109
1136
 
1110
1137
  ```js
1111
- import {registerColorPicker} from '@bhsd/codemirror-mediawiki';
1112
- registerColorPicker();
1138
+ import {
1139
+ registerColorPickerForCSS,
1140
+ registerColorPickerForHTML,
1141
+ registerColorPickerForMediaWiki,
1142
+ registerColorPickerForVue,
1143
+ } from '@bhsd/codemirror-mediawiki';
1144
+ registerColorPickerForCSS();
1145
+ registerColorPickerForHTML();
1146
+ registerColorPickerForMediaWiki();
1147
+ registerColorPickerForVue();
1113
1148
  ```
1114
1149
 
1115
1150
  </details>
@@ -0,0 +1,3 @@
1
+ import type { Extension } from '@codemirror/state';
2
+ declare const _default: () => Extension;
3
+ export default _default;
@@ -0,0 +1,31 @@
1
+ import { EditorView } from '@codemirror/view';
2
+ import { syntaxTree } from '@codemirror/language';
3
+ import { getTag, searchTag } from './matchTag.js';
4
+ import { hasTag } from './mediawiki.js';
5
+ const brackets = ['extTagBracket', 'htmlTagBracket'];
6
+ export default () => EditorView.inputHandler.of((view, from, to, text, insertTransaction) => {
7
+ if (view.composing || view.state.readOnly || from !== to || text !== '>') {
8
+ return false;
9
+ }
10
+ const base = insertTransaction(), { state } = base, tree = syntaxTree(state), closeTags = state.changeByRange(range => {
11
+ const didType = state.sliceDoc(range.from - 1, range.to) === text, { head } = range, after = tree.resolveInner(head, -1);
12
+ if (didType && hasTag(after.name, brackets) && head === after.from + 1) {
13
+ const tag = getTag(state, after.prevSibling);
14
+ if (tag && !tag.closing && !tag.selfClosing && !searchTag(state, tag)) {
15
+ return {
16
+ range,
17
+ changes: { from: head, to: head, insert: `</${tag.name}>` },
18
+ };
19
+ }
20
+ }
21
+ return { range };
22
+ });
23
+ if (closeTags.changes.empty) {
24
+ return false;
25
+ }
26
+ view.dispatch([
27
+ base,
28
+ state.update(closeTags, { userEvent: 'input.complete', scrollIntoView: true }),
29
+ ]);
30
+ return true;
31
+ });
@@ -1,6 +1,6 @@
1
1
  import { EditorView } from '@codemirror/view';
2
- import type { KeyBinding } from '@codemirror/view';
3
- import type { Extension } from '@codemirror/state';
2
+ import type { ViewPlugin, KeyBinding, DecorationSet } from '@codemirror/view';
3
+ import type { Extension, StateField } from '@codemirror/state';
4
4
  import type { SyntaxNode } from '@lezer/common';
5
5
  import type { ConfigGetter } from '@bhsd/browser';
6
6
  import type { ConfigData } from 'wikiparser-node';
@@ -20,6 +20,18 @@ declare interface MenuItem {
20
20
  isActionable(this: void, cm: CodeMirror6): boolean;
21
21
  getItems(this: void, cm: CodeMirror6): HTMLElement[];
22
22
  }
23
+ declare type LintExtension = [
24
+ unknown,
25
+ ViewPlugin<{
26
+ set: boolean;
27
+ force(): void;
28
+ }>,
29
+ [
30
+ StateField<{
31
+ diagnostics: DecorationSet;
32
+ }>
33
+ ]
34
+ ];
23
35
  declare interface OptionalFunctions {
24
36
  statusBar: typeof statusBar;
25
37
  detectIndent: typeof detectIndent;
@@ -82,6 +94,8 @@ export declare class CodeMirror6 {
82
94
  * @param lintSource function for syntax checking
83
95
  */
84
96
  lint(lintSource?: LintSources): void;
97
+ /** @private */
98
+ getLintExtension(): LintExtension | undefined;
85
99
  /** Update syntax checking immediately */
86
100
  update(): void;
87
101
  /**
@@ -2,11 +2,12 @@ import { EditorView, lineNumbers, keymap, highlightActiveLineGutter } from '@cod
2
2
  import { EditorSelection, Compartment, EditorState, SelectionRange, } from '@codemirror/state';
3
3
  import { syntaxHighlighting, defaultHighlightStyle, indentOnInput, indentUnit, ensureSyntaxTree, syntaxTree, } from '@codemirror/language';
4
4
  import { defaultKeymap, historyKeymap, history, redo, indentWithTab, insertNewlineKeepIndent, deleteCharBackwardStrict, } from '@codemirror/commands';
5
- import { searchKeymap } from '@codemirror/search';
6
- import { linter, lintGutter, nextDiagnostic } from '@codemirror/lint';
5
+ import { search, searchKeymap } from '@codemirror/search';
6
+ import { linter, lintGutter } from '@codemirror/lint';
7
7
  import elt from 'crelt';
8
8
  import { base, panelSelector, panelsSelector, diagnosticSelector, noDetectionLangs } from './constants.js';
9
9
  import { light } from './theme.js';
10
+ import { nextDiagnostic } from './lint.js';
10
11
  export const plain = () => [
11
12
  EditorView.contentAttributes.of({ spellcheck: 'true' }),
12
13
  keymap.of([
@@ -32,7 +33,7 @@ export const optionalFunctions = {
32
33
  return () => { };
33
34
  },
34
35
  };
35
- const editExtensions = new Set(['closeBrackets', 'autocompletion', 'signatureHelp', 'escape']);
36
+ const editExtensions = new Set(['closeBrackets', 'closeTags', 'autocompletion', 'signatureHelp', 'escape']);
36
37
  const linters = {};
37
38
  const phrases = {};
38
39
  /**
@@ -155,6 +156,23 @@ export class CodeMirror6 {
155
156
  EditorView.editorAttributes.of({ lang: l }),
156
157
  lineNumbers(),
157
158
  highlightActiveLineGutter(),
159
+ search({
160
+ scrollToMatch(range, view) {
161
+ const scrollRect = view.scrollDOM.getBoundingClientRect(), startCoords = view.coordsAtPos(range.from), endCoords = view.coordsAtPos(range.to), isInViewport = startCoords && startCoords.top >= scrollRect.top
162
+ && endCoords && endCoords.bottom <= scrollRect.bottom;
163
+ return EditorView.scrollIntoView(range, { y: isInViewport ? 'nearest' : 'center' });
164
+ },
165
+ }),
166
+ EditorView.scrollHandler.of((view, { head }, options) => {
167
+ if (options.x === 'nearest' && options.y === 'center') {
168
+ const { scrollDOM } = view, { clientHeight } = scrollDOM, { top, height } = view.lineBlockAt(head);
169
+ if (height < clientHeight - options.yMargin * 2) {
170
+ scrollDOM.scrollTop = top + (height - clientHeight) / 2;
171
+ }
172
+ options.y = 'nearest';
173
+ }
174
+ return false;
175
+ }),
158
176
  keymap.of([
159
177
  ...defaultKeymap,
160
178
  ...searchKeymap,
@@ -196,7 +214,7 @@ export class CodeMirror6 {
196
214
  if (focusChanged) {
197
215
  textarea.dispatchEvent(new FocusEvent(this.#view.hasFocus ? 'focus' : 'blur'));
198
216
  }
199
- if (selectionSet && this.lang === 'mediawiki'
217
+ GH: if (selectionSet && this.lang === 'mediawiki'
200
218
  && ['localhost:8080', 'bhsd-harry.github.io'].includes(location.host)) {
201
219
  const tree = syntaxTree(state), { head } = state.selection.main, { name } = tree.resolve(head), innerName = tree.resolveInner(head).name;
202
220
  if (name !== innerName) {
@@ -277,7 +295,7 @@ export class CodeMirror6 {
277
295
  */
278
296
  lint(lintSource) {
279
297
  const lintSources = typeof lintSource === 'function' ? [lintSource] : lintSource;
280
- const linterExtension = (cm) => lintSources
298
+ const linterExtension = (cm) => lintSources?.length
281
299
  ? [
282
300
  ...lintSources.map(source => linter(async ({ state }) => {
283
301
  const diagnostics = (await source(state)).map((diagnostic) => ({
@@ -301,7 +319,7 @@ export class CodeMirror6 {
301
319
  return diagnostics;
302
320
  })),
303
321
  lintGutter(),
304
- keymap.of([{ key: 'F8', run: nextDiagnostic }]),
322
+ keymap.of([{ key: 'F8', run: () => nextDiagnostic(this) }]),
305
323
  optionalFunctions.statusBar(cm, lintSources[0].fixer),
306
324
  ]
307
325
  : [];
@@ -318,15 +336,17 @@ export class CodeMirror6 {
318
336
  this.#minHeight(Boolean(lintSource));
319
337
  }
320
338
  }
339
+ /** @private */
340
+ getLintExtension() {
341
+ return this.#view && this.#linter.get(this.#view.state)[0];
342
+ }
321
343
  /** Update syntax checking immediately */
322
344
  update() {
323
- if (this.#view) {
324
- const [extension] = this.#linter.get(this.#view.state);
325
- if (extension) {
326
- const plugin = this.#view.plugin(extension[1]);
327
- plugin.set = true;
328
- plugin.force();
329
- }
345
+ const extension = this.getLintExtension();
346
+ if (extension) {
347
+ const plugin = this.#view.plugin(extension[1]);
348
+ plugin.set = true;
349
+ plugin.force();
330
350
  }
331
351
  }
332
352
  /**
@@ -435,7 +455,7 @@ export class CodeMirror6 {
435
455
  this.#textarea.style.display = '';
436
456
  this.#textarea.setSelectionRange(from, to, head === to ? 'forward' : 'backward');
437
457
  if (hasFocus) {
438
- this.#textarea.focus();
458
+ this.#textarea.focus({ preventScroll: true });
439
459
  }
440
460
  requestAnimationFrame(() => {
441
461
  this.#textarea.scrollTop = scrollTop;
package/dist/fold.js CHANGED
@@ -5,7 +5,7 @@ import { getRegex } from '@bhsd/common';
5
5
  import elt from 'crelt';
6
6
  import { tokens } from './config.js';
7
7
  import { bgDark } from './constants.js';
8
- import { matchTag, getTag } from './matchTag.js';
8
+ import { searchTag, getTag } from './matchTag.js';
9
9
  import { braceStackUpdate, sliceDoc } from './util.js';
10
10
  const getExtRegex = /* @__PURE__ */ getRegex(tag => new RegExp(`mw-tag-${tag}(?![a-z])`, 'u'));
11
11
  export const updateSelection = (pos, { to }) => Math.max(pos, to), updateAll = (pos, { from, to }) => from <= pos && to > pos ? to : pos;
@@ -80,10 +80,10 @@ export const foldable = (state, posOrNode, tree, refOnly = false) => {
80
80
  while (nextSibling && !(isExtBracket(nextSibling) && !regex.test(nextSibling.name))) {
81
81
  ({ nextSibling } = nextSibling);
82
82
  }
83
- const next = nextSibling?.nextSibling;
83
+ const next = nextSibling?.nextSibling, closing = next && getTag(state, next);
84
84
  // The closing bracket of the current extension tag
85
- if (nextSibling && (!refOnly || next && refNames.has(getTag(state, next)?.name))) {
86
- return { from: matchTag(state, nextSibling.to).end.to, to: nextSibling.from };
85
+ if (closing && (!refOnly || refNames.has(closing.name))) {
86
+ return { from: searchTag(state, closing).to, to: nextSibling.from };
87
87
  }
88
88
  }
89
89
  return false;
package/dist/html.js CHANGED
@@ -6,7 +6,7 @@ import { LanguageSupport } from '@codemirror/language';
6
6
  import { cssCompletion } from './css.js';
7
7
  import { jsCompletion, markGlobalsPlugin } from './javascript.js';
8
8
  import { mediawikiBase } from './mediawiki.js';
9
- import { getLightHighlightStyle } from './theme.js';
9
+ import { lightHighlightStyle } from './theme.js';
10
10
  export default (config, cm) => {
11
11
  const { language, support } = mediawikiBase(config),
12
12
  /** @test */
@@ -30,7 +30,7 @@ export default (config, cm) => {
30
30
  jsCompletion,
31
31
  cssCompletion(),
32
32
  support,
33
- getLightHighlightStyle(),
33
+ lightHighlightStyle,
34
34
  markGlobalsPlugin(cm),
35
35
  ]);
36
36
  Object.assign(langSupport, { nestedMWLanguage: language });
package/dist/index.d.ts CHANGED
@@ -27,7 +27,10 @@ export declare const registerAllowMultipleSelections: () => void;
27
27
  export declare const registerAutocompletion: () => void;
28
28
  /** Register the `codeFolding` extension */
29
29
  export declare const registerCodeFolding: () => void;
30
- /** Register the `colorPicker` extension */
30
+ /**
31
+ * Register the `colorPicker` extension
32
+ * @deprecated This function does nothing and will be removed in a future release
33
+ */
31
34
  export declare const registerColorPicker: () => void;
32
35
  /** Register all common extensions */
33
36
  export declare const registerCommonExtensions: () => void;
@@ -78,6 +81,8 @@ export declare const registerColorPickerForMediaWiki: () => void;
78
81
  export declare const registerBracketMatchingForMediaWiki: () => void;
79
82
  /** Register the `codeFolding` extension for MediaWiki */
80
83
  export declare const registerCodeFoldingForMediaWiki: () => void;
84
+ /** Register the `closeTags` extension for MediaWiki */
85
+ export declare const registerCloseTagsForMediaWiki: () => void;
81
86
  /**
82
87
  * Register MediaWiki core language support
83
88
  * @param articlePath article path (e.g., 'https://www.mediawiki.org/wiki/')
@@ -89,14 +94,21 @@ export declare const registerMediaWikiCore: (articlePath?: string, templatedata?
89
94
  export declare const registerHTML: () => void;
90
95
  /** Register the `bracketMatching` extension for mixed MediaWiki-HTML */
91
96
  export declare const registerBracketMatchingForHTML: () => void;
92
- /** Register the `closeBrackets` extension for mixed MediaWiki-HTML */
97
+ /**
98
+ * Register the `closeBrackets` extension for mixed MediaWiki-HTML
99
+ * @deprecated This function does nothing and will be removed in a future release
100
+ */
93
101
  export declare const registerCloseBracketsForHTML: () => void;
102
+ /** Register the `closeTags` extension for mixed MediaWiki-HTML */
103
+ export declare const registerCloseTagsForHTML: () => void;
94
104
  /** Register the `colorPicker` extension for mixed MediaWiki-HTML */
95
105
  export declare const registerColorPickerForHTML: () => void;
96
106
  /** Register mixed MediaWiki-HTML core language support */
97
107
  export declare const registerHTMLCore: () => void;
98
108
  /** Register JavaScript language support */
99
109
  export declare const registerJavaScript: () => void;
110
+ /** Register the `bracketMatching` extension for JavaScript */
111
+ export declare const registerBracketMatchingForJavaScript: () => void;
100
112
  /** Register JavaScript core language support */
101
113
  export declare const registerJavaScriptCore: () => void;
102
114
  /** Register CSS language support */
@@ -115,8 +127,15 @@ export declare const registerLua: () => void;
115
127
  export declare const registerLuaCore: () => void;
116
128
  /** Register Vue language support */
117
129
  export declare const registerVue: () => void;
118
- /** Register the `closeBrackets` extension for Vue */
130
+ /** Register the `bracketMatching` extension for Vue */
131
+ export declare const registerBracketMatchingForVue: () => void;
132
+ /**
133
+ * Register the `closeBrackets` extension for Vue
134
+ * @deprecated This function does nothing and will be removed in a future release
135
+ */
119
136
  export declare const registerCloseBracketsForVue: () => void;
137
+ /** Register the `closeTags` extension for Vue */
138
+ export declare const registerCloseTagsForVue: () => void;
120
139
  /** Register the `colorPicker` extension for Vue */
121
140
  export declare const registerColorPickerForVue: () => void;
122
141
  /** Register Vue core language support */
package/dist/index.js CHANGED
@@ -8,6 +8,7 @@ import { abusefilter, analyzer } from '@bhsd/lezer-abusefilter';
8
8
  import { getLSP } from '@bhsd/browser';
9
9
  import { colorPicker } from '@bhsd/codemirror-css-color-picker';
10
10
  import bidiIsolates from './bidi.js';
11
+ import closeTags from './closeTags.js';
11
12
  import { CodeMirror6, avail, languages, linterRegistry, destroyListeners, plain, optionalFunctions, themes, } from './codemirror.js';
12
13
  import mediawikiColorPicker from './color.js';
13
14
  import escapeKeymap from './escape.js';
@@ -27,7 +28,7 @@ import { tagModes, getStaticMwConfig } from './static.js';
27
28
  import statusBar from './statusBar.js';
28
29
  import css from './css.js';
29
30
  import html from './html.js';
30
- import javascript from './javascript.js';
31
+ import javascript, { exclude } from './javascript.js';
31
32
  import lua from './lua.js';
32
33
  import vue from './vue.js';
33
34
  export { CodeMirror6 };
@@ -70,7 +71,7 @@ export const registerBracketMatching = () => {
70
71
  };
71
72
  /** Register the `closeBrackets` extension */
72
73
  export const registerCloseBrackets = () => {
73
- registerExtension('closeBrackets', (e = []) => [closeBrackets(), e]);
74
+ registerExtension('closeBrackets', closeBrackets);
74
75
  };
75
76
  /** Register the `scrollPastEnd` extension */
76
77
  export const registerScrollPastEnd = () => {
@@ -100,12 +101,13 @@ export const registerAutocompletion = () => {
100
101
  export const registerCodeFolding = () => {
101
102
  registerExtension('codeFolding', codeFolding);
102
103
  };
103
- /** Register the `colorPicker` extension */
104
- export const registerColorPicker = () => {
105
- registerExtension('colorPicker', (e = []) => e);
106
- };
107
- /** 注册所有通用扩展(除`colorPicker`) */
108
- const registerExtensions = () => {
104
+ /**
105
+ * Register the `colorPicker` extension
106
+ * @deprecated This function does nothing and will be removed in a future release
107
+ */
108
+ export const registerColorPicker = () => { };
109
+ /** Register all common extensions */
110
+ export const registerCommonExtensions = () => {
109
111
  registerHighlightSpecialChars();
110
112
  registerHighlightActiveLine();
111
113
  registerHighlightWhitespace();
@@ -118,15 +120,15 @@ const registerExtensions = () => {
118
120
  registerAutocompletion();
119
121
  registerCodeFolding();
120
122
  };
121
- /** Register all common extensions */
122
- export const registerCommonExtensions = () => {
123
- registerExtensions();
124
- registerColorPicker();
125
- };
123
+ /**
124
+ * 各语言独立定义的扩展
125
+ * @param e 扩展
126
+ */
127
+ const langExtension = (e = []) => e;
126
128
  function mediawikiOnly(ext) {
127
129
  return typeof ext === 'function'
128
130
  ? [(enable, cm) => enable ? ext(cm) : [], { mediawiki: true }]
129
- : [(e = []) => e, { mediawiki: ext }];
131
+ : [langExtension, { mediawiki: ext }];
130
132
  }
131
133
  /**
132
134
  * 注册特定语言的扩展
@@ -135,7 +137,7 @@ function mediawikiOnly(ext) {
135
137
  * @param ext 扩展
136
138
  */
137
139
  const registerLangExtension = (lang, name, ext) => {
138
- avail[name] ??= [() => []];
140
+ avail[name] ??= [langExtension];
139
141
  const addon = avail[name];
140
142
  addon[1] ??= {};
141
143
  addon[1][lang] = ext;
@@ -158,6 +160,7 @@ export const registerMediaWiki = (articlePath, templatedata) => {
158
160
  registerColorPickerForMediaWiki();
159
161
  registerBracketMatchingForMediaWiki();
160
162
  registerCodeFoldingForMediaWiki();
163
+ registerCloseTagsForMediaWiki();
161
164
  };
162
165
  /**
163
166
  * 注册MediaWiki专用扩展
@@ -231,6 +234,10 @@ export const registerCodeFoldingForMediaWiki = () => {
231
234
  registerLangExtension('mediawiki', 'codeFolding', mediawikiFold);
232
235
  optionalFunctions.foldHandler = foldHandler;
233
236
  };
237
+ /** Register the `closeTags` extension for MediaWiki */
238
+ export const registerCloseTagsForMediaWiki = () => {
239
+ registerLangExtension('mediawiki', 'closeTags', closeTags());
240
+ };
234
241
  /**
235
242
  * 注册LintSource
236
243
  * @param lang 语言
@@ -265,19 +272,24 @@ export const registerHTML = () => {
265
272
  registerCommonExtensions();
266
273
  registerHTMLCore();
267
274
  registerBracketMatchingForHTML();
268
- registerCloseBracketsForHTML();
275
+ registerCloseTagsForHTML();
269
276
  registerColorPickerForHTML();
270
277
  };
271
278
  /** Register the `bracketMatching` extension for mixed MediaWiki-HTML */
272
279
  export const registerBracketMatchingForHTML = () => {
273
280
  registerLangExtension('html', 'bracketMatching', [
274
- {},
281
+ { exclude },
275
282
  tagMatchingState,
276
283
  ]);
277
284
  };
278
- /** Register the `closeBrackets` extension for mixed MediaWiki-HTML */
279
- export const registerCloseBracketsForHTML = () => {
280
- registerLangExtension('html', 'closeBrackets', autoCloseTags);
285
+ /**
286
+ * Register the `closeBrackets` extension for mixed MediaWiki-HTML
287
+ * @deprecated This function does nothing and will be removed in a future release
288
+ */
289
+ export const registerCloseBracketsForHTML = () => { };
290
+ /** Register the `closeTags` extension for mixed MediaWiki-HTML */
291
+ export const registerCloseTagsForHTML = () => {
292
+ registerLangExtension('html', 'closeTags', autoCloseTags);
281
293
  };
282
294
  /** Register the `colorPicker` extension for mixed MediaWiki-HTML */
283
295
  export const registerColorPickerForHTML = () => {
@@ -291,8 +303,13 @@ export const registerHTMLCore = () => {
291
303
  };
292
304
  /** Register JavaScript language support */
293
305
  export const registerJavaScript = () => {
294
- registerExtensions();
306
+ registerCommonExtensions();
295
307
  registerJavaScriptCore();
308
+ registerBracketMatchingForJavaScript();
309
+ };
310
+ /** Register the `bracketMatching` extension for JavaScript */
311
+ export const registerBracketMatchingForJavaScript = () => {
312
+ registerLangExtension('javascript', 'bracketMatching', [{ exclude }]);
296
313
  };
297
314
  /** Register JavaScript core language support */
298
315
  export const registerJavaScriptCore = () => {
@@ -318,7 +335,7 @@ export const registerCSSCore = () => {
318
335
  };
319
336
  /** Register JSON language support */
320
337
  export const registerJSON = () => {
321
- registerExtensions();
338
+ registerCommonExtensions();
322
339
  registerJSONCore();
323
340
  };
324
341
  /** Register JSON core language support */
@@ -329,7 +346,7 @@ export const registerJSONCore = () => {
329
346
  };
330
347
  /** Register Lua language support */
331
348
  export const registerLua = () => {
332
- registerExtensions();
349
+ registerCommonExtensions();
333
350
  registerLuaCore();
334
351
  };
335
352
  /** Register Lua core language support */
@@ -342,12 +359,22 @@ export const registerLuaCore = () => {
342
359
  export const registerVue = () => {
343
360
  registerCommonExtensions();
344
361
  registerVueCore();
345
- registerCloseBracketsForVue();
362
+ registerBracketMatchingForVue();
363
+ registerCloseTagsForVue();
346
364
  registerColorPickerForVue();
347
365
  };
348
- /** Register the `closeBrackets` extension for Vue */
349
- export const registerCloseBracketsForVue = () => {
350
- registerLangExtension('vue', 'closeBrackets', autoCloseTags);
366
+ /** Register the `bracketMatching` extension for Vue */
367
+ export const registerBracketMatchingForVue = () => {
368
+ registerLangExtension('vue', 'bracketMatching', [{ exclude }]);
369
+ };
370
+ /**
371
+ * Register the `closeBrackets` extension for Vue
372
+ * @deprecated This function does nothing and will be removed in a future release
373
+ */
374
+ export const registerCloseBracketsForVue = () => { };
375
+ /** Register the `closeTags` extension for Vue */
376
+ export const registerCloseTagsForVue = () => {
377
+ registerLangExtension('vue', 'closeTags', autoCloseTags);
351
378
  };
352
379
  /** Register the `colorPicker` extension for Vue */
353
380
  export const registerColorPickerForVue = () => {
@@ -361,7 +388,7 @@ export const registerVueCore = () => {
361
388
  };
362
389
  /** Register AbuseFilter language support */
363
390
  export const registerAbuseFilter = () => {
364
- registerExtensions();
391
+ registerCommonExtensions();
365
392
  registerAbuseFilterCore();
366
393
  };
367
394
  /** Register AbuseFilter core language support */
@@ -4,6 +4,13 @@ import type { Tree } from '@lezer/common';
4
4
  import type { DocRange } from './fold';
5
5
  import type { CodeMirror6 } from './codemirror';
6
6
  export declare const jsCompletion: Extension;
7
+ /**
8
+ * 忽略JavaScript正则表达式中的括号匹配
9
+ * @param state
10
+ * @param pos 位置
11
+ * @test
12
+ */
13
+ export declare const exclude: (state: EditorState, pos: number) => boolean;
7
14
  /**
8
15
  * 高亮显示全局变量
9
16
  * @ignore
@@ -5,6 +5,14 @@ import { setDiagnosticsEffect } from '@codemirror/lint';
5
5
  import { builtin } from './javascript-globals.js';
6
6
  export const jsCompletion = javascriptLanguage.data.of({ autocomplete: scopeCompletionSource(globalThis) });
7
7
  const globalsMark = Decoration.mark({ class: 'cm-globals' }), builtinGlobals = new Set(Object.keys(builtin));
8
+ /**
9
+ * 忽略JavaScript正则表达式中的括号匹配
10
+ * @param state
11
+ * @param pos 位置
12
+ * @test
13
+ */
14
+ export const exclude = (state, pos) => javascriptLanguage.isActiveAt(state, pos, 0)
15
+ && syntaxTree(state).resolveInner(pos, 0).name === 'RegExp';
8
16
  /**
9
17
  * 高亮显示全局变量
10
18
  * @ignore
package/dist/lint.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import type { CodeMirror6 } from './codemirror';
2
+ /**
3
+ * 选中下一个诊断的范围
4
+ * @param cm CodeMirror6 实例
5
+ */
6
+ export declare const nextDiagnostic: (cm: CodeMirror6) => boolean;
package/dist/lint.js ADDED
@@ -0,0 +1,28 @@
1
+ const findDiagnostic = (deco, head, anchor = head) => {
2
+ let found;
3
+ deco.between(head, Infinity, (_, __, { spec: { diagnostics } }) => {
4
+ const next = diagnostics.sort((a, b) => a.from - b.from || a.to - b.to)
5
+ .find(({ from, to }) => from > head || from === head && to > anchor);
6
+ if (next) {
7
+ found = next;
8
+ return false;
9
+ }
10
+ return undefined;
11
+ });
12
+ return found;
13
+ };
14
+ /**
15
+ * 选中下一个诊断的范围
16
+ * @param cm CodeMirror6 实例
17
+ */
18
+ export const nextDiagnostic = (cm) => {
19
+ const view = cm.view, { state } = view, { diagnostics } = state.field(cm.getLintExtension()[2][0]), { from, to } = state.selection.main, next = findDiagnostic(diagnostics, from, to) ?? findDiagnostic(diagnostics, 0);
20
+ if (!next || next.from === from && next.to === to) {
21
+ return false;
22
+ }
23
+ view.dispatch({
24
+ selection: { anchor: next.from, head: next.to },
25
+ scrollIntoView: true,
26
+ });
27
+ return true;
28
+ };
package/dist/linter.js CHANGED
@@ -1,5 +1,3 @@
1
- /** @todo revert df69718ab908966bff162fe51e8cfb4595e6b2ec */
2
- import { rules as recommended } from '@eslint/js/src/configs/eslint-recommended.js';
3
1
  import { sanitizeInlineStyle } from '@bhsd/common';
4
2
  import { loadScript, getWikiparse, getLSP } from '@bhsd/browser';
5
3
  import { styleLint } from '@bhsd/stylelint-util';
@@ -131,6 +129,9 @@ export const jsConfig = /* #__PURE__ */ (() => ({
131
129
  * @test
132
130
  */
133
131
  export const getJsLinter = async (cdn = eslintRepo) => {
132
+ /** @todo revert df69718ab908966bff162fe51e8cfb4595e6b2ec */
133
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
134
+ const { rules: recommended } = require('@eslint/js/src/configs/eslint-recommended.js');
134
135
  await loadScript(cdn, 'eslint');
135
136
  /** @see https://www.npmjs.com/package/@codemirror/lang-javascript */
136
137
  const esLinter = new eslint.Linter(), conf = {