diff options
| author | blasty <blasty@local> | 2026-07-27 22:11:28 +0200 |
|---|---|---|
| committer | blasty <blasty@local> | 2026-07-27 22:11:28 +0200 |
| commit | a5c7c55c900ab2da85745386b43f6bc20a70ea3a (patch) | |
| tree | 7b85c97ffae83d085bddad98c5c3bd3aa5c303a7 | |
| parent | rpc: a `trace` verb, and --trace for pane spawn (diff) | |
| download | ida-tui-a5c7c55c900ab2da85745386b43f6bc20a70ea3a.tar.gz ida-tui-a5c7c55c900ab2da85745386b43f6bc20a70ea3a.tar.xz ida-tui-a5c7c55c900ab2da85745386b43f6bc20a70ea3a.zip | |
The status bar said "0x2490" without saying what it belonged to. Obvious once
there are two panes open, or after switching binaries in a project — which
already prefixed its label, so single-binary sessions were the odd ones out.
[cat] .text @ 0x472b [listing] (c code · p func · u undefine · Enter follow)
Uses the opened file's basename, not _module(): that one asks the worker over
RPC and this runs on every status write. Kept in step when the path changes
(project switch, reload).
Three messages already carried the module name themselves and would have read
"[echo] echo — 128 functions"; they don't say it twice now.
tests: +3 scenarios (212) — the bar names the file, keeps naming it as you move
(the idle status is not the only writer), and doesn't say it twice.
| -rw-r--r-- | idatui/app.py | 18 | ||||
| -rw-r--r-- | tests/test_scenarios.py | 31 |
2 files changed, 43 insertions, 6 deletions
diff --git a/idatui/app.py b/idatui/app.py index 71cd9c4..cc9cfa3 100644 --- a/idatui/app.py +++ b/idatui/app.py @@ -3242,6 +3242,7 @@ class IdaTui(App): self._open_path = open_path self._ttl = ttl self._load_args = load_args or "" # IDA switches for a headerless blob + self._title = (os.path.basename(open_path) if open_path else "") self._trace_path = trace_path or "" # Tenet execution trace to explore self._trace = None # the loaded Trace, once analysed self._trail_map = [] # decomp_map for _trail_map_ea @@ -3576,8 +3577,12 @@ class IdaTui(App): self._flash_until = _time.monotonic() + 8.0 elif self._flash and _time.monotonic() < self._flash_until: text = self._flash - if self._binary: # project mode: always say which binary you're in - text = f"[{self._binary}] {text}" + # Always say WHICH file this is. In project mode that's the binary's + # label; otherwise the filename we opened. Cheap on purpose — _module() + # asks the worker, and this runs on every status write. + tag = self._binary or self._title + if tag: + text = f"[{tag}] {text}" # An image with no functions at all is nearly always a blob described # wrongly, and that stays true as you scroll around — so it belongs in # the status bar, not in a one-off message the next write clobbers. @@ -3723,6 +3728,7 @@ class IdaTui(App): self._binary = label self._pool.set_active(label) self._open_path = self._project.by_label(label).staged + self._title = os.path.basename(self._open_path) return client if not self._open_path: self.app.call_from_thread( @@ -3745,7 +3751,6 @@ class IdaTui(App): self._func_index = idx self.app.call_from_thread(lambda: self.query_one("#func-table", DataTable).clear()) last = 0 - module = self._module() while not idx.complete: idx.load_next_page() rows = idx.window(last, len(idx) - last) @@ -3753,14 +3758,14 @@ class IdaTui(App): if rows: self.app.call_from_thread(self._append_rows, rows) self.app.call_from_thread( - self._status, f"{module} — {last} functions…" + self._status, f"{last} functions…" ) # If a filter is active (typed during load), re-apply it over the full set. if self._filter_term: self.app.call_from_thread(self._apply_filter, self._filter_term) else: self.app.call_from_thread( - self._status, f"{module} — {len(idx)} functions (Ctrl+N: find symbol)") + self._status, f"{len(idx)} functions (Ctrl+N: find symbol)") # Land somewhere useful instead of an empty pane: main() if present, # otherwise pop the fuzzy symbol picker. self.app.call_from_thread(self._auto_land) @@ -3977,7 +3982,7 @@ class IdaTui(App): if term: self._status(f"filter '{term}': {len(matched)}/{total}") else: - self._status(f"{self._module()} — {total} functions") + self._status(f"{total} functions") def _apply_pending_filter(self) -> None: self._filter_timer = None @@ -4179,6 +4184,7 @@ class IdaTui(App): self._binary = label self._pool.set_active(label) self._open_path = self._project.by_label(label).staged + self._title = os.path.basename(self._open_path) self._active = st.active if st else "listing" self._split = st.split if st else False self._filter_term = st.filter_term if st else "" diff --git a/tests/test_scenarios.py b/tests/test_scenarios.py index 09e1ac5..9d77680 100644 --- a/tests/test_scenarios.py +++ b/tests/test_scenarios.py @@ -444,6 +444,37 @@ async def s_asm_highlight(c: Ctx): mn is not None, f"{code[0].spans if code else None}") +@scenario("status_names_the_file") +async def s_status_names_the_file(c: Ctx): + """The status bar always says which file you're looking at. + + Obvious once several panes and a project switcher exist: with two idatui + windows open, or after switching binaries, "0x2490" alone doesn't say what + it belongs to. + """ + from textual.widgets import Static + app = c.app + await c.open_biggest("listing") + await c.pause(0.3) + name = os.path.basename(app._open_path) + status = str(app.query_one("#status", Static).render()) + c.check("the status bar names the open file", + status.startswith(f"[{name}]"), f"{status[:60]!r} (want [{name}])") + + # It must survive the messages that WRITE the status, not just the idle one. + c.lst.focus() + await c.press("down") + await c.pause(0.3) + status = str(app.query_one("#status", Static).render()) + c.check("and keeps naming it as you move", + status.startswith(f"[{name}]"), status[:60]) + + # The function-count message used to include the module name itself, which + # would now read "[echo] echo — 128 functions". + c.check("without saying the name twice", + status.count(name) == 1, status[:70]) + + @scenario("command_palette") async def s_command_palette(c: Ctx): app = c.app |
