@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/dist/ref.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { EditorState, Extension } from '@codemirror/state';
2
2
  import type { CodeMirror6 } from './codemirror';
3
- import type { Tag } from './matchTag';
3
+ import type { WikiTag } from './matchTag';
4
4
  /**
5
5
  * 高亮<ref>内容
6
6
  * @param state 编辑器EditorState
@@ -13,6 +13,6 @@ export declare const highlightRef: (state: EditorState, text: string) => string;
13
13
  * @ignore
14
14
  * @test
15
15
  */
16
- export declare const needHover: (state: EditorState, { name, selfClosing, first, last }: Tag) => boolean;
16
+ export declare const needHover: (state: EditorState, { name, selfClosing, first, last }: WikiTag) => boolean;
17
17
  declare const _default: (articlePath?: string) => (cm: CodeMirror6) => Extension;
18
18
  export default _default;
package/dist/ref.js CHANGED
@@ -3,7 +3,7 @@ import { ensureSyntaxTree, language, highlightingFor } from '@codemirror/languag
3
3
  import { highlightCode } from '@lezer/highlight';
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 { tokens } from './config.js';
8
8
  import { getTag } from './matchTag.js';
9
9
  import { sliceDoc, indexToPos, posToIndex, escHTML, toConfigGetter, } from './util.js';
@@ -13,7 +13,7 @@ const trees = new WeakMap(), selector = '.cm-tooltip-ref', noDef = '.cm-tooltip-
13
13
  * @param state
14
14
  * @param node 语法树节点
15
15
  */
16
- const getName = (state, node) => sliceDoc(state, node).trim();
16
+ const getRefName = (state, node) => sliceDoc(state, node).trim();
17
17
  /**
18
18
  * 高亮<ref>内容
19
19
  * @param state 编辑器EditorState
@@ -45,7 +45,7 @@ export const needHover = (state, { name, selfClosing, first, last }) => {
45
45
  if (name === 'ref' && selfClosing) {
46
46
  let prevSibling = last, nextSibling = null;
47
47
  while (prevSibling && prevSibling.from > first.to) {
48
- const key = getName(state, prevSibling);
48
+ const key = getRefName(state, prevSibling);
49
49
  if (prevSibling.name.split('_').includes(tokens.extTagAttribute)
50
50
  && /(?:^|\s)name(?:$|[\s=])/iu.test(key)) {
51
51
  if (/(?:^|\s)name\s*=/iu.test(key)) {
@@ -56,7 +56,7 @@ export const needHover = (state, { name, selfClosing, first, last }) => {
56
56
  ({ prevSibling } = prevSibling);
57
57
  }
58
58
  if (nextSibling?.name.includes(tokens.extTagAttributeValue)) {
59
- let target = getName(state, nextSibling);
59
+ let target = getRefName(state, nextSibling);
60
60
  const quote = target.charAt(0);
61
61
  if (quote === '"' || quote === "'") {
62
62
  target = target.slice(1, target.slice(-1) === quote ? -1 : undefined).trim();
@@ -75,7 +75,7 @@ export default (articlePath) => (cm) => {
75
75
  if (node?.name.includes('-exttag-')) {
76
76
  const tag = getTag(state, node);
77
77
  if (tag && needHover(state, tag)) {
78
- const { doc } = state, ref = await getLSP(view, false, toConfigGetter(cm.getWikiConfig, articlePath), base.CDN)?.provideDefinition(doc.toString(), indexToPos(doc, tag.first.to));
78
+ const { doc } = state, ref = await getLSP(view, false, toConfigGetter(cm.getWikiConfig, articlePath), baseData.CDN)?.provideDefinition(doc.toString(), indexToPos(doc, tag.first.to));
79
79
  return {
80
80
  pos,
81
81
  end: tag.to,
package/dist/signature.js CHANGED
@@ -2,16 +2,16 @@ import { EditorView, showTooltip } from '@codemirror/view';
2
2
  import { StateField, StateEffect } from '@codemirror/state';
3
3
  import { syntaxTree } from '@codemirror/language';
4
4
  import { getLSP } from '@bhsd/browser';
5
- import { base } from './constants.js';
6
- import { createTooltipView, indexToPos, escHTML, toConfigGetter, findTemplateName, isTemplate, } from './util.js';
7
- const stateEffect = StateEffect.define(), field = StateField.define({
5
+ import { baseData } from './constants.js';
6
+ import { createTooltipView, indexToPos, escHTML, toConfigGetter, findTemplateName, isTemplateParam, } from './util.js';
7
+ const signatureEffect = StateEffect.define(), signatureField = StateField.define({
8
8
  create() {
9
9
  return undefined;
10
10
  },
11
11
  update(oldValue, { state: { doc, selection: { main: { head } } }, effects }) {
12
12
  const text = doc.toString();
13
13
  for (const effect of effects) {
14
- if (effect.is(stateEffect)) {
14
+ if (effect.is(signatureEffect)) {
15
15
  const { value } = effect;
16
16
  if (head === value.cursor && text === value.text) {
17
17
  return value;
@@ -40,25 +40,25 @@ export const getSignatureHelp = ({ signatures, activeParameter: active }) => sig
40
40
  }).join('<br>');
41
41
  export default (articlePath) => (cm) => {
42
42
  return [
43
- field,
43
+ signatureField,
44
44
  EditorView.updateListener.of(({ view, state, docChanged, selectionSet }) => {
45
- if (docChanged || selectionSet && state.field(field)?.signatureHelp?.signatures.length) {
45
+ if (docChanged || selectionSet && state.field(signatureField)?.signatureHelp?.signatures.length) {
46
46
  const { doc, selection: { main } } = state, { head: cursor } = main, text = doc.toString();
47
47
  if (!main.empty) {
48
48
  view.dispatch({
49
- effects: stateEffect.of({ text, cursor }),
49
+ effects: signatureEffect.of({ text, cursor }),
50
50
  });
51
51
  return;
52
52
  }
53
53
  (async () => {
54
- let signatureHelp = await getLSP(view, false, toConfigGetter(cm.getWikiConfig, articlePath), base.CDN)?.provideSignatureHelp(text, indexToPos(doc, cursor));
54
+ let signatureHelp = await getLSP(view, false, toConfigGetter(cm.getWikiConfig, articlePath), baseData.CDN)?.provideSignatureHelp(text, indexToPos(doc, cursor));
55
55
  if (!signatureHelp && typeof cm.langConfig?.templateSignature === 'function') {
56
56
  const tree = syntaxTree(state);
57
57
  let node = tree.resolve(cursor, -1);
58
- if (node.to === cursor && !isTemplate(node)) {
58
+ if (node.to === cursor && !isTemplateParam(node)) {
59
59
  node = tree.resolve(cursor, 1);
60
60
  }
61
- if (isTemplate(node)) {
61
+ if (isTemplateParam(node)) {
62
62
  const [templateName, parameterName] = findTemplateName(state, node), tooltip = cm.langConfig.templateSignature(templateName, parameterName);
63
63
  if (tooltip) {
64
64
  signatureHelp = { signatures: [tooltip] };
@@ -66,7 +66,7 @@ export default (articlePath) => (cm) => {
66
66
  }
67
67
  }
68
68
  view.dispatch({
69
- effects: stateEffect.of({ text, cursor, signatureHelp }),
69
+ effects: signatureEffect.of({ text, cursor, signatureHelp }),
70
70
  });
71
71
  })();
72
72
  }
@@ -76,12 +76,12 @@ export default (articlePath) => (cm) => {
76
76
  if (key === 'Escape') {
77
77
  const { doc, selection: { main: { head } } } = view.state;
78
78
  view.dispatch({
79
- effects: stateEffect.of({ text: doc.toString(), cursor: head }),
79
+ effects: signatureEffect.of({ text: doc.toString(), cursor: head }),
80
80
  });
81
81
  }
82
82
  },
83
83
  }),
84
- showTooltip.from(field, (value) => {
84
+ showTooltip.from(signatureField, (value) => {
85
85
  if (!value) {
86
86
  return null;
87
87
  }
package/dist/static.d.ts CHANGED
@@ -21,6 +21,9 @@ export declare const tagModes: {
21
21
  combobox: string;
22
22
  combooption: string;
23
23
  inputbox: string;
24
+ templatedata: string;
25
+ maplink: string;
26
+ mapframe: string;
24
27
  };
25
28
  /**
26
29
  * @ignore
package/dist/static.js CHANGED
@@ -20,6 +20,9 @@ export const tagModes = {
20
20
  combobox: 'text/combobox',
21
21
  combooption: 'mediawiki',
22
22
  inputbox: 'text/inputbox',
23
+ templatedata: 'json',
24
+ maplink: 'jsonc',
25
+ mapframe: 'jsonc',
23
26
  };
24
27
  /**
25
28
  * @ignore
package/dist/theme.d.ts CHANGED
@@ -6,4 +6,4 @@ export declare const light: Extension,
6
6
  * @author Bhsd
7
7
  * @see https://zh.moegirl.org.cn/User:%E9%AC%BC%E5%BD%B1233/nord-moeskin.css
8
8
  */
9
- nord: Extension;
9
+ nordDark: Extension;
package/dist/theme.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { EditorView } from '@codemirror/view';
2
2
  import { syntaxHighlighting, HighlightStyle, defaultHighlightStyle } from '@codemirror/language';
3
- import { nord as nordBase } from 'cm6-theme-nord';
3
+ import { nord } from 'cm6-theme-nord';
4
4
  import { matchingCls, nonmatchingCls, actionSelector, panelsSelector, bgDark, } from './constants.js';
5
5
  const focused = '&.cm-focused', matching = `${focused} .${matchingCls}`, nonmatching = `${focused} .${nonmatchingCls}`;
6
6
  export const lightHighlightStyle = /* @__PURE__ */ (() => syntaxHighlighting(HighlightStyle.define(defaultHighlightStyle.specs, { themeType: 'light' })))();
@@ -30,6 +30,15 @@ export const light = /* @__PURE__ */ EditorView.theme({
30
30
  '.cm-globals, .cm-globals>*': {
31
31
  color: '#164',
32
32
  },
33
+ '.cm-doctag>*': {
34
+ color: '#219',
35
+ },
36
+ '.cm-doctag-type>*': {
37
+ color: '#085',
38
+ },
39
+ '.cm-doctag-var>*': {
40
+ color: '#00f',
41
+ },
33
42
  [matching]: {
34
43
  backgroundColor: 'rgb(50,140,130,.32)',
35
44
  },
@@ -42,8 +51,8 @@ export const light = /* @__PURE__ */ EditorView.theme({
42
51
  * @author Bhsd
43
52
  * @see https://zh.moegirl.org.cn/User:%E9%AC%BC%E5%BD%B1233/nord-moeskin.css
44
53
  */
45
- nord = /* @__PURE__ */ (() => [
46
- nordBase,
54
+ nordDark = /* @__PURE__ */ (() => [
55
+ nord,
47
56
  EditorView.theme({
48
57
  '&': {
49
58
  '--cm-arg': '#9f78a5',
@@ -69,6 +78,15 @@ nord = /* @__PURE__ */ (() => [
69
78
  '.cm-globals, .cm-globals>*': {
70
79
  color: '#d08770',
71
80
  },
81
+ '.cm-doctag>*': {
82
+ color: '#81a1c1',
83
+ },
84
+ '.cm-doctag-type>*': {
85
+ color: '#ebcb8b',
86
+ },
87
+ '.cm-doctag-var>*': {
88
+ color: '#8fbcbb',
89
+ },
72
90
  'div.cm-activeLine': {
73
91
  backgroundColor: 'rgb(76,86,106,.27)',
74
92
  },
package/dist/token.d.ts CHANGED
@@ -4,6 +4,7 @@
4
4
  * @see https://gerrit.wikimedia.org/g/mediawiki/extensions/CodeMirror
5
5
  */
6
6
  import { Tag } from '@lezer/highlight';
7
+ import { jsonBasic, jsonc } from './json.js';
7
8
  import type { MwConfig as MwConfigBase } from '@bhsd/cm-util';
8
9
  import type { EditorState } from '@codemirror/state';
9
10
  import type { StreamParser, StringStream as StringStreamBase } from '@codemirror/language';
@@ -21,21 +22,21 @@ declare interface Nesting extends Record<NestCount, number> {
21
22
  export interface State extends Nesting {
22
23
  readonly stack: Tokenizer[];
23
24
  readonly inHtmlTag: string[];
25
+ readonly dt: Partial<Nesting> & {
26
+ n: number;
27
+ html: number;
28
+ };
29
+ readonly data: MediaWikiData;
24
30
  tokenize: Tokenizer;
25
31
  extMode: StreamParser<object> | false;
26
32
  lbrack: boolean | undefined;
27
33
  bold: boolean;
28
34
  italic: boolean;
29
- dt: Partial<Nesting> & {
30
- n: number;
31
- html: number;
32
- };
33
35
  sof: boolean;
34
36
  redirect: {
35
37
  colon: boolean;
36
38
  } | false;
37
39
  imgLink: boolean;
38
- data: MediaWikiData;
39
40
  }
40
41
  declare type ExtState = Omit<State, 'dt'> & Partial<Pick<State, 'dt'>>;
41
42
  declare interface Token {
@@ -206,5 +207,7 @@ export declare class MediaWiki {
206
207
  'text/inputbox'(): StreamParser<State>;
207
208
  inGallery(section?: boolean): Tokenizer;
208
209
  'text/gallery'(tags: string[]): StreamParser<State>;
210
+ json(): typeof jsonBasic;
211
+ jsonc(): typeof jsonc;
209
212
  }
210
213
  export {};
package/dist/token.js CHANGED
@@ -43,6 +43,7 @@ import { getRegex } from '@bhsd/common';
43
43
  import { decodeHTML } from '@bhsd/browser';
44
44
  import { otherParserFunctions } from '@bhsd/cm-util';
45
45
  import { htmlTags, voidHtmlTags, selfClosingTags, tokenTable, tokens } from './config.js';
46
+ import { jsonBasic, jsonc } from './json.js';
46
47
  class MediaWikiData {
47
48
  constructor(tags, urlProtocols) {
48
49
  this.tags = tags.includes('translate') ? tags.filter(tag => tag !== 'tvar') : tags;
@@ -121,7 +122,7 @@ const copyState = (state) => {
121
122
  else if (key === 'extState') {
122
123
  result[key] = (state.extName && state.extMode && state.extMode.copyState || copyState)(val);
123
124
  }
124
- else if (key !== 'data' && val && typeof val === 'object') {
125
+ else if (key !== 'data' && key !== 'extMode' && val && typeof val === 'object') {
125
126
  // @ts-expect-error initial value
126
127
  result[key] = { ...val }; // eslint-disable-line @typescript-eslint/no-misused-spread
127
128
  }
@@ -1229,7 +1230,7 @@ let MediaWiki = (() => {
1229
1230
  }
1230
1231
  const t = state.stack[0], pipe = (['inTemplateArgument', 'inParserFunctionArgument', 'inVariable'].includes(t.name) ? '|' : '')
1231
1232
  + getEqual(t);
1232
- if (pipe.includes(stream.peek() ?? '')) {
1233
+ if (pipe.includes(stream.peek() || '')) {
1233
1234
  pop(state);
1234
1235
  return '';
1235
1236
  }
@@ -1267,8 +1268,8 @@ let MediaWiki = (() => {
1267
1268
  const mt = stream.match(re);
1268
1269
  if (isLang) {
1269
1270
  let lang = mt[0].trim().toLowerCase();
1270
- if (lang === 'js') {
1271
- lang = 'javascript';
1271
+ if (lang === 'wiki' || lang === 'wikitext') {
1272
+ lang = 'mediawiki';
1272
1273
  }
1273
1274
  if (lang in this) {
1274
1275
  state.extMode = this[lang]();
@@ -1877,6 +1878,12 @@ let MediaWiki = (() => {
1877
1878
  ? state.extMode.indent(state.extState, textAfter, context)
1878
1879
  : null;
1879
1880
  },
1881
+ languageData: {
1882
+ closeBrackets: {
1883
+ brackets: ['(', '[', '{', '"'],
1884
+ before: ')]}>',
1885
+ },
1886
+ },
1880
1887
  ...tags
1881
1888
  ? undefined
1882
1889
  : {
@@ -2021,6 +2028,12 @@ let MediaWiki = (() => {
2021
2028
  },
2022
2029
  };
2023
2030
  }
2031
+ json() {
2032
+ return jsonBasic;
2033
+ }
2034
+ jsonc() {
2035
+ return jsonc;
2036
+ }
2024
2037
  };
2025
2038
  })();
2026
2039
  export { MediaWiki };
package/dist/util.d.ts CHANGED
@@ -1,8 +1,9 @@
1
- import type { EditorView, TooltipView } from '@codemirror/view';
2
- import type { Text, EditorState, SelectionRange } from '@codemirror/state';
1
+ import type { EditorView, TooltipView, Decoration } from '@codemirror/view';
2
+ import type { Text, EditorState, SelectionRange, Range } from '@codemirror/state';
3
3
  import type { SyntaxNode } from '@lezer/common';
4
4
  import type { Position } from 'vscode-languageserver-types';
5
5
  import type { ConfigGetter } from '@bhsd/browser';
6
+ import type { DocRange } from './fold';
6
7
  /**
7
8
  * 转义HTML字符串
8
9
  * @param text 原字符串
@@ -37,17 +38,34 @@ export declare const createTooltipView: (view: EditorView, innerHTML: string) =>
37
38
  */
38
39
  export declare const sliceDoc: (state: EditorState, node: SyntaxNode | SelectionRange) => string;
39
40
  /**
40
- * Update the stack of opening (+) or closing (-) brackets
41
+ * Update the stack of opening (+) or closing (-) braces
41
42
  * @param state
42
43
  * @param node 语法树节点
43
44
  * @test
44
45
  */
45
46
  export declare const braceStackUpdate: (state: EditorState, node: SyntaxNode) => [number, number];
47
+ /**
48
+ * Push a decoration to the array if the range is not empty
49
+ * @param decorations Decoration 数组
50
+ * @param decoration Decoration 实例
51
+ * @param from 起始位置或节点
52
+ * @param to 结束位置
53
+ */
54
+ export declare const pushDecoration: (decorations: Range<Decoration>[], decoration: Decoration, from: number | DocRange, to?: number) => void;
55
+ /**
56
+ * Mark the type in a JSDoc/LDoc comment
57
+ * @param decorations
58
+ * @param from 起始位置
59
+ * @param mt 正则表达式匹配结果,第1个捕获组为标签,第2个捕获组为类型的起始括号`{`
60
+ * @param offset Decoration 向外扩展的大小,默认为0
61
+ * @test
62
+ */
63
+ export declare const markDocTagType: (decorations: Range<Decoration>[], from: number, mt: RegExpExecArray, offset?: number) => number;
46
64
  /**
47
65
  * Check if the node is a template parameter value
48
66
  * @param node 语法树节点
49
67
  */
50
- export declare const isTemplate: (node: SyntaxNode) => boolean;
68
+ export declare const isTemplateParam: (node: SyntaxNode) => boolean;
51
69
  /**
52
70
  * Find the current template name and parameter name
53
71
  * @param state
package/dist/util.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import elt from 'crelt';
2
2
  import { tokens } from './config.js';
3
- import { hoverSelector, } from './constants.js';
3
+ import { hoverSelector, doctagMark, typeMark, } from './constants.js';
4
4
  const dict = { '\n': '<br>', '&': '&amp;', '<': '&lt;' };
5
5
  /**
6
6
  * 转义HTML字符串
@@ -47,7 +47,7 @@ export const createTooltipView = (view, innerHTML) => {
47
47
  */
48
48
  export const sliceDoc = (state, node) => state.sliceDoc(node.from, node.to);
49
49
  /**
50
- * Update the stack of opening (+) or closing (-) brackets
50
+ * Update the stack of opening (+) or closing (-) braces
51
51
  * @param state
52
52
  * @param node 语法树节点
53
53
  * @test
@@ -56,11 +56,52 @@ export const braceStackUpdate = (state, node) => {
56
56
  const brackets = sliceDoc(state, node);
57
57
  return [brackets.split('{{').length - 1, 1 - brackets.split('}}').length];
58
58
  };
59
+ /**
60
+ * Push a decoration to the array if the range is not empty
61
+ * @param decorations Decoration 数组
62
+ * @param decoration Decoration 实例
63
+ * @param from 起始位置或节点
64
+ * @param to 结束位置
65
+ */
66
+ export const pushDecoration = (decorations, decoration, from, to) => {
67
+ if (typeof from !== 'number') {
68
+ ({ from, to } = from);
69
+ }
70
+ if (from < to) {
71
+ decorations.push(decoration.range(from, to));
72
+ }
73
+ };
74
+ /**
75
+ * Mark the type in a JSDoc/LDoc comment
76
+ * @param decorations
77
+ * @param from 起始位置
78
+ * @param mt 正则表达式匹配结果,第1个捕获组为标签,第2个捕获组为类型的起始括号`{`
79
+ * @param offset Decoration 向外扩展的大小,默认为0
80
+ * @test
81
+ */
82
+ export const markDocTagType = (decorations, from, mt, offset = 0) => {
83
+ const { input, indices } = mt, [start, end] = indices[1];
84
+ pushDecoration(decorations, doctagMark, from + start, from + end);
85
+ if (mt[2]) {
86
+ const re = /[{}]/gu, [, left] = indices[2];
87
+ re.lastIndex = left;
88
+ let m = re.exec(input), balance = 1;
89
+ while (m) {
90
+ balance += m[0] === '{' ? 1 : -1;
91
+ if (balance === 0) {
92
+ pushDecoration(decorations, typeMark, from + left - offset, from + m.index + offset);
93
+ return m.index + 1;
94
+ }
95
+ m = re.exec(input);
96
+ }
97
+ }
98
+ return end;
99
+ };
59
100
  /**
60
101
  * Check if the node is a template parameter value
61
102
  * @param node 语法树节点
62
103
  */
63
- export const isTemplate = (node) => node.name.split('_').includes(tokens.template);
104
+ export const isTemplateParam = (node) => node.name.split('_').includes(tokens.template);
64
105
  /**
65
106
  * Find the current template name and parameter name
66
107
  * @param state
@@ -69,7 +110,7 @@ export const isTemplate = (node) => node.name.split('_').includes(tokens.templat
69
110
  */
70
111
  export const findTemplateName = (state, node) => {
71
112
  let stack = -1, { prevSibling } = node,
72
- /** 可包含`_`、`:`等 */ page = '', parameter = '', need = isTemplate(node);
113
+ /** 可包含`_`、`:`等 */ page = '', parameter = '', need = isTemplateParam(node);
73
114
  while (prevSibling) {
74
115
  const { name } = prevSibling;
75
116
  if (name.includes(tokens.templateBracket)) {
package/dist/vue.js CHANGED
@@ -3,13 +3,13 @@ import { htmlLanguage, htmlCompletionSource } from '@codemirror/lang-html';
3
3
  import { javascript } from '@codemirror/lang-javascript';
4
4
  import { LanguageSupport } from '@codemirror/language';
5
5
  import { cssCompletion } from './css.js';
6
- import { jsCompletion, markGlobalsPlugin } from './javascript.js';
6
+ import { jsCompletion, markGlobalsAndDocTagPlugin } from './javascript.js';
7
7
  export default (_, cm) => vue({
8
8
  base: new LanguageSupport(htmlLanguage, [
9
9
  htmlLanguage.data.of({ autocomplete: htmlCompletionSource }),
10
10
  javascript().support,
11
11
  jsCompletion,
12
12
  cssCompletion(),
13
- markGlobalsPlugin(cm),
13
+ markGlobalsAndDocTagPlugin(cm),
14
14
  ]),
15
15
  });