From 31f00a82ab992add03dd3891c96a5e6292be2ed2 Mon Sep 17 00:00:00 2001 From: blasty Date: Fri, 24 Jul 2026 16:17:58 +0200 Subject: palette: replace the stock Ctrl+P command palette with real ida-tui actions The Ctrl+P palette was Textual's stock system commands (change theme / take screenshot / quit) -- useless for RE. Add an IdaCommands command Provider and set IdaTui.COMMANDS = {IdaCommands} so the palette lists real actions instead: goto, find symbol, follow, xrefs, toggle disasm/pseudocode, continuous listing, hex, rename, retype, comment, define code/func, make data/string, undefine, toggle opcodes, structs editor, filter, names pane, save, quit -- each with its keybind as help text, fuzzy-searchable. App-level actions run directly; cursor-scoped ones (rename/xrefs/comment/...) are dispatched to the active code view via IdaTui._palette_action (focus + run the view's action_*), so a palette pick does exactly what the key does. Verified live over RPC: 'hex' switches view, 'goto' opens the prompt, 'struct' opens the StructEditor modal, and the stock 'theme' command is gone. Pilot command_palette scenario: Ctrl+P opens it and a command executes (7/7). --- idatui/app.py | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) (limited to 'idatui/app.py') diff --git a/idatui/app.py b/idatui/app.py index ac55864..2a3e617 100644 --- a/idatui/app.py +++ b/idatui/app.py @@ -28,6 +28,7 @@ from rich.text import Text from textual import work from textual.app import App, ComposeResult from textual.binding import Binding +from textual.command import DiscoveryHit, Hit, Provider from textual.containers import Horizontal, Vertical from textual.geometry import Region, Size from textual.message import Message @@ -2342,7 +2343,71 @@ class StructEditor(ModalScreen): # --------------------------------------------------------------------------- # # The app # --------------------------------------------------------------------------- # +class IdaCommands(Provider): + """Fills the Ctrl+P command palette with real ida-tui actions instead of the + stock Textual system commands (change theme / take screenshot / …). + + App-level actions run directly; cursor-scoped ones (rename/xrefs/comment/…) + are dispatched to the active code view via ``IdaTui._palette_action``.""" + + def _commands(self): + app = self.app + va = app._palette_action # dispatch to the focused code view + return ( + ("Goto address / symbol…", "jump to an address or name (g)", + app.action_goto), + ("Find symbol…", "fuzzy function finder (Ctrl+N)", app.action_symbols), + ("Follow symbol under cursor", "jump to the referenced symbol (Enter)", + lambda: va("follow")), + ("Show xrefs to symbol", "cross-references to the cursor symbol (x)", + lambda: va("xrefs")), + ("Back", "navigation history (Esc)", app.action_back), + ("Toggle disassembly / pseudocode", "decompile / listing (F5, Tab)", + app.action_toggle_view), + ("Continuous listing here", "flat segment listing (L)", + app.action_continuous_here), + ("Hex view", "raw bytes at the cursor (\\)", app.action_hex), + ("Rename symbol…", "rename the symbol under the cursor (n)", + lambda: va("rename")), + ("Set type / prototype…", "retype the symbol under the cursor (y)", + lambda: va("retype")), + ("Add comment…", "comment at the cursor (;)", lambda: va("comment")), + ("Define code", "make code at the cursor (c)", lambda: va("define_code")), + ("Create function", "define a function at the cursor (p)", + lambda: va("define_func")), + ("Make data", "define a data item at the cursor (d)", + lambda: va("make_data")), + ("Make string", "define a string at the cursor (a)", + lambda: va("make_string")), + ("Undefine", "undefine the item at the cursor (u)", + lambda: va("undefine")), + ("Toggle opcode bytes", "cycle the opcode-bytes column (o)", + lambda: va("toggle_opcodes")), + ("Structs / types editor", "view + edit local types (Ctrl+T)", + app.action_structs), + ("Filter functions…", "glob-filter the function list (/)", + app.action_filter), + ("Toggle names pane", "function-list sidebar (Ctrl+B)", + app.action_toggle_functions), + ("Save database (.i64)", "persist changes (Ctrl+S)", app.action_save), + ("Quit", "exit ida-tui (q)", app.action_quit), + ) + + async def discover(self): + for title, help_text, cb in self._commands(): + yield DiscoveryHit(title, cb, help=help_text) + + async def search(self, query: str): + matcher = self.matcher(query) + for title, help_text, cb in self._commands(): + score = matcher.match(title) + if score > 0: + yield Hit(score, matcher.highlight(title), cb, help=help_text) + + class IdaTui(App): + COMMANDS = {IdaCommands} # replace the stock system-commands palette + CSS = """ Screen { layout: vertical; } #panes { height: 1fr; } @@ -4061,6 +4126,20 @@ class IdaTui(App): return self.query_one(DecompView) return None + def _palette_action(self, name: str) -> None: + """Run a cursor-scoped code-view action (rename/xrefs/follow/…) picked from + the command palette against the active code view.""" + view = self._active_code_view() + if view is None: + self._status("open a function first") + return + view.focus() + fn = getattr(view, f"action_{name}", None) + if fn is None: + self._status(f"'{name}' isn't available in this view") + return + fn() + def action_continuous_here(self) -> None: """'L': open the continuous segment listing at the cursor — one long flat view where functions, data and undefined bytes are interleaved, -- cgit v1.3.1-sl0p