diff options
| author | blasty <blasty@local> | 2026-07-25 14:55:25 +0200 |
|---|---|---|
| committer | blasty <blasty@local> | 2026-07-25 14:55:25 +0200 |
| commit | fab16344994fd273396be25be75b6364ea9877a8 (patch) | |
| tree | 6264670a370518f927d4bcee01964064db737456 | |
| parent | fix: project-wide search crashed on a name shared by two binaries (diff) | |
| download | ida-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.py | 11 |
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( |
