/// <summary> /// Start a background search for all instances of this.highlight. /// </summary> /// <returns> /// true if a either a search has been queued or if the adornments/margin have been cleared since the highlight was removed. /// </returns> private bool UpdateMatches(bool wereAdornmentsEnabled = true) { if (_hasEvents) { SnapshotSpan?oldHighlightSpan = _highlightSpan; string oldHighlight = _highlight; bool matchWholeWord = _textView.Selection.IsEmpty; _highlightSpan = matchWholeWord ? BackgroundSearch.GetExtentOfWord(_textView.Caret.Position.BufferPosition) : this.SelectionToHighlightSpan(); _highlight = (_highlightSpan.HasValue) ? _highlightSpan.Value.GetText() : null; //Do a new search if the highlight changed, there is no existing search, or the existing search was on the wrong snapshot. if ((_highlight != oldHighlight) || (_search == null) || (_search.Snapshot != _textView.TextSnapshot)) { //The text of the highlight changes ... restart the search. if (_search != null) { //Stop and blow away the old search (even if it didn't finish, the results are not interesting anymore). _search.Abort(); _search = null; } if (_highlight != null) { //The underlying buffer could be very large, meaning that doing the search for all matches on the UI thread //is a bad idea. Do the search on the background thread and use a callback to invalidate the visual when //the entire search has completed. _search = new BackgroundSearch(_textView.TextSnapshot, _highlight, matchWholeWord, delegate { //Force the invalidate to happen on the UI thread to satisfy WPF this.Dispatcher.Invoke(DispatcherPriority.Normal, new DispatcherOperationCallback(delegate { //Guard against the view closing before dispatcher executes this. if (!_isDisposed) { this.InvalidateVisual(); this.RedrawAdornments(); } return(null); }), null); }); } else { //no highlight == no adornments or marks. _layer.RemoveAllAdornments(); this.InvalidateVisual(); } return(true); } else if ((oldHighlight != null) && wereAdornmentsEnabled && this.AdornmentsActive) { //The highlight didn't change and isn't null ... therefore both old & new highlight spans have values. Update the adornments so we don't highlight the //match the caret is on. SnapshotSpan translatedOldHighlightSpan = oldHighlightSpan.Value.TranslateTo(_textView.TextSnapshot, SpanTrackingMode.EdgeInclusive); if (translatedOldHighlightSpan != _highlightSpan.Value) { //The spans moved (e.g. the user moved from this on one line to this on another). //Remove the adornment from the new highlight. _layer.RemoveAdornmentsByVisualSpan(_highlightSpan.Value); //Add an adornment at the old source of the highlight span. Geometry g = _textView.TextViewLines.GetMarkerGeometry(translatedOldHighlightSpan); if (g != null) { _layer.AddAdornment(translatedOldHighlightSpan, null, new GeometryAdornment(_adornmentMatchBrush, g)); } } } } return(false); }
/// <summary> /// Start a background search for all instances of this.highlight. /// </summary> private void UpdateMarginMatches(bool force) { if (((this.matchBrush != null) && this.IsVisible) || (this.adornmentMatchBrush != null)) { SnapshotSpan?oldHighlightSpan = this.highlightSpan; string oldHighlight = this.highlight; this.highlightSpan = BackgroundSearch.GetExtentOfWord(this.textView.Caret.Position.BufferPosition); this.highlight = this.highlightSpan.HasValue ? this.highlightSpan.Value.GetText() : null; if ((this.highlight != oldHighlight) || force) { //The text of the highlight changes ... restart the search. if (this.search != null) { //Stop and blow away the old search (even if it didn't finish, the results are not interesting anymore). this.search.Abort(); this.search = null; } if (this.highlight != null) { //The underlying buffer could be very large, meaning that doing the search for all matches on the UI thread //is a bad idea. Do the search on the background thread and use a callback to invalidate the visual when //the entire search has completed. this.search = new BackgroundSearch(this.textView.TextSnapshot, this.highlight, delegate { //Force the invalidate to happen on the UI thread to satisfy WPF this.Dispatcher.Invoke(DispatcherPriority.Normal, new DispatcherOperationCallback(delegate { this.InvalidateVisual(); this.RedrawAdornments(null); return(null); }), null); }); } else { //no highlight == no adornments or marks. this.layer.RemoveAllAdornments(); this.InvalidateVisual(); } } else if (oldHighlight != null) { //The highlight didn't change and isn't null ... therefore both old & new highlight spans have values. SnapshotSpan translatedOldHighlightSpan = oldHighlightSpan.Value.TranslateTo(this.textView.TextSnapshot, SpanTrackingMode.EdgeInclusive); if (translatedOldHighlightSpan != this.highlightSpan.Value) { if (this.adornmentMatchBrush != null) { //The spans moved (e.g. the user moved from this on one line to this on another). //Remove the adornment from the new highlight. this.layer.RemoveAdornmentsByVisualSpan(this.highlightSpan.Value); //Add an adornment at the old source of the highlight span. Geometry g = this.textView.TextViewLines.GetMarkerGeometry(translatedOldHighlightSpan); if (g != null) { this.layer.AddAdornment(translatedOldHighlightSpan, null, new GeometryAdornment(this.adornmentMatchBrush, g)); } } //We also need to update the caret position in the margin. this.InvalidateVisual(); } } } else if (this.IsVisible && (this.caretBrush != null)) { //Neither the match brush nor the adornment brush exists so we won't be doing a background search. //But we are visible and the caret brush exists, so invalidate the visual so that we can update the location of the caret. this.InvalidateVisual(); } }