@bhsd/codemirror-mediawiki 3.12.0 → 3.12.2

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/LICENSE CHANGED
@@ -1,3 +1,22 @@
1
+ @bhsd/codemirror-mediawiki
2
+ Copyright (C) 2022 Bhsd
3
+
4
+ This program is free software; you can redistribute it and/or modify
5
+ it under the terms of the GNU General Public License as published by
6
+ the Free Software Foundation; either version 2 of the License, or
7
+ (at your option) any later version.
8
+
9
+ This program is distributed in the hope that it will be useful,
10
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ GNU General Public License for more details.
13
+
14
+ You should have received a copy of the GNU General Public License along
15
+ with this program; if not, write to the Free Software Foundation, Inc.,
16
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
+
18
+
19
+
1
20
  GNU GENERAL PUBLIC LICENSE
2
21
  Version 2, June 1991
3
22
 
package/dist/bidi.js CHANGED
@@ -4,10 +4,11 @@
4
4
  * @see https://gerrit.wikimedia.org/g/mediawiki/extensions/CodeMirror
5
5
  */
6
6
  import { EditorView, Direction, ViewPlugin, Decoration } from '@codemirror/view';
7
- import { Prec, RangeSetBuilder } from '@codemirror/state';
7
+ import { Prec } from '@codemirror/state';
8
8
  import { syntaxTree } from '@codemirror/language';
9
9
  import { tokens } from './config.js';
10
10
  import { getTag } from './matchTag.js';
11
+ import { pushDecoration } from './util.js';
11
12
  const isolateSelector = '.cm-bidi-isolate', ltrSelector = '.cm-bidi-ltr', cls = isolateSelector.slice(1), isolateLTR = Decoration.mark({
12
13
  class: `${cls} ${ltrSelector.slice(1)}`,
13
14
  bidiIsolate: Direction.LTR,
@@ -18,7 +19,7 @@ const isolateSelector = '.cm-bidi-isolate', ltrSelector = '.cm-bidi-ltr', cls =
18
19
  * @test
19
20
  */
20
21
  export const computeIsolates = ({ visibleRanges, state, textDirection }) => {
21
- const set = new RangeSetBuilder();
22
+ const set = [];
22
23
  if (textDirection === Direction.RTL) {
23
24
  for (const { from, to } of visibleRanges) {
24
25
  let node = syntaxTree(state).resolve(from, 1), td = 0, table = 0, parameter = 0;
@@ -27,13 +28,13 @@ export const computeIsolates = ({ visibleRanges, state, textDirection }) => {
27
28
  if (/-(?:ext|html)tag-bracket/u.test(name) && state.sliceDoc(f, t).includes('<')) {
28
29
  const tag = getTag(state, nextSibling);
29
30
  if (tag) {
30
- set.add(tag.from, tag.to, isolateLTR);
31
+ pushDecoration(set, isolateLTR, tag);
31
32
  }
32
33
  }
33
34
  else if (!td && !table && name.includes(tokens.tableDefinition)) {
34
35
  if (/-html-(?:table|tr)/u.test(name)) {
35
36
  table = state.doc.lineAt(f).to;
36
- set.add(f, table, isolateLTR);
37
+ pushDecoration(set, isolateLTR, f, table);
37
38
  }
38
39
  else {
39
40
  td = f;
@@ -43,18 +44,18 @@ export const computeIsolates = ({ visibleRanges, state, textDirection }) => {
43
44
  table = 0;
44
45
  }
45
46
  else if (td && name.includes(tokens.tableDelimiter2)) {
46
- set.add(td, f, isolateLTR);
47
+ pushDecoration(set, isolateLTR, td, f);
47
48
  td = 0;
48
49
  }
49
50
  else if (/-(?:template|parserfunction)-delimiter/u.test(name)) {
50
51
  if (parameter) {
51
- set.add(parameter, f, isolate);
52
+ pushDecoration(set, isolate, parameter, f);
52
53
  }
53
54
  parameter = t;
54
55
  }
55
56
  else if (parameter && /-(?:template|parserfunction)-bracket/u.test(name)) {
56
57
  if (state.sliceDoc(f, f + 1) === '}') {
57
- set.add(parameter, f, isolate);
58
+ pushDecoration(set, isolate, parameter, f);
58
59
  }
59
60
  parameter = 0;
60
61
  }
@@ -62,7 +63,7 @@ export const computeIsolates = ({ visibleRanges, state, textDirection }) => {
62
63
  }
63
64
  }
64
65
  }
65
- return set.finish();
66
+ return Decoration.set(set, true);
66
67
  };
67
68
  export default [
68
69
  ViewPlugin.fromClass(class {
@@ -5,7 +5,7 @@ import { defaultKeymap, historyKeymap, history, redo, indentWithTab, insertNewli
5
5
  import { search, searchKeymap } from '@codemirror/search';
6
6
  import { linter, lintGutter } from '@codemirror/lint';
7
7
  import elt from 'crelt';
8
- import { base, panelSelector, panelsSelector, diagnosticSelector, noDetectionLangs } from './constants.js';
8
+ import { baseData, panelSelector, panelsSelector, diagnosticSelector, noDetectionLangs } from './constants.js';
9
9
  import { light } from './theme.js';
10
10
  import { nextDiagnostic } from './lint.js';
11
11
  export const plain = () => [
@@ -62,10 +62,10 @@ export const replaceSelections = (view, func) => {
62
62
  /** CodeMirror 6 editor */
63
63
  export class CodeMirror6 {
64
64
  static get CDN() {
65
- return base.CDN;
65
+ return baseData.CDN;
66
66
  }
67
67
  static set CDN(url) {
68
- base.CDN = url;
68
+ baseData.CDN = url;
69
69
  }
70
70
  #textarea;
71
71
  #language = new Compartment();
@@ -301,10 +301,10 @@ export class CodeMirror6 {
301
301
  const diagnostics = (await source(state)).map((diagnostic) => ({
302
302
  ...diagnostic,
303
303
  renderMessage(view) {
304
- const span = elt('span', { class: diagnosticSelector.slice(1) }, diagnostic.renderMessage?.(view) ?? diagnostic.message);
304
+ const span = elt('span', { class: diagnosticSelector.slice(1) }, diagnostic.renderMessage?.call(this, view) ?? this.message);
305
305
  span.addEventListener('click', () => {
306
306
  view.dispatch({
307
- selection: { anchor: diagnostic.from, head: diagnostic.to },
307
+ selection: { anchor: this.from, head: this.to },
308
308
  });
309
309
  view.focus();
310
310
  });
@@ -1,3 +1,4 @@
1
- export declare const base: Record<'CDN', string | undefined>, hoverSelector = ".cm-tooltip-hover-mw", diagnosticSelector = ".cm-diagnosticText-clickable", panelSelector = ".cm-panel", panelsSelector = ".cm-panels", actionSelector = ".cm-diagnosticAction", noDetectionLangs: Set<string>, bgDark = "#4c566a", matchingCls = "cm-matchingTag", nonmatchingCls = "cm-nonmatchingTag";
1
+ import { Decoration } from '@codemirror/view';
2
+ export declare const baseData: Record<'CDN', string | undefined>, hoverSelector = ".cm-tooltip-hover-mw", diagnosticSelector = ".cm-diagnosticText-clickable", panelSelector = ".cm-panel", panelsSelector = ".cm-panels", actionSelector = ".cm-diagnosticAction", doctagMark: Decoration, typeMark: Decoration, noDetectionLangs: Set<string>, bgDark = "#4c566a", matchingCls = "cm-matchingTag", nonmatchingCls = "cm-nonmatchingTag";
2
3
  export declare const isWMF: boolean;
3
4
  export declare const isMac: boolean;
package/dist/constants.js CHANGED
@@ -1,5 +1,6 @@
1
+ import { Decoration } from '@codemirror/view';
1
2
  import { wmf } from '@bhsd/common';
2
- export const base = { CDN: undefined }, hoverSelector = '.cm-tooltip-hover-mw', diagnosticSelector = '.cm-diagnosticText-clickable', panelSelector = '.cm-panel', panelsSelector = '.cm-panels', actionSelector = '.cm-diagnosticAction', noDetectionLangs = new Set(['plain', 'mediawiki']), bgDark = '#4c566a', matchingCls = 'cm-matchingTag', nonmatchingCls = 'cm-nonmatchingTag';
3
+ export const baseData = { CDN: undefined }, hoverSelector = '.cm-tooltip-hover-mw', diagnosticSelector = '.cm-diagnosticText-clickable', panelSelector = '.cm-panel', panelsSelector = '.cm-panels', actionSelector = '.cm-diagnosticAction', doctagMark = /* @__PURE__ */ Decoration.mark({ class: 'cm-doctag' }), typeMark = /* @__PURE__ */ Decoration.mark({ class: 'cm-doctag-type' }), noDetectionLangs = new Set(['plain', 'mediawiki']), bgDark = '#4c566a', matchingCls = 'cm-matchingTag', nonmatchingCls = 'cm-nonmatchingTag';
3
4
  export const isWMF = /* @__PURE__ */ (() => typeof location === 'object'
4
5
  && new RegExp(String.raw `\.(?:${wmf})\.org$`, 'u').test(location.hostname))();
5
6
  export const isMac = /* @__PURE__ */ (() => {
package/dist/escape.js CHANGED
@@ -3,7 +3,7 @@ import { EditorSelection } from '@codemirror/state';
3
3
  import { indentMore, indentLess } from '@codemirror/commands';
4
4
  import { getLSP } from '@bhsd/browser';
5
5
  import elt from 'crelt';
6
- import { base } from './constants.js';
6
+ import { baseData } from './constants.js';
7
7
  import { replaceSelections, menuRegistry, } from './codemirror.js';
8
8
  import { sliceDoc, toConfigGetter, } from './util.js';
9
9
  const entity = { '"': 'quot', "'": 'apos', '<': 'lt', '>': 'gt', '&': 'amp', ' ': 'nbsp' };
@@ -70,7 +70,7 @@ escapeWiki = async (view, lsp) => {
70
70
  }));
71
71
  };
72
72
  const escapeWikiCommand = (view, getConfig) => {
73
- const lsp = getLSP(view, false, getConfig, base.CDN);
73
+ const lsp = getLSP(view, false, getConfig, baseData.CDN);
74
74
  if (lsp && 'provideRefactoringAction' in lsp && view.state.selection.ranges.some(({ empty }) => !empty)) {
75
75
  void escapeWiki(view, lsp);
76
76
  return true;
@@ -99,7 +99,7 @@ menuRegistry.push({
99
99
  handlerBase(view, e);
100
100
  });
101
101
  items = [btnHTML, btnURI];
102
- const lsp = getLSP(view, false, cm.getWikiConfig, base.CDN);
102
+ const lsp = getLSP(view, false, cm.getWikiConfig, baseData.CDN);
103
103
  if (lsp && 'provideRefactoringAction' in lsp) {
104
104
  const btnWiki = elt('div', 'Escape with magic words');
105
105
  btnWiki.addEventListener('click', e => {
package/dist/fold.d.ts CHANGED
@@ -17,7 +17,7 @@ export declare const updateSelection: AnchorUpdate, updateAll: AnchorUpdate;
17
17
  * @param refOnly 是否仅检查`<ref>`标签
18
18
  * @test
19
19
  */
20
- export declare const foldable: (state: EditorState, posOrNode: number | SyntaxNode, tree?: Tree | null, refOnly?: boolean) => DocRange | false;
20
+ export declare const foldableInline: (state: EditorState, posOrNode: number | SyntaxNode, tree?: Tree | null, refOnly?: boolean) => DocRange | false;
21
21
  /**
22
22
  * 折叠所有模板
23
23
  * @param state
@@ -31,7 +31,7 @@ export declare const foldable: (state: EditorState, posOrNode: number | SyntaxNo
31
31
  * @test
32
32
  */
33
33
  export declare const traverse: (state: EditorState, tree: Tree, effects: StateEffect<DocRange>[], node: SyntaxNode | null, end: number, anchor: number, update: AnchorUpdate, refOnly?: boolean) => number;
34
- declare class FoldMarker extends GutterMarker {
34
+ declare class MyFoldMarker extends GutterMarker {
35
35
  readonly open: boolean;
36
36
  constructor(open: boolean);
37
37
  eq(other: this): boolean;
@@ -48,7 +48,7 @@ export declare const foldableLine: ({ state, viewportLineBlocks }: EditorView, {
48
48
  * @param view
49
49
  * @test
50
50
  */
51
- export declare const buildMarkers: (view: EditorView) => RangeSet<FoldMarker>;
51
+ export declare const buildMarkers: (view: EditorView) => RangeSet<MyFoldMarker>;
52
52
  /**
53
53
  * 生成折叠命令
54
54
  * @param refOnly 是否仅检查`<ref>`标签
@@ -62,7 +62,7 @@ export declare const unfoldRef: Command;
62
62
  * @param view
63
63
  * @test
64
64
  */
65
- export declare const selectedLines: (view: EditorView) => BlockInfo[];
65
+ export declare const mySelectedLines: (view: EditorView) => BlockInfo[];
66
66
  /**
67
67
  * Fold the template at the selection/cursor
68
68
  * @param view
package/dist/fold.js CHANGED
@@ -47,7 +47,7 @@ const refNames = new Set(['ref', 'references']);
47
47
  * @param refOnly 是否仅检查`<ref>`标签
48
48
  * @test
49
49
  */
50
- export const foldable = (state, posOrNode, tree, refOnly = false) => {
50
+ export const foldableInline = (state, posOrNode, tree, refOnly = false) => {
51
51
  if (typeof posOrNode === 'number') {
52
52
  tree = ensureSyntaxTree(state, posOrNode);
53
53
  }
@@ -141,7 +141,7 @@ const foldSelector = '.cm-tooltip-fold';
141
141
  * @param state
142
142
  */
143
143
  const create = (state) => {
144
- const { head } = state.selection.main, range = foldable(state, head);
144
+ const { head } = state.selection.main, range = foldableInline(state, head);
145
145
  if (range) {
146
146
  const { from, to } = range;
147
147
  let folded = false;
@@ -207,7 +207,7 @@ export const traverse = (state, tree, effects, node, end, anchor, update, refOnl
207
207
  while (node && (node.from < end
208
208
  || node.from === end
209
209
  && !(isTemplateBracket(node) && sliceDoc(state, node).startsWith('}}')))) {
210
- const range = foldable(state, node, tree, refOnly);
210
+ const range = foldableInline(state, node, tree, refOnly);
211
211
  if (range) {
212
212
  effects.push(foldEffect.of(range));
213
213
  node = tree.resolve(range.to, 1);
@@ -219,7 +219,7 @@ export const traverse = (state, tree, effects, node, end, anchor, update, refOnl
219
219
  }
220
220
  return anchor;
221
221
  };
222
- class FoldMarker extends GutterMarker {
222
+ class MyFoldMarker extends GutterMarker {
223
223
  constructor(open) {
224
224
  super();
225
225
  this.open = open;
@@ -231,8 +231,8 @@ class FoldMarker extends GutterMarker {
231
231
  return elt('span', { title: state.phrase(this.open ? 'Fold line' : 'Unfold line') }, this.open ? '⌄' : '›');
232
232
  }
233
233
  }
234
- const canFold = /* @__PURE__ */ new FoldMarker(true), canUnfold = /* @__PURE__ */ new FoldMarker(false);
235
- const findFold = ({ state }, line) => {
234
+ const canFold = /* @__PURE__ */ new MyFoldMarker(true), canUnfold = /* @__PURE__ */ new MyFoldMarker(false);
235
+ const myFindFold = ({ state }, line) => {
236
236
  let found;
237
237
  state.field(foldState, false)?.between(line.from, line.to, (from, to) => {
238
238
  if (!found && to === line.to) {
@@ -323,7 +323,7 @@ export const buildMarkers = (view) => {
323
323
  const builder = new RangeSetBuilder();
324
324
  for (const line of view.viewportLineBlocks) {
325
325
  let mark;
326
- if (findFold(view, line)) {
326
+ if (myFindFold(view, line)) {
327
327
  mark = canUnfold;
328
328
  }
329
329
  else if (foldableLine(view, line)) {
@@ -337,14 +337,17 @@ export const buildMarkers = (view) => {
337
337
  };
338
338
  const markers = /* @__PURE__ */ ViewPlugin.fromClass(class {
339
339
  constructor(view) {
340
+ this.tree = syntaxTree(view.state);
340
341
  this.markers = buildMarkers(view);
341
342
  }
342
343
  update({ docChanged, viewportChanged, startState, state, view }) {
344
+ const tree = syntaxTree(state);
343
345
  if (docChanged
344
346
  || viewportChanged
345
347
  || startState.facet(language) !== state.facet(language)
346
348
  || startState.field(foldState, false) !== state.field(foldState, false)
347
- || syntaxTree(startState) !== syntaxTree(state)) {
349
+ || tree !== this.tree) {
350
+ this.tree = tree;
348
351
  this.markers = buildMarkers(view);
349
352
  }
350
353
  }
@@ -390,7 +393,7 @@ export const unfoldRef = (view) => {
390
393
  * @param view
391
394
  * @test
392
395
  */
393
- export const selectedLines = (view) => {
396
+ export const mySelectedLines = (view) => {
394
397
  const lines = [];
395
398
  for (const { head } of view.state.selection.ranges) {
396
399
  if (lines.some(({ from, to }) => from <= head && to >= head)) {
@@ -400,7 +403,7 @@ export const selectedLines = (view) => {
400
403
  }
401
404
  return lines;
402
405
  };
403
- const foldCode = (view, line) => {
406
+ const myFoldCode = (view, line) => {
404
407
  const range = foldableLine(view, line);
405
408
  if (range) {
406
409
  view.dispatch({ effects: foldEffect.of(range) });
@@ -408,8 +411,8 @@ const foldCode = (view, line) => {
408
411
  }
409
412
  return false;
410
413
  };
411
- const unfoldCode = (view, line) => {
412
- const folded = findFold(view, line);
414
+ const myUnfoldCode = (view, line) => {
415
+ const folded = myFindFold(view, line);
413
416
  return folded && unfoldEffect.of(folded);
414
417
  };
415
418
  /**
@@ -434,8 +437,8 @@ export const foldAt = view => {
434
437
  if (effects.length > 0) {
435
438
  return execute(view, effects, anchor);
436
439
  }
437
- for (const line of selectedLines(view)) {
438
- if (foldCode(view, line)) {
440
+ for (const line of mySelectedLines(view)) {
441
+ if (myFoldCode(view, line)) {
439
442
  return true;
440
443
  }
441
444
  }
@@ -512,8 +515,8 @@ export const mediawikiFold = /* @__PURE__ */ (() => [
512
515
  view.dispatch({ effects, selection });
513
516
  return true;
514
517
  }
515
- for (const line of selectedLines(view)) {
516
- const effect = unfoldCode(view, line);
518
+ for (const line of mySelectedLines(view)) {
519
+ const effect = myUnfoldCode(view, line);
517
520
  if (effect) {
518
521
  effects.push(effect);
519
522
  }
@@ -534,16 +537,16 @@ export const mediawikiFold = /* @__PURE__ */ (() => [
534
537
  return view.plugin(markers)?.markers ?? RangeSet.empty;
535
538
  },
536
539
  initialSpacer() {
537
- return new FoldMarker(false);
540
+ return new MyFoldMarker(false);
538
541
  },
539
542
  domEventHandlers: {
540
543
  click(view, line) {
541
- const effects = unfoldCode(view, line);
544
+ const effects = myUnfoldCode(view, line);
542
545
  if (effects) {
543
546
  view.dispatch({ effects });
544
547
  return true;
545
548
  }
546
- return foldCode(view, line);
549
+ return myFoldCode(view, line);
547
550
  },
548
551
  },
549
552
  }),
package/dist/hover.js CHANGED
@@ -2,7 +2,7 @@ import { hoverTooltip, EditorView } from '@codemirror/view';
2
2
  import { ensureSyntaxTree } from '@codemirror/language';
3
3
  import { getLSP, loadScript, } from '@bhsd/browser';
4
4
  import { tokens } from './config.js';
5
- import { base, hoverSelector, bgDark } from './constants.js';
5
+ import { baseData, hoverSelector, bgDark } from './constants.js';
6
6
  import { indexToPos, posToIndex, createTooltipView, toConfigGetter, escHTML, sliceDoc, findTemplateName, } from './util.js';
7
7
  const code = `${hoverSelector} code`;
8
8
  /**
@@ -58,13 +58,13 @@ export default (articlePath, templatedata) => (cm) => {
58
58
  hoverTooltip(async (view, pos, side) => {
59
59
  const { state } = view, { doc } = state;
60
60
  const { paramSuggest, tags } = cm.langConfig;
61
- let hover = await getLSP(view, false, toConfigGetter(cm.getWikiConfig, articlePath), base.CDN)?.provideHover(doc.toString(), indexToPos(doc, pos));
61
+ let hover = await getLSP(view, false, toConfigGetter(cm.getWikiConfig, articlePath), baseData.CDN)?.provideHover(doc.toString(), indexToPos(doc, pos));
62
62
  if (!hover && paramSuggest && 'templatedata' in tags) {
63
63
  // eslint-disable-next-line require-atomic-updates
64
64
  hover = await getHoverFromApi(state, pos, side, paramSuggest, templatedata);
65
65
  }
66
66
  if (hover) {
67
- const { CDN = '' } = base;
67
+ const { CDN = '' } = baseData;
68
68
  await loadScript(`${CDN}${CDN && '/'}npm/marked/lib/marked.umd.js`, 'marked', true);
69
69
  const { end } = hover.range;
70
70
  return {
package/dist/html.js CHANGED
@@ -4,7 +4,7 @@ import { javascript, javascriptLanguage } from '@codemirror/lang-javascript';
4
4
  import { cssLanguage } from '@codemirror/lang-css';
5
5
  import { LanguageSupport } from '@codemirror/language';
6
6
  import { cssCompletion } from './css.js';
7
- import { jsCompletion, markGlobalsPlugin } from './javascript.js';
7
+ import { jsCompletion, markGlobalsAndDocTagPlugin } from './javascript.js';
8
8
  import { mediawikiBase } from './mediawiki.js';
9
9
  import { lightHighlightStyle } from './theme.js';
10
10
  export default (config, cm) => {
@@ -31,7 +31,7 @@ export default (config, cm) => {
31
31
  cssCompletion(),
32
32
  support,
33
33
  lightHighlightStyle,
34
- markGlobalsPlugin(cm),
34
+ markGlobalsAndDocTagPlugin(cm),
35
35
  ]);
36
36
  Object.assign(langSupport, { nestedMWLanguage: language });
37
37
  return langSupport;
package/dist/index.d.ts CHANGED
@@ -159,4 +159,4 @@ export declare const registerLanguage: (name: string, lang: (config?: unknown) =
159
159
  */
160
160
  export declare const registerLanguageCore: (name: string, lang: (config?: unknown) => LanguageSupport, lintSource?: LintSourceGetter) => void;
161
161
  export declare const registerTheme: (name: string, theme: Extension) => void;
162
- export { nord } from './theme.js';
162
+ export { nordDark as nord } from './theme.js';
package/dist/index.js CHANGED
@@ -422,4 +422,4 @@ export const registerLanguageCore = (name, lang, lintSource) => {
422
422
  export const registerTheme = (name, theme) => {
423
423
  themes[name] = theme;
424
424
  };
425
- export { nord } from './theme.js';
425
+ export { nordDark as nord } from './theme.js';
package/dist/inlay.js CHANGED
@@ -2,7 +2,7 @@ import { StateField, StateEffect } from '@codemirror/state';
2
2
  import { Decoration, EditorView, WidgetType, ViewPlugin } from '@codemirror/view';
3
3
  import { getLSP } from '@bhsd/browser';
4
4
  import elt from 'crelt';
5
- import { base } from './constants.js';
5
+ import { baseData } from './constants.js';
6
6
  import { posToIndex, toConfigGetter, } from './util.js';
7
7
  const cls = 'cm-inlay-hint';
8
8
  class InlayHintWidget extends WidgetType {
@@ -54,7 +54,7 @@ export default (articlePath) => (cm) => {
54
54
  field,
55
55
  ViewPlugin.define(view => {
56
56
  const timer = setInterval(() => {
57
- if (getLSP(view, false, toConfigGetter(cm.getWikiConfig, articlePath), base.CDN)) {
57
+ if (getLSP(view, false, toConfigGetter(cm.getWikiConfig, articlePath), baseData.CDN)) {
58
58
  clearInterval(timer);
59
59
  void update({ view, docChanged: true });
60
60
  }
@@ -12,11 +12,11 @@ export declare const jsCompletion: Extension;
12
12
  */
13
13
  export declare const exclude: (state: EditorState, pos: number) => boolean;
14
14
  /**
15
- * 高亮显示全局变量
15
+ * 高亮显示全局变量和JSDoc标签
16
16
  * @ignore
17
17
  * @test
18
18
  */
19
- export declare const markGlobals: (tree: Tree, visibleRanges: readonly DocRange[], state: EditorState, cm?: CodeMirror6) => DecorationSet;
20
- export declare const markGlobalsPlugin: (cm?: CodeMirror6) => Extension;
19
+ export declare const markGlobalsAndDocTag: (tree: Tree, visibleRanges: readonly DocRange[], state: EditorState, cm?: CodeMirror6) => DecorationSet;
20
+ export declare const markGlobalsAndDocTagPlugin: (cm?: CodeMirror6) => Extension;
21
21
  declare const _default: (_?: unknown, cm?: CodeMirror6) => Extension;
22
22
  export default _default;
@@ -3,8 +3,10 @@ import { ViewPlugin, Decoration } from '@codemirror/view';
3
3
  import { syntaxTree } from '@codemirror/language';
4
4
  import { setDiagnosticsEffect } from '@codemirror/lint';
5
5
  import { builtin } from './javascript-globals.js';
6
+ import { doctagMark } from './constants.js';
7
+ import { markDocTagType, pushDecoration } from './util.js';
6
8
  export const jsCompletion = javascriptLanguage.data.of({ autocomplete: scopeCompletionSource(globalThis) });
7
- const globalsMark = Decoration.mark({ class: 'cm-globals' }), builtinGlobals = new Set(Object.keys(builtin));
9
+ const globalsMark = Decoration.mark({ class: 'cm-globals' }), varMark = Decoration.mark({ class: 'cm-doctag-var' }), builtinGlobals = new Set(Object.keys(builtin));
8
10
  /**
9
11
  * 忽略JavaScript正则表达式中的括号匹配
10
12
  * @param state
@@ -14,11 +16,11 @@ const globalsMark = Decoration.mark({ class: 'cm-globals' }), builtinGlobals = n
14
16
  export const exclude = (state, pos) => javascriptLanguage.isActiveAt(state, pos, 0)
15
17
  && syntaxTree(state).resolveInner(pos, 0).name === 'RegExp';
16
18
  /**
17
- * 高亮显示全局变量
19
+ * 高亮显示全局变量和JSDoc标签
18
20
  * @ignore
19
21
  * @test
20
22
  */
21
- export const markGlobals = (tree, visibleRanges, state, cm) => {
23
+ export const markGlobalsAndDocTag = (tree, visibleRanges, state, cm) => {
22
24
  const decorations = [];
23
25
  let allGlobals = builtinGlobals;
24
26
  if (cm?.lintSources.length && typeof eslint === 'object' && 'environments' in eslint) {
@@ -49,10 +51,29 @@ export const markGlobals = (tree, visibleRanges, state, cm) => {
49
51
  to,
50
52
  enter({ type, from: f, to: t }) {
51
53
  const name = state.sliceDoc(f, t);
52
- if (type.is('VariableName') && javascriptLanguage.isActiveAt(state, f) && allGlobals.has(name)) {
54
+ if (!javascriptLanguage.isActiveAt(state, f)) {
55
+ //
56
+ }
57
+ else if (type.is('VariableName') && allGlobals.has(name)) {
53
58
  const completions = localCompletionSource({ state, pos: t, explicit: true });
54
59
  if (!completions?.options.some(({ label }) => label === name)) {
55
- decorations.push(globalsMark.range(f, t));
60
+ pushDecoration(decorations, globalsMark, f, t);
61
+ }
62
+ }
63
+ else if (type.is('BlockComment') && /^\/\*{2}(?!\*)/u.test(name)) {
64
+ const comment = name.slice(2), pos = f + 2, mtAll = comment.matchAll(/^[ \t]*\*\s*(@[a-z]+)(\s+\{)?|\{(@[a-z]+)/dgimu);
65
+ for (const mt of mtAll) {
66
+ if (mt[3]) {
67
+ const [start, end] = mt.indices[3];
68
+ pushDecoration(decorations, doctagMark, pos + start, pos + end);
69
+ }
70
+ else {
71
+ const index = markDocTagType(decorations, pos, mt), m = /^\s+([a-z_]\w*)\s+-/diu.exec(comment.slice(index));
72
+ if (m) {
73
+ const [start, end] = m.indices[1];
74
+ pushDecoration(decorations, varMark, pos + index + start, pos + index + end);
75
+ }
76
+ }
56
77
  }
57
78
  }
58
79
  },
@@ -60,10 +81,10 @@ export const markGlobals = (tree, visibleRanges, state, cm) => {
60
81
  }
61
82
  return Decoration.set(decorations);
62
83
  };
63
- export const markGlobalsPlugin = (cm) => ViewPlugin.fromClass(class {
84
+ export const markGlobalsAndDocTagPlugin = (cm) => ViewPlugin.fromClass(class {
64
85
  constructor({ state, visibleRanges }) {
65
86
  this.tree = syntaxTree(state);
66
- this.decorations = markGlobals(this.tree, visibleRanges, state, cm);
87
+ this.decorations = markGlobalsAndDocTag(this.tree, visibleRanges, state, cm);
67
88
  }
68
89
  update({ docChanged, viewportChanged, state, view: { visibleRanges }, transactions }) {
69
90
  const tree = syntaxTree(state);
@@ -76,7 +97,7 @@ export const markGlobalsPlugin = (cm) => ViewPlugin.fromClass(class {
76
97
  flag = transactions.some(tr => tr.effects.some(e => e.is(setDiagnosticsEffect)));
77
98
  }
78
99
  if (flag) {
79
- this.decorations = markGlobals(tree, visibleRanges, state, cm);
100
+ this.decorations = markGlobalsAndDocTag(tree, visibleRanges, state, cm);
80
101
  }
81
102
  }
82
103
  }, {
@@ -87,5 +108,5 @@ export const markGlobalsPlugin = (cm) => ViewPlugin.fromClass(class {
87
108
  export default (_, cm) => [
88
109
  js(),
89
110
  jsCompletion,
90
- markGlobalsPlugin(cm),
111
+ markGlobalsAndDocTagPlugin(cm),
91
112
  ];
package/dist/json.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ import type { StreamParser, StringStream } from '@codemirror/language';
2
+ declare interface State {
3
+ tokenize: Tokenizer;
4
+ wb: boolean;
5
+ }
6
+ declare type Tokenizer = (stream: StringStream, state: State) => string;
7
+ export declare const jsonBasic: StreamParser<State>, jsonc: StreamParser<State>;
8
+ export {};
package/dist/json.js ADDED
@@ -0,0 +1,108 @@
1
+ const tokenNumber = (stream, state) => {
2
+ if (stream.match(/^\d+(?:\.\d+)?(?:e[+-]?\d+)?/iu)) {
3
+ state.wb = false;
4
+ return 'number';
5
+ }
6
+ state.wb = true;
7
+ return '';
8
+ };
9
+ const mkJson = (jsoncMode) => {
10
+ // Tokenizer
11
+ const tokenComment = (stream, state) => {
12
+ if (stream.skipTo('*/')) {
13
+ stream.next();
14
+ stream.next();
15
+ state.tokenize = tokenBase;
16
+ }
17
+ else {
18
+ stream.skipToEnd();
19
+ }
20
+ return 'comment';
21
+ };
22
+ const tokenBase = (stream, state) => {
23
+ if (stream.eatSpace()) {
24
+ state.wb = true;
25
+ return '';
26
+ }
27
+ const ch = stream.next();
28
+ switch (ch) {
29
+ case '{':
30
+ case '}':
31
+ state.wb = true;
32
+ return 'brace';
33
+ case '[':
34
+ case ']':
35
+ state.wb = true;
36
+ return 'squareBracket';
37
+ case ':':
38
+ case ',':
39
+ state.wb = true;
40
+ return 'separator';
41
+ case '/':
42
+ state.wb = true;
43
+ if (jsoncMode) {
44
+ if (stream.eat('/')) {
45
+ stream.skipToEnd();
46
+ return 'comment';
47
+ }
48
+ else if (stream.eat('*')) {
49
+ state.tokenize = tokenComment;
50
+ return tokenComment(stream, state);
51
+ }
52
+ }
53
+ return '';
54
+ case '"': {
55
+ state.wb = true;
56
+ let escaped = false, next = stream.next();
57
+ while (next) {
58
+ if (!escaped && next === '"') {
59
+ break;
60
+ }
61
+ escaped = !escaped && next === '\\';
62
+ next = stream.next();
63
+ }
64
+ return stream.match(/^\s*:/u, false) ? 'propertyName.definition' : 'string';
65
+ }
66
+ case '-':
67
+ if (state.wb) {
68
+ return tokenNumber(stream, state);
69
+ }
70
+ state.wb = true;
71
+ return '';
72
+ default:
73
+ if (state.wb) {
74
+ if (ch >= '0' && ch <= '9') {
75
+ stream.backUp(1);
76
+ return tokenNumber(stream, state);
77
+ }
78
+ else if (ch === 'n' && stream.match(/^ull\b/u)) {
79
+ state.wb = false;
80
+ return 'null';
81
+ }
82
+ else if (ch === 't' && stream.match(/^rue\b/u)
83
+ || ch === 'f' && stream.match(/^alse\b/u)) {
84
+ state.wb = false;
85
+ return 'bool';
86
+ }
87
+ }
88
+ state.wb = !/[\w$]/u.test(ch);
89
+ return '';
90
+ }
91
+ };
92
+ // Interface
93
+ return {
94
+ startState() {
95
+ return {
96
+ tokenize: tokenBase,
97
+ wb: true,
98
+ };
99
+ },
100
+ token(stream, state) {
101
+ if (stream.sol()) {
102
+ state.wb = true;
103
+ }
104
+ return state.tokenize(stream, state);
105
+ },
106
+ };
107
+ };
108
+ export const jsonBasic = mkJson(), jsonc = mkJson(true);
package/dist/keymap.d.ts CHANGED
@@ -9,6 +9,6 @@ import type { KeymapConfig } from './keybindings';
9
9
  * @param opt.splitlines 是否分行
10
10
  * @test
11
11
  */
12
- export declare const getKeymap: ({ key, pre, post, splitlines }: KeymapConfig) => KeyBinding;
12
+ export declare const getWikiKeymap: ({ key, pre, post, splitlines }: KeymapConfig) => KeyBinding;
13
13
  declare const _default: KeyBinding[];
14
14
  export default _default;