@bhsd/codemirror-mediawiki 3.9.2 → 3.10.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 +71 -11
- package/dist/bidi.d.ts +4 -8
- package/dist/bidi.js +33 -23
- package/dist/codemirror.js +15 -9
- package/dist/color.d.ts +1 -1
- package/dist/color.js +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.js +1 -0
- package/dist/constants.d.ts +3 -2
- package/dist/constants.js +3 -2
- package/dist/css.js +9 -6
- package/dist/escape.d.ts +1 -1
- package/dist/escape.js +4 -3
- package/dist/fold.d.ts +2 -2
- package/dist/fold.js +106 -39
- package/dist/hover.d.ts +2 -2
- package/dist/hover.js +72 -69
- package/dist/index.d.ts +54 -16
- package/dist/index.js +91 -38
- package/dist/inlay.d.ts +1 -1
- package/dist/inlay.js +26 -25
- package/dist/javascript.js +45 -2
- package/dist/linter.d.ts +15 -2
- package/dist/linter.js +2 -2
- package/dist/lintsource.d.ts +15 -3
- package/dist/lintsource.js +13 -7
- package/dist/lua.d.ts +0 -2
- package/dist/lua.js +32 -26
- package/dist/main.min.js +31 -29
- package/dist/matchTag.js +4 -3
- package/dist/mediawiki.d.ts +4 -2
- package/dist/mediawiki.js +56 -44
- package/dist/mw.min.js +33 -37
- package/dist/mwConfig.js +2 -2
- package/dist/openLinks.d.ts +4 -2
- package/dist/openLinks.js +56 -54
- package/dist/ref.d.ts +1 -1
- package/dist/ref.js +92 -93
- package/dist/signature.d.ts +1 -1
- package/dist/signature.js +50 -49
- package/dist/statusBar.js +9 -8
- package/dist/theme.js +6 -0
- package/dist/token.d.ts +20 -6
- package/dist/token.js +26 -17
- package/dist/util.d.ts +17 -2
- package/dist/util.js +39 -1
- package/dist/wiki.min.js +32 -36
- package/i18n/en.json +2 -2
- package/i18n/zh-hans.json +2 -2
- package/i18n/zh-hant.json +2 -2
- package/package.json +14 -12
package/dist/matchTag.js
CHANGED
|
@@ -3,6 +3,7 @@ import { StateField } from '@codemirror/state';
|
|
|
3
3
|
import { ensureSyntaxTree } from '@codemirror/language';
|
|
4
4
|
import { voidHtmlTags, selfClosingTags } from './config.js';
|
|
5
5
|
import { matchingCls, nonmatchingCls } from './constants.js';
|
|
6
|
+
import { sliceDoc } from './util.js';
|
|
6
7
|
class Tag {
|
|
7
8
|
get closing() {
|
|
8
9
|
return isClosing(this.first, this.type, this.state, true);
|
|
@@ -32,7 +33,7 @@ const isTag = ({ name }) => /-(?:ext|html)tag-(?!bracket)/u.test(name), isTagCom
|
|
|
32
33
|
const reHtml = new RegExp(`-htmltag-${s}`, 'u'), reExt = new RegExp(`-exttag-${s}`, 'u');
|
|
33
34
|
return ({ name }, type) => (type === 'ext' ? reExt : reHtml).test(name);
|
|
34
35
|
}, isBracket = isTagComponent('bracket'), isName = isTagComponent('name'), isClosing = (node, type, state, first) => isBracket(node, type)
|
|
35
|
-
&&
|
|
36
|
+
&& sliceDoc(state, node)[first ? 'endsWith' : 'startsWith']('/'), getName = (state, node) => sliceDoc(state, node).trim().toLowerCase();
|
|
36
37
|
/**
|
|
37
38
|
* 获取标签信息,破损的HTML标签会返回`null`
|
|
38
39
|
* @param state
|
|
@@ -93,9 +94,9 @@ export const matchTag = (state, pos) => {
|
|
|
93
94
|
if (!tree) {
|
|
94
95
|
return null;
|
|
95
96
|
}
|
|
96
|
-
let node = tree.
|
|
97
|
+
let node = tree.resolveInner(pos, -1);
|
|
97
98
|
if (!isTag(node)) {
|
|
98
|
-
node = tree.
|
|
99
|
+
node = tree.resolveInner(pos, 1);
|
|
99
100
|
if (!isTag(node)) {
|
|
100
101
|
return null;
|
|
101
102
|
}
|
package/dist/mediawiki.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ import type { MwConfig } from './token';
|
|
|
15
15
|
export declare const isWikiLink: (name: string) => boolean;
|
|
16
16
|
export declare class FullMediaWiki extends MediaWiki {
|
|
17
17
|
#private;
|
|
18
|
+
readonly templatedata: boolean;
|
|
18
19
|
readonly nsRegex: RegExp;
|
|
19
20
|
readonly functionSynonyms: Completion[];
|
|
20
21
|
readonly doubleUnderscore: Completion[];
|
|
@@ -25,7 +26,7 @@ export declare class FullMediaWiki extends MediaWiki {
|
|
|
25
26
|
readonly htmlAttrs: Completion[];
|
|
26
27
|
readonly elementAttrs: Map<string | undefined, Completion[]>;
|
|
27
28
|
readonly extAttrs: Map<string, Completion[]>;
|
|
28
|
-
constructor(config: MwConfig);
|
|
29
|
+
constructor(config: MwConfig, templatedata?: boolean);
|
|
29
30
|
/**
|
|
30
31
|
* This defines the actual CSS class assigned to each tag/token.
|
|
31
32
|
*
|
|
@@ -39,5 +40,6 @@ export declare class FullMediaWiki extends MediaWiki {
|
|
|
39
40
|
/**
|
|
40
41
|
* Get a LanguageSupport instance for the MediaWiki mode.
|
|
41
42
|
* @param config Configuration for the MediaWiki mode
|
|
43
|
+
* @param templatedata Whether to enable template parameter autocompletion
|
|
42
44
|
*/
|
|
43
|
-
export declare const mediawikiBase: (config: MwConfig) => LanguageSupport;
|
|
45
|
+
export declare const mediawikiBase: (config: MwConfig, templatedata?: boolean) => LanguageSupport;
|
package/dist/mediawiki.js
CHANGED
|
@@ -9,9 +9,10 @@ import { insertCompletionText, pickedCompletion } from '@codemirror/autocomplete
|
|
|
9
9
|
import { isUnderscore } from '@bhsd/cm-util';
|
|
10
10
|
import { commonHtmlAttrs, htmlAttrs, extAttrs } from 'wikiparser-node/dist/util/sharable.mjs';
|
|
11
11
|
import { htmlTags, tokens } from './config.js';
|
|
12
|
-
import {
|
|
12
|
+
import { hoverSelector, isWMF, } from './constants.js';
|
|
13
13
|
import { MediaWiki } from './token.js';
|
|
14
|
-
import { hasTag,
|
|
14
|
+
import { hasTag, leadingSpaces, findTemplateName, } from './util.js';
|
|
15
|
+
const ranks = { Required: 1, Suggested: 2, Optional: 3, Deprecated: 4 };
|
|
15
16
|
/**
|
|
16
17
|
* 是否是普通维基链接
|
|
17
18
|
* @param name 节点名称
|
|
@@ -41,9 +42,10 @@ const apply = (view, completion, from, to) => {
|
|
|
41
42
|
});
|
|
42
43
|
};
|
|
43
44
|
export class FullMediaWiki extends MediaWiki {
|
|
44
|
-
constructor(config) {
|
|
45
|
+
constructor(config, templatedata = false) {
|
|
45
46
|
super(config);
|
|
46
47
|
const { urlProtocols, nsid, functionSynonyms, doubleUnderscore, } = config;
|
|
48
|
+
this.templatedata = templatedata;
|
|
47
49
|
this.nsRegex = new RegExp(String.raw `^(${Object.keys(nsid).filter(ns => ns !== '').join('|')
|
|
48
50
|
.replace(/_/gu, ' ')})\s*:\s*`, 'iu');
|
|
49
51
|
this.functionSynonyms = functionSynonyms.flatMap((obj, i) => Object.keys(obj).map((label) => ({
|
|
@@ -95,7 +97,6 @@ export class FullMediaWiki extends MediaWiki {
|
|
|
95
97
|
const parser = super.mediawiki(tags);
|
|
96
98
|
parser.languageData = {
|
|
97
99
|
closeBrackets: { brackets: ['(', '[', '{', '"'], before: ')]}>' },
|
|
98
|
-
autocomplete: this.completionSource,
|
|
99
100
|
};
|
|
100
101
|
return parser;
|
|
101
102
|
}
|
|
@@ -155,22 +156,29 @@ export class FullMediaWiki extends MediaWiki {
|
|
|
155
156
|
return result?.length
|
|
156
157
|
? {
|
|
157
158
|
offset: leadingSpaces(search).length,
|
|
158
|
-
options: result.
|
|
159
|
+
options: result.flatMap(([keys, detail, info, name]) => keys.map((key) => ({
|
|
160
|
+
type: 'variable',
|
|
161
|
+
label: key + equal,
|
|
162
|
+
section: { name: name, rank: ranks[name] },
|
|
163
|
+
...detail && { detail },
|
|
164
|
+
...info && { info },
|
|
165
|
+
}))),
|
|
159
166
|
}
|
|
160
167
|
: undefined;
|
|
161
168
|
}
|
|
162
169
|
/** 自动补全魔术字和标签名 */
|
|
163
170
|
get completionSource() {
|
|
164
171
|
return async (context) => {
|
|
165
|
-
const { state, pos, explicit } = context, node = syntaxTree(state).
|
|
172
|
+
const { state, pos, explicit } = context, node = syntaxTree(state).resolveInner(pos, -1), { name: n, prevSibling, from: f, to: t, } = node, types = new Set(n.split('_')), isParserFunction = hasTag(types, 'parserFunctionName'),
|
|
166
173
|
/** 开头不包含` `,但可能包含`_` */ search = state.sliceDoc(f, pos).trimStart(), start = pos - search.length;
|
|
167
|
-
|
|
168
|
-
if (explicit || isParserFunction && search.includes('#')
|
|
174
|
+
// 需要opensearch API的建议,只在显式触发时或WMF网站上提供
|
|
175
|
+
if (explicit || isWMF || isParserFunction && search.includes('#')) {
|
|
169
176
|
const obj = isWMF
|
|
170
177
|
? null
|
|
171
178
|
: {
|
|
172
179
|
validFor: /^[^|{}<>[\]#]*$/u,
|
|
173
180
|
};
|
|
181
|
+
// 模板名
|
|
174
182
|
if (isParserFunction || hasTag(types, 'templateName')) {
|
|
175
183
|
const options = search.includes(':') ? [] : [...this.functionSynonyms], suggestions = await this.#linkSuggest(search, 10) ?? { offset: 0, options: [] };
|
|
176
184
|
options.push(...suggestions.options);
|
|
@@ -189,8 +197,12 @@ export class FullMediaWiki extends MediaWiki {
|
|
|
189
197
|
...obj,
|
|
190
198
|
};
|
|
191
199
|
}
|
|
200
|
+
// 页面名
|
|
192
201
|
const isPage = hasTag(types, 'pageName') && hasTag(types, 'parserFunction') || 0;
|
|
193
202
|
if (isPage && search.trim() || hasTag(types, 'linkPageName')) {
|
|
203
|
+
if (!this.config.linkSuggest) {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
194
206
|
const isLink = isWikiLink(n);
|
|
195
207
|
let prefix = '', ns = 0;
|
|
196
208
|
if (isPage) {
|
|
@@ -218,34 +230,18 @@ export class FullMediaWiki extends MediaWiki {
|
|
|
218
230
|
...obj,
|
|
219
231
|
};
|
|
220
232
|
}
|
|
233
|
+
}
|
|
234
|
+
// 需要TemplateData API的建议,只在显式触发时提供
|
|
235
|
+
if (this.config.paramSuggest
|
|
236
|
+
&& (explicit || this.templatedata)
|
|
237
|
+
&& this.tags.includes('templatedata')) {
|
|
221
238
|
const isArgument = hasTag(types, 'templateArgumentName'), prevIsDelimiter = prevSibling?.name.includes(tokens.templateDelimiter), isDelimiter = hasTag(types, 'templateDelimiter')
|
|
222
239
|
|| hasTag(types, 'templateBracket') && prevIsDelimiter;
|
|
223
|
-
if (
|
|
224
|
-
&& (
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
/** 可包含`_`、`:`等 */ page = '';
|
|
229
|
-
while (prevSibling) {
|
|
230
|
-
const { name, from, to } = prevSibling;
|
|
231
|
-
if (name.includes(tokens.templateBracket)) {
|
|
232
|
-
const [lbrace, rbrace] = braceStackUpdate(state, prevSibling);
|
|
233
|
-
stack += lbrace;
|
|
234
|
-
if (stack >= 0) {
|
|
235
|
-
break;
|
|
236
|
-
}
|
|
237
|
-
stack += rbrace;
|
|
238
|
-
}
|
|
239
|
-
else if (stack === -1 && name.includes(tokens.templateName)) {
|
|
240
|
-
page = state.sliceDoc(from, to) + page;
|
|
241
|
-
}
|
|
242
|
-
else if (page && !name.includes(tokens.comment)) {
|
|
243
|
-
prevSibling = null;
|
|
244
|
-
break;
|
|
245
|
-
}
|
|
246
|
-
({ prevSibling } = prevSibling);
|
|
247
|
-
}
|
|
248
|
-
if (prevSibling && page) {
|
|
240
|
+
if (isDelimiter
|
|
241
|
+
|| isArgument && !search.includes('=')
|
|
242
|
+
|| hasTag(types, 'template') && prevIsDelimiter) {
|
|
243
|
+
const page = findTemplateName(state, node);
|
|
244
|
+
if (page) {
|
|
249
245
|
const equal = isArgument && state.sliceDoc(pos, t).trim() === '=' ? '' : '=', suggestions = await this.#paramSuggest(isDelimiter ? '' : search, page, equal);
|
|
250
246
|
if (suggestions && suggestions.options.length > 0) {
|
|
251
247
|
return {
|
|
@@ -293,10 +289,12 @@ export class FullMediaWiki extends MediaWiki {
|
|
|
293
289
|
'comment',
|
|
294
290
|
'templateVariableName',
|
|
295
291
|
'templateName',
|
|
292
|
+
'parserFunctionName',
|
|
296
293
|
'linkPageName',
|
|
297
294
|
'linkToSection',
|
|
298
295
|
'extLink',
|
|
299
296
|
])) {
|
|
297
|
+
// 不可能是状态开关、标签、协议或图片参数名
|
|
300
298
|
return null;
|
|
301
299
|
}
|
|
302
300
|
let mt = context.matchBefore(/__(?:(?!__)[\p{L}\p{N}_])*$/u);
|
|
@@ -374,7 +372,7 @@ const getSelector = (cls, prefix = '') => typeof prefix === 'string'
|
|
|
374
372
|
: prefix.map(p => getSelector(cls, p)).join();
|
|
375
373
|
const getGround = (type, ground) => ground ? `${type}${ground === 1 ? '' : ground}-` : '';
|
|
376
374
|
const getGrounds = (grounds, r, g, b, a) => ({
|
|
377
|
-
[grounds.map(([template, ext, link]) => `.cm-mw-${getGround('template', template)}${getGround('
|
|
375
|
+
[grounds.map(([template, ext, link]) => `.cm-mw-${getGround('template', template)}${getGround('ext', ext)}${getGround('link', link)}ground`).join()]: {
|
|
378
376
|
backgroundColor: `rgb(${r},${g},${b},${a})`,
|
|
379
377
|
},
|
|
380
378
|
});
|
|
@@ -429,7 +427,7 @@ const theme = /* @__PURE__ */ EditorView.theme({
|
|
|
429
427
|
color: 'var(--cm-tpl)',
|
|
430
428
|
fontWeight: 'bold',
|
|
431
429
|
},
|
|
432
|
-
'
|
|
430
|
+
[getSelector(['-argument-name'], ['template', 'parserfunction'])]: {
|
|
433
431
|
color: 'var(--cm-arg)',
|
|
434
432
|
fontWeight: 'normal',
|
|
435
433
|
},
|
|
@@ -507,19 +505,33 @@ const theme = /* @__PURE__ */ EditorView.theme({
|
|
|
507
505
|
'.cm-mw-tag-ref': {
|
|
508
506
|
backgroundColor: 'var(--cm-ref)',
|
|
509
507
|
},
|
|
510
|
-
|
|
511
|
-
|
|
508
|
+
// hover tooltip and signature tooltip
|
|
509
|
+
[hoverSelector]: {
|
|
510
|
+
padding: '2px 5px',
|
|
511
|
+
width: 'max-content',
|
|
512
|
+
maxWidth: '60vw',
|
|
513
|
+
maxHeight: '60vh',
|
|
514
|
+
overflowY: 'auto',
|
|
515
|
+
},
|
|
516
|
+
[`${hoverSelector} *`]: {
|
|
517
|
+
marginTop: '0!important',
|
|
518
|
+
marginBottom: '0!important',
|
|
512
519
|
},
|
|
513
|
-
[
|
|
514
|
-
|
|
515
|
-
|
|
520
|
+
[`${hoverSelector}>div`]: {
|
|
521
|
+
fontSize: '90%',
|
|
522
|
+
lineHeight: 1.4,
|
|
516
523
|
},
|
|
517
524
|
});
|
|
518
525
|
/**
|
|
519
526
|
* Get a LanguageSupport instance for the MediaWiki mode.
|
|
520
527
|
* @param config Configuration for the MediaWiki mode
|
|
528
|
+
* @param templatedata Whether to enable template parameter autocompletion
|
|
521
529
|
*/
|
|
522
|
-
export const mediawikiBase = (config) => {
|
|
523
|
-
const mode = new FullMediaWiki(config), lang = StreamLanguage.define(mode.mediawiki());
|
|
524
|
-
return new LanguageSupport(lang, [
|
|
530
|
+
export const mediawikiBase = (config, templatedata) => {
|
|
531
|
+
const mode = new FullMediaWiki(config, templatedata), lang = StreamLanguage.define(mode.mediawiki());
|
|
532
|
+
return new LanguageSupport(lang, [
|
|
533
|
+
syntaxHighlighting(HighlightStyle.define(mode.getTagStyles())),
|
|
534
|
+
theme,
|
|
535
|
+
lang.data.of({ autocomplete: mode.completionSource }),
|
|
536
|
+
]);
|
|
525
537
|
};
|