codemirror-lang-msil 0.0.1-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +37 -0
- package/dist/index.cjs +2153 -0
- package/dist/index.d.cts +25 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +2147 -0
- package/package.json +63 -0
- package/src/complete.ts +340 -0
- package/src/index.ts +70 -0
- package/src/instructions.ts +931 -0
- package/src/keywords.ts +755 -0
- package/src/syntax.grammar +1594 -0
- package/src/syntax.grammar.d.ts +3 -0
- package/src/tokens.ts +60 -0
- package/src/tooltip.ts +49 -0
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "codemirror-lang-msil",
|
|
3
|
+
"version": "0.0.1-beta.1",
|
|
4
|
+
"description": "A IL mode for CodeMirror 6",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.cjs",
|
|
7
|
+
"module": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"require": "./dist/index.cjs"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"dev": "npm run build && run-p dev-cm dev-vite",
|
|
15
|
+
"dev-vite": "vite ./dev",
|
|
16
|
+
"dev-cm": "node ./dev/watch.cjs src/index.ts",
|
|
17
|
+
"build": "cm-buildhelper src/index.ts",
|
|
18
|
+
"build-vite": "npm run build && vite build ./dev",
|
|
19
|
+
"test": "cm-runtests",
|
|
20
|
+
"publish": "npm run build && npm publish"
|
|
21
|
+
},
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"author": "wherewhere7",
|
|
24
|
+
"sideEffects": false,
|
|
25
|
+
"funding": [
|
|
26
|
+
{
|
|
27
|
+
"type": "patreon",
|
|
28
|
+
"url": "https://www.patreon.com/wherewhere"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"type": "爱发电",
|
|
32
|
+
"url": "https://ifdian.net/@wherewhere"
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "git+https://github.com/wherewhere/codemirror-lang-msil.git"
|
|
38
|
+
},
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"@codemirror/autocomplete": "^6.0.0",
|
|
41
|
+
"@codemirror/language": "^6.0.0",
|
|
42
|
+
"@codemirror/state": "^6.0.0",
|
|
43
|
+
"@codemirror/view": "^6.0.0",
|
|
44
|
+
"@lezer/common": "^1.0.0",
|
|
45
|
+
"@lezer/highlight": "^1.0.0",
|
|
46
|
+
"@lezer/lr": "^1.0.0"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@codemirror/autocomplete": "^6.0.0",
|
|
50
|
+
"@codemirror/buildhelper": "^1.0.0",
|
|
51
|
+
"@codemirror/language": "^6.0.0",
|
|
52
|
+
"@codemirror/state": "^6.0.0",
|
|
53
|
+
"@codemirror/theme-one-dark": "^6.0.0",
|
|
54
|
+
"@codemirror/view": "^6.0.0",
|
|
55
|
+
"@lezer/generator": "^1.0.0",
|
|
56
|
+
"@lezer/common": "^1.0.0",
|
|
57
|
+
"@lezer/highlight": "^1.0.0",
|
|
58
|
+
"@lezer/lr": "^1.0.0",
|
|
59
|
+
"codemirror": "^6.0.0",
|
|
60
|
+
"npm-run-all": "^4.0.0",
|
|
61
|
+
"vite": "^8.0.0"
|
|
62
|
+
}
|
|
63
|
+
}
|
package/src/complete.ts
ADDED
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
import type { Completion, CompletionContext } from "@codemirror/autocomplete";
|
|
2
|
+
import type { SyntaxNode } from "@lezer/common";
|
|
3
|
+
import {
|
|
4
|
+
memberDecl,
|
|
5
|
+
classMemberDecl,
|
|
6
|
+
eventMemberDecl,
|
|
7
|
+
propMemberDecl,
|
|
8
|
+
methodMemberDecl,
|
|
9
|
+
assemblyMemberDecl,
|
|
10
|
+
assemblyRefMemberDecl,
|
|
11
|
+
exptypeMemberDecl,
|
|
12
|
+
manifestResMemberDecl
|
|
13
|
+
} from "./keywords";
|
|
14
|
+
|
|
15
|
+
function findDescendant(node: SyntaxNode, nodes: readonly string[]) {
|
|
16
|
+
for (let pos: SyntaxNode | null = node; pos; pos = pos.parent) {
|
|
17
|
+
if (nodes.indexOf(pos.name) > -1) { return pos; }
|
|
18
|
+
if (pos.type.isTop) { break; }
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function findPrevSibling(node: SyntaxNode, nodes: readonly string[]) {
|
|
23
|
+
for (let pos: SyntaxNode | null = node; pos; pos = pos.prevSibling) {
|
|
24
|
+
if (nodes.indexOf(pos.name) > -1) { return pos; }
|
|
25
|
+
if (pos.type.isTop) { break; }
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function isAtRoot(node: SyntaxNode, nodes: readonly string[]) {
|
|
30
|
+
if (nodes.indexOf(node.name) > -1) { return node; }
|
|
31
|
+
let parent = node.parent;
|
|
32
|
+
if (parent) {
|
|
33
|
+
if (parent.name === '⚠') {
|
|
34
|
+
parent = parent.parent;
|
|
35
|
+
if (parent && nodes.indexOf(parent.name) > -1) {
|
|
36
|
+
return parent;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
else if (nodes.indexOf(parent.name) > -1) {
|
|
40
|
+
return parent;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function memberBody(from: number) {
|
|
47
|
+
return {
|
|
48
|
+
from,
|
|
49
|
+
options: memberDecl
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function classBody(from: number) {
|
|
54
|
+
return {
|
|
55
|
+
from,
|
|
56
|
+
options: classMemberDecl
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function eventBody(from: number) {
|
|
61
|
+
return {
|
|
62
|
+
from,
|
|
63
|
+
options: eventMemberDecl
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function propBody(from: number) {
|
|
68
|
+
return {
|
|
69
|
+
from,
|
|
70
|
+
options: propMemberDecl
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function methodBody(from: number) {
|
|
75
|
+
return {
|
|
76
|
+
from,
|
|
77
|
+
options: methodMemberDecl
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function assemblyBody(from: number) {
|
|
82
|
+
return {
|
|
83
|
+
from,
|
|
84
|
+
options: assemblyMemberDecl
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function assemblyRefBody(from: number) {
|
|
89
|
+
return {
|
|
90
|
+
from,
|
|
91
|
+
options: assemblyRefMemberDecl
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function exptypeBody(from: number) {
|
|
96
|
+
return {
|
|
97
|
+
from,
|
|
98
|
+
options: exptypeMemberDecl
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function manifestResBody(from: number) {
|
|
103
|
+
return {
|
|
104
|
+
from,
|
|
105
|
+
options: manifestResMemberDecl
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
import {
|
|
110
|
+
classAttr,
|
|
111
|
+
classNestAttr,
|
|
112
|
+
classExtendsDecl,
|
|
113
|
+
fieldAttr,
|
|
114
|
+
eventAttr,
|
|
115
|
+
propAttr,
|
|
116
|
+
methodAttr,
|
|
117
|
+
callConv,
|
|
118
|
+
paramAttr
|
|
119
|
+
} from "./keywords";
|
|
120
|
+
|
|
121
|
+
function classAttrBody(node: SyntaxNode, context: CompletionContext) {
|
|
122
|
+
if (node.parent?.name === '⚠') {
|
|
123
|
+
node = node.parent;
|
|
124
|
+
}
|
|
125
|
+
let prevSibling = node.prevSibling;
|
|
126
|
+
if (prevSibling) {
|
|
127
|
+
if (prevSibling.name === '⚠') {
|
|
128
|
+
prevSibling = prevSibling.prevSibling;
|
|
129
|
+
}
|
|
130
|
+
if (prevSibling) {
|
|
131
|
+
const code = context.state.sliceDoc(prevSibling.from, prevSibling.to);
|
|
132
|
+
if (code === "nested") {
|
|
133
|
+
return {
|
|
134
|
+
from: node.from,
|
|
135
|
+
options: classNestAttr
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
if (findPrevSibling(prevSibling, ["ClassName"])) {
|
|
139
|
+
return {
|
|
140
|
+
from: node.from,
|
|
141
|
+
options: classExtendsDecl
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
else if (node.name === '⚠') {
|
|
147
|
+
if (node.parent?.name === "ClassName") {
|
|
148
|
+
return {
|
|
149
|
+
from: node.from,
|
|
150
|
+
options: classExtendsDecl
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
from: node.from,
|
|
156
|
+
options: classAttr
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function fieldAttrBody({ from }: Pick<SyntaxNode, "from">) {
|
|
161
|
+
return {
|
|
162
|
+
from,
|
|
163
|
+
options: fieldAttr
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function eventAttrBody({ from }: Pick<SyntaxNode, "from">) {
|
|
168
|
+
return {
|
|
169
|
+
from,
|
|
170
|
+
options: eventAttr
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function propAttrBody(node: SyntaxNode) {
|
|
175
|
+
switch (node.prevSibling?.name) {
|
|
176
|
+
case "CallingConvention":
|
|
177
|
+
return {
|
|
178
|
+
from: node.from,
|
|
179
|
+
options: callConv
|
|
180
|
+
};
|
|
181
|
+
default:
|
|
182
|
+
return {
|
|
183
|
+
from: node.from,
|
|
184
|
+
options: [...propAttr, ...callConv]
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function methodAttrBody(node: SyntaxNode) {
|
|
190
|
+
switch (node.prevSibling?.name) {
|
|
191
|
+
case "CallingConvention":
|
|
192
|
+
return {
|
|
193
|
+
from: node.from,
|
|
194
|
+
options: callConv
|
|
195
|
+
};
|
|
196
|
+
default:
|
|
197
|
+
return {
|
|
198
|
+
from: node.from,
|
|
199
|
+
options: [...methodAttr, ...callConv]
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function paramAttrBody({ from }: Pick<SyntaxNode, "from">) {
|
|
205
|
+
return {
|
|
206
|
+
from,
|
|
207
|
+
options: paramAttr
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
import { opcodes } from "./instructions";
|
|
212
|
+
|
|
213
|
+
function methodScopeBlock(node: SyntaxNode, context: CompletionContext) {
|
|
214
|
+
function getCode() {
|
|
215
|
+
if (node.parent?.name === "OpCode") {
|
|
216
|
+
return context.state.sliceDoc(node.parent.from, node.parent.to).trimEnd();
|
|
217
|
+
}
|
|
218
|
+
else if (node.prevSibling?.name === "Instrction") {
|
|
219
|
+
const prevSibling = node.prevSibling;
|
|
220
|
+
if (prevSibling.firstChild?.name === "OpCode") {
|
|
221
|
+
const firstChild = prevSibling.firstChild;
|
|
222
|
+
if (firstChild.nextSibling?.name === '⚠') {
|
|
223
|
+
const code = context.state.sliceDoc(firstChild.from, firstChild.to);
|
|
224
|
+
if (!code.match(/\s/)) {
|
|
225
|
+
return code.trimEnd() + context.state.sliceDoc(node.from, node.to);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
else if (firstChild.lastChild?.name !== '⚠') {
|
|
229
|
+
return context.state.sliceDoc(node.from, node.to);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
return context.state.sliceDoc(node.from, node.to);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
const code = getCode();
|
|
238
|
+
if (!code?.match(/^\w/)) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
const parts = code.split('.');
|
|
242
|
+
type opcode = Omit<Record<string, object | undefined>, "desc"> & { desc?: string };
|
|
243
|
+
let opcode = opcodes as opcode;
|
|
244
|
+
for (let i = 0; i < parts.length; i++) {
|
|
245
|
+
const part = parts[i];
|
|
246
|
+
if (part in opcode) {
|
|
247
|
+
opcode = opcode[part] as opcode;
|
|
248
|
+
}
|
|
249
|
+
else if (i !== parts.length - 1) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
const result: Completion[] = [];
|
|
254
|
+
for (const key in opcode) {
|
|
255
|
+
if (key === "desc") {
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
const { desc } = opcode[key] as { desc?: string };
|
|
259
|
+
result.push({
|
|
260
|
+
label: key,
|
|
261
|
+
type: key.match(/^[mM]?[0-8]$/) ? "constant" : "keyword",
|
|
262
|
+
info: desc
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
if (result.length) {
|
|
266
|
+
return {
|
|
267
|
+
from: node.name === '.' ? node.to : node.from,
|
|
268
|
+
options: result
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
import { syntaxTree } from "@codemirror/language";
|
|
274
|
+
|
|
275
|
+
export function autocomplete(context: CompletionContext) {
|
|
276
|
+
const node = syntaxTree(context.state).resolveInner(context.pos, -1);
|
|
277
|
+
if (node.parent?.name === "OpCode" || node.prevSibling?.name === "Instrction") {
|
|
278
|
+
const result = methodScopeBlock(node, context);
|
|
279
|
+
if (result) {
|
|
280
|
+
return result;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
const code = context.state.sliceDoc(node.from, node.to);
|
|
284
|
+
if (code.startsWith('.') || code.startsWith('#')) {
|
|
285
|
+
let delim = isAtRoot(node, ["Delim"]);
|
|
286
|
+
if (delim) {
|
|
287
|
+
switch (delim.parent?.name) {
|
|
288
|
+
case "Namespace":
|
|
289
|
+
return memberBody(node.from);
|
|
290
|
+
case "Class":
|
|
291
|
+
return classBody(node.from);
|
|
292
|
+
case "Event":
|
|
293
|
+
return eventBody(node.from);
|
|
294
|
+
case "Property":
|
|
295
|
+
return propBody(node.from);
|
|
296
|
+
case "Method":
|
|
297
|
+
case "MethodScopeBlock":
|
|
298
|
+
return methodBody(node.from);
|
|
299
|
+
case "Assembly":
|
|
300
|
+
return assemblyBody(node.from);
|
|
301
|
+
case "AssemblyReference":
|
|
302
|
+
return assemblyRefBody(node.from);
|
|
303
|
+
case "ExportType":
|
|
304
|
+
return exptypeBody(node.from);
|
|
305
|
+
case "ManifestResource":
|
|
306
|
+
return manifestResBody(node.from);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
else if (isAtRoot(node, ["CompilationUnit"])) {
|
|
310
|
+
return memberBody(node.from);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
else if (code.match(/^\w/)) {
|
|
314
|
+
if (isAtRoot(node, ["Class", "ClassName"])) {
|
|
315
|
+
return classAttrBody(node, context);
|
|
316
|
+
}
|
|
317
|
+
let parent = node.parent;
|
|
318
|
+
switch (parent?.name) {
|
|
319
|
+
case "Field":
|
|
320
|
+
return fieldAttrBody(node);
|
|
321
|
+
case "Event":
|
|
322
|
+
return eventAttrBody(node);
|
|
323
|
+
case "Property":
|
|
324
|
+
return propAttrBody(node);
|
|
325
|
+
case "Method":
|
|
326
|
+
return methodAttrBody(node);
|
|
327
|
+
case "Delim":
|
|
328
|
+
parent = parent.parent;
|
|
329
|
+
if (parent) {
|
|
330
|
+
if (parent.name === "Method" || parent.name === "MethodScopeBlock") {
|
|
331
|
+
return methodScopeBlock(node, context);
|
|
332
|
+
}
|
|
333
|
+
else if (parent.name === "ParameterAttribute") {
|
|
334
|
+
return paramAttrBody(node);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
break;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { parser } from "./syntax.grammar";
|
|
2
|
+
export { parser };
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
LRLanguage,
|
|
6
|
+
LanguageSupport,
|
|
7
|
+
indentNodeProp,
|
|
8
|
+
foldNodeProp,
|
|
9
|
+
foldInside,
|
|
10
|
+
continuedIndent,
|
|
11
|
+
} from "@codemirror/language";
|
|
12
|
+
import { styleTags, tags } from "@lezer/highlight";
|
|
13
|
+
import { autocomplete } from "./complete";
|
|
14
|
+
|
|
15
|
+
export const msilLanguage = LRLanguage.define({
|
|
16
|
+
parser: parser.configure({
|
|
17
|
+
props: [
|
|
18
|
+
indentNodeProp.add({
|
|
19
|
+
Delim: continuedIndent()
|
|
20
|
+
}),
|
|
21
|
+
foldNodeProp.add({
|
|
22
|
+
Delim: foldInside
|
|
23
|
+
}),
|
|
24
|
+
styleTags({
|
|
25
|
+
"Keyword SimpleType OpCode": tags.keyword,
|
|
26
|
+
BooleanLiteral: tags.bool,
|
|
27
|
+
NullLiteral: tags.null,
|
|
28
|
+
"IntegerLiteral ByteLiteral": tags.integer,
|
|
29
|
+
RealLiteral: tags.float,
|
|
30
|
+
'QSTRING SQSTRING': tags.string,
|
|
31
|
+
LineComment: tags.lineComment,
|
|
32
|
+
BlockComment: tags.blockComment,
|
|
33
|
+
|
|
34
|
+
". : Astrisk Slash + - Not & | =": tags.operator,
|
|
35
|
+
|
|
36
|
+
PP_Directive: tags.definitionKeyword,
|
|
37
|
+
|
|
38
|
+
"Identifier TypeIdentifier": tags.typeName,
|
|
39
|
+
AssemblyName: [tags.strong, tags.name],
|
|
40
|
+
ModuleName: tags.name,
|
|
41
|
+
NamespaceName: tags.namespace,
|
|
42
|
+
ClassName: tags.className,
|
|
43
|
+
"ArgumentName FieldName": tags.variableName,
|
|
44
|
+
ConstName: tags.constant(tags.variableName),
|
|
45
|
+
|
|
46
|
+
MethodName: tags.function(tags.variableName),
|
|
47
|
+
ParamName: [tags.emphasis, tags.variableName],
|
|
48
|
+
"PropertyName EventName": tags.propertyName,
|
|
49
|
+
LabelName: tags.labelName,
|
|
50
|
+
|
|
51
|
+
"( )": tags.paren,
|
|
52
|
+
"{ }": tags.brace,
|
|
53
|
+
"[ ]": tags.squareBracket,
|
|
54
|
+
"< >": tags.angleBracket,
|
|
55
|
+
})
|
|
56
|
+
]
|
|
57
|
+
}),
|
|
58
|
+
languageData: {
|
|
59
|
+
commentTokens: { line: "//", block: { open: "/*", close: "*/" } },
|
|
60
|
+
closeBrackets: { brackets: ['(', '[', '{', '"', '\'', '<'] },
|
|
61
|
+
indentOnInput: /^\s*((\)|\]|\})$|(catch|finally)\b)/,
|
|
62
|
+
autocomplete: autocomplete
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
export function msil() {
|
|
67
|
+
return new LanguageSupport(msilLanguage);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export * from "./tooltip";
|