ui: cmd+shift+g go-to-offset in disassembler and memory views

This commit is contained in:
Steven Hugg committed 2026-09-01 11:22:44 +02:00
1 parent b4ea378903
commit 7c026da92a
4 files changed
+121 -3

No files matched your search

+5
View File
@@ -537,6 +537,11 @@ div.markdown th {
.seg_io { color: #ffcccc; }
.seg_stack { color: #ffff66; }
.seg_unknown { color: #cccccc; }
.goto_line {
background-color: #1155cc;
color: #ffffff;
width: 100%;
}
.segment.segment-ram {
background-color:#aaeeaa;
}
+59 -1
View File
@@ -1095,6 +1095,55 @@ function jumpToError(err: WorkerError) {
}
}
// go-to-address (mod+shift+g): works while the Disassembly, Memory Browser,
// or VRAM Browser is the active view; accepts a hex address ("$1234", "0x1234",
// "1234") or a symbol name ("main")
function getGoToAddressView(): DisassemblerView | MemoryView | null {
var wnd = projectWindows.getActive();
if (wnd instanceof DisassemblerView || wnd instanceof MemoryView) return wnd;
return null;
}
function parseGoToTarget(s: string): number {
s = s.trim();
if (!s) return -1;
// symbol name first (so symbols like "add" aren't parsed as hex)
var symmap = platform.debugSymbols && platform.debugSymbols.symbolmap;
if (symmap && s in symmap) return symmap[s];
var ls = s.toLowerCase();
if (symmap) {
for (var sym in symmap) {
if (sym.toLowerCase() === ls) return symmap[sym];
}
}
// hex address (bare digits are parsed as hex, like the listings)
if (/^(0x|\$)[0-9a-f]+$/i.test(s)) return parseInt(s.replace(/^(0x|\$)/i, ''), 16);
if (/^[0-9a-f]+$/i.test(s)) return parseInt(s, 16);
return -1;
}
function promptGoToAddress(): boolean {
var wnd = getGoToAddressView();
if (!wnd) return false;
var cur = (wnd as any).getCursorPC ? (wnd as any).getCursorPC() : -1;
(bootbox as any).prompt({
title: "Go to Address",
placeholder: "address in hex",
value: cur >= 0 ? hex(cur, 4) : "",
callback: (result: string) => {
if (!result) return; // canceled or empty
var addr = parseGoToTarget(result);
if (isNaN(addr) || addr < 0) {
alertError("Can't find address or symbol: " + DOMPurify.sanitize(result));
return;
}
wnd.goToAddress(addr);
}
});
return true;
}
function getGlobalShortcuts(): Shortcut[] {
var shortcuts: Shortcut[] = [];
if (platform && isPlatformReady()) {
@@ -1108,9 +1157,13 @@ function getGlobalShortcuts(): Shortcut[] {
if (platform.isRunning && platform.isRunning()) {
// in a debug session but emulator running: just Pause
shortcuts.push({ key: 'mod+shift+h', label: 'Pause', fn: pause });
} else if (platform.isRunning && !platform.isRunning()) {
} else if (platform.isRunning && !platform.isRunning() && !getGoToAddressView()) {
// mod+shift+g becomes "Go To Address" while a debug tool view is active
shortcuts.push({ key: 'mod+shift+g', label: 'Resume', fn: resume });
}
if (getGoToAddressView()) {
shortcuts.push({ key: 'mod+shift+g', label: 'Go To Address', fn: promptGoToAddress });
}
}
return shortcuts;
}
@@ -1642,6 +1695,11 @@ function setupDebugControls() {
uitoolbar.grp.prop('id', 'run_bar');
uitoolbar.add('mod+shift+r', 'Reset', 'glyphicon-refresh', resetAndRun).prop('id', 'dbg_reset');
uitoolbar.add('mod+shift+h', 'Pause', 'glyphicon-pause', pause).prop('id', 'dbg_pause');
// bound before Resume so it takes priority in the debug tool views;
// falls through to Resume everywhere else
uitoolbar.add('mod+shift+g', 'Go To Address', '', (e) => {
if (!promptGoToAddress()) resume();
});
uitoolbar.add('mod+shift+g', 'Resume', 'glyphicon-play', resume).prop('id', 'dbg_go');
if (platform.restartAtPC) {
uitoolbar.add('mod+shift+a', 'Restart at Cursor', 'glyphicon-play-circle', restartAtCursor).prop('id', 'dbg_restartatline');
+26
View File
@@ -37,6 +37,8 @@ export class MemoryView implements ProjectView {
return this.maindiv = div;
}
gotoRow = -1; // row highlighted by the Go To Address prompt
showMemoryWindow(workspace: HTMLElement, parent: HTMLElement) {
this.memorylist = new VirtualList({
w: $(workspace).width(),
@@ -50,6 +52,7 @@ export class MemoryView implements ProjectView {
var dlr = this.dumplines[row];
if (dlr) linediv.classList.add('seg_' + this.getMemorySegment(this.dumplines[row].a | this.hibits));
}
if (row == this.gotoRow) linediv.classList.add('goto_line');
linediv.appendChild(document.createTextNode(s));
return linediv;
}
@@ -68,6 +71,28 @@ export class MemoryView implements ProjectView {
}
}
// jump to an address (or symbol), via the Go To Address prompt;
// centers the target row in the viewport and highlights it
goToAddress(addr: number) {
this.hibits = addr & 0xffff0000;
var dump = this.getDumpLines();
var row;
if (dump && dump.length) {
row = this.findMemoryWindowLine(addr & 0xffff);
} else {
// no symbol map (e.g. VRAM): rows are raw 16-byte chunks
row = (addr & 0xffff) >> 4;
}
if (row === undefined) return;
this.gotoRow = row;
if (this.memorylist) {
var c = this.memorylist.container;
var y = row * this.memorylist.itemHeight - (c.clientHeight - this.memorylist.itemHeight) / 2;
c.scrollTop = Math.max(0, y);
}
this.tick();
}
refresh() {
this.dumplines = null;
this.tick();
@@ -82,6 +107,7 @@ export class MemoryView implements ProjectView {
var newtext = this.getMemoryLineAt(row);
if (oldtext != newtext)
div.text(newtext);
div.toggleClass('goto_line', row == this.gotoRow);
});
}
}
+31 -2
View File
@@ -793,9 +793,9 @@ export class DisassemblerView implements ProjectView {
}
// TODO: too many globals
refresh(moveCursor: boolean) {
refresh(moveCursor: boolean, centerAddr?: number) {
let state = lastDebugState || platform.saveState(); // TODO?
let pc = state.c ? state.c.PC : 0;
let pc = centerAddr !== undefined ? centerAddr : (state.c ? state.c.PC : 0);
let curline = 0;
let selline = 0;
let addr2symbol = (platform.debugSymbols && platform.debugSymbols.addr2symbol) || {};
@@ -862,6 +862,35 @@ export class DisassemblerView implements ProjectView {
}
}
// jump to an address (or symbol), via the Go To Address prompt
goToAddress(addr: number) {
addr |= 0;
if (!this.findAndSelectAddress(addr)) {
// re-center the disassembly window on this address
this.refresh(false, addr);
this.findAndSelectAddress(addr);
}
this.disasmview.focus();
}
private findAndSelectAddress(addr: number): boolean {
const h = hex(addr, 4); // every line starts with the address + tab
const doc = this.disasmview.state.doc;
for (let i = 1; i <= doc.lines; i++) {
const line = doc.line(i);
if (line.text.startsWith(h + "\t")) {
// select the address so the line stays highlighted; scrollIntoView
// centers it in the viewport
this.disasmview.dispatch({
selection: { anchor: line.from, head: line.from + h.length },
effects: EditorView.scrollIntoView(line.from, { y: "center" }),
});
return true;
}
}
return false;
}
getCursorPC(): number {
const pos = this.disasmview.state.selection.main.head;
const lineNum = this.disasmview.state.doc.lineAt(pos).number;