aboutsummaryrefslogtreecommitdiffstats
path: root/idatui
diff options
context:
space:
mode:
authorblasty <blasty@local>2026-07-23 20:03:39 +0200
committerblasty <blasty@local>2026-07-23 20:03:39 +0200
commit4dbb9de9117a85b48f16efbf97477fc58b8283fe (patch)
tree255fc47da48ff8c33d15cc98d7ea7da09c582081 /idatui
parentunify: the continuous listing is the one code view; deprecate DisasmView (diff)
downloadida-tui-4dbb9de9117a85b48f16efbf97477fc58b8283fe.tar.gz
ida-tui-4dbb9de9117a85b48f16efbf97477fc58b8283fe.tar.xz
ida-tui-4dbb9de9117a85b48f16efbf97477fc58b8283fe.zip
listing: IDA-style function boundary banners in the unified view
Each function gets clear boundary annotations in the continuous listing: a blank + '; ==== S U B R O U T I N E ====' separator and a 'name proc near' header before the entry, and a 'name endp' + rule after the last item. The function name moves to the proc header (stripped from the inline entry instruction). Server: heads gains an annotate flag (default off, so DisasmModel/test_domain stay 1:1 with instructions); when on, _rows_for emits kind=sep/funchdr rows at function starts/ends. ListingModel requests annotate=true and does NOT index banner rows by ea, so goto/xref/follow land on real code. ListingView renders sep (dim) and funchdr (bold gold); new _S_SEP/_S_FUNCHDR styles. Verified: func_banners 5/0; region_define/listing_view/listing_name_addr/ listing_struct_expand/continuous_view/follow_xrefs/disasm_nav/scroll_restore/ paging green. Needs a supervisor restart (heads tool changed).
Diffstat (limited to 'idatui')
-rw-r--r--idatui/app.py8
-rw-r--r--idatui/domain.py8
2 files changed, 14 insertions, 2 deletions
diff --git a/idatui/app.py b/idatui/app.py
index c924fa5..fd8c8c0 100644
--- a/idatui/app.py
+++ b/idatui/app.py
@@ -54,6 +54,8 @@ _S_OPBYTES = Style(color="grey50") # raw opcode bytes column
_S_DATA = Style(color="#c8a15a") # data items in the flat listing (db/dw/strings)
_S_UNK = Style(color="grey54", italic=True) # undefined bytes in the flat listing
_S_MEMBER = Style(color="#9a8a6a") # struct field rows (expanded, indented)
+_S_SEP = Style(color="grey42") # function boundary separators / banners
+_S_FUNCHDR = Style(color="#d7a021", bold=True) # 'name proc near'/'endp' headers
# Tokens that look like identifiers but aren't renamable symbols (so 'n' on them
# in the listing names the address instead of trying to rename the token).
@@ -963,6 +965,8 @@ class ListingView(SearchMixin, NavMixin, ColumnCursor, ScrollView, can_focus=Tru
h = self._head(idx)
if h is None:
return None
+ if h.kind in ("sep", "funchdr"):
+ return h.text
indent = " " if h.kind == "member" else ""
return (f"{h.ea:08X} " + self._op_field(h) + indent
+ self._name_prefix(h) + h.text)
@@ -1092,6 +1096,10 @@ class ListingView(SearchMixin, NavMixin, ColumnCursor, ScrollView, can_focus=Tru
h = model.get(idx)
if h is None:
strip = Strip([Segment(f" {idx:>8} …", _S_DIM)])
+ elif h.kind == "sep":
+ strip = Strip([Segment(h.text, _S_SEP)])
+ elif h.kind == "funchdr":
+ strip = Strip([Segment(h.text, _S_FUNCHDR)])
else:
segs: list[Segment] = [Segment(f"{h.ea:08X} ", _S_ADDR)]
op = self._op_field(h)
diff --git a/idatui/domain.py b/idatui/domain.py
index e392936..7452ffd 100644
--- a/idatui/domain.py
+++ b/idatui/domain.py
@@ -595,7 +595,8 @@ class ListingModel:
if self._done or self._next is None:
return 0
frm = self._next
- payload = self._prog.client.call("heads", addr=hex(frm), count=self.PAGE)
+ payload = self._prog.client.call(
+ "heads", addr=hex(frm), count=self.PAGE, annotate=True)
rows = payload.get("heads", []) if isinstance(payload, dict) else []
cur = payload.get("cursor", {}) if isinstance(payload, dict) else {}
page = []
@@ -608,7 +609,10 @@ class ListingModel:
with self._lock:
base = len(self._heads)
for i, h in enumerate(page):
- self._by_ea.setdefault(h.ea, base + i)
+ # Banner rows (function headers/separators) are display-only;
+ # don't index them so navigation lands on real code/data.
+ if h.kind not in ("sep", "funchdr"):
+ self._by_ea.setdefault(h.ea, base + i)
self._heads.append(h)
nxt = cur.get("next")
if nxt is None: