@bhsd/codemirror-mediawiki 3.10.0 → 3.10.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/README.md +78 -78
- package/dist/bidi.d.ts +5 -0
- package/dist/bidi.js +5 -0
- package/dist/codemirror.d.ts +7 -0
- package/dist/codemirror.js +10 -3
- package/dist/color.d.ts +7 -4
- package/dist/color.js +4 -0
- package/dist/css.d.ts +5 -0
- package/dist/css.js +5 -0
- package/dist/escape.d.ts +21 -1
- package/dist/escape.js +42 -24
- package/dist/fold.d.ts +55 -3
- package/dist/fold.js +54 -30
- package/dist/hover.d.ts +14 -1
- package/dist/hover.js +49 -31
- package/dist/html.js +17 -12
- package/dist/indent.d.ts +7 -0
- package/dist/indent.js +7 -1
- package/dist/inlay.js +1 -1
- package/dist/javascript.d.ts +10 -1
- package/dist/javascript.js +6 -1
- package/dist/keybindings.d.ts +1 -0
- package/dist/keybindings.js +1 -0
- package/dist/keymap.d.ts +11 -0
- package/dist/keymap.js +3 -4
- package/dist/linter.d.ts +16 -0
- package/dist/linter.js +8 -1
- package/dist/lintsource.d.ts +36 -4
- package/dist/lintsource.js +37 -4
- package/dist/lua.js +31 -20
- package/dist/main.min.js +29 -29
- package/dist/matchBrackets.d.ts +16 -0
- package/dist/matchBrackets.js +16 -0
- package/dist/matchTag.d.ts +5 -2
- package/dist/matchTag.js +7 -4
- package/dist/mediawiki.d.ts +11 -0
- package/dist/mediawiki.js +8 -4
- package/dist/mw.min.js +31 -31
- package/dist/mwConfig.js +1 -1
- package/dist/openLinks.d.ts +8 -0
- package/dist/openLinks.js +8 -0
- package/dist/ref.d.ts +15 -1
- package/dist/ref.js +81 -65
- package/dist/signature.d.ts +6 -0
- package/dist/signature.js +22 -19
- package/dist/static.d.ts +4 -0
- package/dist/static.js +4 -0
- package/dist/theme.d.ts +1 -0
- package/dist/theme.js +3 -1
- package/dist/token.d.ts +9 -1
- package/dist/token.js +12 -6
- package/dist/util.d.ts +8 -0
- package/dist/util.js +8 -0
- package/dist/wiki.min.js +30 -30
- package/i18n/en.json +1 -1
- package/i18n/zh-hans.json +1 -1
- package/i18n/zh-hant.json +1 -1
- package/package.json +22 -22
package/dist/color.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type {
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { makeColorPicker } from '@bhsd/codemirror-css-color-picker';
|
|
2
|
+
import type { Extension } from '@codemirror/state';
|
|
3
|
+
/**
|
|
4
|
+
* @implements
|
|
5
|
+
* @test
|
|
6
|
+
*/
|
|
7
|
+
export declare const discoverColors: Parameters<typeof makeColorPicker>[0]['discoverColors'];
|
|
5
8
|
declare const _default: Extension[];
|
|
6
9
|
export default _default;
|
package/dist/color.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { splitColors, numToHex } from '@bhsd/common';
|
|
2
2
|
import { parseCallExpression, parseColorLiteral, ColorType, colorPickerTheme, makeColorPicker, } from '@bhsd/codemirror-css-color-picker';
|
|
3
|
+
/**
|
|
4
|
+
* @implements
|
|
5
|
+
* @test
|
|
6
|
+
*/
|
|
3
7
|
export const discoverColors = (_, from, to, type, doc) => {
|
|
4
8
|
if (!/mw-(?:(?:ext|html)tag-attribute-value|table-definition)/u.test(type)
|
|
5
9
|
&& (!/mw-(?:template|parserfunction)(?:$|_)/u.test(type)
|
package/dist/css.d.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { LanguageSupport } from '@codemirror/language';
|
|
2
2
|
import type { Extension } from '@codemirror/state';
|
|
3
3
|
import type { Dialect } from './codemirror';
|
|
4
|
+
/**
|
|
5
|
+
* CSS completion source with dialect-specific adjustments.
|
|
6
|
+
* @param dialect 是否是sanitized-css
|
|
7
|
+
* @test
|
|
8
|
+
*/
|
|
4
9
|
export declare const cssCompletion: (dialect?: Dialect) => Extension;
|
|
5
10
|
declare const _default: (dialect: Dialect) => LanguageSupport;
|
|
6
11
|
export default _default;
|
package/dist/css.js
CHANGED
|
@@ -2,6 +2,11 @@ import { cssLanguage, cssCompletionSource } from '@codemirror/lang-css';
|
|
|
2
2
|
import { LanguageSupport, syntaxTree } from '@codemirror/language';
|
|
3
3
|
import { sliceDoc } from './util.js';
|
|
4
4
|
const cssWideKeywords = /* @__PURE__ */ (() => ['revert', 'revert-layer'].map((label) => ({ label, type: 'keyword' })))();
|
|
5
|
+
/**
|
|
6
|
+
* CSS completion source with dialect-specific adjustments.
|
|
7
|
+
* @param dialect 是否是sanitized-css
|
|
8
|
+
* @test
|
|
9
|
+
*/
|
|
5
10
|
export const cssCompletion = (dialect) => {
|
|
6
11
|
const source = context => {
|
|
7
12
|
const { state, pos } = context, node = syntaxTree(state).resolveInner(pos, -1), result = cssCompletionSource(context);
|
package/dist/escape.d.ts
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
|
+
import { getLSP } from '@bhsd/browser';
|
|
2
|
+
import type { EditorView } from '@codemirror/view';
|
|
1
3
|
import type { Extension } from '@codemirror/state';
|
|
2
4
|
import type { CodeMirror6 } from './codemirror';
|
|
3
|
-
|
|
5
|
+
/**
|
|
6
|
+
* 转义HTML
|
|
7
|
+
* @param str 输入字符串
|
|
8
|
+
* @test
|
|
9
|
+
*/
|
|
10
|
+
export declare const escapeHTML: (str: string) => string,
|
|
11
|
+
/**
|
|
12
|
+
* 转义URI
|
|
13
|
+
* @param str 输入字符串
|
|
14
|
+
* @test
|
|
15
|
+
*/
|
|
16
|
+
escapeURI: (str: string) => string,
|
|
17
|
+
/**
|
|
18
|
+
* 使用魔术字转义选中文本
|
|
19
|
+
* @param view
|
|
20
|
+
* @param lsp LSP实例
|
|
21
|
+
* @test
|
|
22
|
+
*/
|
|
23
|
+
escapeWiki: (view: EditorView, lsp: Exclude<ReturnType<typeof getLSP>, undefined>) => Promise<void>;
|
|
4
24
|
declare const _default: (articlePath?: string) => (cm: CodeMirror6) => Extension;
|
|
5
25
|
export default _default;
|
package/dist/escape.js
CHANGED
|
@@ -19,13 +19,24 @@ const convert = (func, cmd) => (view) => {
|
|
|
19
19
|
}
|
|
20
20
|
return cmd(view);
|
|
21
21
|
};
|
|
22
|
+
/**
|
|
23
|
+
* 转义HTML
|
|
24
|
+
* @param str 输入字符串
|
|
25
|
+
* @test
|
|
26
|
+
*/
|
|
22
27
|
export const escapeHTML = (str) => [...str].map(c => {
|
|
23
28
|
if (c in entity) {
|
|
24
29
|
return `&${entity[c]};`;
|
|
25
30
|
}
|
|
26
31
|
const code = c.codePointAt(0);
|
|
27
32
|
return code < 256 ? `&#${code};` : `&#x${code.toString(16)};`;
|
|
28
|
-
}).join(''),
|
|
33
|
+
}).join(''),
|
|
34
|
+
/**
|
|
35
|
+
* 转义URI
|
|
36
|
+
* @param str 输入字符串
|
|
37
|
+
* @test
|
|
38
|
+
*/
|
|
39
|
+
escapeURI = (str) => {
|
|
29
40
|
if (str.includes('%')) {
|
|
30
41
|
try {
|
|
31
42
|
return decodeURIComponent(str);
|
|
@@ -33,28 +44,35 @@ export const escapeHTML = (str) => [...str].map(c => {
|
|
|
33
44
|
catch { }
|
|
34
45
|
}
|
|
35
46
|
return encodeURIComponent(str);
|
|
47
|
+
},
|
|
48
|
+
/**
|
|
49
|
+
* 使用魔术字转义选中文本
|
|
50
|
+
* @param view
|
|
51
|
+
* @param lsp LSP实例
|
|
52
|
+
* @test
|
|
53
|
+
*/
|
|
54
|
+
escapeWiki = async (view, lsp) => {
|
|
55
|
+
const { state } = view, { ranges } = state.selection, replacements = new WeakMap();
|
|
56
|
+
for (const range of ranges) {
|
|
57
|
+
// eslint-disable-next-line no-await-in-loop
|
|
58
|
+
const [action] = await lsp.provideRefactoringAction(sliceDoc(state, range));
|
|
59
|
+
replacements.set(range, action?.edit.changes[''][0].newText);
|
|
60
|
+
}
|
|
61
|
+
view.dispatch(state.changeByRange(range => {
|
|
62
|
+
const insert = replacements.get(range);
|
|
63
|
+
if (insert === undefined) {
|
|
64
|
+
return { range };
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
range: EditorSelection.range(range.from, range.from + insert.length),
|
|
68
|
+
changes: { from: range.from, to: range.to, insert },
|
|
69
|
+
};
|
|
70
|
+
}));
|
|
36
71
|
};
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
if (lsp && 'provideRefactoringAction' in lsp && ranges.some(({ empty }) => !empty)) {
|
|
40
|
-
(
|
|
41
|
-
const replacements = new WeakMap();
|
|
42
|
-
for (const range of ranges) {
|
|
43
|
-
// eslint-disable-next-line no-await-in-loop
|
|
44
|
-
const [action] = await lsp.provideRefactoringAction(sliceDoc(state, range));
|
|
45
|
-
replacements.set(range, action?.edit.changes[''][0].newText);
|
|
46
|
-
}
|
|
47
|
-
view.dispatch(state.changeByRange(range => {
|
|
48
|
-
const insert = replacements.get(range);
|
|
49
|
-
if (insert === undefined) {
|
|
50
|
-
return { range };
|
|
51
|
-
}
|
|
52
|
-
return {
|
|
53
|
-
range: EditorSelection.range(range.from, range.from + insert.length),
|
|
54
|
-
changes: { from: range.from, to: range.to, insert },
|
|
55
|
-
};
|
|
56
|
-
}));
|
|
57
|
-
})();
|
|
72
|
+
const escapeWikiCommand = (view, getConfig) => {
|
|
73
|
+
const lsp = getLSP(view, false, getConfig, base.CDN);
|
|
74
|
+
if (lsp && 'provideRefactoringAction' in lsp && view.state.selection.ranges.some(({ empty }) => !empty)) {
|
|
75
|
+
void escapeWiki(view, lsp);
|
|
58
76
|
return true;
|
|
59
77
|
}
|
|
60
78
|
return false;
|
|
@@ -85,7 +103,7 @@ menuRegistry.push({
|
|
|
85
103
|
if (lsp && 'provideRefactoringAction' in lsp) {
|
|
86
104
|
const btnWiki = elt('div', 'Escape with magic words');
|
|
87
105
|
btnWiki.addEventListener('click', e => {
|
|
88
|
-
|
|
106
|
+
escapeWikiCommand(view, cm.getWikiConfig);
|
|
89
107
|
handlerBase(view, e);
|
|
90
108
|
});
|
|
91
109
|
items.unshift(btnWiki);
|
|
@@ -100,7 +118,7 @@ export default (articlePath) => (cm) => keymap.of([
|
|
|
100
118
|
{
|
|
101
119
|
key: 'Mod-\\',
|
|
102
120
|
run(view) {
|
|
103
|
-
return
|
|
121
|
+
return escapeWikiCommand(view, toConfigGetter(cm.getWikiConfig, articlePath));
|
|
104
122
|
},
|
|
105
123
|
},
|
|
106
124
|
]);
|
package/dist/fold.d.ts
CHANGED
|
@@ -1,22 +1,74 @@
|
|
|
1
|
-
import { EditorView } from '@codemirror/view';
|
|
2
|
-
import
|
|
3
|
-
import type {
|
|
1
|
+
import { GutterMarker, EditorView } from '@codemirror/view';
|
|
2
|
+
import { RangeSet } from '@codemirror/state';
|
|
3
|
+
import type { BlockInfo, Command } from '@codemirror/view';
|
|
4
|
+
import type { EditorState, StateEffect, Extension } from '@codemirror/state';
|
|
4
5
|
import type { SyntaxNode, Tree } from '@lezer/common';
|
|
5
6
|
export interface DocRange {
|
|
6
7
|
from: number;
|
|
7
8
|
to: number;
|
|
8
9
|
}
|
|
10
|
+
declare type AnchorUpdate = (pos: number, range: DocRange) => number;
|
|
11
|
+
export declare const updateSelection: AnchorUpdate, updateAll: AnchorUpdate;
|
|
9
12
|
/**
|
|
10
13
|
* 寻找可折叠的范围
|
|
11
14
|
* @param state
|
|
12
15
|
* @param posOrNode 字符位置或语法树节点
|
|
13
16
|
* @param tree 语法树
|
|
14
17
|
* @param refOnly 是否仅检查`<ref>`标签
|
|
18
|
+
* @test
|
|
15
19
|
*/
|
|
16
20
|
export declare const foldable: (state: EditorState, posOrNode: number | SyntaxNode, tree?: Tree | null, refOnly?: boolean) => DocRange | false;
|
|
21
|
+
/**
|
|
22
|
+
* 折叠所有模板
|
|
23
|
+
* @param state
|
|
24
|
+
* @param tree 语法树
|
|
25
|
+
* @param effects 折叠
|
|
26
|
+
* @param node 语法树节点
|
|
27
|
+
* @param end 终止位置
|
|
28
|
+
* @param anchor 光标位置
|
|
29
|
+
* @param update 更新光标位置
|
|
30
|
+
* @param refOnly 是否仅检查`<ref>`标签
|
|
31
|
+
* @test
|
|
32
|
+
*/
|
|
33
|
+
export declare const traverse: (state: EditorState, tree: Tree, effects: StateEffect<DocRange>[], node: SyntaxNode | null, end: number, anchor: number, update: AnchorUpdate, refOnly?: boolean) => number;
|
|
34
|
+
declare class FoldMarker extends GutterMarker {
|
|
35
|
+
readonly open: boolean;
|
|
36
|
+
constructor(open: boolean);
|
|
37
|
+
eq(other: this): boolean;
|
|
38
|
+
toDOM({ state }: EditorView): HTMLElement;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* 寻找可折叠的行范围
|
|
42
|
+
* @ignore
|
|
43
|
+
* @test
|
|
44
|
+
*/
|
|
17
45
|
export declare const foldableLine: ({ state, viewportLineBlocks }: EditorView, { from: f, to: t }: DocRange) => DocRange | false;
|
|
46
|
+
/**
|
|
47
|
+
* 生成行号旁的折叠标记
|
|
48
|
+
* @param view
|
|
49
|
+
* @test
|
|
50
|
+
*/
|
|
51
|
+
export declare const buildMarkers: (view: EditorView) => RangeSet<FoldMarker>;
|
|
52
|
+
/**
|
|
53
|
+
* 生成折叠命令
|
|
54
|
+
* @param refOnly 是否仅检查`<ref>`标签
|
|
55
|
+
* @test
|
|
56
|
+
*/
|
|
57
|
+
export declare const foldCommand: (refOnly?: boolean) => Command;
|
|
18
58
|
export declare const foldRef: Command;
|
|
19
59
|
export declare const unfoldRef: Command;
|
|
60
|
+
/**
|
|
61
|
+
* 获取所有光标所在的行
|
|
62
|
+
* @param view
|
|
63
|
+
* @test
|
|
64
|
+
*/
|
|
65
|
+
export declare const selectedLines: (view: EditorView) => BlockInfo[];
|
|
66
|
+
/**
|
|
67
|
+
* Fold the template at the selection/cursor
|
|
68
|
+
* @param view
|
|
69
|
+
* @test
|
|
70
|
+
*/
|
|
71
|
+
export declare const foldAt: Command;
|
|
20
72
|
declare const _default: (e?: Extension | undefined) => Extension;
|
|
21
73
|
export default _default;
|
|
22
74
|
export declare const mediawikiFold: Extension;
|
package/dist/fold.js
CHANGED
|
@@ -8,7 +8,7 @@ import { bgDark } from './constants.js';
|
|
|
8
8
|
import { matchTag, getTag } from './matchTag.js';
|
|
9
9
|
import { braceStackUpdate, sliceDoc } from './util.js';
|
|
10
10
|
const getExtRegex = /* @__PURE__ */ getRegex(tag => new RegExp(`mw-tag-${tag}(?![a-z])`, 'u'));
|
|
11
|
-
const updateSelection = (pos, { to }) => Math.max(pos, to), updateAll = (pos, { from, to }) => from <= pos && to > pos ? to : pos;
|
|
11
|
+
export const updateSelection = (pos, { to }) => Math.max(pos, to), updateAll = (pos, { from, to }) => from <= pos && to > pos ? to : pos;
|
|
12
12
|
/**
|
|
13
13
|
* Check if a SyntaxNode is among the specified components
|
|
14
14
|
* @param keys The keys of the tokens to check
|
|
@@ -45,6 +45,7 @@ const refNames = new Set(['ref', 'references']);
|
|
|
45
45
|
* @param posOrNode 字符位置或语法树节点
|
|
46
46
|
* @param tree 语法树
|
|
47
47
|
* @param refOnly 是否仅检查`<ref>`标签
|
|
48
|
+
* @test
|
|
48
49
|
*/
|
|
49
50
|
export const foldable = (state, posOrNode, tree, refOnly = false) => {
|
|
50
51
|
if (typeof posOrNode === 'number') {
|
|
@@ -140,7 +141,7 @@ const foldSelector = '.cm-tooltip-fold';
|
|
|
140
141
|
* @param state
|
|
141
142
|
*/
|
|
142
143
|
const create = (state) => {
|
|
143
|
-
const {
|
|
144
|
+
const { head } = state.selection.main, range = foldable(state, head);
|
|
144
145
|
if (range) {
|
|
145
146
|
const { from, to } = range;
|
|
146
147
|
let folded = false;
|
|
@@ -200,8 +201,9 @@ const getAnchor = (state) => Math.max(...state.selection.ranges.map(({ to }) =>
|
|
|
200
201
|
* @param anchor 光标位置
|
|
201
202
|
* @param update 更新光标位置
|
|
202
203
|
* @param refOnly 是否仅检查`<ref>`标签
|
|
204
|
+
* @test
|
|
203
205
|
*/
|
|
204
|
-
const traverse = (state, tree, effects, node, end, anchor, update, refOnly) => {
|
|
206
|
+
export const traverse = (state, tree, effects, node, end, anchor, update, refOnly) => {
|
|
205
207
|
while (node && (node.from < end
|
|
206
208
|
|| node.from === end
|
|
207
209
|
&& !(isTemplateBracket(node) && sliceDoc(state, node).startsWith('}}')))) {
|
|
@@ -239,6 +241,11 @@ const findFold = ({ state }, line) => {
|
|
|
239
241
|
});
|
|
240
242
|
return found;
|
|
241
243
|
};
|
|
244
|
+
/**
|
|
245
|
+
* 寻找可折叠的行范围
|
|
246
|
+
* @ignore
|
|
247
|
+
* @test
|
|
248
|
+
*/
|
|
242
249
|
export const foldableLine = ({ state, viewportLineBlocks }, { from: f, to: t }) => {
|
|
243
250
|
const tree = syntaxTree(state), { doc } = state, { length } = viewportLineBlocks;
|
|
244
251
|
/**
|
|
@@ -307,7 +314,12 @@ export const foldableLine = ({ state, viewportLineBlocks }, { from: f, to: t })
|
|
|
307
314
|
}
|
|
308
315
|
return false;
|
|
309
316
|
};
|
|
310
|
-
|
|
317
|
+
/**
|
|
318
|
+
* 生成行号旁的折叠标记
|
|
319
|
+
* @param view
|
|
320
|
+
* @test
|
|
321
|
+
*/
|
|
322
|
+
export const buildMarkers = (view) => {
|
|
311
323
|
const builder = new RangeSetBuilder();
|
|
312
324
|
for (const line of view.viewportLineBlocks) {
|
|
313
325
|
let mark;
|
|
@@ -341,8 +353,9 @@ const defaultFoldExtension = /* @__PURE__ */ (() => [foldGutter(), keymap.of(fol
|
|
|
341
353
|
/**
|
|
342
354
|
* 生成折叠命令
|
|
343
355
|
* @param refOnly 是否仅检查`<ref>`标签
|
|
356
|
+
* @test
|
|
344
357
|
*/
|
|
345
|
-
const foldCommand = (refOnly) => view => {
|
|
358
|
+
export const foldCommand = (refOnly) => view => {
|
|
346
359
|
const { state } = view, tree = ensureSyntaxTree(state, state.doc.length, 1e3) ?? syntaxTree(state), effects = [];
|
|
347
360
|
let anchor = traverse(state, tree, effects, tree.topNode.firstChild, Infinity, getAnchor(state), updateAll, refOnly);
|
|
348
361
|
if (!refOnly) {
|
|
@@ -372,7 +385,12 @@ export const unfoldRef = (view) => {
|
|
|
372
385
|
}
|
|
373
386
|
return false;
|
|
374
387
|
};
|
|
375
|
-
|
|
388
|
+
/**
|
|
389
|
+
* 获取所有光标所在的行
|
|
390
|
+
* @param view
|
|
391
|
+
* @test
|
|
392
|
+
*/
|
|
393
|
+
export const selectedLines = (view) => {
|
|
376
394
|
const lines = [];
|
|
377
395
|
for (const { head } of view.state.selection.ranges) {
|
|
378
396
|
if (lines.some(({ from, to }) => from <= head && to >= head)) {
|
|
@@ -394,6 +412,35 @@ const unfoldCode = (view, line) => {
|
|
|
394
412
|
const folded = findFold(view, line);
|
|
395
413
|
return folded && unfoldEffect.of(folded);
|
|
396
414
|
};
|
|
415
|
+
/**
|
|
416
|
+
* Fold the template at the selection/cursor
|
|
417
|
+
* @param view
|
|
418
|
+
* @test
|
|
419
|
+
*/
|
|
420
|
+
export const foldAt = view => {
|
|
421
|
+
const { state } = view, tree = syntaxTree(state), effects = [];
|
|
422
|
+
let anchor = getAnchor(state);
|
|
423
|
+
for (const { from, to, empty } of state.selection.ranges) {
|
|
424
|
+
let node;
|
|
425
|
+
if (empty) {
|
|
426
|
+
// No selection, try both sides of the cursor position
|
|
427
|
+
node = tree.resolve(from, -1);
|
|
428
|
+
}
|
|
429
|
+
if (!node || node.name === 'Document') {
|
|
430
|
+
node = tree.resolve(from, 1);
|
|
431
|
+
}
|
|
432
|
+
anchor = traverse(state, tree, effects, node, to, anchor, updateSelection);
|
|
433
|
+
}
|
|
434
|
+
if (effects.length > 0) {
|
|
435
|
+
return execute(view, effects, anchor);
|
|
436
|
+
}
|
|
437
|
+
for (const line of selectedLines(view)) {
|
|
438
|
+
if (foldCode(view, line)) {
|
|
439
|
+
return true;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
return false;
|
|
443
|
+
};
|
|
397
444
|
export default ((e = defaultFoldExtension) => [
|
|
398
445
|
e,
|
|
399
446
|
EditorView.theme({
|
|
@@ -436,30 +483,7 @@ export const mediawikiFold = /* @__PURE__ */ (() => [
|
|
|
436
483
|
// Fold the template at the selection/cursor
|
|
437
484
|
key: 'Ctrl-Shift-[',
|
|
438
485
|
mac: 'Cmd-Alt-[',
|
|
439
|
-
run
|
|
440
|
-
const { state } = view, tree = syntaxTree(state), effects = [];
|
|
441
|
-
let anchor = getAnchor(state);
|
|
442
|
-
for (const { from, to, empty } of state.selection.ranges) {
|
|
443
|
-
let node;
|
|
444
|
-
if (empty) {
|
|
445
|
-
// No selection, try both sides of the cursor position
|
|
446
|
-
node = tree.resolve(from, -1);
|
|
447
|
-
}
|
|
448
|
-
if (!node || node.name === 'Document') {
|
|
449
|
-
node = tree.resolve(from, 1);
|
|
450
|
-
}
|
|
451
|
-
anchor = traverse(state, tree, effects, node, to, anchor, updateSelection);
|
|
452
|
-
}
|
|
453
|
-
if (effects.length > 0) {
|
|
454
|
-
return execute(view, effects, anchor);
|
|
455
|
-
}
|
|
456
|
-
for (const line of selectedLines(view)) {
|
|
457
|
-
if (foldCode(view, line)) {
|
|
458
|
-
return true;
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
return false;
|
|
462
|
-
},
|
|
486
|
+
run: foldAt,
|
|
463
487
|
},
|
|
464
488
|
{
|
|
465
489
|
// Fold all templates in the document
|
package/dist/hover.d.ts
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
|
-
import type { Extension } from '@codemirror/state';
|
|
1
|
+
import type { Extension, EditorState } from '@codemirror/state';
|
|
2
|
+
import type { Hover } from 'vscode-languageserver-types';
|
|
2
3
|
import type { CodeMirror6 } from './codemirror';
|
|
4
|
+
import type { CompletionSectionName, ApiSuggest } from './token';
|
|
5
|
+
/**
|
|
6
|
+
* @ignore
|
|
7
|
+
* @test
|
|
8
|
+
*/
|
|
9
|
+
export declare const getDoc: (section: CompletionSectionName, info?: string) => string;
|
|
10
|
+
/**
|
|
11
|
+
* 从TemplateData API获取hover信息
|
|
12
|
+
* @ignore
|
|
13
|
+
* @test
|
|
14
|
+
*/
|
|
15
|
+
export declare const getHoverFromApi: (state: EditorState, pos: number, side: 1 | -1, paramSuggest: ApiSuggest, templatedata?: boolean) => Promise<Hover | undefined>;
|
|
3
16
|
declare const _default: (articlePath?: string, templatedata?: boolean) => (cm: CodeMirror6) => Extension;
|
|
4
17
|
export default _default;
|
package/dist/hover.js
CHANGED
|
@@ -5,8 +5,54 @@ import { tokens } from './config.js';
|
|
|
5
5
|
import { base, hoverSelector, bgDark } from './constants.js';
|
|
6
6
|
import { indexToPos, posToIndex, createTooltipView, toConfigGetter, escHTML, sliceDoc, findTemplateName, } from './util.js';
|
|
7
7
|
const code = `${hoverSelector} code`;
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* @ignore
|
|
10
|
+
* @test
|
|
11
|
+
*/
|
|
12
|
+
export const getDoc = (section, info = '') => escHTML(info)
|
|
9
13
|
+ (section === 'Optional' ? '' : `<br><b><i>@${section.toLowerCase()}</i></b>`);
|
|
14
|
+
/**
|
|
15
|
+
* 从TemplateData API获取hover信息
|
|
16
|
+
* @ignore
|
|
17
|
+
* @test
|
|
18
|
+
*/
|
|
19
|
+
export const getHoverFromApi = async (state, pos, side, paramSuggest, templatedata) => {
|
|
20
|
+
const node = ensureSyntaxTree(state, pos + Math.max(side, 0))?.resolve(pos, side), { doc } = state;
|
|
21
|
+
if (node?.name.includes(tokens.templateName)) {
|
|
22
|
+
const result = await paramSuggest(sliceDoc(state, node), templatedata), { description, length } = result;
|
|
23
|
+
if (description || length > 0) {
|
|
24
|
+
return {
|
|
25
|
+
contents: {
|
|
26
|
+
kind: 'plaintext',
|
|
27
|
+
value: (description ? `<p>${escHTML(description)}</p>` : '')
|
|
28
|
+
+ ['Required', 'Suggested', 'Optional', 'Deprecated'].map(name => {
|
|
29
|
+
const sectionResult = result.filter(([, , , section]) => section === name);
|
|
30
|
+
return sectionResult.length === 0
|
|
31
|
+
? ''
|
|
32
|
+
: `<h4>${name}</h4><ul>${sectionResult.map(([keys, , info]) => `<li>${keys.map(key => `<code>${escHTML(key)}</code>`).join('/')}${info && ` - ${escHTML(info)}`}</li>`).join('')}</ul>`;
|
|
33
|
+
}).join(''),
|
|
34
|
+
},
|
|
35
|
+
range: { start: indexToPos(doc, node.from), end: indexToPos(doc, node.to) },
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
else if (node?.name.includes(tokens.templateArgumentName)) {
|
|
40
|
+
const name = findTemplateName(state, node);
|
|
41
|
+
if (name) {
|
|
42
|
+
const result = await paramSuggest(name, templatedata), param = sliceDoc(state, node).trim().slice(0, -1).trim(), [, , info, section] = result.find(([keys]) => keys.includes(param)) ?? [];
|
|
43
|
+
if (info || section && section !== 'Optional') {
|
|
44
|
+
return {
|
|
45
|
+
contents: {
|
|
46
|
+
kind: 'plaintext',
|
|
47
|
+
value: getDoc(section, info).replace(/^<br>/u, ''),
|
|
48
|
+
},
|
|
49
|
+
range: { start: indexToPos(doc, node.from), end: indexToPos(doc, node.to) },
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return undefined;
|
|
55
|
+
};
|
|
10
56
|
export default (articlePath, templatedata) => (cm) => {
|
|
11
57
|
return [
|
|
12
58
|
hoverTooltip(async (view, pos, side) => {
|
|
@@ -14,36 +60,8 @@ export default (articlePath, templatedata) => (cm) => {
|
|
|
14
60
|
const { paramSuggest, tags } = cm.langConfig;
|
|
15
61
|
let hover = await getLSP(view, false, toConfigGetter(cm.getWikiConfig, articlePath), base.CDN)?.provideHover(doc.toString(), indexToPos(doc, pos));
|
|
16
62
|
if (!hover && paramSuggest && 'templatedata' in tags) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const result = await paramSuggest(sliceDoc(state, node), templatedata), { description, length } = result;
|
|
20
|
-
if (description || length > 0) {
|
|
21
|
-
hover = {
|
|
22
|
-
contents: {
|
|
23
|
-
kind: 'plaintext',
|
|
24
|
-
value: (description ? `<p>${escHTML(description)}</p>` : '') + (length === 0
|
|
25
|
-
? ''
|
|
26
|
-
: `<ul>${result.map(([keys, , info, section]) => `<li>${keys.map(key => `<code>${escHTML(key)}</code>`).join('/')}${info && ' - '}${getDoc(section, info)}</li>`).join('')}</ul>`),
|
|
27
|
-
},
|
|
28
|
-
range: { start: indexToPos(doc, node.from), end: indexToPos(doc, node.to) },
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
else if (node?.name.includes(tokens.templateArgumentName)) {
|
|
33
|
-
const name = findTemplateName(state, node);
|
|
34
|
-
if (name) {
|
|
35
|
-
const result = await paramSuggest(name, templatedata), param = sliceDoc(state, node).trim().slice(0, -1).trim(), [, , info, section] = result.find(([keys]) => keys.includes(param)) ?? [];
|
|
36
|
-
if (info || section !== 'Optional') {
|
|
37
|
-
hover = {
|
|
38
|
-
contents: {
|
|
39
|
-
kind: 'plaintext',
|
|
40
|
-
value: getDoc(section, info).replace(/^<br>/u, ''),
|
|
41
|
-
},
|
|
42
|
-
range: { start: indexToPos(doc, node.from), end: indexToPos(doc, node.to) },
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
63
|
+
// eslint-disable-next-line require-atomic-updates
|
|
64
|
+
hover = await getHoverFromApi(state, pos, side, paramSuggest, templatedata);
|
|
47
65
|
}
|
|
48
66
|
if (hover) {
|
|
49
67
|
const { CDN = '' } = base;
|
package/dist/html.js
CHANGED
|
@@ -2,31 +2,36 @@ import { configureNesting } from '@lezer/html';
|
|
|
2
2
|
import { htmlLanguage, htmlCompletionSourceWith } from '@codemirror/lang-html';
|
|
3
3
|
import { javascript, javascriptLanguage } from '@codemirror/lang-javascript';
|
|
4
4
|
import { cssLanguage } from '@codemirror/lang-css';
|
|
5
|
-
import { LanguageSupport
|
|
5
|
+
import { LanguageSupport } from '@codemirror/language';
|
|
6
6
|
import { cssCompletion } from './css.js';
|
|
7
7
|
import { jsCompletion } from './javascript.js';
|
|
8
8
|
import { mediawikiBase } from './mediawiki.js';
|
|
9
|
+
import { getLightHighlightStyle } from './theme.js';
|
|
9
10
|
export default (config) => {
|
|
10
|
-
const { language, support } = mediawikiBase(config),
|
|
11
|
+
const { language, support } = mediawikiBase(config),
|
|
12
|
+
/** @test */
|
|
13
|
+
lang = htmlLanguage.configure({
|
|
11
14
|
wrap: configureNesting([
|
|
12
15
|
{ tag: 'script', parser: javascriptLanguage.parser },
|
|
13
16
|
{ tag: 'style', parser: cssLanguage.parser },
|
|
14
17
|
{ tag: 'noinclude', parser: language.parser },
|
|
15
18
|
], [{ name: 'style', parser: cssLanguage.parser.configure({ top: 'Styles' }) }]),
|
|
16
|
-
}),
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
},
|
|
22
|
-
}
|
|
19
|
+
}),
|
|
20
|
+
/** @test */
|
|
21
|
+
autocomplete = htmlLanguage.data.of({
|
|
22
|
+
autocomplete: htmlCompletionSourceWith({
|
|
23
|
+
extraTags: {
|
|
24
|
+
noinclude: { globalAttrs: false },
|
|
25
|
+
},
|
|
23
26
|
}),
|
|
27
|
+
}), langSupport = new LanguageSupport(lang, [
|
|
28
|
+
autocomplete,
|
|
24
29
|
javascript().support,
|
|
25
30
|
jsCompletion,
|
|
26
31
|
cssCompletion(),
|
|
27
32
|
support,
|
|
28
|
-
|
|
33
|
+
getLightHighlightStyle(),
|
|
29
34
|
]);
|
|
30
|
-
Object.assign(
|
|
31
|
-
return
|
|
35
|
+
Object.assign(langSupport, { nestedMWLanguage: language });
|
|
36
|
+
return langSupport;
|
|
32
37
|
};
|
package/dist/indent.d.ts
CHANGED
|
@@ -3,10 +3,17 @@ export interface Text extends TextBase {
|
|
|
3
3
|
children: readonly Text[] | null;
|
|
4
4
|
text?: string[];
|
|
5
5
|
}
|
|
6
|
+
/**
|
|
7
|
+
* 获取逐行文本内容
|
|
8
|
+
* @param text
|
|
9
|
+
* @test
|
|
10
|
+
*/
|
|
11
|
+
export declare const getLines: (text: Text) => string[];
|
|
6
12
|
/**
|
|
7
13
|
* 检测文本的缩进方式
|
|
8
14
|
* @param text 文本内容
|
|
9
15
|
* @param defaultIndent 默认缩进方式
|
|
10
16
|
* @param lang 语言
|
|
17
|
+
* @test
|
|
11
18
|
*/
|
|
12
19
|
export declare const detectIndent: (text: string | Text, defaultIndent: string, lang: string) => string;
|
package/dist/indent.js
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { noDetectionLangs } from './constants.js';
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* 获取逐行文本内容
|
|
4
|
+
* @param text
|
|
5
|
+
* @test
|
|
6
|
+
*/
|
|
7
|
+
export const getLines = (text) => text.children?.flatMap(getLines) ?? text.text;
|
|
3
8
|
/**
|
|
4
9
|
* 检测文本的缩进方式
|
|
5
10
|
* @param text 文本内容
|
|
6
11
|
* @param defaultIndent 默认缩进方式
|
|
7
12
|
* @param lang 语言
|
|
13
|
+
* @test
|
|
8
14
|
*/
|
|
9
15
|
export const detectIndent = (text, defaultIndent, lang) => {
|
|
10
16
|
if (noDetectionLangs.has(lang)) {
|
package/dist/inlay.js
CHANGED
|
@@ -22,7 +22,7 @@ const stateEffect = StateEffect.define(), field = StateField.define({
|
|
|
22
22
|
const str = doc.toString();
|
|
23
23
|
for (const effect of effects) {
|
|
24
24
|
if (effect.is(stateEffect)) {
|
|
25
|
-
const {
|
|
25
|
+
const { text, inlayHints } = effect.value;
|
|
26
26
|
if (str === text) {
|
|
27
27
|
return inlayHints
|
|
28
28
|
? Decoration.set(inlayHints.map(({ position, label }) => Decoration.widget({
|
package/dist/javascript.d.ts
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import type { Extension } from '@codemirror/state';
|
|
1
|
+
import type { Extension, EditorState } from '@codemirror/state';
|
|
2
|
+
import type { DecorationSet } from '@codemirror/view';
|
|
3
|
+
import type { Tree } from '@lezer/common';
|
|
4
|
+
import type { DocRange } from './fold';
|
|
2
5
|
export declare const jsCompletion: Extension;
|
|
6
|
+
/**
|
|
7
|
+
* 高亮显示全局变量
|
|
8
|
+
* @ignore
|
|
9
|
+
* @test
|
|
10
|
+
*/
|
|
11
|
+
export declare const markGlobals: (tree: Tree, visibleRanges: readonly DocRange[], state: EditorState) => DecorationSet;
|
|
3
12
|
declare const _default: () => Extension;
|
|
4
13
|
export default _default;
|
package/dist/javascript.js
CHANGED
|
@@ -4,7 +4,12 @@ import { syntaxTree } from '@codemirror/language';
|
|
|
4
4
|
import { builtin } from 'globals/globals.json';
|
|
5
5
|
export const jsCompletion = javascriptLanguage.data.of({ autocomplete: scopeCompletionSource(globalThis) });
|
|
6
6
|
const globals = Decoration.mark({ class: 'cm-globals' });
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* 高亮显示全局变量
|
|
9
|
+
* @ignore
|
|
10
|
+
* @test
|
|
11
|
+
*/
|
|
12
|
+
export const markGlobals = (tree, visibleRanges, state) => {
|
|
8
13
|
const decorations = [];
|
|
9
14
|
for (const { from, to } of visibleRanges) {
|
|
10
15
|
tree.iterate({
|
package/dist/keybindings.d.ts
CHANGED