Browse Source

implement FindAll

pull/23/head
Siegfried Pammer 14 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 @@ -28,7 +28,7 @@ namespace SearchAndReplace
/// </summary>
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>();
@ -40,28 +40,39 @@ namespace SearchAndReplace @@ -40,28 +40,39 @@ namespace SearchAndReplace
files.Add(vc.TextEditor.FileName);
break;
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);
}
break;
case SearchTarget.WholeProject:
if (ProjectService.CurrentProject == null)
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));
}
break;
case SearchTarget.WholeSolution:
if (ProjectService.OpenSolution == null)
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)));
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));
}
break;
case SearchTarget.Directory:
if (!Directory.Exists(baseDirectory))
break;
foreach (var name in FileUtility.SearchDirectory(baseDirectory, filter, searchSubdirs))
files.Add(new FileName(name));
return EnumerateDirectories(baseDirectory, filter, searchSubdirs, ct);
break;
default:
throw new Exception("Invalid value for FileListType");
@ -69,20 +80,60 @@ namespace SearchAndReplace @@ -69,20 +80,60 @@ namespace SearchAndReplace
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,
SearchTarget target, string baseDirectory = null, string filter = "*.*", bool searchSubdirs = false)
{
CancellationTokenSource cts = new CancellationTokenSource();
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;
var strategy = SearchStrategyFactory.Create(pattern, ignoreCase, matchWholeWords, mode);
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);
}
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
{
IObserver<SearchResultMatch> observer;
@ -104,7 +155,9 @@ namespace SearchAndReplace @@ -104,7 +155,9 @@ namespace SearchAndReplace
public IDisposable Subscribe(IObserver<SearchResultMatch> 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;
}
@ -115,17 +168,23 @@ namespace SearchAndReplace @@ -115,17 +168,23 @@ namespace SearchAndReplace
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/"))
return;
var source = DocumentUtilitites.GetTextSource(buffer);
foreach(var result in strategy.FindAll(source)) {
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)
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; @@ -7,7 +7,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
using ICSharpCode.Core;
using ICSharpCode.Core.Presentation;
using ICSharpCode.SharpDevelop;
@ -130,15 +130,13 @@ namespace SearchAndReplace @@ -130,15 +130,13 @@ namespace SearchAndReplace
public class ObserverSearchResult : ISearchResult, IObserver<SearchResultMatch>
{
List<SearchResultMatch> matches;
Button stopButton;
static Button stopButton;
SearchRootNode rootNode;
public ObserverSearchResult(string title)
{
Text = title;
matches = new List<SearchResultMatch>();
rootNode = new SearchRootNode(title, matches);
rootNode = new SearchRootNode(title, new List<SearchResultMatch>());
}
public string Text { get; private set; }
@ -156,17 +154,76 @@ namespace SearchAndReplace @@ -156,17 +154,76 @@ namespace SearchAndReplace
return resultsTreeViewInstance;
}
static IList toolbarItems;
static MenuItem flatItem, perFileItem;
public IList GetToolbarItems()
{
stopButton = new Button { Content = "Stop" };
stopButton.Click += delegate { if (Registration != null) Registration.Dispose(); };
return new ArrayList { stopButton };
WorkbenchSingleton.AssertMainThread();
if (toolbarItems == null) {
toolbarItems = new List<object>();
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)
{
matches.Add(value);
WorkbenchSingleton.SafeThreadCall((Action)delegate { rootNode.Add(value); });
}
void IObserver<SearchResultMatch>.OnError(Exception error)
@ -177,7 +234,12 @@ namespace SearchAndReplace @@ -177,7 +234,12 @@ namespace SearchAndReplace
void OnCompleted()
{
stopButton.Visibility = Visibility.Collapsed;
WorkbenchSingleton.SafeThreadCall(
(Action)delegate {
stopButton.Visibility = Visibility.Collapsed;
if (Registration != null)
Registration.Dispose();
});
}
void IObserver<SearchResultMatch>.OnCompleted()

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

@ -132,7 +132,7 @@ namespace SearchAndReplace @@ -132,7 +132,7 @@ namespace SearchAndReplace
}
} else {
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; @@ -5,6 +5,7 @@ using System;
using System.IO;
using System.Windows.Controls;
using System.Windows.Documents;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Editor.Search;
@ -13,11 +14,12 @@ namespace SearchAndReplace @@ -13,11 +14,12 @@ namespace SearchAndReplace
{
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.IsExpanded = true;
}
@ -26,15 +28,15 @@ namespace SearchAndReplace @@ -26,15 +28,15 @@ namespace SearchAndReplace
{
return new TextBlock {
Inlines = {
new Bold(new Run(Path.GetFileName(fileName))),
new Run(StringParser.Parse(" (${res:MainWindow.Windows.SearchResultPanel.In} ") + Path.GetDirectoryName(fileName) + ")")
new Bold(new Run(Path.GetFileName(FileName))),
new Run(StringParser.Parse(" (${res:MainWindow.Windows.SearchResultPanel.In} ") + Path.GetDirectoryName(FileName) + ")")
}
};
}
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 @@ -58,7 +58,7 @@ namespace SearchAndReplace
protected abstract object CreateText();
protected void InvalidateText()
protected internal void InvalidateText()
{
cachedText = null;
OnPropertyChanged("Text");

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

@ -3,13 +3,13 @@ @@ -3,13 +3,13 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Editor.Search;
@ -18,20 +18,32 @@ namespace SearchAndReplace @@ -18,20 +18,32 @@ namespace SearchAndReplace
{
sealed class SearchRootNode : SearchNode
{
IList<SearchResultNode> resultNodes;
IList<SearchFileNode> fileNodes;
ObservableCollection<SearchResultNode> resultNodes;
ObservableCollection<SearchFileNode> fileNodes;
public string Title { get; private set; }
public SearchRootNode(string title, IList<SearchResultMatch> results)
{
this.Title = title;
this.resultNodes = results.Select(r => new SearchResultNode(r)).ToArray();
this.fileNodes = resultNodes.GroupBy(r => r.FileName).Select(g => new SearchFileNode(g.Key, g.ToArray())).ToArray();
this.Children = this.resultNodes;
this.resultNodes = new ObservableCollection<SearchResultNode>(results.Select(r => new SearchResultNode(r)));
this.fileNodes = new ObservableCollection<SearchFileNode>(resultNodes.GroupBy(r => r.FileName).Select(g => new SearchFileNode(g.Key, g.ToList())));
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)
{

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

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

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

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

Loading…
Cancel
Save