@bhsd/codemirror-mediawiki 3.12.1 → 3.12.3

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.
Files changed (51) hide show
  1. package/dist/codemirror.d.ts +2 -1
  2. package/dist/codemirror.js +5 -5
  3. package/dist/constants.d.ts +2 -1
  4. package/dist/constants.js +1 -1
  5. package/dist/css.js +2 -2
  6. package/dist/escape.js +9 -10
  7. package/dist/fold.d.ts +5 -8
  8. package/dist/fold.js +18 -18
  9. package/dist/hover.js +3 -3
  10. package/dist/index.d.ts +1 -1
  11. package/dist/index.js +1 -1
  12. package/dist/inlay.js +2 -2
  13. package/dist/javascript.d.ts +1 -1
  14. package/dist/javascript.js +8 -4
  15. package/dist/json.d.ts +8 -0
  16. package/dist/json.js +101 -0
  17. package/dist/keymap.d.ts +8 -1
  18. package/dist/keymap.js +89 -7
  19. package/dist/lilypond.d.ts +14 -0
  20. package/dist/lilypond.js +240 -0
  21. package/dist/lint.js +2 -2
  22. package/dist/lintsource.d.ts +1 -1
  23. package/dist/lintsource.js +7 -7
  24. package/dist/lua.d.ts +1 -1
  25. package/dist/lua.js +39 -63
  26. package/dist/main.min.js +32 -31
  27. package/dist/matchBrackets.d.ts +3 -11
  28. package/dist/matchBrackets.js +51 -39
  29. package/dist/matchTag.d.ts +5 -5
  30. package/dist/matchTag.js +4 -4
  31. package/dist/math.d.ts +3 -0
  32. package/dist/math.js +58 -0
  33. package/dist/mediawiki.d.ts +2 -3
  34. package/dist/mediawiki.js +34 -26
  35. package/dist/mw.min.js +33 -32
  36. package/dist/ref.d.ts +2 -2
  37. package/dist/ref.js +5 -5
  38. package/dist/signature.js +13 -13
  39. package/dist/static.d.ts +7 -0
  40. package/dist/static.js +7 -0
  41. package/dist/theme.d.ts +1 -1
  42. package/dist/theme.js +9 -3
  43. package/dist/token.d.ts +10 -6
  44. package/dist/token.js +54 -20
  45. package/dist/util.d.ts +35 -10
  46. package/dist/util.js +43 -15
  47. package/dist/wiki.min.js +33 -32
  48. package/i18n/en.json +1 -1
  49. package/i18n/zh-hans.json +1 -1
  50. package/i18n/zh-hant.json +1 -1
  51. package/package.json +8 -10
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,13 @@ 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;
27
+ math: string;
28
+ chem: string;
29
+ ce: string;
30
+ score: string;
24
31
  };
25
32
  /**
26
33
  * @ignore
package/dist/static.js CHANGED
@@ -20,6 +20,13 @@ 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',
26
+ math: 'text/math',
27
+ chem: 'text/math',
28
+ ce: 'text/math',
29
+ score: 'lilypond',
23
30
  };
24
31
  /**
25
32
  * @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' })))();
@@ -36,6 +36,9 @@ export const light = /* @__PURE__ */ EditorView.theme({
36
36
  '.cm-doctag-type>*': {
37
37
  color: '#085',
38
38
  },
39
+ '.cm-doctag-var>*': {
40
+ color: '#00f',
41
+ },
39
42
  [matching]: {
40
43
  backgroundColor: 'rgb(50,140,130,.32)',
41
44
  },
@@ -48,8 +51,8 @@ export const light = /* @__PURE__ */ EditorView.theme({
48
51
  * @author Bhsd
49
52
  * @see https://zh.moegirl.org.cn/User:%E9%AC%BC%E5%BD%B1233/nord-moeskin.css
50
53
  */
51
- nord = /* @__PURE__ */ (() => [
52
- nordBase,
54
+ nordDark = /* @__PURE__ */ (() => [
55
+ nord,
53
56
  EditorView.theme({
54
57
  '&': {
55
58
  '--cm-arg': '#9f78a5',
@@ -81,6 +84,9 @@ nord = /* @__PURE__ */ (() => [
81
84
  '.cm-doctag-type>*': {
82
85
  color: '#ebcb8b',
83
86
  },
87
+ '.cm-doctag-var>*': {
88
+ color: '#8fbcbb',
89
+ },
84
90
  'div.cm-activeLine': {
85
91
  backgroundColor: 'rgb(76,86,106,.27)',
86
92
  },
package/dist/token.d.ts CHANGED
@@ -21,21 +21,21 @@ declare interface Nesting extends Record<NestCount, number> {
21
21
  export interface State extends Nesting {
22
22
  readonly stack: Tokenizer[];
23
23
  readonly inHtmlTag: string[];
24
+ readonly dt: Partial<Nesting> & {
25
+ n: number;
26
+ html: number;
27
+ };
28
+ readonly data: MediaWikiData;
24
29
  tokenize: Tokenizer;
25
- extMode: StreamParser<object> | false;
30
+ extMode: StreamParser<object> | boolean;
26
31
  lbrack: boolean | undefined;
27
32
  bold: boolean;
28
33
  italic: boolean;
29
- dt: Partial<Nesting> & {
30
- n: number;
31
- html: number;
32
- };
33
34
  sof: boolean;
34
35
  redirect: {
35
36
  colon: boolean;
36
37
  } | false;
37
38
  imgLink: boolean;
38
- data: MediaWikiData;
39
39
  }
40
40
  declare type ExtState = Omit<State, 'dt'> & Partial<Pick<State, 'dt'>>;
41
41
  declare interface Token {
@@ -206,5 +206,9 @@ export declare class MediaWiki {
206
206
  'text/inputbox'(): StreamParser<State>;
207
207
  inGallery(section?: boolean): Tokenizer;
208
208
  'text/gallery'(tags: string[]): StreamParser<State>;
209
+ 'text/math'(): StreamParser<object>;
210
+ json(): StreamParser<object>;
211
+ jsonc(): StreamParser<object>;
212
+ lilypond(): StreamParser<object>;
209
213
  }
210
214
  export {};
package/dist/token.js CHANGED
@@ -43,6 +43,9 @@ 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';
47
+ import { math } from './math.js';
48
+ import { lilypond } from './lilypond.js';
46
49
  class MediaWikiData {
47
50
  constructor(tags, urlProtocols) {
48
51
  this.tags = tags.includes('translate') ? tags.filter(tag => tag !== 'tvar') : tags;
@@ -119,7 +122,10 @@ const copyState = (state) => {
119
122
  result[key] = [...val];
120
123
  }
121
124
  else if (key === 'extState') {
122
- result[key] = (state.extName && state.extMode && state.extMode.copyState || copyState)(val);
125
+ const f = state.extName && typeof state.extMode !== 'boolean'
126
+ && state.extMode.copyState?.bind(state.extMode)
127
+ || copyState;
128
+ result[key] = f(val);
123
129
  }
124
130
  else if (key !== 'data' && key !== 'extMode' && val && typeof val === 'object') {
125
131
  // @ts-expect-error initial value
@@ -330,7 +336,7 @@ const peekSpace = (stream, sol) => {
330
336
  const peek = stream.peek();
331
337
  return Boolean(peek && !peek.trim());
332
338
  };
333
- const syntaxHighlight = new Set(['syntaxhighlight', 'source', 'pre']), pageFunctions = new Set([
339
+ const syntaxHighlight = new Set(['syntaxhighlight', 'source', 'pre', 'score']), pageFunctions = new Set([
334
340
  'raw',
335
341
  'msg',
336
342
  'filepath',
@@ -342,7 +348,7 @@ const syntaxHighlight = new Set(['syntaxhighlight', 'source', 'pre']), pageFunct
342
348
  'canonicalurle',
343
349
  'int',
344
350
  'msgnw',
345
- ]), substs = new Set(['subst', 'safesubst']), headerRegex = new RegExp(String.raw `^(?:[^&[<{~'\-]|${lookahead("<{~'-")})+`, 'iu'), templateRegex = new RegExp(`^(?:[^|{}<]|${lookahead('{}<', true)})+`, 'u'), argumentRegex = new RegExp(String.raw `^(?:[^|[&:}{<~'__\-]|${lookahead("}{<~'__-")})+`, 'iu'), styleRegex = new RegExp(String.raw `^(?:[^|[&}{<~'__\-]|${lookahead("}{<~'__-")})+`, 'iu'), wikiRegex = new RegExp(String.raw `^(?:[^&:'{[<~__\-]|${lookahead("'{[<~__-")})+`, 'iu'), tableDefinitionRegex = new RegExp(`^(?:[^&={<]|${lookahead('{<')})+`, 'iu'), tableCellRegex = /^\s*(?:[|!]|\{\{\s*![!)\-+]?\s*\}\})/u, extLinkChars = "[{'<-", tableDefinitionChars = '{<', tableCellChars = "'<~__{-", htmlAttrChars = '{/', freeRegex = [false, true].map(lpar => {
351
+ ]), substs = new Set(['subst', 'safesubst']), extLinkChars = "[{'<-", tableDefinitionChars = '{<', tableCellChars = "'<~__{-", htmlAttrChars = '{/', tableDefinitionLookAhead = lookahead(tableDefinitionChars), htmlAttrLookAhead = lookahead(htmlAttrChars), tableCellLookAhead = lookahead(tableCellChars), argumentLookAhead = lookahead("}{<~'__-"), headerRegex = new RegExp(String.raw `^(?:[^&[<{~'\-]|${lookahead("<{~'-")})+`, 'iu'), templateRegex = new RegExp(`^(?:[^|{}<]|${lookahead('{}<', true)})+`, 'u'), argumentRegex = new RegExp(String.raw `^(?:[^|[&:}{<~'__\-]|${argumentLookAhead})+`, 'iu'), styleRegex = new RegExp(String.raw `^(?:[^|[&}{<~'__\-]|${argumentLookAhead})+`, 'iu'), wikiRegex = new RegExp(String.raw `^(?:[^&:'{[<~__\-]|${lookahead("'{[<~__-")})+`, 'iu'), tableDefinitionRegex = new RegExp(`^(?:[^&={<]|${tableDefinitionLookAhead})+`, 'iu'), tableCellRegex = /^\s*(?:[|!]|\{\{\s*![!)\-+]?\s*\}\})/u, freeRegex = [false, true].map(lpar => {
346
352
  const punctuations = getPunctuations(lpar), source = getUrlRegex(punctuations);
347
353
  return new RegExp(`^(?:${source}|[${punctuations}]+(?=${source}))*`, 'u');
348
354
  }), indentedTableRegex = [false, true].map(isTemplate => new RegExp(String.raw `^:*\s*(?=\{${getPipe(isTemplate)})`, 'u')), tableRegex = [false, true]
@@ -353,10 +359,10 @@ const syntaxHighlight = new Set(['syntaxhighlight', 'source', 'pre']), pageFunct
353
359
  new RegExp(String.raw `^(?:[<>{}]|%(?:3[ce]|[57][bd])|${lookahead('[]')})+`, 'iu'),
354
360
  new RegExp(String.raw `^(?:\}|${lookahead('[]{')})+`, 'u'),
355
361
  new RegExp(String.raw `^(?:[>}]|%(?:3[ce]|[57][bd])|${lookahead('[]{<')})+`, 'iu'),
356
- ], tableDefinitionValueRegex = ['', '='].map(equal => new RegExp(String.raw `^(?:[^\s&${tableDefinitionChars}${equal}]|${lookahead(tableDefinitionChars)})+`, 'iu')), variableRegex = [false, true].map(isDefault => new RegExp(String.raw `^(?:[^|{}<${isDefault ? String.raw `[&~'__:\-` : ''}]|\}(?!\}\})|${isDefault ? lookahead("{<~'__-") : lookahead('{<', true)})+`, 'iu')), parserFunctionRegex = ['', '[&', '[&:'].map(s => getRegex(chars => new RegExp(`^(?:[^|${s}${escapeCharClass(chars)}]|${lookahead(chars)})+`, 'iu'))), doubleUnderscoreRegex = {
362
+ ], tableDefinitionValueRegex = ['', '='].map(equal => new RegExp(String.raw `^(?:[^\s&${tableDefinitionChars}${equal}]|${tableDefinitionLookAhead})+`, 'iu')), variableRegex = [false, true].map(isDefault => new RegExp(String.raw `^(?:[^|{}<${isDefault ? String.raw `[&~'__:\-` : ''}]|\}(?!\}\})|${isDefault ? tableCellLookAhead : lookahead(tableDefinitionChars, true)})+`, 'iu')), parserFunctionRegex = ['', '[&', '[&:'].map(s => getRegex(chars => new RegExp(`^(?:[^|${s}${escapeCharClass(chars)}]|${lookahead(chars)})+`, 'iu'))), doubleUnderscoreRegex = {
357
363
  _: /^[\p{L}\p{N}_]+?__/u,
358
364
  '_': /^[\p{L}\p{N}__]+?_{2}/u,
359
- }, getExtLinkTextRegex = getRegex(pipe => new RegExp(String.raw `^(?:[^\]&${pipe}${escapeCharClass(extLinkChars)}]|${lookahead(extLinkChars)})+`, 'iu')), getExtLinkRegex = getRegex(pipe => new RegExp(`^(?:${getUrlRegex(pipe)})+`, 'u')), getTableDefinitionRegex = getRegex(s => new RegExp(`^(?:[^&${tableDefinitionChars}${s}]|${lookahead(tableDefinitionChars)})+`, 'iu')), getTableCellRegex = getRegex(s => new RegExp(`^(?:[^[&${s}${escapeCharClass(tableCellChars)}]|${lookahead(tableCellChars)})+`, 'iu')), getHtmlAttrRegex = getRegex(s => new RegExp(`^(?:[^<>&${htmlAttrChars}${s}]|${lookahead(htmlAttrChars)})+`, 'u')), getHtmlAttrKeyRegex = getRegex(pipe => new RegExp(`^(?:[^<>&={/${pipe}]|${lookahead('{/')})+`, 'u')), getExtAttrRegex = getRegex(s => new RegExp(`^(?:[^>/${s}]|${lookahead('/')})+`, 'u')), getExtTagCloseRegex = getRegex(name => name === 'onlyinclude'
365
+ }, getExtLinkTextRegex = getRegex(pipe => new RegExp(String.raw `^(?:[^\]&${pipe}${escapeCharClass(extLinkChars)}]|${lookahead(extLinkChars)})+`, 'iu')), getExtLinkRegex = getRegex(pipe => new RegExp(`^(?:${getUrlRegex(pipe)})+`, 'u')), getTableDefinitionRegex = getRegex(s => new RegExp(`^(?:[^&${tableDefinitionChars}${s}]|${tableDefinitionLookAhead})+`, 'iu')), getTableCellRegex = getRegex(s => new RegExp(`^(?:[^[&${s}${escapeCharClass(tableCellChars)}]|${tableCellLookAhead})+`, 'iu')), getHtmlAttrRegex = getRegex(s => new RegExp(`^(?:[^<>&${htmlAttrChars}${s}]|${htmlAttrLookAhead})+`, 'u')), getHtmlAttrKeyRegex = getRegex(pipe => new RegExp(`^(?:[^<>&={/${pipe}]|${htmlAttrLookAhead})+`, 'u')), getExtAttrRegex = getRegex(s => new RegExp(`^(?:[^>/${s}]|${lookahead('/')})+`, 'u')), getExtTagCloseRegex = getRegex(name => name === 'onlyinclude'
360
366
  ? /<\/onlyinclude(?:>|$)/u
361
367
  : new RegExp(String.raw `</${name}\s*(?:>|$)`, 'iu')), getNestedRegex = getRegex(tag => new RegExp(String.raw `^(?:[^<]|<(?!${tag}(?:[\s/>]|$)))+`, 'iu'));
362
368
  /** Adapted from the original CodeMirror 5 stream parser by Pavel Astakhov */
@@ -482,7 +488,7 @@ let MediaWiki = (() => {
482
488
  .join('|')}))|\d+\s*(?:${spImgKeys.filter(word => img[word] !== 'img_width').map(word => word.slice(2))
483
489
  .join('|')}))\s*(?=\||\]\]|$))`, 'u');
484
490
  this.tags = [...Object.keys(tags), 'includeonly', 'noinclude', 'onlyinclude'];
485
- this.convertRegex = new RegExp(String.raw `^(?:[^}|;&='{[<~__\-]|\}(?!-)|=(?!>)|\[(?!\[|${urlProtocols})|${lookahead("'{<~__-")})+`, 'iu');
491
+ this.convertRegex = new RegExp(String.raw `^(?:[^}|;&='{[<~__\-]|\}(?!-)|=(?!>)|\[(?!\[|${urlProtocols})|${tableCellLookAhead})+`, 'iu');
486
492
  this.convertSemicolon = variants && new RegExp(String.raw `^;\s*(?=(?:[^;]*?=>\s*)?(?:${variants.join('|')})\s*:|(?:$|\}-))`, 'iu');
487
493
  this.convertLang = variants
488
494
  && new RegExp(String.raw `^(?:=>\s*)?(?:${variants.join('|')})\s*:`, 'iu');
@@ -559,6 +565,9 @@ let MediaWiki = (() => {
559
565
  }
560
566
  for (const tag of this.tags) {
561
567
  this.addToken(`tag-${tag}`, tag !== 'nowiki' && tag !== 'pre' && tag !== 'ref');
568
+ if (tag === 'score') {
569
+ this.addToken('tag-score-scheme');
570
+ }
562
571
  this.addToken(`ext-${tag}`, true);
563
572
  }
564
573
  for (const tag of this.permittedHtmlTags) {
@@ -1229,7 +1238,7 @@ let MediaWiki = (() => {
1229
1238
  }
1230
1239
  const t = state.stack[0], pipe = (['inTemplateArgument', 'inParserFunctionArgument', 'inVariable'].includes(t.name) ? '|' : '')
1231
1240
  + getEqual(t);
1232
- if (pipe.includes(stream.peek() ?? '')) {
1241
+ if (pipe.includes(stream.peek() || '')) {
1233
1242
  pop(state);
1234
1243
  return '';
1235
1244
  }
@@ -1266,12 +1275,17 @@ let MediaWiki = (() => {
1266
1275
  const advance = (stream, state, re) => {
1267
1276
  const mt = stream.match(re);
1268
1277
  if (isLang) {
1269
- let lang = mt[0].trim().toLowerCase();
1270
- if (lang === 'js') {
1271
- lang = 'javascript';
1278
+ if (name !== 'score') {
1279
+ let lang = mt[0].trim().toLowerCase();
1280
+ if (lang === 'wiki' || lang === 'wikitext') {
1281
+ lang = 'mediawiki';
1282
+ }
1283
+ if (lang in this.config.tagModes && lang in this) {
1284
+ state.extMode = this[lang]();
1285
+ }
1272
1286
  }
1273
- if (lang in this) {
1274
- state.extMode = this[lang]();
1287
+ else if (mt[0].trim() === 'ABC') {
1288
+ state.extMode = true;
1275
1289
  }
1276
1290
  }
1277
1291
  return makeLocalStyle(tokens.extTagAttributeValue + (isPage ? ` ${tokens.pageName}` : ''), state);
@@ -1294,7 +1308,7 @@ let MediaWiki = (() => {
1294
1308
  ...state.data.tags.filter(tag => tag !== name),
1295
1309
  ...name === 'translate' ? ['tvar'] : [],
1296
1310
  ]);
1297
- if (state.extMode) {
1311
+ if (typeof state.extMode !== 'boolean') {
1298
1312
  state.extState = state.extMode.startState(0);
1299
1313
  }
1300
1314
  state.tokenize = this.eatExtTagArea(name);
@@ -1349,7 +1363,7 @@ let MediaWiki = (() => {
1349
1363
  inExtTokens(origString) {
1350
1364
  return (stream, state) => {
1351
1365
  let ret;
1352
- if (state.extMode === false) {
1366
+ if (typeof state.extMode === 'boolean') {
1353
1367
  ret = `mw-tag-${state.extName} ${tokens.extTag}`;
1354
1368
  stream.skipToEnd();
1355
1369
  }
@@ -1709,7 +1723,7 @@ let MediaWiki = (() => {
1709
1723
  };
1710
1724
  }
1711
1725
  eatEntity(stream, style) {
1712
- const entity = stream.match(/^(?:#x[a-f\d]+|#\d+|[a-z\d]+);/iu);
1726
+ const entity = stream.match(/^(?:#x[a-f\d]+|#\d+|[a-z][a-z\d]*);/iu);
1713
1727
  return entity && isHtmlEntity(entity[0]) ? tokens.htmlEntity : style;
1714
1728
  }
1715
1729
  /**
@@ -1745,7 +1759,7 @@ let MediaWiki = (() => {
1745
1759
  state[key] = other[key];
1746
1760
  }
1747
1761
  }
1748
- if (!(state.extName && state.extMode)
1762
+ if (!(state.extName && typeof state.extMode !== 'boolean')
1749
1763
  && state.nLink === 0
1750
1764
  && typeof style === 'string'
1751
1765
  && style.includes(tokens.apostrophes)) {
@@ -1868,15 +1882,21 @@ let MediaWiki = (() => {
1868
1882
  return '';
1869
1883
  },
1870
1884
  blankLine(state) {
1871
- if (state.extName && state.extMode && state.extMode.blankLine) {
1885
+ if (state.extName && typeof state.extMode !== 'boolean' && state.extMode.blankLine) {
1872
1886
  state.extMode.blankLine(state.extState, 0);
1873
1887
  }
1874
1888
  },
1875
1889
  indent(state, textAfter, context) {
1876
- return state.extName && state.extMode && state.extMode.indent
1890
+ return state.extName && typeof state.extMode !== 'boolean' && state.extMode.indent
1877
1891
  ? state.extMode.indent(state.extState, textAfter, context)
1878
1892
  : null;
1879
1893
  },
1894
+ languageData: {
1895
+ closeBrackets: {
1896
+ brackets: ['(', '[', '{', '"'],
1897
+ before: ')]}>',
1898
+ },
1899
+ },
1880
1900
  ...tags
1881
1901
  ? undefined
1882
1902
  : {
@@ -1978,8 +1998,10 @@ let MediaWiki = (() => {
1978
1998
  chain(state, this.inComment);
1979
1999
  return tokens.comment;
1980
2000
  }
1981
- /** @todo braces should also be parsed */
1982
- stream.match(/^(?:[^<]|<(?!!--))+/u);
2001
+ else if (stream.match(/^\{\{(?!\{)/u)) {
2002
+ return this.eatTransclusion(stream, state) ?? '';
2003
+ }
2004
+ stream.match(/^(?:[^<{]|<(?!!--)|\{(?!\{(?!\{)))+/u);
1983
2005
  return '';
1984
2006
  };
1985
2007
  }
@@ -2021,6 +2043,18 @@ let MediaWiki = (() => {
2021
2043
  },
2022
2044
  };
2023
2045
  }
2046
+ 'text/math'() {
2047
+ return math;
2048
+ }
2049
+ json() {
2050
+ return jsonBasic;
2051
+ }
2052
+ jsonc() {
2053
+ return jsonc;
2054
+ }
2055
+ lilypond() {
2056
+ return lilypond;
2057
+ }
2024
2058
  };
2025
2059
  })();
2026
2060
  export { MediaWiki };
package/dist/util.d.ts CHANGED
@@ -1,9 +1,14 @@
1
1
  import type { EditorView, TooltipView, Decoration } from '@codemirror/view';
2
2
  import type { Text, EditorState, SelectionRange, Range } from '@codemirror/state';
3
+ import type { StringStream } from '@codemirror/language';
4
+ import type { Completion } from '@codemirror/autocomplete';
3
5
  import type { SyntaxNode } from '@lezer/common';
4
6
  import type { Position } from 'vscode-languageserver-types';
5
7
  import type { ConfigGetter } from '@bhsd/browser';
6
- import type { DocRange } from './fold';
8
+ export interface DocRange {
9
+ from: number;
10
+ to: number;
11
+ }
7
12
  /**
8
13
  * 转义HTML字符串
9
14
  * @param text 原字符串
@@ -52,19 +57,46 @@ export declare const braceStackUpdate: (state: EditorState, node: SyntaxNode) =>
52
57
  * @param to 结束位置
53
58
  */
54
59
  export declare const pushDecoration: (decorations: Range<Decoration>[], decoration: Decoration, from: number | DocRange, to?: number) => void;
60
+ export declare const toConfigGetter: (configGetter?: ConfigGetter, articlePath?: string) => ConfigGetter | undefined;
61
+ /**
62
+ * Tokenizer for multiline comments
63
+ * @param parent 外层 Tokenizer
64
+ * @param end 注释结束标志
65
+ */
66
+ export declare const inComment: <T extends {
67
+ tokenize(stream: StringStream, state: T): string;
68
+ }>(parent: (stream: StringStream, state: T) => string, end: string) => (stream: StringStream, state: T) => string;
69
+ /**
70
+ * 生成自动补全选项
71
+ * @param labels 选项标签列表
72
+ * @param type 选项类型
73
+ */
74
+ export declare const getCompletions: (labels: string[], type?: string) => Completion[];
75
+ /**
76
+ * 从Token类型中获取扩展标签名
77
+ * @param types Token类型列表
78
+ */
79
+ export declare const getExtTags: (types: string[]) => string[];
80
+ /**
81
+ * 获取字符串开头的空白字符
82
+ * @param str 字符串
83
+ * @test
84
+ */
85
+ export declare const leadingSpaces: (str: string) => string;
55
86
  /**
56
87
  * Mark the type in a JSDoc/LDoc comment
57
88
  * @param decorations
58
89
  * @param from 起始位置
59
90
  * @param mt 正则表达式匹配结果,第1个捕获组为标签,第2个捕获组为类型的起始括号`{`
91
+ * @param offset Decoration 向外扩展的大小,默认为0
60
92
  * @test
61
93
  */
62
- export declare const markDocTagType: (decorations: Range<Decoration>[], from: number, mt: RegExpExecArray) => Range<Decoration>[];
94
+ export declare const markDocTagType: (decorations: Range<Decoration>[], from: number, mt: RegExpExecArray, offset?: number) => number;
63
95
  /**
64
96
  * Check if the node is a template parameter value
65
97
  * @param node 语法树节点
66
98
  */
67
- export declare const isTemplate: (node: SyntaxNode) => boolean;
99
+ export declare const isTemplateParam: (node: SyntaxNode) => boolean;
68
100
  /**
69
101
  * Find the current template name and parameter name
70
102
  * @param state
@@ -72,10 +104,3 @@ export declare const isTemplate: (node: SyntaxNode) => boolean;
72
104
  * @test
73
105
  */
74
106
  export declare const findTemplateName: (state: EditorState, node: SyntaxNode) => [string | null, string];
75
- export declare const toConfigGetter: (configGetter?: ConfigGetter, articlePath?: string) => ConfigGetter | undefined;
76
- /**
77
- * 获取字符串开头的空白字符
78
- * @param str 字符串
79
- * @test
80
- */
81
- export declare const leadingSpaces: (str: string) => string;
package/dist/util.js CHANGED
@@ -71,14 +71,51 @@ export const pushDecoration = (decorations, decoration, from, to) => {
71
71
  decorations.push(decoration.range(from, to));
72
72
  }
73
73
  };
74
+ export const toConfigGetter = (configGetter, articlePath) => articlePath
75
+ ? async () => Object.assign(await (configGetter ?? wikiparse.getConfig)(), { articlePath })
76
+ : configGetter;
77
+ /**
78
+ * Tokenizer for multiline comments
79
+ * @param parent 外层 Tokenizer
80
+ * @param end 注释结束标志
81
+ */
82
+ export const inComment = (parent, end) => (stream, state) => {
83
+ if (stream.skipTo(end)) {
84
+ stream.next();
85
+ stream.next();
86
+ state.tokenize = parent;
87
+ }
88
+ else {
89
+ stream.skipToEnd();
90
+ }
91
+ return 'comment';
92
+ };
93
+ /**
94
+ * 生成自动补全选项
95
+ * @param labels 选项标签列表
96
+ * @param type 选项类型
97
+ */
98
+ export const getCompletions = (labels, type = 'keyword') => labels.map((label) => ({ label, type }));
99
+ /**
100
+ * 从Token类型中获取扩展标签名
101
+ * @param types Token类型列表
102
+ */
103
+ export const getExtTags = (types) => types.filter(type => type.startsWith('mw-tag-')).map(type => type.slice(7));
104
+ /**
105
+ * 获取字符串开头的空白字符
106
+ * @param str 字符串
107
+ * @test
108
+ */
109
+ export const leadingSpaces = (str) => /^\s*/u.exec(str)[0];
74
110
  /**
75
111
  * Mark the type in a JSDoc/LDoc comment
76
112
  * @param decorations
77
113
  * @param from 起始位置
78
114
  * @param mt 正则表达式匹配结果,第1个捕获组为标签,第2个捕获组为类型的起始括号`{`
115
+ * @param offset Decoration 向外扩展的大小,默认为0
79
116
  * @test
80
117
  */
81
- export const markDocTagType = (decorations, from, mt) => {
118
+ export const markDocTagType = (decorations, from, mt, offset = 0) => {
82
119
  const { input, indices } = mt, [start, end] = indices[1];
83
120
  pushDecoration(decorations, doctagMark, from + start, from + end);
84
121
  if (mt[2]) {
@@ -88,19 +125,19 @@ export const markDocTagType = (decorations, from, mt) => {
88
125
  while (m) {
89
126
  balance += m[0] === '{' ? 1 : -1;
90
127
  if (balance === 0) {
91
- pushDecoration(decorations, typeMark, from + left, from + m.index);
92
- break;
128
+ pushDecoration(decorations, typeMark, from + left - offset, from + m.index + offset);
129
+ return m.index + 1;
93
130
  }
94
131
  m = re.exec(input);
95
132
  }
96
133
  }
97
- return decorations;
134
+ return end;
98
135
  };
99
136
  /**
100
137
  * Check if the node is a template parameter value
101
138
  * @param node 语法树节点
102
139
  */
103
- export const isTemplate = (node) => node.name.split('_').includes(tokens.template);
140
+ export const isTemplateParam = (node) => node.name.split('_').includes(tokens.template);
104
141
  /**
105
142
  * Find the current template name and parameter name
106
143
  * @param state
@@ -109,7 +146,7 @@ export const isTemplate = (node) => node.name.split('_').includes(tokens.templat
109
146
  */
110
147
  export const findTemplateName = (state, node) => {
111
148
  let stack = -1, { prevSibling } = node,
112
- /** 可包含`_`、`:`等 */ page = '', parameter = '', need = isTemplate(node);
149
+ /** 可包含`_`、`:`等 */ page = '', parameter = '', need = isTemplateParam(node);
113
150
  while (prevSibling) {
114
151
  const { name } = prevSibling;
115
152
  if (name.includes(tokens.templateBracket)) {
@@ -149,12 +186,3 @@ export const findTemplateName = (state, node) => {
149
186
  }
150
187
  return [prevSibling && page, parameter];
151
188
  };
152
- export const toConfigGetter = (configGetter, articlePath) => articlePath
153
- ? async () => Object.assign(await (configGetter ?? wikiparse.getConfig)(), { articlePath })
154
- : configGetter;
155
- /**
156
- * 获取字符串开头的空白字符
157
- * @param str 字符串
158
- * @test
159
- */
160
- export const leadingSpaces = (str) => /^\s*/u.exec(str)[0];