| Commit message (Collapse) | Author | Age | Files | Lines |
| ... | |
| |
|
|
|
|
|
|
|
|
|
|
| |
- Incremental client-side filter over the cached function list (no per-keystroke
server round-trips): narrows + highlights the matched substring in each name
as you type, debounced ~80ms. Substring or glob (*/?), smartcase.
- Clear the filter: Esc in the filter box, OR Esc on the focused list, OR empty
Enter. Status shows 'filter <q>: N/total'.
- streaming pauses while a filter is active; re-applied over the full set once
load completes. Row selection resolves the name from the index (cells can now
be styled Text).
- FunctionIndex.all_loaded(); pilot suite 27/27.
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
- Fix overlap: #search, #status and Footer all docked bottom, and Input defaults
to a 3-row bordered widget squeezed into 1 row. Now the search bar is a clean
1-row (border:none) that REPLACES the status line while active (status hidden),
so the query is always visible.
- Incremental search: highlight all matches + preview-jump from the origin on
every keystroke (Input.Changed), not just on submit. Disasm indexes once then
each keystroke is instant. Enter keeps; empty Enter repeats last; Esc cancels
and restores the origin cursor + status line.
- pilot suite 23/23 (adds: bar-visible/no-overlap, incremental-before-Enter,
Esc-cancel).
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
- SearchMixin shared by both views: '/' search forward, '?' backward, empty query
repeats last; n/N next/prev. Smartcase. All matches highlighted (substring
overlay), cursor lands on current match; status shows k/N.
- disasm indexes line text lazily in a worker (handles huge funcs); pseudocode
has text in hand. Substring highlight via crop/join Strip overlay.
- app: bottom search prompt, SearchRequested routing; '/' is context-sensitive
(filters when the function table is focused, searches in code views).
- gotcha fixed: Textual only merges BINDINGS from DOMNode subclasses, so mixin
bindings are ignored -> each view lists SEARCH_BINDINGS explicitly.
- pilot suite 20/20 (adds search finds/hl/repeat-fwd/repeat-back).
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Highlighting: Textual has NO C/C++ tree-sitter grammar (and the syntax extra
wasn't installed), so language='cpp' was a silent no-op. Replace TextArea with a
virtualized, Pygments-highlighted ScrollView:
- idatui/highlight.py: CLexer -> Rich styles, per-line Segment lists
- DecompView: lines highlighted ONCE at load, cached as Strips; O(1) scroll
Jank: profiling showed our code is ~3.6ms/move (render_line 0.018ms) -- the cost
was terminal repaint volume, since every cursor move refreshed the whole ~43-line
viewport. Now:
- cursor reactive repaint=False (no implicit full refresh)
- region-limited refresh: in-place moves repaint only the 2 changed rows
(measured 43 -> 2 render_line calls/move), full refresh only on scroll
Applied to both DisasmView and DecompView.
pilot suite 15/15 (adds highlighting assertion).
|
| |
|
|
|
|
|
|
|
|
|
| |
- domain.decompile now fetches the FULL body: server caps responses at 50KB and
clips strings to 1KB, but caches the full output and exposes it at
_meta.ida_mcp.download_url -> we GET that so pseudocode is complete
(main: 43257 chars, not a 1KB stub). include_addresses gives per-line /*0xEA*/.
- DecompView (read-only TextArea, cpp highlighting best-effort, line numbers)
- Tab and Shift+Tab toggle the code pane; lazy decompile in a worker, cached;
hard-failures ('decompilation failed') shown gracefully with disasm fallback
- pilot suite 14/14 (adds tab->pseudocode, full-body, shift+tab->disasm)
|
| |
|
|
|
|
|
| |
- app: when open_path is set, idb_open (idle_ttl=1e9, long timeout) and pin the
returned session; clean status on failure (e.g. non-writable dir)
- tui.py: --open flag + usage docs (attach vs open)
- verified: --open targets/cat -> new session, 161 funcs loaded
|
| |
|
|
|
|
| |
- action_toggle_functions: collapse/restore left pane, disasm takes full width;
focus follows (disasm when hidden, table when shown)
- pilot test covers the toggle (11/11)
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
- idatui/app.py: keyboard-first Textual app
* FunctionsPanel: lazy-streamed function list (DataTable) with glob filter
* DisasmView: line-VIRTUALIZED ScrollView -- paints from domain block cache,
background-fetches misses, prefetches neighbors. Proven: 52,120-insn func,
jump-to-bottom in 0.31s with only ~6 blocks (~1.5k lines) ever materialized.
* address-history nav stack (Enter follow / Esc back); goto by name or 0xADDR
* bump_idle_ttl + KeepAlive on startup so the session never idles out
* all MCP work on Textual worker threads; UI never blocks
- idatui/tui.py: launcher (python -m idatui.tui / console script 'idatui')
- tests/test_tui.py: headless Pilot test, 9/9 (boot, load 10k funcs, open,
scroll, bg-cache, goto-bottom, filter)
- pyproject: textual extra + idatui script entry
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Root cause: idalib workers run an idle watchdog (worker_lifecycle.py) that
self-exits after idle_ttl_sec (default 600s) of no requests -- deliberate
resource hygiene for a shared/ephemeral supervisor, wrong for an interactive
TUI. CLI-opened startup binary always gets 600s (no flag).
Fix (both verified against a 12s-TTL session):
- IDAClient.bump_idle_ttl(1e9): re-open (idempotent) re-applies TTL, no upper
cap -> effectively immortal. Primary fix, call once at startup.
- IDAClient.keepalive()/KeepAlive: background server_health heartbeat resets the
watchdog; covers adopted sessions too. Safety net.
tests/test_keepalive.py: 4/4 (heartbeat + bump both keep a short-TTL worker
alive past 2x TTL). docs updated with why + fix.
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
- Program: model registry + small prefetch pool; cache invalidation hooks
- FunctionIndex: lazy pagination (advance by len, clamp page=500), filter globs,
viewport windows; full 10,092-func enumerate matches survey in ~3.1s
- DisasmModel: block-cached windowed disasm (256/block), forward+back prefetch,
cached instruction totals; deep window revisit 196ms -> 0ms (cache proven)
- Decompilation: truncation-aware, hard-failure surfaced as data (monster funcs)
- resolve(): int/hex/symbol via lookup_funcs (string-array query shape)
- tests/test_domain.py: 17/17 on both ls (~290 funcs) and libcrypto (10k+52k-insn)
- smoke_client no longer assumes 'main' exists (works on libraries)
- doc: idle_ttl worker self-exit finding ("not reachable" needs re-open)
|
| |
|
|
|
|
|
|
|
|
|
| |
- auto-recover stale db ("Session not found" after server restart): drop pin,
re-resolve sole session, retry once. Only when db was auto-injected; never
silently switches an explicitly-pinned db (auto_recover_session gate).
- tests/serverctl.sh: start/stop/kill9/ready control over spawn.sh
- tests/stress_client.py: 6 adversarial scenarios, 24 assertions:
dead-port fail-fast, tight-timeout recovery, worker crash, full restart
(naive auto-recovery + no-autoswitch), 300/16 concurrency, kill-under-churn.
All clean: no hangs, no deadlocks, no non-IDAError leaks.
|
|
|
thread-safe
- IDAClient: single handshake, ~7ms warm calls (vs ~60ms cold CLI)
- keep-alive connection pool over http.client; concurrent-safe (40 threads OK)
- grounded error taxonomy: IDAToolError on isError, soft per-item errors as data
- session-expiry recovery (404 -> re-handshake -> retry), bounded transport retries
- auto db-injection + resolve_db (ignores stale empty-id sessions)
- live smoke test: 13/13 pass
|