Browse Source

Implemented incremental search for AvalonEdit.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@5270 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
pull/1/head
Daniel Grunwald 17 years ago
parent
commit
923174fbbe
  1. 8
      AddIns/ICSharpCode.SharpDevelop.addin
  2. 14
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.addin
  3. 5
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj
  4. 0
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Resources/IncrementalSearchCursor.cur
  5. 0
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Resources/ReverseIncrementalSearchCursor.cur
  6. 44
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/Commands/RunIncrementalSearch.cs
  7. 319
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/IncrementalSearch.cs
  8. 3
      src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
  9. 2
      src/Main/Base/Project/Src/Project/BuildEngine.cs
  10. 37
      src/Main/Base/Project/Src/TextEditor/Commands/SearchCommands.cs
  11. 410
      src/Main/Base/Project/Src/TextEditor/Gui/Editor/IncrementalSearch.cs
  12. 35
      src/Main/Base/Project/Src/Util/ExtensionMethods.cs

8
AddIns/ICSharpCode.SharpDevelop.addin

@ -1532,14 +1532,6 @@
</MenuItem> <!-- end DEBUG menu --> </MenuItem> <!-- end DEBUG menu -->
<MenuItem id = "Search" label = "${res:XML.MainMenu.SearchMenu}" type="Menu"> <MenuItem id = "Search" label = "${res:XML.MainMenu.SearchMenu}" type="Menu">
<MenuItem id = "SearchIncremental"
label = "${res:XML.MainMenu.SearchMenu.IncrementalSearch}"
shortcut = "Control|E"
class = "ICSharpCode.SharpDevelop.DefaultEditor.Commands.RunIncrementalSearch"/>
<MenuItem id = "SearchReverseIncremental"
label = "${res:XML.MainMenu.SearchMenu.ReverseIncrementalSearch}"
shortcut = "Control|Shift|E"
class = "ICSharpCode.SharpDevelop.DefaultEditor.Commands.RunReverseIncrementalSearch"/>
<MenuItem id = "SearchInFilesSeparator" type = "Separator" /> <MenuItem id = "SearchInFilesSeparator" type = "Separator" />
<!-- <Condition name = "WindowActive" activewindow="ICSharpCode.SharpDevelop.Editor.ITextEditorProvider" action="Disable"> --> <!-- <Condition name = "WindowActive" activewindow="ICSharpCode.SharpDevelop.Editor.ITextEditorProvider" action="Disable"> -->
<MenuItem id = "ToggleBookmark" <MenuItem id = "ToggleBookmark"

14
src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.addin

@ -101,6 +101,20 @@
</MenuItem> </MenuItem>
</Path> </Path>
<Path name = "/SharpDevelop/Workbench/MainMenu/Search">
<Condition name = "WindowActive" activewindow="ICSharpCode.SharpDevelop.Editor.ITextEditorProvider" action="Disable">
<MenuItem id = "SearchIncremental"
label = "${res:XML.MainMenu.SearchMenu.IncrementalSearch}"
shortcut = "Control|E"
class = "ICSharpCode.AvalonEdit.AddIn.Commands.RunIncrementalSearch"/>
<MenuItem id = "SearchReverseIncremental"
label = "${res:XML.MainMenu.SearchMenu.ReverseIncrementalSearch}"
shortcut = "Control|Shift|E"
class = "ICSharpCode.AvalonEdit.AddIn.Commands.RunReverseIncrementalSearch"
insertbefore = "SearchInFilesSeparator"/>
</Condition>
</Path>
<Path name = "/SharpDevelop/Dialogs/OptionsDialog/CodingOptions"> <Path name = "/SharpDevelop/Dialogs/OptionsDialog/CodingOptions">
<OptionPanel id = "CodeTemplates" <OptionPanel id = "CodeTemplates"
label = "${res:Dialog.Options.CodeTemplatesText}" label = "${res:Dialog.Options.CodeTemplatesText}"

5
src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj

@ -78,6 +78,7 @@
<Compile Include="Src\CodeEditorAdapter.cs" /> <Compile Include="Src\CodeEditorAdapter.cs" />
<Compile Include="Src\CodeEditorView.cs" /> <Compile Include="Src\CodeEditorView.cs" />
<Compile Include="Src\Commands\FoldingCommands.cs" /> <Compile Include="Src\Commands\FoldingCommands.cs" />
<Compile Include="Src\Commands\RunIncrementalSearch.cs" />
<Compile Include="Src\Commands\SortOptionsDialog.xaml.cs"> <Compile Include="Src\Commands\SortOptionsDialog.xaml.cs">
<DependentUpon>SortOptionsDialog.xaml</DependentUpon> <DependentUpon>SortOptionsDialog.xaml</DependentUpon>
<SubType>Code</SubType> <SubType>Code</SubType>
@ -85,6 +86,7 @@
<Compile Include="Src\Commands\SortSelectionCommand.cs" /> <Compile Include="Src\Commands\SortSelectionCommand.cs" />
<Compile Include="Src\Commands\SurroundWithCommand.cs" /> <Compile Include="Src\Commands\SurroundWithCommand.cs" />
<Compile Include="Src\CustomCommands.cs" /> <Compile Include="Src\CustomCommands.cs" />
<Compile Include="Src\IncrementalSearch.cs" />
<Compile Include="Src\InlineUIElementGenerator.cs" /> <Compile Include="Src\InlineUIElementGenerator.cs" />
<Compile Include="Src\IconBarManager.cs" /> <Compile Include="Src\IconBarManager.cs" />
<Compile Include="Src\IconBarMargin.cs" /> <Compile Include="Src\IconBarMargin.cs" />
@ -117,8 +119,11 @@
<Compile Include="Src\SharpDevelopInsightWindow.cs"> <Compile Include="Src\SharpDevelopInsightWindow.cs">
</Compile> </Compile>
<Compile Include="Src\TextMarkerService.cs" /> <Compile Include="Src\TextMarkerService.cs" />
<EmbeddedResource Include="Resources\IncrementalSearchCursor.cur" />
<EmbeddedResource Include="Resources\ReverseIncrementalSearchCursor.cur" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="Resources" />
<Folder Include="Src" /> <Folder Include="Src" />
<Folder Include="Src\Commands" /> <Folder Include="Src\Commands" />
<Folder Include="Src\Options" /> <Folder Include="Src\Options" />

0
src/Main/Base/Project/Resources/IncrementalSearchCursor.cur → src/AddIns/DisplayBindings/AvalonEdit.AddIn/Resources/IncrementalSearchCursor.cur

Before

Width:  |  Height:  |  Size: 326 B

After

Width:  |  Height:  |  Size: 326 B

0
src/Main/Base/Project/Resources/ReverseIncrementalSearchCursor.cur → src/AddIns/DisplayBindings/AvalonEdit.AddIn/Resources/ReverseIncrementalSearchCursor.cur

Before

Width:  |  Height:  |  Size: 326 B

After

Width:  |  Height:  |  Size: 326 B

44
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/Commands/RunIncrementalSearch.cs

@ -0,0 +1,44 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <author name="Daniel Grunwald"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Windows.Documents;
using ICSharpCode.AvalonEdit.Editing;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Gui;
namespace ICSharpCode.AvalonEdit.AddIn.Commands
{
public class RunIncrementalSearch : AbstractMenuCommand
{
public override void Run()
{
ITextEditorProvider provider = WorkbenchSingleton.Workbench.ActiveViewContent as ITextEditorProvider;
if (provider != null) {
TextArea textArea = provider.TextEditor.GetService(typeof(TextArea)) as TextArea;
if (textArea != null) {
textArea.ActiveInputHandler = new IncrementalSearch(textArea, LogicalDirection.Forward);
}
}
}
}
public class RunReverseIncrementalSearch : AbstractMenuCommand
{
public override void Run()
{
ITextEditorProvider provider = WorkbenchSingleton.Workbench.ActiveViewContent as ITextEditorProvider;
if (provider != null) {
TextArea textArea = provider.TextEditor.GetService(typeof(TextArea)) as TextArea;
if (textArea != null) {
textArea.ActiveInputHandler = new IncrementalSearch(textArea, LogicalDirection.Backward);
}
}
}
}
}

319
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/IncrementalSearch.cs

@ -0,0 +1,319 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <author name="Daniel Grunwald"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Text;
using System.Linq;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Input;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Editing;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop;
namespace ICSharpCode.AvalonEdit.AddIn
{
/// <summary>
/// IncrementalSearch handler for AvalonEdit.
/// </summary>
public sealed class IncrementalSearch : ITextAreaInputHandler
{
readonly LogicalDirection direction;
readonly TextArea textArea;
StringBuilder searchText = new StringBuilder();
string text;
int startIndex;
int originalStartIndex;
bool passedEndOfDocument;
public IncrementalSearch(TextArea textArea, LogicalDirection direction)
{
if (textArea == null)
throw new ArgumentNullException("textArea");
this.textArea = textArea;
this.direction = direction;
}
#region Attach + Detach
TextArea ITextAreaInputHandler.TextArea {
get { return textArea; }
}
void ITextAreaInputHandler.Attach()
{
textArea.PreviewKeyDown += textArea_PreviewKeyDown;
textArea.TextEntering += textArea_TextEntering;
textArea.LostFocus += textArea_LostFocus;
textArea.PreviewMouseDown += textArea_PreviewMouseDown;
EnableIncrementalSearchCursor();
// Get text to search and initial search position.
text = textArea.Document.Text;
startIndex = textArea.Caret.Offset;
originalStartIndex = startIndex;
GetInitialSearchText();
ShowTextFound(searchText.ToString());
}
void ITextAreaInputHandler.Detach()
{
textArea.PreviewKeyDown -= textArea_PreviewKeyDown;
textArea.TextEntering -= textArea_TextEntering;
textArea.LostFocus -= textArea_LostFocus;
textArea.PreviewMouseDown -= textArea_PreviewMouseDown;
DisableIncrementalSearchCursor();
ClearStatusBarMessage();
}
public void StopIncrementalSearch()
{
if (textArea.ActiveInputHandler == this) {
textArea.ActiveInputHandler = textArea.DefaultInputHandler;
}
}
/// <summary>
/// Gets the initial text to include in the incremental search.
/// </summary>
/// <remarks>
/// If multiple lines are selected then no initial search text
/// is set.
/// </remarks>
void GetInitialSearchText()
{
if (!textArea.Selection.IsEmpty) {
startIndex = textArea.Selection.SurroundingSegment.Offset;
if (!textArea.Selection.IsMultiline(textArea.Document)) {
searchText.Append(textArea.Selection.GetText(textArea.Document));
}
}
}
/// <summary>
/// Changes the text editor's cursor so the user knows we are in
/// incremental search mode.
/// </summary>
void EnableIncrementalSearchCursor()
{
string resourceName = "ICSharpCode.AvalonEdit.AddIn.Resources.IncrementalSearchCursor.cur";
if (direction == LogicalDirection.Backward) {
resourceName = "ICSharpCode.AvalonEdit.AddIn.Resources.ReverseIncrementalSearchCursor.cur";
}
textArea.Cursor = new Cursor(GetType().Assembly.GetManifestResourceStream(resourceName));
}
void DisableIncrementalSearchCursor()
{
textArea.ClearValue(TextArea.CursorProperty);
}
#endregion
#region Status bar functions
/// <summary>
/// Shows the status bar message. All messages are prefixed
/// with the standard Incremental Search string.
/// </summary>
void ShowTextFound(string find)
{
if (passedEndOfDocument) {
ShowMessage(String.Concat(find, StringParser.Parse(" ${res:ICSharpCode.SharpDevelop.DefaultEditor.IncrementalSearch.PassedEndOfDocumentStatusBarMessage}")), true);
} else {
ShowMessage(find, false);
}
}
/// <summary>
/// Shows a highlighed status bar message.
/// </summary>
void ShowMessage(string message, bool highlight)
{
string incrementalSearchStartMessage;
if (direction == LogicalDirection.Forward) {
incrementalSearchStartMessage = StringParser.Parse("${res:ICSharpCode.SharpDevelop.DefaultEditor.IncrementalSearch.ForwardsSearchStatusBarMessage} ");
} else {
incrementalSearchStartMessage = StringParser.Parse("${res:ICSharpCode.SharpDevelop.DefaultEditor.IncrementalSearch.ReverseSearchStatusBarMessage} ");
}
string fullMessage = incrementalSearchStartMessage + message;
StatusBarService.SetMessage(fullMessage, highlight);
}
/// <summary>
/// Shows a status bar message indicating that the specified text
/// was not found.
/// </summary>
void ShowTextNotFound(string find)
{
ShowMessage(find + StringParser.Parse(" ${res:ICSharpCode.SharpDevelop.DefaultEditor.IncrementalSearch.NotFoundStatusBarMessage}"), true);
}
/// <summary>
/// Clears the status bar message.
/// </summary>
void ClearStatusBarMessage()
{
StatusBarService.SetMessage(String.Empty);
}
#endregion
#region Running the search
void HighlightText(int offset, int length)
{
int endOffset = offset + length;
textArea.Caret.Offset = endOffset;
textArea.Selection = new SimpleSelection(offset, endOffset);
}
/// <summary>
/// Runs the incremental search either forwards or backwards.
/// </summary>
void RunSearch()
{
string find = searchText.ToString();
int index = FindText(find, startIndex);
if (index == -1) {
index = FindText(find, GetWrapAroundStartIndex());
passedEndOfDocument = true;
}
// Highlight found text and show status bar message.
if (index >= 0) {
startIndex = index;
HighlightText(index, find.Length);
ShowTextFound(find);
} else {
ShowTextNotFound(find);
}
}
/// <summary>
/// Gets the index the search should start from when we wrap around. This
/// is either the start of the string or the very end depending on which
/// way we are searching.
/// </summary>
int GetWrapAroundStartIndex()
{
int wrapAroundIndex = 0;
if (direction == LogicalDirection.Backward) {
wrapAroundIndex = text.Length - 1;
}
return wrapAroundIndex;
}
/// <summary>
/// Looks for the string from the last position we searched from. The
/// search is case insensitive if all the characters of the search
/// string are lower case. If one of the search characters is upper case
/// then the search is case sensitive. The search can be either forwards
/// or backwards.
/// </summary>
int FindText(string find, int startIndex)
{
StringComparison stringComparison = GetStringComparisonType(find);
if (direction == LogicalDirection.Forward) {
return text.IndexOf(find, startIndex, stringComparison);
} else {
// Reverse search.
string searchText = GetReverseSearchText(startIndex + find.Length);
return searchText.LastIndexOf(find, stringComparison);
}
}
/// <summary>
/// Gets whether the search string comparision should be case
/// sensitive. If all the characters of the find string are lower case
/// then the search is case insensitive. If any character is upper case
/// then the search is case sensitive.
/// </summary>
StringComparison GetStringComparisonType(string find)
{
foreach (char c in find) {
if (Char.IsUpper(c)) {
return StringComparison.InvariantCulture;
}
}
return StringComparison.InvariantCultureIgnoreCase;
}
/// <summary>
/// Gets the text to search when doing a reverse search.
/// </summary>
string GetReverseSearchText(int endIndex)
{
if (endIndex < text.Length) {
return text.Substring(0, endIndex);
}
endIndex = text.Length - 1;
if (endIndex >= 0) {
return text;
}
return String.Empty;
}
#endregion
#region Event Handlers
void textArea_PreviewKeyDown(object sender, KeyEventArgs e)
{
switch (e.Key) {
case Key.Enter:
case Key.Escape:
e.Handled = true;
StopIncrementalSearch();
break;
case Key.Back:
// Remove last search char and try search again.
int length = searchText.Length;
if (length > 0) {
searchText.Remove(length - 1, 1);
// Run search back at original starting point.
startIndex = originalStartIndex;
passedEndOfDocument = false;
RunSearch();
e.Handled = true;
} else {
StopIncrementalSearch();
}
break;
default:
TextAreaInputHandler[] rootHandlers = { textArea.DefaultInputHandler };
var allHandlers = rootHandlers.Flatten(h => h.NestedInputHandlers.OfType<TextAreaInputHandler>());
var keyGestures = allHandlers.SelectMany(h => h.InputBindings).Select(h => h.Gesture).OfType<KeyGesture>();
foreach (KeyGesture gesture in keyGestures) {
if (gesture.Key == e.Key) {
StopIncrementalSearch();
break;
}
}
break;
}
}
void textArea_TextEntering(object sender, TextCompositionEventArgs e)
{
searchText.Append(e.Text);
e.Handled = true;
RunSearch();
}
void textArea_LostFocus(object sender, RoutedEventArgs e)
{
StopIncrementalSearch();
}
void textArea_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
StopIncrementalSearch();
}
#endregion
}
}

3
src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj

@ -721,9 +721,6 @@
<DependentUpon>SolutionConfigurationEditor.cs</DependentUpon> <DependentUpon>SolutionConfigurationEditor.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Src\Gui\Dialogs\SolutionConfiguration\SolutionConfigurationEditor.cs" /> <Compile Include="Src\Gui\Dialogs\SolutionConfiguration\SolutionConfigurationEditor.cs" />
<Compile Include="Src\TextEditor\Gui\Editor\IncrementalSearch.cs" />
<EmbeddedResource Include="Resources\IncrementalSearchCursor.cur" />
<EmbeddedResource Include="Resources\ReverseIncrementalSearchCursor.cur" />
<Compile Include="Src\Gui\Dialogs\SharpDevelopColorDialog.cs" /> <Compile Include="Src\Gui\Dialogs\SharpDevelopColorDialog.cs" />
<Compile Include="Src\Util\ExtensionMethods.cs" /> <Compile Include="Src\Util\ExtensionMethods.cs" />
<Compile Include="Src\Util\DebugTimer.cs" /> <Compile Include="Src\Util\DebugTimer.cs" />

2
src/Main/Base/Project/Src/Project/BuildEngine.cs

@ -694,7 +694,7 @@ namespace ICSharpCode.SharpDevelop.Project
lock (this) { lock (this) {
if (progressMonitor != null) { if (progressMonitor != null) {
progressMonitor.TaskName = "${res:MainWindow.CompilerMessages.BuildVerb} " progressMonitor.TaskName = "${res:MainWindow.CompilerMessages.BuildVerb} "
+ projectsCurrentlyBuilding.Select(n => n.project.Name).Join(", ") + string.Join(", ", projectsCurrentlyBuilding.Select(n => n.project.Name))
+ "..."; + "...";
} }
} }

37
src/Main/Base/Project/Src/TextEditor/Commands/SearchCommands.cs

@ -7,9 +7,7 @@
using System; using System;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor;
using ICSharpCode.SharpDevelop.Gui; using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.TextEditor.Actions;
namespace ICSharpCode.SharpDevelop.DefaultEditor.Commands namespace ICSharpCode.SharpDevelop.DefaultEditor.Commands
{ {
@ -20,39 +18,4 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Commands
GotoDialog.ShowSingleInstance(); GotoDialog.ShowSingleInstance();
} }
} }
/*
public class RunIncrementalSearch : AbstractMenuCommand
{
static IncrementalSearch incrementalSearch;
public override void Run()
{
IWorkbenchWindow window = WorkbenchSingleton.Workbench.ActiveWorkbenchWindow;
if (window != null) {
ITextEditorControlProvider textEditorControlProvider = window.ActiveViewContent as ITextEditorControlProvider;
if (textEditorControlProvider != null) {
if (incrementalSearch != null) {
incrementalSearch.Dispose();
}
incrementalSearch = new IncrementalSearch(textEditorControlProvider.TextEditorControl, Forwards);
}
}
}
protected virtual bool Forwards {
get {
return true;
}
}
}
public class RunReverseIncrementalSearch : RunIncrementalSearch
{
protected override bool Forwards {
get {
return false;
}
}
}*/
} }

410
src/Main/Base/Project/Src/TextEditor/Gui/Editor/IncrementalSearch.cs

@ -1,410 +0,0 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Text;
using System.Windows.Forms;
using ICSharpCode.Core;
using ICSharpCode.TextEditor;
using ICSharpCode.TextEditor.Document;
namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor
{
/*
public class IncrementalSearch : IDisposable
{
bool disposed;
TextEditorControl textEditor;
ICSharpCode.TextEditor.Document.IFormattingStrategy previousFormattingStrategy;
string incrementalSearchStartMessage;
StringBuilder searchText = new StringBuilder();
string text;
int startIndex;
int originalStartIndex;
Cursor cursor;
bool passedEndOfDocument;
bool codeCompletionEnabled;
// Indicates whether this is a forward search or a reverse search.
bool forwards = true;
/// <summary>
/// Dummy formatting strategy that stops the FormatLine method
/// from automatically adding things like xml comment tags.
/// </summary>
class IncrementalSearchFormattingStrategy : DefaultFormattingStrategy
{
public override void FormatLine(TextArea textArea, int line, int cursorOffset, char ch)
{
}
}
/// <summary>
/// Creates a incremental search that goes forwards.
/// </summary>
public IncrementalSearch(TextEditorControl textEditor)
: this(textEditor, true)
{
}
/// <summary>
/// Creates an incremental search for the specified text editor.
/// </summary>
/// <param name="textEditor">The text editor to search in.</param>
/// <param name="forwards">Indicates whether the search goes
/// forward from the cursor or backwards.</param>
public IncrementalSearch(TextEditorControl textEditor, bool forwards)
{
this.forwards = forwards;
if (forwards) {
incrementalSearchStartMessage = StringParser.Parse("${res:ICSharpCode.SharpDevelop.DefaultEditor.IncrementalSearch.ForwardsSearchStatusBarMessage} ");
} else {
incrementalSearchStartMessage = StringParser.Parse("${res:ICSharpCode.SharpDevelop.DefaultEditor.IncrementalSearch.ReverseSearchStatusBarMessage} ");
}
this.textEditor = textEditor;
// Disable code completion.
codeCompletionEnabled = CodeCompletionOptions.EnableCodeCompletion;
CodeCompletionOptions.EnableCodeCompletion = false;
AddFormattingStrategy();
TextArea.KeyEventHandler += TextAreaKeyPress;
TextArea.DoProcessDialogKey += TextAreaProcessDialogKey;
TextArea.LostFocus += TextAreaLostFocus;
TextArea.MouseClick += TextAreaMouseClick;
EnableIncrementalSearchCursor();
// Get text to search and initial search position.
text = textEditor.Document.TextContent;
startIndex = TextArea.Caret.Offset;
originalStartIndex = startIndex;
GetInitialSearchText();
ShowTextFound(searchText.ToString());
}
public void Dispose()
{
if (!disposed) {
disposed = true;
TextArea.KeyEventHandler -= TextAreaKeyPress;
TextArea.DoProcessDialogKey -= TextAreaProcessDialogKey;
TextArea.LostFocus -= TextAreaLostFocus;
TextArea.MouseClick -= TextAreaMouseClick;
DisableIncrementalSearchCursor();
RemoveFormattingStrategy();
if (cursor != null) {
cursor.Dispose();
}
ClearStatusBarMessage();
}
}
TextArea TextArea {
get {
return textEditor.ActiveTextAreaControl.TextArea;
}
}
void TextAreaLostFocus(object source, EventArgs e)
{
StopIncrementalSearch();
}
/// <summary>
/// Stop the incremental search if the user clicks.
/// </summary>
void TextAreaMouseClick(object source, MouseEventArgs e)
{
StopIncrementalSearch();
}
void StopIncrementalSearch()
{
// Reset code completion state.
CodeCompletionOptions.EnableCodeCompletion = codeCompletionEnabled;
Dispose();
}
/// <summary>
/// Searches the text incrementally on each key press.
/// </summary>
bool TextAreaKeyPress(char ch)
{
// Search for text.
searchText.Append(ch);
RunSearch();
return true;
}
void HighlightText(int offset, int length)
{
int endOffset = offset + length;
TextArea.Caret.Position = TextArea.Document.OffsetToPosition(endOffset);
TextArea.SelectionManager.ClearSelection();
IDocument document = TextArea.Document;
DefaultSelection selection = new DefaultSelection(document, document.OffsetToPosition(offset), document.OffsetToPosition(endOffset));
TextArea.SelectionManager.SetSelection(selection);
textEditor.Refresh();
}
/// <summary>
/// Runs the incremental search either forwards or backwards.
/// </summary>
void RunSearch()
{
string find = searchText.ToString();
int index = FindText(find, startIndex, forwards);
if (index == -1) {
index = FindText(find, GetWrapAroundStartIndex(), forwards);
passedEndOfDocument = true;
}
// Highlight found text and show status bar message.
if (index >= 0) {
startIndex = index;
HighlightText(index, find.Length);
ShowTextFound(find);
} else {
ShowTextNotFound(find);
}
}
/// <summary>
/// Gets the index the search should start from when we wrap around. This
/// is either the start of the string or the very end depending on which
/// way we are searching.
/// </summary>
int GetWrapAroundStartIndex()
{
int wrapAroundIndex = 0;
if (!forwards) {
wrapAroundIndex = text.Length - 1;
}
return wrapAroundIndex;
}
/// <summary>
/// Looks for the string from the last position we searched from. The
/// search is case insensitive if all the characters of the search
/// string are lower case. If one of the search characters is upper case
/// then the search is case sensitive. The search can be either forwards
/// or backwards.
/// </summary>
int FindText(string find, int startIndex, bool forwards)
{
StringComparison stringComparison = GetStringComparisonType(find);
if (forwards) {
return text.IndexOf(find, startIndex, stringComparison);
}
// Reverse search.
string searchText = GetReverseSearchText(startIndex + find.Length);
return searchText.LastIndexOf(find, stringComparison);
}
/// <summary>
/// Gets whether the search string comparision should be case
/// sensitive. If all the characters of the find string are lower case
/// then the search is case insensitive. If any character is upper case
/// then the search is case sensitive.
/// </summary>
StringComparison GetStringComparisonType(string find)
{
foreach (char c in find) {
if (Char.IsUpper(c)) {
return StringComparison.InvariantCulture;
}
}
return StringComparison.InvariantCultureIgnoreCase;
}
/// <summary>
/// Gets the text to search when doing a reverse search.
/// </summary>
string GetReverseSearchText(int endIndex)
{
if (endIndex < text.Length) {
return text.Substring(0, endIndex);
}
endIndex = text.Length - 1;
if (endIndex >= 0) {
return text;
}
return String.Empty;
}
/// <summary>
/// Checks the dialog key to see if the incremental search
/// should be cancelled.
/// </summary>
/// <remarks>
/// If the user presses the escape or enter key then the
/// incremental search is aborted. If the user executes any
/// edit action via the keyboard the incremental search is aborted
/// and the edit action is allowed to execute.
/// </remarks>
bool TextAreaProcessDialogKey(Keys keys)
{
if (keys == Keys.Escape ||
keys == Keys.Enter) {
StopIncrementalSearch();
return true;
} else if (keys == Keys.Back) {
// Remove last search char and try search again.
int length = searchText.ToString().Length;
if (length > 0) {
searchText.Remove(length - 1, 1);
// Run search back at original starting point.
startIndex = originalStartIndex;
passedEndOfDocument = false;
RunSearch();
return true;
} else {
StopIncrementalSearch();
return false;
}
} else if (textEditor.IsEditAction(keys)) {
StopIncrementalSearch();
return false;
}
return false;
}
static bool IsGreaterThanKey(Keys keys)
{
return (int)(keys & Keys.KeyCode) == '>';
}
/// <summary>
/// Shows the status bar message. All messages are prefixed
/// with the standard Incremental Search string.
/// </summary>
void ShowTextFound(string find)
{
if (passedEndOfDocument) {
ShowMessage(String.Concat(find, StringParser.Parse(" ${res:ICSharpCode.SharpDevelop.DefaultEditor.IncrementalSearch.PassedEndOfDocumentStatusBarMessage}")), true);
} else {
ShowMessage(find, false);
}
}
/// <summary>
/// Shows a highlighed status bar message.
/// </summary>
void ShowMessage(string message, bool highlight)
{
string fullMessage = String.Concat(incrementalSearchStartMessage, message);
StatusBarService.SetMessage(fullMessage, highlight);
}
/// <summary>
/// Shows a status bar message indicating that the specified text
/// was not found.
/// </summary>
void ShowTextNotFound(string find)
{
ShowMessage(String.Concat(find, StringParser.Parse(" ${res:ICSharpCode.SharpDevelop.DefaultEditor.IncrementalSearch.NotFoundStatusBarMessage}")), true);
}
/// <summary>
/// Clears the status bar message.
/// </summary>
void ClearStatusBarMessage()
{
StatusBarService.SetMessage(String.Empty);
}
/// <summary>
/// Gets the initial text to include in the incremental search.
/// </summary>
/// <remarks>
/// If multiple lines are selected then no initial search text
/// is set.
/// </remarks>
void GetInitialSearchText()
{
if (TextArea.SelectionManager.HasSomethingSelected) {
ISelection selection = TextArea.SelectionManager.SelectionCollection[0];
startIndex = selection.Offset;
if (!IsMultilineSelection(selection)) {
searchText.Append(selection.SelectedText);
}
}
}
bool IsMultilineSelection(ISelection selection)
{
return selection.StartPosition.Y != selection.EndPosition.Y;
}
/// <summary>
/// Gets the cursor to be displayed in the text editor whilst doing
/// an incremental search.
/// </summary>
Cursor GetCursor()
{
if (cursor == null) {
string resourceName = "Resources.IncrementalSearchCursor.cur";
if (!forwards) {
resourceName = "Resources.ReverseIncrementalSearchCursor.cur";
}
cursor = new Cursor(GetType().Assembly.GetManifestResourceStream(resourceName));
}
return cursor;
}
/// <summary>
/// Changes the text editor's cursor so the user knows we are in
/// incremental search mode.
/// </summary>
void EnableIncrementalSearchCursor()
{
Cursor cursor = GetCursor();
TextArea.Cursor = cursor;
TextArea.TextView.Cursor = cursor;
}
void DisableIncrementalSearchCursor()
{
TextArea.Cursor = Cursors.IBeam;
TextArea.TextView.Cursor = Cursors.IBeam;
}
/// <summary>
/// Replace the existing formatting strategy with our dummy one.
/// </summary>
/// <remarks>
/// Special case. We need to replace the formatting strategy to
/// prevent the text editor from autocompletiong xml elements and
/// xml comment tags. The text editor always calls
/// IFormattingStrategy.FormatLine regardless of whether any text was
/// actually inserted or replaced.
/// </remarks>
void AddFormattingStrategy()
{
IDocument document = textEditor.Document;
previousFormattingStrategy = document.FormattingStrategy;
textEditor.Document.FormattingStrategy = new IncrementalSearchFormattingStrategy();
}
/// <summary>
/// Removes our dummy formatting strategy and replaces it with
/// the original before the incremental search was triggered.
/// </summary>
void RemoveFormattingStrategy()
{
textEditor.Document.FormattingStrategy = previousFormattingStrategy;
}
}
*/
}

35
src/Main/Base/Project/Src/Util/ExtensionMethods.cs

@ -98,17 +98,36 @@ namespace ICSharpCode.SharpDevelop
return new ReadOnlyCollectionWrapper<T>(arr); return new ReadOnlyCollectionWrapper<T>(arr);
} }
public static string Join(this IEnumerable<string> input, string separator) public static IEnumerable<WinForms.Control> GetRecursive(this WinForms.Control.ControlCollection collection)
{ {
return string.Join(separator, input.ToArray()); return collection.Cast<WinForms.Control>().Flatten(c => c.Controls.Cast<WinForms.Control>());
} }
public static IEnumerable<WinForms.Control> GetRecursive(this WinForms.Control.ControlCollection collection) /// <summary>
{ /// Converts a recursive data structure into a flat list.
foreach (WinForms.Control ctl in collection) { /// </summary>
yield return ctl; /// <param name="input">The root elements of the recursive data structure.</param>
foreach (WinForms.Control subCtl in ctl.Controls.GetRecursive()) { /// <param name="recursion">The function that gets the children of an element.</param>
yield return subCtl; /// <returns>Iterator that enumerates the tree structure in preorder.</returns>
public static IEnumerable<T> Flatten<T>(this IEnumerable<T> input, Func<T, IEnumerable<T>> recursion)
{
Stack<IEnumerator<T>> stack = new Stack<IEnumerator<T>>();
try {
stack.Push(input.GetEnumerator());
while (stack.Count > 0) {
while (stack.Peek().MoveNext()) {
T element = stack.Peek().Current;
yield return element;
IEnumerable<T> children = recursion(element);
if (children != null) {
stack.Push(children.GetEnumerator());
}
}
stack.Pop().Dispose();
}
} finally {
while (stack.Count > 0) {
stack.Pop().Dispose();
} }
} }
} }

Loading…
Cancel
Save