Skip to content

Extract Marker Bar logic out of the LogWindow.MarkerBar.cs partial #719

Description

@Hirogen

Problem

LogWindow.MarkerBar.cs (about 390 lines, added in #27) is a partial of LogWindow. Splitting the file doesn't shrink the class. Its 20 marker fields sit on LogWindow itself, and any code in the 7,800-line LogWindow.cs can read or change them. LogWindow.cs calls into it from about 26 places. Those fields include:

  • two MarkerIndex instances and the MarkerBar control
  • the timer, the lock, the visibility tuple
  • the generation, revision and content-revision counters, the pending-tail flag, the current frame and the reported errors

The real logic is already in separate classes: MarkerBar draws, and MarkerIndex and MarkerCriteria scan and match. What's left in the partial is coordination: configuring the indexes, the 250 ms timer loop that takes snapshots and renders frames, turning preferences into visibility, the generation checks, error reporting and disposal.

Proposal

Move that coordination into its own class, for example MarkerBarController, which owns the indexes, the timer, the counters and the frame state. LogWindow keeps only the wiring:

  • create it, passing the MarkerBar control and a small host interface or callbacks for:
    • the reader, the file name and the line count
    • whether loading, dead or closing
    • the column-header height and the columnizer columns
    • the current highlight entries and search
    • reporting a status-line error
    • going to a line
  • forward the events it already raises: content changed and tail lines, highlight or search changes, preference changes, DPI changes and disposal. These are the calls to InvalidateMarkerCriteria, MarkMarkerDataChanged, TrackMarkerSearch, ApplyMarkerPreferences and DisposeMarkers.

This matches how the hide-line feature (#338, PR #718) was restructured: VisibleRows and HiddenLinesBar are real classes, and LogWindow only keeps the wiring.

Acceptance criteria

  • LogWindow.MarkerBar.cs is removed, and no marker state fields are left on LogWindow.
  • Marker behaviour is unchanged: MarkerWindowTests and the Core marker tests pass without changing what they check.
  • The controller's snapshot and generation handling can be unit-tested without a real Log Window where that's practical.

Out of scope

Any change to marker behaviour, colours or scanning.

Activity

  1. self-assigned this
    on Sep 27, 2026
  2. added this to the Release 1.50.0 milestone on Sep 27, 2026
  3. Hirogen commented on Oct 2, 2026

    @Hirogen
    MemberAuthor

    Category: enhancement (behaviour-preserving refactor)
    Summary: Move the Marker Bar coordination out of the LogWindow partial into a MarkerBarController class

    Current behavior:
    The LogWindow.MarkerBar.cs partial holds ~20 marker state fields directly on LogWindow (two MarkerIndexes, the MarkerBar control, the 250 ms timer, _markerStateLock, visibility tuple, generation/revision/content-revision counters, tail-pending counter, current MarkerFrame, reported errors, rendering/disposed flags) plus the coordination logic: InitializeMarkerBar, ApplyMarkerPreferences, InvalidateMarkerCriteria, MarkMarkerDataChanged, TrackMarkerSearch, clear-search handling, ConfigureMarkerIndexes, CaptureMarkerColumns, the timer tick (snapshot → bucket aggregation → generation-checked render), error reporting, go-to-line on click, and disposal. LogWindow.cs calls into it from ~26 places, and also mutates _markerTailPending and _markerContentRevision directly (partly from the reader/tail thread) and calls _markerBar.ClearBuckets() directly.

    Desired behavior:
    A MarkerBarController class (same folder as VisibleRows / HiddenLinesBar, following their pattern: constructor takes the control(s) plus a small IMarkerBarHost interface) owns all of that state and logic, including the private MarkerScanSource, MarkerFrame and MarkerLineSource types. LogWindow keeps only wiring: create the controller, implement the host interface, forward events, dispose it. Marker behaviour is byte-for-byte unchanged, including all threading and generation guarantees (invalidation may still be called from the reader/tail worker; snapshot + generation captured atomically under the lock; pixel aggregation off the UI thread; bookmarks captured on the UI thread; stale frames discarded).

    Key interfaces:

    • IMarkerBarHost — what the controller reads from the window. At least: current reader, file name, loading/dead/closing state, column-header height + visibility, grid back/fore colour, DPI, current columnizer, config dir, highlight entries (taken under the window's highlight-group lock), bookmark line numbers, filter hits (copied under the filter-hit lock), the line-visibility map's line count (for click validation), StatusLineError, and RequestGotoLine. Exact shape is the implementer's choice; keep it minimal.
    • Controller operations replacing today's calls: invalidate criteria (highlights / search / all, with the bar clear folded in where LogWindow currently pairs them), mark data changed, mark content changed, begin/end tail (replacing direct _markerTailPending increments/decrements), track search, apply preferences, dispose.
    • The timer tick delegates to a Task-returning update method so tests can drive one update without the WinForms timer.
    • The MarkerBar control keeps Name = "markerBar" and its place in tableLayoutPanel1; LogWindow keeps the table-layout column sizing and its OnDpiChangedAfterParent override (forwarding to the controller).

    Acceptance criteria:

    • LogWindow.MarkerBar.cs is deleted; no marker state fields remain on LogWindow (one controller field is fine)
    • LogWindow.cs no longer touches marker counters, the MarkerBar control's buckets, or MarkerIndex directly
    • MarkerWindowTests, MarkerBarTests, SettingsDialogMarkerBarTests, and the Core Marker/* tests pass with no change to what they assert
    • New unit tests drive the controller through a fake host (no real Log Window) covering at least: a frame from a stale generation is not rendered after invalidation; nothing renders while a tail is pending; a discovery error is reported to the host once per snapshot
    • The behaviour-preservation claim is stated explicitly in the PR, with any intentional deviation flagged (there should be none)
    • Full test suite passes

    Out of scope:

    • Any change to marker behaviour, colours, categories, scanning, or MarkerIndex / MarkerCriteria / MarkerBucket / MarkerBar
    • Other LogWindow decomposition work
    • Fixing the pre-existing flaky LineVisibilityTrackerTests.Dispose_CancelsAPendingScan_WithoutPublishing
  4. added a commit that references this issue on Oct 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementthis will make things better

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions