@bhsd/codemirror-mediawiki 2.29.2 → 2.30.1
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 +0 -1
- package/dist/codemirror.js +23 -8
- package/dist/config.d.ts +1 -0
- package/dist/config.js +1 -0
- package/dist/fold.d.ts +4 -2
- package/dist/fold.js +31 -14
- package/dist/indent.d.ts +2 -2
- package/dist/indent.js +3 -3
- package/dist/linter.d.ts +15 -6
- package/dist/linter.js +18 -9
- package/dist/main.min.js +20 -20
- package/dist/mw.min.js +25 -25
- package/dist/openLinks.js +19 -8
- package/dist/statusBar.js +2 -2
- package/dist/token.js +3 -3
- package/dist/wiki.min.js +24 -24
- package/i18n/en.json +3 -2
- package/i18n/zh-hans.json +3 -2
- package/i18n/zh-hant.json +3 -2
- package/mediawiki.css +1 -1
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
[](https://www.npmjs.com/package/@bhsd/codemirror-mediawiki)
|
|
2
|
-
[](https://github.com/bhsd-harry/codemirror-mediawiki/actions/workflows/codeql.yml)
|
|
3
2
|
[](https://www.npmjs.com/package/@bhsd/codemirror-mediawiki)
|
|
4
3
|
[](https://app.codacy.com/gh/bhsd-harry/codemirror-mediawiki/dashboard)
|
|
5
4
|
|
package/dist/codemirror.js
CHANGED
|
@@ -22,7 +22,7 @@ import { tagModes, getStaticMwConfig } from './static';
|
|
|
22
22
|
import bidiIsolation from './bidi';
|
|
23
23
|
import toolKeymap from './keymap';
|
|
24
24
|
import statusBar from './statusBar';
|
|
25
|
-
import { detectIndent
|
|
25
|
+
import { detectIndent } from './indent';
|
|
26
26
|
import bracketMatching from './matchBrackets';
|
|
27
27
|
import javascript from './javascript';
|
|
28
28
|
import css from './css';
|
|
@@ -89,7 +89,7 @@ const avail = {
|
|
|
89
89
|
hover: mediawikiOnly(magicWordHover),
|
|
90
90
|
signatureHelp: mediawikiOnly(signatureHelp),
|
|
91
91
|
inlayHints: mediawikiOnly(inlayHints),
|
|
92
|
-
};
|
|
92
|
+
}, editExtensions = new Set(['closeBrackets', 'autocompletion', 'signatureHelp']);
|
|
93
93
|
const linters = {};
|
|
94
94
|
const phrases = {};
|
|
95
95
|
/**
|
|
@@ -185,8 +185,8 @@ export class CodeMirror6 {
|
|
|
185
185
|
textarea.value = doc.toString();
|
|
186
186
|
textarea.dispatchEvent(new Event('input'));
|
|
187
187
|
}, 400);
|
|
188
|
-
if (!
|
|
189
|
-
this.setIndent(
|
|
188
|
+
if (!startDoc.toString().trim()) {
|
|
189
|
+
this.setIndent(this.#indentStr);
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
192
|
if (focusChanged) {
|
|
@@ -194,7 +194,13 @@ export class CodeMirror6 {
|
|
|
194
194
|
}
|
|
195
195
|
}),
|
|
196
196
|
...readOnly
|
|
197
|
-
? [
|
|
197
|
+
? [
|
|
198
|
+
EditorState.readOnly.of(true),
|
|
199
|
+
EditorState.transactionFilter.of(tr => tr.docChanged ? [] : tr),
|
|
200
|
+
EditorView.theme({
|
|
201
|
+
'input[type="color"]': { pointerEvents: 'none' },
|
|
202
|
+
}),
|
|
203
|
+
]
|
|
198
204
|
: [
|
|
199
205
|
history(),
|
|
200
206
|
indentOnInput(),
|
|
@@ -261,7 +267,15 @@ export class CodeMirror6 {
|
|
|
261
267
|
lint(lintSource) {
|
|
262
268
|
const linterExtension = lintSource
|
|
263
269
|
? [
|
|
264
|
-
linter(
|
|
270
|
+
linter(async ({ state: { doc, readOnly } }) => {
|
|
271
|
+
const diagnostics = await lintSource(doc);
|
|
272
|
+
if (readOnly) {
|
|
273
|
+
for (const diagnostic of diagnostics) {
|
|
274
|
+
delete diagnostic.actions;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return diagnostics;
|
|
278
|
+
}),
|
|
265
279
|
lintGutter(),
|
|
266
280
|
keymap.of(lintKeymap),
|
|
267
281
|
statusBar(lintSource.fixer),
|
|
@@ -308,7 +322,8 @@ export class CodeMirror6 {
|
|
|
308
322
|
}
|
|
309
323
|
}
|
|
310
324
|
if (this.#view) {
|
|
311
|
-
this.#
|
|
325
|
+
const { readOnly } = this.#view.state;
|
|
326
|
+
this.#effects(this.#extensions.reconfigure([...this.#preferred].filter(name => !readOnly || !editExtensions.has(name)).map(name => {
|
|
312
327
|
const [extension, configs] = avail[name];
|
|
313
328
|
return extension(configs[this.#lang], this);
|
|
314
329
|
})));
|
|
@@ -320,7 +335,7 @@ export class CodeMirror6 {
|
|
|
320
335
|
*/
|
|
321
336
|
setIndent(indent) {
|
|
322
337
|
if (this.#view) {
|
|
323
|
-
this.#effects(this.#indent.reconfigure(indentUnit.of(indent)));
|
|
338
|
+
this.#effects(this.#indent.reconfigure(indentUnit.of(detectIndent(this.#view.state.doc, indent, this.#lang))));
|
|
324
339
|
}
|
|
325
340
|
else {
|
|
326
341
|
this.#indentStr = indent;
|
package/dist/config.d.ts
CHANGED
package/dist/config.js
CHANGED
|
@@ -112,6 +112,7 @@ var tokens = {
|
|
|
112
112
|
htmlTagAttributeValue: "mw-htmltag-attribute-value",
|
|
113
113
|
htmlTagBracket: "mw-htmltag-bracket",
|
|
114
114
|
htmlTagName: "mw-htmltag-name",
|
|
115
|
+
ignored: "mw-ignored",
|
|
115
116
|
imageParameter: "mw-image-parameter",
|
|
116
117
|
linkBracket: "mw-link-bracket",
|
|
117
118
|
linkDelimiter: "mw-link-delimiter",
|
package/dist/fold.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { StateField } from '@codemirror/state';
|
|
2
|
-
import type { EditorView, Tooltip } from '@codemirror/view';
|
|
2
|
+
import type { EditorView, Tooltip, Command } from '@codemirror/view';
|
|
3
3
|
import type { EditorState, Extension } from '@codemirror/state';
|
|
4
4
|
import type { SyntaxNode, Tree } from '@lezer/common';
|
|
5
5
|
export interface DocRange {
|
|
@@ -17,9 +17,11 @@ export declare const braceStackUpdate: (state: EditorState, node: SyntaxNode) =>
|
|
|
17
17
|
* @param state
|
|
18
18
|
* @param posOrNode 字符位置或语法树节点
|
|
19
19
|
* @param tree 语法树
|
|
20
|
+
* @param refOnly 是否仅检查`<ref>`标签
|
|
20
21
|
*/
|
|
21
|
-
export declare const foldable: (state: EditorState, posOrNode: number | SyntaxNode, tree?: Tree | null) => DocRange | false;
|
|
22
|
+
export declare const foldable: (state: EditorState, posOrNode: number | SyntaxNode, tree?: Tree | null, refOnly?: boolean) => DocRange | false;
|
|
22
23
|
export declare const foldableLine: ({ state, viewport: { to: end }, viewportLineBlocks }: EditorView, { from: f, to: t }: DocRange) => DocRange | false;
|
|
24
|
+
export declare const foldRef: Command;
|
|
23
25
|
declare const _default: [(e?: Extension | undefined) => Extension, {
|
|
24
26
|
mediawiki: (Extension | StateField<Tooltip | null>)[];
|
|
25
27
|
}];
|
package/dist/fold.js
CHANGED
|
@@ -3,7 +3,7 @@ import { StateField, RangeSetBuilder, RangeSet } from '@codemirror/state';
|
|
|
3
3
|
import { syntaxTree, ensureSyntaxTree, foldEffect, unfoldEffect, foldedRanges, unfoldAll, codeFolding, foldGutter, foldKeymap, foldState, language, } from '@codemirror/language';
|
|
4
4
|
import { getRegex } from '@bhsd/common';
|
|
5
5
|
import { tokens } from './config';
|
|
6
|
-
import { matchTag } from './matchTag';
|
|
6
|
+
import { matchTag, getTag } from './matchTag';
|
|
7
7
|
const getExtRegex = getRegex(tag => new RegExp(`mw-tag-${tag}(?![a-z])`, 'u'));
|
|
8
8
|
const updateSelection = (pos, { to }) => Math.max(pos, to), updateAll = (pos, { from, to }) => from <= pos && to > pos ? to : pos;
|
|
9
9
|
/**
|
|
@@ -25,8 +25,9 @@ isExtBracket = isComponent(['extTagBracket']),
|
|
|
25
25
|
/**
|
|
26
26
|
* Check if a SyntaxNode is part of a extension tag
|
|
27
27
|
* @param node 语法树节点
|
|
28
|
+
* @param refOnly 是否仅检查`<ref>`标签
|
|
28
29
|
*/
|
|
29
|
-
isExt = (node) => node.name.includes(
|
|
30
|
+
isExt = (node, refOnly) => node.name.includes(`mw-tag-${refOnly ? 'ref' : ''}`);
|
|
30
31
|
/**
|
|
31
32
|
* Update the stack of opening (+) or closing (-) brackets
|
|
32
33
|
* @param state
|
|
@@ -36,13 +37,15 @@ export const braceStackUpdate = (state, node) => {
|
|
|
36
37
|
const brackets = state.sliceDoc(node.from, node.to);
|
|
37
38
|
return [brackets.split('{{').length - 1, 1 - brackets.split('}}').length];
|
|
38
39
|
};
|
|
40
|
+
const refNames = new Set(['ref', 'references']);
|
|
39
41
|
/**
|
|
40
42
|
* 寻找可折叠的范围
|
|
41
43
|
* @param state
|
|
42
44
|
* @param posOrNode 字符位置或语法树节点
|
|
43
45
|
* @param tree 语法树
|
|
46
|
+
* @param refOnly 是否仅检查`<ref>`标签
|
|
44
47
|
*/
|
|
45
|
-
export const foldable = (state, posOrNode, tree) => {
|
|
48
|
+
export const foldable = (state, posOrNode, tree, refOnly = false) => {
|
|
46
49
|
if (typeof posOrNode === 'number') {
|
|
47
50
|
tree = ensureSyntaxTree(state, posOrNode); // eslint-disable-line no-param-reassign
|
|
48
51
|
}
|
|
@@ -53,12 +56,12 @@ export const foldable = (state, posOrNode, tree) => {
|
|
|
53
56
|
if (typeof posOrNode === 'number') {
|
|
54
57
|
// Find the initial template node on both sides of the position
|
|
55
58
|
const left = tree.resolve(posOrNode, -1);
|
|
56
|
-
if (isTemplate(left)) {
|
|
59
|
+
if (!refOnly && isTemplate(left)) {
|
|
57
60
|
node = left;
|
|
58
61
|
}
|
|
59
62
|
else {
|
|
60
63
|
const right = tree.resolve(posOrNode, 1);
|
|
61
|
-
node = isExt(left)
|
|
64
|
+
node = isExt(left, refOnly)
|
|
62
65
|
&& left.name.split('mw-tag-').length > right.name.split('mw-tag-').length
|
|
63
66
|
? left
|
|
64
67
|
: right;
|
|
@@ -67,15 +70,17 @@ export const foldable = (state, posOrNode, tree) => {
|
|
|
67
70
|
else {
|
|
68
71
|
node = posOrNode;
|
|
69
72
|
}
|
|
70
|
-
if (!isTemplate(node)) {
|
|
73
|
+
if (refOnly || !isTemplate(node)) {
|
|
71
74
|
// Not a template
|
|
72
|
-
if (isExt(node)) {
|
|
75
|
+
if (isExt(node, refOnly)) {
|
|
73
76
|
const { name } = node, [tag] = /^[a-z]+/u.exec(name.slice(name.lastIndexOf('mw-tag-') + 7)), regex = getExtRegex(tag);
|
|
74
77
|
let { nextSibling } = node;
|
|
75
78
|
while (nextSibling && !(isExtBracket(nextSibling) && !regex.test(nextSibling.name))) {
|
|
76
79
|
({ nextSibling } = nextSibling);
|
|
77
80
|
}
|
|
78
|
-
|
|
81
|
+
const next = nextSibling?.nextSibling;
|
|
82
|
+
// The closing bracket of the current extension tag
|
|
83
|
+
if (nextSibling && (!refOnly || next && refNames.has(getTag(state, next)?.name))) {
|
|
79
84
|
return { from: matchTag(state, nextSibling.to).end.to, to: nextSibling.from };
|
|
80
85
|
}
|
|
81
86
|
}
|
|
@@ -189,11 +194,12 @@ const getAnchor = (state) => Math.max(...state.selection.ranges.map(({ to }) =>
|
|
|
189
194
|
* @param end 终止位置
|
|
190
195
|
* @param anchor 光标位置
|
|
191
196
|
* @param update 更新光标位置
|
|
197
|
+
* @param refOnly 是否仅检查`<ref>`标签
|
|
192
198
|
*/
|
|
193
|
-
const traverse = (state, tree, effects, node, end, anchor, update) => {
|
|
199
|
+
const traverse = (state, tree, effects, node, end, anchor, update, refOnly) => {
|
|
194
200
|
while (node && node.from <= end) {
|
|
195
201
|
/* eslint-disable no-param-reassign */
|
|
196
|
-
const range = foldable(state, node, tree);
|
|
202
|
+
const range = foldable(state, node, tree, refOnly);
|
|
197
203
|
if (range) {
|
|
198
204
|
effects.push(foldEffect.of(range));
|
|
199
205
|
node = tree.resolve(range.to, 1);
|
|
@@ -311,6 +317,15 @@ const markers = ViewPlugin.fromClass(class {
|
|
|
311
317
|
}
|
|
312
318
|
});
|
|
313
319
|
const defaultFoldExtension = [foldGutter(), keymap.of(foldKeymap)];
|
|
320
|
+
/**
|
|
321
|
+
* 生成折叠命令
|
|
322
|
+
* @param refOnly 是否仅检查`<ref>`标签
|
|
323
|
+
*/
|
|
324
|
+
const foldCommand = (refOnly) => view => {
|
|
325
|
+
const { state } = view, tree = syntaxTree(state), effects = [], anchor = traverse(state, tree, effects, tree.topNode.firstChild, Infinity, getAnchor(state), updateAll, refOnly);
|
|
326
|
+
return execute(view, effects, anchor);
|
|
327
|
+
};
|
|
328
|
+
export const foldRef = foldCommand(true);
|
|
314
329
|
export default [
|
|
315
330
|
(e = defaultFoldExtension) => e,
|
|
316
331
|
{
|
|
@@ -372,10 +387,12 @@ export default [
|
|
|
372
387
|
{
|
|
373
388
|
// Fold all templates in the document
|
|
374
389
|
key: 'Ctrl-Alt-[',
|
|
375
|
-
run(
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
390
|
+
run: foldCommand(),
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
// Fold all `<ref>` tags in the document
|
|
394
|
+
key: 'Mod-Alt-,',
|
|
395
|
+
run: foldRef,
|
|
379
396
|
},
|
|
380
397
|
{
|
|
381
398
|
// Unfold the template at the selection/cursor
|
package/dist/indent.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
import type { Text } from '@codemirror/state';
|
|
2
2
|
/**
|
|
3
3
|
* 检测文本的缩进方式
|
|
4
4
|
* @param text 文本内容
|
|
5
5
|
* @param defaultIndent 默认缩进方式
|
|
6
6
|
* @param lang 语言
|
|
7
7
|
*/
|
|
8
|
-
export declare const detectIndent: (text: string, defaultIndent: string, lang: string) => string;
|
|
8
|
+
export declare const detectIndent: (text: string | Text, defaultIndent: string, lang: string) => string;
|
package/dist/indent.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
const noDetectionLangs = new Set(['plain', 'mediawiki', 'html']);
|
|
2
2
|
/**
|
|
3
3
|
* 检测文本的缩进方式
|
|
4
4
|
* @param text 文本内容
|
|
@@ -9,9 +9,9 @@ export const detectIndent = (text, defaultIndent, lang) => {
|
|
|
9
9
|
if (noDetectionLangs.has(lang)) {
|
|
10
10
|
return defaultIndent;
|
|
11
11
|
}
|
|
12
|
-
const lineSpaces = [];
|
|
12
|
+
const lineSpaces = [], lines = typeof text === 'string' ? text.split('\n') : text.text;
|
|
13
13
|
let tabLines = 0;
|
|
14
|
-
for (const line of
|
|
14
|
+
for (const line of lines) {
|
|
15
15
|
if (!line.trim()) {
|
|
16
16
|
continue;
|
|
17
17
|
}
|
package/dist/linter.d.ts
CHANGED
|
@@ -33,12 +33,21 @@ declare interface JsonError {
|
|
|
33
33
|
*/
|
|
34
34
|
export declare const getWikiLinter: getAsyncLinter<Promise<MixedDiagnostic[]>, Option, object>;
|
|
35
35
|
export declare const jsConfig: Linter.Config<Linter.RulesRecord, Linter.RulesRecord>;
|
|
36
|
-
/**
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
/**
|
|
37
|
+
* 获取 ESLint
|
|
38
|
+
* @param cdn CDN 地址
|
|
39
|
+
*/
|
|
40
|
+
export declare const getJsLinter: getAsyncLinter<Linter.LintMessage[], string>;
|
|
41
|
+
/**
|
|
42
|
+
* 获取 Stylelint
|
|
43
|
+
* @param cdn CDN 地址
|
|
44
|
+
*/
|
|
45
|
+
export declare const getCssLinter: getAsyncLinter<Promise<Warning[]>, string>;
|
|
46
|
+
/**
|
|
47
|
+
* 获取 Luacheck
|
|
48
|
+
* @param cdn CDN 地址
|
|
49
|
+
*/
|
|
50
|
+
export declare const getLuaLinter: getAsyncLinter<Promise<Diagnostic[]>, string>;
|
|
42
51
|
/** JSON.parse */
|
|
43
52
|
export declare const getJsonLinter: getLinter<JsonError[]>;
|
|
44
53
|
export {};
|
package/dist/linter.js
CHANGED
|
@@ -57,9 +57,12 @@ export const jsConfig = /* #__PURE__ */ (() => ({
|
|
|
57
57
|
importStylesheetURI: 'readonly',
|
|
58
58
|
},
|
|
59
59
|
}))();
|
|
60
|
-
/**
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
/**
|
|
61
|
+
* 获取 ESLint
|
|
62
|
+
* @param cdn CDN 地址
|
|
63
|
+
*/
|
|
64
|
+
export const getJsLinter = async (cdn = 'npm/@bhsd/eslint-browserify') => {
|
|
65
|
+
await loadScript(cdn, 'eslint');
|
|
63
66
|
/** @see https://www.npmjs.com/package/@codemirror/lang-javascript */
|
|
64
67
|
const esLinter = new eslint.Linter(), conf = {
|
|
65
68
|
env: { browser: true, es2024: true },
|
|
@@ -84,9 +87,12 @@ export const getJsLinter = async () => {
|
|
|
84
87
|
linter.fixer = (code, rule) => esLinter.verifyAndFix(code, rule ? { ...linter.config, rules: { [rule]: linter.config.rules?.[rule] ?? 2 } } : linter.config).output;
|
|
85
88
|
return linter;
|
|
86
89
|
};
|
|
87
|
-
/**
|
|
88
|
-
|
|
89
|
-
|
|
90
|
+
/**
|
|
91
|
+
* 获取 Stylelint
|
|
92
|
+
* @param cdn CDN 地址
|
|
93
|
+
*/
|
|
94
|
+
export const getCssLinter = async (cdn = 'npm/@bhsd/stylelint-browserify') => {
|
|
95
|
+
await loadScript(cdn, 'stylelint');
|
|
90
96
|
const linter = async (code, opt) => {
|
|
91
97
|
const warnings = await styleLint(stylelint, code, opt);
|
|
92
98
|
if (opt && 'rules' in opt) {
|
|
@@ -102,9 +108,12 @@ export const getCssLinter = async () => {
|
|
|
102
108
|
};
|
|
103
109
|
return linter;
|
|
104
110
|
};
|
|
105
|
-
/**
|
|
106
|
-
|
|
107
|
-
|
|
111
|
+
/**
|
|
112
|
+
* 获取 Luacheck
|
|
113
|
+
* @param cdn CDN 地址
|
|
114
|
+
*/
|
|
115
|
+
export const getLuaLinter = async (cdn = 'npm/luacheck-browserify') => {
|
|
116
|
+
await loadScript(cdn, 'luacheck');
|
|
108
117
|
// eslint-disable-next-line @typescript-eslint/await-thenable
|
|
109
118
|
const luachecker = await luacheck(undefined);
|
|
110
119
|
return async (text) => (await luachecker.queue(text)).filter(({ severity }) => severity);
|