aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--idatui/app.py21
-rw-r--r--tests/test_scenarios.py13
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