aboutsummaryrefslogtreecommitdiffstats
path: root/idatui
diff options
context:
space:
mode:
authorblasty <blasty@local>2026-07-09 21:27:24 +0200
committerblasty <blasty@local>2026-07-09 21:27:24 +0200
commit584af7e8edf6224c0f3affb2bb98230078a9e836 (patch)
tree0f6b107020a5e0008666bc08851af4e04f053a8b /idatui
parentdecompiler: xref jump within the current function moves the cursor (diff)
downloadida-tui-584af7e8edf6224c0f3affb2bb98230078a9e836.tar.gz
ida-tui-584af7e8edf6224c0f3affb2bb98230078a9e836.tar.xz
ida-tui-584af7e8edf6224c0f3affb2bb98230078a9e836.zip
decompiler: fix stale scroll frame on same-function jump
DecompView.goto derived its scroll with _scroll_cursor_into_view() -> a plain scroll_to, which updates scroll_offset but doesn't repaint at the new offset (Textual only repaints on a rounded scroll change, and there's no layout pass since the pane isn't reloaded). So a same-function xref jump landed the cursor correctly but painted the old scroll until the next cursor move nudged a repaint. Route goto's scroll (derived or explicit) through _apply_scroll, which re-applies after the next refresh with layout=True -- the same pattern show() uses. The cross-function path already worked because reloading + clearing the loading cover forces a fresh paint. Document the gotcha in TEXTUAL_NOTES. full suite 64/64.
Diffstat (limited to 'idatui')
-rw-r--r--idatui/app.py32
1 files changed, 25 insertions, 7 deletions
diff --git a/idatui/app.py b/idatui/app.py
index 6356a1b..6b76e63 100644
--- a/idatui/app.py
+++ b/idatui/app.py
@@ -882,19 +882,37 @@ class DecompView(SearchMixin, NavMixin, ColumnCursor, ScrollView, can_focus=True
scroll_x: int = 0) -> None:
"""Move the cursor/scroll on the already-loaded text (no re-highlight).
Used to jump to a target inside the function already displayed, e.g. an
- xref/goto that resolves to this same function."""
+ xref/goto that resolves to this same function.
+
+ All scrolling routes through ``_apply_scroll`` (which re-applies after the
+ next refresh with ``layout=True``): unlike the reload path there's no new
+ layout pass to force a repaint, so a plain ``scroll_to`` would leave a
+ stale frame until the next interaction.
+ """
total = len(self._strips)
if total == 0:
return
self.cursor = max(0, min(total - 1, cursor))
self.cursor_x = cursor_x
self._clamp_x()
- if scroll_y >= 0:
- self._apply_scroll(min(scroll_y, max(total - 1, 0)), scroll_x)
- else:
- self._scroll_cursor_into_view()
- self._hscroll()
- self.refresh()
+ if scroll_y < 0: # derive a viewport that shows the (line, column)
+ height = self._visible_height()
+ top = round(self.scroll_offset.y)
+ if self.cursor < top:
+ scroll_y = self.cursor
+ elif self.cursor >= top + height:
+ scroll_y = max(self.cursor - height + 1, 0)
+ else:
+ scroll_y = top
+ width = max(self.size.width - self._gutter, 1)
+ sx = round(self.scroll_offset.x)
+ if self.cursor_x < sx:
+ scroll_x = self.cursor_x
+ elif self.cursor_x >= sx + width:
+ scroll_x = self.cursor_x - width + 1
+ else:
+ scroll_x = sx
+ self._apply_scroll(min(max(scroll_y, 0), max(total - 1, 0)), max(scroll_x, 0))
self._after_cursor_move()
def get_loading_widget(self): # type: ignore[override]