@bhsd/codemirror-mediawiki 3.5.2 → 3.6.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
@@ -1111,7 +1111,7 @@ registerHighlightWhitespace();
1111
1111
 
1112
1112
  *version added: 2.21.1*
1113
1113
 
1114
- Show the help information of a magic word when hovering.
1114
+ Show the help information of a magic word or a template name when hovering.
1115
1115
 
1116
1116
  For granular control over the bundled extensions, you can import the `registerHover` function:
1117
1117
 
package/dist/bidi.js CHANGED
@@ -6,12 +6,13 @@
6
6
  import { EditorView, Direction, ViewPlugin, Decoration } from '@codemirror/view';
7
7
  import { Prec, RangeSetBuilder } from '@codemirror/state';
8
8
  import { syntaxTree } from '@codemirror/language';
9
- import { getTag } from './matchTag';
10
9
  import { tokens } from './config';
11
- const isolateLTR = Decoration.mark({
12
- class: 'cm-bidi-isolate cm-bidi-ltr',
10
+ import { isolateSelector, ltrSelector } from './constants';
11
+ import { getTag } from './matchTag';
12
+ const cls = isolateSelector.slice(1), isolateLTR = Decoration.mark({
13
+ class: `${cls} ${ltrSelector.slice(1)}`,
13
14
  bidiIsolate: Direction.LTR,
14
- }), isolate = Decoration.mark({ class: 'cm-bidi-isolate' });
15
+ }), isolate = Decoration.mark({ class: cls });
15
16
  export const computeIsolates = ({ visibleRanges, state, textDirection }) => {
16
17
  const set = new RangeSetBuilder();
17
18
  if (textDirection === Direction.RTL) {
@@ -3,12 +3,12 @@ import type { KeyBinding } from '@codemirror/view';
3
3
  import type { Extension } from '@codemirror/state';
4
4
  import type { SyntaxNode } from '@lezer/common';
5
5
  import type { ConfigData } from 'wikiparser-node';
6
- import type { MwConfig } from './token';
7
6
  import type { DocRange, foldHandler } from './fold';
7
+ import type { detectIndent } from './indent';
8
8
  import type { Option, LiveOption } from './linter';
9
9
  import type { LintSource, LintSources, LintSourceGetter } from './lintsource';
10
- import type { detectIndent } from './indent';
11
10
  import type statusBar from './statusBar';
11
+ import type { MwConfig } from './token';
12
12
  export type AddonMain<T> = (config?: T, cm?: CodeMirror6) => Extension;
13
13
  export type Addon<T> = [AddonMain<T>, Record<string, T>?];
14
14
  export type Dialect = 'sanitized-css' | undefined;
@@ -5,6 +5,7 @@ import { defaultKeymap, historyKeymap, history, redo, indentWithTab } from '@cod
5
5
  import { searchKeymap } from '@codemirror/search';
6
6
  import { linter, lintGutter, lintKeymap } from '@codemirror/lint';
7
7
  import elt from 'crelt';
8
+ import { panelSelector, panelsSelector, diagnosticSelector } from './constants';
8
9
  import { light } from './theme';
9
10
  export const plain = () => EditorView.contentAttributes.of({ spellcheck: 'true' });
10
11
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -83,6 +84,9 @@ export class CodeMirror6 {
83
84
  #getLanguage(config) {
84
85
  const lang = (languages[this.#lang] ?? plain)(config);
85
86
  this.#nestedMWLanguage = lang.nestedMWLanguage;
87
+ if (this.#lang === 'mediawiki') {
88
+ this.langConfig = config;
89
+ }
86
90
  return lang;
87
91
  }
88
92
  /**
@@ -122,16 +126,16 @@ export class CodeMirror6 {
122
126
  },
123
127
  ]),
124
128
  EditorView.theme({
125
- '.cm-panels': {
129
+ [panelsSelector]: {
126
130
  direction: document.dir,
127
131
  },
128
132
  '& .cm-lineNumbers .cm-gutterElement': {
129
133
  textAlign: 'end',
130
134
  },
131
- '.cm-textfield, .cm-button, .cm-panel.cm-search label, .cm-panel.cm-gotoLine label': {
135
+ [`.cm-textfield,.cm-button,${panelSelector}.cm-search label,${panelSelector}.cm-gotoLine label`]: {
132
136
  fontSize: 'inherit',
133
137
  },
134
- '.cm-panel [name="close"]': {
138
+ [`${panelSelector} [name="close"]`]: {
135
139
  color: 'inherit',
136
140
  },
137
141
  }),
@@ -233,11 +237,12 @@ export class CodeMirror6 {
233
237
  const diagnostics = (await source(state)).map((diagnostic) => ({
234
238
  ...diagnostic,
235
239
  renderMessage(view) {
236
- const span = elt('span', { class: 'cm-diagnosticText-clickable' }, diagnostic.message);
240
+ const span = elt('span', { class: diagnosticSelector.slice(1) }, diagnostic.message);
237
241
  span.addEventListener('click', () => {
238
242
  view.dispatch({
239
243
  selection: { anchor: diagnostic.from, head: diagnostic.to },
240
244
  });
245
+ view.focus();
241
246
  });
242
247
  return span;
243
248
  },
@@ -0,0 +1 @@
1
+ export declare const panelSelector = ".cm-panel", panelsSelector = ".cm-panels", diagnosticSelector = ".cm-diagnosticText-clickable", foldSelector = ".cm-tooltip-fold", hoverSelector = ".cm-tooltip-hover", matchingCls = "cm-matchingTag", nonmatchingCls = "cm-nonmatchingTag", isolateSelector = ".cm-bidi-isolate", ltrSelector = ".cm-bidi-ltr", menuSelector = ".cm-status-fix-menu", messageSelector = ".cm-status-message", actionSelector = ".cm-diagnosticAction", isWMF: boolean, isMac: boolean;
@@ -0,0 +1,9 @@
1
+ import { wmf } from '@bhsd/common';
2
+ export const panelSelector = '.cm-panel', panelsSelector = '.cm-panels', diagnosticSelector = '.cm-diagnosticText-clickable', foldSelector = '.cm-tooltip-fold', hoverSelector = '.cm-tooltip-hover', matchingCls = 'cm-matchingTag', nonmatchingCls = 'cm-nonmatchingTag', isolateSelector = '.cm-bidi-isolate', ltrSelector = '.cm-bidi-ltr', menuSelector = '.cm-status-fix-menu', messageSelector = '.cm-status-message', actionSelector = '.cm-diagnosticAction', isWMF = /* @__PURE__ */ (() => typeof location === 'object'
3
+ && new RegExp(String.raw `\.(?:${wmf})\.org$`, 'u').test(location.hostname))(), isMac = /* @__PURE__ */ (() => {
4
+ const { vendor, userAgent, maxTouchPoints, platform } = navigator;
5
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
6
+ return vendor?.includes('Apple Computer')
7
+ && (userAgent.includes('Mobile/') || maxTouchPoints > 2)
8
+ || platform.includes('Mac');
9
+ })();
package/dist/fold.d.ts CHANGED
@@ -6,12 +6,6 @@ export interface DocRange {
6
6
  from: number;
7
7
  to: number;
8
8
  }
9
- /**
10
- * Update the stack of opening (+) or closing (-) brackets
11
- * @param state
12
- * @param node 语法树节点
13
- */
14
- export declare const braceStackUpdate: (state: EditorState, node: SyntaxNode) => [number, number];
15
9
  /**
16
10
  * 寻找可折叠的范围
17
11
  * @param state
package/dist/fold.js CHANGED
@@ -4,7 +4,9 @@ import { syntaxTree, ensureSyntaxTree, foldEffect, unfoldEffect, foldedRanges, u
4
4
  import { getRegex } from '@bhsd/common';
5
5
  import elt from 'crelt';
6
6
  import { tokens } from './config';
7
+ import { foldSelector } from './constants';
7
8
  import { matchTag, getTag } from './matchTag';
9
+ import { braceStackUpdate } from './util';
8
10
  const getExtRegex = /* @__PURE__ */ getRegex(tag => new RegExp(`mw-tag-${tag}(?![a-z])`, 'u'));
9
11
  const updateSelection = (pos, { to }) => Math.max(pos, to), updateAll = (pos, { from, to }) => from <= pos && to > pos ? to : pos;
10
12
  /**
@@ -36,15 +38,6 @@ isExtBracket = /* @__PURE__ */ isComponent(['extTagBracket']),
36
38
  * @param refOnly 是否仅检查`<ref>`标签
37
39
  */
38
40
  isExt = (node, refOnly) => node.name.includes(`mw-tag-${refOnly ? 'ref' : ''}`);
39
- /**
40
- * Update the stack of opening (+) or closing (-) brackets
41
- * @param state
42
- * @param node 语法树节点
43
- */
44
- export const braceStackUpdate = (state, node) => {
45
- const brackets = state.sliceDoc(node.from, node.to);
46
- return [brackets.split('{{').length - 1, 1 - brackets.split('}}').length];
47
- };
48
41
  const refNames = new Set(['ref', 'references']);
49
42
  /**
50
43
  * 寻找可折叠的范围
@@ -161,7 +154,10 @@ const create = (state) => {
161
154
  pos: head,
162
155
  above: true,
163
156
  create() {
164
- const dom = elt('div', { class: 'cm-tooltip-fold', title: state.phrase('Fold template or extension tag') }, '\uff0d');
157
+ const dom = elt('div', {
158
+ class: foldSelector.slice(1),
159
+ title: state.phrase('Fold template or extension tag'),
160
+ }, '\uff0d');
165
161
  dom.dataset['from'] = String(from);
166
162
  dom.dataset['to'] = String(to);
167
163
  return { dom };
@@ -178,7 +174,7 @@ const create = (state) => {
178
174
  */
179
175
  const execute = (view, effects, anchor) => {
180
176
  if (effects.length > 0) {
181
- view.dom.querySelector('.cm-tooltip-fold')?.remove();
177
+ view.dom.querySelector(foldSelector)?.remove();
182
178
  // Fold the template(s) and update the cursor position
183
179
  view.dispatch({
184
180
  effects,
@@ -332,7 +328,6 @@ const foldCommand = (refOnly) => view => {
332
328
  };
333
329
  export const foldRef = /* @__PURE__ */ foldCommand(true);
334
330
  export default ((e = defaultFoldExtension) => e);
335
- const selector = '.cm-tooltip-fold';
336
331
  export const mediaWikiFold = /* @__PURE__ */ (() => [
337
332
  codeFolding({
338
333
  placeholderDOM(view) {
@@ -442,13 +437,13 @@ export const mediaWikiFold = /* @__PURE__ */ (() => [
442
437
  },
443
438
  }),
444
439
  EditorView.theme({
445
- [selector]: {
440
+ [foldSelector]: {
446
441
  cursor: 'pointer',
447
442
  lineHeight: 1.2,
448
443
  padding: '0 1px',
449
444
  opacity: 0.6,
450
445
  },
451
- [`${selector}:hover`]: {
446
+ [`${foldSelector}:hover`]: {
452
447
  opacity: 1,
453
448
  },
454
449
  }),
@@ -458,7 +453,7 @@ export const mediaWikiFold = /* @__PURE__ */ (() => [
458
453
  * @param view
459
454
  */
460
455
  export const foldHandler = (view) => (e) => {
461
- const dom = e.target.closest('.cm-tooltip-fold');
456
+ const dom = e.target.closest(foldSelector);
462
457
  if (dom) {
463
458
  e.preventDefault();
464
459
  const { dataset } = dom, from = Number(dataset['from']), to = Number(dataset['to']);
package/dist/hover.d.ts CHANGED
@@ -1,25 +1,4 @@
1
- import { EditorView } from '@codemirror/view';
2
- import type { TooltipView } from '@codemirror/view';
3
- import type { Text, Extension } from '@codemirror/state';
4
- import type { Position } from 'vscode-languageserver-types';
1
+ import type { Extension } from '@codemirror/state';
5
2
  import type { CodeMirror6 } from './codemirror';
6
- /**
7
- * 将索引转换为位置
8
- * @param doc Text 实例
9
- * @param index 索引
10
- */
11
- export declare const indexToPos: (doc: Text, index: number) => Position;
12
- /**
13
- * 将位置转换为索引
14
- * @param doc Text 实例
15
- * @param pos 位置
16
- */
17
- export declare const posToIndex: (doc: Text, pos: Position) => number;
18
- /**
19
- * 创建 TooltipView
20
- * @param view EditorView 实例
21
- * @param innerHTML 提示内容
22
- */
23
- export declare const createTooltipView: (view: EditorView, innerHTML: string) => TooltipView;
24
3
  declare const _default: (cm: CodeMirror6) => Extension;
25
4
  export default _default;
package/dist/hover.js CHANGED
@@ -1,40 +1,32 @@
1
1
  import { hoverTooltip, EditorView } from '@codemirror/view';
2
+ import { ensureSyntaxTree } from '@codemirror/language';
2
3
  import { loadScript, getLSP } from '@bhsd/browser';
3
- import elt from 'crelt';
4
- /**
5
- * 将索引转换为位置
6
- * @param doc Text 实例
7
- * @param index 索引
8
- */
9
- export const indexToPos = (doc, index) => {
10
- const line = doc.lineAt(index);
11
- return { line: line.number - 1, character: index - line.from };
12
- };
13
- /**
14
- * 将位置转换为索引
15
- * @param doc Text 实例
16
- * @param pos 位置
17
- */
18
- export const posToIndex = (doc, pos) => {
19
- const line = doc.line(pos.line + 1);
20
- return Math.min(line.from + pos.character, line.to);
21
- };
22
- /**
23
- * 创建 TooltipView
24
- * @param view EditorView 实例
25
- * @param innerHTML 提示内容
26
- */
27
- export const createTooltipView = (view, innerHTML) => {
28
- const inner = elt('div'), dom = elt('div', { class: 'cm-tooltip-hover' }, inner);
29
- dom.style.font = getComputedStyle(view.contentDOM).font;
30
- inner.innerHTML = innerHTML;
31
- return { dom };
32
- };
33
- const selector = '.cm-tooltip-hover';
4
+ import { tokens } from './config';
5
+ import { hoverSelector } from './constants';
6
+ import { escHTML, indexToPos, posToIndex, createTooltipView } from './util';
34
7
  export default (cm) => [
35
- hoverTooltip(async (view, pos) => {
36
- const { state: { doc } } = view, hover = await getLSP(view, false, cm.getWikiConfig)
8
+ hoverTooltip(async (view, pos, side) => {
9
+ const { state } = view, { doc } = state, { paramSuggest, tags } = cm.langConfig;
10
+ let hover = await getLSP(view, false, cm.getWikiConfig)
37
11
  ?.provideHover(doc.toString(), indexToPos(doc, pos));
12
+ if (!hover && paramSuggest && 'templatedata' in tags) {
13
+ const node = ensureSyntaxTree(state, pos + Math.max(side, 0))?.resolve(pos, side);
14
+ if (node?.name.includes(tokens.templateName)) {
15
+ const result = await paramSuggest(state.sliceDoc(node.from, node.to), false), { description, length } = result;
16
+ if (description || length > 0) {
17
+ // eslint-disable-next-line require-atomic-updates
18
+ hover = {
19
+ contents: {
20
+ kind: 'plaintext',
21
+ value: (description ? `<p>${escHTML(description)}</p>` : '') + (length === 0
22
+ ? ''
23
+ : `<ul>${result.map(([key, details]) => `<li><code>${escHTML(key)}</code>${details ? ` — ${escHTML(details)}` : ''}</li>`).join('')}</ul>`),
24
+ },
25
+ range: { start: indexToPos(doc, node.from), end: indexToPos(doc, node.to) },
26
+ };
27
+ }
28
+ }
29
+ }
38
30
  if (hover) {
39
31
  await loadScript('npm/marked/lib/marked.umd.js', 'marked', true);
40
32
  const { end } = hover.range;
@@ -43,27 +35,29 @@ export default (cm) => [
43
35
  end: posToIndex(doc, end),
44
36
  above: true,
45
37
  create() {
46
- return createTooltipView(view, marked.parse(hover.contents.value));
38
+ const { kind, value } = hover.contents;
39
+ return createTooltipView(view, kind === 'plaintext' ? value : marked.parse(value));
47
40
  },
48
41
  };
49
42
  }
50
43
  return null;
51
44
  }),
52
45
  EditorView.theme({
53
- [selector]: {
46
+ [hoverSelector]: {
54
47
  padding: '2px 5px',
55
48
  width: 'max-content',
56
49
  maxWidth: '60vw',
50
+ overflowY: 'auto',
57
51
  },
58
- [`${selector} *`]: {
52
+ [`${hoverSelector} *`]: {
59
53
  marginTop: '0!important',
60
54
  marginBottom: '0!important',
61
55
  },
62
- [`${selector}>div`]: {
56
+ [`${hoverSelector}>div`]: {
63
57
  fontSize: '90%',
64
58
  lineHeight: 1.4,
65
59
  },
66
- [`${selector} code`]: {
60
+ [`${hoverSelector} code`]: {
67
61
  padding: '.1em .4em',
68
62
  borderRadius: '.4em',
69
63
  },
package/dist/html.js CHANGED
@@ -3,9 +3,9 @@ import { htmlLanguage, htmlCompletionSourceWith } from '@codemirror/lang-html';
3
3
  import { javascript, javascriptLanguage } from '@codemirror/lang-javascript';
4
4
  import { cssLanguage } from '@codemirror/lang-css';
5
5
  import { LanguageSupport } from '@codemirror/language';
6
+ import { cssCompletion } from './css';
6
7
  import { jsCompletion } from './javascript';
7
8
  import { mediawiki } from './mediawiki';
8
- import { cssCompletion } from './css';
9
9
  export default (config) => {
10
10
  const { language, support } = mediawiki(config), lang = new LanguageSupport(htmlLanguage.configure({
11
11
  wrap: configureNesting([
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { CodeMirror6 } from './codemirror';
2
2
  import type { Extension } from '@codemirror/state';
3
3
  import type { LanguageSupport } from '@codemirror/language';
4
- import type { MwConfig } from './token';
5
4
  import type { LintSourceGetter } from './lintsource';
5
+ import type { MwConfig } from './token';
6
6
  export type { MwConfig };
7
7
  export { CodeMirror6 };
8
8
  /** Register the `highlightSpecialChars` extension */
package/dist/index.js CHANGED
@@ -6,29 +6,29 @@ import { json } from '@codemirror/lang-json';
6
6
  import { autoCloseTags } from '@codemirror/lang-html';
7
7
  import { getLSP } from '@bhsd/browser';
8
8
  import { colorPicker as cssColorPicker, colorPickerTheme, makeColorPicker } from '@bhsd/codemirror-css-color-picker';
9
+ import bidiIsolation from './bidi';
10
+ import { CodeMirror6, avail, languages, linterRegistry, destroyListeners, plain, optionalFunctions, themes, } from './codemirror';
9
11
  import colorPicker, { discoverColors } from './color';
10
- import { mediawiki } from './mediawiki';
11
12
  import escape from './escape';
12
13
  import codeFolding, { mediaWikiFold, foldHandler } from './fold';
13
- import tagMatchingState from './matchTag';
14
- import refHover from './ref';
15
14
  import magicWordHover from './hover';
16
- import signatureHelp from './signature';
15
+ import { detectIndent } from './indent';
17
16
  import inlayHints from './inlay';
17
+ import toolKeymap from './keymap';
18
18
  import { getWikiLintSource, getJsLintSource, getCssLintSource, getJsonLintSource, getLuaLintSource, getVueLintSource, getHTMLLintSource, } from './lintsource';
19
+ import bracketMatching from './matchBrackets';
20
+ import tagMatchingState from './matchTag';
21
+ import { mediawiki } from './mediawiki';
19
22
  import openLinks from './openLinks';
23
+ import refHover from './ref';
24
+ import signatureHelp from './signature';
20
25
  import { tagModes, getStaticMwConfig } from './static';
21
- import bidiIsolation from './bidi';
22
- import toolKeymap from './keymap';
23
- import bracketMatching from './matchBrackets';
24
26
  import statusBar from './statusBar';
25
- import { detectIndent } from './indent';
26
- import javascript from './javascript';
27
27
  import css from './css';
28
+ import html from './html';
29
+ import javascript from './javascript';
28
30
  import lua from './lua';
29
31
  import vue from './vue';
30
- import html from './html';
31
- import { CodeMirror6, avail, languages, linterRegistry, destroyListeners, plain, optionalFunctions, themes, } from './codemirror';
32
32
  export { CodeMirror6 };
33
33
  /**
34
34
  * 注册通用扩展
package/dist/inlay.js CHANGED
@@ -2,14 +2,15 @@ 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 { posToIndex } from './hover';
5
+ import { posToIndex } from './util';
6
+ const cls = 'cm-inlay-hint';
6
7
  class InlayHintWidget extends WidgetType {
7
8
  constructor(label) {
8
9
  super();
9
10
  this.label = label;
10
11
  }
11
12
  toDOM() {
12
- return elt('span', { class: 'cm-inlay-hint' }, this.label);
13
+ return elt('span', { class: cls }, this.label);
13
14
  }
14
15
  }
15
16
  const stateEffect = StateEffect.define(), field = StateField.define({
@@ -64,7 +65,7 @@ export default (cm) => [
64
65
  }
65
66
  }),
66
67
  EditorView.theme({
67
- '.cm-inlay-hint': {
68
+ [`.${cls}`]: {
68
69
  color: '#969696',
69
70
  fontStyle: 'italic',
70
71
  '-webkitUserSelect': 'none',
@@ -3,7 +3,7 @@ import { cssLanguage } from '@codemirror/lang-css';
3
3
  import { javascriptLanguage } from '@codemirror/lang-javascript';
4
4
  import { sanitizeInlineStyle } from '@bhsd/common';
5
5
  import { getWikiLinter, getJsLinter, getCssLinter, getJsonLinter, getLuaLinter } from './linter';
6
- import { posToIndex } from './hover';
6
+ import { posToIndex } from './util';
7
7
  /**
8
8
  * 获取Linter选项
9
9
  * @param opt Linter选项