@bhsd/codemirror-mediawiki 3.1.0 → 3.2.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/dist/escape.js CHANGED
@@ -2,7 +2,7 @@ import { keymap } from '@codemirror/view';
2
2
  import { EditorSelection } from '@codemirror/state';
3
3
  import { indentMore, indentLess } from '@codemirror/commands';
4
4
  import { getLSP } from '@bhsd/browser';
5
- import { CodeMirror6 } from './codemirror';
5
+ import { CodeMirror6, menuRegistry } from './codemirror';
6
6
  const entity = { '"': 'quot', "'": 'apos', '<': 'lt', '>': 'gt', '&': 'amp', ' ': 'nbsp' };
7
7
  /**
8
8
  * 根据函数转换选中文本
@@ -32,35 +32,76 @@ export const escapeHTML = (str) => [...str].map(c => {
32
32
  }
33
33
  return encodeURIComponent(str);
34
34
  };
35
+ const escapeWiki = (cm) => {
36
+ const view = cm.view, { state } = view, { ranges } = state.selection, lsp = getLSP(view, false, cm.getWikiConfig);
37
+ if (lsp && 'provideRefactoringAction' in lsp && ranges.some(({ empty }) => !empty)) {
38
+ (async () => {
39
+ const replacements = new WeakMap();
40
+ for (const range of ranges) {
41
+ // eslint-disable-next-line no-await-in-loop
42
+ const [action] = await lsp.provideRefactoringAction(state.sliceDoc(range.from, range.to));
43
+ replacements.set(range, action?.edit.changes[''][0].newText);
44
+ }
45
+ view.dispatch(state.changeByRange(range => {
46
+ const insert = replacements.get(range);
47
+ if (insert === undefined) {
48
+ return { range };
49
+ }
50
+ return {
51
+ range: EditorSelection.range(range.from, range.from + insert.length),
52
+ changes: { from: range.from, to: range.to, insert },
53
+ };
54
+ }));
55
+ })();
56
+ return true;
57
+ }
58
+ return false;
59
+ };
60
+ const handlerBase = (view, e) => {
61
+ e.stopPropagation();
62
+ view.focus();
63
+ };
64
+ let items;
65
+ menuRegistry.push({
66
+ name: 'escape',
67
+ isActionable({ lang, view }) {
68
+ return lang === 'mediawiki' && view.state.selection.ranges.some(({ empty }) => !empty);
69
+ },
70
+ getItems(cm) {
71
+ if (!items) {
72
+ const view = cm.view, btnHTML = document.createElement('div'), btnURI = document.createElement('div');
73
+ btnHTML.textContent = 'HTML escape';
74
+ btnHTML.addEventListener('click', e => {
75
+ CodeMirror6.replaceSelections(view, escapeHTML);
76
+ handlerBase(view, e);
77
+ });
78
+ btnURI.textContent = 'URI encode/decode';
79
+ btnURI.addEventListener('click', e => {
80
+ CodeMirror6.replaceSelections(view, escapeURI);
81
+ handlerBase(view, e);
82
+ });
83
+ items = [btnHTML, btnURI];
84
+ const lsp = getLSP(view, false, cm.getWikiConfig);
85
+ if (lsp && 'provideRefactoringAction' in lsp) {
86
+ const btnWiki = document.createElement('div');
87
+ btnWiki.textContent = 'Escape with magic words';
88
+ btnWiki.addEventListener('click', e => {
89
+ escapeWiki(cm);
90
+ handlerBase(view, e);
91
+ });
92
+ items.unshift(btnWiki);
93
+ }
94
+ }
95
+ return items;
96
+ },
97
+ });
35
98
  export default (cm) => keymap.of([
36
99
  { key: 'Mod-[', run: convert(escapeHTML, indentLess) },
37
100
  { key: 'Mod-]', run: convert(escapeURI, indentMore) },
38
101
  {
39
102
  key: 'Mod-\\',
40
- run(view) {
41
- const { state } = view, { ranges } = state.selection, lsp = getLSP(view, false, cm.getWikiConfig);
42
- if (lsp && 'provideRefactoringAction' in lsp && ranges.some(({ empty }) => !empty)) {
43
- (async () => {
44
- const replacements = new WeakMap();
45
- for (const range of ranges) {
46
- // eslint-disable-next-line no-await-in-loop
47
- const [action] = await lsp.provideRefactoringAction(state.sliceDoc(range.from, range.to));
48
- replacements.set(range, action?.edit.changes[''][0].newText);
49
- }
50
- view.dispatch(state.changeByRange(range => {
51
- const insert = replacements.get(range);
52
- if (insert === undefined) {
53
- return { range };
54
- }
55
- return {
56
- range: EditorSelection.range(range.from, range.from + insert.length),
57
- changes: { from: range.from, to: range.to, insert },
58
- };
59
- }));
60
- })();
61
- return true;
62
- }
63
- return false;
103
+ run() {
104
+ return escapeWiki(cm);
64
105
  },
65
106
  },
66
107
  ]);
package/dist/fold.js CHANGED
@@ -323,7 +323,7 @@ const markers = /* @__PURE__ */ ViewPlugin.fromClass(class {
323
323
  }
324
324
  }
325
325
  });
326
- const defaultFoldExtension = [foldGutter(), keymap.of(foldKeymap)];
326
+ const defaultFoldExtension = /* @__PURE__ */ (() => [foldGutter(), keymap.of(foldKeymap)])();
327
327
  /**
328
328
  * 生成折叠命令
329
329
  * @param refOnly 是否仅检查`<ref>`标签
package/dist/html.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import { LanguageSupport } from '@codemirror/language';
2
+ import type { MwConfig } from './token';
3
+ declare const _default: (config: MwConfig) => LanguageSupport;
4
+ export default _default;
package/dist/html.js ADDED
@@ -0,0 +1,31 @@
1
+ import { configureNesting } from '@lezer/html';
2
+ import { htmlPlain, htmlCompletionSourceWith } from '@codemirror/lang-html';
3
+ import { javascript, javascriptLanguage } from '@codemirror/lang-javascript';
4
+ import { cssLanguage } from '@codemirror/lang-css';
5
+ import { LanguageSupport } from '@codemirror/language';
6
+ import { jsCompletion } from './javascript';
7
+ import { mediawiki } from './mediawiki';
8
+ import { cssCompletion } from './css';
9
+ export default (config) => {
10
+ const { language, support } = mediawiki(config), lang = new LanguageSupport(htmlPlain.configure({
11
+ wrap: configureNesting([
12
+ { tag: 'script', parser: javascriptLanguage.parser },
13
+ { tag: 'style', parser: cssLanguage.parser },
14
+ { tag: 'noinclude', parser: language.parser },
15
+ ], [{ name: 'style', parser: cssLanguage.parser.configure({ top: 'Styles' }) }]),
16
+ }), [
17
+ htmlPlain.data.of({
18
+ autocomplete: htmlCompletionSourceWith({
19
+ extraTags: {
20
+ noinclude: { globalAttrs: false },
21
+ },
22
+ }),
23
+ }),
24
+ javascript().support,
25
+ jsCompletion,
26
+ cssCompletion(),
27
+ support,
28
+ ]);
29
+ Object.assign(lang, { nestedMWLanguage: language });
30
+ return lang;
31
+ };
package/dist/indent.js CHANGED
@@ -1,4 +1,4 @@
1
- const noDetectionLangs = new Set(['plain', 'mediawiki', 'html']);
1
+ const noDetectionLangs = new Set(['plain', 'mediawiki']);
2
2
  const getLines = (text) => text.children?.flatMap(getLines) ?? text.text;
3
3
  /**
4
4
  * 检测文本的缩进方式
package/dist/index.d.ts CHANGED
@@ -54,7 +54,11 @@ export declare const registerCodeFoldingForMediaWiki: () => void;
54
54
  export declare const registerMediaWikiCore: () => void;
55
55
  /** Register mixed MediaWiki-HTML language support */
56
56
  export declare const registerHTML: () => void;
57
- /** Register HTML core language support */
57
+ /** Register the `closeBrackets` extension for mixed MediaWiki-HTML */
58
+ export declare const registerCloseBracketsForHTML: () => void;
59
+ /** Register the `colorPicker` extension for mixed MediaWiki-HTML */
60
+ export declare const registerColorPickerForHTML: () => void;
61
+ /** Register mixed MediaWiki-HTML core language support */
58
62
  export declare const registerHTMLCore: () => void;
59
63
  /** Register JavaScript language support */
60
64
  export declare const registerJavaScript: () => void;
package/dist/index.js CHANGED
@@ -4,29 +4,31 @@ import { highlightSelectionMatches } from '@codemirror/search';
4
4
  import { closeBrackets, autocompletion, acceptCompletion, completionKeymap, startCompletion, } from '@codemirror/autocomplete';
5
5
  import { json } from '@codemirror/lang-json';
6
6
  import { autoCloseTags } from '@codemirror/lang-html';
7
- import { css as cssParser } from '@codemirror/legacy-modes/mode/css';
8
7
  import { getLSP } from '@bhsd/browser';
9
8
  import { colorPicker as cssColorPicker, colorPickerTheme, makeColorPicker } from '@bhsd/codemirror-css-color-picker';
10
9
  import colorPicker, { discoverColors } from './color';
11
- import { mediawiki, html, FullMediaWiki } from './mediawiki';
10
+ import { mediawiki } from './mediawiki';
12
11
  import escape from './escape';
13
- import codeFolding, { mediaWikiFold } from './fold';
12
+ import codeFolding, { mediaWikiFold, foldHandler } from './fold';
14
13
  import tagMatchingState from './matchTag';
15
14
  import refHover from './ref';
16
15
  import magicWordHover from './hover';
17
16
  import signatureHelp from './signature';
18
17
  import inlayHints from './inlay';
19
- import { getWikiLintSource, getJsLintSource, getCssLintSource, getJsonLintSource, getLuaLintSource, getVueLintSource, } from './lintsource';
18
+ import { getWikiLintSource, getJsLintSource, getCssLintSource, getJsonLintSource, getLuaLintSource, getVueLintSource, getHTMLLintSource, } from './lintsource';
20
19
  import openLinks from './openLinks';
21
20
  import { tagModes, getStaticMwConfig } from './static';
22
21
  import bidiIsolation from './bidi';
23
22
  import toolKeymap from './keymap';
24
23
  import bracketMatching from './matchBrackets';
24
+ import statusBar from './statusBar';
25
+ import { detectIndent } from './indent';
25
26
  import javascript from './javascript';
26
27
  import css from './css';
27
28
  import lua from './lua';
28
29
  import vue from './vue';
29
- import { CodeMirror6, avail, languages, linterRegistry, destroyListeners, plain } from './codemirror';
30
+ import html from './html';
31
+ import { CodeMirror6, avail, languages, linterRegistry, destroyListeners, plain, optionalFunctions } from './codemirror';
30
32
  export { CodeMirror6 };
31
33
  /**
32
34
  * 注册通用扩展
@@ -200,6 +202,16 @@ export const registerBracketMatchingForMediaWiki = () => {
200
202
  /** Register the `codeFolding` extension for MediaWiki */
201
203
  export const registerCodeFoldingForMediaWiki = () => {
202
204
  registerLangExtension('mediawiki', 'codeFolding', mediaWikiFold);
205
+ optionalFunctions.foldHandler = foldHandler;
206
+ };
207
+ /**
208
+ * 注册LintSource
209
+ * @param lang 语言
210
+ * @param lintSource
211
+ */
212
+ const registerLintSource = (lang, lintSource) => {
213
+ linterRegistry[lang] = lintSource;
214
+ optionalFunctions.statusBar = statusBar;
203
215
  };
204
216
  /** Register MediaWiki core language support */
205
217
  export const registerMediaWikiCore = () => {
@@ -210,22 +222,29 @@ export const registerMediaWikiCore = () => {
210
222
  bidiIsolation,
211
223
  toolKeymap,
212
224
  ];
213
- linterRegistry['mediawiki'] = getWikiLintSource;
225
+ registerLintSource('mediawiki', getWikiLintSource);
214
226
  destroyListeners.push(view => getLSP(view)?.destroy());
215
227
  };
216
228
  /** Register mixed MediaWiki-HTML language support */
217
229
  export const registerHTML = () => {
218
230
  registerCommonExtensions();
219
231
  registerHTMLCore();
232
+ registerCloseBracketsForHTML();
233
+ registerColorPickerForHTML();
234
+ };
235
+ /** Register the `closeBrackets` extension for mixed MediaWiki-HTML */
236
+ export const registerCloseBracketsForHTML = () => {
237
+ registerLangExtension('html', 'closeBrackets', autoCloseTags);
238
+ };
239
+ /** Register the `colorPicker` extension for mixed MediaWiki-HTML */
240
+ export const registerColorPickerForHTML = () => {
241
+ registerLangExtension('html', 'colorPicker', [cssColorPicker]);
220
242
  };
221
- /** Register HTML core language support */
243
+ /** Register mixed MediaWiki-HTML core language support */
222
244
  export const registerHTMLCore = () => {
223
- Object.assign(FullMediaWiki.prototype, {
224
- css() {
225
- return cssParser;
226
- },
227
- });
228
245
  languages['html'] = html;
246
+ registerLintSource('html', getHTMLLintSource);
247
+ optionalFunctions.detectIndent = detectIndent;
229
248
  };
230
249
  /** Register JavaScript language support */
231
250
  export const registerJavaScript = () => {
@@ -235,7 +254,8 @@ export const registerJavaScript = () => {
235
254
  /** Register JavaScript core language support */
236
255
  export const registerJavaScriptCore = () => {
237
256
  languages['javascript'] = javascript;
238
- linterRegistry['javascript'] = getJsLintSource;
257
+ registerLintSource('javascript', getJsLintSource);
258
+ optionalFunctions.detectIndent = detectIndent;
239
259
  };
240
260
  /** Register CSS language support */
241
261
  export const registerCSS = () => {
@@ -250,7 +270,8 @@ export const registerColorPickerForCSS = () => {
250
270
  /** Register CSS core language support */
251
271
  export const registerCSSCore = () => {
252
272
  languages['css'] = css;
253
- linterRegistry['css'] = getCssLintSource;
273
+ registerLintSource('css', getCssLintSource);
274
+ optionalFunctions.detectIndent = detectIndent;
254
275
  };
255
276
  /** Register JSON language support */
256
277
  export const registerJSON = () => {
@@ -260,7 +281,8 @@ export const registerJSON = () => {
260
281
  /** Register JSON core language support */
261
282
  export const registerJSONCore = () => {
262
283
  languages['json'] = json;
263
- linterRegistry['json'] = getJsonLintSource;
284
+ registerLintSource('json', getJsonLintSource);
285
+ optionalFunctions.detectIndent = detectIndent;
264
286
  };
265
287
  /** Register Lua language support */
266
288
  export const registerLua = () => {
@@ -270,7 +292,8 @@ export const registerLua = () => {
270
292
  /** Register Lua core language support */
271
293
  export const registerLuaCore = () => {
272
294
  languages['lua'] = lua;
273
- linterRegistry['lua'] = getLuaLintSource;
295
+ registerLintSource('lua', getLuaLintSource);
296
+ optionalFunctions.detectIndent = detectIndent;
274
297
  };
275
298
  /** Register Vue language support */
276
299
  export const registerVue = () => {
@@ -290,7 +313,8 @@ export const registerColorPickerForVue = () => {
290
313
  /** Register Vue core language support */
291
314
  export const registerVueCore = () => {
292
315
  languages['vue'] = vue;
293
- linterRegistry['vue'] = getVueLintSource;
316
+ registerLintSource('vue', getVueLintSource);
317
+ optionalFunctions.detectIndent = detectIndent;
294
318
  };
295
319
  /**
296
320
  * Register a custom language support
@@ -311,6 +335,6 @@ export const registerLanguage = (name, lang, lintSource) => {
311
335
  export const registerLanguageCore = (name, lang, lintSource) => {
312
336
  languages[name] = lang;
313
337
  if (lintSource) {
314
- linterRegistry[name] = lintSource;
338
+ registerLintSource(name, lintSource);
315
339
  }
316
340
  };
@@ -1,14 +1,16 @@
1
1
  import type { EditorView } from '@codemirror/view';
2
2
  import type { EditorState, Text } from '@codemirror/state';
3
+ import type { Language } from '@codemirror/language';
3
4
  import type { Diagnostic } from '@codemirror/lint';
4
5
  import type { Option, LiveOption } from './linter';
5
6
  export type LintSource = ((state: EditorState) => Diagnostic[] | Promise<Diagnostic[]>) & {
6
7
  fixer?: (doc: Text, rule?: string) => string | Promise<string>;
7
8
  };
8
- export type LintSourceGetter = (opt?: Option | LiveOption, view?: EditorView) => LintSource | Promise<LintSource>;
9
+ export type LintSourceGetter = (opt?: Option | LiveOption, view?: EditorView, nestedMWLanguage?: Language) => LintSource | Promise<LintSource>;
9
10
  export declare const getWikiLintSource: LintSourceGetter;
10
11
  export declare const getJsLintSource: LintSourceGetter;
11
12
  export declare const getCssLintSource: LintSourceGetter;
12
13
  export declare const getVueLintSource: LintSourceGetter;
14
+ export declare const getHTMLLintSource: LintSourceGetter;
13
15
  export declare const getJsonLintSource: LintSourceGetter;
14
16
  export declare const getLuaLintSource: LintSourceGetter;
@@ -25,33 +25,6 @@ const pos = (doc, line, column, from = 0) => {
25
25
  character: (line === 1 ? from - lineDesc.from : 0) + column - 1,
26
26
  });
27
27
  };
28
- export const getWikiLintSource = async (opt, v) => {
29
- const wikiLint = await getWikiLinter(await getOpt(opt), v);
30
- const lintSource = async ({ doc }) => (await wikiLint(doc.toString(), await getOpt(opt, true)))
31
- .map(({ severity, code, message, range: r, from, to, data = [], source }) => ({
32
- source: source,
33
- from: from ?? posToIndex(doc, r.start),
34
- to: to ?? posToIndex(doc, r.end),
35
- severity: severity === 2 ? 'warning' : 'error',
36
- message: source === 'Stylelint' ? message : `${message} (${code})`,
37
- actions: data.map(({ title, range, newText }) => ({
38
- name: title,
39
- apply(view) {
40
- view.dispatch({
41
- changes: {
42
- from: posToIndex(doc, range.start),
43
- to: posToIndex(doc, range.end),
44
- insert: newText,
45
- },
46
- });
47
- },
48
- })),
49
- }));
50
- if (wikiLint.fixer) {
51
- lintSource.fixer = (_, rule) => wikiLint.fixer('', rule);
52
- }
53
- return lintSource;
54
- };
55
28
  const getRange = (doc, line, column, endLine, endColumn, f = 0, t = Infinity) => {
56
29
  const start = pos(doc, line, column, f);
57
30
  return {
@@ -59,6 +32,35 @@ const getRange = (doc, line, column, endLine, endColumn, f = 0, t = Infinity) =>
59
32
  to: endLine === undefined ? Math.min(t, start + 1) : pos(doc, endLine, endColumn, f),
60
33
  };
61
34
  };
35
+ const wikiLintSource = async (wikiLint, text, opt, doc, f = 0, t) => (await wikiLint(text, opt))
36
+ .map(({ severity, code, message, range: r, from, to, data = [], source }) => ({
37
+ source: source,
38
+ severity: severity === 2 ? 'warning' : 'error',
39
+ message: source === 'Stylelint' ? message : `${message} (${code})`,
40
+ actions: data.map(({ title, range, newText }) => ({
41
+ name: title,
42
+ apply(view) {
43
+ view.dispatch({
44
+ changes: {
45
+ from: posToIndex(doc, range.start),
46
+ to: posToIndex(doc, range.end),
47
+ insert: newText,
48
+ },
49
+ });
50
+ },
51
+ })),
52
+ ...from === undefined
53
+ ? getRange(doc, r.start.line + 1, r.start.character + 1, r.end.line + 1, r.end.character + 1, f, t)
54
+ : { from: from + f, to: (to ?? from) + f },
55
+ }));
56
+ export const getWikiLintSource = async (opt, v) => {
57
+ const wikiLint = await getWikiLinter(await getOpt(opt), v);
58
+ const lintSource = async ({ doc }) => wikiLintSource(wikiLint, doc.toString(), await getOpt(opt, true), doc);
59
+ if (wikiLint.fixer) {
60
+ lintSource.fixer = (_, rule) => wikiLint.fixer('', rule);
61
+ }
62
+ return lintSource;
63
+ };
62
64
  const jsLintSource = (esLint, code, opt, doc, f = 0, t) => esLint(code, opt)
63
65
  .map(({ ruleId, message, severity, line, column, endLine, endColumn, fix, suggestions = [] }) => {
64
66
  const diagnostic = {
@@ -80,6 +82,12 @@ const jsLintSource = (esLint, code, opt, doc, f = 0, t) => esLint(code, opt)
80
82
  }
81
83
  return diagnostic;
82
84
  });
85
+ export const getJsLintSource = async (opt) => {
86
+ const esLint = await getJsLinter();
87
+ const lintSource = async ({ doc }) => jsLintSource(esLint, doc.toString(), await getOpt(opt), doc);
88
+ lintSource.fixer = (doc, rule) => esLint.fixer(doc.toString(), rule);
89
+ return lintSource;
90
+ };
83
91
  const cssLintSource = async (styleLint, code, opt, doc, f = 0, t) => {
84
92
  let option = opt ?? {};
85
93
  if (!('extends' in option || 'rules' in option)) {
@@ -108,12 +116,6 @@ const cssLintSource = async (styleLint, code, opt, doc, f = 0, t) => {
108
116
  return diagnostic;
109
117
  });
110
118
  };
111
- export const getJsLintSource = async (opt) => {
112
- const esLint = await getJsLinter();
113
- const lintSource = async ({ doc }) => jsLintSource(esLint, doc.toString(), await getOpt(opt), doc);
114
- lintSource.fixer = (doc, rule) => esLint.fixer(doc.toString(), rule);
115
- return lintSource;
116
- };
117
119
  export const getCssLintSource = async (opt) => {
118
120
  const styleLint = await getCssLinter();
119
121
  const lintSource = async ({ doc }) => cssLintSource(styleLint, doc.toString(), await getOpt(opt), doc);
@@ -132,6 +134,17 @@ export const getVueLintSource = async (opt) => {
132
134
  ];
133
135
  };
134
136
  };
137
+ export const getHTMLLintSource = async (opt, view, language) => {
138
+ const vueLintSource = await getVueLintSource(opt), wikiLint = await getWikiLinter({ include: false, ...await getOpt(opt) }, view);
139
+ return async (state) => {
140
+ const { doc } = state, option = await getOpt(opt) ?? {}, wiki = option['wiki'];
141
+ return [
142
+ ...await vueLintSource(state),
143
+ ...(await Promise.all(language.findRegions(state)
144
+ .map(({ from, to }) => wikiLintSource(wikiLint, state.sliceDoc(from, to), wiki, doc, from, to)))).flat(),
145
+ ];
146
+ };
147
+ };
135
148
  export const getJsonLintSource = () => {
136
149
  const jsonLint = getJsonLinter();
137
150
  return ({ doc }) => {