aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorblasty <blasty@local>2026-07-09 15:19:48 +0200
committerblasty <blasty@local>2026-07-09 15:19:48 +0200
commit54e83ebad8ae06d5fa8411e1ac907e1512c69ac8 (patch)
tree047a945053bb75c5ad50e643b7ec9d3d062bd2d5
parentpreserve scroll offset in jump history (both views) (diff)
downloadida-tui-54e83ebad8ae06d5fa8411e1ac907e1512c69ac8.tar.gz
ida-tui-54e83ebad8ae06d5fa8411e1ac907e1512c69ac8.tar.xz
ida-tui-54e83ebad8ae06d5fa8411e1ac907e1512c69ac8.zip
search: land the cursor on the match's starting column
_goto_line now sets cursor_x to the first match range's start on the target line (and _hscroll's it into view for the pseudocode view), so jumping to a search hit positions the cursor exactly on the match, not the old column. Search cancel/empty also restore the origin column. pilot suite 47/47.
-rw-r--r--idatui/app.py8
-rw-r--r--tests/test_tui.py3
2 files changed, 11 insertions, 0 deletions
diff --git a/idatui/app.py b/idatui/app.py
index d68e827..497f0ac 100644
--- a/idatui/app.py
+++ b/idatui/app.py
@@ -346,6 +346,7 @@ class SearchMixin:
# --- driven by the app's prompt (incremental / as-you-type) ---
def search_begin(self, direction: int) -> None:
self._search_origin = self.cursor
+ self._search_origin_x = self.cursor_x
self._search_dir = direction
def search_update(self, term: str) -> None:
@@ -355,6 +356,7 @@ class SearchMixin:
self._matches = []
self._ranges = {}
self.cursor = getattr(self, "_search_origin", self.cursor)
+ self.cursor_x = getattr(self, "_search_origin_x", self.cursor_x)
self.refresh()
self._app_status("/")
return
@@ -392,6 +394,7 @@ class SearchMixin:
self._matches = []
self._ranges = {}
self.cursor = getattr(self, "_search_origin", self.cursor)
+ self.cursor_x = getattr(self, "_search_origin_x", self.cursor_x)
self.scroll_to(y=max(self.cursor - self._visible_height() // 2, 0), animate=False)
self.refresh()
@@ -447,7 +450,12 @@ class SearchMixin:
def _goto_line(self, idx: int) -> None:
total = self._search_line_count()
self.cursor = max(0, min(total - 1, idx))
+ # Land the cursor on the start of the (first) match on this line.
+ ranges = self._ranges.get(self.cursor)
+ if ranges:
+ self.cursor_x = ranges[0][0]
self.scroll_to(y=max(self.cursor - self._visible_height() // 2, 0), animate=False)
+ self._hscroll() # bring the match column into horizontal view
self.refresh()
def clear_search(self) -> None:
diff --git a/tests/test_tui.py b/tests/test_tui.py
index 9813c1f..688df57 100644
--- a/tests/test_tui.py
+++ b/tests/test_tui.py
@@ -171,6 +171,9 @@ async def run(db):
check("cursor sits on a match", dis.cursor in dis._matches, f"cursor={dis.cursor}")
check("match substring highlighted",
bool(dis._ranges.get(dis.cursor)), str(dis._ranges.get(dis.cursor)))
+ check("search cursor lands on the match's starting column",
+ dis.cursor_x == dis._ranges[dis.cursor][0][0],
+ f"cursor_x={dis.cursor_x} ranges={dis._ranges.get(dis.cursor)}")
prev = dis.cursor
await pilot.press("slash")
await pilot.pause(0.1)