diff --git a/src/AddIns/Misc/SearchAndReplace/Project/Commands/SearchMainMenuCommands.cs b/src/AddIns/Misc/SearchAndReplace/Project/Commands/SearchMainMenuCommands.cs index c0e7b444de..d6eeb21801 100644 --- a/src/AddIns/Misc/SearchAndReplace/Project/Commands/SearchMainMenuCommands.cs +++ b/src/AddIns/Misc/SearchAndReplace/Project/Commands/SearchMainMenuCommands.cs @@ -39,7 +39,7 @@ namespace SearchAndReplace public override void Run() { if (SearchOptions.CurrentFindPattern.Length > 0) { - var result = SearchManager.FindNext(SearchOptions.FindPattern, !SearchOptions.MatchCase, SearchOptions.MatchWholeWord, SearchOptions.SearchStrategyType, SearchOptions.DocumentIteratorType, SearchOptions.LookIn, SearchOptions.LookInFiletypes, SearchOptions.IncludeSubdirectories); + var result = SearchManager.FindNext(SearchOptions.FindPattern, !SearchOptions.MatchCase, SearchOptions.MatchWholeWord, SearchOptions.SearchStrategyType, SearchOptions.SearchTarget, SearchOptions.LookIn, SearchOptions.LookInFiletypes, SearchOptions.IncludeSubdirectories); SearchManager.SelectResult(result); } else { Find find = new Find(); @@ -90,10 +90,10 @@ namespace SearchAndReplace if (textToFind != null && textToFind.Length > 0) { SearchOptions.CurrentFindPattern = textToFind; - if (SearchOptions.DocumentIteratorType == SearchTarget.CurrentSelection) { - SearchOptions.DocumentIteratorType = SearchTarget.CurrentDocument; + if (SearchOptions.SearchTarget == SearchTarget.CurrentSelection) { + SearchOptions.SearchTarget = SearchTarget.CurrentDocument; } - var result = SearchManager.FindNext(SearchOptions.FindPattern, !SearchOptions.MatchCase, SearchOptions.MatchWholeWord, SearchOptions.SearchStrategyType, SearchOptions.DocumentIteratorType, SearchOptions.LookIn, SearchOptions.LookInFiletypes, SearchOptions.IncludeSubdirectories); + var result = SearchManager.FindNext(SearchOptions.FindPattern, !SearchOptions.MatchCase, SearchOptions.MatchWholeWord, SearchOptions.SearchStrategyType, SearchOptions.SearchTarget, SearchOptions.LookIn, SearchOptions.LookInFiletypes, SearchOptions.IncludeSubdirectories); SearchManager.SelectResult(result); } } diff --git a/src/AddIns/Misc/SearchAndReplace/Project/Engine/SearchManager.cs b/src/AddIns/Misc/SearchAndReplace/Project/Engine/SearchManager.cs index f95141dda7..bb59298fae 100644 --- a/src/AddIns/Misc/SearchAndReplace/Project/Engine/SearchManager.cs +++ b/src/AddIns/Misc/SearchAndReplace/Project/Engine/SearchManager.cs @@ -75,17 +75,17 @@ namespace SearchAndReplace } public static IObservable 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, bool useParallel = true, IProgressMonitor progressMonitor = null) { currentSearchRegion = null; - CancellationTokenSource cts = new CancellationTokenSource(); - var monitor = WorkbenchSingleton.Workbench.StatusBar.CreateProgressMonitor(cts.Token); + CancellationToken ct = progressMonitor != null ? progressMonitor.CancellationToken : new CancellationTokenSource().Token; + var monitor = progressMonitor ?? WorkbenchSingleton.Workbench.StatusBar.CreateProgressMonitor(ct); 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(); IEnumerable fileList = GenerateFileList(target, baseDirectory, filter, searchSubdirs); - return new SearchRun(strategy, fileFinder, fileList, monitor, cts); + return new SearchRun(strategy, fileFinder, fileList, monitor); } static string GetTargetDescription(SearchTarget target, string baseDirectory = null) @@ -115,35 +115,40 @@ namespace SearchAndReplace ParseableFileContentFinder fileFinder; IEnumerable fileList; IProgressMonitor monitor; - CancellationTokenSource cts; int count; - public SearchRun(ISearchStrategy strategy, ParseableFileContentFinder fileFinder, IEnumerable fileList, IProgressMonitor monitor, CancellationTokenSource cts) + public bool UseParallel { get; set; } + + public SearchRun(ISearchStrategy strategy, ParseableFileContentFinder fileFinder, IEnumerable fileList, IProgressMonitor monitor) { this.strategy = strategy; this.fileFinder = fileFinder; this.fileList = fileList; this.monitor = monitor; - this.cts = cts; } public IDisposable Subscribe(IObserver observer) { this.observer = observer; - var task = new System.Threading.Tasks.Task( - delegate { - var list = fileList.ToList(); - this.count = list.Count; - Parallel.ForEach(list, 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(); + if (UseParallel) { + var task = new System.Threading.Tasks.Task( + delegate { + var list = fileList.ToList(); + this.count = list.Count; + Parallel.ForEach(list, 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(); + } else { + foreach (var file in fileList) { + SearchFile(file, strategy, monitor.CancellationToken); + } + } return this; } public void Dispose() { - cts.Cancel(); monitor.Dispose(); } @@ -169,7 +174,7 @@ namespace SearchAndReplace var start = document.GetLocation(result.Offset).ToLocation(); var end = document.GetLocation(result.Offset + result.Length).ToLocation(); lock (observer) - observer.OnNext(new SearchResultMatch(fileName, start, end, SearchResultsPad.CreateInlineBuilder(start, end, document, Path.GetExtension(fileName)))); + observer.OnNext(new SearchResultMatch(fileName, start, end, result.Offset, result.Length, SearchResultsPad.CreateInlineBuilder(start, end, document, Path.GetExtension(fileName)))); } lock (monitor) monitor.Progress += 1.0 / count; @@ -280,7 +285,7 @@ namespace SearchAndReplace if (result != null) { var start = document.OffsetToPosition(result.Offset); var end = document.OffsetToPosition(result.EndOffset); - return new SearchResultMatch(file, start, end, null); + return new SearchResultMatch(file, start, end, result.Offset, result.Length, null); } return null; } @@ -357,6 +362,30 @@ namespace SearchAndReplace ); } + public static void ReplaceAll(IObservable results, string replacement, CancellationToken ct) + { + var differences = new Dictionary(); + 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++; + } + }, + error => MessageService.ShowException(error), + () => ShowReplaceDoneMessage(count) + ); + } + static void MarkResult(SearchResultMatch result, bool switchToOpenedView = true) { ITextEditor textArea = OpenTextArea(result.FileName, switchToOpenedView); @@ -373,7 +402,7 @@ namespace SearchAndReplace BookmarkManager.AddMark(new Bookmark(result.FileName, result.StartLocation)); } } - + static ITextEditor OpenTextArea(string fileName, bool switchToOpenedView = true) { ITextEditorProvider textEditorProvider; @@ -389,6 +418,18 @@ namespace SearchAndReplace return null; } + static void ShowReplaceDoneMessage(int count) + { + if (count == 0) { + ShowNotFoundMessage(); + } else { + MessageService.ShowMessage( + StringParser.Parse("${res:ICSharpCode.TextEditor.Document.SearchReplaceManager.ReplaceAllDone}", + new StringTagPair("Count", count.ToString())), + "${res:Global.FinishedCaptionText}"); + } + } + static void ShowMarkDoneMessage(int count) { if (count == 0) { @@ -400,12 +441,12 @@ namespace SearchAndReplace "${res:Global.FinishedCaptionText}"); } } - + static void ShowNotFoundMessage() { MessageService.ShowMessage("${res:Dialog.NewProject.SearchReplace.SearchStringNotFound}", "${res:Dialog.NewProject.SearchReplace.SearchStringNotFound.Title}"); } - + public static void SelectResult(SearchResultMatch result) { if (result == null) { @@ -420,5 +461,26 @@ namespace SearchAndReplace editor.Select(start, end - start); } } + + public static bool IsResultSelected(SearchResultMatch result) + { + if (result == null) + return false; + var editor = GetActiveTextEditor(); + if (editor == null) + return false; + return result.FileName.Equals(editor.FileName) + && result.StartOffset == editor.SelectionStart + && result.Length == editor.SelectionLength; + } + + public static void Replace(SearchResultMatch lastMatch, string replacement) + { + if (lastMatch == null) + return; + ITextEditor textArea = OpenTextArea(lastMatch.FileName); + if (textArea != null) + textArea.Document.Replace(lastMatch.StartOffset, lastMatch.Length, lastMatch.TransformReplacePattern(replacement)); + } } } diff --git a/src/AddIns/Misc/SearchAndReplace/Project/Gui/SearchAndReplacePanel.cs b/src/AddIns/Misc/SearchAndReplace/Project/Gui/SearchAndReplacePanel.cs index 6c2fe568bd..815d278860 100644 --- a/src/AddIns/Misc/SearchAndReplace/Project/Gui/SearchAndReplacePanel.cs +++ b/src/AddIns/Misc/SearchAndReplace/Project/Gui/SearchAndReplacePanel.cs @@ -106,70 +106,46 @@ namespace SearchAndReplace } } + SearchResultMatch lastMatch; + void FindNextButtonClicked(object sender, EventArgs e) { WritebackOptions(); - var result = SearchManager.FindNext(SearchOptions.FindPattern, !SearchOptions.MatchCase, SearchOptions.MatchWholeWord, SearchOptions.SearchStrategyType, SearchOptions.DocumentIteratorType, SearchOptions.LookIn, SearchOptions.LookInFiletypes, SearchOptions.IncludeSubdirectories); - SearchManager.SelectResult(result); + lastMatch = SearchManager.FindNext(SearchOptions.FindPattern, !SearchOptions.MatchCase, SearchOptions.MatchWholeWord, SearchOptions.SearchStrategyType, SearchOptions.SearchTarget, SearchOptions.LookIn, SearchOptions.LookInFiletypes, SearchOptions.IncludeSubdirectories); + SearchManager.SelectResult(lastMatch); Focus(); } void FindAllButtonClicked(object sender, EventArgs e) { WritebackOptions(); - if (IsSelectionSearch) { - if (selection.IsTextSelected) { - RunAllInSelection(0); - } - } else { - var results = SearchManager.FindAll(SearchOptions.FindPattern, !SearchOptions.MatchCase, SearchOptions.MatchWholeWord, SearchOptions.SearchStrategyType, SearchOptions.DocumentIteratorType, SearchOptions.LookIn, SearchOptions.LookInFiletypes, SearchOptions.IncludeSubdirectories); - SearchManager.ShowSearchResults(SearchOptions.FindPattern, results); - } + var results = SearchManager.FindAll(SearchOptions.FindPattern, !SearchOptions.MatchCase, SearchOptions.MatchWholeWord, SearchOptions.SearchStrategyType, SearchOptions.SearchTarget, SearchOptions.LookIn, SearchOptions.LookInFiletypes, SearchOptions.IncludeSubdirectories); + SearchManager.ShowSearchResults(SearchOptions.FindPattern, results); } void BookmarkAllButtonClicked(object sender, EventArgs e) { WritebackOptions(); - if (IsSelectionSearch) { - if (selection.IsTextSelected) { - RunAllInSelection(1); - } - } else { - var results = SearchManager.FindAll(SearchOptions.FindPattern, !SearchOptions.MatchCase, SearchOptions.MatchWholeWord, SearchOptions.SearchStrategyType, SearchOptions.DocumentIteratorType, SearchOptions.LookIn, SearchOptions.LookInFiletypes, SearchOptions.IncludeSubdirectories); - SearchManager.MarkAll(results); - } + var results = SearchManager.FindAll(SearchOptions.FindPattern, !SearchOptions.MatchCase, SearchOptions.MatchWholeWord, SearchOptions.SearchStrategyType, SearchOptions.SearchTarget, SearchOptions.LookIn, SearchOptions.LookInFiletypes, SearchOptions.IncludeSubdirectories); + SearchManager.MarkAll(results); } void ReplaceAllButtonClicked(object sender, EventArgs e) { WritebackOptions(); - if (IsSelectionSearch) { - if (selection.IsTextSelected) { - RunAllInSelection(2); - } - } else { - using (AsynchronousWaitDialog monitor = AsynchronousWaitDialog.ShowWaitDialog("Search", true)) - { - monitor.Progress = double.NaN; // progress not implemented, use indeterminate progress -// SearchReplaceManager.ReplaceAll(monitor); - } + using (AsynchronousWaitDialog monitor = AsynchronousWaitDialog.ShowWaitDialog("Search", true)) { + var results = SearchManager.FindAll(SearchOptions.FindPattern, !SearchOptions.MatchCase, SearchOptions.MatchWholeWord, SearchOptions.SearchStrategyType, SearchOptions.SearchTarget, SearchOptions.LookIn, SearchOptions.LookInFiletypes, SearchOptions.IncludeSubdirectories, false, monitor); + SearchManager.ReplaceAll(results, SearchOptions.ReplacePattern, monitor.CancellationToken); } } void ReplaceButtonClicked(object sender, EventArgs e) { WritebackOptions(); - if (IsSelectionSearch) { - if (selection.IsTextSelected) { - ReplaceInSelection(); - } - } else { - using (AsynchronousWaitDialog monitor = AsynchronousWaitDialog.ShowWaitDialog("Search", true)) - { - monitor.Progress = double.NaN; // progress not implemented, use indeterminate progress -// SearchReplaceManager.Replace(monitor); - } - } + if (SearchManager.IsResultSelected(lastMatch)) + SearchManager.Replace(lastMatch, SearchOptions.ReplacePattern); + lastMatch = SearchManager.FindNext(SearchOptions.FindPattern, !SearchOptions.MatchCase, SearchOptions.MatchWholeWord, SearchOptions.SearchStrategyType, SearchOptions.SearchTarget, SearchOptions.LookIn, SearchOptions.LookInFiletypes, SearchOptions.IncludeSubdirectories); + SearchManager.SelectResult(lastMatch); Focus(); } @@ -191,9 +167,9 @@ namespace SearchAndReplace SearchOptions.SearchStrategyType = (SearchMode)Get("use").SelectedIndex; if (Get("lookIn").DropDownStyle == ComboBoxStyle.DropDown) { - SearchOptions.DocumentIteratorType = SearchTarget.Directory; + SearchOptions.SearchTarget = SearchTarget.Directory; } else { - SearchOptions.DocumentIteratorType = (SearchTarget)Get("lookIn").SelectedIndex; + SearchOptions.SearchTarget = (SearchTarget)Get("lookIn").SelectedIndex; } } @@ -236,10 +212,10 @@ namespace SearchAndReplace if (IsMultipleLineSelection(SearchManager.GetActiveTextEditor())) { SearchTarget = SearchTarget.CurrentSelection; } else { - if (SearchOptions.DocumentIteratorType == SearchTarget.CurrentSelection) { - SearchOptions.DocumentIteratorType = SearchTarget.CurrentDocument; + if (SearchOptions.SearchTarget == SearchTarget.CurrentSelection) { + SearchOptions.SearchTarget = SearchTarget.CurrentDocument; } - SearchTarget = SearchOptions.DocumentIteratorType; + SearchTarget = SearchOptions.SearchTarget; } Get("fileTypes").Text = SearchOptions.LookInFiletypes; @@ -374,36 +350,6 @@ namespace SearchAndReplace RemoveActiveWindowChangedHandler(); } - /// - /// action: 0 = find, 1 = mark, 2 = replace - /// - void RunAllInSelection(int action) - { - const IProgressMonitor monitor = null; - - int startOffset = Math.Min(selection.Offset, selection.EndOffset); - int endOffset = Math.Max(selection.Offset, selection.EndOffset); - - textEditor.Select(startOffset, endOffset - startOffset); - - try { - ignoreSelectionChanges = true; - if (action == 0) { -// SearchInFilesManager.FindAll(startOffset, endOffset - startOffset, monitor); - } else if (action == 1) { -// SearchReplaceManager.MarkAll(startOffset, endOffset - startOffset, monitor); - } else if (action == 2) { - // use anchor for endOffset because the replacement might change the text length - var anchor = textEditor.Document.CreateAnchor(endOffset); -// SearchReplaceManager.ReplaceAll(startOffset, endOffset - startOffset, monitor); - endOffset = anchor.Offset; - } - textEditor.Select(startOffset, endOffset - startOffset); - } finally { - ignoreSelectionChanges = false; - } - } - void ReplaceInSelection() { int startOffset = Math.Min(selection.Offset, selection.EndOffset); diff --git a/src/AddIns/Misc/SearchAndReplace/Project/SearchOptions.cs b/src/AddIns/Misc/SearchAndReplace/Project/SearchOptions.cs index 52a7aa5caf..89e878be5a 100644 --- a/src/AddIns/Misc/SearchAndReplace/Project/SearchOptions.cs +++ b/src/AddIns/Misc/SearchAndReplace/Project/SearchOptions.cs @@ -136,7 +136,7 @@ namespace SearchAndReplace } } - public static SearchTarget DocumentIteratorType { + public static SearchTarget SearchTarget { get { return properties.Get("DocumentIteratorType", SearchTarget.CurrentDocument); } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/ISearchStrategy.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/ISearchStrategy.cs index f52d12368e..b8f1d7e7b1 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/ISearchStrategy.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/ISearchStrategy.cs @@ -36,7 +36,7 @@ namespace ICSharpCode.AvalonEdit.Search /// public interface ISearchResult : ISegment { - + string ReplaceWith(string replacement); } /// diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/RegexSearchStrategy.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/RegexSearchStrategy.cs index 68f681cd5c..0610d33c54 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/RegexSearchStrategy.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/RegexSearchStrategy.cs @@ -4,7 +4,9 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text; using System.Text.RegularExpressions; + using ICSharpCode.AvalonEdit.Document; namespace ICSharpCode.AvalonEdit.Search @@ -22,7 +24,7 @@ namespace ICSharpCode.AvalonEdit.Search { foreach (Match result in searchPattern.Matches(document.Text)) { if (offset <= result.Index && (offset + length) >= (result.Index + result.Length)) - yield return new SearchResult { StartOffset = result.Index, Length = result.Length }; + yield return new SearchResult { StartOffset = result.Index, Length = result.Length, Data = result }; } } @@ -34,5 +36,11 @@ namespace ICSharpCode.AvalonEdit.Search class SearchResult : TextSegment, ISearchResult { + public Match Data { get; set; } + + public string ReplaceWith(string replacement) + { + return Data.Result(replacement); + } } } diff --git a/src/Main/Base/Project/Src/Editor/Search/SearchResultMatch.cs b/src/Main/Base/Project/Src/Editor/Search/SearchResultMatch.cs index a2bb2f331b..8b899f5ed5 100644 --- a/src/Main/Base/Project/Src/Editor/Search/SearchResultMatch.cs +++ b/src/Main/Base/Project/Src/Editor/Search/SearchResultMatch.cs @@ -15,6 +15,8 @@ namespace ICSharpCode.SharpDevelop.Editor.Search public class SearchResultMatch { FileName fileName; + int offset; + int length; Location startLocation; Location endLocation; HighlightedInlineBuilder builder; @@ -35,16 +37,30 @@ namespace ICSharpCode.SharpDevelop.Editor.Search get { return builder; } } + public int StartOffset { + get { return offset; } + } + + public int Length { + get { return length; } + } + + public int EndOffset { + get { return offset + length; } + } + public virtual string TransformReplacePattern(string pattern) { return pattern; } - public SearchResultMatch(FileName fileName, Location startLocation, Location endLocation, HighlightedInlineBuilder builder) + public SearchResultMatch(FileName fileName, Location startLocation, Location endLocation, int offset, int length, HighlightedInlineBuilder builder) { this.fileName = fileName; this.startLocation = startLocation; this.endLocation = endLocation; + this.offset = offset; + this.length = length; this.builder = builder; } @@ -75,10 +91,26 @@ namespace ICSharpCode.SharpDevelop.Editor.Search } } - public SimpleSearchResultMatch(FileName fileName, Location position, string displayText) - : base(fileName, position, position, new HighlightedInlineBuilder(displayText)) + public SimpleSearchResultMatch(FileName fileName, Location position, int offset, string displayText) + : base(fileName, position, position, offset, 0, null) { this.displayText = displayText; } } + + public class RegexResultMatch : SearchResultMatch + { + ICSharpCode.AvalonEdit.Search.ISearchResult match; + + public RegexResultMatch(FileName fileName, Location startLocation, Location endLocation, int offset, int length, HighlightedInlineBuilder builder, ICSharpCode.AvalonEdit.Search.ISearchResult match) + : base(fileName, startLocation, endLocation, offset, length, builder) + { + this.match = match; + } + + public override string TransformReplacePattern(string pattern) + { + return match.ReplaceWith(pattern); + } + } } diff --git a/src/Main/Base/Project/Src/Services/RefactoringService/FindReferencesAndRenameHelper.cs b/src/Main/Base/Project/Src/Services/RefactoringService/FindReferencesAndRenameHelper.cs index ba8c10e244..7510099272 100644 --- a/src/Main/Base/Project/Src/Services/RefactoringService/FindReferencesAndRenameHelper.cs +++ b/src/Main/Base/Project/Src/Services/RefactoringService/FindReferencesAndRenameHelper.cs @@ -376,7 +376,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring var document = new TextDocument(DocumentUtilitites.GetTextSource(ParserService.GetParseableFileContent(r.FileName))); var start = document.GetLocation(r.Offset).ToLocation(); var end = document.GetLocation(r.Offset + r.Length).ToLocation(); - SearchResultMatch res = new SearchResultMatch(new FileName(r.FileName), start, end, SearchResultsPad.CreateInlineBuilder(start, end, document, Path.GetExtension(r.FileName))); + SearchResultMatch res = new SearchResultMatch(new FileName(r.FileName), start, end, r.Offset, r.Length, SearchResultsPad.CreateInlineBuilder(start, end, document, Path.GetExtension(r.FileName))); results.Add(res); } SearchResultsPad.Instance.ShowSearchResults(title, results); diff --git a/src/Main/Base/Project/Src/Util/ReactiveExtensions.cs b/src/Main/Base/Project/Src/Util/ReactiveExtensions.cs index dc1db32485..e0ae81b2f3 100644 --- a/src/Main/Base/Project/Src/Util/ReactiveExtensions.cs +++ b/src/Main/Base/Project/Src/Util/ReactiveExtensions.cs @@ -2,7 +2,11 @@ // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; +using System.Collections.Generic; +using System.Reflection; +using System.Threading; using System.Windows.Threading; + using ICSharpCode.SharpDevelop.Gui; namespace ICSharpCode.SharpDevelop @@ -30,6 +34,18 @@ namespace ICSharpCode.SharpDevelop return source.Subscribe(new AnonymousObserver(onNext, onError, onCompleted)); } + public static List ToList(this IObservable source, CancellationToken cancellation) + { + List results = new List(); + ManualResetEventSlim ev = new ManualResetEventSlim(); + Exception error = null; + using (source.Subscribe(item => results.Add(item), exception => { error = exception; ev.Set(); }, () => ev.Set())) + ev.Wait(cancellation); + if (error != null) + throw new TargetInvocationException(error); + return results; + } + class AnonymousObservable : IObservable { Func, IDisposable> subscribe;