-
Notifications
You must be signed in to change notification settings - Fork 183
Add configurable log overview marker bar #712
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
ba69052
Add configurable log overview marker bar (#27)
3ec43c6
remove
1102400
Address marker bar code-quality review comments
04f6268
Use Path.Join for the marker bar test artifact
4d58c69
Preserve Square Bracket state in marker snapshots
a6648d3
Address marker bar matching and code quality review
9f9b118
remove not needed docs
b0ac6bb
Resolve marker colors in Core and separate display categories
2eee20b
Strengthen marker DPI tests and type columnizer snapshots
3786f2d
Complete marker localization and isolate DPI validation
983c4fb
Translate marker resources and consolidate WinForms tests
1111da6
remove
ecb2b5d
update
6e9a4a2
Revert "remove"
6d93bf1
Revert "update"
523ad41
Clarify marker validation report and include it in solution
782c0ff
Use floating-point marker indicator spacing
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| namespace ColumnizerLib; | ||
|
|
||
| /// <summary> | ||
| /// Optional capability for capturing a columnizer's current parsing state for background use. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// The snapshot must be initialized with the current configuration and detected column layout, | ||
| /// without sharing mutable parsing state with the original columnizer. The Marker Bar captures | ||
| /// the snapshot on the UI thread and uses it on a worker thread without calling | ||
| /// <see cref="IInitColumnizerMemory.Selected"/>. Capture must be quick and perform no file I/O. | ||
| /// </remarks> | ||
| public interface IColumnizerSnapshotMemory | ||
| { | ||
| /// <summary> | ||
| /// Creates an independent, initialized columnizer with the current configuration and detected layout. | ||
| /// </summary> | ||
| /// <returns>A columnizer ready to parse log lines on a worker thread.</returns> | ||
| ILogLineMemoryColumnizer CreateSnapshot (); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| namespace LogExpert.Core.Classes.Marker; | ||
|
|
||
| /// <summary>A populated vertical pixel, including its logical range and navigation target.</summary> | ||
| public readonly record struct MarkerBucket (int Pixel, int FirstLine, int LastLine, int Count, int TargetLine, int ColorArgb) | ||
| { | ||
| /// <summary>Aggregates a line-ordered index without sampling and resolves inherited foregrounds; duplicate lines count only once.</summary> | ||
| public static IReadOnlyList<MarkerBucket> Aggregate (IEnumerable<MarkerLine> matches, int lineCount, int height, int defaultForegroundArgb) | ||
| { | ||
| ArgumentNullException.ThrowIfNull(matches); | ||
| if (lineCount <= 0 || height <= 0) | ||
| { | ||
| return []; | ||
| } | ||
|
|
||
| var buckets = new MarkerBucket[height]; | ||
| var priorities = new int[height]; | ||
| Array.Fill(priorities, int.MaxValue); | ||
| var previousLine = -1; | ||
| foreach (var match in matches) | ||
| { | ||
| var line = match.LineNumber; | ||
| if (line < 0 || line >= lineCount) | ||
| { | ||
| continue; | ||
| } | ||
|
|
||
| var pixel = lineCount == 1 ? 0 : (int)((long)line * (height - 1) / (lineCount - 1)); | ||
| var bucket = buckets[pixel]; | ||
| // Invert the floor mapping above: a bucket starts at ceil(pixel * (lines - 1) / (height - 1)) | ||
| // and ends immediately before the next bucket starts. The final pixel includes the last line. | ||
| var first = height == 1 ? 0 : (int)(((long)pixel * (lineCount - 1) + height - 2) / (height - 1)); | ||
| var last = height == 1 || pixel == height - 1 || lineCount == 1 ? lineCount - 1 | ||
| : (int)(((long)(pixel + 1) * (lineCount - 1) + height - 2) / (height - 1)) - 1; | ||
| var target = bucket.Count == 0 ? line : bucket.TargetLine; | ||
| // Double distances to compare against the midpoint without rounding half-line ties. | ||
| var distance = Math.Abs(2L * line - first - last); | ||
| var targetDistance = Math.Abs(2L * target - first - last); | ||
| if (distance < targetDistance || (distance == targetDistance && line < target)) | ||
| { | ||
| target = line; | ||
| } | ||
|
|
||
| var color = bucket.ColorArgb; | ||
| if (match.Priority < priorities[pixel]) | ||
| { | ||
| priorities[pixel] = match.Priority; | ||
| color = match.ColorArgb ?? defaultForegroundArgb; | ||
| } | ||
|
|
||
| buckets[pixel] = new MarkerBucket(pixel, first, last, | ||
| bucket.Count + (line == previousLine ? 0 : 1), target, color); | ||
| previousLine = line; | ||
| } | ||
|
|
||
| return buckets.Where(bucket => bucket.Count > 0).ToArray(); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,121 @@ | ||
| using System.Drawing; | ||
|
|
||
| using ColumnizerLib; | ||
|
|
||
| using LogExpert.Core.Classes.Highlight; | ||
| using LogExpert.Core.Entities; | ||
|
|
||
| namespace LogExpert.Core.Classes.Marker; | ||
|
|
||
| /// <summary>Snapshot of the matching and visual portions of marker criteria. Never invokes triggers.</summary> | ||
| public sealed class MarkerCriteria | ||
| { | ||
| private readonly HighlightEntry[] _entries; | ||
| private HighlightEntry? _search; | ||
|
|
||
| public bool IsEmpty => _search == null && !_entries.Any(IsVisual); | ||
|
|
||
| private MarkerCriteria (HighlightEntry[] entries) | ||
| { | ||
| _entries = entries; | ||
| } | ||
|
|
||
| public static MarkerCriteria ForHighlights (IEnumerable<HighlightEntry> entries) | ||
| { | ||
| ArgumentNullException.ThrowIfNull(entries); | ||
| return new MarkerCriteria(entries.Select(entry => (HighlightEntry)entry.Clone()).ToArray()); | ||
| } | ||
|
|
||
| public static MarkerCriteria ForSearch (SearchParams search, int colorArgb) | ||
| { | ||
| ArgumentNullException.ThrowIfNull(search); | ||
| return new MarkerCriteria([]) | ||
| { | ||
| _search = string.IsNullOrEmpty(search.SearchText) ? null : new HighlightEntry | ||
| { | ||
| SearchText = search.SearchText, | ||
| IsRegex = search.IsRegex, | ||
| IsCaseSensitive = search.IsCaseSensitive, | ||
| BackgroundColor = Color.FromArgb(colorArgb) | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| public MarkerLine? Match (int lineNumber, ILogLineMemory line, | ||
| Func<int, ILogLineMemory, IReadOnlyList<ITextValueMemory>>? getColumns = null, | ||
| CancellationToken cancellationToken = default) | ||
| { | ||
| ArgumentNullException.ThrowIfNull(line); | ||
| cancellationToken.ThrowIfCancellationRequested(); | ||
| if (_search != null) | ||
| { | ||
| var matched = _search.IsRegex | ||
| ? _search.Regex.IsMatch(line.FullLine.Span) | ||
| : line.FullLine.Span.Contains(_search.SearchText, | ||
| _search.IsCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase); | ||
| return matched ? new MarkerLine(lineNumber, _search.BackgroundColor.ToArgb()) : null; | ||
| } | ||
|
|
||
| IReadOnlyList<ITextValueMemory>? columns = null; | ||
| for (var priority = 0; priority < _entries.Length; priority++) | ||
| { | ||
| cancellationToken.ThrowIfCancellationRequested(); | ||
| var entry = _entries[priority]; | ||
| if (!IsVisual(entry)) | ||
| { | ||
| continue; | ||
| } | ||
|
|
||
| bool matched; | ||
| if (entry.IsWordMatch) | ||
| { | ||
| columns ??= getColumns?.Invoke(lineNumber, line) | ||
| ?? [new Column { FullValue = line.FullLine }]; | ||
| matched = columns.Any(column => HasVisibleWordMatch(entry, column)); | ||
| } | ||
| else | ||
| { | ||
| matched = HighlightEvaluator.IsMatch(entry, line); | ||
| } | ||
|
|
||
| if (matched) | ||
| { | ||
| int? color = HasBackground(entry) ? entry.BackgroundColor.ToArgb() | ||
| : HasForeground(entry) ? entry.ForegroundColor.ToArgb() : null; | ||
| return new MarkerLine(lineNumber, color, priority); | ||
| } | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
|
|
||
| private static bool IsVisual (HighlightEntry entry) | ||
| { | ||
| return !entry.IsSearchHit && (HasBackground(entry) | ||
| || HasForeground(entry) || entry.IsBold); | ||
| } | ||
|
|
||
| private static bool HasBackground (HighlightEntry entry) | ||
| { | ||
| return (!entry.IsWordMatch || !entry.NoBackground) && entry.BackgroundColor.A > 0; | ||
| } | ||
|
|
||
| private static bool HasForeground (HighlightEntry entry) | ||
| { | ||
| return entry.ForegroundColor.A > 0; | ||
| } | ||
|
|
||
| private static bool HasVisibleWordMatch (HighlightEntry entry, ITextValueMemory column) | ||
| { | ||
| // Word highlighting uses Regex even for literal text, against each displayed column. | ||
| foreach (var match in entry.Regex.EnumerateMatches(column.Text.Span)) | ||
| { | ||
| if (match.Length > 0) | ||
| { | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.