From 68c5dab2a621d940a8af8e3d0db7a74b4e78fbea Mon Sep 17 00:00:00 2001 From: BRUNER Patrick Date: Thu, 25 Jun 2026 17:51:43 +0200 Subject: [PATCH 1/4] Extract highlight match/trigger logic into Core HighlightEvaluator The highlight-match semantics lived only inside LogWindow and had been copy-pasted into HighlightBookmarkScanner (its own comment admitted "Replicates the logic from LogWindow.CheckHighlightEntryMatch"), so two copies had to be kept in sync by hand and the logic had no direct test surface. Add a pure LogExpert.Core HighlightEvaluator with IsMatch, FindMatchingEntries, and GetTriggerActions (returning a HighlightActions record struct). Route both consumers through it: - HighlightBookmarkScanner: delete its duplicated CheckHighlightEntryMatch, call HighlightEvaluator.IsMatch. - LogWindow: FindMatchingHighlightEntries becomes a thin lock-wrapper over the evaluator; GetHighlightActions and the private CheckHighlightEntryMatch are removed; GetHighlightEntryMatches and FindHighlightEntry use HighlightEvaluator.IsMatch. The evaluator reports the decision only; side effects (Audio Alert, Set Bookmark, Stop Tail) stay at the LogWindow call sites, "Audio Alert fires only on the tail trigger path" invariant remains structural and cannot leak onto the bulk/scanner/paint paths. 12 new tests pin match/find/action behavior plus the null-arg guards. --- .../Bookmark/HighlightBookmarkScanner.cs | 36 +---- .../Classes/Highlight/HighlightEvaluator.cs | 107 ++++++++++++++ .../Highlight/HighlightEvaluatorTests.cs | 138 ++++++++++++++++++ .../Controls/LogWindow/LogWindow.cs | 91 +----------- 4 files changed, 253 insertions(+), 119 deletions(-) create mode 100644 src/LogExpert.Core/Classes/Highlight/HighlightEvaluator.cs create mode 100644 src/LogExpert.Tests/Highlight/HighlightEvaluatorTests.cs diff --git a/src/LogExpert.Core/Classes/Bookmark/HighlightBookmarkScanner.cs b/src/LogExpert.Core/Classes/Bookmark/HighlightBookmarkScanner.cs index afbbc1fd..cc61f918 100644 --- a/src/LogExpert.Core/Classes/Bookmark/HighlightBookmarkScanner.cs +++ b/src/LogExpert.Core/Classes/Bookmark/HighlightBookmarkScanner.cs @@ -84,7 +84,7 @@ private static (bool SetBookmark, string BookmarkComment, string SourceHighlight var bookmarkCommentBuilder = new StringBuilder(); var sourceHighlightText = string.Empty; - foreach (var entry in bookmarkEntries.Where(entry => CheckHighlightEntryMatch(entry, line))) + foreach (var entry in bookmarkEntries.Where(entry => HighlightEvaluator.IsMatch(entry, line))) { setBookmark = true; sourceHighlightText = entry.SearchText; @@ -98,40 +98,6 @@ private static (bool SetBookmark, string BookmarkComment, string SourceHighlight return (setBookmark, bookmarkCommentBuilder.ToString().TrimEnd('\r', '\n'), sourceHighlightText); } - /// - /// Matches a highlight entry against a line. Replicates the logic from LogWindow.CheckHighlightEntryMatch so the - /// scanner works identically to the existing tail-mode matching. - /// - 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; - } - /// /// Resolves the bookmark comment template using ParamParser, matching SetBookmarkFromTrigger behavior. /// diff --git a/src/LogExpert.Core/Classes/Highlight/HighlightEvaluator.cs b/src/LogExpert.Core/Classes/Highlight/HighlightEvaluator.cs new file mode 100644 index 00000000..ffbb9efb --- /dev/null +++ b/src/LogExpert.Core/Classes/Highlight/HighlightEvaluator.cs @@ -0,0 +1,107 @@ +using ColumnizerLib; + +namespace LogExpert.Core.Classes.Highlight; + +/// +/// Pure evaluation of rules against a log line: which entries match and +/// which trigger actions their matches imply. This is the single home of highlight-match semantics, +/// shared by the tail trigger path (LogWindow.CheckFilterAndHighlight) and the bulk +/// . +/// +/// It reports the decision only. Side-effecting triggers (Audio Alert, Set Bookmark, +/// Stop Tail) are fired by the caller, so the "Audio Alert fires only on the tail path" invariant +/// stays at the call site and cannot leak onto bulk/paint paths. +/// +/// +public static class HighlightEvaluator +{ + /// + /// Returns whether a single matches the given line. + /// + public static bool IsMatch (HighlightEntry entry, ITextValueMemory line) + { + ArgumentNullException.ThrowIfNull(entry); + ArgumentNullException.ThrowIfNull(line); + + if (entry.IsRegex) + { + return entry.Regex.IsMatch(line.Text.ToString()); + } + + var comparison = entry.IsCaseSensitive + ? StringComparison.Ordinal + : StringComparison.OrdinalIgnoreCase; + + return line.Text.Span.Contains(entry.SearchText.AsSpan(), comparison); + } + + /// + /// Returns every entry in that matches the line, preserving order. + /// A null line matches nothing. + /// + public static IList FindMatchingEntries (IEnumerable entries, ITextValueMemory line) + { + ArgumentNullException.ThrowIfNull(entries); + + List result = []; + if (line == null) + { + return result; + } + + foreach (var entry in entries.Where(e => IsMatch(e, line))) + { + result.Add(entry); + } + + return result; + } + + /// + /// Classifies the non-plugin trigger actions implied by a set of matching entries: whether to + /// suppress the dirty LED, stop tailing, set a bookmark, and the concatenated bookmark comment. + /// Plugin and Audio Alert triggers are intentionally not handled here — they are fired by the + /// caller. + /// + public static HighlightActions GetTriggerActions (IEnumerable matchingEntries) + { + ArgumentNullException.ThrowIfNull(matchingEntries); + + var suppressLed = false; + var stopTail = false; + var setBookmark = false; + var bookmarkComment = string.Empty; + + foreach (var entry in matchingEntries) + { + if (entry.IsLedSwitch) + { + suppressLed = true; + } + + if (entry.IsSetBookmark) + { + setBookmark = true; + if (!string.IsNullOrEmpty(entry.BookmarkComment)) + { + bookmarkComment += entry.BookmarkComment + "\r\n"; + } + } + + if (entry.IsStopTail) + { + stopTail = true; + } + } + + bookmarkComment = bookmarkComment.TrimEnd(['\r', '\n']); + + return new HighlightActions(suppressLed, stopTail, setBookmark, bookmarkComment); + } +} + +/// +/// The non-plugin trigger decision for a line: which LED/tail/bookmark side effects its matching +/// entries imply. Carries no side effects of its own — the caller acts on it. +/// +public readonly record struct HighlightActions (bool SuppressLed, bool StopTail, bool SetBookmark, string BookmarkComment); diff --git a/src/LogExpert.Tests/Highlight/HighlightEvaluatorTests.cs b/src/LogExpert.Tests/Highlight/HighlightEvaluatorTests.cs new file mode 100644 index 00000000..bed2c3d7 --- /dev/null +++ b/src/LogExpert.Tests/Highlight/HighlightEvaluatorTests.cs @@ -0,0 +1,138 @@ +using ColumnizerLib; + +using LogExpert.Core.Classes.Highlight; + +using NUnit.Framework; + +namespace LogExpert.Tests.Highlight; + +[TestFixture] +public class HighlightEvaluatorTests +{ + #region Helper + + private sealed class TestLine (string text) : ITextValueMemory + { + public ReadOnlyMemory Text => text.AsMemory(); + } + + private static ITextValueMemory Line (string text) => new TestLine(text); + + #endregion + + [Test] + public void IsMatch_PlainSubstring_MatchesCaseInsensitiveByDefault () + { + var entry = new HighlightEntry { SearchText = "error" }; + + Assert.That(HighlightEvaluator.IsMatch(entry, Line("Something ERROR happened")), Is.True); + } + + [Test] + public void IsMatch_CaseSensitive_RespectsCase () + { + var entry = new HighlightEntry { SearchText = "error", IsCaseSensitive = true }; + + Assert.That(HighlightEvaluator.IsMatch(entry, Line("ERROR happened")), Is.False); + Assert.That(HighlightEvaluator.IsMatch(entry, Line("an error here")), Is.True); + } + + [Test] + public void IsMatch_Regex_Matches () + { + var entry = new HighlightEntry { SearchText = "ERR\\w+", IsRegex = true }; + + Assert.That(HighlightEvaluator.IsMatch(entry, Line("ERROR happened")), Is.True); + } + + [Test] + public void IsMatch_NoSubstring_ReturnsFalse () + { + var entry = new HighlightEntry { SearchText = "FATAL" }; + + Assert.That(HighlightEvaluator.IsMatch(entry, Line("just an info line")), Is.False); + } + + [Test] + public void FindMatchingEntries_ReturnsOnlyMatches_InOrder () + { + var error = new HighlightEntry { SearchText = "error" }; + var warn = new HighlightEntry { SearchText = "warn" }; + var fatal = new HighlightEntry { SearchText = "fatal" }; + var entries = new[] { error, warn, fatal }; + + var result = HighlightEvaluator.FindMatchingEntries(entries, Line("warn: an error occurred")); + + Assert.That(result, Is.EqualTo(new[] { error, warn })); + } + + [Test] + public void FindMatchingEntries_NullLine_ReturnsEmpty () + { + var entries = new[] { new HighlightEntry { SearchText = "error" } }; + + var result = HighlightEvaluator.FindMatchingEntries(entries, null); + + Assert.That(result, Is.Empty); + } + + [Test] + public void GetTriggerActions_EmptyList_AllFalse () + { + var actions = HighlightEvaluator.GetTriggerActions([]); + + Assert.That(actions.SuppressLed, Is.False); + Assert.That(actions.StopTail, Is.False); + Assert.That(actions.SetBookmark, Is.False); + Assert.That(actions.BookmarkComment, Is.Empty); + } + + [Test] + public void GetTriggerActions_AggregatesFlagsAcrossEntries () + { + var matching = new[] + { + new HighlightEntry { IsLedSwitch = true }, + new HighlightEntry { IsStopTail = true }, + new HighlightEntry { IsSetBookmark = true } + }; + + var actions = HighlightEvaluator.GetTriggerActions(matching); + + Assert.That(actions.SuppressLed, Is.True); + Assert.That(actions.StopTail, Is.True); + Assert.That(actions.SetBookmark, Is.True); + } + + [Test] + public void GetTriggerActions_JoinsBookmarkComments_WithCrlf_TrimmingTrailing () + { + var matching = new[] + { + new HighlightEntry { IsSetBookmark = true, BookmarkComment = "first" }, + new HighlightEntry { IsSetBookmark = true, BookmarkComment = "second" } + }; + + var actions = HighlightEvaluator.GetTriggerActions(matching); + + Assert.That(actions.BookmarkComment, Is.EqualTo("first\r\nsecond")); + } + + [Test] + public void IsMatch_NullEntry_Throws () + { + _ = Assert.Throws(() => HighlightEvaluator.IsMatch(null, Line("x"))); + } + + [Test] + public void FindMatchingEntries_NullEntries_Throws () + { + _ = Assert.Throws(() => HighlightEvaluator.FindMatchingEntries(null, Line("x"))); + } + + [Test] + public void GetTriggerActions_NullList_Throws () + { + _ = Assert.Throws(() => HighlightEvaluator.GetTriggerActions(null)); + } +} diff --git a/src/LogExpert.UI/Controls/LogWindow/LogWindow.cs b/src/LogExpert.UI/Controls/LogWindow/LogWindow.cs index 76d42c08..c433cb3d 100644 --- a/src/LogExpert.UI/Controls/LogWindow/LogWindow.cs +++ b/src/LogExpert.UI/Controls/LogWindow/LogWindow.cs @@ -3204,7 +3204,7 @@ private void CheckFilterAndHighlight (LogEventArgs e) var matchingList = FindMatchingHighlightEntries(line); LaunchHighlightPlugins(matchingList, i); - var (suppressLed, stopTail, setBookmark, bookmarkComment) = GetHighlightActions(matchingList); + var (suppressLed, stopTail, setBookmark, bookmarkComment) = HighlightEvaluator.GetTriggerActions(matchingList); SafeTriggerAudioAlert(matchingList); if (setBookmark) { @@ -3256,7 +3256,7 @@ private void CheckFilterAndHighlight (LogEventArgs e) { var matchingList = FindMatchingHighlightEntries(line); LaunchHighlightPlugins(matchingList, i); - var (suppressLed, stopTail, setBookmark, bookmarkComment) = GetHighlightActions(matchingList); + var (suppressLed, stopTail, setBookmark, bookmarkComment) = HighlightEvaluator.GetTriggerActions(matchingList); SafeTriggerAudioAlert(matchingList); if (setBookmark) { @@ -3814,58 +3814,15 @@ private HighlightEntry FindFirstNoWordMatchHighlightEntry (ITextValueMemory line return FindHighlightEntry(line, true); } - private static bool CheckHighlightEntryMatch (HighlightEntry entry, ITextValueMemory column) - { - if (entry.IsRegex) - { - //Regex rex = new Regex(entry.SearchText, entry.IsCaseSensitive ? RegexOptions.None : RegexOptions.IgnoreCase); - 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; - } - /// /// Returns all HighlightEntry entries which matches the given line /// private IList FindMatchingHighlightEntries (ITextValueMemory line) { - IList resultList = []; - if (line != null) + lock (_currentHighlightGroupLock) { - lock (_currentHighlightGroupLock) - { - foreach (var entry in _currentHighlightGroup.HighlightEntryList) - { - if (CheckHighlightEntryMatch(entry, line)) - { - resultList.Add(entry); - } - } - } + return HighlightEvaluator.FindMatchingEntries(_currentHighlightGroup.HighlightEntryList, line); } - - return resultList; } private static void GetHighlightEntryMatches (ITextValueMemory line, IList hilightEntryList, IList resultList) @@ -3889,7 +3846,7 @@ private static void GetHighlightEntryMatches (ITextValueMemory line, IList matchingList) - { - var noLed = false; - var stopTail = false; - var setBookmark = false; - var bookmarkComment = string.Empty; - - foreach (var entry in matchingList) - { - if (entry.IsLedSwitch) - { - noLed = true; - } - - if (entry.IsSetBookmark) - { - setBookmark = true; - if (!string.IsNullOrEmpty(entry.BookmarkComment)) - { - bookmarkComment += entry.BookmarkComment + "\r\n"; - } - } - - if (entry.IsStopTail) - { - stopTail = true; - } - } - - bookmarkComment = bookmarkComment.TrimEnd(['\r', '\n']); - - return (noLed, stopTail, setBookmark, bookmarkComment); - } - /// /// Fires an audio alert for the first matching highlight entry that has /// enabled. Iteration stops after the @@ -7046,7 +6969,7 @@ public HighlightEntry FindHighlightEntry (ITextValueMemory line, bool noWordMatc continue; } - if (CheckHighlightEntryMatch(entry, line)) + if (HighlightEvaluator.IsMatch(entry, line)) { return entry; } @@ -7062,7 +6985,7 @@ public HighlightEntry FindHighlightEntry (ITextValueMemory line, bool noWordMatc continue; } - if (CheckHighlightEntryMatch(entry, line)) + if (HighlightEvaluator.IsMatch(entry, line)) { return entry; } From 0fac8b8ee8b95dde805f84236d86b5ae51eedabe Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 25 Jun 2026 15:56:05 +0000 Subject: [PATCH 2/4] chore: update plugin hashes [skip ci] --- .../PluginHashGenerator.Generated.cs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/PluginRegistry/PluginHashGenerator.Generated.cs b/src/PluginRegistry/PluginHashGenerator.Generated.cs index 2640e4fd..5387d086 100644 --- a/src/PluginRegistry/PluginHashGenerator.Generated.cs +++ b/src/PluginRegistry/PluginHashGenerator.Generated.cs @@ -10,7 +10,7 @@ public static partial class PluginValidator { /// /// Gets pre-calculated SHA256 hashes for built-in plugins. - /// Generated: 2026-06-25 15:09:22 UTC + /// Generated: 2026-06-25 15:56:03 UTC /// Configuration: Release /// Plugin count: 21 /// @@ -18,27 +18,27 @@ public static Dictionary GetBuiltInPluginHashes() { return new Dictionary(StringComparer.OrdinalIgnoreCase) { - ["AutoColumnizer.dll"] = "ED598729A6BCB3175F02FF54576DDD5DAD7CBBC0E47A1E07ADC8A9B4AB330675", + ["AutoColumnizer.dll"] = "45C5F4010F66451239E57448DDD34C4AC74871A83F8AD9BD8484CADBE25C31A3", ["BouncyCastle.Cryptography.dll"] = "E5EEAF6D263C493619982FD3638E6135077311D08C961E1FE128F9107D29EBC6", ["BouncyCastle.Cryptography.dll (x86)"] = "E5EEAF6D263C493619982FD3638E6135077311D08C961E1FE128F9107D29EBC6", - ["CsvColumnizer.dll"] = "D2200DE16E125F7B3FD4A0603F094455712987773C98AE71B46EBEB1F181D5FC", - ["CsvColumnizer.dll (x86)"] = "D2200DE16E125F7B3FD4A0603F094455712987773C98AE71B46EBEB1F181D5FC", - ["DefaultPlugins.dll"] = "9E7CD9B83067F83273D4D0BDD0E50DEBD3073015B55F63E7683AAF768FFB913F", - ["FlashIconHighlighter.dll"] = "0E9DB7648FF41A3AAF7924A2A8A01476B803A0471711B9A3FE1D3C927EC02199", - ["GlassfishColumnizer.dll"] = "F65B6305AF29B1F883C2523140C230CBB900046022572618A41C3C128703FF3D", - ["JsonColumnizer.dll"] = "514EDAC4465FF75D604989F20B0B12ADBFD2092E514B4E80FE8CBF2272CF7C9A", - ["JsonCompactColumnizer.dll"] = "04B454BDBA03DD8683F9065AF8080E7ED5A443A63611D738AC3B2B11A912E537", - ["Log4jXmlColumnizer.dll"] = "7CEB38D227528C08C21D403F7CA91DFE689655022EFB7280761167C7F7C2AFBC", - ["LogExpert.Resources.dll"] = "A4AC70A6FCB3997821127C3F59BF78933794CF0B07F26DFFCD9004FD506F03C5", + ["CsvColumnizer.dll"] = "EF17204E467639A6CD41E6534D5C032BD8BCB65C23E73015778FC9E01284C160", + ["CsvColumnizer.dll (x86)"] = "EF17204E467639A6CD41E6534D5C032BD8BCB65C23E73015778FC9E01284C160", + ["DefaultPlugins.dll"] = "533CA6DFF1A04A015BD9ABB9CAF9686B24A20ADD9C822EF6207658625A473B0C", + ["FlashIconHighlighter.dll"] = "0920211196835B231712B94AF26D14D273379DABB6CCF1ABDD214B1FC7EBAEEC", + ["GlassfishColumnizer.dll"] = "CC9F89943DCA24FDC5FD4668779361FE4DFC1EFD1D5269880A9C1A6C5755261F", + ["JsonColumnizer.dll"] = "24D05FAC7F7479B1CC5CB9E95FFC7BB35205E5780A01AF957542A86FD2854962", + ["JsonCompactColumnizer.dll"] = "67CEDFA0F02FE79AE32CA5776294D1BE1ADF7DF24E87299C2D179D7099A56DC9", + ["Log4jXmlColumnizer.dll"] = "70684E896F6EDB75C82D4BC0F74E971134A4CFF760567799526D89DFD1BA52A7", + ["LogExpert.Resources.dll"] = "C68848694DC56EBD259C91C3FBA24284CE5DFC7B1C3528FF63B59BBCB37359C2", ["Microsoft.Extensions.DependencyInjection.Abstractions.dll"] = "67FA4325000DB017DC0C35829B416F024F042D24EFB868BCF17A895EE6500A93", ["Microsoft.Extensions.DependencyInjection.Abstractions.dll (x86)"] = "67FA4325000DB017DC0C35829B416F024F042D24EFB868BCF17A895EE6500A93", ["Microsoft.Extensions.Logging.Abstractions.dll"] = "BB853130F5AFAF335BE7858D661F8212EC653835100F5A4E3AA2C66A4D4F685D", ["Microsoft.Extensions.Logging.Abstractions.dll (x86)"] = "BB853130F5AFAF335BE7858D661F8212EC653835100F5A4E3AA2C66A4D4F685D", - ["RegexColumnizer.dll"] = "7D343457626A42062C885716047E6DC6649983E2AD612B7C38D73F695250355A", - ["SftpFileSystem.dll"] = "3011E98941B793FBBC891AA38939058CC057F3CBDC0AD1E410A7B1715821FF3B", - ["SftpFileSystem.dll (x86)"] = "5D79B06F7109C61C5A5A9049DB355627F4E4EB85E557DB7786C0412954A85B72", - ["SftpFileSystem.Resources.dll"] = "0CE8807D06B4027FA3358773581C0B1DBC646BB2B607F4F358853AC8640C4976", - ["SftpFileSystem.Resources.dll (x86)"] = "0CE8807D06B4027FA3358773581C0B1DBC646BB2B607F4F358853AC8640C4976", + ["RegexColumnizer.dll"] = "A68DE17DBDFA8A6E0479B58B9B394EF430C0D93F42C0BC4FBABDA223E452DCAF", + ["SftpFileSystem.dll"] = "A2B84E3DE03A5EDD3B12F019884452894303EE1517459C4DC4AC7226A769F596", + ["SftpFileSystem.dll (x86)"] = "2FB5EEF1671AE28B5603D3FE4F326A434418A33E80AC7E3F6D02A67644EC58A1", + ["SftpFileSystem.Resources.dll"] = "EDD5AA7915B15346E4AE0A8CDF80D9CBF330F82D33821A289FB472372E615A5D", + ["SftpFileSystem.Resources.dll (x86)"] = "EDD5AA7915B15346E4AE0A8CDF80D9CBF330F82D33821A289FB472372E615A5D", }; } From fb9cf545138d0fe8f59343066d7c95552d202225 Mon Sep 17 00:00:00 2001 From: BRUNER Patrick Date: Thu, 25 Jun 2026 18:04:55 +0200 Subject: [PATCH 3/4] update --- .../Classes/Highlight/HighlightEvaluator.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/LogExpert.Core/Classes/Highlight/HighlightEvaluator.cs b/src/LogExpert.Core/Classes/Highlight/HighlightEvaluator.cs index ffbb9efb..6f24ec38 100644 --- a/src/LogExpert.Core/Classes/Highlight/HighlightEvaluator.cs +++ b/src/LogExpert.Core/Classes/Highlight/HighlightEvaluator.cs @@ -1,3 +1,5 @@ +using System.Text; + using ColumnizerLib; namespace LogExpert.Core.Classes.Highlight; @@ -70,7 +72,7 @@ public static HighlightActions GetTriggerActions (IEnumerable ma var suppressLed = false; var stopTail = false; var setBookmark = false; - var bookmarkComment = string.Empty; + var bookmarkCommentBuilder = new StringBuilder(); foreach (var entry in matchingEntries) { @@ -84,7 +86,9 @@ public static HighlightActions GetTriggerActions (IEnumerable ma setBookmark = true; if (!string.IsNullOrEmpty(entry.BookmarkComment)) { - bookmarkComment += entry.BookmarkComment + "\r\n"; + + _ = bookmarkCommentBuilder.Append(entry.BookmarkComment); + _ = bookmarkCommentBuilder.Append("\r\n"); } } @@ -94,7 +98,7 @@ public static HighlightActions GetTriggerActions (IEnumerable ma } } - bookmarkComment = bookmarkComment.TrimEnd(['\r', '\n']); + var bookmarkComment = bookmarkCommentBuilder.ToString().TrimEnd(['\r', '\n']); return new HighlightActions(suppressLed, stopTail, setBookmark, bookmarkComment); } From 0f916a72875b56f4f1c5958c80be998de95a28e4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 25 Jun 2026 16:07:17 +0000 Subject: [PATCH 4/4] chore: update plugin hashes [skip ci] --- .../PluginHashGenerator.Generated.cs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/PluginRegistry/PluginHashGenerator.Generated.cs b/src/PluginRegistry/PluginHashGenerator.Generated.cs index 5387d086..7a160ebe 100644 --- a/src/PluginRegistry/PluginHashGenerator.Generated.cs +++ b/src/PluginRegistry/PluginHashGenerator.Generated.cs @@ -10,7 +10,7 @@ public static partial class PluginValidator { /// /// Gets pre-calculated SHA256 hashes for built-in plugins. - /// Generated: 2026-06-25 15:56:03 UTC + /// Generated: 2026-06-25 16:07:16 UTC /// Configuration: Release /// Plugin count: 21 /// @@ -18,27 +18,27 @@ public static Dictionary GetBuiltInPluginHashes() { return new Dictionary(StringComparer.OrdinalIgnoreCase) { - ["AutoColumnizer.dll"] = "45C5F4010F66451239E57448DDD34C4AC74871A83F8AD9BD8484CADBE25C31A3", + ["AutoColumnizer.dll"] = "01A6AE19C49DFF72F14E95621D8D656740610E7347BF76FF7BD521173A7E2942", ["BouncyCastle.Cryptography.dll"] = "E5EEAF6D263C493619982FD3638E6135077311D08C961E1FE128F9107D29EBC6", ["BouncyCastle.Cryptography.dll (x86)"] = "E5EEAF6D263C493619982FD3638E6135077311D08C961E1FE128F9107D29EBC6", - ["CsvColumnizer.dll"] = "EF17204E467639A6CD41E6534D5C032BD8BCB65C23E73015778FC9E01284C160", - ["CsvColumnizer.dll (x86)"] = "EF17204E467639A6CD41E6534D5C032BD8BCB65C23E73015778FC9E01284C160", - ["DefaultPlugins.dll"] = "533CA6DFF1A04A015BD9ABB9CAF9686B24A20ADD9C822EF6207658625A473B0C", - ["FlashIconHighlighter.dll"] = "0920211196835B231712B94AF26D14D273379DABB6CCF1ABDD214B1FC7EBAEEC", - ["GlassfishColumnizer.dll"] = "CC9F89943DCA24FDC5FD4668779361FE4DFC1EFD1D5269880A9C1A6C5755261F", - ["JsonColumnizer.dll"] = "24D05FAC7F7479B1CC5CB9E95FFC7BB35205E5780A01AF957542A86FD2854962", - ["JsonCompactColumnizer.dll"] = "67CEDFA0F02FE79AE32CA5776294D1BE1ADF7DF24E87299C2D179D7099A56DC9", - ["Log4jXmlColumnizer.dll"] = "70684E896F6EDB75C82D4BC0F74E971134A4CFF760567799526D89DFD1BA52A7", - ["LogExpert.Resources.dll"] = "C68848694DC56EBD259C91C3FBA24284CE5DFC7B1C3528FF63B59BBCB37359C2", + ["CsvColumnizer.dll"] = "8C088610C395B467035FF822CBA5FC9752B039ED3EC32FEBD78B04F6E085498B", + ["CsvColumnizer.dll (x86)"] = "8C088610C395B467035FF822CBA5FC9752B039ED3EC32FEBD78B04F6E085498B", + ["DefaultPlugins.dll"] = "9A34EDE8590C3E14CC8F3B8E1A03672E3DFFB14F13814B7E8393EFF4B5F21B7E", + ["FlashIconHighlighter.dll"] = "4AB87A53B7C8C1F02D241754FB8CE008C64DF752DD2A3E311562CC9C80637921", + ["GlassfishColumnizer.dll"] = "85A92A1F23A33648EBB368CE81E8BF435A3D6E71C0417A818382713E2C342058", + ["JsonColumnizer.dll"] = "6C20EA5127BEFF1932387CF3538BE175FA458F9901C29999378BAB18DD5EE8F8", + ["JsonCompactColumnizer.dll"] = "79D4921588313832BA2044B0D4F898AF9A52A568A26A0135953FEAA27CB9906F", + ["Log4jXmlColumnizer.dll"] = "12DB495986480AC150E2AAAF4F0003ABDF39E5F52A6D16ABB0AD498D401BC8C6", + ["LogExpert.Resources.dll"] = "8646D8D2D2B34331F4595A9A0A4E92D0A6B48EAE6816026DC5B91625DC050535", ["Microsoft.Extensions.DependencyInjection.Abstractions.dll"] = "67FA4325000DB017DC0C35829B416F024F042D24EFB868BCF17A895EE6500A93", ["Microsoft.Extensions.DependencyInjection.Abstractions.dll (x86)"] = "67FA4325000DB017DC0C35829B416F024F042D24EFB868BCF17A895EE6500A93", ["Microsoft.Extensions.Logging.Abstractions.dll"] = "BB853130F5AFAF335BE7858D661F8212EC653835100F5A4E3AA2C66A4D4F685D", ["Microsoft.Extensions.Logging.Abstractions.dll (x86)"] = "BB853130F5AFAF335BE7858D661F8212EC653835100F5A4E3AA2C66A4D4F685D", - ["RegexColumnizer.dll"] = "A68DE17DBDFA8A6E0479B58B9B394EF430C0D93F42C0BC4FBABDA223E452DCAF", - ["SftpFileSystem.dll"] = "A2B84E3DE03A5EDD3B12F019884452894303EE1517459C4DC4AC7226A769F596", - ["SftpFileSystem.dll (x86)"] = "2FB5EEF1671AE28B5603D3FE4F326A434418A33E80AC7E3F6D02A67644EC58A1", - ["SftpFileSystem.Resources.dll"] = "EDD5AA7915B15346E4AE0A8CDF80D9CBF330F82D33821A289FB472372E615A5D", - ["SftpFileSystem.Resources.dll (x86)"] = "EDD5AA7915B15346E4AE0A8CDF80D9CBF330F82D33821A289FB472372E615A5D", + ["RegexColumnizer.dll"] = "AD1427C21C83F0087D2DDC6161D80A1F976C91D05CCB6164769116A2EFDD5758", + ["SftpFileSystem.dll"] = "F998A62944D8B39AFCF67142B9A75711EED3AE3B2373009ADA00CFCEC4BFF18B", + ["SftpFileSystem.dll (x86)"] = "3735FEF98FCAA13973AF5549649F60D0D717FAF42F965FD3F1AA70B93E060E56", + ["SftpFileSystem.Resources.dll"] = "ACCC15F3AE6C8888A3CC99AC32BBFA45E798B546FB8E0C9C6A8CFA22FB2C20B7", + ["SftpFileSystem.Resources.dll (x86)"] = "ACCC15F3AE6C8888A3CC99AC32BBFA45E798B546FB8E0C9C6A8CFA22FB2C20B7", }; }