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/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
+ }
@@ -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";