diff options
| author | blasty <blasty@local> | 2026-07-24 16:43:36 +0200 |
|---|---|---|
| committer | blasty <blasty@local> | 2026-07-24 16:43:36 +0200 |
| commit | b13b1f181483dfb61d1be1d666f1595b4e7d504f (patch) | |
| tree | a52e87b5e6e7d9e288012d3f5872d98c1c479a9b | |
| parent | palette: fix results width (were compounded narrower than the modal) (diff) | |
| download | ida-tui-b13b1f181483dfb61d1be1d666f1595b4e7d504f.tar.gz ida-tui-b13b1f181483dfb61d1be1d666f1595b4e7d504f.tar.xz ida-tui-b13b1f181483dfb61d1be1d666f1595b4e7d504f.zip | |
decomp: restore the 'decompiling…' overlay on F5/Tab from the listing
The F5/Tab-from-listing path decompiled the function inside _decomp_from_listing
(via _decomp_line_for) in a background thread with no overlay, THEN _show_active
re-decompiled it -- but that second call hit the cache and returned instantly, so
dec.loading was set and cleared within a frame and the overlay only flashed. The
long wait (the real decompile) happened with nothing on screen.
Raise the pseudocode pane + loading overlay synchronously in action_toggle_view
before launching the background decompile, so the wait is covered. Non-function
F5 restores the listing via _decomp_from_listing_failed.
Pilot view_toggle now drives the real path (action_toggle_view from the listing)
and asserts the overlay is raised synchronously; F5 paths (decomp_fallback,
continuous_view, region_define) all still pass (20/20 + view_toggle 7/7).
| -rw-r--r-- | idatui/app.py | 21 | ||||
| -rw-r--r-- | tests/test_scenarios.py | 13 |
2 files changed, 27 insertions, 7 deletions
diff --git a/idatui/app.py b/idatui/app.py index 3e84c6f..e0f455d 100644 --- a/idatui/app.py +++ b/idatui/app.py @@ -2958,6 +2958,17 @@ class IdaTui(App): if ea is None: self._status("no address here to decompile") return + # Raise the pseudocode pane + 'decompiling…' overlay NOW, before the + # background decompile runs — otherwise the (cold) decompile happens in + # _decomp_from_listing first and _show_active's re-decompile is cached, + # so the overlay only flashes for an instant. + dec = self.query_one(DecompView) + self.query_one(ListingView).display = False + self.query_one(HexView).display = False + dec.display = True + dec.loading = True + dec.focus() + self._status("decompiling\u2026") self._decomp_from_listing(ea) return # active == "decomp": F5/Tab returns to the linear listing. @@ -3005,12 +3016,20 @@ class IdaTui(App): fn = self.program.function_of(ea) if fn is None: self.app.call_from_thread( - self._status, + self._decomp_from_listing_failed, "F5 — cursor is not inside a defined function ('p' to make one)") return dec_idx = self._decomp_line_for(fn.addr, ea) self.app.call_from_thread(self._enter_decomp, fn.addr, fn.name, dec_idx) + def _decomp_from_listing_failed(self, msg: str) -> None: + # We optimistically raised the pseudocode overlay; drop back to the + # listing since there's nothing to decompile here. + self.query_one(DecompView).loading = False + self._active = "listing" + self._show_active() + self._status(msg) + def _enter_decomp(self, fn_addr: int, fn_name: str, dec_idx: int) -> None: # Snapshot the listing position so F5/Tab returns exactly here. lst = self.query_one(ListingView) diff --git a/tests/test_scenarios.py b/tests/test_scenarios.py index 94baf93..e21226c 100644 --- a/tests/test_scenarios.py +++ b/tests/test_scenarios.py @@ -617,17 +617,18 @@ async def s_view_toggle(c: Ctx): c.check("tab switches to disassembly", app._active == "listing" and dis.display and not dec.display, f"active={app._active}") - await c.press("tab") - await c.wait(lambda: app._active == "decomp", 10) - # a (re)decompile shows the grayed overlay + # F5/Tab from the listing must raise the 'decompiling…' overlay synchronously, + # BEFORE the background decompile runs (regression: it used to decompile first + # in _decomp_from_listing, so the overlay only flashed once cached). dec.loaded_ea = None - app._show_active() + app.action_toggle_view() cover = dec._cover_widget - c.check("decompile shows a 'decompiling…' overlay", + c.check("F5 from the listing raises the 'decompiling…' overlay", dec.loading and cover is not None and "decomp-loading" in cover.classes and "decompiling" in str(cover.render()), f"loading={dec.loading} cover={cover!r}") - await c.wait(lambda: not dec.loading and dec._cover_widget is None, 25) + await c.wait(lambda: app._active == "decomp" and not dec.loading + and dec._cover_widget is None, 25) c.check("overlay clears when the decompile finishes", not dec.loading and dec._cover_widget is None) # line-number gutter |
