1+ using System . Drawing ;
2+
3+ using ColumnizerLib ;
4+
5+ using LogExpert . Core . Classes . Highlight ;
6+ using LogExpert . Core . Entities ;
7+
8+ namespace LogExpert . Core . Classes . Marker ;
9+
10+ /// <summary>Snapshot of the matching and visual portions of marker criteria. Never invokes triggers.</summary>
11+ public sealed class MarkerCriteria
12+ {
13+ private readonly HighlightEntry [ ] _entries ;
14+ private HighlightEntry ? _search ;
15+
16+ public bool IsEmpty => _search == null && ! _entries . Any ( IsVisual ) ;
17+
18+ private MarkerCriteria ( HighlightEntry [ ] entries )
19+ {
20+ _entries = entries ;
21+ }
22+
23+ public static MarkerCriteria ForHighlights ( IEnumerable < HighlightEntry > entries )
24+ {
25+ ArgumentNullException . ThrowIfNull ( entries ) ;
26+ return new MarkerCriteria ( entries . Select ( entry => ( HighlightEntry ) entry . Clone ( ) ) . ToArray ( ) ) ;
27+ }
28+
29+ public static MarkerCriteria ForSearch ( SearchParams search , int colorArgb )
30+ {
31+ ArgumentNullException . ThrowIfNull ( search ) ;
32+ return new MarkerCriteria ( [ ] )
33+ {
34+ _search = string . IsNullOrEmpty ( search . SearchText ) ? null : new HighlightEntry
35+ {
36+ SearchText = search . SearchText ,
37+ IsRegex = search . IsRegex ,
38+ IsCaseSensitive = search . IsCaseSensitive ,
39+ BackgroundColor = Color . FromArgb ( colorArgb )
40+ }
41+ } ;
42+ }
43+
44+ public MarkerLine ? Match ( int lineNumber , ILogLineMemory line ,
45+ Func < int , ILogLineMemory , IReadOnlyList < ITextValueMemory > > ? getColumns = null ,
46+ CancellationToken cancellationToken = default )
47+ {
48+ ArgumentNullException . ThrowIfNull ( line ) ;
49+ cancellationToken . ThrowIfCancellationRequested ( ) ;
50+ if ( _search != null )
51+ {
52+ var matched = _search . IsRegex
53+ ? _search . Regex . IsMatch ( line . FullLine . Span )
54+ : line . FullLine . Span . Contains ( _search . SearchText ,
55+ _search . IsCaseSensitive ? StringComparison . Ordinal : StringComparison . OrdinalIgnoreCase ) ;
56+ return matched ? new MarkerLine ( lineNumber , _search . BackgroundColor . ToArgb ( ) ) : null ;
57+ }
58+
59+ IReadOnlyList < ITextValueMemory > ? columns = null ;
60+ for ( var priority = 0 ; priority < _entries . Length ; priority ++ )
61+ {
62+ cancellationToken . ThrowIfCancellationRequested ( ) ;
63+ var entry = _entries [ priority ] ;
64+ if ( ! IsVisual ( entry ) )
65+ {
66+ continue ;
67+ }
68+
69+ bool matched ;
70+ if ( entry . IsWordMatch )
71+ {
72+ columns ??= getColumns ? . Invoke ( lineNumber , line )
73+ ?? [ new Column { FullValue = line . FullLine } ] ;
74+ matched = columns . Any ( column => HasVisibleWordMatch ( entry , column ) ) ;
75+ }
76+ else
77+ {
78+ matched = HighlightEvaluator . IsMatch ( entry , line ) ;
79+ }
80+
81+ if ( matched )
82+ {
83+ int ? color = HasBackground ( entry ) ? entry . BackgroundColor . ToArgb ( )
84+ : HasForeground ( entry ) ? entry . ForegroundColor . ToArgb ( ) : null ;
85+ return new MarkerLine ( lineNumber , color , priority ) ;
86+ }
87+ }
88+
89+ return null ;
90+ }
91+
92+ private static bool IsVisual ( HighlightEntry entry )
93+ {
94+ return ! entry . IsSearchHit && ( HasBackground ( entry )
95+ || HasForeground ( entry ) || entry . IsBold ) ;
96+ }
97+
98+ private static bool HasBackground ( HighlightEntry entry )
99+ {
100+ return ( ! entry . IsWordMatch || ! entry . NoBackground ) && entry . BackgroundColor . A > 0 ;
101+ }
102+
103+ private static bool HasForeground ( HighlightEntry entry )
104+ {
105+ return entry . ForegroundColor . A > 0 ;
106+ }
107+
108+ private static bool HasVisibleWordMatch ( HighlightEntry entry , ITextValueMemory column )
109+ {
110+ // Word highlighting uses Regex even for literal text, against each displayed column.
111+ foreach ( var match in entry . Regex . EnumerateMatches ( column . Text . Span ) )
112+ {
113+ if ( match . Length > 0 )
114+ {
115+ return true ;
116+ }
117+ }
118+
119+ return false ;
120+ }
121+ }
0 commit comments