-
Notifications
You must be signed in to change notification settings - Fork 186
Expand file tree
/
Copy pathHighlightBookmarkScanner.cs
More file actions
156 lines (136 loc) · 5.59 KB
/
HighlightBookmarkScanner.cs
File metadata and controls
156 lines (136 loc) · 5.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
using System.Text;
using ColumnizerLib;
using LogExpert.Core.Classes.Highlight;
namespace LogExpert.Core.Classes.Bookmark;
/// <summary>
/// Scans all lines of a log file against highlight entries that have <see cref="HighlightEntry.IsSetBookmark"/> set,
/// producing a list of auto-generated bookmarks. This is a pure computation unit with no UI dependencies.
/// </summary>
public static class HighlightBookmarkScanner
{
/// <summary>
/// Scans lines [0..lineCount) for highlight matches and returns bookmarks for matching lines.
/// </summary>
/// <param name="lineCount">Total number of lines in the file.</param>
/// <param name="getLine">
/// Delegate that returns the log line at a given index. May return null for unavailable lines.
/// </param>
/// <param name="entries">
/// The highlight entries to check. Only entries with <see cref="HighlightEntry.IsSetBookmark"/> == true produce
/// bookmarks.
/// </param>
/// <param name="fileName">
/// The file name, passed to <see cref="ParamParser"/> for bookmark comment template resolution.
/// </param>
/// <param name="progressBarModulo">
/// Interval of lines for reporting progress via the <paramref name="progress"/> callback.
/// </param>
/// <param name="cancellationToken">Token to support cooperative cancellation.</param>
/// <param name="progress">
/// Optional progress callback receiving the current line index (for progress reporting).
/// </param>
/// <returns>List of auto-generated bookmarks for all matched lines.</returns>
public static List<Entities.Bookmark> Scan (int lineCount, Func<int, ILogLineMemory> getLine, IList<HighlightEntry> entries, string fileName, int progressBarModulo = 1000, IProgress<int> progress = null, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(getLine);
List<Entities.Bookmark> result = [];
// Pre-filter: only entries with IsSetBookmark matter
var bookmarkEntries = entries.Where(e => e.IsSetBookmark).ToList();
if (bookmarkEntries.Count == 0)
{
return result;
}
for (var i = 0; i < lineCount; i++)
{
cancellationToken.ThrowIfCancellationRequested();
var line = getLine(i);
if (line == null)
{
continue;
}
var (setBookmark, bookmarkComment, sourceHighlightText) = GetBookmarkAction(line, bookmarkEntries);
if (setBookmark)
{
var comment = ResolveComment(bookmarkComment, line, i, fileName);
result.Add(Entities.Bookmark.CreateAutoGenerated(i, comment, sourceHighlightText));
}
if (i % progressBarModulo == 0)
{
progress?.Report(i);
}
}
return result;
}
/// <summary>
/// Checks a single line against the bookmark-producing highlight entries. Returns whether a bookmark should be set,
/// the concatenated comment template, and the source highlight text.
/// </summary>
private static (bool SetBookmark, string BookmarkComment, string SourceHighlightText) GetBookmarkAction (ITextValueMemory line, List<HighlightEntry> bookmarkEntries)
{
var setBookmark = false;
var bookmarkCommentBuilder = new StringBuilder();
var sourceHighlightText = string.Empty;
foreach (var entry in bookmarkEntries.Where(entry => CheckHighlightEntryMatch(entry, line)))
{
setBookmark = true;
sourceHighlightText = entry.SearchText;
if (!string.IsNullOrEmpty(entry.BookmarkComment))
{
_ = bookmarkCommentBuilder.Append(entry.BookmarkComment).Append("\r\n");
}
}
return (setBookmark, bookmarkCommentBuilder.ToString().TrimEnd('\r', '\n'), sourceHighlightText);
}
/// <summary>
/// Matches a highlight entry against a line. Replicates the logic from LogWindow.CheckHighlightEntryMatch so the
/// scanner works identically to the existing tail-mode matching.
/// </summary>
private static bool CheckHighlightEntryMatch (HighlightEntry entry, ITextValueMemory column)
{
if (entry.IsRegex)
{
if (entry.Regex.IsMatch(column.Text.ToString()))
{
return true;
}
}
else
{
if (entry.IsCaseSensitive)
{
if (column.Text.Span.Contains(entry.SearchText.AsSpan(), StringComparison.Ordinal))
{
return true;
}
}
else
{
if (column.Text.Span.Contains(entry.SearchText.AsSpan(), StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
}
return false;
}
/// <summary>
/// Resolves the bookmark comment template using ParamParser, matching SetBookmarkFromTrigger behavior.
/// </summary>
private static string ResolveComment (string commentTemplate, ILogLineMemory line, int lineNum, string fileName)
{
if (string.IsNullOrEmpty(commentTemplate))
{
return commentTemplate;
}
try
{
var paramParser = new ParamParser(commentTemplate);
return paramParser.ReplaceParams(line, lineNum, fileName);
}
catch (ArgumentException)
{
// Invalid regex in template — return raw template (matches SetBookmarkFromTrigger behavior)
return commentTemplate;
}
}
}