@mirascript/monaco 0.1.83 → 0.1.84
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/basic/index.js +1 -1
- package/dist/basic/tokens-provider.d.ts.map +1 -1
- package/dist/{chunk-7ZK72GPH.js → chunk-7NT3EI6C.js} +21 -16
- package/dist/{chunk-7ZK72GPH.js.map → chunk-7NT3EI6C.js.map} +1 -1
- package/dist/lsp/compile-result.d.ts +1 -1
- package/dist/lsp/compile-result.d.ts.map +1 -1
- package/dist/lsp/index.js +148 -178
- package/dist/lsp/index.js.map +1 -1
- package/dist/lsp/providers/color-provider.d.ts.map +1 -1
- package/dist/lsp/providers/hover-provider.d.ts.map +1 -1
- package/dist/lsp/providers/inlay-hints-provider.d.ts.map +1 -1
- package/dist/lsp/utils.d.ts.map +1 -1
- package/dist/lsp/worker-core.d.ts +23 -0
- package/dist/lsp/worker-core.d.ts.map +1 -0
- package/dist/lsp/worker.d.ts +1 -22
- package/dist/lsp/worker.d.ts.map +1 -1
- package/dist/lsp/worker.js +3 -45
- package/dist/lsp/worker.js.map +1 -1
- package/dist/worker-core-MG2FDDNN.js +45 -0
- package/dist/worker-core-MG2FDDNN.js.map +6 -0
- package/package.json +7 -9
- package/src/basic/tokens-provider.ts +23 -18
- package/src/lsp/compile-result.ts +1 -1
- package/src/lsp/providers/color-provider.ts +42 -44
- package/src/lsp/providers/hover-provider.ts +27 -59
- package/src/lsp/providers/inlay-hints-provider.ts +93 -89
- package/src/lsp/utils.ts +1 -3
- package/src/lsp/worker-core.ts +68 -0
- package/src/lsp/worker-helper.ts +3 -3
- package/src/lsp/worker.ts +5 -70
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { HighlighterCore, Grammar } from '
|
|
2
|
-
import type { StateStack } from '
|
|
1
|
+
import type { HighlighterCore, Grammar } from 'shiki/core';
|
|
2
|
+
import type { StateStack } from 'shiki/textmate';
|
|
3
3
|
import { languages, type IDisposable } from '../monaco-api.js';
|
|
4
4
|
import { CONTRIBUTE_IDS } from '../contribute.js';
|
|
5
5
|
|
|
@@ -35,29 +35,35 @@ function tokenScope(scopes: string[]): string {
|
|
|
35
35
|
/** Shared instance of highlighter. */
|
|
36
36
|
class HighlighterManager implements IDisposable {
|
|
37
37
|
private highlighterPromise: Promise<HighlighterCore> | null = null;
|
|
38
|
-
|
|
38
|
+
private INITIAL!: StateStack;
|
|
39
39
|
/**
|
|
40
|
-
*
|
|
40
|
+
* Load the shared highlighter instance.
|
|
41
41
|
*/
|
|
42
|
-
private async
|
|
43
|
-
if (this.highlighterPromise) return this.highlighterPromise;
|
|
44
|
-
|
|
42
|
+
private async loadHighlighter(): Promise<HighlighterCore> {
|
|
45
43
|
const [
|
|
46
44
|
{ createHighlighterCore },
|
|
47
|
-
{
|
|
48
|
-
|
|
45
|
+
{ createJavaScriptRegexEngine },
|
|
46
|
+
{ INITIAL },
|
|
49
47
|
{ mirascript, mirascriptDoc, mirascriptTemplate },
|
|
50
48
|
] = await Promise.all([
|
|
51
|
-
import('
|
|
52
|
-
import('
|
|
53
|
-
import('
|
|
49
|
+
import('shiki/core'),
|
|
50
|
+
import('shiki/engine-javascript.mjs'),
|
|
51
|
+
import('shiki/textmate'),
|
|
54
52
|
import('@mirascript/textmate'),
|
|
55
53
|
]);
|
|
56
|
-
this.
|
|
54
|
+
this.INITIAL = INITIAL;
|
|
55
|
+
return await createHighlighterCore({
|
|
57
56
|
langs: [mirascript, mirascriptDoc, mirascriptTemplate],
|
|
58
57
|
themes: [],
|
|
59
|
-
engine:
|
|
58
|
+
engine: createJavaScriptRegexEngine(),
|
|
60
59
|
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get the shared highlighter instance.
|
|
64
|
+
*/
|
|
65
|
+
private async getHighlighter(): Promise<HighlighterCore> {
|
|
66
|
+
this.highlighterPromise ??= this.loadHighlighter();
|
|
61
67
|
return this.highlighterPromise;
|
|
62
68
|
}
|
|
63
69
|
|
|
@@ -66,9 +72,8 @@ class HighlighterManager implements IDisposable {
|
|
|
66
72
|
return {
|
|
67
73
|
create: async () => {
|
|
68
74
|
const highlighter = await this.getHighlighter();
|
|
69
|
-
const { INITIAL } = await import('@shikijs/vscode-textmate');
|
|
70
75
|
const grammar = highlighter.getLanguage(languageId);
|
|
71
|
-
return new TokensProvider(grammar, INITIAL);
|
|
76
|
+
return new TokensProvider(grammar, this.INITIAL);
|
|
72
77
|
},
|
|
73
78
|
};
|
|
74
79
|
}
|
|
@@ -91,11 +96,11 @@ class HighlighterManager implements IDisposable {
|
|
|
91
96
|
class TokensProvider implements languages.TokensProvider {
|
|
92
97
|
constructor(
|
|
93
98
|
private readonly grammar: Grammar,
|
|
94
|
-
private readonly
|
|
99
|
+
private readonly INITIAL: StateStack,
|
|
95
100
|
) {}
|
|
96
101
|
/** @inheritdoc */
|
|
97
102
|
getInitialState(): StateStack {
|
|
98
|
-
return this.
|
|
103
|
+
return this.INITIAL;
|
|
99
104
|
}
|
|
100
105
|
/** @inheritdoc */
|
|
101
106
|
tokenize(line: string, state: StateStack): languages.ILineTokens {
|
|
@@ -2,7 +2,7 @@ import type { Writable } from 'type-fest';
|
|
|
2
2
|
import { type editor, Range, type IRange, type IPosition, Position } from '../monaco-api.js';
|
|
3
3
|
import { strictContainsPosition } from './monaco-utils.js';
|
|
4
4
|
import { REG_IDENTIFIER_FULL, REG_ORDINAL_FULL } from '../constants.js';
|
|
5
|
-
import type { CacheKey, MonacoResult } from './worker.js';
|
|
5
|
+
import type { CacheKey, MonacoResult } from './worker-core.js';
|
|
6
6
|
import {
|
|
7
7
|
parseDiagnostics,
|
|
8
8
|
DiagnosticCode,
|
|
@@ -5,6 +5,15 @@ import { DiagnosticCode } from '@mirascript/constants';
|
|
|
5
5
|
const REG_COLOR_STR = /^(@*)(['"`])(#(?:[0-9a-f]{6}|[0-9a-f]{3}|[0-9a-f]{8}|[0-9a-f]{4}))\2\1$/iu;
|
|
6
6
|
const { parseInt } = Number;
|
|
7
7
|
|
|
8
|
+
/** 解析颜色 */
|
|
9
|
+
function parseColorPart(text: string): number {
|
|
10
|
+
if (text.length === 1) {
|
|
11
|
+
// 处理单字符的情况,例如 #f00 -> #ff0000
|
|
12
|
+
text = text + text;
|
|
13
|
+
}
|
|
14
|
+
return parseInt(text, 16) / 255;
|
|
15
|
+
}
|
|
16
|
+
|
|
8
17
|
/** 解析颜色 */
|
|
9
18
|
function parseColorString(text: string):
|
|
10
19
|
| {
|
|
@@ -25,36 +34,21 @@ function parseColorString(text: string):
|
|
|
25
34
|
if (colorString.startsWith('#')) {
|
|
26
35
|
const colorCode = colorString.slice(1);
|
|
27
36
|
|
|
28
|
-
if (colorCode.length === 3) {
|
|
29
|
-
//
|
|
30
|
-
color = {
|
|
31
|
-
red: parseInt(colorCode[0]! + colorCode[0]!, 16) / 255,
|
|
32
|
-
green: parseInt(colorCode[1]! + colorCode[1]!, 16) / 255,
|
|
33
|
-
blue: parseInt(colorCode[2]! + colorCode[2]!, 16) / 255,
|
|
34
|
-
alpha: 1,
|
|
35
|
-
};
|
|
36
|
-
} else if (colorCode.length === 6) {
|
|
37
|
-
color = {
|
|
38
|
-
red: parseInt(colorCode.slice(0, 2), 16) / 255,
|
|
39
|
-
green: parseInt(colorCode.slice(2, 4), 16) / 255,
|
|
40
|
-
blue: parseInt(colorCode.slice(4, 6), 16) / 255,
|
|
41
|
-
alpha: 1, // 假设不透明
|
|
42
|
-
};
|
|
43
|
-
} else if (colorCode.length === 8) {
|
|
44
|
-
// 处理 #RRGGBBAA 格式
|
|
37
|
+
if (colorCode.length === 3 || colorCode.length === 4) {
|
|
38
|
+
// 处理短格式的颜色代码,例如 #f00 或 #f00f
|
|
45
39
|
color = {
|
|
46
|
-
red:
|
|
47
|
-
green:
|
|
48
|
-
blue:
|
|
49
|
-
alpha:
|
|
40
|
+
red: parseColorPart(colorCode[0]!),
|
|
41
|
+
green: parseColorPart(colorCode[1]!),
|
|
42
|
+
blue: parseColorPart(colorCode[2]!),
|
|
43
|
+
alpha: colorCode[3] ? parseColorPart(colorCode[3]) : 1, // 如果没有 alpha,则假设不透明
|
|
50
44
|
};
|
|
51
|
-
} else if (colorCode.length ===
|
|
52
|
-
//
|
|
45
|
+
} else if (colorCode.length === 6 || colorCode.length === 8) {
|
|
46
|
+
// 处理长格式的颜色代码,例如 #ff0000 或 #ff0000ff
|
|
53
47
|
color = {
|
|
54
|
-
red:
|
|
55
|
-
green:
|
|
56
|
-
blue:
|
|
57
|
-
alpha:
|
|
48
|
+
red: parseColorPart(colorCode.slice(0, 2)),
|
|
49
|
+
green: parseColorPart(colorCode.slice(2, 4)),
|
|
50
|
+
blue: parseColorPart(colorCode.slice(4, 6)),
|
|
51
|
+
alpha: colorCode.length === 8 ? parseColorPart(colorCode.slice(6, 8)) : 1, // 如果没有 alpha,则假设不透明
|
|
58
52
|
};
|
|
59
53
|
} else {
|
|
60
54
|
return undefined; // 不支持的颜色格式
|
|
@@ -71,6 +65,26 @@ function parseColorString(text: string):
|
|
|
71
65
|
};
|
|
72
66
|
}
|
|
73
67
|
|
|
68
|
+
/** 生成颜色 */
|
|
69
|
+
function serializeColorPart(part: number): string {
|
|
70
|
+
const intPart = Math.round(part * 255);
|
|
71
|
+
const hex = intPart.toString(16).padStart(2, '0');
|
|
72
|
+
return hex;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** 生成颜色 */
|
|
76
|
+
function serializeColor(color: languages.IColor): string {
|
|
77
|
+
const r = serializeColorPart(color.red);
|
|
78
|
+
const g = serializeColorPart(color.green);
|
|
79
|
+
const b = serializeColorPart(color.blue);
|
|
80
|
+
if (color.alpha >= 1) {
|
|
81
|
+
return `#${r}${g}${b}`;
|
|
82
|
+
} else {
|
|
83
|
+
const a = serializeColorPart(color.alpha);
|
|
84
|
+
return `#${r}${g}${b}${a}`;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
74
88
|
/** @inheritdoc */
|
|
75
89
|
export class ColorProvider extends Provider implements languages.DocumentColorProvider {
|
|
76
90
|
/** @inheritdoc */
|
|
@@ -109,22 +123,6 @@ export class ColorProvider extends Provider implements languages.DocumentColorPr
|
|
|
109
123
|
token: CancellationToken,
|
|
110
124
|
): languages.ProviderResult<languages.IColorPresentation[]> {
|
|
111
125
|
const { color } = colorInfo;
|
|
112
|
-
return [
|
|
113
|
-
{
|
|
114
|
-
label: `#${Math.round(color.red * 255)
|
|
115
|
-
.toString(16)
|
|
116
|
-
.padStart(2, '0')}${Math.round(color.green * 255)
|
|
117
|
-
.toString(16)
|
|
118
|
-
.padStart(2, '0')}${Math.round(color.blue * 255)
|
|
119
|
-
.toString(16)
|
|
120
|
-
.padStart(2, '0')}${
|
|
121
|
-
color.alpha >= 1
|
|
122
|
-
? ''
|
|
123
|
-
: Math.round(color.alpha * 255)
|
|
124
|
-
.toString(16)
|
|
125
|
-
.padStart(2, '0')
|
|
126
|
-
}`,
|
|
127
|
-
},
|
|
128
|
-
];
|
|
126
|
+
return [{ label: serializeColor(color) }];
|
|
129
127
|
}
|
|
130
128
|
}
|
|
@@ -6,7 +6,7 @@ import type { editor, CancellationToken, IMarkdownString, IRange, languages, Pos
|
|
|
6
6
|
import { codeblock, getDeep, valueDoc, paramsList, serializeNumber, serializeInteger } from '../utils.js';
|
|
7
7
|
import { tokenAt } from '../monaco-private.js';
|
|
8
8
|
import { rangeAt } from '../monaco-utils.js';
|
|
9
|
-
import type { FieldsAccessAt, VariableAccessAt } from '../compile-result.js';
|
|
9
|
+
import type { FieldsAccessAt, LocalDefinition, VariableAccessAt } from '../compile-result.js';
|
|
10
10
|
import { Provider } from './base.js';
|
|
11
11
|
|
|
12
12
|
const OPERATOR_TOKENS_DESC = Object.keys(HELP_OPERATORS).sort((a, b) => b.length - a.length);
|
|
@@ -44,6 +44,25 @@ function operatorAt(lineContent: string, column: number): { token: string; range
|
|
|
44
44
|
return undefined;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
const LOCAL_HOVER = {
|
|
48
|
+
[DiagnosticCode.ParameterSubPatternImmutable]: '(parameter pattern) ',
|
|
49
|
+
[DiagnosticCode.ParameterSubPatternMutable]: '(parameter pattern) mut ',
|
|
50
|
+
[DiagnosticCode.ParameterImmutable]: '(parameter) ',
|
|
51
|
+
[DiagnosticCode.ParameterMutable]: '(parameter) mut ',
|
|
52
|
+
[DiagnosticCode.ParameterIt]: '(parameter) it',
|
|
53
|
+
[DiagnosticCode.ParameterImmutableRest]: '(parameter) ..',
|
|
54
|
+
[DiagnosticCode.ParameterMutableRest]: '(parameter) ..mut ',
|
|
55
|
+
[DiagnosticCode.LocalModule]: 'mod ',
|
|
56
|
+
[DiagnosticCode.LocalImmutable]: 'let ',
|
|
57
|
+
[DiagnosticCode.LocalConst]: 'const ',
|
|
58
|
+
[DiagnosticCode.LocalMutable]: 'let mut ',
|
|
59
|
+
[DiagnosticCode.LocalFunction]: (text: string, model: editor.ITextModel, def: LocalDefinition): string => {
|
|
60
|
+
return `fn ${text}${paramsList(model, def.fn)}`;
|
|
61
|
+
},
|
|
62
|
+
} satisfies Partial<
|
|
63
|
+
Record<DiagnosticCode, string | ((text: string, model: editor.ITextModel, def: LocalDefinition) => string)>
|
|
64
|
+
>;
|
|
65
|
+
|
|
47
66
|
/** @inheritdoc */
|
|
48
67
|
export class HoverProvider extends Provider implements languages.HoverProvider {
|
|
49
68
|
/** 变量提示 */
|
|
@@ -65,69 +84,18 @@ export class HoverProvider extends Provider implements languages.HoverProvider {
|
|
|
65
84
|
} else {
|
|
66
85
|
let content: IMarkdownString | undefined;
|
|
67
86
|
const tag = def.definition;
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
};
|
|
73
|
-
break;
|
|
74
|
-
case DiagnosticCode.ParameterSubPatternMutable:
|
|
75
|
-
content = {
|
|
76
|
-
value: codeblock(`\0(parameter pattern) mut ${model.getValueInRange(tag.range)}`),
|
|
77
|
-
};
|
|
78
|
-
break;
|
|
79
|
-
case DiagnosticCode.ParameterImmutable:
|
|
80
|
-
content = {
|
|
81
|
-
value: codeblock(`\0(parameter) ${model.getValueInRange(tag.range)}`),
|
|
82
|
-
};
|
|
83
|
-
break;
|
|
84
|
-
case DiagnosticCode.ParameterMutable:
|
|
85
|
-
content = {
|
|
86
|
-
value: codeblock(`\0(parameter) mut ${model.getValueInRange(tag.range)}`),
|
|
87
|
-
};
|
|
88
|
-
break;
|
|
89
|
-
case DiagnosticCode.ParameterIt:
|
|
90
|
-
content = {
|
|
91
|
-
value: codeblock(`\0(parameter) it`),
|
|
92
|
-
};
|
|
93
|
-
break;
|
|
94
|
-
case DiagnosticCode.ParameterImmutableRest:
|
|
87
|
+
if (tag.code in LOCAL_HOVER) {
|
|
88
|
+
const hover = LOCAL_HOVER[tag.code];
|
|
89
|
+
const text = model.getValueInRange(tag.range);
|
|
90
|
+
if (typeof hover == 'string') {
|
|
95
91
|
content = {
|
|
96
|
-
value: codeblock(`\0
|
|
92
|
+
value: codeblock(`\0${hover}${text}`),
|
|
97
93
|
};
|
|
98
|
-
|
|
99
|
-
case DiagnosticCode.ParameterMutableRest:
|
|
100
|
-
content = {
|
|
101
|
-
value: codeblock(`\0(parameter) ..mut ${model.getValueInRange(tag.range)}`),
|
|
102
|
-
};
|
|
103
|
-
break;
|
|
104
|
-
case DiagnosticCode.LocalFunction: {
|
|
105
|
-
const params = paramsList(model, def.fn);
|
|
94
|
+
} else {
|
|
106
95
|
content = {
|
|
107
|
-
value: codeblock(`\
|
|
96
|
+
value: codeblock(`\0${hover(text, model, def)}`),
|
|
108
97
|
};
|
|
109
|
-
break;
|
|
110
98
|
}
|
|
111
|
-
case DiagnosticCode.LocalModule:
|
|
112
|
-
content = {
|
|
113
|
-
value: codeblock(`\0mod ${model.getValueInRange(tag.range)}`),
|
|
114
|
-
};
|
|
115
|
-
break;
|
|
116
|
-
case DiagnosticCode.LocalImmutable:
|
|
117
|
-
content = {
|
|
118
|
-
value: codeblock(`\0let ${model.getValueInRange(tag.range)}`),
|
|
119
|
-
};
|
|
120
|
-
break;
|
|
121
|
-
case DiagnosticCode.LocalConst:
|
|
122
|
-
content = {
|
|
123
|
-
value: codeblock(`\0const ${model.getValueInRange(tag.range)}`),
|
|
124
|
-
};
|
|
125
|
-
break;
|
|
126
|
-
case DiagnosticCode.LocalMutable:
|
|
127
|
-
content = {
|
|
128
|
-
value: codeblock(`\0let mut ${model.getValueInRange(tag.range)}`),
|
|
129
|
-
};
|
|
130
|
-
break;
|
|
131
99
|
}
|
|
132
100
|
if (ref == null) {
|
|
133
101
|
range = tag.range;
|
|
@@ -1,7 +1,96 @@
|
|
|
1
1
|
import { languages, Range, type CancellationToken, type editor, type IEvent } from '../../monaco-api.js';
|
|
2
|
+
import type { SourceDiagnostic } from '../compile-result.js';
|
|
2
3
|
import { Provider } from './base.js';
|
|
3
4
|
import { DiagnosticCode } from '@mirascript/constants';
|
|
4
5
|
|
|
6
|
+
/** 生成 hint */
|
|
7
|
+
function provideInlayHint(tag: SourceDiagnostic, model: editor.ITextModel): languages.InlayHint | null {
|
|
8
|
+
let lineNumber = 0;
|
|
9
|
+
let column = 0;
|
|
10
|
+
let label = '';
|
|
11
|
+
const kind = languages.InlayHintKind.Parameter;
|
|
12
|
+
let paddingLeft = true;
|
|
13
|
+
let paddingRight = false;
|
|
14
|
+
const edits: languages.TextEdit[] = [];
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check
|
|
16
|
+
switch (tag.code) {
|
|
17
|
+
case DiagnosticCode.ParameterIt: {
|
|
18
|
+
if (!tag.references.length) {
|
|
19
|
+
// 没有引用,隐藏该隐式参数
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
lineNumber = tag.range.endLineNumber;
|
|
23
|
+
column = tag.range.endColumn;
|
|
24
|
+
label = '(it)';
|
|
25
|
+
paddingLeft = /\bfn$/u.test(model.getValueInRange(new Range(lineNumber, column - 3, lineNumber, column)));
|
|
26
|
+
paddingRight = model.getValueInRange(new Range(lineNumber, column, lineNumber, column + 1)) === '{';
|
|
27
|
+
edits.push({
|
|
28
|
+
range: new Range(lineNumber, column, lineNumber, column),
|
|
29
|
+
text: `${paddingLeft ? ' ' : ''}${label}${paddingRight ? ' ' : ''}`,
|
|
30
|
+
});
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
case DiagnosticCode.UnnamedRecordField0:
|
|
34
|
+
case DiagnosticCode.UnnamedRecordField1:
|
|
35
|
+
case DiagnosticCode.UnnamedRecordField2:
|
|
36
|
+
case DiagnosticCode.UnnamedRecordField3:
|
|
37
|
+
case DiagnosticCode.UnnamedRecordField4:
|
|
38
|
+
case DiagnosticCode.UnnamedRecordField5:
|
|
39
|
+
case DiagnosticCode.UnnamedRecordField6:
|
|
40
|
+
case DiagnosticCode.UnnamedRecordField7:
|
|
41
|
+
case DiagnosticCode.UnnamedRecordField8:
|
|
42
|
+
case DiagnosticCode.UnnamedRecordField9:
|
|
43
|
+
case DiagnosticCode.UnnamedRecordFieldN: {
|
|
44
|
+
const index = tag.code - DiagnosticCode.UnnamedRecordField0;
|
|
45
|
+
if (index > 9) break;
|
|
46
|
+
lineNumber = tag.range.startLineNumber;
|
|
47
|
+
column = tag.range.startColumn;
|
|
48
|
+
label = `${index}:`;
|
|
49
|
+
paddingLeft = /[^\s(]/u.test(model.getValueInRange(new Range(lineNumber, column - 1, lineNumber, column)));
|
|
50
|
+
paddingRight = true;
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
case DiagnosticCode.OmitNamedRecordField: {
|
|
54
|
+
const ref = tag.references[0];
|
|
55
|
+
if (ref?.code !== DiagnosticCode.OmitNamedRecordFieldName) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
lineNumber = tag.range.startLineNumber;
|
|
59
|
+
column = tag.range.startColumn;
|
|
60
|
+
label = model.getValueInRange(ref.range);
|
|
61
|
+
paddingLeft = /[^\s(]/u.test(model.getValueInRange(new Range(lineNumber, column - 1, lineNumber, column)));
|
|
62
|
+
paddingRight = false;
|
|
63
|
+
|
|
64
|
+
const insertRight = !/\s/u.test(
|
|
65
|
+
model.getValueInRange(
|
|
66
|
+
new Range(
|
|
67
|
+
tag.range.endLineNumber,
|
|
68
|
+
tag.range.endColumn,
|
|
69
|
+
tag.range.endLineNumber,
|
|
70
|
+
tag.range.endColumn + 1,
|
|
71
|
+
),
|
|
72
|
+
),
|
|
73
|
+
);
|
|
74
|
+
edits.push({
|
|
75
|
+
range: tag.range,
|
|
76
|
+
text: `${paddingLeft ? ' ' : ''}${label}${model.getValueInRange(tag.range)}${insertRight ? ' ' : ''}`,
|
|
77
|
+
});
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (!label) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
position: { lineNumber, column },
|
|
86
|
+
label,
|
|
87
|
+
kind,
|
|
88
|
+
paddingLeft,
|
|
89
|
+
paddingRight,
|
|
90
|
+
textEdits: edits,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
5
94
|
/** @inheritdoc */
|
|
6
95
|
export class InlayHintsProvider extends Provider implements languages.InlayHintsProvider {
|
|
7
96
|
/** @inheritdoc */
|
|
@@ -23,99 +112,14 @@ export class InlayHintsProvider extends Provider implements languages.InlayHints
|
|
|
23
112
|
if (!range.containsRange(tag.range)) {
|
|
24
113
|
continue;
|
|
25
114
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const kind = languages.InlayHintKind.Parameter;
|
|
30
|
-
let paddingLeft = true;
|
|
31
|
-
let paddingRight = false;
|
|
32
|
-
const edits: languages.TextEdit[] = [];
|
|
33
|
-
// eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check
|
|
34
|
-
switch (tag.code) {
|
|
35
|
-
case DiagnosticCode.ParameterIt: {
|
|
36
|
-
if (!tag.references.length) {
|
|
37
|
-
// 没有引用,隐藏该隐式参数
|
|
38
|
-
continue;
|
|
39
|
-
}
|
|
40
|
-
lineNumber = tag.range.endLineNumber;
|
|
41
|
-
column = tag.range.endColumn;
|
|
42
|
-
label = '(it)';
|
|
43
|
-
paddingLeft = /\bfn$/u.test(
|
|
44
|
-
model.getValueInRange(new Range(lineNumber, column - 3, lineNumber, column)),
|
|
45
|
-
);
|
|
46
|
-
paddingRight = model.getValueInRange(new Range(lineNumber, column, lineNumber, column + 1)) === '{';
|
|
47
|
-
edits.push({
|
|
48
|
-
range: new Range(lineNumber, column, lineNumber, column),
|
|
49
|
-
text: `${paddingLeft ? ' ' : ''}${label}${paddingRight ? ' ' : ''}`,
|
|
50
|
-
});
|
|
51
|
-
break;
|
|
52
|
-
}
|
|
53
|
-
case DiagnosticCode.UnnamedRecordField0:
|
|
54
|
-
case DiagnosticCode.UnnamedRecordField1:
|
|
55
|
-
case DiagnosticCode.UnnamedRecordField2:
|
|
56
|
-
case DiagnosticCode.UnnamedRecordField3:
|
|
57
|
-
case DiagnosticCode.UnnamedRecordField4:
|
|
58
|
-
case DiagnosticCode.UnnamedRecordField5:
|
|
59
|
-
case DiagnosticCode.UnnamedRecordField6:
|
|
60
|
-
case DiagnosticCode.UnnamedRecordField7:
|
|
61
|
-
case DiagnosticCode.UnnamedRecordField8:
|
|
62
|
-
case DiagnosticCode.UnnamedRecordField9:
|
|
63
|
-
case DiagnosticCode.UnnamedRecordFieldN: {
|
|
64
|
-
const index = tag.code - DiagnosticCode.UnnamedRecordField0;
|
|
65
|
-
if (index > 9) break;
|
|
66
|
-
lineNumber = tag.range.startLineNumber;
|
|
67
|
-
column = tag.range.startColumn;
|
|
68
|
-
label = `${index}:`;
|
|
69
|
-
paddingLeft = /[^\s(]/u.test(
|
|
70
|
-
model.getValueInRange(new Range(lineNumber, column - 1, lineNumber, column)),
|
|
71
|
-
);
|
|
72
|
-
paddingRight = true;
|
|
73
|
-
break;
|
|
74
|
-
}
|
|
75
|
-
case DiagnosticCode.OmitNamedRecordField: {
|
|
76
|
-
const ref = tag.references[0];
|
|
77
|
-
if (ref?.code !== DiagnosticCode.OmitNamedRecordFieldName) continue;
|
|
78
|
-
lineNumber = tag.range.startLineNumber;
|
|
79
|
-
column = tag.range.startColumn;
|
|
80
|
-
label = model.getValueInRange(ref.range);
|
|
81
|
-
paddingLeft = /[^\s(]/u.test(
|
|
82
|
-
model.getValueInRange(new Range(lineNumber, column - 1, lineNumber, column)),
|
|
83
|
-
);
|
|
84
|
-
paddingRight = false;
|
|
85
|
-
|
|
86
|
-
const insertRight = !/\s/u.test(
|
|
87
|
-
model.getValueInRange(
|
|
88
|
-
new Range(
|
|
89
|
-
tag.range.endLineNumber,
|
|
90
|
-
tag.range.endColumn,
|
|
91
|
-
tag.range.endLineNumber,
|
|
92
|
-
tag.range.endColumn + 1,
|
|
93
|
-
),
|
|
94
|
-
),
|
|
95
|
-
);
|
|
96
|
-
edits.push({
|
|
97
|
-
range: tag.range,
|
|
98
|
-
text: `${paddingLeft ? ' ' : ''}${label}${model.getValueInRange(tag.range)}${insertRight ? ' ' : ''}`,
|
|
99
|
-
});
|
|
100
|
-
break;
|
|
101
|
-
}
|
|
115
|
+
const hint = provideInlayHint(tag, model);
|
|
116
|
+
if (hint) {
|
|
117
|
+
hints.push(hint);
|
|
102
118
|
}
|
|
103
|
-
if (!label) continue;
|
|
104
|
-
const hint: languages.InlayHint = {
|
|
105
|
-
position: { lineNumber, column },
|
|
106
|
-
label,
|
|
107
|
-
kind,
|
|
108
|
-
paddingLeft,
|
|
109
|
-
paddingRight,
|
|
110
|
-
textEdits: edits,
|
|
111
|
-
};
|
|
112
|
-
hints.push(hint);
|
|
113
119
|
}
|
|
114
120
|
return {
|
|
115
121
|
hints,
|
|
116
|
-
dispose: () =>
|
|
117
|
-
// Cleanup if necessary
|
|
118
|
-
},
|
|
122
|
+
dispose: () => undefined,
|
|
119
123
|
};
|
|
120
124
|
}
|
|
121
125
|
}
|
package/src/lsp/utils.ts
CHANGED
|
@@ -400,10 +400,8 @@ export function valueDoc(
|
|
|
400
400
|
prefix = `let ${name} = `;
|
|
401
401
|
}
|
|
402
402
|
suffix = ';';
|
|
403
|
-
} else if (/^\d/.test(name)) {
|
|
404
|
-
prefix = `[${name}]: `;
|
|
405
403
|
} else {
|
|
406
|
-
prefix = `${name}: `;
|
|
404
|
+
prefix = `${serializeRecordKey(name)}: `;
|
|
407
405
|
}
|
|
408
406
|
if (isVmModule(value)) {
|
|
409
407
|
let script;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { InputMode } from '@mirascript/mirascript';
|
|
2
|
+
import type { BcModule } from '@mirascript/bindings/wasm';
|
|
3
|
+
import type { Tagged } from 'type-fest';
|
|
4
|
+
|
|
5
|
+
const { loadModule } = await import('@mirascript/bindings/wasm');
|
|
6
|
+
const mod = await loadModule();
|
|
7
|
+
const { wasm, createConfig } = mod;
|
|
8
|
+
|
|
9
|
+
/** Monaco 编译结果 */
|
|
10
|
+
export interface MonacoResult extends BcModule.CompileResult {
|
|
11
|
+
/** 格式化结果 */
|
|
12
|
+
formatted?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** 缓存 Key (model id) */
|
|
16
|
+
export type CacheKey = Tagged<string, 'modelId'>;
|
|
17
|
+
/** 请求参数 */
|
|
18
|
+
export type Req = [key: CacheKey, version: number, script: string, mode: InputMode];
|
|
19
|
+
/** 编译结果 */
|
|
20
|
+
export type ResOk = [key: CacheKey, version: number, result: MonacoResult];
|
|
21
|
+
/** 编译结果 */
|
|
22
|
+
export type ResErr = [key: CacheKey, version: number, error: Error];
|
|
23
|
+
/** 编译结果 */
|
|
24
|
+
export type Res = ResOk | ResErr;
|
|
25
|
+
/** Ready */
|
|
26
|
+
export type Ready = 'mirascript lsp ready';
|
|
27
|
+
|
|
28
|
+
const configTemplate = createConfig({
|
|
29
|
+
diagnostic_position_encoding: 'Utf16',
|
|
30
|
+
diagnostic_tag: true,
|
|
31
|
+
diagnostic_sourcemap: true,
|
|
32
|
+
trivia: true,
|
|
33
|
+
input_mode: 'Template',
|
|
34
|
+
});
|
|
35
|
+
const configScript = createConfig({
|
|
36
|
+
diagnostic_position_encoding: 'Utf16',
|
|
37
|
+
diagnostic_tag: true,
|
|
38
|
+
diagnostic_sourcemap: true,
|
|
39
|
+
trivia: true,
|
|
40
|
+
input_mode: 'Script',
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
/** 编译 */
|
|
44
|
+
export function compile(script: string, mode: InputMode): MonacoResult {
|
|
45
|
+
const config = mode === 'Script' ? configScript : configTemplate;
|
|
46
|
+
const compiler = new wasm.MonacoCompiler(script, config);
|
|
47
|
+
try {
|
|
48
|
+
const parseOk = compiler.parse();
|
|
49
|
+
if (!parseOk) {
|
|
50
|
+
return { diagnostics: compiler.diagnostics(), chunk: undefined };
|
|
51
|
+
}
|
|
52
|
+
const chunk = compiler.emit();
|
|
53
|
+
const formatted = compiler.format();
|
|
54
|
+
return {
|
|
55
|
+
diagnostics: compiler.diagnostics(),
|
|
56
|
+
chunk,
|
|
57
|
+
formatted,
|
|
58
|
+
};
|
|
59
|
+
} finally {
|
|
60
|
+
try {
|
|
61
|
+
compiler.free();
|
|
62
|
+
} catch (ex) {
|
|
63
|
+
/* 忽略错误 */
|
|
64
|
+
// eslint-disable-next-line no-console
|
|
65
|
+
console.error(ex);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
package/src/lsp/worker-helper.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { InputMode } from '@mirascript/constants';
|
|
2
2
|
import { editor } from '../monaco-api.js';
|
|
3
|
-
import type { Ready, CacheKey, Req, Res, ResOk } from './worker.js';
|
|
3
|
+
import type { Ready, CacheKey, Req, Res, ResOk } from './worker-core.js';
|
|
4
4
|
import { CompileResult } from './compile-result.js';
|
|
5
5
|
import { makeModelMarkers } from './diagnostics.js';
|
|
6
6
|
|
|
@@ -89,12 +89,12 @@ async function compileWorker(req: Req): Promise<CompileResult> {
|
|
|
89
89
|
return new CompileResult(key, version, source, result);
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
let compileImpl: typeof import('./worker.js').compile;
|
|
92
|
+
let compileImpl: typeof import('./worker-core.js').compile;
|
|
93
93
|
/** 使用当前线程编译 */
|
|
94
94
|
async function compileSync(req: Req): Promise<CompileResult> {
|
|
95
95
|
const [key, version, script, mode] = req;
|
|
96
96
|
if (compileImpl == null) {
|
|
97
|
-
const mod = await import('./worker.js');
|
|
97
|
+
const mod = await import('./worker-core.js');
|
|
98
98
|
compileImpl = mod.compile;
|
|
99
99
|
}
|
|
100
100
|
const result = compileImpl(script, mode);
|