Skip to content

Extract Log Search execution into LogSearcher #652

Merged
Hirogen merged 9 commits into
Developmentfrom
feature/log-search-extraction
Jul 9, 2026
Merged

Extract Log Search execution into LogSearcher #652
Hirogen merged 9 commits into
Developmentfrom
feature/log-search-extraction

Conversation

@Hirogen

@Hirogen Hirogen commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Summary

Extracts the Log Window's search implementation (Ctrl+F / F3 / Shift+F3) into a pure, table-tested LogSearcher module in LogExpert.Core — Candidate 2 of the LogWindow architecture review, following the pattern proven by the Filter Spread extraction pure module in Core, table-tested in LogExpert.Tests, control delegates.

The ~113-line private search loop in LogWindow.cs is gone. LogSearcher.Find owns direction resolution, the wrap-around-once rule, and regex/ordinal matching over ILogfileReader; the Log Window keeps only the UI half — status-line narration, progress bar, grid scrolling, and cancellation wiring.

Commits / tickets

  1. LogSearcher module + 34 testsFind(params, reader, token, progress) returns a SearchResult (Found / NotFound / Cancelled / InvalidPattern + wrapped flag). Direction truth table, wrap semantics, case modes, regex, cancellation ordering and progress cadence all pinned against a mocked reader — no WinForms needed.
  2. Log Window migration — search runs through the module with a CancellationToken (ESC, reload, dead-file, and close all cancel); wrap notices and progress keep their original mid-search timing; the dead "not found" MessageBox is deleted. The shared _shouldCancel flag is untouched for the filter loops.
  3. One direction rule — the (IsForward || IsFindNext) && !IsShiftF3Pressed predicate, previously hand-copied in three places, now exists only in LogSearcher.ResolveDirection.
  4. Invalid regex narration — a bad pattern reaching a search (e.g. F3 with persisted params) shows the existing localized status-line error instead of dying silently on the background task.
  5. Contract & glossary — CONTEXT.md gains the canonical Log Search entry (disambiguated from filtering / FilterSearch); the contract sweep closed one pinning gap (backward wrapped-full-circle).

Plus one drive-by fix found during manual smoke: the Regex Helper dialog crashed on every OK click (Items modified while DataSource was bound) — fixed and regression-tested with three tests replaying the Load→OK sequence.

User-visible changes

  • Regex search compiles the pattern once per search instead of once per line (perf).
  • Invalid regex patterns report an error instead of hanging on "Searching…".
  • A running search is immune to F3 mutating the shared search params mid-flight.
  • Regex Helper dialog OK no longer throws ArgumentException.

Everything else is behaviour-preserving, pinned by tests (including cancellation ordering and wrap-notice timing).

Testing

  • 34 new LogSearcherTests + 3 RegexHelperDialogTests; full solution suite green (1,394 passed, 0 failed).
  • Manual smoke on a large file: ESC cancels a running search; F3/Shift+F3 work through the whole file and across the wrap; not-found narrates correctly.

Hirogen and others added 9 commits July 9, 2026 21:50
Extracts the Log Window search loop semantics into a table-tested
module per docs/specs/log-search-extraction.md: direction resolution
(forward/find-next/Shift+F3), wrap-around-once, regex/ordinal matching
over ILogfileReader, progress + wrap events via IProgress, cancellation
via CancellationToken. Regex compiles once per search and a bad pattern
returns InvalidPattern before any line is read; Find operates on a
snapshot so mutating the shared SearchParams cannot affect a run.

Lands unused; call-site migration is ticket 2.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Deletes the private ~113-line Search loop; StartSearch runs
LogSearcher.Find on the background task with a CancellationToken and a
synchronous progress relay. The control keeps the UI half: wrap notices
and not-found on the status line, progress bar updates, SelectLine on a
hit. ESC cancels the search token (the shared _shouldCancel flag stays
for the filter loops and SelectLine gate, exactly as before); reload,
dead-file, and close teardown cancel a running search too. The
unreachable not-found MessageBox in SelectLine is removed.

The token source is replaced only after a consumed cancellation,
mirroring the old _shouldCancel=false reset so a superseding search
keeps racing and one ESC cancels every running search.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…cket 3)

The start-line computation now calls LogSearcher.ResolveDirection, so
the (IsForward || IsFindNext) && !IsShiftF3Pressed predicate exists in
exactly one place. Truth-table equivalent to the deleted expression.
The snapshot half of the ticket was already satisfied in the module:
Find copies its params at entry, pinned by
Find_MutatingSearchParamsMidRun_DoesNotAffectTheRunningSearch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t 4)

An invalid regex reaching a search (F3 with SearchParams restored from
persisted settings, or programmatic callers - the Search dialog
pre-validates its own input) now shows the existing localized
invalid-regular-expression status-line error, the same resource the
filter path uses, instead of dying silently on the background task.
Menus and progress bar were already restored for every outcome by
SearchComplete.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CONTEXT.md gains the canonical Log Search entry - the Ctrl+F/F3 feature
owned by LogSearcher - with the Avoid note disambiguating it from
filtering (FilterSearch belongs to the filter path). The sweep found
one pinning gap: backward wrapped-full-circle NotFound was untested;
added Find_BackwardWrappedFullCircleWithoutMatch_ReturnsNotFoundWrapped.

Closes the Log Search extraction epic (tickets 1-5).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Root cause: LoadHistory (run on dialog Load) binds both combo boxes via
DataSource, and OnButtonOkClick then modified ComboBox.Items directly -
which WinForms forbids while a DataSource is set. Every OK click on a
shown dialog threw. The handler now mutates only the bound history
lists and never touches Items; this also removes a latent value-vs-
index bug (Items.Remove(Count - 1) removed the value, not the last
entry). Regression-tested by replaying the Load-then-OK sequence.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Hirogen Hirogen merged commit ccf0743 into Development Jul 9, 2026
1 check passed
@Hirogen Hirogen deleted the feature/log-search-extraction branch July 9, 2026 21:09
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.

1 participant