private void UpdateEventHandlers(bool?forceEvents) { bool needEvents = forceEvents.HasValue ? forceEvents.Value : (this.IsVisible || (this.adornmentMatchBrush != null)); if (needEvents != this.hasEvents) { this.hasEvents = needEvents; if (needEvents) { this.textView.LayoutChanged += OnLayoutChanged; this.textView.Caret.PositionChanged += OnPositionChanged; this.scrollBar.TrackSpanChanged += OnTrackSpanChanged; //Rescan everything since things might have changed. this.UpdateMarginMatches(true); } else { this.textView.LayoutChanged -= OnLayoutChanged; this.textView.Caret.PositionChanged -= OnPositionChanged; this.scrollBar.TrackSpanChanged -= OnTrackSpanChanged; if (this.search != null) { this.search.Abort(); this.search = null; } this.highlight = null; this.highlightSpan = null; } } }
public async Task <PagedSearchResult <Background> > SearchBackgrounds(BackgroundSearch backgroundSearch) { var filter = ""; if (!string.IsNullOrEmpty(backgroundSearch.Name)) { filter = $"Name eq '{backgroundSearch.Name}'"; } if (backgroundSearch.ContentType.HasValue && backgroundSearch.ContentType != ContentType.None) { if (!string.IsNullOrEmpty(filter)) { filter = $"{filter} and"; } filter = $"{filter} ContentType eq '{backgroundSearch.ContentType.ToString()}'"; } var query = new TableQuery <Background>().Where(filter); var backgrounds = await _tableStorage.QueryAsync("backgrounds", query); switch (backgroundSearch.BackgroundSearchOrdering) { case BackgroundSearchOrdering.NameAscending: backgrounds = backgrounds.OrderBy(p => p.Name); break; case BackgroundSearchOrdering.NameDescending: backgrounds = backgrounds.OrderByDescending(p => p.Name); break; case BackgroundSearchOrdering.ContentTypeAscending: backgrounds = backgrounds.OrderBy(p => p.ContentType); break; case BackgroundSearchOrdering.ContentTypeDescending: backgrounds = backgrounds.OrderByDescending(p => p.ContentType); break; } return(new PagedSearchResult <Background>(backgrounds.ToList(), backgroundSearch.PageSize, backgroundSearch.CurrentPage)); }
public static int?GetMatch(string searchText, string text, int offset) { while (true) { int match = text.IndexOf(searchText, offset, StringComparison.Ordinal); if (match == -1) { break; } if (BackgroundSearch.IsCompleteWord(match, searchText.Length, text)) { return(match); } //Continue searching where the old search left off. offset = match + 1; } return(null); }
private bool UpdateEventHandlers(bool checkEvents, bool wereAdornmentsEnabled = true) { bool needEvents = checkEvents && _textView.VisualElement.IsVisible && (this.MarginActive || this.AdornmentsActive); if (needEvents != _hasEvents) { _hasEvents = needEvents; if (needEvents) { _editorFormatMap.FormatMappingChanged += OnFormatMappingChanged; _textView.LayoutChanged += OnLayoutChanged; _textView.Selection.SelectionChanged += OnPositionChanged; _scrollBar.Map.MappingChanged += OnMappingChanged; this.OnFormatMappingChanged(null, null); return(this.UpdateMatches(wereAdornmentsEnabled)); } else { _editorFormatMap.FormatMappingChanged -= OnFormatMappingChanged; _textView.LayoutChanged -= OnLayoutChanged; _textView.Selection.SelectionChanged -= OnPositionChanged; _scrollBar.Map.MappingChanged -= OnMappingChanged; if (_search != null) { _search.Abort(); _search = null; } _highlight = null; _highlightSpan = null; } } return(false); }
/// <summary> /// Search for all instances of <paramref name="searchText"/> in <paramref name="snapshot"/>. Call /// <paramref name="completionCallback"/> once the search has completed. /// </summary> /// <param name="snapshot">Text snapshot in which to search.</param> /// <param name="searchText">Test to search for.</param> /// <param name="completionCallback">Delegate to call if the search is completed (will be called on the UI thread).</param> /// <remarks>The constructor must be called from the UI thread.</remarks> public BackgroundSearch(ITextSnapshot snapshot, string searchText, bool matchWholeWord, Action completionCallback) { this.Snapshot = snapshot; ThreadPool.QueueUserWorkItem(delegate(object state) { //Lower our priority so that we do not compete with the rendering. System.Threading.Thread.CurrentThread.Priority = ThreadPriority.Lowest; System.Threading.Thread.CurrentThread.IsBackground = true; List <SnapshotSpan> newMatches = new List <SnapshotSpan>(); int start = 0; while (true) { int end = Math.Min(snapshot.Length, start + MatchMarginElement.SearchBufferSize); string text = snapshot.GetText(start, end - start); int offset = (start == 0) ? 0 : 1; while (true) { int match = text.IndexOf(searchText, offset, StringComparison.Ordinal); if (match == -1) { break; } if (matchWholeWord) { //Make sure the character preceeding the match is a word break //(or the very start of the buffer, which is the only time match can equal 0). if ((match == 0) || !BackgroundSearch.IsWordCharacter(text[match - 1])) { //Make sure the character after the match is a word break. //If we're at the end of text, then it is only considered a word break if that is also the very end of the buffer. if ((match + searchText.Length == text.Length) ? (end == snapshot.Length) : !BackgroundSearch.IsWordCharacter(text[match + searchText.Length])) { SnapshotSpan matchSpan = new SnapshotSpan(snapshot, match + start, searchText.Length); newMatches.Add(matchSpan); } } } else { //Any match is a match. SnapshotSpan matchSpan = new SnapshotSpan(snapshot, match + start, searchText.Length); newMatches.Add(matchSpan); } //Continue searching at the location of the next possible match //(we could add one more since there needs to be one character of whitespace between matches, but //then we'd need an if to guard against placing offset past the end of text). offset = match + searchText.Length; } //Check to see if the search should be aborted because no one cares about the result any more. if (_abort) { return; } if (end == snapshot.Length) { break; //All done. } //rollback from the end enough so that we can match something that we previously matched at the very end of the //(along with the preceeding character so we can ensure it starts on a word break). start = end - (searchText.Length + 1); } //This should be a thread safe operation since it is atomic _matches = newMatches; completionCallback(); }); }
/// <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); }
public async Task <ActionResult <IEnumerable <Background> > > Get([FromQuery] BackgroundSearch backgroundSearch) { var backgrounds = await _backgroundManager.SearchBackgrounds(backgroundSearch); return(Ok(backgrounds)); }
/// <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(); } }