diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/ISearchStrategy.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/ISearchStrategy.cs index d54f998c31..fd10474e9f 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/ISearchStrategy.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/ISearchStrategy.cs @@ -10,33 +10,45 @@ using ICSharpCode.AvalonEdit.Document; namespace ICSharpCode.AvalonEdit.Search { /// - /// Description of ISearchStrategy. + /// Basic interface for search algorithms. /// public interface ISearchStrategy { + /// + /// Finds all matches for a predicate in the given ITextSource. + /// + /// This method is thread-safe. IEnumerable FindAll(ITextSource document); } + /// + /// Represents a search result. + /// public interface ISearchResult : ISegment { } + /// public class SearchPatternException : Exception, ISerializable { + /// public SearchPatternException() { } - - public SearchPatternException(string message) : base(message) + + /// + public SearchPatternException(string message) : base(message) { } - + + /// public SearchPatternException(string message, Exception innerException) : base(message, innerException) { } // This constructor is needed for serialization. + /// protected SearchPatternException(SerializationInfo info, StreamingContext context) : base(info, context) { } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchCommands.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchCommands.cs index bcc472dd80..71ea4226ac 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchCommands.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchCommands.cs @@ -14,12 +14,22 @@ using ICSharpCode.AvalonEdit.Rendering; namespace ICSharpCode.AvalonEdit.Search { + /// + /// Search commands for AvalonEdit. + /// public static class SearchCommands { + /// + /// Finds the next occurrence in the file. + /// public static readonly RoutedCommand FindNext = new RoutedCommand( "FindNext", typeof(SearchPanel), new InputGestureCollection { new KeyGesture(Key.F3) } ); + + /// + /// Finds the previous occurrence in the file. + /// public static readonly RoutedCommand FindPrevious = new RoutedCommand( "FindPrevious", typeof(SearchPanel), new InputGestureCollection { new KeyGesture(Key.F3, ModifierKeys.Shift) } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchPanel.xaml b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchPanel.xaml index 599e97ab8b..f7302bf84c 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchPanel.xaml +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchPanel.xaml @@ -16,7 +16,7 @@ - + @@ -30,10 +30,11 @@ - diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchPanel.xaml.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchPanel.xaml.cs index b7c9b0b2c8..ae61516157 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchPanel.xaml.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchPanel.xaml.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Windows; @@ -30,13 +31,16 @@ namespace ICSharpCode.AvalonEdit.Search SearchResult currentResult; FoldingManager foldingManager; - public bool UseRegex { get { return useRegex.IsChecked == true; } } - public bool MatchCase { get { return matchCase.IsChecked == true; } } - public bool WholeWords { get { return wholeWords.IsChecked == true; } } + bool UseRegex { get { return useRegex.IsChecked == true; } } + bool MatchCase { get { return matchCase.IsChecked == true; } } + bool WholeWords { get { return wholeWords.IsChecked == true; } } string searchPattern; ISearchStrategy strategy; + /// + /// Gets/sets the content of the search box and the search string. + /// public string SearchPattern { get { return searchPattern; } set { @@ -45,18 +49,16 @@ namespace ICSharpCode.AvalonEdit.Search } } - void UpdateSearch(bool throwException = true) + void UpdateSearch() { - if (!string.IsNullOrEmpty(searchPattern)) { - try { - strategy = SearchStrategyFactory.Create(searchPattern, !MatchCase, UseRegex, WholeWords); - DoSearch(true); - } catch (SearchPatternException) { - if (throwException) throw; - } - } + messageView.IsOpen = false; + strategy = SearchStrategyFactory.Create(searchPattern ?? "", !MatchCase, UseRegex, WholeWords); + DoSearch(true); } + /// + /// Creates a new SearchPanel and attaches it to a text area. + /// public SearchPanel(TextArea textArea) { if (textArea == null) @@ -73,13 +75,25 @@ namespace ICSharpCode.AvalonEdit.Search textArea.Document.TextChanged += delegate { DoSearch(false); }; this.Loaded += delegate { searchTextBox.Focus(); }; - useRegex.Checked += delegate { UpdateSearch(false); }; - matchCase.Checked += delegate { UpdateSearch(false); }; - wholeWords.Checked += delegate { UpdateSearch(false); }; + useRegex.Checked += ValidateSearchText; + matchCase.Checked += ValidateSearchText; + wholeWords.Checked += ValidateSearchText; - useRegex.Unchecked += delegate { UpdateSearch(false); }; - matchCase.Unchecked += delegate { UpdateSearch(false); }; - wholeWords.Unchecked += delegate { UpdateSearch(false); }; + useRegex.Unchecked += ValidateSearchText; + matchCase.Unchecked += ValidateSearchText; + wholeWords.Unchecked += ValidateSearchText; + } + + void ValidateSearchText(object sender, RoutedEventArgs e) + { + var be = searchTextBox.GetBindingExpression(TextBox.TextProperty); + try { + Validation.ClearInvalid(be); + UpdateSearch(); + } catch (SearchPatternException ex) { + var ve = new ValidationError(be.ParentBinding.ValidationRules[0], be, ex.Message, ex); + Validation.MarkInvalid(be, ve); + } } /// @@ -91,6 +105,9 @@ namespace ICSharpCode.AvalonEdit.Search searchTextBox.SelectAll(); } + /// + /// Moves to the next occurrence in the file. + /// public void FindNext() { SearchResult result = null; @@ -104,6 +121,9 @@ namespace ICSharpCode.AvalonEdit.Search } } + /// + /// Moves to the previous occurrence in the file. + /// public void FindPrevious() { SearchResult result = null; @@ -155,17 +175,21 @@ namespace ICSharpCode.AvalonEdit.Search { switch (e.Key) { case Key.Enter: - try { - messageView.IsOpen = false; - messageView.Content = null; - UpdateSearch(); - } catch (SearchPatternException ex) { - messageView.Content = "Error: " + ex.Message; + e.Handled = true; + messageView.IsOpen = false; + if ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift) + FindPrevious(); + else + FindNext(); + var error = Validation.GetErrors(searchTextBox).FirstOrDefault(); + if (error != null) { + messageView.Content = "Error: " + error.ErrorContent; messageView.PlacementTarget = searchTextBox; messageView.IsOpen = true; } break; case Key.Escape: + e.Handled = true; CloseClick(sender, e); break; } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchStrategyFactory.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchStrategyFactory.cs index 85a126a6f8..4df76ad654 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchStrategyFactory.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchStrategyFactory.cs @@ -3,11 +3,18 @@ using System; using System.Text.RegularExpressions; +using System.Windows.Controls; namespace ICSharpCode.AvalonEdit.Search { + /// + /// Provides factory methods for ISearchStrategies. + /// public class SearchStrategyFactory { + /// + /// Creates a default ISearchStrategy with the given parameters. + /// public static ISearchStrategy Create(string searchPattern, bool ignoreCase, bool useRegularExpressions, bool matchWholeWords) { if (searchPattern == null)