@razdolbai/merls 1.2.2 → 1.3.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/AGENTS.md +64 -64
- package/dist/src/asm/parser.js +2 -1
- package/dist/src/lsp/completion.js +14 -11
- package/dist/test/completion.test.js +1 -1
- package/dist/test/definition-references.test.js +3 -3
- package/dist/test/diagnostics.test.js +2 -2
- package/dist/test/document-symbol.test.js +1 -1
- package/dist/test/fixture-corpus.test.js +4 -4
- package/dist/test/hover.test.js +1 -1
- package/dist/test/lexer.test.js +4 -4
- package/dist/test/local-labels.test.js +15 -15
- package/dist/test/publish-diagnostics.test.js +1 -1
- package/dist/test/semantic-tokens.test.js +1 -1
- package/dist/test/symbols.test.js +9 -4
- package/dist/test/workspace-symbol.test.js +2 -2
- package/dist/test/workspace.test.js +2 -2
- package/package.json +1 -1
- package/src/asm/parser.ts +4 -1
- package/src/lsp/completion.ts +15 -11
- package/test/completion.test.ts +151 -151
- package/test/definition-references.test.ts +152 -152
- package/test/diagnostics.test.ts +129 -129
- package/test/document-symbol.test.ts +131 -131
- package/test/fixture-corpus.test.ts +33 -33
- package/test/fixtures/valid/{merlin32-linkscript.asm → merlin32-linkscript.S} +16 -16
- package/test/fixtures/valid/{merlin32-main-6502.asm → merlin32-main-6502.S} +1 -0
- package/test/hover.test.ts +175 -175
- package/test/lexer.test.ts +87 -87
- package/test/local-labels.test.ts +47 -47
- package/test/publish-diagnostics.test.ts +206 -206
- package/test/semantic-tokens.test.ts +128 -128
- package/test/smoke/run-smoke.ps1 +177 -177
- package/test/symbols.test.ts +47 -41
- package/test/workspace-symbol.test.ts +139 -139
- package/test/workspace.test.ts +29 -29
- /package/test/fixtures/invalid/{65816-bank-ops.asm → 65816-bank-ops.S} +0 -0
- /package/test/fixtures/invalid/{65816-long-addressing.asm → 65816-long-addressing.S} +0 -0
- /package/test/fixtures/valid/{smoke-test.asm → smoke-test.S} +0 -0
package/test/diagnostics.test.ts
CHANGED
|
@@ -1,129 +1,129 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import fs from "node:fs";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
|
|
5
|
-
import { parseDocument } from "../src/asm/document";
|
|
6
|
-
import {
|
|
7
|
-
collectWorkspaceDiagnostics,
|
|
8
|
-
type Diagnostic
|
|
9
|
-
} from "../src/asm/diagnostics";
|
|
10
|
-
|
|
11
|
-
export function runDiagnosticsTest(): void {
|
|
12
|
-
const duplicateSource = [
|
|
13
|
-
"dup equ 1",
|
|
14
|
-
" lda missing",
|
|
15
|
-
"dup equ 2",
|
|
16
|
-
" adc (",
|
|
17
|
-
" dsk ../build/WORLD",
|
|
18
|
-
" typ BIN",
|
|
19
|
-
" typ BLAH",
|
|
20
|
-
" end BLAH"
|
|
21
|
-
].join("\n");
|
|
22
|
-
|
|
23
|
-
const bankOpsPath = path.resolve(
|
|
24
|
-
process.cwd(),
|
|
25
|
-
"test/fixtures/invalid/65816-bank-ops.
|
|
26
|
-
);
|
|
27
|
-
const longPath = path.resolve(
|
|
28
|
-
process.cwd(),
|
|
29
|
-
"test/fixtures/invalid/65816-long-addressing.
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
const diagnostics = collectWorkspaceDiagnostics([
|
|
33
|
-
{
|
|
34
|
-
filePath: "<memory>",
|
|
35
|
-
document: parseDocument(duplicateSource)
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
filePath: bankOpsPath,
|
|
39
|
-
document: parseDocument(fs.readFileSync(bankOpsPath, "utf8"))
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
filePath: longPath,
|
|
43
|
-
document: parseDocument(fs.readFileSync(longPath, "utf8"))
|
|
44
|
-
}
|
|
45
|
-
]);
|
|
46
|
-
|
|
47
|
-
assert.equal(
|
|
48
|
-
diagnostics.some(
|
|
49
|
-
(diagnostic: Diagnostic) =>
|
|
50
|
-
diagnostic.filePath === "<memory>" &&
|
|
51
|
-
diagnostic.code === "duplicate-symbol" &&
|
|
52
|
-
diagnostic.line === 2
|
|
53
|
-
),
|
|
54
|
-
true
|
|
55
|
-
);
|
|
56
|
-
|
|
57
|
-
assert.equal(
|
|
58
|
-
diagnostics.some(
|
|
59
|
-
(diagnostic: Diagnostic) =>
|
|
60
|
-
diagnostic.filePath === "<memory>" &&
|
|
61
|
-
diagnostic.code === "unresolved-reference" &&
|
|
62
|
-
diagnostic.line === 1 &&
|
|
63
|
-
diagnostic.message.includes("missing")
|
|
64
|
-
),
|
|
65
|
-
true
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
assert.equal(
|
|
69
|
-
diagnostics.some(
|
|
70
|
-
(diagnostic: Diagnostic) =>
|
|
71
|
-
diagnostic.filePath === "<memory>" &&
|
|
72
|
-
diagnostic.code === "unresolved-reference" &&
|
|
73
|
-
(diagnostic.line === 4 || diagnostic.line === 5)
|
|
74
|
-
),
|
|
75
|
-
false
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
assert.equal(
|
|
79
|
-
diagnostics.some(
|
|
80
|
-
(diagnostic: Diagnostic) =>
|
|
81
|
-
diagnostic.filePath === "<memory>" &&
|
|
82
|
-
diagnostic.code === "unresolved-reference" &&
|
|
83
|
-
diagnostic.line === 6 &&
|
|
84
|
-
diagnostic.message.includes("BLAH")
|
|
85
|
-
),
|
|
86
|
-
true
|
|
87
|
-
);
|
|
88
|
-
|
|
89
|
-
assert.equal(
|
|
90
|
-
diagnostics.some(
|
|
91
|
-
(diagnostic: Diagnostic) =>
|
|
92
|
-
diagnostic.filePath === "<memory>" &&
|
|
93
|
-
diagnostic.code === "malformed-line" &&
|
|
94
|
-
diagnostic.line === 3
|
|
95
|
-
),
|
|
96
|
-
true
|
|
97
|
-
);
|
|
98
|
-
|
|
99
|
-
assert.equal(
|
|
100
|
-
diagnostics.some(
|
|
101
|
-
(diagnostic: Diagnostic) =>
|
|
102
|
-
diagnostic.filePath === "<memory>" &&
|
|
103
|
-
diagnostic.code === "malformed-line" &&
|
|
104
|
-
diagnostic.line === 7 &&
|
|
105
|
-
diagnostic.message === "unexpected operand for end"
|
|
106
|
-
),
|
|
107
|
-
true
|
|
108
|
-
);
|
|
109
|
-
|
|
110
|
-
assert.equal(
|
|
111
|
-
diagnostics.some(
|
|
112
|
-
(diagnostic: Diagnostic) =>
|
|
113
|
-
diagnostic.filePath === bankOpsPath &&
|
|
114
|
-
diagnostic.code === "unsupported-65816" &&
|
|
115
|
-
diagnostic.message.includes("mvn")
|
|
116
|
-
),
|
|
117
|
-
true
|
|
118
|
-
);
|
|
119
|
-
|
|
120
|
-
assert.equal(
|
|
121
|
-
diagnostics.some(
|
|
122
|
-
(diagnostic: Diagnostic) =>
|
|
123
|
-
diagnostic.filePath === longPath &&
|
|
124
|
-
diagnostic.code === "unsupported-65816" &&
|
|
125
|
-
diagnostic.message.includes("^")
|
|
126
|
-
),
|
|
127
|
-
true
|
|
128
|
-
);
|
|
129
|
-
}
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
import { parseDocument } from "../src/asm/document";
|
|
6
|
+
import {
|
|
7
|
+
collectWorkspaceDiagnostics,
|
|
8
|
+
type Diagnostic
|
|
9
|
+
} from "../src/asm/diagnostics";
|
|
10
|
+
|
|
11
|
+
export function runDiagnosticsTest(): void {
|
|
12
|
+
const duplicateSource = [
|
|
13
|
+
"dup equ 1",
|
|
14
|
+
" lda missing",
|
|
15
|
+
"dup equ 2",
|
|
16
|
+
" adc (",
|
|
17
|
+
" dsk ../build/WORLD",
|
|
18
|
+
" typ BIN",
|
|
19
|
+
" typ BLAH",
|
|
20
|
+
" end BLAH"
|
|
21
|
+
].join("\n");
|
|
22
|
+
|
|
23
|
+
const bankOpsPath = path.resolve(
|
|
24
|
+
process.cwd(),
|
|
25
|
+
"test/fixtures/invalid/65816-bank-ops.S"
|
|
26
|
+
);
|
|
27
|
+
const longPath = path.resolve(
|
|
28
|
+
process.cwd(),
|
|
29
|
+
"test/fixtures/invalid/65816-long-addressing.S"
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const diagnostics = collectWorkspaceDiagnostics([
|
|
33
|
+
{
|
|
34
|
+
filePath: "<memory>",
|
|
35
|
+
document: parseDocument(duplicateSource)
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
filePath: bankOpsPath,
|
|
39
|
+
document: parseDocument(fs.readFileSync(bankOpsPath, "utf8"))
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
filePath: longPath,
|
|
43
|
+
document: parseDocument(fs.readFileSync(longPath, "utf8"))
|
|
44
|
+
}
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
assert.equal(
|
|
48
|
+
diagnostics.some(
|
|
49
|
+
(diagnostic: Diagnostic) =>
|
|
50
|
+
diagnostic.filePath === "<memory>" &&
|
|
51
|
+
diagnostic.code === "duplicate-symbol" &&
|
|
52
|
+
diagnostic.line === 2
|
|
53
|
+
),
|
|
54
|
+
true
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
assert.equal(
|
|
58
|
+
diagnostics.some(
|
|
59
|
+
(diagnostic: Diagnostic) =>
|
|
60
|
+
diagnostic.filePath === "<memory>" &&
|
|
61
|
+
diagnostic.code === "unresolved-reference" &&
|
|
62
|
+
diagnostic.line === 1 &&
|
|
63
|
+
diagnostic.message.includes("missing")
|
|
64
|
+
),
|
|
65
|
+
true
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
assert.equal(
|
|
69
|
+
diagnostics.some(
|
|
70
|
+
(diagnostic: Diagnostic) =>
|
|
71
|
+
diagnostic.filePath === "<memory>" &&
|
|
72
|
+
diagnostic.code === "unresolved-reference" &&
|
|
73
|
+
(diagnostic.line === 4 || diagnostic.line === 5)
|
|
74
|
+
),
|
|
75
|
+
false
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
assert.equal(
|
|
79
|
+
diagnostics.some(
|
|
80
|
+
(diagnostic: Diagnostic) =>
|
|
81
|
+
diagnostic.filePath === "<memory>" &&
|
|
82
|
+
diagnostic.code === "unresolved-reference" &&
|
|
83
|
+
diagnostic.line === 6 &&
|
|
84
|
+
diagnostic.message.includes("BLAH")
|
|
85
|
+
),
|
|
86
|
+
true
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
assert.equal(
|
|
90
|
+
diagnostics.some(
|
|
91
|
+
(diagnostic: Diagnostic) =>
|
|
92
|
+
diagnostic.filePath === "<memory>" &&
|
|
93
|
+
diagnostic.code === "malformed-line" &&
|
|
94
|
+
diagnostic.line === 3
|
|
95
|
+
),
|
|
96
|
+
true
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
assert.equal(
|
|
100
|
+
diagnostics.some(
|
|
101
|
+
(diagnostic: Diagnostic) =>
|
|
102
|
+
diagnostic.filePath === "<memory>" &&
|
|
103
|
+
diagnostic.code === "malformed-line" &&
|
|
104
|
+
diagnostic.line === 7 &&
|
|
105
|
+
diagnostic.message === "unexpected operand for end"
|
|
106
|
+
),
|
|
107
|
+
true
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
assert.equal(
|
|
111
|
+
diagnostics.some(
|
|
112
|
+
(diagnostic: Diagnostic) =>
|
|
113
|
+
diagnostic.filePath === bankOpsPath &&
|
|
114
|
+
diagnostic.code === "unsupported-65816" &&
|
|
115
|
+
diagnostic.message.includes("mvn")
|
|
116
|
+
),
|
|
117
|
+
true
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
assert.equal(
|
|
121
|
+
diagnostics.some(
|
|
122
|
+
(diagnostic: Diagnostic) =>
|
|
123
|
+
diagnostic.filePath === longPath &&
|
|
124
|
+
diagnostic.code === "unsupported-65816" &&
|
|
125
|
+
diagnostic.message.includes("^")
|
|
126
|
+
),
|
|
127
|
+
true
|
|
128
|
+
);
|
|
129
|
+
}
|
|
@@ -1,131 +1,131 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import fs from "node:fs";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { spawn } from "node:child_process";
|
|
5
|
-
|
|
6
|
-
type JsonRpcMessage = {
|
|
7
|
-
id?: number;
|
|
8
|
-
jsonrpc: "2.0";
|
|
9
|
-
method?: string;
|
|
10
|
-
result?: unknown;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
function encodeMessage(message: object): string {
|
|
14
|
-
const body = JSON.stringify(message);
|
|
15
|
-
return `Content-Length: ${Buffer.byteLength(body, "utf8")}\r\n\r\n${body}`;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function decodeMessages(streamBuffer: string): { messages: JsonRpcMessage[]; rest: string } {
|
|
19
|
-
const messages: JsonRpcMessage[] = [];
|
|
20
|
-
let buffer = streamBuffer;
|
|
21
|
-
|
|
22
|
-
for (;;) {
|
|
23
|
-
const separator = buffer.indexOf("\r\n\r\n");
|
|
24
|
-
if (separator === -1) {
|
|
25
|
-
return { messages, rest: buffer };
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const header = buffer.slice(0, separator);
|
|
29
|
-
const match = /Content-Length: (\d+)/i.exec(header);
|
|
30
|
-
if (!match) {
|
|
31
|
-
throw new Error(`Missing Content-Length header: ${header}`);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const length = Number(match[1]);
|
|
35
|
-
const body = buffer.slice(separator + 4);
|
|
36
|
-
if (Buffer.byteLength(body, "utf8") < length) {
|
|
37
|
-
return { messages, rest: buffer };
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
messages.push(JSON.parse(body.slice(0, length)) as JsonRpcMessage);
|
|
41
|
-
buffer = body.slice(length);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export async function runDocumentSymbolTest(): Promise<void> {
|
|
46
|
-
const serverPath = path.resolve(__dirname, "../src/server.js");
|
|
47
|
-
const fixturePath = path.resolve(
|
|
48
|
-
process.cwd(),
|
|
49
|
-
"test/fixtures/valid/merlin32-main-6502.
|
|
50
|
-
);
|
|
51
|
-
const uri = `file://${fixturePath.replace(/\\/g, "/")}`;
|
|
52
|
-
const text = fs.readFileSync(fixturePath, "utf8");
|
|
53
|
-
const child = spawn(process.execPath, [serverPath], {
|
|
54
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
let stdout = "";
|
|
58
|
-
let nextId = 1;
|
|
59
|
-
const pending = new Map<number, (message: JsonRpcMessage) => void>();
|
|
60
|
-
|
|
61
|
-
child.stdout.setEncoding("utf8");
|
|
62
|
-
child.stdout.on("data", (chunk: string) => {
|
|
63
|
-
stdout += chunk;
|
|
64
|
-
const decoded = decodeMessages(stdout);
|
|
65
|
-
stdout = decoded.rest;
|
|
66
|
-
|
|
67
|
-
for (const message of decoded.messages) {
|
|
68
|
-
if (message.id !== undefined) {
|
|
69
|
-
pending.get(message.id)?.(message);
|
|
70
|
-
pending.delete(message.id);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
function sendRequest(method: string, params: object): Promise<JsonRpcMessage> {
|
|
76
|
-
const id = nextId++;
|
|
77
|
-
child.stdin.write(
|
|
78
|
-
encodeMessage({
|
|
79
|
-
id,
|
|
80
|
-
jsonrpc: "2.0",
|
|
81
|
-
method,
|
|
82
|
-
params
|
|
83
|
-
})
|
|
84
|
-
);
|
|
85
|
-
|
|
86
|
-
return new Promise((resolve) => {
|
|
87
|
-
pending.set(id, resolve);
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function sendNotification(method: string, params: object): void {
|
|
92
|
-
child.stdin.write(
|
|
93
|
-
encodeMessage({
|
|
94
|
-
jsonrpc: "2.0",
|
|
95
|
-
method,
|
|
96
|
-
params
|
|
97
|
-
})
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
try {
|
|
102
|
-
const initialize = await sendRequest("initialize", {
|
|
103
|
-
capabilities: {},
|
|
104
|
-
processId: process.pid,
|
|
105
|
-
rootUri: null
|
|
106
|
-
});
|
|
107
|
-
assert.equal(initialize.id, 1);
|
|
108
|
-
|
|
109
|
-
sendNotification("initialized", {});
|
|
110
|
-
sendNotification("textDocument/didOpen", {
|
|
111
|
-
textDocument: {
|
|
112
|
-
uri,
|
|
113
|
-
languageId: "asm",
|
|
114
|
-
version: 1,
|
|
115
|
-
text
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
const response = await sendRequest("textDocument/documentSymbol", {
|
|
120
|
-
textDocument: { uri }
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
const symbols = response.result as Array<{ name: string; kind: number }>;
|
|
124
|
-
assert.equal(Array.isArray(symbols), true);
|
|
125
|
-
assert.equal(symbols.some((symbol) => symbol.name === "TEXT" && symbol.kind === 13), true);
|
|
126
|
-
assert.equal(symbols.some((symbol) => symbol.name === "TEST_START" && symbol.kind === 6), true);
|
|
127
|
-
assert.equal(symbols.some((symbol) => symbol.name === "dum0" && symbol.kind === 8), true);
|
|
128
|
-
} finally {
|
|
129
|
-
child.kill();
|
|
130
|
-
}
|
|
131
|
-
}
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
5
|
+
|
|
6
|
+
type JsonRpcMessage = {
|
|
7
|
+
id?: number;
|
|
8
|
+
jsonrpc: "2.0";
|
|
9
|
+
method?: string;
|
|
10
|
+
result?: unknown;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
function encodeMessage(message: object): string {
|
|
14
|
+
const body = JSON.stringify(message);
|
|
15
|
+
return `Content-Length: ${Buffer.byteLength(body, "utf8")}\r\n\r\n${body}`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function decodeMessages(streamBuffer: string): { messages: JsonRpcMessage[]; rest: string } {
|
|
19
|
+
const messages: JsonRpcMessage[] = [];
|
|
20
|
+
let buffer = streamBuffer;
|
|
21
|
+
|
|
22
|
+
for (;;) {
|
|
23
|
+
const separator = buffer.indexOf("\r\n\r\n");
|
|
24
|
+
if (separator === -1) {
|
|
25
|
+
return { messages, rest: buffer };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const header = buffer.slice(0, separator);
|
|
29
|
+
const match = /Content-Length: (\d+)/i.exec(header);
|
|
30
|
+
if (!match) {
|
|
31
|
+
throw new Error(`Missing Content-Length header: ${header}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const length = Number(match[1]);
|
|
35
|
+
const body = buffer.slice(separator + 4);
|
|
36
|
+
if (Buffer.byteLength(body, "utf8") < length) {
|
|
37
|
+
return { messages, rest: buffer };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
messages.push(JSON.parse(body.slice(0, length)) as JsonRpcMessage);
|
|
41
|
+
buffer = body.slice(length);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export async function runDocumentSymbolTest(): Promise<void> {
|
|
46
|
+
const serverPath = path.resolve(__dirname, "../src/server.js");
|
|
47
|
+
const fixturePath = path.resolve(
|
|
48
|
+
process.cwd(),
|
|
49
|
+
"test/fixtures/valid/merlin32-main-6502.S"
|
|
50
|
+
);
|
|
51
|
+
const uri = `file://${fixturePath.replace(/\\/g, "/")}`;
|
|
52
|
+
const text = fs.readFileSync(fixturePath, "utf8");
|
|
53
|
+
const child = spawn(process.execPath, [serverPath], {
|
|
54
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
let stdout = "";
|
|
58
|
+
let nextId = 1;
|
|
59
|
+
const pending = new Map<number, (message: JsonRpcMessage) => void>();
|
|
60
|
+
|
|
61
|
+
child.stdout.setEncoding("utf8");
|
|
62
|
+
child.stdout.on("data", (chunk: string) => {
|
|
63
|
+
stdout += chunk;
|
|
64
|
+
const decoded = decodeMessages(stdout);
|
|
65
|
+
stdout = decoded.rest;
|
|
66
|
+
|
|
67
|
+
for (const message of decoded.messages) {
|
|
68
|
+
if (message.id !== undefined) {
|
|
69
|
+
pending.get(message.id)?.(message);
|
|
70
|
+
pending.delete(message.id);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
function sendRequest(method: string, params: object): Promise<JsonRpcMessage> {
|
|
76
|
+
const id = nextId++;
|
|
77
|
+
child.stdin.write(
|
|
78
|
+
encodeMessage({
|
|
79
|
+
id,
|
|
80
|
+
jsonrpc: "2.0",
|
|
81
|
+
method,
|
|
82
|
+
params
|
|
83
|
+
})
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
return new Promise((resolve) => {
|
|
87
|
+
pending.set(id, resolve);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function sendNotification(method: string, params: object): void {
|
|
92
|
+
child.stdin.write(
|
|
93
|
+
encodeMessage({
|
|
94
|
+
jsonrpc: "2.0",
|
|
95
|
+
method,
|
|
96
|
+
params
|
|
97
|
+
})
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
const initialize = await sendRequest("initialize", {
|
|
103
|
+
capabilities: {},
|
|
104
|
+
processId: process.pid,
|
|
105
|
+
rootUri: null
|
|
106
|
+
});
|
|
107
|
+
assert.equal(initialize.id, 1);
|
|
108
|
+
|
|
109
|
+
sendNotification("initialized", {});
|
|
110
|
+
sendNotification("textDocument/didOpen", {
|
|
111
|
+
textDocument: {
|
|
112
|
+
uri,
|
|
113
|
+
languageId: "asm",
|
|
114
|
+
version: 1,
|
|
115
|
+
text
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const response = await sendRequest("textDocument/documentSymbol", {
|
|
120
|
+
textDocument: { uri }
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const symbols = response.result as Array<{ name: string; kind: number }>;
|
|
124
|
+
assert.equal(Array.isArray(symbols), true);
|
|
125
|
+
assert.equal(symbols.some((symbol) => symbol.name === "TEXT" && symbol.kind === 13), true);
|
|
126
|
+
assert.equal(symbols.some((symbol) => symbol.name === "TEST_START" && symbol.kind === 6), true);
|
|
127
|
+
assert.equal(symbols.some((symbol) => symbol.name === "dum0" && symbol.kind === 8), true);
|
|
128
|
+
} finally {
|
|
129
|
+
child.kill();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import fs from "node:fs";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
|
|
5
|
-
const validFixturePaths = [
|
|
6
|
-
"test/fixtures/valid/merlin32-linkscript.
|
|
7
|
-
"test/fixtures/valid/merlin32-main-6502.
|
|
8
|
-
];
|
|
9
|
-
|
|
10
|
-
const invalidFixturePaths = [
|
|
11
|
-
"test/fixtures/invalid/65816-bank-ops.
|
|
12
|
-
"test/fixtures/invalid/65816-long-addressing.
|
|
13
|
-
];
|
|
14
|
-
|
|
15
|
-
export function runFixtureCorpusTest(): void {
|
|
16
|
-
for (const fixturePath of validFixturePaths) {
|
|
17
|
-
const absolutePath = path.resolve(process.cwd(), fixturePath);
|
|
18
|
-
assert.equal(fs.existsSync(absolutePath), true, `${fixturePath} should exist`);
|
|
19
|
-
|
|
20
|
-
const content = fs.readFileSync(absolutePath, "utf8");
|
|
21
|
-
assert.match(content, /Source: apple2accumulator\/merlin32/);
|
|
22
|
-
assert.ok(content.trim().length > 0, `${fixturePath} should not be empty`);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
for (const fixturePath of invalidFixturePaths) {
|
|
26
|
-
const absolutePath = path.resolve(process.cwd(), fixturePath);
|
|
27
|
-
assert.equal(fs.existsSync(absolutePath), true, `${fixturePath} should exist`);
|
|
28
|
-
|
|
29
|
-
const content = fs.readFileSync(absolutePath, "utf8");
|
|
30
|
-
assert.match(content, /Source: apple2accumulator\/merlin32/);
|
|
31
|
-
assert.match(content, /65816-only/);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
const validFixturePaths = [
|
|
6
|
+
"test/fixtures/valid/merlin32-linkscript.S",
|
|
7
|
+
"test/fixtures/valid/merlin32-main-6502.S"
|
|
8
|
+
];
|
|
9
|
+
|
|
10
|
+
const invalidFixturePaths = [
|
|
11
|
+
"test/fixtures/invalid/65816-bank-ops.S",
|
|
12
|
+
"test/fixtures/invalid/65816-long-addressing.S"
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
export function runFixtureCorpusTest(): void {
|
|
16
|
+
for (const fixturePath of validFixturePaths) {
|
|
17
|
+
const absolutePath = path.resolve(process.cwd(), fixturePath);
|
|
18
|
+
assert.equal(fs.existsSync(absolutePath), true, `${fixturePath} should exist`);
|
|
19
|
+
|
|
20
|
+
const content = fs.readFileSync(absolutePath, "utf8");
|
|
21
|
+
assert.match(content, /Source: apple2accumulator\/merlin32/);
|
|
22
|
+
assert.ok(content.trim().length > 0, `${fixturePath} should not be empty`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
for (const fixturePath of invalidFixturePaths) {
|
|
26
|
+
const absolutePath = path.resolve(process.cwd(), fixturePath);
|
|
27
|
+
assert.equal(fs.existsSync(absolutePath), true, `${fixturePath} should exist`);
|
|
28
|
+
|
|
29
|
+
const content = fs.readFileSync(absolutePath, "utf8");
|
|
30
|
+
assert.match(content, /Source: apple2accumulator\/merlin32/);
|
|
31
|
+
assert.match(content, /65816-only/);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
; Source: apple2accumulator/merlin32
|
|
2
|
-
; Upstream file: Test/linkscript.s
|
|
3
|
-
; Transcribed for a 6502-only positive fixture corpus.
|
|
4
|
-
|
|
5
|
-
* linkscript.s
|
|
6
|
-
* Merlin32 Test
|
|
7
|
-
*
|
|
8
|
-
* Created by Lane Roathe on 8/21/19.
|
|
9
|
-
|
|
10
|
-
typ $06
|
|
11
|
-
|
|
12
|
-
dsk Merlin32Test
|
|
13
|
-
org $800
|
|
14
|
-
|
|
15
|
-
asm "merlin32-main-6502.
|
|
16
|
-
sna main
|
|
1
|
+
; Source: apple2accumulator/merlin32
|
|
2
|
+
; Upstream file: Test/linkscript.s
|
|
3
|
+
; Transcribed for a 6502-only positive fixture corpus.
|
|
4
|
+
|
|
5
|
+
* linkscript.s
|
|
6
|
+
* Merlin32 Test
|
|
7
|
+
*
|
|
8
|
+
* Created by Lane Roathe on 8/21/19.
|
|
9
|
+
|
|
10
|
+
typ $06
|
|
11
|
+
|
|
12
|
+
dsk Merlin32Test
|
|
13
|
+
org $800
|
|
14
|
+
|
|
15
|
+
asm "merlin32-main-6502.S"
|
|
16
|
+
sna main
|