11using System ;
2+ using System . Collections . Generic ;
23using System . Diagnostics ;
34using System . Drawing ;
45using System . Windows . Forms ;
@@ -25,8 +26,13 @@ class LineNumberTextBox : UserControl
2526 {
2627 BufferedPanel gutter ;
2728 RichTextBox rtb ;
29+ Timer highlightTimer ;
30+ bool suppressEvents ;
31+ string lastHighlightedWord ;
32+ List < int > highlightPositions = new List < int > ( ) ;
2833
2934 const int GutterPadding = 6 ;
35+ const int MinHighlightLength = 2 ;
3036
3137 public event EventHandler ContentChanged ;
3238
@@ -56,7 +62,13 @@ public LineNumberTextBox()
5662 rtb . TextChanged += ( s , e ) =>
5763 {
5864 gutter . Invalidate ( ) ;
59- if ( ContentChanged != null ) ContentChanged ( this , EventArgs . Empty ) ;
65+ if ( ! suppressEvents )
66+ {
67+ // Text changed by user — clear stale highlights
68+ highlightPositions . Clear ( ) ;
69+ lastHighlightedWord = null ;
70+ if ( ContentChanged != null ) ContentChanged ( this , EventArgs . Empty ) ;
71+ }
6072 } ;
6173 rtb . VScroll += ( s , e ) => gutter . Invalidate ( ) ;
6274 rtb . Resize += ( s , e ) => gutter . Invalidate ( ) ;
@@ -97,6 +109,11 @@ public LineNumberTextBox()
97109
98110 gutter . Paint += OnGutterPaint ;
99111
112+ highlightTimer = new Timer ( ) ;
113+ highlightTimer . Interval = 200 ;
114+ highlightTimer . Tick += delegate { highlightTimer . Stop ( ) ; ApplyOccurrenceHighlights ( ) ; } ;
115+ rtb . SelectionChanged += delegate { OnSelectionChanged ( ) ; } ;
116+
100117 Controls . Add ( rtb ) ;
101118 Controls . Add ( gutter ) ;
102119 }
@@ -286,6 +303,85 @@ void OnDragDrop(object sender, DragEventArgs e)
286303 }
287304 }
288305
306+ // ── Occurrence highlighting ──
307+
308+ void OnSelectionChanged ( )
309+ {
310+ if ( suppressEvents ) return ;
311+ highlightTimer . Stop ( ) ;
312+ highlightTimer . Start ( ) ;
313+ }
314+
315+ void ApplyOccurrenceHighlights ( )
316+ {
317+ string selectedText = rtb . SelectedText ;
318+
319+ // Trim trailing newlines that RichTextBox may include
320+ if ( selectedText != null )
321+ selectedText = selectedText . TrimEnd ( '\r ' , '\n ' ) ;
322+
323+ // Determine if we should highlight
324+ bool shouldHighlight = selectedText != null
325+ && selectedText . Length >= MinHighlightLength
326+ && selectedText . IndexOf ( '\n ' ) < 0
327+ && selectedText . IndexOf ( '\r ' ) < 0
328+ && selectedText . Trim ( ) . Length == selectedText . Length ;
329+
330+ string word = shouldHighlight ? selectedText : null ;
331+
332+ // Skip if same word is already highlighted
333+ if ( word == lastHighlightedWord
334+ || ( word != null && lastHighlightedWord != null && word == lastHighlightedWord ) )
335+ return ;
336+
337+ suppressEvents = true ;
338+ int savedSelStart = rtb . SelectionStart ;
339+ int savedSelLength = rtb . SelectionLength ;
340+
341+ // Remove previous highlights
342+ ClearHighlights ( ) ;
343+
344+ if ( word != null )
345+ {
346+ // Find all occurrences
347+ string text = rtb . Text ;
348+ int idx = 0 ;
349+ while ( idx < text . Length )
350+ {
351+ idx = text . IndexOf ( word , idx , StringComparison . OrdinalIgnoreCase ) ;
352+ if ( idx < 0 ) break ;
353+ // Skip the current selection itself
354+ if ( idx != savedSelStart )
355+ {
356+ highlightPositions . Add ( idx ) ;
357+ rtb . Select ( idx , word . Length ) ;
358+ rtb . SelectionBackColor = Theme . MatchHighlight ;
359+ }
360+ idx += word . Length ;
361+ }
362+ lastHighlightedWord = word ;
363+ }
364+
365+ // Restore selection
366+ rtb . Select ( savedSelStart , savedSelLength ) ;
367+ suppressEvents = false ;
368+ }
369+
370+ void ClearHighlights ( )
371+ {
372+ if ( highlightPositions . Count > 0 && lastHighlightedWord != null )
373+ {
374+ int wordLen = lastHighlightedWord . Length ;
375+ for ( int i = 0 ; i < highlightPositions . Count ; i ++ )
376+ {
377+ rtb . Select ( highlightPositions [ i ] , wordLen ) ;
378+ rtb . SelectionBackColor = rtb . BackColor ;
379+ }
380+ highlightPositions . Clear ( ) ;
381+ }
382+ lastHighlightedWord = null ;
383+ }
384+
289385 // ── Gutter painting ──
290386
291387 void OnGutterPaint ( object sender , PaintEventArgs e )
0 commit comments