@bhsd/codemirror-mediawiki 2.30.3 → 2.31.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
@@ -396,6 +396,7 @@ cm.scrollTo();
396
396
  *version added: 2.1.8*
397
397
 
398
398
  **param**: `string` new content
399
+ **param**: `boolean` whether to force the content to be set in the read-only mode, default as false
399
400
  Reset the content of the editor. Need initialization first.
400
401
 
401
402
  ```js
@@ -654,18 +655,17 @@ Matched or unmatched tags are highlighted in cyan or dark red when the cursor is
654
655
 
655
656
  ### Transclusion
656
657
 
657
- 1. Substitution is not correctly highlighted ([Example](http://bhsd-harry.github.io/monaco-wiki/tests.html#Scribunto%3A%20isSubsting%20during%20PST)).
658
658
  1. Non-existing parser functions starting with `#` are highlighted as parser functions ([Example](http://bhsd-harry.github.io/codemirror-mediawiki/tests.html#Parsoid%3A%20unknown%20parser%20function%20(T314524))).
659
659
  1. Wikitext in template parameter names is not highlighted ([Example](http://bhsd-harry.github.io/codemirror-mediawiki/tests.html#Templates%3A%20Other%20wikitext%20in%20parameter%20names%20(T69657))).
660
660
  1. Template parameter names followed by a newline are not recognized ([Example](http://bhsd-harry.github.io/codemirror-mediawiki/tests.html#Templates%3A%20Handle%20comments%20in%20parameter%20names%20(T69657))).
661
661
 
662
662
  ### Heading
663
663
 
664
- 1. Comments at the SOL break section headings ([Example](https://bhsd-harry.github.io/codemirror-mediawiki/tests.html#Section%20extraction%20prefixed%20by%20comment%20(section%201))).
664
+ 1. Comments at the SOL should not break section headings ([Example](https://bhsd-harry.github.io/codemirror-mediawiki/tests.html#Section%20extraction%20prefixed%20by%20comment%20(section%201))).
665
665
 
666
666
  ### Table
667
667
 
668
- 1. Comments at the SOL break table syntax ([Example](https://bhsd-harry.github.io/codemirror-mediawiki/tests.html#3c.%20Table%20cells%20without%20escapable%20prefixes%20after%20edits)).
668
+ 1. Comments at the SOL should not break table syntax ([Example](https://bhsd-harry.github.io/codemirror-mediawiki/tests.html#3c.%20Table%20cells%20without%20escapable%20prefixes%20after%20edits)).
669
669
 
670
670
  ### External link
671
671
 
@@ -676,3 +676,7 @@ Matched or unmatched tags are highlighted in cyan or dark red when the cursor is
676
676
 
677
677
  1. Comments at the SOL break the highlighting ([Example](https://bhsd-harry.github.io/codemirror-mediawiki/tests.html#1.%20Lists%20with%20start-of-line-transparent%20tokens%20before%20bullets%3A%20Comments)).
678
678
  1. False positives of preformatted text when there are categories ([Example](http://bhsd-harry.github.io/codemirror-mediawiki/tests.html#Category%20%2F%20paragraph%20interactions)) or HTML tags ([Example](http://bhsd-harry.github.io/codemirror-mediawiki/tests.html#Parsing%20optional%20HTML%20elements%20(T8171))).
679
+
680
+ ### Language conversion
681
+
682
+ 1. BCP 47 language codes are not supported in language conversion ([Example](https://bhsd-harry.github.io/wikiparser-node/tests.html#Explicit%20definition%20of%20language%20variant%20alternatives%20(BCP%2047%20codes))).
@@ -40,7 +40,7 @@ export declare class CodeMirror6 {
40
40
  * @param lang 语言
41
41
  * @param config 语言设置
42
42
  */
43
- setLanguage(lang?: string, config?: unknown): void | Promise<void>;
43
+ setLanguage(lang?: string, config?: unknown): Promise<void>;
44
44
  /**
45
45
  * 开始语法检查
46
46
  * @param lintSource 语法检查函数
@@ -71,8 +71,9 @@ export declare class CodeMirror6 {
71
71
  /**
72
72
  * 重设编辑器内容
73
73
  * @param insert 新内容
74
+ * @param force 是否强制
74
75
  */
75
- setContent(insert: string): void;
76
+ setContent(insert: string, force?: boolean): void;
76
77
  /**
77
78
  * 在编辑器和文本框之间切换
78
79
  * @param show 是否显示编辑器
@@ -6,7 +6,7 @@ import { searchKeymap, highlightSelectionMatches } from '@codemirror/search';
6
6
  import { linter, lintGutter, lintKeymap } from '@codemirror/lint';
7
7
  import { closeBrackets, autocompletion, acceptCompletion, completionKeymap, startCompletion, } from '@codemirror/autocomplete';
8
8
  import { json } from '@codemirror/lang-json';
9
- import { getLSP } from '@bhsd/common';
9
+ import { getLSP } from '@bhsd/browser';
10
10
  import colorPicker from './color';
11
11
  import { mediawiki, html } from './mediawiki';
12
12
  import escapeKeymap from './escape';
@@ -24,6 +24,7 @@ import toolKeymap from './keymap';
24
24
  import statusBar from './statusBar';
25
25
  import { detectIndent } from './indent';
26
26
  import bracketMatching from './matchBrackets';
27
+ import wikitextLSP from './lsp';
27
28
  import javascript from './javascript';
28
29
  import css from './css';
29
30
  import lua from './lua';
@@ -249,11 +250,17 @@ export class CodeMirror6 {
249
250
  * @param lang 语言
250
251
  * @param config 语言设置
251
252
  */
252
- setLanguage(lang = 'plain', config) {
253
+ async setLanguage(lang = 'plain', config) {
253
254
  this.#lang = lang;
254
255
  if (this.#view) {
256
+ let ext = languages[lang](config);
257
+ ws: { // eslint-disable-line no-unused-labels
258
+ if (lang === 'mediawiki') {
259
+ ext = [ext, await wikitextLSP()];
260
+ }
261
+ }
255
262
  this.#effects([
256
- this.#language.reconfigure(languages[lang](config)),
263
+ this.#language.reconfigure(ext),
257
264
  this.#linter.reconfigure(linters[lang] ?? []),
258
265
  ]);
259
266
  this.#minHeight(Boolean(linters[lang]));
@@ -355,11 +362,11 @@ export class CodeMirror6 {
355
362
  * @param opt 选项
356
363
  */
357
364
  async getLinter(opt) {
358
- const isFunc = typeof opt === 'function', getOpt = (runtime) => isFunc ? opt(runtime) : opt;
365
+ const isFunc = typeof opt === 'function', getOpt = runtime => isFunc ? opt(runtime) : opt;
359
366
  switch (this.#lang) {
360
367
  case 'mediawiki': {
361
- const wikiLint = await getWikiLinter(getOpt(), this.#view);
362
- return async (doc) => (await wikiLint(doc.toString(), getOpt(true)))
368
+ const wikiLint = await getWikiLinter(await getOpt(), this.#view);
369
+ return async (doc) => (await wikiLint(doc.toString(), await getOpt(true)))
363
370
  .map(({ severity, code, message, range: r, from, to, data = [], source }) => ({
364
371
  source: source,
365
372
  from: from ?? posToIndex(doc, r.start),
@@ -382,7 +389,7 @@ export class CodeMirror6 {
382
389
  }
383
390
  case 'javascript': {
384
391
  const esLint = await getJsLinter();
385
- const lintSource = doc => esLint(doc.toString(), getOpt())
392
+ const lintSource = async (doc) => esLint(doc.toString(), await getOpt())
386
393
  .map(({ ruleId, message, severity, line, column, endLine, endColumn, fix, suggestions = [] }) => {
387
394
  const start = pos(doc, line, column), diagnostic = {
388
395
  source: 'ESLint',
@@ -409,29 +416,10 @@ export class CodeMirror6 {
409
416
  }
410
417
  case 'css': {
411
418
  const styleLint = await getCssLinter();
412
- let option = getOpt() ?? {};
419
+ let option = await getOpt() ?? {};
413
420
  if (!('extends' in option || 'rules' in option)) {
414
421
  option = { rules: option };
415
422
  }
416
- if (this.dialect === 'sanitized-css') {
417
- const rules = option['rules'];
418
- option = {
419
- ...option,
420
- rules: {
421
- ...rules,
422
- 'property-no-vendor-prefix': [
423
- true,
424
- {
425
- ignoreProperties: ['user-select'],
426
- },
427
- ],
428
- 'property-disallowed-list': [
429
- ...rules?.['property-disallowed-list'] ?? [],
430
- '/^--/',
431
- ],
432
- },
433
- };
434
- }
435
423
  const lintSource = async (doc) => (await styleLint(doc.toString(), option))
436
424
  .map(({ text, severity, line, column, endLine, endColumn, fix }) => {
437
425
  const diagnostic = {
@@ -494,11 +482,13 @@ export class CodeMirror6 {
494
482
  /**
495
483
  * 重设编辑器内容
496
484
  * @param insert 新内容
485
+ * @param force 是否强制
497
486
  */
498
- setContent(insert) {
487
+ setContent(insert, force) {
499
488
  if (this.#view) {
500
489
  this.#view.dispatch({
501
490
  changes: { from: 0, to: this.#view.state.doc.length, insert },
491
+ filter: !force,
502
492
  });
503
493
  }
504
494
  }
package/dist/config.js CHANGED
@@ -68,8 +68,7 @@ var html = [
68
68
  "wbr",
69
69
  "hr",
70
70
  "meta",
71
- "link",
72
- "img"
71
+ "link"
73
72
  ]
74
73
  ];
75
74
 
package/dist/fold.js CHANGED
@@ -10,11 +10,18 @@ const updateSelection = (pos, { to }) => Math.max(pos, to), updateAll = (pos, {
10
10
  * Check if a SyntaxNode is among the specified components
11
11
  * @param keys The keys of the tokens to check
12
12
  */
13
- const isComponent = (keys) => ({ name }) => keys.some(key => name.includes(tokens[key])),
13
+ const isComponent = (keys) => (node) => keys.some(key => node?.name.includes(tokens[key])),
14
14
  /** Check if a SyntaxNode is a template bracket (`{{` or `}}`) */
15
15
  isTemplateBracket = isComponent(['templateBracket', 'parserFunctionBracket']),
16
+ /** Check if a SyntaxNode is a template name */
17
+ isTemplateName = isComponent(['templateName', 'parserFunctionName']),
16
18
  /** Check if a SyntaxNode is a template delimiter (`|` or `:`) */
17
19
  isDelimiter = isComponent(['templateDelimiter', 'parserFunctionDelimiter']),
20
+ /**
21
+ * Check if a SyntaxNode is a template delimiter (`|` or `:`), excluding `subst:` and `safesubst:`
22
+ * @param node SyntaxNode
23
+ */
24
+ isTemplateDelimiter = (node) => isDelimiter(node) && !isTemplateName(node.nextSibling),
18
25
  /**
19
26
  * Check if a SyntaxNode is part of a template, except for the brackets
20
27
  * @param node 语法树节点
@@ -88,7 +95,7 @@ export const foldable = (state, posOrNode, tree, refOnly = false) => {
88
95
  }
89
96
  let { prevSibling, nextSibling } = node,
90
97
  /** The stack of opening (+) or closing (-) brackets */ stack = 1,
91
- /** The first delimiter */ delimiter = isDelimiter(node) ? node : null,
98
+ /** The first delimiter */ delimiter = isTemplateDelimiter(node) ? node : null,
92
99
  /** The start of the closing bracket */ to = 0;
93
100
  while (nextSibling) {
94
101
  if (isTemplateBracket(nextSibling)) {
@@ -103,7 +110,7 @@ export const foldable = (state, posOrNode, tree, refOnly = false) => {
103
110
  }
104
111
  stack += lbrace;
105
112
  }
106
- else if (!delimiter && stack === 1 && isDelimiter(nextSibling)) {
113
+ else if (!delimiter && stack === 1 && isTemplateDelimiter(nextSibling)) {
107
114
  // The first delimiter of the current template so far
108
115
  delimiter = nextSibling;
109
116
  }
@@ -124,7 +131,7 @@ export const foldable = (state, posOrNode, tree, refOnly = false) => {
124
131
  }
125
132
  stack += rbrace;
126
133
  }
127
- else if (stack === -1 && isDelimiter(prevSibling)) {
134
+ else if (stack === -1 && isTemplateDelimiter(prevSibling)) {
128
135
  // The first delimiter of the current template so far
129
136
  delimiter = prevSibling;
130
137
  }
package/dist/hover.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { hoverTooltip } from '@codemirror/view';
2
- import { loadScript, getLSP } from '@bhsd/common';
2
+ import { loadScript, getLSP } from '@bhsd/browser';
3
3
  /**
4
4
  * 将索引转换为位置
5
5
  * @param doc Text 实例
package/dist/inlay.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { StateField, StateEffect } from '@codemirror/state';
2
2
  import { Decoration, EditorView, WidgetType, ViewPlugin } from '@codemirror/view';
3
- import { getLSP } from '@bhsd/common';
3
+ import { getLSP } from '@bhsd/browser';
4
4
  import { posToIndex } from './hover';
5
5
  class InlayHintWidget extends WidgetType {
6
6
  constructor(label) {
package/dist/linter.d.ts CHANGED
@@ -3,7 +3,7 @@ import type { Linter } from 'eslint';
3
3
  import type { Warning } from 'stylelint';
4
4
  import type { Diagnostic } from 'luacheck-browserify';
5
5
  export type Option = Record<string, unknown> | null | undefined;
6
- export type LiveOption = (runtime?: boolean) => Option;
6
+ export type LiveOption = (runtime?: boolean) => Option | Promise<Option>;
7
7
  declare type getLinter<T> = () => (text: string) => T;
8
8
  declare type asyncLinter<T, S = Record<string, unknown>> = ((text: string, config?: Option) => T) & {
9
9
  config?: S;
package/dist/linter.js CHANGED
@@ -1,6 +1,7 @@
1
1
  /* eslint-disable unicorn/no-unreadable-iife */
2
- import { loadScript, getWikiparse, getLSP, sanitizeInlineStyle } from '@bhsd/common';
3
- import { styleLint } from '@bhsd/common/dist/stylelint';
2
+ import { sanitizeInlineStyle } from '@bhsd/common';
3
+ import { loadScript, getWikiparse, getLSP } from '@bhsd/browser';
4
+ import { styleLint } from '@bhsd/stylelint-util';
4
5
  /**
5
6
  * 计算位置
6
7
  * @param range 范围
@@ -22,7 +23,7 @@ export const getWikiLinter = async (opt, obj) => {
22
23
  await getWikiparse(opt?.['getConfig'], opt?.['i18n']);
23
24
  const lsp = getLSP(obj, opt?.['include']);
24
25
  return async (text, config) => {
25
- const diagnostics = (await lsp.provideDiagnostics(text)).filter(({ code, severity }) => Number(config?.[code] ?? 2) > Number(severity === 2)), tokens = 'findStyleTokens' in lsp && config?.['invalid-css'] !== '0' ? await lsp.findStyleTokens() : [];
26
+ const defaultSeverity = config?.['defaultSeverity'] ?? 2, diagnostics = (await lsp.provideDiagnostics(text)).filter(({ code, severity }) => Number(config?.[code] ?? defaultSeverity) > Number(severity === 2)), tokens = 'findStyleTokens' in lsp && config?.['invalid-css'] !== '0' ? await lsp.findStyleTokens() : [];
26
27
  if (tokens.length === 0) {
27
28
  return diagnostics;
28
29
  }
@@ -30,7 +31,7 @@ export const getWikiLinter = async (opt, obj) => {
30
31
  return [
31
32
  ...diagnostics,
32
33
  ...(await cssLint(tokens.map(({ childNodes, type, tag }, i) => `${type === 'ext-attr' ? 'div' : tag}#${i}{\n${sanitizeInlineStyle(childNodes[1].childNodes[0].data)
33
- .replace(/\n/gu, ' ')}\n}`).join('\n'))).map(({ line, column, endLine, endColumn, rule, severity, text: message }) => {
34
+ .replace(/\n/gu, ' ')}\n}`).join('\n'), config?.['css'])).map(({ line, column, endLine, endColumn, rule, severity, text: message }) => {
34
35
  const i = Math.ceil(line / 3), { range } = tokens[i - 1].childNodes[1].childNodes[0], from = offsetAt(range, line - 3 * i, column - 1);
35
36
  return {
36
37
  from,
package/dist/lsp.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import type { Extension } from '@codemirror/state';
2
+ declare const _default: () => Promise<Extension>;
3
+ export default _default;
package/dist/lsp.js ADDED
@@ -0,0 +1,34 @@
1
+ import { LSPClient, languageServerSupport } from '@codemirror/lsp-client';
2
+ let promise, file = 0;
3
+ export default async () => {
4
+ if (location.hostname !== 'localhost') {
5
+ return [];
6
+ }
7
+ promise ??= new Promise(resolve => {
8
+ let handlers = [];
9
+ const ws = new WebSocket('ws://localhost:3000/wikitext');
10
+ ws.onmessage = ({ data }) => {
11
+ for (const handler of handlers) {
12
+ handler(String(data));
13
+ }
14
+ };
15
+ ws.onopen = () => {
16
+ resolve({
17
+ send(message) {
18
+ ws.send(message);
19
+ },
20
+ subscribe(handler) {
21
+ handlers.push(handler);
22
+ },
23
+ unsubscribe(handler) {
24
+ handlers = handlers.filter(h => h !== handler);
25
+ },
26
+ });
27
+ };
28
+ ws.onerror = () => {
29
+ resolve(null);
30
+ };
31
+ });
32
+ const transport = await promise;
33
+ return transport ? languageServerSupport(new LSPClient().connect(transport), `file:///${file++}.wiki`) : [];
34
+ };