From 6bc2cc233cc65e725b94750e093709c86296612d Mon Sep 17 00:00:00 2001 From: blasty Date: Thu, 23 Jul 2026 23:23:05 +0200 Subject: fix: keep the pseudocode cursor/scroll when a rename re-decompiles Renaming a symbol in the decompiler re-decompiles and refreshes (good), but the pane rendered at the wrong scroll until the first cursor move nudged it back. Cause: _reload_active_code only cleared loaded_ea and re-showed, and dec_scroll_y isn't tracked on every move, so _apply_decomp fell into show()'s DERIVE branch -- a bare scroll_to that, on a freshly shown pane whose size isn't computed yet, clamps/leaves a stale frame until the next interaction. Now the decomp refresh snapshots the LIVE pseudocode cursor + scroll from the widget before forcing the recompile, so _apply_decomp takes show()'s robust _apply_scroll path (deferred re-apply + refresh(layout=True)) and repaints at the right place immediately. Verified on main (421-line pseudocode, 22-row viewport): before rename cursor=54/scroll=33; after rename cursor=54/scroll=33, cursor visible -- position preserved with no stale frame. rename/rename_history/decomp_nav/ decomp_follow_self/view_toggle/follow_xrefs/xref_labels/listing_view/ continuous_view/func_banners/search/mouse/disasm_nav green (follow_xrefs was an ordering flake; 14/0 in isolation). --- idatui/app.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/idatui/app.py b/idatui/app.py index 2e00c67..122ffcc 100644 --- a/idatui/app.py +++ b/idatui/app.py @@ -3124,7 +3124,18 @@ class IdaTui(App): if cur is None: return if self._active == "decomp": - self.query_one(DecompView).loaded_ea = None # force re-decompile + # Snapshot the LIVE pseudocode position before forcing a recompile. + # dec_scroll_y isn't tracked on every move, so without this the reload + # falls into show()'s derive path (a bare scroll_to) and leaves a + # stale frame until the next cursor move; capturing the real scroll + # makes show() take the robust _apply_scroll path and repaint now. + dec = self.query_one(DecompView) + if cur.ea == dec.loaded_ea: + cur.dec_cursor = dec.cursor + cur.dec_cursor_x = dec.cursor_x + cur.dec_scroll_y = round(dec.scroll_offset.y) + cur.dec_scroll_x = round(dec.scroll_offset.x) + dec.loaded_ea = None # force re-decompile self._show_active() else: # Capture the LIVE listing position straight from the widget (the -- cgit v1.3.1-sl0p