Browse Source

fix some threading issues and reduce UI invokes

pull/23/head
Siegfried Pammer 14 years ago
parent
commit
9096193009
  1. 111
      src/AddIns/Misc/SearchAndReplace/Project/Engine/SearchManager.cs
  2. 11
      src/AddIns/Misc/SearchAndReplace/Project/Gui/DefaultSearchResult.cs
  3. 14
      src/AddIns/Misc/SearchAndReplace/Project/Gui/SearchRootNode.cs
  4. 2
      src/Main/Base/Project/Src/Editor/Search/ISearchResultFactory.cs
  5. 34
      src/Main/Base/Project/Src/Editor/Search/SearchResultMatch.cs
  6. 4
      src/Main/Base/Project/Src/Editor/Search/SearchResultsPad.cs

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

@ -66,7 +66,7 @@ namespace SearchAndReplace @@ -66,7 +66,7 @@ namespace SearchAndReplace
return files.Distinct();
}
public static void ShowSearchResults(string pattern, IObservable<SearchResultMatch> results)
public static void ShowSearchResults(string pattern, IObservable<SearchedFile> results)
{
string title = StringParser.Parse("${res:MainWindow.Windows.SearchResultPanel.OccurrencesOf}",
new StringTagPair("Pattern", pattern));
@ -74,8 +74,8 @@ namespace SearchAndReplace @@ -74,8 +74,8 @@ namespace SearchAndReplace
SearchResultsPad.Instance.BringToFront();
}
public static IObservable<SearchResultMatch> FindAll(IProgressMonitor progressMonitor, string pattern, bool ignoreCase, bool matchWholeWords, SearchMode mode,
SearchTarget target, string baseDirectory = null, string filter = "*.*", bool searchSubdirs = false, ISegment selection = null, bool useParallel = true)
public static IObservable<SearchedFile> FindAll(IProgressMonitor progressMonitor, string pattern, bool ignoreCase, bool matchWholeWords, SearchMode mode,
SearchTarget target, string baseDirectory = null, string filter = "*.*", bool searchSubdirs = false, ISegment selection = null, bool useParallel = true)
{
currentSearchRegion = null;
var strategy = SearchStrategyFactory.Create(pattern, ignoreCase, matchWholeWords, mode);
@ -84,15 +84,13 @@ namespace SearchAndReplace @@ -84,15 +84,13 @@ namespace SearchAndReplace
return new SearchRun(strategy, fileFinder, fileList, progressMonitor) { UseParallel = useParallel, Target = target, Selection = selection };
}
class SearchRun : IObservable<SearchResultMatch>, IDisposable
class SearchRun : IObservable<SearchedFile>, IDisposable
{
IObserver<SearchResultMatch> observer;
IObserver<SearchedFile> observer;
ISearchStrategy strategy;
ParseableFileContentFinder fileFinder;
IEnumerable<FileName> fileList;
IProgressMonitor monitor;
int count;
double oneStep;
CancellationTokenSource cts;
public bool UseParallel { get; set; }
@ -110,48 +108,85 @@ namespace SearchAndReplace @@ -110,48 +108,85 @@ namespace SearchAndReplace
this.cts = new CancellationTokenSource();
}
public IDisposable Subscribe(IObserver<SearchResultMatch> observer)
public IDisposable Subscribe(IObserver<SearchedFile> observer)
{
this.observer = observer;
if (UseParallel) {
var task = new System.Threading.Tasks.Task(
delegate {
var list = fileList.ToList();
this.count = list.Count;
this.oneStep = 1.0 / this.count;
monitor.CancellationToken.ThrowIfCancellationRequested();
cts.Token.ThrowIfCancellationRequested();
Parallel.ForEach(list, new ParallelOptions { CancellationToken = monitor.CancellationToken, MaxDegreeOfParallelism = Environment.ProcessorCount }, fileName => SearchFile(fileName, strategy, monitor.CancellationToken));
ThrowIfCancellationRequested();
SearchParallel(list, observer, fileName => SearchFile(fileName, strategy));
});
task.ContinueWith(t => { if (t.Exception != null) observer.OnError(t.Exception); else observer.OnCompleted(); this.Dispose(); });
task.Start();
} else {
var list = fileList.ToList();
this.count = list.Count;
this.oneStep = 1.0 / this.count;
monitor.CancellationToken.ThrowIfCancellationRequested();
cts.Token.ThrowIfCancellationRequested();
foreach (var file in list) {
SearchFile(file, strategy, monitor.CancellationToken);
//SearchFile(file, strategy, monitor.CancellationToken);
}
}
return this;
}
void SearchParallel(List<FileName> files, IObserver<SearchedFile> observer, Func<FileName, SearchedFile> searchInFile)
{
int taskCount = 2 * Environment.ProcessorCount;
Queue<Task<SearchedFile>> queue = new Queue<Task<SearchedFile>>(taskCount);
List<Exception> exceptions = new List<Exception>();
for (int i = 0; i < files.Count; i++) {
if (i >= taskCount) {
HandleResult(queue.Dequeue(), observer, exceptions, files);
}
if (exceptions.Count == 0) {
FileName file = files[i];
queue.Enqueue(System.Threading.Tasks.Task.Factory.StartNew(() => searchInFile(file)));
}
}
while (queue.Count > 0) {
HandleResult(queue.Dequeue(), observer, exceptions, files);
}
if (exceptions.Count != 0)
throw new AggregateException(exceptions);
}
void HandleResult(Task<SearchedFile> task, IObserver<SearchedFile> observer, List<Exception> exceptions, List<FileName> files)
{
SearchedFile result;
try {
result = task.Result;
} catch (AggregateException ex) {
exceptions.AddRange(ex.InnerExceptions);
return;
}
if (exceptions.Count == 0 && result.Count > 0)
observer.OnNext(result);
monitor.Progress += 1.0 / files.Count;
}
void ThrowIfCancellationRequested()
{
monitor.CancellationToken.ThrowIfCancellationRequested();
cts.Token.ThrowIfCancellationRequested();
}
public void Dispose()
{
cts.Cancel();
monitor.Dispose();
}
void SearchFile(FileName fileName, ISearchStrategy strategy, CancellationToken ct)
SearchedFile SearchFile(FileName fileName, ISearchStrategy strategy)
{
ITextBuffer buffer = fileFinder.Create(fileName);
List<SearchResultMatch> results = new List<SearchResultMatch>();
if (buffer == null)
return;
return SearchedFile.Empty(fileName);
if (!MimeTypeDetection.FindMimeType(buffer).StartsWith("text/", StringComparison.Ordinal))
return;
return SearchedFile.Empty(fileName);
var source = DocumentUtilitites.GetTextSource(buffer);
TextDocument document = null;
DocumentHighlighter highlighter = null;
@ -162,8 +197,7 @@ namespace SearchAndReplace @@ -162,8 +197,7 @@ namespace SearchAndReplace
length = Selection.Length;
}
foreach(var result in strategy.FindAll(source, offset, length)) {
ct.ThrowIfCancellationRequested();
cts.Token.ThrowIfCancellationRequested();
ThrowIfCancellationRequested();
if (document == null) {
document = new TextDocument(source);
var highlighting = HighlightingManager.Instance.GetDefinitionByExtension(Path.GetExtension(fileName));
@ -175,12 +209,9 @@ namespace SearchAndReplace @@ -175,12 +209,9 @@ namespace SearchAndReplace
var start = document.GetLocation(result.Offset).ToLocation();
var end = document.GetLocation(result.Offset + result.Length).ToLocation();
var builder = SearchResultsPad.CreateInlineBuilder(start, end, document, highlighter);
var match = new SearchResultMatch(fileName, start, end, result.Offset, result.Length, builder);
lock (observer)
observer.OnNext(match);
results.Add(new SearchResultMatch(fileName, start, end, result.Offset, result.Length, builder));
}
lock (monitor)
monitor.Progress += oneStep;
return new SearchedFile(fileName, results);
}
}
@ -354,34 +385,32 @@ namespace SearchAndReplace @@ -354,34 +385,32 @@ namespace SearchAndReplace
}
}
public static void MarkAll(IObservable<SearchResultMatch> results)
public static void MarkAll(IObservable<SearchedFile> results)
{
int count = 0;
results.ObserveOnUIThread()
.Subscribe(
match => { count++; MarkResult(match, false); },
match => { count++; match.ForEach(m => MarkResult(m, false)); },
error => MessageService.ShowException(error),
() => ShowMarkDoneMessage(count)
);
}
public static void ReplaceAll(IObservable<SearchResultMatch> results, string replacement, CancellationToken ct)
public static void ReplaceAll(IObservable<SearchedFile> results, string replacement, CancellationToken ct)
{
var differences = new Dictionary<FileName, int>();
int count = 0;
results.ObserveOnUIThread()
.Subscribe(
match => {
ITextEditor textArea = OpenTextArea(match.FileName, false);
if (textArea != null) {
int difference = 0;
if (!differences.TryGetValue(match.FileName, out difference))
differences.Add(match.FileName, 0);
string newString = match.TransformReplacePattern(replacement);
textArea.Document.Replace(match.StartOffset + difference, match.Length, newString);
difference += newString.Length - match.Length;
differences[match.FileName] = difference;
count++;
matches => {
int difference = 0;
foreach (var match in matches) {
ITextEditor textArea = OpenTextArea(match.FileName, false);
if (textArea != null) {
string newString = match.TransformReplacePattern(replacement);
textArea.Document.Replace(match.StartOffset + difference, match.Length, newString);
difference += newString.Length - match.Length;
count++;
}
}
},
error => MessageService.ShowException(error),

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

@ -122,7 +122,7 @@ namespace SearchAndReplace @@ -122,7 +122,7 @@ namespace SearchAndReplace
return new DefaultSearchResult(title, matches);
}
public ISearchResult CreateSearchResult(string title, IObservable<SearchResultMatch> matches)
public ISearchResult CreateSearchResult(string title, IObservable<SearchedFile> matches)
{
var osr = new ObserverSearchResult(title);
osr.Registration = matches.ObserveOnUIThread().Subscribe(osr);
@ -130,7 +130,7 @@ namespace SearchAndReplace @@ -130,7 +130,7 @@ namespace SearchAndReplace
}
}
public class ObserverSearchResult : ISearchResult, IObserver<SearchResultMatch>
public class ObserverSearchResult : ISearchResult, IObserver<SearchedFile>
{
static Button stopButton;
SearchRootNode rootNode;
@ -227,12 +227,12 @@ namespace SearchAndReplace @@ -227,12 +227,12 @@ namespace SearchAndReplace
}
}
void IObserver<SearchResultMatch>.OnNext(SearchResultMatch value)
void IObserver<SearchedFile>.OnNext(SearchedFile value)
{
rootNode.Add(value);
}
void IObserver<SearchResultMatch>.OnError(Exception error)
void IObserver<SearchedFile>.OnError(Exception error)
{
MessageService.ShowException(error);
OnCompleted();
@ -245,9 +245,10 @@ namespace SearchAndReplace @@ -245,9 +245,10 @@ namespace SearchAndReplace
Registration.Dispose();
}
void IObserver<SearchResultMatch>.OnCompleted()
void IObserver<SearchedFile>.OnCompleted()
{
OnCompleted();
}
}
}

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

@ -31,17 +31,11 @@ namespace SearchAndReplace @@ -31,17 +31,11 @@ namespace SearchAndReplace
this.IsExpanded = true;
}
public void Add(SearchResultMatch match)
public void Add(SearchedFile matches)
{
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();
}
var results = matches.Select(m => new SearchResultNode(m)).ToList();
resultNodes.AddRange(results);
this.fileNodes.Add(new SearchFileNode(matches.Key, results));
InvalidateText();
}

2
src/Main/Base/Project/Src/Editor/Search/ISearchResultFactory.cs

@ -14,6 +14,6 @@ namespace ICSharpCode.SharpDevelop.Editor.Search @@ -14,6 +14,6 @@ namespace ICSharpCode.SharpDevelop.Editor.Search
{
ISearchResult CreateSearchResult(string title, IEnumerable<SearchResultMatch> matches);
ISearchResult CreateSearchResult(string title, IObservable<SearchResultMatch> matches);
ISearchResult CreateSearchResult(string title, IObservable<SearchedFile> matches);
}
}

34
src/Main/Base/Project/Src/Editor/Search/SearchResultMatch.cs

@ -2,9 +2,11 @@ @@ -2,9 +2,11 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.Core;
using ICSharpCode.NRefactory;
@ -113,4 +115,34 @@ namespace ICSharpCode.SharpDevelop.Editor.Search @@ -113,4 +115,34 @@ namespace ICSharpCode.SharpDevelop.Editor.Search
return match.ReplaceWith(pattern);
}
}
public class SearchedFile : IGrouping<FileName, SearchResultMatch>
{
IList<SearchResultMatch> matches;
public FileName Key { get; private set; }
public int Count { get { return matches.Count; } }
public SearchedFile(FileName file, IList<SearchResultMatch> matches)
{
this.Key = file;
this.matches = matches;
}
public IEnumerator<SearchResultMatch> GetEnumerator()
{
return matches.GetEnumerator();
}
IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return (IEnumerator)matches.GetEnumerator();
}
public static SearchedFile Empty(FileName fileName)
{
return new SearchedFile(fileName, new List<SearchResultMatch>());
}
}
}

4
src/Main/Base/Project/Src/Editor/Search/SearchResultsPad.cs

@ -111,7 +111,7 @@ namespace ICSharpCode.SharpDevelop.Editor.Search @@ -111,7 +111,7 @@ namespace ICSharpCode.SharpDevelop.Editor.Search
ShowSearchResults(CreateSearchResult(title, matches));
}
public void ShowSearchResults(string title, IObservable<SearchResultMatch> matches)
public void ShowSearchResults(string title, IObservable<SearchedFile> matches)
{
ShowSearchResults(CreateSearchResult(title, matches));
}
@ -133,7 +133,7 @@ namespace ICSharpCode.SharpDevelop.Editor.Search @@ -133,7 +133,7 @@ namespace ICSharpCode.SharpDevelop.Editor.Search
}
public static ISearchResult CreateSearchResult(string title, IObservable<SearchResultMatch> matches)
public static ISearchResult CreateSearchResult(string title, IObservable<SearchedFile> matches)
{
if (title == null)
throw new ArgumentNullException("title");

Loading…
Cancel
Save