aboutsummaryrefslogtreecommitdiffstats
path: root/idatui
diff options
context:
space:
mode:
authoruser <user@clank>2026-07-11 10:09:03 +0200
committeruser <user@clank>2026-07-11 10:09:03 +0200
commit156168cb42237884edd39af15bc80138c9443910 (patch)
treea82e78c5dc0229775911b71706d82e5022169b88 /idatui
parentdocs: blueprint for generalizing the TUI-driving layer (e.g. gdb) (diff)
downloadida-tui-156168cb42237884edd39af15bc80138c9443910.tar.gz
ida-tui-156168cb42237884edd39af15bc80138c9443910.tar.xz
ida-tui-156168cb42237884edd39af15bc80138c9443910.zip
drive pc: render pseudocode in the TUI, not just the driver
The pc command called only the read-only pseudocode RPC (runs off the UI loop, never touches the screen), so LLM-driven sessions showed nothing on the live pane. Compose goto + toggle_view + search so the real TUI navigates to the function, makes the decomp pane the visibly-active view, and jumps the cursor to the needle — then return the same text as before.
Diffstat (limited to 'idatui')
-rw-r--r--idatui/drive.py35
1 files changed, 32 insertions, 3 deletions
diff --git a/idatui/drive.py b/idatui/drive.py
index 35909df..e77eb76 100644
--- a/idatui/drive.py
+++ b/idatui/drive.py
@@ -7,7 +7,7 @@ blobs, and bundles the common composite gestures (goto+rename, batch rename,
function comment, filtered pseudocode). Use it for low-overhead tool-calls.
python3 -m idatui.drive where # where am I
- python3 -m idatui.drive pc main strrchr # pseudocode of main, lines matching 'strrchr'
+ python3 -m idatui.drive pc main strrchr # SHOW main's pseudocode on screen, lines matching 'strrchr'
python3 -m idatui.drive callees main # what main calls
python3 -m idatui.drive rename sub_5BE0 stdout_isatty
python3 -m idatui.drive mv sub_A=foo sub_B=bar # batch rename
@@ -84,15 +84,44 @@ def cmd_go(c, args):
return _fmt_where(c.call("state"))
+def _show_decomp(c):
+ """Make the pseudocode pane the visibly-active view (best effort).
+
+ Tab toggles disasm<->decomp, and leaves hex back to the preferred code
+ view; so at most two toggles reach decomp from any state. If the
+ decompiler fails for the current function the view falls back to disasm
+ and we simply stop — the caller still returns the pseudocode error text.
+ """
+ for _ in range(2):
+ if c.call("state").get("active") == "decomp":
+ return
+ try:
+ c.call("toggle_view")
+ except RpcError:
+ return
+
+
def cmd_pc(c, args):
target = _tgt(args[0]) if args else None
- needle = args[1].lower() if len(args) > 1 else None
+ needle = args[1] if len(args) > 1 else None
+ # Drive the real UI so viewers see the pseudocode, not just the driver.
+ if target is not None:
+ c.call("goto", target=target, delay_ms=0)
+ _show_decomp(c)
+ if needle is not None:
+ # Incremental-search in the now-visible pane so the cursor lands on
+ # the first hit (best effort: the term may not be a single token).
+ try:
+ c.call("search", term=needle, direction=1)
+ except RpcError:
+ pass
d = c.call("pseudocode", target=target)
if not d.get("code"):
return f"(no pseudocode: {d.get('error')})"
lines = d["code"].splitlines()
if needle:
- lines = [f"{i:4} {l}" for i, l in enumerate(lines) if needle in l.lower()]
+ nlow = needle.lower()
+ lines = [f"{i:4} {l}" for i, l in enumerate(lines) if nlow in l.lower()]
return "\n".join(lines) or f"(no line matches {needle!r})"
return d["code"]