@razdolbai/merls 1.2.1 → 1.3.0

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.
Files changed (36) hide show
  1. package/AGENTS.md +64 -64
  2. package/dist/src/lsp/completion.js +14 -11
  3. package/dist/test/completion.test.js +1 -1
  4. package/dist/test/definition-references.test.js +1 -1
  5. package/dist/test/diagnostics.test.js +2 -2
  6. package/dist/test/document-symbol.test.js +1 -1
  7. package/dist/test/fixture-corpus.test.js +4 -4
  8. package/dist/test/hover.test.js +1 -1
  9. package/dist/test/lexer.test.js +4 -4
  10. package/dist/test/local-labels.test.js +1 -1
  11. package/dist/test/publish-diagnostics.test.js +1 -1
  12. package/dist/test/semantic-tokens.test.js +2 -1
  13. package/dist/test/symbols.test.js +1 -1
  14. package/dist/test/workspace-symbol.test.js +2 -2
  15. package/dist/test/workspace.test.js +2 -2
  16. package/package.json +1 -1
  17. package/src/lsp/completion.ts +15 -11
  18. package/test/completion.test.ts +151 -151
  19. package/test/definition-references.test.ts +152 -152
  20. package/test/diagnostics.test.ts +129 -129
  21. package/test/document-symbol.test.ts +131 -131
  22. package/test/fixture-corpus.test.ts +33 -33
  23. package/test/fixtures/valid/{merlin32-linkscript.asm → merlin32-linkscript.S} +16 -16
  24. package/test/hover.test.ts +175 -175
  25. package/test/lexer.test.ts +87 -87
  26. package/test/local-labels.test.ts +47 -47
  27. package/test/publish-diagnostics.test.ts +206 -206
  28. package/test/semantic-tokens.test.ts +128 -128
  29. package/test/smoke/run-smoke.ps1 +177 -177
  30. package/test/symbols.test.ts +41 -41
  31. package/test/workspace-symbol.test.ts +139 -139
  32. package/test/workspace.test.ts +29 -29
  33. /package/test/fixtures/invalid/{65816-bank-ops.asm → 65816-bank-ops.S} +0 -0
  34. /package/test/fixtures/invalid/{65816-long-addressing.asm → 65816-long-addressing.S} +0 -0
  35. /package/test/fixtures/valid/{merlin32-main-6502.asm → merlin32-main-6502.S} +0 -0
  36. /package/test/fixtures/valid/{smoke-test.asm → smoke-test.S} +0 -0
@@ -1,139 +1,139 @@
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
- result?: unknown;
10
- };
11
-
12
- function encodeMessage(message: object): string {
13
- const body = JSON.stringify(message);
14
- return `Content-Length: ${Buffer.byteLength(body, "utf8")}\r\n\r\n${body}`;
15
- }
16
-
17
- function decodeMessages(streamBuffer: string): { messages: JsonRpcMessage[]; rest: string } {
18
- const messages: JsonRpcMessage[] = [];
19
- let buffer = streamBuffer;
20
-
21
- for (;;) {
22
- const separator = buffer.indexOf("\r\n\r\n");
23
- if (separator === -1) {
24
- return { messages, rest: buffer };
25
- }
26
-
27
- const header = buffer.slice(0, separator);
28
- const match = /Content-Length: (\d+)/i.exec(header);
29
- if (!match) {
30
- throw new Error(`Missing Content-Length header: ${header}`);
31
- }
32
-
33
- const length = Number(match[1]);
34
- const body = buffer.slice(separator + 4);
35
- if (Buffer.byteLength(body, "utf8") < length) {
36
- return { messages, rest: buffer };
37
- }
38
-
39
- messages.push(JSON.parse(body.slice(0, length)) as JsonRpcMessage);
40
- buffer = body.slice(length);
41
- }
42
- }
43
-
44
- export async function runWorkspaceSymbolTest(): Promise<void> {
45
- const serverPath = path.resolve(__dirname, "../src/server.js");
46
- const linkPath = path.resolve(
47
- process.cwd(),
48
- "test/fixtures/valid/merlin32-linkscript.asm"
49
- );
50
- const mainPath = path.resolve(
51
- process.cwd(),
52
- "test/fixtures/valid/merlin32-main-6502.asm"
53
- );
54
- const linkUri = `file://${linkPath.replace(/\\/g, "/")}`;
55
- const mainUri = `file://${mainPath.replace(/\\/g, "/")}`;
56
- const child = spawn(process.execPath, [serverPath], {
57
- stdio: ["pipe", "pipe", "pipe"]
58
- });
59
-
60
- let stdout = "";
61
- let nextId = 1;
62
- const pending = new Map<number, (message: JsonRpcMessage) => void>();
63
-
64
- child.stdout.setEncoding("utf8");
65
- child.stdout.on("data", (chunk: string) => {
66
- stdout += chunk;
67
- const decoded = decodeMessages(stdout);
68
- stdout = decoded.rest;
69
-
70
- for (const message of decoded.messages) {
71
- if (message.id !== undefined) {
72
- pending.get(message.id)?.(message);
73
- pending.delete(message.id);
74
- }
75
- }
76
- });
77
-
78
- function sendRequest(method: string, params: object): Promise<JsonRpcMessage> {
79
- const id = nextId++;
80
- child.stdin.write(
81
- encodeMessage({
82
- id,
83
- jsonrpc: "2.0",
84
- method,
85
- params
86
- })
87
- );
88
-
89
- return new Promise((resolve) => {
90
- pending.set(id, resolve);
91
- });
92
- }
93
-
94
- function sendNotification(method: string, params: object): void {
95
- child.stdin.write(
96
- encodeMessage({
97
- jsonrpc: "2.0",
98
- method,
99
- params
100
- })
101
- );
102
- }
103
-
104
- try {
105
- await sendRequest("initialize", {
106
- capabilities: {},
107
- processId: process.pid,
108
- rootUri: `file://${path.resolve(process.cwd()).replace(/\\/g, "/")}`
109
- });
110
-
111
- sendNotification("initialized", {});
112
- sendNotification("textDocument/didOpen", {
113
- textDocument: {
114
- uri: linkUri,
115
- languageId: "asm",
116
- version: 1,
117
- text: fs.readFileSync(linkPath, "utf8")
118
- }
119
- });
120
- sendNotification("textDocument/didOpen", {
121
- textDocument: {
122
- uri: mainUri,
123
- languageId: "asm",
124
- version: 1,
125
- text: fs.readFileSync(mainPath, "utf8")
126
- }
127
- });
128
-
129
- const response = await sendRequest("workspace/symbol", {
130
- query: "Get"
131
- });
132
-
133
- const symbols = response.result as Array<{ name: string; location: { uri: string } }>;
134
- assert.equal(Array.isArray(symbols), true);
135
- assert.equal(symbols.some((symbol) => symbol.name === "GetKey" && symbol.location.uri === mainUri), true);
136
- } finally {
137
- child.kill();
138
- }
139
- }
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
+ result?: unknown;
10
+ };
11
+
12
+ function encodeMessage(message: object): string {
13
+ const body = JSON.stringify(message);
14
+ return `Content-Length: ${Buffer.byteLength(body, "utf8")}\r\n\r\n${body}`;
15
+ }
16
+
17
+ function decodeMessages(streamBuffer: string): { messages: JsonRpcMessage[]; rest: string } {
18
+ const messages: JsonRpcMessage[] = [];
19
+ let buffer = streamBuffer;
20
+
21
+ for (;;) {
22
+ const separator = buffer.indexOf("\r\n\r\n");
23
+ if (separator === -1) {
24
+ return { messages, rest: buffer };
25
+ }
26
+
27
+ const header = buffer.slice(0, separator);
28
+ const match = /Content-Length: (\d+)/i.exec(header);
29
+ if (!match) {
30
+ throw new Error(`Missing Content-Length header: ${header}`);
31
+ }
32
+
33
+ const length = Number(match[1]);
34
+ const body = buffer.slice(separator + 4);
35
+ if (Buffer.byteLength(body, "utf8") < length) {
36
+ return { messages, rest: buffer };
37
+ }
38
+
39
+ messages.push(JSON.parse(body.slice(0, length)) as JsonRpcMessage);
40
+ buffer = body.slice(length);
41
+ }
42
+ }
43
+
44
+ export async function runWorkspaceSymbolTest(): Promise<void> {
45
+ const serverPath = path.resolve(__dirname, "../src/server.js");
46
+ const linkPath = path.resolve(
47
+ process.cwd(),
48
+ "test/fixtures/valid/merlin32-linkscript.S"
49
+ );
50
+ const mainPath = path.resolve(
51
+ process.cwd(),
52
+ "test/fixtures/valid/merlin32-main-6502.S"
53
+ );
54
+ const linkUri = `file://${linkPath.replace(/\\/g, "/")}`;
55
+ const mainUri = `file://${mainPath.replace(/\\/g, "/")}`;
56
+ const child = spawn(process.execPath, [serverPath], {
57
+ stdio: ["pipe", "pipe", "pipe"]
58
+ });
59
+
60
+ let stdout = "";
61
+ let nextId = 1;
62
+ const pending = new Map<number, (message: JsonRpcMessage) => void>();
63
+
64
+ child.stdout.setEncoding("utf8");
65
+ child.stdout.on("data", (chunk: string) => {
66
+ stdout += chunk;
67
+ const decoded = decodeMessages(stdout);
68
+ stdout = decoded.rest;
69
+
70
+ for (const message of decoded.messages) {
71
+ if (message.id !== undefined) {
72
+ pending.get(message.id)?.(message);
73
+ pending.delete(message.id);
74
+ }
75
+ }
76
+ });
77
+
78
+ function sendRequest(method: string, params: object): Promise<JsonRpcMessage> {
79
+ const id = nextId++;
80
+ child.stdin.write(
81
+ encodeMessage({
82
+ id,
83
+ jsonrpc: "2.0",
84
+ method,
85
+ params
86
+ })
87
+ );
88
+
89
+ return new Promise((resolve) => {
90
+ pending.set(id, resolve);
91
+ });
92
+ }
93
+
94
+ function sendNotification(method: string, params: object): void {
95
+ child.stdin.write(
96
+ encodeMessage({
97
+ jsonrpc: "2.0",
98
+ method,
99
+ params
100
+ })
101
+ );
102
+ }
103
+
104
+ try {
105
+ await sendRequest("initialize", {
106
+ capabilities: {},
107
+ processId: process.pid,
108
+ rootUri: `file://${path.resolve(process.cwd()).replace(/\\/g, "/")}`
109
+ });
110
+
111
+ sendNotification("initialized", {});
112
+ sendNotification("textDocument/didOpen", {
113
+ textDocument: {
114
+ uri: linkUri,
115
+ languageId: "asm",
116
+ version: 1,
117
+ text: fs.readFileSync(linkPath, "utf8")
118
+ }
119
+ });
120
+ sendNotification("textDocument/didOpen", {
121
+ textDocument: {
122
+ uri: mainUri,
123
+ languageId: "asm",
124
+ version: 1,
125
+ text: fs.readFileSync(mainPath, "utf8")
126
+ }
127
+ });
128
+
129
+ const response = await sendRequest("workspace/symbol", {
130
+ query: "Get"
131
+ });
132
+
133
+ const symbols = response.result as Array<{ name: string; location: { uri: string } }>;
134
+ assert.equal(Array.isArray(symbols), true);
135
+ assert.equal(symbols.some((symbol) => symbol.name === "GetKey" && symbol.location.uri === mainUri), true);
136
+ } finally {
137
+ child.kill();
138
+ }
139
+ }
@@ -1,29 +1,29 @@
1
- import assert from "node:assert/strict";
2
- import path from "node:path";
3
-
4
- import { indexWorkspace } from "../src/asm/workspace";
5
-
6
- export function runWorkspaceGraphTest(): void {
7
- const entryPath = path.resolve(
8
- process.cwd(),
9
- "test/fixtures/valid/merlin32-linkscript.asm"
10
- );
11
- const mainPath = path.resolve(
12
- process.cwd(),
13
- "test/fixtures/valid/merlin32-main-6502.asm"
14
- );
15
-
16
- const workspace = indexWorkspace(entryPath);
17
-
18
- assert.deepEqual(workspace.loadOrder, [entryPath, mainPath]);
19
- assert.deepEqual(workspace.dependencies.get(entryPath), [mainPath]);
20
- assert.equal(workspace.documents.has(entryPath), true);
21
- assert.equal(workspace.documents.has(mainPath), true);
22
-
23
- assert.deepEqual(workspace.symbols.get("TEXT"), {
24
- name: "TEXT",
25
- kind: "equate",
26
- line: 11,
27
- filePath: mainPath
28
- });
29
- }
1
+ import assert from "node:assert/strict";
2
+ import path from "node:path";
3
+
4
+ import { indexWorkspace } from "../src/asm/workspace";
5
+
6
+ export function runWorkspaceGraphTest(): void {
7
+ const entryPath = path.resolve(
8
+ process.cwd(),
9
+ "test/fixtures/valid/merlin32-linkscript.S"
10
+ );
11
+ const mainPath = path.resolve(
12
+ process.cwd(),
13
+ "test/fixtures/valid/merlin32-main-6502.S"
14
+ );
15
+
16
+ const workspace = indexWorkspace(entryPath);
17
+
18
+ assert.deepEqual(workspace.loadOrder, [entryPath, mainPath]);
19
+ assert.deepEqual(workspace.dependencies.get(entryPath), [mainPath]);
20
+ assert.equal(workspace.documents.has(entryPath), true);
21
+ assert.equal(workspace.documents.has(mainPath), true);
22
+
23
+ assert.deepEqual(workspace.symbols.get("TEXT"), {
24
+ name: "TEXT",
25
+ kind: "equate",
26
+ line: 11,
27
+ filePath: mainPath
28
+ });
29
+ }