Skip to content

Fix #18: replace 'Continue?' popups with wait-box + Cancel#24

Merged
mahmoudimus merged 8 commits into
mainfrom
fix/issue-18-cancelable-search
May 19, 2026
Merged

Fix #18: replace 'Continue?' popups with wait-box + Cancel#24
mahmoudimus merged 8 commits into
mainfrom
fix/issue-18-cancelable-search

Conversation

@mahmoudimus

@mahmoudimus mahmoudimus commented May 19, 2026

Copy link
Copy Markdown
Owner

Closes #18.

Background

The original cancelable-search work I shipped in PR #20 gave users an exponential-backoff modal "Continue?" prompt every 10s/20s/40s of generation. @bukowa reported in the issue that this was "ultra confusing" and clarified the actual ask:

My expectation wasn't an automated timeout or recurring prompts, I simply want a 'Cancel' button that allows the user to stop the search manually if they decide it's taking too long.

This PR delivers that. I kept the exponential-backoff path around as an opt-in for anyone who liked it.

Four bugs I fixed

  1. SigMakerConfig defaults made popups loud. I had shipped enable_continue_prompt=True and prompt_interval=10 as defaults, so popups fired 10s into every generation.
  2. No wait-box for single/range generation. I never wrapped SignatureMaker.make_signature in ProgressDialog, so there was no Cancel button in the CREATE_UNIQUE and COPY_RANGE modes. The XREF and Search paths were already wrapped.
  3. CheckContinuePrompt.should_cancel ignored the wait-box flag. It only checked an internal _user_canceled flag and never polled idaapi_user_canceled(), so even if a wait box had been shown, hitting Cancel wouldn't have propagated.
  4. InstructionWalker.__next__ swallowed cancellations. On cancel it raised StopIteration("Aborted by user"), which the generators above it read as "ran out of instructions" and reported as the confusing "Signature not unique" error instead of a clean cancellation.

Changes

Commit What I did
refactor: introduce Action IntEnum for SigMakerPlugin dispatch Replaced the 0/1/2/3 magic in SigMakerPlugin.run with Action.CREATE_UNIQUE / FIND_XREF / COPY_RANGE / SEARCH.
fix(walker): raise UserCanceledError on cancel instead of StopIteration Cancellation now propagates cleanly to the existing except UserCanceledError handler.
fix(progress): poll idaapi_user_canceled in CheckContinuePrompt.should_cancel Wait-box Cancel now flows through the progress reporter regardless of prompt state.
feat: flip SigMakerConfig defaults to off-by-default prompts (-1 sentinel) enable_continue_prompt=False, prompt_interval=-1. I picked -1 as the canonical "off" sentinel.
feat(form): default 'Enable continue prompt' OFF; clarify tooltip SignatureMakerForm.cGroupOptions.value: 13 to 5 (drops bit 8).
feat(form): expose prompt_interval in ConfigureOptionsForm (-1 disables) Added a numeric field in Other options so users can opt in via the UI.
feat: wrap single/range make_signature in ProgressDialog for cancel CREATE_UNIQUE and COPY_RANGE branches now wrap in the wait box. FIND_XREF already wraps internally so I left it alone.

Net behavior

  • Default. Wait-box with a Cancel button during signature generation. No popups. Cancel produces a clean Operation cancelled by user log with no traceback.
  • Opt-in. Tick "Enable continue prompt (opt-in)" in the main form, and optionally set "Prompt interval (seconds, -1 disables)" via Other options.

Verification

Suite Baseline After
Host unit (tests/unit_test_sigmaker.py) 111 OK 118 OK (+7 new)
Docker idapro-tests (9.0/9.1) 127 OK 134 OK
Docker idapro-tests-9.2 127 OK 134 OK

Zero regressions. The new test coverage I added:

  • TestActionEnum. Locks Action values to the radio order.
  • TestInstructionWalkerCancellation. Cancel raises UserCanceledError, not StopIteration.
  • TestProgressReporter.test_should_cancel_polls_idaapi_user_canceled + test_should_cancel_returns_false_when_no_cancel. The wait-box poll fires regardless of prompt state, and once flagged it stays canceled.
  • TestSigMakerConfigDefaults. Defaults are enable_continue_prompt=False and prompt_interval=-1.

Notes

  • Single-file plugin invariant preserved. All production-code changes live in src/sigmaker/__init__.py.
  • No new dependencies.
  • ExponentialBackoffTimer and its tests are untouched. The opt-in path is fully intact.

Replace the bare 0/1/2/3 magic numbers in SigMakerPlugin.run with
a named IntEnum. Values still mirror the rAction radio-group order
so the form mapping is unchanged. Adds TestActionEnum to lock the
mapping against accidental reordering.
Previously, InstructionWalker.__next__ raised StopIteration when the
user pressed Cancel on the wait box. The generators above caught it
as 'iteration finished naturally' and surfaced the confusing
'Signature not unique' error instead of a clean cancellation.

UserCanceledError propagates out of make_signature() and is caught
by SigMakerPlugin.run's existing handler, which logs 'Operation
cancelled by user'.
…d_cancel

Previously, should_cancel() only checked its internal _user_canceled
flag, which is set when the modal 'Continue?' prompt is dismissed.
This meant that pressing Cancel on IDA's wait box was a no-op for
the progress reporter even though the underlying flag was set.

Add a poll of idaapi_user_canceled() so cancellation works regardless
of whether the exponential continue prompts are enabled.

Also tighten the TestProgressReporter fixture: with idaapi mocked at
module level, idaapi_user_canceled (a top-level wrapper) returns a
truthy MagicMock instance by default, which fires the new wait-box
poll on every call. Reset it explicitly in setUp.
…inel)

Per issue #18 the original requester wanted a Cancel button, not
recurring 'Continue?' popups. Default enable_continue_prompt to
False and prompt_interval to -1 (the documented sentinel for 'no
prompts'). Users can still opt in via the form checkbox.

_should_prompt already guards on prompt_interval > 0, so both -1
and 0 disable prompting; -1 is chosen as the canonical sentinel
for documentation clarity.
Flip the cGroupOptions default bitmask 13 -> 5 to drop bit 8
(enable_continue_prompt). Wait-box Cancel covers the cancellation
need for the default user; the checkbox stays as an opt-in for
those who want the periodic 'Continue?' dialog. Tooltip updated
to make the trade-off explicit.
Add a numeric field to 'Other options...' so users can configure the
wait time before the optional 'Continue?' popup fires, or set -1 to
disable the popup entirely (default). Mirrors the existing pattern
where ConfigureOptionsForm reads from and writes to SigMakerConfig
class attributes.
The XREF and Search code paths already show a wait box with a Cancel
button. The CREATE_UNIQUE and COPY_RANGE paths did not, so the user
had no way to interrupt a long-running unique-signature generation
short of the modal continue prompt (now opt-in).

Wrap both branches with the existing ProgressDialog context manager.
The cancellation path now reads as: wait-box Cancel ->
idaapi_user_canceled() -> InstructionWalker raises UserCanceledError
-> caught by SigMakerPlugin.run's existing handler.

display() stays outside the with-block so the wait box closes before
any console/clipboard output. FIND_XREF already wraps internally and
is intentionally left alone.
@mahmoudimus mahmoudimus force-pushed the fix/issue-18-cancelable-search branch from 5143bc8 to 8d319f6 Compare May 19, 2026 19:07
…parser

bukowa reported on issue #18 that the form failed with 'form: error at %/'.
IDA Form treats ':' as the label/control delimiter; a colon inside the
tooltip (between the # delimiters) confuses the parser and the failure
surfaces later when the group terminator %/ is reached.

The tooltip I added in #24 was the only one in the file with a colon.
Rewrite it without one. Same fix will need to apply to the issue-22
branch's tooltip too.
@mahmoudimus mahmoudimus merged commit a38cf3d into main May 19, 2026
2 checks passed
@mahmoudimus mahmoudimus deleted the fix/issue-18-cancelable-search branch May 19, 2026 21:02
mahmoudimus added a commit that referenced this pull request May 27, 2026
* docs: add CHANGELOG.md covering 1.7.0 (#24, #25)

Keep a Changelog format. Documents the wait-box cancel UX from
issue #18 (PR #24) and the opt-in partial-on-cancel output from
issue #22 (PR #25), including the user-visible behavior changes,
the new opt-in form controls, and the bug fixes that were folded
in along the way.

* fix: use US spelling in user-facing cancel message

The output line on UserCanceledError was 'Operation cancelled by
user' (UK), the only UK-spelled user-visible string left in the
plugin. Align with the rest of the codebase which uses 'canceled'
throughout (per the comment at line 87).

* chore: bump version to 1.7.0

Cuts the 1.7.0 release covering the wait-box cancel UX (#24) and
opt-in partial-on-cancel output (#25). See CHANGELOG.md for the
full set of user-visible and internal changes.
mahmoudimus added a commit that referenced this pull request May 27, 2026
Brings in PR #24 (issue #18 cancel), PR #25 (issue #22 partial on
cancel), and PR #28 (1.7.0 release). The auto-merge handled
src/sigmaker/__init__.py cleanly; tests/unit_test_sigmaker.py had
overlapping test-class insertions at the same anchor, resolved by
taking main's test file as the base and appending PR #26's three
new test classes (TestMinimalFunctionSignatureGenerator,
TestActionEnumAddsFunctionSig, TestGeneratedSignatureOrdering)
before the if __name__ block.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ability to cancel search.

1 participant