@razdolbai/merls 0.1.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.
- package/.serena/memories/conventions.md +6 -0
- package/.serena/memories/core.md +8 -0
- package/.serena/memories/memory_maintenance.md +33 -0
- package/.serena/memories/suggested_commands.md +4 -0
- package/.serena/memories/task_completion.md +7 -0
- package/.serena/memories/tech_stack.md +6 -0
- package/.serena/project.yml +132 -0
- package/AGENTS.md +63 -0
- package/README.md +137 -0
- package/dist/src/asm/diagnostics.js +202 -0
- package/dist/src/asm/document.js +26 -0
- package/dist/src/asm/expression.js +163 -0
- package/dist/src/asm/lexer.js +122 -0
- package/dist/src/asm/local-labels.js +140 -0
- package/dist/src/asm/metadata.js +101 -0
- package/dist/src/asm/parser.js +118 -0
- package/dist/src/asm/symbols.js +40 -0
- package/dist/src/asm/syntax.js +44 -0
- package/dist/src/asm/workspace.js +73 -0
- package/dist/src/cli.js +21 -0
- package/dist/src/index.js +4 -0
- package/dist/src/lsp/completion.js +32 -0
- package/dist/src/lsp/diagnostics.js +63 -0
- package/dist/src/lsp/document-symbols.js +80 -0
- package/dist/src/lsp/hover.js +75 -0
- package/dist/src/lsp/symbol-navigation.js +181 -0
- package/dist/src/lsp/workspace-symbols.js +17 -0
- package/dist/src/server.js +77 -0
- package/dist/test/bootstrap.test.js +11 -0
- package/dist/test/cli-contract.test.js +74 -0
- package/dist/test/coc-config.test.js +21 -0
- package/dist/test/completion.test.js +126 -0
- package/dist/test/definition-references.test.js +126 -0
- package/dist/test/diagnostics.test.js +66 -0
- package/dist/test/document-model.test.js +30 -0
- package/dist/test/document-symbol.test.js +107 -0
- package/dist/test/expression.test.js +100 -0
- package/dist/test/fixture-corpus.test.js +33 -0
- package/dist/test/hover.test.js +142 -0
- package/dist/test/lexer.test.js +53 -0
- package/dist/test/line-parser.test.js +67 -0
- package/dist/test/local-labels.test.js +43 -0
- package/dist/test/metadata.test.js +27 -0
- package/dist/test/publish-diagnostics.test.js +137 -0
- package/dist/test/run-tests.js +132 -0
- package/dist/test/server-entrypoint.test.js +14 -0
- package/dist/test/server-initialize.test.js +77 -0
- package/dist/test/symbols.test.js +37 -0
- package/dist/test/syntax-shape.test.js +18 -0
- package/dist/test/workspace-symbol.test.js +113 -0
- package/dist/test/workspace.test.js +24 -0
- package/examples/coc-settings.json +18 -0
- package/package.json +26 -0
- package/publish.ps1 +9 -0
- package/src/asm/diagnostics.ts +294 -0
- package/src/asm/document.ts +43 -0
- package/src/asm/expression.ts +242 -0
- package/src/asm/lexer.ts +197 -0
- package/src/asm/local-labels.ts +204 -0
- package/src/asm/metadata.ts +150 -0
- package/src/asm/parser.ts +197 -0
- package/src/asm/symbols.ts +55 -0
- package/src/asm/syntax.ts +76 -0
- package/src/asm/workspace.ts +105 -0
- package/src/cli.ts +24 -0
- package/src/index.ts +1 -0
- package/src/lsp/completion.ts +42 -0
- package/src/lsp/diagnostics.ts +82 -0
- package/src/lsp/document-symbols.ts +111 -0
- package/src/lsp/hover.ts +90 -0
- package/src/lsp/symbol-navigation.ts +244 -0
- package/src/lsp/workspace-symbols.ts +24 -0
- package/src/server.ts +121 -0
- package/test/bootstrap.test.ts +7 -0
- package/test/cli-contract.test.ts +94 -0
- package/test/coc-config.test.ts +28 -0
- package/test/completion.test.ts +151 -0
- package/test/definition-references.test.ts +152 -0
- package/test/diagnostics.test.ts +129 -0
- package/test/document-model.test.ts +29 -0
- package/test/document-symbol.test.ts +131 -0
- package/test/expression.test.ts +111 -0
- package/test/fixture-corpus.test.ts +33 -0
- package/test/fixtures/invalid/65816-bank-ops.asm +17 -0
- package/test/fixtures/invalid/65816-long-addressing.asm +26 -0
- package/test/fixtures/valid/merlin32-linkscript.asm +16 -0
- package/test/fixtures/valid/merlin32-main-6502.asm +103 -0
- package/test/fixtures/valid/smoke-test.asm +7 -0
- package/test/hover.test.ts +175 -0
- package/test/lexer.test.ts +87 -0
- package/test/line-parser.test.ts +69 -0
- package/test/local-labels.test.ts +47 -0
- package/test/metadata.test.ts +27 -0
- package/test/publish-diagnostics.test.ts +206 -0
- package/test/run-tests.ts +139 -0
- package/test/server-entrypoint.test.ts +11 -0
- package/test/server-initialize.test.ts +101 -0
- package/test/smoke/run-smoke.ps1 +177 -0
- package/test/smoke/vimrc +17 -0
- package/test/symbols.test.ts +41 -0
- package/test/syntax-shape.test.ts +18 -0
- package/test/workspace-symbol.test.ts +139 -0
- package/test/workspace.test.ts +29 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# coc.nvim smoke test for merls language server.
|
|
2
|
+
#
|
|
3
|
+
# Launches Vim in headless mode with a minimal coc.nvim configuration,
|
|
4
|
+
# opens an .asm fixture file, waits for the language server to start and
|
|
5
|
+
# publish diagnostics, then exits with 0 on success / 1 on failure.
|
|
6
|
+
#
|
|
7
|
+
# Usage: pwsh test/smoke/run-smoke.ps1
|
|
8
|
+
|
|
9
|
+
$ErrorActionPreference = "Stop"
|
|
10
|
+
|
|
11
|
+
$projectRoot = (Resolve-Path "$PSScriptRoot/../..").Path
|
|
12
|
+
$smokeDir = "$projectRoot/test/smoke"
|
|
13
|
+
$fixture = "$projectRoot/test/fixtures/valid/smoke-test.asm"
|
|
14
|
+
$cliPath = "$projectRoot/dist/src/cli.js"
|
|
15
|
+
|
|
16
|
+
# Ensure the project is built
|
|
17
|
+
if (-not (Test-Path $cliPath)) {
|
|
18
|
+
Write-Host "Building project..."
|
|
19
|
+
Push-Location $projectRoot
|
|
20
|
+
npm run build
|
|
21
|
+
Pop-Location
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
# Create an isolated temporary directory for coc.nvim config/data
|
|
25
|
+
$tmpBase = "$projectRoot/test/smoke/tmp"
|
|
26
|
+
$configDir = "$tmpBase/config"
|
|
27
|
+
$dataDir = "$tmpBase/data"
|
|
28
|
+
|
|
29
|
+
if (Test-Path $tmpBase) { Remove-Item -Recurse -Force $tmpBase }
|
|
30
|
+
New-Item -ItemType Directory -Force -Path $configDir | Out-Null
|
|
31
|
+
New-Item -ItemType Directory -Force -Path $dataDir | Out-Null
|
|
32
|
+
|
|
33
|
+
# Write the coc-settings.json into the temporary config directory
|
|
34
|
+
$cocSettings = @{
|
|
35
|
+
languageserver = @{
|
|
36
|
+
merls = @{
|
|
37
|
+
command = "node"
|
|
38
|
+
args = @($cliPath.Replace("\", "/"), "--stdio")
|
|
39
|
+
rootPatterns = @(".git", "package.json")
|
|
40
|
+
filetypes = @("asm")
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
} | ConvertTo-Json -Depth 5
|
|
44
|
+
|
|
45
|
+
Set-Content -Path "$configDir/coc-settings.json" -Value $cocSettings
|
|
46
|
+
|
|
47
|
+
# Write a tiny Vim script that will:
|
|
48
|
+
# 1. Open the fixture file
|
|
49
|
+
# 2. Wait for coc.nvim to attach the language server
|
|
50
|
+
# 3. Check CocAction('diagnosticList') for expected diagnostics
|
|
51
|
+
# 4. Write results to a file and quit
|
|
52
|
+
$checkScript = @"
|
|
53
|
+
" Wait for coc.nvim to be ready, then run checks.
|
|
54
|
+
function! s:RunChecks(timer)
|
|
55
|
+
" Check if coc.nvim is running
|
|
56
|
+
try
|
|
57
|
+
let l:services = CocAction('services')
|
|
58
|
+
catch
|
|
59
|
+
" coc.nvim not ready yet, retry
|
|
60
|
+
call timer_start(1000, function('s:RunChecks'))
|
|
61
|
+
return
|
|
62
|
+
endtry
|
|
63
|
+
|
|
64
|
+
let l:result_file = '$($tmpBase.Replace("\", "/"))/result.txt'
|
|
65
|
+
let l:lines = []
|
|
66
|
+
|
|
67
|
+
" Check that the merls service is running
|
|
68
|
+
let l:found = 0
|
|
69
|
+
for l:svc in l:services
|
|
70
|
+
if l:svc.id =~# 'merls'
|
|
71
|
+
let l:found = 1
|
|
72
|
+
let l:state = l:svc.state
|
|
73
|
+
call add(l:lines, 'SERVICE: merls ' . l:state)
|
|
74
|
+
endif
|
|
75
|
+
endfor
|
|
76
|
+
|
|
77
|
+
if !l:found
|
|
78
|
+
call add(l:lines, 'SERVICE: merls NOT FOUND')
|
|
79
|
+
call writefile(l:lines, l:result_file)
|
|
80
|
+
qall!
|
|
81
|
+
return
|
|
82
|
+
endif
|
|
83
|
+
|
|
84
|
+
" Wait a moment for diagnostics to arrive, then collect them
|
|
85
|
+
call timer_start(2000, {-> s:CollectDiagnostics(l:lines, l:result_file)})
|
|
86
|
+
endfunction
|
|
87
|
+
|
|
88
|
+
function! s:CollectDiagnostics(lines, result_file)
|
|
89
|
+
try
|
|
90
|
+
let l:diags = CocAction('diagnosticList')
|
|
91
|
+
call add(a:lines, 'DIAGNOSTICS_COUNT: ' . len(l:diags))
|
|
92
|
+
for l:d in l:diags
|
|
93
|
+
call add(a:lines, 'DIAG: ' . l:d.severity . ' | ' . l:d.message)
|
|
94
|
+
endfor
|
|
95
|
+
catch
|
|
96
|
+
call add(a:lines, 'DIAGNOSTICS_ERROR: ' . v:exception)
|
|
97
|
+
endtry
|
|
98
|
+
|
|
99
|
+
call writefile(a:lines, a:result_file)
|
|
100
|
+
qall!
|
|
101
|
+
endfunction
|
|
102
|
+
|
|
103
|
+
" Start the check timer after a 3 second delay to let coc.nvim initialize
|
|
104
|
+
call timer_start(3000, function('s:RunChecks'))
|
|
105
|
+
"@
|
|
106
|
+
|
|
107
|
+
Set-Content -Path "$smokeDir/check.vim" -Value $checkScript
|
|
108
|
+
|
|
109
|
+
# Run Vim in headless mode
|
|
110
|
+
Write-Host "Starting Vim headless smoke test..."
|
|
111
|
+
$env:MERLS_SMOKE_CONFIG = $configDir
|
|
112
|
+
$env:MERLS_SMOKE_DATA = $dataDir
|
|
113
|
+
|
|
114
|
+
$vimArgs = @(
|
|
115
|
+
"-u", "$smokeDir/vimrc",
|
|
116
|
+
"-N", # nocompatible
|
|
117
|
+
"--not-a-term", # headless
|
|
118
|
+
"-S", "$smokeDir/check.vim",
|
|
119
|
+
$fixture
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
$vimProcess = Start-Process -FilePath "vim" -ArgumentList $vimArgs `
|
|
123
|
+
-PassThru -NoNewWindow -Wait -ErrorAction Stop
|
|
124
|
+
|
|
125
|
+
# Read results
|
|
126
|
+
$resultFile = "$tmpBase/result.txt"
|
|
127
|
+
if (-not (Test-Path $resultFile)) {
|
|
128
|
+
Write-Host "FAIL: Vim exited without writing results."
|
|
129
|
+
exit 1
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
$results = Get-Content $resultFile
|
|
133
|
+
Write-Host ""
|
|
134
|
+
Write-Host "=== Smoke test results ==="
|
|
135
|
+
$results | ForEach-Object { Write-Host " $_" }
|
|
136
|
+
Write-Host ""
|
|
137
|
+
|
|
138
|
+
# Validate
|
|
139
|
+
$passed = $true
|
|
140
|
+
|
|
141
|
+
# Check service was found and running
|
|
142
|
+
$serviceLine = $results | Where-Object { $_ -match "^SERVICE:" }
|
|
143
|
+
if ($serviceLine -match "running") {
|
|
144
|
+
Write-Host "PASS: merls language server is running in coc.nvim"
|
|
145
|
+
} else {
|
|
146
|
+
Write-Host "FAIL: merls language server not running. Got: $serviceLine"
|
|
147
|
+
$passed = $false
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
# Check diagnostics were received
|
|
151
|
+
$diagCountLine = $results | Where-Object { $_ -match "^DIAGNOSTICS_COUNT:" }
|
|
152
|
+
if ($diagCountLine) {
|
|
153
|
+
$count = [int]($diagCountLine -replace "DIAGNOSTICS_COUNT:\s*", "")
|
|
154
|
+
if ($count -gt 0) {
|
|
155
|
+
Write-Host "PASS: received $count diagnostic(s) from merls"
|
|
156
|
+
} else {
|
|
157
|
+
Write-Host "FAIL: expected at least 1 diagnostic, got 0"
|
|
158
|
+
$passed = $false
|
|
159
|
+
}
|
|
160
|
+
} else {
|
|
161
|
+
Write-Host "FAIL: no diagnostic count in results"
|
|
162
|
+
$passed = $false
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
# Cleanup
|
|
166
|
+
Remove-Item -Recurse -Force $tmpBase -ErrorAction SilentlyContinue
|
|
167
|
+
Remove-Item -Force "$smokeDir/check.vim" -ErrorAction SilentlyContinue
|
|
168
|
+
|
|
169
|
+
if ($passed) {
|
|
170
|
+
Write-Host ""
|
|
171
|
+
Write-Host "Smoke test PASSED."
|
|
172
|
+
exit 0
|
|
173
|
+
} else {
|
|
174
|
+
Write-Host ""
|
|
175
|
+
Write-Host "Smoke test FAILED."
|
|
176
|
+
exit 1
|
|
177
|
+
}
|
package/test/smoke/vimrc
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
" Minimal vimrc for the coc.nvim smoke test.
|
|
2
|
+
" Loads only coc.nvim from the user's vim-plug directory,
|
|
3
|
+
" configures the merls language server, and runs assertions.
|
|
4
|
+
|
|
5
|
+
set nocompatible
|
|
6
|
+
filetype off
|
|
7
|
+
|
|
8
|
+
" Load coc.nvim from the user's vim-plug directory
|
|
9
|
+
set runtimepath+=$USERPROFILE/vimfiles/local/plugged/coc.nvim
|
|
10
|
+
|
|
11
|
+
filetype plugin indent on
|
|
12
|
+
syntax on
|
|
13
|
+
|
|
14
|
+
" Point coc.nvim at a temporary config directory so it does not
|
|
15
|
+
" interfere with the user's real configuration.
|
|
16
|
+
let g:coc_config_home = $MERLS_SMOKE_CONFIG
|
|
17
|
+
let g:coc_data_home = $MERLS_SMOKE_DATA
|
|
@@ -0,0 +1,41 @@
|
|
|
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 { collectSymbols } from "../src/asm/symbols";
|
|
7
|
+
|
|
8
|
+
export function runSymbolsTest(): void {
|
|
9
|
+
const fixturePath = path.resolve(
|
|
10
|
+
process.cwd(),
|
|
11
|
+
"test/fixtures/valid/merlin32-main-6502.asm"
|
|
12
|
+
);
|
|
13
|
+
const source = fs.readFileSync(fixturePath, "utf8");
|
|
14
|
+
|
|
15
|
+
const document = parseDocument(source);
|
|
16
|
+
const symbols = collectSymbols(document);
|
|
17
|
+
|
|
18
|
+
assert.deepEqual(symbols.get("TEXT"), {
|
|
19
|
+
name: "TEXT",
|
|
20
|
+
kind: "equate",
|
|
21
|
+
line: 11
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
assert.deepEqual(symbols.get("TEST_START"), {
|
|
25
|
+
name: "TEST_START",
|
|
26
|
+
kind: "label",
|
|
27
|
+
line: 31
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
assert.deepEqual(symbols.get("dum0"), {
|
|
31
|
+
name: "dum0",
|
|
32
|
+
kind: "data",
|
|
33
|
+
line: 17
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
assert.deepEqual(symbols.get("_num1"), {
|
|
37
|
+
name: "_num1",
|
|
38
|
+
kind: "data",
|
|
39
|
+
line: 25
|
|
40
|
+
});
|
|
41
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
lineShapeTable,
|
|
5
|
+
tokenKindTable
|
|
6
|
+
} from "../src/asm/syntax";
|
|
7
|
+
|
|
8
|
+
export function runSyntaxShapeTest(): void {
|
|
9
|
+
assert.equal(tokenKindTable.get("comment")?.captureExamples[0], "; trailing note");
|
|
10
|
+
assert.equal(tokenKindTable.get("localLabel")?.captureExamples[0], "]loop");
|
|
11
|
+
assert.equal(tokenKindTable.get("modifier")?.captureExamples.includes("<value"), true);
|
|
12
|
+
assert.equal(tokenKindTable.has("expressionOperator"), true);
|
|
13
|
+
|
|
14
|
+
assert.equal(lineShapeTable.get("instruction")?.allowsLabel, true);
|
|
15
|
+
assert.equal(lineShapeTable.get("directive")?.requiresOperand, false);
|
|
16
|
+
assert.equal(lineShapeTable.get("equate")?.allowsExpression, true);
|
|
17
|
+
assert.equal(lineShapeTable.get("malformed")?.terminal, true);
|
|
18
|
+
}
|
|
@@ -0,0 +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
|
+
}
|
|
@@ -0,0 +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
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "CommonJS",
|
|
5
|
+
"moduleResolution": "Node",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"rootDir": ".",
|
|
8
|
+
"outDir": "dist",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"skipLibCheck": true,
|
|
13
|
+
"types": ["node"]
|
|
14
|
+
},
|
|
15
|
+
"include": ["src/**/*.ts", "test/**/*.ts"]
|
|
16
|
+
}
|