You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Extract Marker Bar logic out of the LogWindow.MarkerBar.cs partial #719
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.
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
Activity
Hirogen commentedon Oct 2, 2026
Category: enhancement (behaviour-preserving refactor)
Summary: Move the Marker Bar coordination out of the
LogWindowpartial into aMarkerBarControllerclassCurrent behavior:
The
LogWindow.MarkerBar.cspartial holds ~20 marker state fields directly onLogWindow(twoMarkerIndexes, theMarkerBarcontrol, the 250 ms timer,_markerStateLock, visibility tuple, generation/revision/content-revision counters, tail-pending counter, currentMarkerFrame, 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.cscalls into it from ~26 places, and also mutates_markerTailPendingand_markerContentRevisiondirectly (partly from the reader/tail thread) and calls_markerBar.ClearBuckets()directly.Desired behavior:
A
MarkerBarControllerclass (same folder asVisibleRows/HiddenLinesBar, following their pattern: constructor takes the control(s) plus a smallIMarkerBarHostinterface) owns all of that state and logic, including the privateMarkerScanSource,MarkerFrameandMarkerLineSourcetypes.LogWindowkeeps 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, andRequestGotoLine. Exact shape is the implementer's choice; keep it minimal.LogWindowcurrently pairs them), mark data changed, mark content changed, begin/end tail (replacing direct_markerTailPendingincrements/decrements), track search, apply preferences, dispose.Task-returning update method so tests can drive one update without the WinForms timer.MarkerBarcontrol keepsName = "markerBar"and its place intableLayoutPanel1;LogWindowkeeps the table-layout column sizing and itsOnDpiChangedAfterParentoverride (forwarding to the controller).Acceptance criteria:
LogWindow.MarkerBar.csis deleted; no marker state fields remain onLogWindow(one controller field is fine)LogWindow.csno longer touches marker counters, theMarkerBarcontrol's buckets, orMarkerIndexdirectlyMarkerWindowTests,MarkerBarTests,SettingsDialogMarkerBarTests, and the CoreMarker/*tests pass with no change to what they assertOut of scope:
MarkerIndex/MarkerCriteria/MarkerBucket/MarkerBarLogWindowdecomposition workLineVisibilityTrackerTests.Dispose_CancelsAPendingScan_WithoutPublishingMerge pull request #722 from LogExperts/refactor/719-marker-bar-contr…
Extract Marker Bar coordination into MarkerBarController (LogExperts#719