Browse Source

implement FindAll

pull/23/head
Siegfried Pammer 15 years ago
parent
commit
f72af03258
  1. 85
      src/AddIns/Misc/SearchAndReplace/Project/Engine/SearchManager.cs
  2. 84
      src/AddIns/Misc/SearchAndReplace/Project/Gui/DefaultSearchResult.cs
  3. 2
      src/AddIns/Misc/SearchAndReplace/Project/Gui/SearchAndReplacePanel.cs
  4. 14
      src/AddIns/Misc/SearchAndReplace/Project/Gui/SearchFileNode.cs
  5. 2
      src/AddIns/Misc/SearchAndReplace/Project/Gui/SearchNode.cs
  6. 26
      src/AddIns/Misc/SearchAndReplace/Project/Gui/SearchRootNode.cs
  7. 2
      src/AddIns/Misc/SearchAndReplace/Project/SearchAndReplace.addin
  8. 1
      src/Main/Base/Project/ICSharpCode.SharpDevelop.addin

85
src/AddIns/Misc/SearchAndReplace/Project/Engine/SearchManager.cs

@ -28,7 +28,7 @@ namespace SearchAndReplace
/// </summary> /// </summary>
public class SearchManager public class SearchManager
{ {
static IEnumerable<FileName> GenerateFileList(SearchTarget target, string baseDirectory = null, string filter = "*.*", bool searchSubdirs = false) static IEnumerable<FileName> GenerateFileList(SearchTarget target, string baseDirectory = null, string filter = "*.*", bool searchSubdirs = false, CancellationToken ct = default(CancellationToken))
{ {
List<FileName> files = new List<FileName>(); List<FileName> files = new List<FileName>();
@ -40,28 +40,39 @@ namespace SearchAndReplace
files.Add(vc.TextEditor.FileName); files.Add(vc.TextEditor.FileName);
break; break;
case SearchTarget.AllOpenFiles: case SearchTarget.AllOpenFiles:
foreach (ITextEditorProvider editor in WorkbenchSingleton.Workbench.ViewContentCollection.OfType<ITextEditorProvider>()) foreach (ITextEditorProvider editor in WorkbenchSingleton.Workbench.ViewContentCollection.OfType<ITextEditorProvider>()) {
if (ct != default(CancellationToken))
ct.ThrowIfCancellationRequested();
files.Add(editor.TextEditor.FileName); files.Add(editor.TextEditor.FileName);
}
break; break;
case SearchTarget.WholeProject: case SearchTarget.WholeProject:
if (ProjectService.CurrentProject == null) if (ProjectService.CurrentProject == null)
break; break;
foreach (FileProjectItem item in ProjectService.CurrentProject.Items.OfType<FileProjectItem>()) foreach (FileProjectItem item in ProjectService.CurrentProject.Items.OfType<FileProjectItem>()) {
if (ct != default(CancellationToken))
ct.ThrowIfCancellationRequested();
files.Add(new FileName(item.FileName)); files.Add(new FileName(item.FileName));
}
break; break;
case SearchTarget.WholeSolution: case SearchTarget.WholeSolution:
if (ProjectService.OpenSolution == null) if (ProjectService.OpenSolution == null)
break; break;
foreach (var item in ProjectService.OpenSolution.SolutionFolderContainers.Select(f => f.SolutionItems).SelectMany(si => si.Items)) foreach (var item in ProjectService.OpenSolution.SolutionFolderContainers.Select(f => f.SolutionItems).SelectMany(si => si.Items)) {
if (ct != default(CancellationToken))
ct.ThrowIfCancellationRequested();
files.Add(new FileName(Path.Combine(ProjectService.OpenSolution.Directory, item.Location))); files.Add(new FileName(Path.Combine(ProjectService.OpenSolution.Directory, item.Location)));
foreach (var item in ProjectService.OpenSolution.Projects.SelectMany(p => p.Items).OfType<FileProjectItem>()) }
foreach (var item in ProjectService.OpenSolution.Projects.SelectMany(p => p.Items).OfType<FileProjectItem>()) {
if (ct != default(CancellationToken))
ct.ThrowIfCancellationRequested();
files.Add(new FileName(item.FileName)); files.Add(new FileName(item.FileName));
}
break; break;
case SearchTarget.Directory: case SearchTarget.Directory:
if (!Directory.Exists(baseDirectory)) if (!Directory.Exists(baseDirectory))
break; break;
foreach (var name in FileUtility.SearchDirectory(baseDirectory, filter, searchSubdirs)) return EnumerateDirectories(baseDirectory, filter, searchSubdirs, ct);
files.Add(new FileName(name));
break; break;
default: default:
throw new Exception("Invalid value for FileListType"); throw new Exception("Invalid value for FileListType");
@ -69,20 +80,60 @@ namespace SearchAndReplace
return files.Distinct(); return files.Distinct();
} }
static IEnumerable<FileName> EnumerateDirectories(string baseDirectory, string filter, bool searchSubdirs)
{
foreach (var name in FileUtility.SearchDirectory(baseDirectory, filter, searchSubdirs)) {
yield return name;
}
}
public static void ShowSearchResults(string pattern, IObservable<SearchResultMatch> results)
{
string title = StringParser.Parse("${res:MainWindow.Windows.SearchResultPanel.OccurrencesOf}",
new StringTagPair("Pattern", pattern));
SearchResultsPad.Instance.ShowSearchResults(title, results);
SearchResultsPad.Instance.BringToFront();
}
public static IObservable<SearchResultMatch> FindAll(string pattern, bool ignoreCase, bool matchWholeWords, SearchMode mode, public static IObservable<SearchResultMatch> FindAll(string pattern, bool ignoreCase, bool matchWholeWords, SearchMode mode,
SearchTarget target, string baseDirectory = null, string filter = "*.*", bool searchSubdirs = false) SearchTarget target, string baseDirectory = null, string filter = "*.*", bool searchSubdirs = false)
{ {
CancellationTokenSource cts = new CancellationTokenSource(); CancellationTokenSource cts = new CancellationTokenSource();
var monitor = WorkbenchSingleton.Workbench.StatusBar.CreateProgressMonitor(cts.Token); var monitor = WorkbenchSingleton.Workbench.StatusBar.CreateProgressMonitor(cts.Token);
monitor.TaskName = "Find All"; monitor.TaskName = "Find all occurrences of '" + pattern + "' in " + GetTargetDescription(target, baseDirectory);
monitor.Status = OperationStatus.Normal; monitor.Status = OperationStatus.Normal;
var strategy = SearchStrategyFactory.Create(pattern, ignoreCase, matchWholeWords, mode); var strategy = SearchStrategyFactory.Create(pattern, ignoreCase, matchWholeWords, mode);
ParseableFileContentFinder fileFinder = new ParseableFileContentFinder(); ParseableFileContentFinder fileFinder = new ParseableFileContentFinder();
var fileList = GenerateFileList(target, baseDirectory, filter, searchSubdirs); IEnumerable<FileName> fileList;
using (IProgressMonitor dialog = AsynchronousWaitDialog.ShowWaitDialog("Prepare search ...", true)) {
dialog.Progress = double.NaN;
fileList = GenerateFileList(target, baseDirectory, filter, searchSubdirs, dialog.CancellationToken);
}
return new SearchRun(strategy, fileFinder, fileList, monitor, cts); return new SearchRun(strategy, fileFinder, fileList, monitor, cts);
} }
static string GetTargetDescription(SearchTarget target, string baseDirectory = null)
{
switch (target) {
case SearchTarget.CurrentDocument:
return "the current document";
case SearchTarget.CurrentSelection:
return "the current selection";
case SearchTarget.AllOpenFiles:
return "all open files";
case SearchTarget.WholeProject:
return "the whole project";
case SearchTarget.WholeSolution:
return "the whole solution";
case SearchTarget.Directory:
return "the directory '" + baseDirectory + "'";
default:
throw new Exception("Invalid value for SearchTarget");
}
}
class SearchRun : IObservable<SearchResultMatch>, IDisposable class SearchRun : IObservable<SearchResultMatch>, IDisposable
{ {
IObserver<SearchResultMatch> observer; IObserver<SearchResultMatch> observer;
@ -104,7 +155,9 @@ namespace SearchAndReplace
public IDisposable Subscribe(IObserver<SearchResultMatch> observer) public IDisposable Subscribe(IObserver<SearchResultMatch> observer)
{ {
this.observer = observer; this.observer = observer;
new System.Threading.Tasks.Task(delegate { Parallel.ForEach(fileList, fileName => SearchFile(fileFinder.Create(fileName).CreateSnapshot(), strategy, monitor.CancellationToken)); }).Start(); var task = new System.Threading.Tasks.Task(delegate { Parallel.ForEach(fileList, new ParallelOptions { CancellationToken = monitor.CancellationToken, MaxDegreeOfParallelism = Environment.ProcessorCount }, fileName => SearchFile(fileName, strategy, monitor.CancellationToken)); });
task.ContinueWith(t => { if (t.Exception != null) observer.OnError(t.Exception); else observer.OnCompleted(); this.Dispose(); });
task.Start();
return this; return this;
} }
@ -115,17 +168,23 @@ namespace SearchAndReplace
monitor.Dispose(); monitor.Dispose();
} }
void SearchFile(ITextBuffer buffer, ISearchStrategy strategy, CancellationToken ct) void SearchFile(FileName fileName, ISearchStrategy strategy, CancellationToken ct)
{ {
ITextBuffer buffer = fileFinder.Create(fileName);
if (buffer == null)
return;
buffer = buffer.CreateSnapshot();
if (!MimeTypeDetection.FindMimeType(buffer).StartsWith("text/")) if (!MimeTypeDetection.FindMimeType(buffer).StartsWith("text/"))
return; return;
var source = DocumentUtilitites.GetTextSource(buffer); var source = DocumentUtilitites.GetTextSource(buffer);
foreach(var result in strategy.FindAll(source)) { foreach(var result in strategy.FindAll(source)) {
ct.ThrowIfCancellationRequested(); ct.ThrowIfCancellationRequested();
observer.OnNext(new SearchResultMatch(result.Offset, result.Length)); lock (observer)
observer.OnNext(new SearchResultMatch(new ProvidedDocumentInformation(buffer, fileName, 0), result.Offset, result.Length));
} }
lock (monitor) lock (monitor)
monitor.Progress += 1 / fileList.Count(); monitor.Progress += 1.0 / fileList.Count();
} }
} }
} }

84
src/AddIns/Misc/SearchAndReplace/Project/Gui/DefaultSearchResult.cs

@ -7,7 +7,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Threading;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.Core.Presentation; using ICSharpCode.Core.Presentation;
using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop;
@ -130,15 +130,13 @@ namespace SearchAndReplace
public class ObserverSearchResult : ISearchResult, IObserver<SearchResultMatch> public class ObserverSearchResult : ISearchResult, IObserver<SearchResultMatch>
{ {
List<SearchResultMatch> matches; static Button stopButton;
Button stopButton;
SearchRootNode rootNode; SearchRootNode rootNode;
public ObserverSearchResult(string title) public ObserverSearchResult(string title)
{ {
Text = title; Text = title;
matches = new List<SearchResultMatch>(); rootNode = new SearchRootNode(title, new List<SearchResultMatch>());
rootNode = new SearchRootNode(title, matches);
} }
public string Text { get; private set; } public string Text { get; private set; }
@ -156,17 +154,76 @@ namespace SearchAndReplace
return resultsTreeViewInstance; return resultsTreeViewInstance;
} }
static IList toolbarItems;
static MenuItem flatItem, perFileItem;
public IList GetToolbarItems() public IList GetToolbarItems()
{ {
stopButton = new Button { Content = "Stop" }; WorkbenchSingleton.AssertMainThread();
stopButton.Click += delegate { if (Registration != null) Registration.Dispose(); }; if (toolbarItems == null) {
toolbarItems = new List<object>();
return new ArrayList { stopButton }; DropDownButton perFileDropDown = new DropDownButton();
perFileDropDown.Content = new Image { Height = 16, Source = PresentationResourceService.GetBitmapSource("Icons.16x16.FindIcon") };
perFileDropDown.SetValueToExtension(DropDownButton.ToolTipProperty, new LocalizeExtension("MainWindow.Windows.SearchResultPanel.SelectViewMode.ToolTip"));
flatItem = new MenuItem();
flatItem.SetValueToExtension(MenuItem.HeaderProperty, new LocalizeExtension("MainWindow.Windows.SearchResultPanel.Flat"));
flatItem.Click += delegate { SetPerFile(false); };
perFileItem = new MenuItem();
perFileItem.SetValueToExtension(MenuItem.HeaderProperty, new LocalizeExtension("MainWindow.Windows.SearchResultPanel.PerFile"));
perFileItem.Click += delegate { SetPerFile(true); };
perFileDropDown.DropDownMenu = new ContextMenu();
perFileDropDown.DropDownMenu.Items.Add(flatItem);
perFileDropDown.DropDownMenu.Items.Add(perFileItem);
toolbarItems.Add(perFileDropDown);
toolbarItems.Add(new Separator());
Button expandAll = new Button();
expandAll.SetValueToExtension(Button.ToolTipProperty, new LocalizeExtension("MainWindow.Windows.SearchResultPanel.ExpandAll.ToolTip"));
expandAll.Content = new Image { Height = 16, Source = PresentationResourceService.GetBitmapSource("Icons.16x16.OpenAssembly") };
expandAll.Click += delegate { ExpandCollapseAll(true); };
toolbarItems.Add(expandAll);
Button collapseAll = new Button();
collapseAll.SetValueToExtension(Button.ToolTipProperty, new LocalizeExtension("MainWindow.Windows.SearchResultPanel.CollapseAll.ToolTip"));
collapseAll.Content = new Image { Height = 16, Source = PresentationResourceService.GetBitmapSource("Icons.16x16.Assembly") };
collapseAll.Click += delegate { ExpandCollapseAll(false); };
toolbarItems.Add(collapseAll);
stopButton = new Button { Content = "Stop" };
stopButton.Click += delegate {
stopButton.Visibility = Visibility.Collapsed;
if (Registration != null) Registration.Dispose();
};
toolbarItems.Add(stopButton);
}
return toolbarItems;
}
static void ExpandCollapseAll(bool newIsExpanded)
{
if (resultsTreeViewInstance != null) {
foreach (SearchNode node in resultsTreeViewInstance.ItemsSource.OfType<SearchNode>().Flatten(n => n.Children)) {
node.IsExpanded = newIsExpanded;
}
}
}
static void SetPerFile(bool perFile)
{
ResultsTreeView.GroupResultsByFile = perFile;
if (resultsTreeViewInstance != null) {
foreach (SearchRootNode node in resultsTreeViewInstance.ItemsSource.OfType<SearchRootNode>()) {
node.GroupResultsByFile(perFile);
}
}
} }
void IObserver<SearchResultMatch>.OnNext(SearchResultMatch value) void IObserver<SearchResultMatch>.OnNext(SearchResultMatch value)
{ {
matches.Add(value); WorkbenchSingleton.SafeThreadCall((Action)delegate { rootNode.Add(value); });
} }
void IObserver<SearchResultMatch>.OnError(Exception error) void IObserver<SearchResultMatch>.OnError(Exception error)
@ -177,7 +234,12 @@ namespace SearchAndReplace
void OnCompleted() void OnCompleted()
{ {
stopButton.Visibility = Visibility.Collapsed; WorkbenchSingleton.SafeThreadCall(
(Action)delegate {
stopButton.Visibility = Visibility.Collapsed;
if (Registration != null)
Registration.Dispose();
});
} }
void IObserver<SearchResultMatch>.OnCompleted() void IObserver<SearchResultMatch>.OnCompleted()

2
src/AddIns/Misc/SearchAndReplace/Project/Gui/SearchAndReplacePanel.cs

@ -132,7 +132,7 @@ namespace SearchAndReplace
} }
} else { } else {
var results = SearchManager.FindAll(SearchOptions.FindPattern, !SearchOptions.MatchCase, SearchOptions.MatchWholeWord, SearchOptions.SearchStrategyType, SearchOptions.DocumentIteratorType, SearchOptions.LookIn, SearchOptions.LookInFiletypes, SearchOptions.IncludeSubdirectories); var results = SearchManager.FindAll(SearchOptions.FindPattern, !SearchOptions.MatchCase, SearchOptions.MatchWholeWord, SearchOptions.SearchStrategyType, SearchOptions.DocumentIteratorType, SearchOptions.LookIn, SearchOptions.LookInFiletypes, SearchOptions.IncludeSubdirectories);
SearchResultsPad.Instance.ShowSearchResults("Search", results); SearchManager.ShowSearchResults(SearchOptions.FindPattern, results);
} }
} }

14
src/AddIns/Misc/SearchAndReplace/Project/Gui/SearchFileNode.cs

@ -5,6 +5,7 @@ using System;
using System.IO; using System.IO;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Documents; using System.Windows.Documents;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Editor.Search; using ICSharpCode.SharpDevelop.Editor.Search;
@ -13,11 +14,12 @@ namespace SearchAndReplace
{ {
class SearchFileNode : SearchNode class SearchFileNode : SearchNode
{ {
FileName fileName; public FileName FileName { get; private set; }
public SearchFileNode(FileName fileName, SearchResultNode[] resultNodes) public SearchFileNode(FileName fileName, System.Collections.Generic.List<SearchResultNode> resultNodes)
{ {
this.fileName = fileName; this.FileName = fileName;
this.Children = resultNodes; this.Children = resultNodes;
this.IsExpanded = true; this.IsExpanded = true;
} }
@ -26,15 +28,15 @@ namespace SearchAndReplace
{ {
return new TextBlock { return new TextBlock {
Inlines = { Inlines = {
new Bold(new Run(Path.GetFileName(fileName))), new Bold(new Run(Path.GetFileName(FileName))),
new Run(StringParser.Parse(" (${res:MainWindow.Windows.SearchResultPanel.In} ") + Path.GetDirectoryName(fileName) + ")") new Run(StringParser.Parse(" (${res:MainWindow.Windows.SearchResultPanel.In} ") + Path.GetDirectoryName(FileName) + ")")
} }
}; };
} }
public override void ActivateItem() public override void ActivateItem()
{ {
FileService.OpenFile(fileName); FileService.OpenFile(FileName);
} }
} }
} }

2
src/AddIns/Misc/SearchAndReplace/Project/Gui/SearchNode.cs

@ -58,7 +58,7 @@ namespace SearchAndReplace
protected abstract object CreateText(); protected abstract object CreateText();
protected void InvalidateText() protected internal void InvalidateText()
{ {
cachedText = null; cachedText = null;
OnPropertyChanged("Text"); OnPropertyChanged("Text");

26
src/AddIns/Misc/SearchAndReplace/Project/Gui/SearchRootNode.cs

@ -3,13 +3,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Documents; using System.Windows.Documents;
using System.Windows.Media; using System.Windows.Media;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Editor.Search; using ICSharpCode.SharpDevelop.Editor.Search;
@ -18,20 +18,32 @@ namespace SearchAndReplace
{ {
sealed class SearchRootNode : SearchNode sealed class SearchRootNode : SearchNode
{ {
IList<SearchResultNode> resultNodes; ObservableCollection<SearchResultNode> resultNodes;
IList<SearchFileNode> fileNodes; ObservableCollection<SearchFileNode> fileNodes;
public string Title { get; private set; } public string Title { get; private set; }
public SearchRootNode(string title, IList<SearchResultMatch> results) public SearchRootNode(string title, IList<SearchResultMatch> results)
{ {
this.Title = title; this.Title = title;
this.resultNodes = results.Select(r => new SearchResultNode(r)).ToArray(); this.resultNodes = new ObservableCollection<SearchResultNode>(results.Select(r => new SearchResultNode(r)));
this.fileNodes = resultNodes.GroupBy(r => r.FileName).Select(g => new SearchFileNode(g.Key, g.ToArray())).ToArray(); this.fileNodes = new ObservableCollection<SearchFileNode>(resultNodes.GroupBy(r => r.FileName).Select(g => new SearchFileNode(g.Key, g.ToList())));
this.Children = this.resultNodes;
this.IsExpanded = true; this.IsExpanded = true;
} }
public void Add(SearchResultMatch match)
{
var matchNode = new SearchResultNode(match);
resultNodes.Add(matchNode);
int index = fileNodes.FindIndex(node => node.FileName.Equals(match.FileName));
if (index == -1)
fileNodes.Add(new SearchFileNode(match.FileName, new List<SearchResultNode> { matchNode }));
else {
fileNodes[index].Children = resultNodes.Where(node => node.FileName.Equals(match.FileName)).ToArray();
fileNodes[index].InvalidateText();
}
InvalidateText();
}
public void GroupResultsByFile(bool perFile) public void GroupResultsByFile(bool perFile)
{ {

2
src/AddIns/Misc/SearchAndReplace/Project/SearchAndReplace.addin

@ -29,8 +29,8 @@
shortcut = "Control|F3" shortcut = "Control|F3"
class = "SearchAndReplace.FindNextSelected"/> class = "SearchAndReplace.FindNextSelected"/>
<MenuItem id = "Replace" <MenuItem id = "Replace"
insertbefore = "SearchIncremental"
insertafter = "FindNextSelected" insertafter = "FindNextSelected"
insertbefore = "SearchSeparator"
label = "${res:XML.MainMenu.SearchMenu.Replace}" label = "${res:XML.MainMenu.SearchMenu.Replace}"
icon = "Icons.16x16.ReplaceIcon" icon = "Icons.16x16.ReplaceIcon"
shortcut = "Control|H" shortcut = "Control|H"

1
src/Main/Base/Project/ICSharpCode.SharpDevelop.addin

@ -1624,6 +1624,7 @@
</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 = "SearchSeparator" 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"
label = "${res:XML.MainMenu.SearchMenu.ToggleBookmark}" label = "${res:XML.MainMenu.SearchMenu.ToggleBookmark}"

Loading…
Cancel
Save