| Commit message (Collapse) | Author | Age | Files | Lines |
| ... | |
| |
|
|
|
|
|
|
|
|
|
| |
- ColumnCursor.on_click maps the click to a virtual (line, column) via
event.get_content_offset (handles padding/border) + scroll_offset, then places
the line+column cursor (disasm posts CursorMoved for the status line).
- double-click (event.chain>=2) = place cursor + follow the symbol under it,
identical to selecting it and pressing Enter.
- works in both views (verified: disasm click on 'call sub_XXXX' lands on the
exact token; pseudocode click maps columns with no padding).
- pilot suite 36/36.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
- ColumnCursor mixin for both views: h/l/left/right, w/b word-hops, 0/$ line
ends; block cursor cell + word-under-cursor highlight rendered on the cursor
line (region-refresh so it stays cheap). vertical moves clamp the column.
- follow/xrefs now use the token under the cursor, so a line with multiple refs
follows the one you're pointing at (proven: 'return sub_A(sub_B,..)' -> cursor
on sub_B follows sub_B). decomp matches a decompiler ref by exact name; disasm
resolves the symbol, falling back to the line's from-xref (address column /
hex operands are excluded so the default position still follows the target).
- horizontal scroll follows the cursor in the pseudocode view.
- dropped the f/b paging aliases (b is now word-back).
- pilot suite 34/34.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
- Enter follows the reference under the cursor: disasm uses xref_query(from) to
find the code target; pseudocode matches a decompiler ref name on the line.
Navigates to the containing function at the exact line (address-history push).
- x opens an xrefs popup (XrefsScreen modal): xrefs_to the subject under cursor
(call target if any, else the current item); Enter jumps to a referencing
site, Esc closes.
- domain: Program.function_of (mid-addr -> containing func via lookup_funcs),
xrefs_from/xrefs_to (xref_query), Xref model, DisasmModel ea->index map for
landing on an exact address.
- fix: goto to a mid-function address now lands on the right line (was opening
it as a bogus function start).
- pilot suite 32/32; domain 17/17.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
- 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)
|
| |
|
|
|
|
| |
- 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)
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Measured against libcrypto.so.3 (10,092 funcs, biggest 52,120 insns):
- list_* count cap ~700, disasm max_instructions cap ~500, then SILENT
collapse to 10 (not clamped) -> must clamp client-side (use 500).
- pagination must advance by len(data); next_offset=offset+count skips data.
- disasm offset paging is O(offset): 6ms@0 -> 180ms@50k, no resumable cursor
-> domain layer must cache windows + prefetch + over-fetch.
- include_total scans whole func (~217ms on monster) -> fetch once, cache.
- decompile hard-fails on huge funcs as a soft error (code=null) -> handle.
- idb_open needs a writable path for the .i64.
tests/stress_paging.py: durable paging benchmark harness
docs/PAGING_FINDINGS.md: constraints that drive the paging layer
|
| |
|
|
|
|
|
|
|
|
|
| |
- 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
|