aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorblasty <blasty@local>2026-07-25 14:55:25 +0200
committerblasty <blasty@local>2026-07-25 14:55:25 +0200
commitfab16344994fd273396be25be75b6364ea9877a8 (patch)
tree6264670a370518f927d4bcee01964064db737456
parentfix: project-wide search crashed on a name shared by two binaries (diff)
downloadida-tui-fab16344994fd273396be25be75b6364ea9877a8.tar.gz
ida-tui-fab16344994fd273396be25be75b6364ea9877a8.tar.xz
ida-tui-fab16344994fd273396be25be75b6364ea9877a8.zip
palette: cap project-scope results at 60 (mitigation for sluggish arrowing)
Reported: arrowing through project-scope results is sluggish, and switching back to this-binary scope is instantly fast again. Everything I can measure here says the palette is fine, so this is a hypothesis-driven mitigation, not a proven fix. Ruled out by measurement: * per-option render + highlight move: 0.80 ms (local) vs 0.82 ms (project) at 200 options each — identical and fast * wrapping from the binary-name prefix: none, 1.00 lines/option in both * option count driving render cost: flat from 20 to 400 options * an app-level OptionHighlighted handler doing work: none exists * scroll animation: Textual's scroll_to_highlight already passes animate=False, immediate=True The synthetic pilot harness has a ~62 ms floor per press (Textual awaits a full refresh cycle), so end-to-end key timing there is meaningless. What remains plausible is simply LIST LENGTH in a real terminal: project scope saturates its cap because every binary contributes, while a local filter usually returns a handful — which matches the symptom exactly. So project scope now caps at 60 rather than 200 (and fetches 3x that from the index). 60 is plenty for a search palette; typing narrows further.
-rw-r--r--idatui/app.py11
1 files changed, 8 insertions, 3 deletions
diff --git a/idatui/app.py b/idatui/app.py
index c3578da..f9ddfea 100644
--- a/idatui/app.py
+++ b/idatui/app.py
@@ -2069,6 +2069,10 @@ class SymbolPalette(ModalScreen):
Binding("f2", "scope", "This binary / whole project", show=False),
]
LIMIT = 200
+ #: Project scope nearly always saturates its cap (every binary contributes),
+ #: where a local filter usually returns a handful. Keep the list short so
+ #: arrowing through it stays snappy; narrow further by typing.
+ PROJECT_LIMIT = 60
def __init__(self, funcs: list[Func], index=None, binary=None) -> None:
super().__init__()
@@ -2114,7 +2118,7 @@ class SymbolPalette(ModalScreen):
# full fuzzy pass, and fetching 3x the display limit rather than 10x
# keeps the per-keystroke work down on a big project.
hits = self._index.search(query, kind=KIND_FUNC,
- limit=self.LIMIT * 3) if query else []
+ limit=self.PROJECT_LIMIT * 3) if query else []
q = query.lower()
scored = []
for h in hits:
@@ -2126,7 +2130,7 @@ class SymbolPalette(ModalScreen):
scored.sort(key=lambda t: (t[0], t[1], t[2], t[3].binary, t[3].addr))
rows = [(h.binary, h.addr, h.text,
tuple(range(at, at + len(q))) if at < (1 << 30) else ())
- for at, _, _, h in scored[:self.LIMIT]]
+ for at, _, _, h in scored[:self.PROJECT_LIMIT]]
elif query:
scored = []
for f in self._funcs:
@@ -2156,7 +2160,8 @@ class SymbolPalette(ModalScreen):
if self._results:
ol.highlighted = 0
scope = "project" if self._project_scope else "this binary"
- more = "+" if len(self._results) == self.LIMIT else ""
+ cap = self.PROJECT_LIMIT if self._project_scope else self.LIMIT
+ more = "+" if len(self._results) == cap else ""
hint = " (F2: this binary)" if self._project_scope else (
" (F2: whole project)" if self._index is not None else "")
self.query_one("#pal-title", Static).update(