@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.
@@ -25,15 +25,12 @@ export declare const findEnclosingPlainBrackets: (state: EditorState, pos: numbe
25
25
  * @test
26
26
  */
27
27
  export declare const trySelectMatchingBrackets: (state: EditorState, pos: number, dir: 1 | -1, config?: Config, inside?: boolean) => Selection | false;
28
+ /** @test */
29
+ export declare const customSelection: Record<number, (state: EditorState, pos: number, config?: Config) => Selection | false>;
28
30
  /**
29
31
  * @ignore
30
32
  * @test
31
33
  */
32
- export declare const selectMatchingBrackets: (state: EditorState, pos: number, config?: Config) => Selection | false;
33
- /**
34
- * @ignore
35
- * @test
36
- */
37
- export declare const bracketDeco: (state: EditorState, config: RequiredConfig) => DecorationSet;
34
+ export declare const myBracketDeco: (state: EditorState, config: RequiredConfig) => DecorationSet;
38
35
  declare const _default: (configs?: BracketConfig) => Extension;
39
36
  export default _default;
@@ -1,4 +1,5 @@
1
1
  import { Decoration, EditorView, ViewPlugin } from '@codemirror/view';
2
+ import { EditorSelection } from '@codemirror/state';
2
3
  import { bracketMatching, matchBrackets, syntaxTree } from '@codemirror/language';
3
4
  /**
4
5
  * @ignore
@@ -48,29 +49,41 @@ export const trySelectMatchingBrackets = (state, pos, dir, config, inside = fals
48
49
  head: match.end[rightInside ? 'from' : 'to'],
49
50
  };
50
51
  };
52
+ /** @test */
53
+ export const customSelection = {
54
+ 2: (state, pos, config) => trySelectMatchingBrackets(state, pos, -1, config)
55
+ || trySelectMatchingBrackets(state, pos, 1, config)
56
+ || trySelectMatchingBrackets(state, pos + 1, -1, config, true)
57
+ || trySelectMatchingBrackets(state, pos - 1, 1, config, true),
58
+ 3: (state, pos, config) => {
59
+ const { doc } = state, matching = tryMatchBracetks(state, pos, { ...config, afterCursor: true });
60
+ if (!matching || !matching.matched) {
61
+ return false;
62
+ }
63
+ const { start, end } = matching, a = doc.lineAt(start.from), b = doc.lineAt(end.from), dir = a.from < b.from;
64
+ return {
65
+ anchor: (dir ? a : b).from,
66
+ head: Math.min(doc.length, (dir ? b : a).to + 1),
67
+ };
68
+ },
69
+ 4: state => ({ anchor: 0, head: state.doc.length }),
70
+ };
71
+ const tryMatchBracetks = (state, pos, config) => matchBrackets(state, pos, -1, config)
72
+ || pos > 0 && matchBrackets(state, pos - 1, 1, config)
73
+ || config.afterCursor && (matchBrackets(state, pos, 1, config)
74
+ || pos < state.doc.length && matchBrackets(state, pos + 1, -1, config));
51
75
  /**
52
76
  * @ignore
53
77
  * @test
54
78
  */
55
- export const selectMatchingBrackets = (state, pos, config) => trySelectMatchingBrackets(state, pos, -1, config)
56
- || trySelectMatchingBrackets(state, pos, 1, config)
57
- || trySelectMatchingBrackets(state, pos + 1, -1, config, true)
58
- || trySelectMatchingBrackets(state, pos - 1, 1, config, true);
59
- /**
60
- * @ignore
61
- * @test
62
- */
63
- export const bracketDeco = (state, config) => {
79
+ export const myBracketDeco = (state, config) => {
64
80
  const decorations = [], { afterCursor, brackets, renderMatch, exclude, } = config;
65
81
  for (const { empty, head } of state.selection.ranges) {
66
82
  if (!empty) {
67
83
  continue;
68
84
  }
69
85
  const tree = syntaxTree(state), excluded = exclude?.(state, head), match = !excluded && // eslint-disable-line @stylistic/operator-linebreak
70
- (matchBrackets(state, head, -1, config)
71
- || head > 0 && matchBrackets(state, head - 1, 1, config)
72
- || afterCursor && (matchBrackets(state, head, 1, config)
73
- || head < state.doc.length && matchBrackets(state, head + 1, -1, config)))
86
+ tryMatchBracetks(state, head, config)
74
87
  || findEnclosingBrackets(tree.resolveInner(head, -1), head, brackets)
75
88
  || afterCursor && findEnclosingBrackets(tree.resolveInner(head, 1), head, brackets)
76
89
  || // eslint-disable-line @stylistic/operator-linebreak
@@ -82,11 +95,25 @@ export const bracketDeco = (state, config) => {
82
95
  }
83
96
  return Decoration.set(decorations, true);
84
97
  };
98
+ const clickHandler = (e, view, facet, select) => {
99
+ const pos = view.posAtCoords(e), { state } = view, config = state.facet(facet);
100
+ if (pos === null
101
+ || config.exclude?.(state, pos)) {
102
+ return false;
103
+ }
104
+ const range = select(state, pos, config);
105
+ if (range) {
106
+ const selection = EditorSelection.single(range.anchor, range.head);
107
+ view.dispatch({ selection });
108
+ return selection;
109
+ }
110
+ return false;
111
+ };
85
112
  export default (configs) => {
86
113
  const extension = bracketMatching(configs), [{ facet }, plugins] = extension;
87
114
  plugins[0] = ViewPlugin.fromClass(class {
88
115
  constructor({ state }) {
89
- this.decorations = bracketDeco(state, state.facet(facet));
116
+ this.decorations = myBracketDeco(state, state.facet(facet));
90
117
  this.paused = false;
91
118
  }
92
119
  update({ docChanged, selectionSet, changes, state, view: { composing } }) {
@@ -96,7 +123,7 @@ export default (configs) => {
96
123
  this.paused = true;
97
124
  }
98
125
  else {
99
- this.decorations = bracketDeco(state, state.facet(facet));
126
+ this.decorations = myBracketDeco(state, state.facet(facet));
100
127
  this.paused = false;
101
128
  }
102
129
  }
@@ -106,6 +133,7 @@ export default (configs) => {
106
133
  return decorations;
107
134
  },
108
135
  });
136
+ let selection = false;
109
137
  return [
110
138
  extension,
111
139
  EditorView.domEventHandlers({
@@ -113,18 +141,27 @@ export default (configs) => {
113
141
  * @ignore
114
142
  * @todo 由于括号高亮的重绘,双击会被识别为两次单击,导致功能失效
115
143
  */
116
- dblclick(e, view) {
117
- const pos = view.posAtCoords(e), { state } = view, config = state.facet(facet);
118
- if (pos === null
119
- || config.exclude?.(state, pos)) {
144
+ mousedown(e, view) {
145
+ selection = e.detail in customSelection && clickHandler(e, view, facet, customSelection[e.detail]);
146
+ return Boolean(selection);
147
+ },
148
+ /** @ignore */
149
+ mouseup() {
150
+ selection = false;
151
+ },
152
+ /** @ignore */
153
+ mousemove(e, view) {
154
+ if (!selection) {
120
155
  return false;
121
156
  }
122
- const selection = selectMatchingBrackets(state, pos, config);
123
- if (selection) {
124
- view.dispatch({ selection });
125
- return true;
157
+ const head = view.posAtCoords(e), { from, to } = selection.main;
158
+ if (head === null || head >= from && head <= to) {
159
+ return false;
126
160
  }
127
- return false;
161
+ view.dispatch({
162
+ selection: { head, anchor: head < from ? to : from },
163
+ });
164
+ return true;
128
165
  },
129
166
  }),
130
167
  ];
@@ -5,11 +5,11 @@ import type { MatchResult } from '@codemirror/language';
5
5
  import type { SyntaxNode } from '@lezer/common';
6
6
  declare type TagType = 'ext' | 'html';
7
7
  export interface TagMatchResult extends MatchResult {
8
- start: Tag;
9
- end?: Tag;
8
+ start: WikiTag;
9
+ end?: WikiTag;
10
10
  }
11
11
  /** @test */
12
- export declare class Tag {
12
+ export declare class WikiTag {
13
13
  readonly type: TagType;
14
14
  readonly name: string;
15
15
  readonly first: SyntaxNode;
@@ -27,13 +27,13 @@ export declare class Tag {
27
27
  * @param node 语法树节点
28
28
  * @test
29
29
  */
30
- export declare const getTag: (state: EditorState, node: SyntaxNode) => Tag | null;
30
+ export declare const getTag: (state: EditorState, node: SyntaxNode) => WikiTag | null;
31
31
  /**
32
32
  * 搜索匹配的标签
33
33
  * @param state
34
34
  * @param origin 起始标签
35
35
  */
36
- export declare const searchTag: (state: EditorState, origin: Tag) => Tag | null;
36
+ export declare const searchTag: (state: EditorState, origin: WikiTag) => WikiTag | null;
37
37
  /**
38
38
  * 匹配标签
39
39
  * @param state
package/dist/matchTag.js CHANGED
@@ -3,9 +3,9 @@ import { StateField } from '@codemirror/state';
3
3
  import { ensureSyntaxTree } from '@codemirror/language';
4
4
  import { voidHtmlTags, selfClosingTags } from './config.js';
5
5
  import { matchingCls, nonmatchingCls } from './constants.js';
6
- import { sliceDoc } from './util.js';
6
+ import { sliceDoc, pushDecoration } from './util.js';
7
7
  /** @test */
8
- export class Tag {
8
+ export class WikiTag {
9
9
  get closing() {
10
10
  return isClosing(this.first, this.type, this.state, true);
11
11
  }
@@ -59,7 +59,7 @@ export const getTag = (state, node) => {
59
59
  ({ prevSibling } = prevSibling);
60
60
  }
61
61
  const name = getName(state, nameNode);
62
- return new Tag(type, name, prevSibling, nextSibling, state);
62
+ return new WikiTag(type, name, prevSibling, nextSibling, state);
63
63
  };
64
64
  /**
65
65
  * 搜索匹配的标签
@@ -114,7 +114,7 @@ export const matchTag = (state, pos) => {
114
114
  const end = searchTag(state, start);
115
115
  return end ? { matched: true, start, end } : { matched: false, start };
116
116
  };
117
- const matchingMark = /* @__PURE__ */ Decoration.mark({ class: matchingCls }), nonmatchingMark = /* @__PURE__ */ Decoration.mark({ class: nonmatchingCls });
117
+ const matchingTag = /* @__PURE__ */ Decoration.mark({ class: matchingCls }), nonmatchingTag = /* @__PURE__ */ Decoration.mark({ class: nonmatchingCls });
118
118
  export default /* @__PURE__ */ StateField.define({
119
119
  create() {
120
120
  return Decoration.none;
@@ -128,15 +128,15 @@ export default /* @__PURE__ */ StateField.define({
128
128
  if (range.empty) {
129
129
  const match = matchTag(state, range.head);
130
130
  if (match) {
131
- const mark = match.matched ? matchingMark : nonmatchingMark, { start: { from, to, closing }, end } = match;
132
- decorations.push(mark.range(from, to));
131
+ const mark = match.matched ? matchingTag : nonmatchingTag, { start, end } = match;
132
+ pushDecoration(decorations, mark, start);
133
133
  if (end) {
134
- decorations[closing ? 'unshift' : 'push'](mark.range(end.from, end.to));
134
+ pushDecoration(decorations, mark, end);
135
135
  }
136
136
  }
137
137
  }
138
138
  }
139
- return Decoration.set(decorations);
139
+ return Decoration.set(decorations, true);
140
140
  },
141
141
  provide(f) {
142
142
  return EditorView.decorations.from(f);
@@ -6,7 +6,7 @@
6
6
  import { LanguageSupport } from '@codemirror/language';
7
7
  import { EditorView } from '@codemirror/view';
8
8
  import { MediaWiki } from './token.js';
9
- import type { StreamParser, TagStyle } from '@codemirror/language';
9
+ import type { TagStyle } from '@codemirror/language';
10
10
  import type { CompletionSource, Completion } from '@codemirror/autocomplete';
11
11
  import type { MwConfig } from './token';
12
12
  import type { TagName } from './config';
@@ -52,7 +52,6 @@ export declare class FullMediaWiki extends MediaWiki {
52
52
  * @see https://codemirror.net/docs/ref/#language.TagStyle
53
53
  */
54
54
  getTagStyles(): TagStyle[];
55
- mediawiki(tags?: string[]): StreamParser<any>;
56
55
  /** 自动补全魔术字和标签名 */
57
56
  get completionSource(): CompletionSource;
58
57
  }
package/dist/mediawiki.js CHANGED
@@ -10,6 +10,7 @@ import { isUnderscore } from '@bhsd/cm-util';
10
10
  import { commonHtmlAttrs, htmlAttrs, extAttrs } from 'wikiparser-node/dist/util/sharable.mjs';
11
11
  import { htmlTags, tokens } from './config.js';
12
12
  import { hoverSelector, isWMF, } from './constants.js';
13
+ import { lightHighlightStyle } from './theme.js';
13
14
  import { MediaWiki } from './token.js';
14
15
  import { leadingSpaces, findTemplateName } from './util.js';
15
16
  const ranks = { Required: 1, Suggested: 2, Optional: 3, Deprecated: 4 };
@@ -107,13 +108,6 @@ export class FullMediaWiki extends MediaWiki {
107
108
  class: `cm-${className}`,
108
109
  }));
109
110
  }
110
- mediawiki(tags) {
111
- const parser = super.mediawiki(tags);
112
- parser.languageData = {
113
- closeBrackets: { brackets: ['(', '[', '{', '"'], before: ')]}>' },
114
- };
115
- return parser;
116
- }
117
111
  /**
118
112
  * 提供链接建议
119
113
  * @param str 搜索字符串,开头不包含` `,但可能包含`_`
@@ -395,7 +389,7 @@ const getGrounds = (grounds, r, g, b, a) => ({
395
389
  * @license GPL-2.0-or-later
396
390
  * @see https://gerrit.wikimedia.org/g/mediawiki/extensions/CodeMirror
397
391
  */
398
- const theme = /* @__PURE__ */ EditorView.theme({
392
+ const wikiTheme = /* @__PURE__ */ EditorView.theme({
399
393
  [getSelector(['', '~*'], 'section--1')]: {
400
394
  fontSize: '1.8em',
401
395
  lineHeight: '1.2em',
@@ -546,8 +540,9 @@ const theme = /* @__PURE__ */ EditorView.theme({
546
540
  export const mediawikiBase = (config, templatedata) => {
547
541
  const mode = new FullMediaWiki(config, templatedata), lang = StreamLanguage.define(mode.mediawiki());
548
542
  return new LanguageSupport(lang, [
543
+ lightHighlightStyle,
549
544
  syntaxHighlighting(HighlightStyle.define(mode.getTagStyles())),
550
- theme,
545
+ wikiTheme,
551
546
  lang.data.of({ autocomplete: mode.completionSource }),
552
547
  ]);
553
548
  };