wikilint 2.18.0 → 2.18.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 +1 -1
- package/bin/config.js +4 -2
- package/config/.schema.json +14 -1
- package/config/default.json +460 -4
- package/config/enwiki.json +111 -0
- package/config/jawiki.json +954 -0
- package/config/llwiki.json +117 -0
- package/config/minimum.json +3 -0
- package/config/moegirl.json +104 -0
- package/config/zhwiki.json +109 -0
- package/data/signatures.json +141 -353
- package/dist/base.d.mts +10 -1
- package/dist/base.d.ts +10 -1
- package/dist/bin/config.js +67 -39
- package/dist/index.js +7 -5
- package/dist/lib/document.d.ts +6 -6
- package/dist/lib/document.js +43 -36
- package/dist/lib/lsp.d.ts +20 -3
- package/dist/lib/lsp.js +267 -103
- package/dist/src/attribute.js +10 -4
- package/dist/src/index.js +3 -2
- package/dist/src/nowiki/doubleUnderscore.d.ts +1 -0
- package/dist/src/nowiki/doubleUnderscore.js +2 -0
- package/dist/src/transclude.js +20 -9
- package/dist/util/diff.js +1 -1
- package/dist/util/lint.js +32 -1
- package/i18n/zh-hans.json +1 -0
- package/i18n/zh-hant.json +1 -0
- package/package.json +6 -3
package/dist/lib/lsp.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.LanguageService = exports.tasks = void 0;
|
|
6
|
+
exports.LanguageService = exports.isAttr = exports.tasks = void 0;
|
|
7
7
|
const common_1 = require("@bhsd/common");
|
|
8
8
|
const sharable_1 = require("../util/sharable");
|
|
9
9
|
const lint_1 = require("../util/lint");
|
|
@@ -16,6 +16,7 @@ const util_1 = __importDefault(require("util"));
|
|
|
16
16
|
const child_process_1 = require("child_process");
|
|
17
17
|
const crypto_1 = require("crypto");
|
|
18
18
|
const stylelint_1 = require("@bhsd/common/dist/stylelint");
|
|
19
|
+
const config_1 = __importDefault(require("../bin/config"));
|
|
19
20
|
const document_1 = require("./document");
|
|
20
21
|
/** @see https://www.npmjs.com/package/stylelint-config-recommended */
|
|
21
22
|
const cssRules = {
|
|
@@ -27,7 +28,6 @@ exports.tasks = new WeakMap();
|
|
|
27
28
|
const refTags = new Set(['ref']), referencesTags = new Set(['ref', 'references']), nameAttrs = new Set(['name', 'extends', 'follow']), groupAttrs = new Set(['group']), renameTypes = new Set([
|
|
28
29
|
'arg-name',
|
|
29
30
|
'template-name',
|
|
30
|
-
'magic-word-name',
|
|
31
31
|
'link-target',
|
|
32
32
|
'parameter-key',
|
|
33
33
|
]), referenceTypes = new Set([
|
|
@@ -37,13 +37,33 @@ const refTags = new Set(['ref']), referencesTags = new Set(['ref', 'references']
|
|
|
37
37
|
'image-parameter',
|
|
38
38
|
'heading-title',
|
|
39
39
|
'heading',
|
|
40
|
+
'magic-word-name',
|
|
40
41
|
...renameTypes,
|
|
41
42
|
]), plainTypes = new Set(['text', 'comment', 'noinclude', 'include']), cssSelector = ['ext', 'html', 'table'].map(s => `${s}-attr#style`).join();
|
|
43
|
+
/**
|
|
44
|
+
* Check if a token is a plain attribute.
|
|
45
|
+
* @param token
|
|
46
|
+
* @param token.type
|
|
47
|
+
* @param token.parentNode
|
|
48
|
+
* @param token.length
|
|
49
|
+
* @param token.firstChild
|
|
50
|
+
* @param style whether it is a style attribute
|
|
51
|
+
*/
|
|
52
|
+
const isAttr = ({ type, parentNode, length, firstChild }, style) => type === 'attr-value' && length === 1 && firstChild.type === 'text'
|
|
53
|
+
&& (!style
|
|
54
|
+
|| parentNode.name === 'style'
|
|
55
|
+
&& Boolean(document_1.cssLSP));
|
|
56
|
+
exports.isAttr = isAttr;
|
|
57
|
+
/**
|
|
58
|
+
* Check if a token is an HTML attribute.
|
|
59
|
+
* @param token
|
|
60
|
+
*/
|
|
61
|
+
const isHtmlAttr = (token) => token.type === 'html-attr' || token.type === 'table-attr';
|
|
42
62
|
/**
|
|
43
63
|
* Check if all child nodes are plain text or comments.
|
|
44
|
-
* @param
|
|
64
|
+
* @param token
|
|
45
65
|
*/
|
|
46
|
-
const isPlain = (
|
|
66
|
+
const isPlain = (token) => token.childNodes.every(({ type }) => plainTypes.has(type));
|
|
47
67
|
/**
|
|
48
68
|
* Get the position of a character in the document.
|
|
49
69
|
* @param root root token
|
|
@@ -213,11 +233,12 @@ const getStylelintPos = (rect, bottom, line, column) => {
|
|
|
213
233
|
};
|
|
214
234
|
/**
|
|
215
235
|
* Convert LilyPond errors to VSCode diagnostics.
|
|
236
|
+
* @param root root token
|
|
216
237
|
* @param token `<score>` extension token
|
|
217
238
|
* @param errors LilyPond errors
|
|
218
239
|
*/
|
|
219
|
-
const getLilyPondDiagnostics = (token, errors) => {
|
|
220
|
-
const { top, left } = token.lastChild.
|
|
240
|
+
const getLilyPondDiagnostics = (root, token, errors) => {
|
|
241
|
+
const { top, left } = root.posFromIndex(token.lastChild.getAbsoluteIndex());
|
|
221
242
|
return errors.map(({ line, col, message }) => {
|
|
222
243
|
const pos = (0, lint_1.getEndPos)(top, left, line, col);
|
|
223
244
|
return {
|
|
@@ -254,20 +275,24 @@ class LanguageService {
|
|
|
254
275
|
#completionConfig;
|
|
255
276
|
include = true;
|
|
256
277
|
/** @private */
|
|
278
|
+
config;
|
|
279
|
+
/** @private */
|
|
257
280
|
data;
|
|
258
281
|
/* NOT FOR BROWSER ONLY */
|
|
259
282
|
lilypond;
|
|
283
|
+
/** @private */
|
|
260
284
|
lilypondData;
|
|
261
285
|
/* NOT FOR BROWSER ONLY END */
|
|
262
286
|
/** @param uri 任务标识 */
|
|
263
287
|
constructor(uri) {
|
|
264
288
|
exports.tasks.set(uri, this);
|
|
265
|
-
/* NOT FOR BROWSER ONLY */
|
|
266
289
|
Object.defineProperties(this, {
|
|
290
|
+
config: { enumerable: false },
|
|
267
291
|
data: {
|
|
268
292
|
value: require(path_1.default.join('..', '..', 'data', 'signatures')),
|
|
269
293
|
enumerable: false,
|
|
270
294
|
},
|
|
295
|
+
/* NOT FOR BROWSER ONLY */
|
|
271
296
|
lilypondData: {
|
|
272
297
|
value: require(path_1.default.join('..', '..', 'data', 'ext', 'score')),
|
|
273
298
|
enumerable: false,
|
|
@@ -281,16 +306,18 @@ class LanguageService {
|
|
|
281
306
|
const dir = path_1.default.join(__dirname, 'lilypond');
|
|
282
307
|
if (fs_1.default.existsSync(dir)) {
|
|
283
308
|
for (const file of fs_1.default.readdirSync(dir)) {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
309
|
+
(async () => {
|
|
310
|
+
try {
|
|
311
|
+
await fs_1.default.promises.unlink(path_1.default.join(dir, file));
|
|
312
|
+
}
|
|
313
|
+
catch { }
|
|
314
|
+
})();
|
|
288
315
|
}
|
|
289
316
|
}
|
|
290
317
|
}
|
|
291
318
|
/** 检查解析设置有无更新 */
|
|
292
319
|
#checkConfig() {
|
|
293
|
-
return this.#config ===
|
|
320
|
+
return this.#config === this.config && this.#include === this.include;
|
|
294
321
|
}
|
|
295
322
|
/**
|
|
296
323
|
* 提交解析任务
|
|
@@ -316,10 +343,10 @@ class LanguageService {
|
|
|
316
343
|
* - 总是返回最新的解析结果
|
|
317
344
|
*/
|
|
318
345
|
async #parse() {
|
|
319
|
-
|
|
320
|
-
this.#config =
|
|
346
|
+
this.config ??= index_1.default.getConfig();
|
|
347
|
+
this.#config = this.config;
|
|
321
348
|
this.#include = this.include;
|
|
322
|
-
const text = this.#text, root = await index_1.default.partialParse(text, () => this.#text, this.include, config);
|
|
349
|
+
const text = this.#text, root = await index_1.default.partialParse(text, () => this.#text, this.include, this.config);
|
|
323
350
|
if (this.#checkConfig() && this.#text === text) {
|
|
324
351
|
this.#done = root;
|
|
325
352
|
this.#running = undefined;
|
|
@@ -354,10 +381,10 @@ class LanguageService {
|
|
|
354
381
|
* - 总是返回最新的解析结果
|
|
355
382
|
*/
|
|
356
383
|
async #parseSignature() {
|
|
357
|
-
|
|
358
|
-
this.#config =
|
|
384
|
+
this.config ??= index_1.default.getConfig();
|
|
385
|
+
this.#config = this.config;
|
|
359
386
|
this.#include = this.include;
|
|
360
|
-
const text = this.#text2, root = await index_1.default.partialParse(text, () => this.#text2, this.include, config);
|
|
387
|
+
const text = this.#text2, root = await index_1.default.partialParse(text, () => this.#text2, this.include, this.config);
|
|
361
388
|
if (this.#checkConfig() && this.#text2 === text) {
|
|
362
389
|
this.#done2 = root;
|
|
363
390
|
this.#running2 = undefined;
|
|
@@ -378,14 +405,38 @@ class LanguageService {
|
|
|
378
405
|
*/
|
|
379
406
|
async provideDocumentColors(rgba, text, hsl = true) {
|
|
380
407
|
const root = await this.#queue(text);
|
|
408
|
+
/* NOT FOR BROWSER ONLY */
|
|
409
|
+
let colors;
|
|
410
|
+
try {
|
|
411
|
+
colors = new RegExp(String.raw `\b${Object.keys((await import('color-name')).default).join('|')}\b`, 'giu');
|
|
412
|
+
}
|
|
413
|
+
catch { }
|
|
414
|
+
/* NOT FOR BROWSER ONLY END */
|
|
381
415
|
return root.querySelectorAll('attr-value,parameter-value,arg-default').reverse()
|
|
382
|
-
.flatMap(
|
|
383
|
-
|
|
416
|
+
.flatMap(token => {
|
|
417
|
+
const { type, childNodes,
|
|
418
|
+
/* NOT FOR BROWSER ONLY */
|
|
419
|
+
parentNode, } = token;
|
|
420
|
+
if (type !== 'attr-value' && !isPlain(token)) {
|
|
384
421
|
return [];
|
|
422
|
+
/* NOT FOR BROWSER ONLY */
|
|
385
423
|
}
|
|
424
|
+
else if ((0, exports.isAttr)(token, true)) {
|
|
425
|
+
const textDoc = new document_1.EmbeddedCSSDocument(root, token);
|
|
426
|
+
return document_1.cssLSP.findDocumentColors(textDoc, textDoc.styleSheet);
|
|
427
|
+
/* NOT FOR BROWSER ONLY END */
|
|
428
|
+
}
|
|
429
|
+
/* NOT FOR BROWSER ONLY */
|
|
430
|
+
const isStyle = colors && type === 'attr-value' && parentNode.name === 'style';
|
|
431
|
+
/* NOT FOR BROWSER ONLY END */
|
|
386
432
|
return childNodes.filter((child) => child.type === 'text').reverse()
|
|
387
433
|
.flatMap(child => {
|
|
388
|
-
const parts = (0, common_1.splitColors)(
|
|
434
|
+
const { data } = child, parts = (0, common_1.splitColors)(data, hsl).filter(([, , , isColor]) => isColor);
|
|
435
|
+
/* NOT FOR BROWSER ONLY */
|
|
436
|
+
if (isStyle) {
|
|
437
|
+
parts.push(...[...data.matchAll(colors)].map(({ index, 0: s }) => [s, index, index + s.length, true]));
|
|
438
|
+
}
|
|
439
|
+
/* NOT FOR BROWSER ONLY END */
|
|
389
440
|
if (parts.length === 0) {
|
|
390
441
|
return [];
|
|
391
442
|
}
|
|
@@ -423,8 +474,9 @@ class LanguageService {
|
|
|
423
474
|
}
|
|
424
475
|
/** 准备自动补全设置 */
|
|
425
476
|
#prepareCompletionConfig() {
|
|
426
|
-
if (!this.#completionConfig) {
|
|
427
|
-
|
|
477
|
+
if (!this.#completionConfig || this.#completionConfig[1] !== this.config) {
|
|
478
|
+
this.config ??= index_1.default.getConfig();
|
|
479
|
+
const { nsid, ext, html, parserFunction: [insensitive, sensitive, ...other], doubleUnderscore, protocol, img, } = this.config, tags = new Set([ext, html].flat(2));
|
|
428
480
|
const re = new RegExp('(?:' // eslint-disable-line prefer-template
|
|
429
481
|
+ String.raw `<(\/?\w*)` // tag
|
|
430
482
|
+ '|'
|
|
@@ -439,23 +491,27 @@ class LanguageService {
|
|
|
439
491
|
// attribute key
|
|
440
492
|
+ String.raw `<(\w+)(?:\s(?:[^<>{}|=\s]+(?:\s*=\s*(?:[^\s"']\S*|(["']).*?\8))?(?=\s))*)?\s(\w*)`
|
|
441
493
|
+ ')$', 'iu');
|
|
442
|
-
this.#completionConfig =
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
494
|
+
this.#completionConfig = [
|
|
495
|
+
{
|
|
496
|
+
re,
|
|
497
|
+
ext,
|
|
498
|
+
tags,
|
|
499
|
+
allTags: [...tags, 'onlyinclude', 'includeonly', 'noinclude'],
|
|
500
|
+
functions: [
|
|
501
|
+
Object.keys(insensitive),
|
|
502
|
+
Array.isArray(sensitive) ? /* istanbul ignore next */ sensitive : Object.keys(sensitive),
|
|
503
|
+
other,
|
|
504
|
+
].flat(2),
|
|
505
|
+
switches: doubleUnderscore.slice(0, 2).flat().map(w => `__${w}__`),
|
|
506
|
+
protocols: protocol.split('|'),
|
|
507
|
+
params: Object.keys(img)
|
|
508
|
+
.filter(k => k.endsWith('$1') || !k.includes('$1'))
|
|
509
|
+
.map(k => k.replace(/\$1$/u, '')),
|
|
510
|
+
},
|
|
511
|
+
this.config,
|
|
512
|
+
];
|
|
457
513
|
}
|
|
458
|
-
return this.#completionConfig;
|
|
514
|
+
return this.#completionConfig[0];
|
|
459
515
|
}
|
|
460
516
|
/**
|
|
461
517
|
* Provide auto-completion
|
|
@@ -465,13 +521,25 @@ class LanguageService {
|
|
|
465
521
|
* @param position 位置
|
|
466
522
|
*/
|
|
467
523
|
async provideCompletionItems(text, position) {
|
|
468
|
-
const { re, allTags, functions, switches, protocols, params, tags, ext } = this.#prepareCompletionConfig(), { line, character } = position, curLine = text.split(/\r?\n/u, line + 1)[line], mt = re.exec(curLine?.slice(0, character) ?? '');
|
|
524
|
+
const { re, allTags, functions, switches, protocols, params, tags, ext } = this.#prepareCompletionConfig(), { line, character } = position, curLine = text.split(/\r?\n/u, line + 1)[line], mt = re.exec(curLine?.slice(0, character) ?? ''), [, , iAlias = {}, sAlias = {}] = this.config.doubleUnderscore;
|
|
469
525
|
if (mt?.[1] !== undefined) { // tag
|
|
470
526
|
const closing = mt[1].startsWith('/');
|
|
471
527
|
return getCompletion(allTags, 'Class', mt[1].slice(closing ? 1 : 0), position, closing && !curLine?.slice(character).trim().startsWith('>') ? '>' : '');
|
|
472
528
|
}
|
|
473
529
|
else if (mt?.[4]) { // behavior switch
|
|
474
|
-
return getCompletion(switches, 'Constant', mt[4], position, '', name =>
|
|
530
|
+
return getCompletion(switches, 'Constant', mt[4], position, '', name => {
|
|
531
|
+
if (!this.data) {
|
|
532
|
+
return undefined;
|
|
533
|
+
}
|
|
534
|
+
name = name.slice(2, -2);
|
|
535
|
+
if (name in iAlias) {
|
|
536
|
+
name = iAlias[name];
|
|
537
|
+
}
|
|
538
|
+
else if (name in sAlias) {
|
|
539
|
+
name = sAlias[name];
|
|
540
|
+
}
|
|
541
|
+
return this.#getBehaviorSwitch(name.toLowerCase());
|
|
542
|
+
});
|
|
475
543
|
}
|
|
476
544
|
else if (mt?.[5] !== undefined) { // protocol
|
|
477
545
|
return getCompletion(protocols, 'Reference', mt[5], position);
|
|
@@ -485,27 +553,46 @@ class LanguageService {
|
|
|
485
553
|
return getCompletion(root.querySelectorAll('arg').filter(token => token.name && token !== cur)
|
|
486
554
|
.map(({ name }) => name), 'Variable', match, position);
|
|
487
555
|
}
|
|
488
|
-
const colon = match.startsWith(':'), str = colon ? match.slice(1).trimStart() : match;
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
556
|
+
const [insensitive, sensitive] = this.config.parserFunction, isOld = Array.isArray(sensitive), next = curLine.charAt(character), colon = match.startsWith(':'), str = colon ? match.slice(1).trimStart() : match;
|
|
557
|
+
if (mt[2] === '[[') { // link
|
|
558
|
+
return getCompletion(root.querySelectorAll('link,file,category,redirect-target').filter(token => token !== cur).map(({ name }) => name), 'Folder', str, position);
|
|
559
|
+
}
|
|
560
|
+
// parser function or template
|
|
561
|
+
let words = functions;
|
|
562
|
+
if (next === ':') {
|
|
563
|
+
words = functions.filter(s => !s.endsWith(':'));
|
|
564
|
+
}
|
|
565
|
+
else if (next === ':') {
|
|
566
|
+
words = functions.filter(s => s.endsWith(':')).map(s => s.slice(0, -1));
|
|
567
|
+
}
|
|
568
|
+
return [
|
|
569
|
+
...getCompletion(words, 'Function', match, position, '', name => {
|
|
570
|
+
if (!this.data) {
|
|
571
|
+
return undefined;
|
|
572
|
+
}
|
|
573
|
+
else if (name in insensitive) {
|
|
574
|
+
name = insensitive[name];
|
|
575
|
+
}
|
|
576
|
+
else if (!isOld && name in sensitive) {
|
|
577
|
+
name = sensitive[name];
|
|
578
|
+
}
|
|
579
|
+
return this.#getParserFunction(name.toLowerCase());
|
|
580
|
+
}),
|
|
581
|
+
...match.startsWith('#')
|
|
582
|
+
? []
|
|
583
|
+
: getCompletion(root.querySelectorAll('template').filter(token => token !== cur)
|
|
584
|
+
.map(token => {
|
|
585
|
+
const { name } = token;
|
|
586
|
+
if (colon) {
|
|
587
|
+
return name;
|
|
588
|
+
}
|
|
589
|
+
const { ns } = token.getAttribute('title');
|
|
590
|
+
if (ns === 0) {
|
|
591
|
+
return `:${name}`;
|
|
592
|
+
}
|
|
593
|
+
return ns === 10 ? name.slice(9) : name;
|
|
594
|
+
}), 'Folder', str, position),
|
|
595
|
+
];
|
|
509
596
|
}
|
|
510
597
|
let type, parentNode;
|
|
511
598
|
if (mt?.[7] === undefined) {
|
|
@@ -514,7 +601,7 @@ class LanguageService {
|
|
|
514
601
|
}
|
|
515
602
|
if (mt?.[6] !== undefined || type === 'image-parameter') { // image parameter
|
|
516
603
|
const index = root.indexFromPos(line, character), match = mt?.[6]?.trimStart()
|
|
517
|
-
?? this.#text.slice(cur.getAbsoluteIndex(), index).trimStart(), equal = this.#text
|
|
604
|
+
?? this.#text.slice(cur.getAbsoluteIndex(), index).trimStart(), equal = this.#text[index] === '=';
|
|
518
605
|
return [
|
|
519
606
|
...getCompletion(params, 'Property', match, position)
|
|
520
607
|
.filter(({ label }) => !equal || !/[= ]$/u.test(label)),
|
|
@@ -550,7 +637,7 @@ class LanguageService {
|
|
|
550
637
|
if (t === 'magic-word' && n !== 'invoke') {
|
|
551
638
|
return undefined;
|
|
552
639
|
}
|
|
553
|
-
const
|
|
640
|
+
const key = this.#text.slice(cur.getAbsoluteIndex(), root.indexFromPos(line, character)).trimStart(), [module, func] = t === 'magic-word' ? transclusion.getModule() : [];
|
|
554
641
|
return key
|
|
555
642
|
? getCompletion(root.querySelectorAll('parameter').filter(token => {
|
|
556
643
|
if (token === parentNode
|
|
@@ -568,7 +655,7 @@ class LanguageService {
|
|
|
568
655
|
: undefined;
|
|
569
656
|
/* NOT FOR BROWSER ONLY */
|
|
570
657
|
}
|
|
571
|
-
else if (
|
|
658
|
+
else if ((0, exports.isAttr)(cur, true)) {
|
|
572
659
|
const textDoc = new document_1.EmbeddedCSSDocument(root, cur);
|
|
573
660
|
return document_1.cssLSP.doComplete(textDoc, position, textDoc.styleSheet).items.map((item) => ({
|
|
574
661
|
...item,
|
|
@@ -587,9 +674,9 @@ class LanguageService {
|
|
|
587
674
|
if (lang !== undefined && lang !== 'lilypond') {
|
|
588
675
|
return undefined;
|
|
589
676
|
}
|
|
590
|
-
const
|
|
677
|
+
const before = this.#text.slice(cur.getAbsoluteIndex(), root.indexFromPos(line, character)), comment = before.lastIndexOf('%');
|
|
591
678
|
if (comment !== -1
|
|
592
|
-
&& (before
|
|
679
|
+
&& (before[comment + 1] === '{' || !before.slice(comment).includes('\n'))) {
|
|
593
680
|
return undefined;
|
|
594
681
|
}
|
|
595
682
|
const word = /\\?\b(?:\w|\b(?:->?|\.)|\bly:)+$/u.exec(curLine.slice(0, character))?.[0];
|
|
@@ -604,6 +691,14 @@ class LanguageService {
|
|
|
604
691
|
}
|
|
605
692
|
/* NOT FOR BROWSER ONLY END */
|
|
606
693
|
}
|
|
694
|
+
else if ((0, exports.isAttr)(cur) && isHtmlAttr(parentNode)) {
|
|
695
|
+
const data = lint_1.htmlData.provideValues(parentNode.tag, parentNode.name);
|
|
696
|
+
if (data.length === 0) {
|
|
697
|
+
return undefined;
|
|
698
|
+
}
|
|
699
|
+
const val = this.#text.slice(cur.getAbsoluteIndex(), root.indexFromPos(line, character)).trimStart();
|
|
700
|
+
return getCompletion(data.map(({ name }) => name), 'Value', val, position);
|
|
701
|
+
}
|
|
607
702
|
return undefined;
|
|
608
703
|
}
|
|
609
704
|
/**
|
|
@@ -695,7 +790,7 @@ class LanguageService {
|
|
|
695
790
|
lilypondDiagnostics = await Promise.all(tokens.map(async (token) => {
|
|
696
791
|
const { innerText } = token, score = `showLastLength = R1${token.getAttr('raw') === undefined ? ` \\score {\n${innerText}\n}` : `\n${innerText}`}`;
|
|
697
792
|
if (scores.has(score)) {
|
|
698
|
-
return getLilyPondDiagnostics(token, scores.get(score));
|
|
793
|
+
return getLilyPondDiagnostics(root, token, scores.get(score));
|
|
699
794
|
}
|
|
700
795
|
const hash = (0, crypto_1.createHash)('sha256');
|
|
701
796
|
hash.update(score);
|
|
@@ -717,7 +812,7 @@ class LanguageService {
|
|
|
717
812
|
};
|
|
718
813
|
});
|
|
719
814
|
scores.set(score, lilypondErrors);
|
|
720
|
-
return getLilyPondDiagnostics(token, lilypondErrors);
|
|
815
|
+
return getLilyPondDiagnostics(root, token, lilypondErrors);
|
|
721
816
|
}
|
|
722
817
|
}
|
|
723
818
|
return [];
|
|
@@ -797,16 +892,23 @@ class LanguageService {
|
|
|
797
892
|
* @param text source Wikitext / 源代码
|
|
798
893
|
*/
|
|
799
894
|
async provideLinks(text) {
|
|
800
|
-
|
|
895
|
+
this.config ??= index_1.default.getConfig();
|
|
896
|
+
const { articlePath, protocol } = this.config, absolute = articlePath?.includes('//'), protocolRegex = new RegExp(`^(?:${protocol}|//)`, 'iu');
|
|
801
897
|
return (await this.#queue(text))
|
|
802
|
-
.querySelectorAll(`magic-link,ext-link-url,free-ext-link,attr-value,image-parameter#link${absolute ? ',link-target,template-name,invoke-module' : ''}`)
|
|
898
|
+
.querySelectorAll(`magic-link,ext-link-url,free-ext-link,attr-value,image-parameter#link${absolute ? ',link-target,template-name,invoke-module,magic-word#filepath,magic-word#widget' : ''}`)
|
|
803
899
|
.reverse()
|
|
804
900
|
.map((token) => {
|
|
805
|
-
|
|
901
|
+
let name;
|
|
902
|
+
if (token.is('magic-word')) {
|
|
903
|
+
({ name } = token);
|
|
904
|
+
token = token.childNodes[1].lastChild; // eslint-disable-line no-param-reassign
|
|
905
|
+
}
|
|
906
|
+
const { type, parentNode, firstChild, lastChild, childNodes, length } = token, { tag } = parentNode;
|
|
907
|
+
name ??= parentNode.name;
|
|
806
908
|
if (!(type !== 'attr-value'
|
|
807
909
|
|| name === 'src' && ['templatestyles', 'img'].includes(tag)
|
|
808
910
|
|| name === 'cite' && ['blockquote', 'del', 'ins', 'q'].includes(tag))
|
|
809
|
-
|| !isPlain(
|
|
911
|
+
|| !isPlain(token)) {
|
|
810
912
|
return false;
|
|
811
913
|
}
|
|
812
914
|
let target = childNodes.filter((node) => node.type === 'text')
|
|
@@ -833,21 +935,25 @@ class LanguageService {
|
|
|
833
935
|
else if (type === 'template-name') {
|
|
834
936
|
target = parentNode.getAttribute('title').getUrl(articlePath);
|
|
835
937
|
}
|
|
836
|
-
else if (['link-target', 'invoke-module'].includes(type)
|
|
938
|
+
else if (['link-target', 'invoke-module', 'parameter-value'].includes(type)
|
|
837
939
|
|| type === 'attr-value' && name === 'src' && tag === 'templatestyles'
|
|
838
940
|
|| type === 'image-parameter' && !protocolRegex.test(target)) {
|
|
839
941
|
if (!absolute || target.startsWith('/')) {
|
|
840
942
|
return false;
|
|
841
943
|
}
|
|
842
944
|
let ns = 0;
|
|
843
|
-
|
|
844
|
-
|
|
945
|
+
switch (type) {
|
|
946
|
+
case 'attr-value':
|
|
947
|
+
ns = 10;
|
|
948
|
+
break;
|
|
949
|
+
case 'invoke-module':
|
|
950
|
+
ns = 828;
|
|
951
|
+
break;
|
|
952
|
+
case 'parameter-value':
|
|
953
|
+
ns = name === 'filepath' ? 6 : 274;
|
|
954
|
+
// no default
|
|
845
955
|
}
|
|
846
|
-
|
|
847
|
-
ns = 828;
|
|
848
|
-
}
|
|
849
|
-
const title = index_1.default
|
|
850
|
-
.normalizeTitle(target, ns, false, undefined, true);
|
|
956
|
+
const title = index_1.default.normalizeTitle(target, ns, false, this.config, true);
|
|
851
957
|
/* istanbul ignore if */
|
|
852
958
|
if (!title.valid) {
|
|
853
959
|
return false;
|
|
@@ -991,51 +1097,88 @@ class LanguageService {
|
|
|
991
1097
|
if (!this.data) {
|
|
992
1098
|
return undefined;
|
|
993
1099
|
}
|
|
994
|
-
const root = await this.#queue(text)
|
|
995
|
-
let
|
|
996
|
-
if (
|
|
997
|
-
|
|
1100
|
+
const root = await this.#queue(text);
|
|
1101
|
+
let { offsetNode, offset } = caretPositionFromWord(root, this.#text, position);
|
|
1102
|
+
if (offsetNode.type === 'text') {
|
|
1103
|
+
offset += offsetNode.getRelativeIndex();
|
|
1104
|
+
offsetNode = offsetNode.parentNode;
|
|
1105
|
+
}
|
|
1106
|
+
const { type, parentNode, length, name } = offsetNode;
|
|
1107
|
+
let info, f, colon, range;
|
|
1108
|
+
if (offsetNode.is('double-underscore') && offset > 0) {
|
|
1109
|
+
info = this.#getBehaviorSwitch(offsetNode.name);
|
|
998
1110
|
}
|
|
999
1111
|
else if (type === 'magic-word-name') {
|
|
1000
1112
|
info = this.#getParserFunction(parentNode.name);
|
|
1001
|
-
f =
|
|
1113
|
+
f = offsetNode.toString(true).trim();
|
|
1114
|
+
colon = parentNode.getAttribute('colon');
|
|
1002
1115
|
}
|
|
1003
|
-
else if (
|
|
1004
|
-
&& (offset > 0 || root.posFromIndex(
|
|
1116
|
+
else if (offsetNode.is('magic-word') && !offsetNode.modifier && length === 1
|
|
1117
|
+
&& (offset > 0 || root.posFromIndex(offsetNode.getAbsoluteIndex()).left === position.character)) {
|
|
1005
1118
|
info = this.#getParserFunction(name);
|
|
1006
|
-
f =
|
|
1119
|
+
f = offsetNode.firstChild.toString(true).trim();
|
|
1120
|
+
colon = offsetNode.getAttribute('colon');
|
|
1007
1121
|
}
|
|
1008
|
-
else if ((
|
|
1009
|
-
&&
|
|
1010
|
-
f =
|
|
1122
|
+
else if ((offsetNode.is('magic-word') || offsetNode.is('template'))
|
|
1123
|
+
&& offsetNode.modifier && offset >= 2 && offsetNode.getRelativeIndex(0) > offset) {
|
|
1124
|
+
f = offsetNode.modifier.trim().slice(0, -1);
|
|
1011
1125
|
info = this.#getParserFunction(f.toLowerCase());
|
|
1126
|
+
colon = ':';
|
|
1012
1127
|
if (info) {
|
|
1013
|
-
const aIndex =
|
|
1128
|
+
const aIndex = offsetNode.getAbsoluteIndex();
|
|
1014
1129
|
range = {
|
|
1015
1130
|
start: positionAt(root, aIndex + 2),
|
|
1016
|
-
end: positionAt(root, aIndex +
|
|
1131
|
+
end: positionAt(root, aIndex + offsetNode.modifier.trimEnd().length + 1),
|
|
1017
1132
|
};
|
|
1018
1133
|
}
|
|
1019
1134
|
/* NOT FOR BROWSER ONLY */
|
|
1020
1135
|
}
|
|
1021
|
-
else if (
|
|
1022
|
-
const textDoc = new document_1.EmbeddedCSSDocument(root,
|
|
1136
|
+
else if ((0, exports.isAttr)(offsetNode, true)) {
|
|
1137
|
+
const textDoc = new document_1.EmbeddedCSSDocument(root, offsetNode);
|
|
1023
1138
|
return document_1.cssLSP.doHover(textDoc, position, textDoc.styleSheet) ?? undefined;
|
|
1024
1139
|
}
|
|
1025
1140
|
else if (document_1.jsonLSP && type === 'ext-inner' && document_1.jsonTags.includes(name)) {
|
|
1026
|
-
const textDoc = new document_1.EmbeddedJSONDocument(root,
|
|
1141
|
+
const textDoc = new document_1.EmbeddedJSONDocument(root, offsetNode);
|
|
1027
1142
|
return await document_1.jsonLSP.doHover(textDoc, position, textDoc.jsonDoc) ?? undefined;
|
|
1143
|
+
}
|
|
1144
|
+
else if (lint_1.htmlData.provideTags && lint_1.htmlData.provideAttributes) {
|
|
1145
|
+
if (type === 'html' && offset <= offsetNode.getRelativeIndex(0)
|
|
1146
|
+
|| type === 'html-attr-dirty' && offset === 0 && parentNode.firstChild === offsetNode) {
|
|
1147
|
+
const token = type === 'html' ? offsetNode : parentNode.parentNode, data = lint_1.htmlData.provideTags().find(({ name: n }) => n === token.name);
|
|
1148
|
+
if (data?.description) {
|
|
1149
|
+
const start = positionAt(root, token.getAbsoluteIndex());
|
|
1150
|
+
return {
|
|
1151
|
+
contents: data.description,
|
|
1152
|
+
range: {
|
|
1153
|
+
start,
|
|
1154
|
+
end: {
|
|
1155
|
+
line: start.line,
|
|
1156
|
+
character: start.character + token.getRelativeIndex(0),
|
|
1157
|
+
},
|
|
1158
|
+
},
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
else if (type === 'attr-key' && isHtmlAttr(parentNode)) {
|
|
1163
|
+
const data = lint_1.htmlData.provideAttributes(parentNode.tag).find(({ name: n }) => n === parentNode.name);
|
|
1164
|
+
if (data?.description) {
|
|
1165
|
+
return {
|
|
1166
|
+
contents: data.description,
|
|
1167
|
+
range: createNodeRange(offsetNode),
|
|
1168
|
+
};
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1028
1171
|
/* NOT FOR BROWSER ONLY END */
|
|
1029
1172
|
}
|
|
1030
1173
|
return info && {
|
|
1031
1174
|
contents: {
|
|
1032
1175
|
kind: 'markdown',
|
|
1033
1176
|
value: (info.signatures
|
|
1034
|
-
? `${info.signatures.map(params => `- **{{ ${f}${params.length === 0 ? '**' :
|
|
1177
|
+
? `${info.signatures.map(params => `- **{{ ${f}${params.length === 0 ? '**' : `${colon}** `}${params.map(({ label, const: c }) => c ? label : `*${label}*`).join(' **|** ')} **}}**`).join('\n')}\n\n`
|
|
1035
1178
|
: '')
|
|
1036
1179
|
+ info.description,
|
|
1037
1180
|
},
|
|
1038
|
-
range: range ?? createNodeRange(
|
|
1181
|
+
range: range ?? createNodeRange(offsetNode),
|
|
1039
1182
|
};
|
|
1040
1183
|
}
|
|
1041
1184
|
/**
|
|
@@ -1073,10 +1216,10 @@ class LanguageService {
|
|
|
1073
1216
|
break;
|
|
1074
1217
|
}
|
|
1075
1218
|
}
|
|
1076
|
-
const f = firstChild.toString(true).trim();
|
|
1219
|
+
const f = firstChild.toString(true).trim(), colon = lastChild.getAttribute('colon');
|
|
1077
1220
|
return {
|
|
1078
1221
|
signatures: candidates.map((params) => ({
|
|
1079
|
-
label: `{{${f}${params.length === 0 ? '' :
|
|
1222
|
+
label: `{{${f}${params.length === 0 ? '' : colon}${params.map(({ label }) => label).join('|')}}}`,
|
|
1080
1223
|
parameters: params.map(({ label, const: c }) => ({
|
|
1081
1224
|
label,
|
|
1082
1225
|
...c ? { documentation: 'Predefined parameter' } : undefined,
|
|
@@ -1108,8 +1251,7 @@ class LanguageService {
|
|
|
1108
1251
|
}
|
|
1109
1252
|
/** @private */
|
|
1110
1253
|
findStyleTokens() {
|
|
1111
|
-
return this.#done.querySelectorAll(cssSelector)
|
|
1112
|
-
.filter(({ lastChild: { length, firstChild } }) => length === 1 && firstChild.type === 'text');
|
|
1254
|
+
return this.#done.querySelectorAll(cssSelector).filter(({ lastChild }) => (0, exports.isAttr)(lastChild));
|
|
1113
1255
|
}
|
|
1114
1256
|
/* NOT FOR BROWSER ONLY */
|
|
1115
1257
|
/**
|
|
@@ -1171,5 +1313,27 @@ class LanguageService {
|
|
|
1171
1313
|
}
|
|
1172
1314
|
return symbols;
|
|
1173
1315
|
}
|
|
1316
|
+
/**
|
|
1317
|
+
* Set the target Wikipedia
|
|
1318
|
+
*
|
|
1319
|
+
* 设置目标维基百科
|
|
1320
|
+
* @param wiki Wikipedia URL / 维基百科网址
|
|
1321
|
+
* @throws `RangeError` 不是有效的维基百科网址
|
|
1322
|
+
*/
|
|
1323
|
+
async setTargetWikipedia(wiki) {
|
|
1324
|
+
const mt = /^https?:\/\/([^./]+)\.wikipedia\.org/iu.exec(wiki);
|
|
1325
|
+
if (!mt) {
|
|
1326
|
+
throw new RangeError('Invalid Wikipedia URL!');
|
|
1327
|
+
}
|
|
1328
|
+
const site = `${mt[1].toLowerCase()}wiki`;
|
|
1329
|
+
try {
|
|
1330
|
+
const config = require(path_1.default.join('..', '..', 'config', site));
|
|
1331
|
+
this.config = index_1.default.getConfig(config);
|
|
1332
|
+
}
|
|
1333
|
+
catch {
|
|
1334
|
+
this.config = index_1.default.getConfig(await (0, config_1.default)(site, `${mt[0]}/w`));
|
|
1335
|
+
}
|
|
1336
|
+
Object.assign(this.config, { articlePath: `${mt[0]}/wiki/` });
|
|
1337
|
+
}
|
|
1174
1338
|
}
|
|
1175
1339
|
exports.LanguageService = LanguageService;
|