aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--idatui/app.py18
-rw-r--r--tests/test_scenarios.py34
2 files changed, 50 insertions, 2 deletions
diff --git a/idatui/app.py b/idatui/app.py
index 0012e32..f79d04d 100644
--- a/idatui/app.py
+++ b/idatui/app.py
@@ -1449,8 +1449,13 @@ class DecompView(SearchMixin, NavMixin, ColumnCursor, ScrollView, can_focus=True
Binding("ctrl+u", "half_page(-1)", "½↑", show=False),
Binding("pagedown", "page(1)", "PgDn", show=False),
Binding("pageup", "page(-1)", "PgUp", show=False),
- Binding("home", "goto_top", "Top", show=False),
- Binding("G,end", "goto_bottom", "Bottom", show=False),
+ # Home/End move the cursor along the line (as in the listing); top and
+ # bottom of the function move to the ctrl+ pair (G still works too).
+ Binding("home", "col_home", "bol", show=False),
+ Binding("shift+home", "col_code_home", "code start", show=False),
+ Binding("end", "col_end", "eol", show=False),
+ Binding("ctrl+home", "goto_top", "Top", show=False),
+ Binding("G,ctrl+end", "goto_bottom", "Bottom", show=False),
*SearchMixin.SEARCH_BINDINGS,
*NavMixin.NAV_BINDINGS,
*ColumnCursor.COL_BINDINGS,
@@ -1649,6 +1654,15 @@ class DecompView(SearchMixin, NavMixin, ColumnCursor, ScrollView, can_focus=True
if round(old_value) != round(new_value):
self.post_message(DecompView.Scrolled())
+ def action_col_code_home(self) -> None:
+ """shift+home: first non-blank column — past the C indentation, the
+ pseudocode analogue of the listing's skip-the-address-gutter."""
+ text = self._line_plain(self.cursor) or ""
+ self.cursor_x = len(text) - len(text.lstrip()) if text.strip() else 0
+ self._hscroll()
+ _refresh_lines(self, self.cursor)
+ self._refresh_hl()
+
def line_for_ea(self, ea: int) -> int | None:
"""The pseudocode line whose marker ea is the largest <= ``ea`` (the C
line that best covers an instruction address)."""
diff --git a/tests/test_scenarios.py b/tests/test_scenarios.py
index b6ed230..1a55f23 100644
--- a/tests/test_scenarios.py
+++ b/tests/test_scenarios.py
@@ -853,6 +853,40 @@ async def s_view_toggle(c: Ctx):
c.check("pseudocode has a numbered gutter (line 1 first)",
dec._gutter > 0 and row0[:dec._gutter].strip() == "1",
f"gutter={dec._gutter} row0={row0[:10]!r}")
+ # Home/End move along the line here too (they used to scroll to top/bottom).
+ line = next((i for i, t in enumerate(dec._texts)
+ if t.startswith(" ") and t.strip()), None)
+ if line is not None:
+ text = dec._texts[line]
+ dec.focus()
+ dec.cursor, dec.cursor_x = line, 0
+ dec.refresh()
+ await c.pause(0.05)
+ top = round(dec.scroll_offset.y)
+ await c.press("end")
+ await c.pause(0.05)
+ c.check("<end> in pseudocode goes to end-of-line, not the bottom",
+ dec.cursor == line and dec.cursor_x == max(len(text) - 1, 0)
+ and round(dec.scroll_offset.y) == top,
+ f"line={dec.cursor} col={dec.cursor_x} len={len(text)}")
+ await c.press("home")
+ await c.pause(0.05)
+ c.check("<home> in pseudocode goes to start-of-line",
+ dec.cursor == line and dec.cursor_x == 0,
+ f"line={dec.cursor} col={dec.cursor_x}")
+ await c.press("shift+home")
+ await c.pause(0.05)
+ c.check("<shift+home> skips the indentation",
+ dec.cursor_x == len(text) - len(text.lstrip()),
+ f"col={dec.cursor_x} indent={len(text) - len(text.lstrip())}")
+ await c.press("ctrl+end")
+ await c.pause(0.1)
+ c.check("<ctrl+end> still goes to the bottom",
+ dec.cursor >= dec.total - 1, f"{dec.cursor}/{dec.total}")
+ await c.press("ctrl+home")
+ await c.pause(0.1)
+ c.check("<ctrl+home> still goes to the top", dec.cursor == 0,
+ f"{dec.cursor}")
@scenario("search")