diff --git a/CHANGELOG.md b/CHANGELOG.md index f1e7ffcf..1862618e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ See [extension/CHANGELOG.md](extension/CHANGELOG.md). - Editor moved from CodeMirror 5 to CodeMirror 6 with Lezer grammars for 6502, Z80, 6809, Verilog, BASIC, batari BASIC, FastBasic, Inform 6 and Wiz; new Cobalt and MBO themes and better indent handling. - Rust syntax highlighting for `.rs` files, via `@codemirror/lang-rust`; the urust toolchain now claims the `rust` editor style instead of falling back to C. - A `mod foo;` line shows one include-link arrow instead of two (it had linked to both `foo.rs` and `foo/mod.rs`); the arrow now points at whichever file exists. +- Rust `use rs8::nes::…` (and the other register modules) opens the module source: urust embeds them in its `.wasm`, so they ship again as `urust-fs.zip` for the editor to read. - Source search across the project and toolchain headers (F1 for help), with arrow-key navigation, plus symbol search without source. - Shortcut bar and keyboard shortcut help, F8 pause/resume, Cmd+Shift+G go to offset. - Settings menu with developer options (highlight whitespace, highlight executed lines, line numbers off by default). diff --git a/src/common/toolmeta.ts b/src/common/toolmeta.ts index 80800b4d..84d23294 100644 --- a/src/common/toolmeta.ts +++ b/src/common/toolmeta.ts @@ -255,13 +255,19 @@ export const DIALOG_INCLUDE_PATTERNS: RegExp[] = [ // `mod foo;` pulls in foo.rs (or foo/mod.rs) beside the including file, the // way rustc resolves a module, so those files ride along as includes. `use // foo::...;` only names a module `mod` already declared; it is still scanned -// so the editor can link it, but built-ins (rs8) and path keywords are left -// out. urust loads modules itself -- no separate link step is involved. +// so the editor can link it. The rs8 register modules resolve to the sources +// shipped in urust-fs.zip (see the tool's includeDirs): urust splices them in +// from its .wasm, so they're copied out again for the editor to read; path +// keywords (self/super/crate) stay out. urust loads modules itself -- no +// separate link step is involved. const URUST_MOD_PATTERN = /^\s*(?:pub(?:\([^)]*\))?\s+)?mod\s+([A-Za-z_][A-Za-z0-9_]*)\s*;/gm; +// the modules in crates/rs8/src (and the io shim) that urust bundles +const URUST_RS8_MODULE_PATTERN = /\brs8::(atari8|c64|conio|io|nes|rand|vcs)\b/g; export const URUST_INCLUDE_PATTERNS: (RegExp | ToolIncludePattern)[] = [ /\binclude_(?:bytes|str)!\s*\(\s*"(.+?)"\s*\)/g, { re: URUST_MOD_PATTERN, suffix: '.rs' }, { re: URUST_MOD_PATTERN, suffix: '/mod.rs' }, + { re: URUST_RS8_MODULE_PATTERN, suffix: '.rs' }, { re: /^\s*(?:pub(?:\([^)]*\))?\s+)?use\s+(?!self\b|super\b|crate\b|rs8\b)([A-Za-z_][A-Za-z0-9_]*)\s*::/gm, suffix: '.rs' }, ]; @@ -771,6 +777,10 @@ export const TOOL_META: { [id: string]: ToolMeta } = { editorStyle: 'rust', wasmModule: 'urust', version: '0.0.1', + // the compiler embeds the rs8 sources; ship them as a WASI filesystem so + // the editor can open them (view-only -- the build uses the embedded ones) + wasiFSZip: 'urust-fs.zip', + includeDirs: ['/rs8'], includePatterns: URUST_INCLUDE_PATTERNS, }, diff --git a/src/worker/tools/urust.ts b/src/worker/tools/urust.ts index 57c730bf..c9e24635 100644 --- a/src/worker/tools/urust.ts +++ b/src/worker/tools/urust.ts @@ -27,7 +27,8 @@ export function compileUrust(step: BuildStep): BuildStepResult { wasi.initSync(wasiModule); populateWASIFiles(wasi, step); const args = ['urust', 'build', step.path, '--platform', platform, '--emit', 'bin', '-o', binpath, - '-fno-bounds-checks', + '-Ob', '-fno-bounds-checks', + '-Wall', '--debug-out', dbgpath, '--error-format', 'json']; args.push.apply(args, extraArgsFor('urust', step.params && step.params.buildArgs)); wasi.setArgs(args); diff --git a/src/worker/tools/urustparse.ts b/src/worker/tools/urustparse.ts index 1deb7db8..4c922dc7 100644 --- a/src/worker/tools/urustparse.ts +++ b/src/worker/tools/urustparse.ts @@ -107,6 +107,5 @@ export function urustDebugToResult(dbg: UrustDebug, paths: string[]): UrustDebug export function urustPlatformName(platform: string): string | undefined { const root = platform.split(/[.-]/)[0]; if (platform === 'devel-6502' || root === 'devel') return 'devel-6502'; - if (['nes', 'c64', 'atari8', 'vcs'].includes(root)) return root; - return undefined; + return root; } diff --git a/test/cli/testworker.js b/test/cli/testworker.js index 041609e5..c275c9ac 100644 --- a/test/cli/testworker.js +++ b/test/cli/testworker.js @@ -344,7 +344,7 @@ describe('Worker', function() { } it('should compile urust and return source ranges, symbols and segments', async function() { - var code = 'use rs8::reset;\n\nstatic mut OUT: u8 = 0;\n\nfn double(x: u8) -> u8 {\n x.wrapping_add(x)\n}\n\n#[reset]\nfn main() {\n unsafe { OUT = double(21); }\n}\n'; + var code = 'use rs8::reset;\n\nstatic mut OUT: u8 = 0;\n\n#[no_cross(256)] fn double(x: u8) -> u8 {\n x.wrapping_add(x)\n}\n\n#[reset]\nfn main() {\n unsafe { OUT = double(21); }\n}\n'; var result = await buildOne({code:code, platform:'devel-6502', tool:'urust', path:'main.rs', mainfile:true}); assert.ok(!result.errors || result.errors.length === 0, JSON.stringify(result.errors)); assert.ok(result.output instanceof Uint8Array && result.output.length > 0); diff --git a/test/unit/testurust.ts b/test/unit/testurust.ts index d2eb5841..cbfa303b 100644 --- a/test/unit/testurust.ts +++ b/test/unit/testurust.ts @@ -1,6 +1,8 @@ import assert from "assert"; +import * as fs from "fs"; +import JSZip from "jszip"; import { describe, it } from "mocha"; -import { getIncludePatterns, matchDependencyPatterns } from "../../src/common/toolmeta"; +import { getIncludeDirs, getIncludePatterns, getSharedFileSystemName, matchDependencyPatterns } from "../../src/common/toolmeta"; import { getToolForPlatform } from "../../src/common/toolselect"; import { parseUrustDiagnostics, urustDebugToResult, urustPlatformName, UrustDebug @@ -24,11 +26,28 @@ describe('urust tool', function () { 'use audio::{music_play, SND_COIN};\nuse bcd::bcd_add;\nuse rs8::reset;\n'; const deps = matchDependencyPatterns(src, getIncludePatterns('urust')); // `mod` names the file (foo.rs or foo/mod.rs); `use` is scanned for editor - // links but skips the built-in rs8 module + // links but skips the bare rs8 items (they live in the compiler) assert.deepStrictEqual(deps, ['audio.rs', 'bcd.rs', 'audio/mod.rs', 'bcd/mod.rs', 'audio.rs', 'bcd.rs']); }); + it('links rs8 register modules to the bundled sources', function () { + const src = 'use rs8::nes::*;\nuse rs8::io;\nuse rs8::{reg, reset};\n'; + const deps = matchDependencyPatterns(src, getIncludePatterns('urust')); + // one file per register module; bare items (reg/reset) have no file + assert.deepStrictEqual(deps, ['nes.rs', 'io.rs']); + }); + + it('exposes the rs8 sources as a WASI filesystem for the editor', async function () { + assert.strictEqual(getSharedFileSystemName('urust', 'devel-6502'), 'wasi:urust-fs.zip'); + assert.deepStrictEqual(getIncludeDirs('urust', 'devel-6502'), ['/rs8']); + // /rs8/.rs has to resolve for every module the include pattern links + const zip = await JSZip.loadAsync(fs.readFileSync('src/worker/fs/urust-fs.zip')); + for (const f of ['lib.rs', 'atari8.rs', 'c64.rs', 'conio.rs', 'io.rs', 'nes.rs', 'rand.rs', 'vcs.rs']) { + assert.ok(zip.file('rs8/' + f), 'missing rs8/' + f); + } + }); + it('maps platform ids to urust platforms', function () { assert.strictEqual(urustPlatformName('devel-6502'), 'devel-6502'); assert.strictEqual(urustPlatformName('nes'), 'nes');