From 89aaa9a030321cd3ae4289add9db5b41769b16be Mon Sep 17 00:00:00 2001 From: MCpiroman Date: Sun, 24 Mar 2019 15:59:40 +0100 Subject: [PATCH] Refactor searching --- ILSpy/Search/AbstractSearchStrategy.cs | 116 ++++---- ILSpy/Search/LiteralSearchStrategy.cs | 15 +- ILSpy/Search/MemberSearchStrategy.cs | 28 +- ILSpy/Search/MetadataTokenSearchStrategy.cs | 19 +- ILSpy/Search/SearchPane.cs | 281 +++++++++++--------- ILSpy/Search/SearchPane.xaml | 14 +- 6 files changed, 259 insertions(+), 214 deletions(-) diff --git a/ILSpy/Search/AbstractSearchStrategy.cs b/ILSpy/Search/AbstractSearchStrategy.cs index c03d80d3b..71b00c363 100644 --- a/ILSpy/Search/AbstractSearchStrategy.cs +++ b/ILSpy/Search/AbstractSearchStrategy.cs @@ -1,15 +1,11 @@ using System; -using System.Collections.Generic; -using System.Linq; +using System.Collections.Concurrent; using System.Text.RegularExpressions; +using System.Threading; using System.Windows.Media; -using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.Decompiler.Metadata; -using System.Reflection; using ICSharpCode.Decompiler.TypeSystem; -using System.Reflection.Metadata; -using ICSharpCode.Decompiler; -using ICSharpCode.Decompiler.Util; +using ICSharpCode.ILSpy.TreeNodes; namespace ICSharpCode.ILSpy.Search { @@ -19,12 +15,12 @@ namespace ICSharpCode.ILSpy.Search protected readonly Regex regex; protected readonly bool fullNameSearch; protected readonly Language language; - protected readonly Action addResult; + private readonly IProducerConsumerCollection resultQueue; - protected AbstractSearchStrategy(Language language, Action addResult, params string[] terms) + protected AbstractSearchStrategy(Language language, IProducerConsumerCollection resultQueue, params string[] terms) { this.language = language; - this.addResult = addResult; + this.resultQueue = resultQueue; if (terms.Length == 1 && terms[0].Length > 2) { string search = terms[0]; @@ -41,26 +37,7 @@ namespace ICSharpCode.ILSpy.Search searchTerm = terms; } - protected float CalculateFitness(IEntity member) - { - string text = member.Name; - - // Probably compiler generated types without meaningful names, show them last - if (text.StartsWith("<")) { - return 0; - } - - // Constructors always have the same name in IL: - // Use type name instead - if (text == ".cctor" || text == ".ctor") { - text = member.DeclaringType.Name; - } - - // Ignore generic arguments, it not possible to search based on them either - text = ReflectionHelper.SplitTypeParameterCountFromReflectionName(text); - - return 1.0f / text.Length; - } + public abstract void Search(PEFile module, CancellationToken cancellationToken); protected virtual bool IsMatch(string entityName) { @@ -131,8 +108,58 @@ namespace ICSharpCode.ILSpy.Search } return false; } + + protected void OnFoundResult(IEntity entity) + { + var result = ResultFromEntity(entity); + resultQueue.TryAdd(result); + } + + Regex SafeNewRegex(string unsafePattern) + { + try { + return new Regex(unsafePattern, RegexOptions.Compiled); + } catch (ArgumentException) { + return null; + } + } + + SearchResult ResultFromEntity(IEntity item) + { + var declaringType = item.DeclaringTypeDefinition; + return new SearchResult { + Member = item, + Fitness = CalculateFitness(item), + Image = GetIcon(item), + Name = GetLanguageSpecificName(item), + LocationImage = declaringType != null ? TypeTreeNode.GetIcon(declaringType) : Images.Namespace, + Location = declaringType != null ? language.TypeToString(declaringType, includeNamespace: true) : item.Namespace, + ToolTip = item.ParentModule.PEFile?.FileName + }; + } + + float CalculateFitness(IEntity member) + { + string text = member.Name; + + // Probably compiler generated types without meaningful names, show them last + if (text.StartsWith("<")) { + return 0; + } + + // Constructors always have the same name in IL: + // Use type name instead + if (text == ".cctor" || text == ".ctor") { + text = member.DeclaringType.Name; + } + + // Ignore generic arguments, it not possible to search based on them either + text = ReflectionHelper.SplitTypeParameterCountFromReflectionName(text); + + return 1.0f / text.Length; + } - protected string GetLanguageSpecificName(IEntity member) + string GetLanguageSpecificName(IEntity member) { switch (member) { case ITypeDefinition t: @@ -150,7 +177,7 @@ namespace ICSharpCode.ILSpy.Search } } - protected ImageSource GetIcon(IEntity member) + ImageSource GetIcon(IEntity member) { switch (member) { case ITypeDefinition t: @@ -167,30 +194,5 @@ namespace ICSharpCode.ILSpy.Search throw new NotSupportedException(member?.GetType() + " not supported!"); } } - - public abstract void Search(PEFile module); - - Regex SafeNewRegex(string unsafePattern) - { - try { - return new Regex(unsafePattern, RegexOptions.Compiled); - } catch (ArgumentException) { - return null; - } - } - - protected SearchResult ResultFromEntity(IEntity item) - { - var declaringType = item.DeclaringTypeDefinition; - return new SearchResult { - Member = item, - Fitness = CalculateFitness(item), - Image = GetIcon(item), - Name = GetLanguageSpecificName(item), - LocationImage = declaringType != null ? TypeTreeNode.GetIcon(declaringType) : Images.Namespace, - Location = declaringType != null ? language.TypeToString(declaringType, includeNamespace: true) : item.Namespace, - ToolTip = item.ParentModule.PEFile?.FileName - }; - } } } diff --git a/ILSpy/Search/LiteralSearchStrategy.cs b/ILSpy/Search/LiteralSearchStrategy.cs index 4eb7d6707..658033862 100644 --- a/ILSpy/Search/LiteralSearchStrategy.cs +++ b/ILSpy/Search/LiteralSearchStrategy.cs @@ -10,6 +10,8 @@ using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Metadata; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; +using System.Threading; +using System.Collections.Concurrent; namespace ICSharpCode.ILSpy.Search { @@ -18,8 +20,8 @@ namespace ICSharpCode.ILSpy.Search readonly TypeCode searchTermLiteralType; readonly object searchTermLiteralValue; - public LiteralSearchStrategy(Language language, Action addResult, params string[] terms) - : base(language, addResult, terms) + public LiteralSearchStrategy(Language language, IProducerConsumerCollection resultQueue, params string[] terms) + : base(language, resultQueue, terms) { if (terms.Length == 1) { var lexer = new Lexer(new LATextReader(new System.IO.StringReader(terms[0]))); @@ -50,20 +52,23 @@ namespace ICSharpCode.ILSpy.Search } } - public override void Search(PEFile module) + public override void Search(PEFile module, CancellationToken cancellationToken) { + cancellationToken.ThrowIfCancellationRequested(); var metadata = module.Metadata; var typeSystem = module.GetTypeSystemOrNull(); if (typeSystem == null) return; foreach (var handle in metadata.MethodDefinitions) { + cancellationToken.ThrowIfCancellationRequested(); var md = metadata.GetMethodDefinition(handle); if (!md.HasBody() || !MethodIsLiteralMatch(module, md)) continue; var method = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); - addResult(ResultFromEntity(method)); + OnFoundResult(method); } foreach (var handle in metadata.FieldDefinitions) { + cancellationToken.ThrowIfCancellationRequested(); var fd = metadata.GetFieldDefinition(handle); if (!fd.HasFlag(System.Reflection.FieldAttributes.Literal)) continue; @@ -75,7 +80,7 @@ namespace ICSharpCode.ILSpy.Search if (!IsLiteralMatch(metadata, blob.ReadConstant(constant.TypeCode))) continue; IField field = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); - addResult(ResultFromEntity(field)); + OnFoundResult(field); } } diff --git a/ILSpy/Search/MemberSearchStrategy.cs b/ILSpy/Search/MemberSearchStrategy.cs index 010a1fe61..cf7cb7b93 100644 --- a/ILSpy/Search/MemberSearchStrategy.cs +++ b/ILSpy/Search/MemberSearchStrategy.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Concurrent; +using System.Threading; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; @@ -8,71 +10,77 @@ namespace ICSharpCode.ILSpy.Search { readonly MemberSearchKind searchKind; - public MemberSearchStrategy(Language language, Action addResult, string term, MemberSearchKind searchKind = MemberSearchKind.All) - : this(language, addResult, new[] { term }, searchKind) + public MemberSearchStrategy(Language language, string term, IProducerConsumerCollection resultQueue, MemberSearchKind searchKind = MemberSearchKind.All) + : this(language, resultQueue, new[] { term }, searchKind) { } - public MemberSearchStrategy(Language language, Action addResult, string[] terms, MemberSearchKind searchKind = MemberSearchKind.All) - : base(language, addResult, terms) + public MemberSearchStrategy(Language language, IProducerConsumerCollection resultQueue, string[] terms, MemberSearchKind searchKind = MemberSearchKind.All) + : base(language, resultQueue, terms) { this.searchKind = searchKind; } - public override void Search(PEFile module) + public override void Search(PEFile module, CancellationToken cancellationToken) { + cancellationToken.ThrowIfCancellationRequested(); var metadata = module.Metadata; var typeSystem = module.GetTypeSystemOrNull(); if (typeSystem == null) return; if (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Type) { foreach (var handle in metadata.TypeDefinitions) { + cancellationToken.ThrowIfCancellationRequested(); string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch); if (languageSpecificName != null && !IsMatch(languageSpecificName)) continue; var type = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); - addResult(ResultFromEntity(type)); + OnFoundResult(type); } } if (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Member || searchKind == MemberSearchKind.Method) { foreach (var handle in metadata.MethodDefinitions) { + cancellationToken.ThrowIfCancellationRequested(); // TODO use method semantics to skip accessors string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch); if (languageSpecificName != null && !IsMatch(languageSpecificName)) continue; var method = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); - addResult(ResultFromEntity(method)); + OnFoundResult(method); } } if (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Member || searchKind == MemberSearchKind.Field) { foreach (var handle in metadata.FieldDefinitions) { + cancellationToken.ThrowIfCancellationRequested(); string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch); if (languageSpecificName != null && !IsMatch(languageSpecificName)) continue; var field = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); - addResult(ResultFromEntity(field)); + OnFoundResult(field); } } if (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Member || searchKind == MemberSearchKind.Property) { foreach (var handle in metadata.PropertyDefinitions) { + cancellationToken.ThrowIfCancellationRequested(); string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch); if (languageSpecificName != null && !IsMatch(languageSpecificName)) continue; var property = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); - addResult(ResultFromEntity(property)); + OnFoundResult(property); } } if (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Member || searchKind == MemberSearchKind.Event) { foreach (var handle in metadata.EventDefinitions) { + cancellationToken.ThrowIfCancellationRequested(); string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch); if (!IsMatch(languageSpecificName)) continue; var @event = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); - addResult(ResultFromEntity(@event)); + OnFoundResult(@event); } } } diff --git a/ILSpy/Search/MetadataTokenSearchStrategy.cs b/ILSpy/Search/MetadataTokenSearchStrategy.cs index d1951907f..c31541915 100644 --- a/ILSpy/Search/MetadataTokenSearchStrategy.cs +++ b/ILSpy/Search/MetadataTokenSearchStrategy.cs @@ -1,7 +1,9 @@ using System; +using System.Collections.Concurrent; using System.Globalization; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; +using System.Threading; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; @@ -11,8 +13,8 @@ namespace ICSharpCode.ILSpy.Search { readonly EntityHandle searchTermToken; - public MetadataTokenSearchStrategy(Language language, Action addResult, params string[] terms) - : base(language, addResult, terms) + public MetadataTokenSearchStrategy(Language language, IProducerConsumerCollection resultQueue, params string[] terms) + : base(language, resultQueue, terms) { if (terms.Length == 1) { int.TryParse(terms[0], NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var token); @@ -20,8 +22,9 @@ namespace ICSharpCode.ILSpy.Search } } - public override void Search(PEFile module) + public override void Search(PEFile module, CancellationToken cancellationToken) { + cancellationToken.ThrowIfCancellationRequested(); if (searchTermToken.IsNil) return; var typeSystem = module.GetTypeSystemOrNull(); if (typeSystem == null) return; @@ -33,31 +36,31 @@ namespace ICSharpCode.ILSpy.Search if (row < 1 || row > module.Metadata.TypeDefinitions.Count) break; var type = metadataModule.GetDefinition((TypeDefinitionHandle)searchTermToken); - addResult(ResultFromEntity(type)); + OnFoundResult(type); break; case HandleKind.MethodDefinition: if (row < 1 || row > module.Metadata.MethodDefinitions.Count) break; var method = metadataModule.GetDefinition((MethodDefinitionHandle)searchTermToken); - addResult(ResultFromEntity(method)); + OnFoundResult(method); break; case HandleKind.FieldDefinition: if (row < 1 || row > module.Metadata.FieldDefinitions.Count) break; var field = metadataModule.GetDefinition((FieldDefinitionHandle)searchTermToken); - addResult(ResultFromEntity(field)); + OnFoundResult(field); break; case HandleKind.PropertyDefinition: if (row < 1 || row > module.Metadata.PropertyDefinitions.Count) break; var property = metadataModule.GetDefinition((PropertyDefinitionHandle)searchTermToken); - addResult(ResultFromEntity(property)); + OnFoundResult(property); break; case HandleKind.EventDefinition: if (row < 1 || row > module.Metadata.EventDefinitions.Count) break; var @event = metadataModule.GetDefinition((EventDefinitionHandle)searchTermToken); - addResult(ResultFromEntity(@event)); + OnFoundResult(@event); break; } } diff --git a/ILSpy/Search/SearchPane.cs b/ILSpy/Search/SearchPane.cs index 9d9dea18f..76ea90a53 100644 --- a/ILSpy/Search/SearchPane.cs +++ b/ILSpy/Search/SearchPane.cs @@ -17,9 +17,13 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Collections.Concurrent; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; +using System.Diagnostics; using System.Threading; +using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Input; @@ -36,9 +40,19 @@ namespace ICSharpCode.ILSpy /// public partial class SearchPane : UserControl, IPane { + const int MAX_RESULTS = 1000; + const int MAX_REFRESH_TIME_MS = 10; // More means quicker forward of data, less means better responsibility static SearchPane instance; RunningSearch currentSearch; - + bool runSearchOnNextShow; + + public static readonly DependencyProperty ResultsProperty = + DependencyProperty.Register("Results", typeof(ObservableCollection), typeof(SearchPane), + new PropertyMetadata(new ObservableCollection())); + public ObservableCollection Results { + get { return (ObservableCollection)GetValue(ResultsProperty); } + } + public static SearchPane Instance { get { if (instance == null) { @@ -61,15 +75,15 @@ namespace ICSharpCode.ILSpy searchModeComboBox.Items.Add(new { Image = Images.Event, Name = "Event" }); searchModeComboBox.Items.Add(new { Image = Images.Literal, Name = "Constant" }); searchModeComboBox.Items.Add(new { Image = Images.Library, Name = "Metadata Token" }); - searchModeComboBox.SelectedIndex = (int)MainWindow.Instance.SessionSettings.SelectedSearchMode; - searchModeComboBox.SelectionChanged += (sender, e) => MainWindow.Instance.SessionSettings.SelectedSearchMode = (SearchMode)searchModeComboBox.SelectedIndex; + ContextMenuProvider.Add(listBox); - MainWindow.Instance.CurrentAssemblyListChanged += MainWindow_Instance_CurrentAssemblyListChanged; + CompositionTarget.Rendering += UpdateResults; + + // This starts empty search right away, so do at the end (we're still in ctor) + searchModeComboBox.SelectedIndex = (int)MainWindow.Instance.SessionSettings.SelectedSearchMode; } - - bool runSearchOnNextShow; - + void MainWindow_Instance_CurrentAssemblyListChanged(object sender, NotifyCollectionChangedEventArgs e) { if (IsVisible) { @@ -79,7 +93,7 @@ namespace ICSharpCode.ILSpy runSearchOnNextShow = true; } } - + public void Show() { if (!IsVisible) { @@ -97,43 +111,27 @@ namespace ICSharpCode.ILSpy searchBox.SelectAll(); })); } - + public static readonly DependencyProperty SearchTermProperty = DependencyProperty.Register("SearchTerm", typeof(string), typeof(SearchPane), - new FrameworkPropertyMetadata(string.Empty, OnSearchTermChanged)); - + new FrameworkPropertyMetadata(string.Empty, OnSearchTermChanged)); + public string SearchTerm { get { return (string)GetValue(SearchTermProperty); } set { SetValue(SearchTermProperty, value); } } - + static void OnSearchTermChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) { ((SearchPane)o).StartSearch((string)e.NewValue); } - + void SearchModeComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { + MainWindow.Instance.SessionSettings.SelectedSearchMode = (SearchMode)searchModeComboBox.SelectedIndex; StartSearch(this.SearchTerm); } - - void StartSearch(string searchTerm) - { - if (currentSearch != null) { - currentSearch.Cancel(); - } - if (string.IsNullOrEmpty(searchTerm)) { - currentSearch = null; - listBox.ItemsSource = null; - } else { - MainWindow mainWindow = MainWindow.Instance; - currentSearch = new RunningSearch(mainWindow.CurrentAssemblyList.GetAssemblies(), searchTerm, - (SearchMode)searchModeComboBox.SelectedIndex, mainWindow.CurrentLanguage); - listBox.ItemsSource = currentSearch.Results; - new Thread(currentSearch.Run).Start(); - } - } - + void IPane.Closed() { this.SearchTerm = string.Empty; @@ -150,17 +148,13 @@ namespace ICSharpCode.ILSpy if (e.Key == Key.Return) { e.Handled = true; JumpToSelectedItem(); + } else if(e.Key == Key.Up && listBox.SelectedIndex == 0) { + e.Handled = true; + listBox.SelectedIndex = -1; + searchBox.Focus(); } } - - void JumpToSelectedItem() - { - SearchResult result = listBox.SelectedItem as SearchResult; - if (result != null) { - MainWindow.Instance.JumpToReference(result.Member); - } - } - + protected override void OnKeyDown(KeyEventArgs e) { base.OnKeyDown(e); @@ -175,7 +169,7 @@ namespace ICSharpCode.ILSpy e.Handled = true; } } - + void SearchBox_PreviewKeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.Down && listBox.HasItems) { @@ -185,26 +179,94 @@ namespace ICSharpCode.ILSpy } } + void UpdateResults(object sender, EventArgs e) + { + if (currentSearch == null) + return; + + var timer = Stopwatch.StartNew(); + int resultsAdded = 0; + while (Results.Count < MAX_RESULTS && timer.ElapsedMilliseconds < MAX_REFRESH_TIME_MS && currentSearch.resultQueue.TryTake(out var result)) { + InsertResult(Results, result); + ++resultsAdded; + } + + if (resultsAdded > 0 && Results.Count == MAX_RESULTS) { + Results.Add(new SearchResult { Name = "Search aborted, more than 1000 results found." }); + currentSearch.Cancel(); + } + } + + async void StartSearch(string searchTerm) + { + if (currentSearch != null) { + currentSearch.Cancel(); + currentSearch = null; + } + + Results.Clear(); + + RunningSearch startedSearch = null; + if (!string.IsNullOrEmpty(searchTerm)) { + MainWindow mainWindow = MainWindow.Instance; + + searchProgressBar.IsIndeterminate = true; + startedSearch = new RunningSearch(mainWindow.CurrentAssemblyList.GetAssemblies(), searchTerm, + (SearchMode)searchModeComboBox.SelectedIndex, mainWindow.CurrentLanguage); + currentSearch = startedSearch; + + await startedSearch.Run(); + } + + if (currentSearch == startedSearch) { //are we still running the same search + searchProgressBar.IsIndeterminate = false; + } + } + + void InsertResult(IList results, SearchResult result) + { + if (results.Count == 0) { + results.Add(result); + } else if (Options.DisplaySettingsPanel.CurrentDisplaySettings.SortResults) { + // Keep results collection sorted by "Fitness" by inserting result into correct place + // Inserts in the beginning shifts all elements, but there can be no more than 1000 items. + for (int i = 0; i < results.Count; i++) { + if (results[i].Fitness < result.Fitness) { + results.Insert(i, result); + return; + } + } + results.Insert(results.Count - 1, result); + } else { + // Original Code + int index = results.BinarySearch(result, 0, results.Count - 1, SearchResult.Comparer); + results.Insert(index < 0 ? ~index : index, result); + } + } + + void JumpToSelectedItem() + { + if (listBox.SelectedItem is SearchResult result) { + MainWindow.Instance.JumpToReference(result.Member); + } + } + sealed class RunningSearch { - readonly Dispatcher dispatcher; readonly CancellationTokenSource cts = new CancellationTokenSource(); readonly LoadedAssembly[] assemblies; readonly string[] searchTerm; readonly SearchMode searchMode; readonly Language language; - public readonly ObservableCollection Results = new ObservableCollection(); - int resultCount; + public readonly IProducerConsumerCollection resultQueue = new ConcurrentQueue(); public RunningSearch(LoadedAssembly[] assemblies, string searchTerm, SearchMode searchMode, Language language) { - this.dispatcher = Dispatcher.CurrentDispatcher; this.assemblies = assemblies; this.searchTerm = NativeMethods.CommandLineToArgumentArray(searchTerm); this.language = language; this.searchMode = searchMode; - this.Results.Add(new SearchResult { Name = "Searching..." }); } public void Cancel() @@ -212,114 +274,75 @@ namespace ICSharpCode.ILSpy cts.Cancel(); } - public void Run() - { - try { - var searcher = GetSearchStrategy(searchMode, searchTerm); - // TODO : parallelize - foreach (var loadedAssembly in assemblies) { - var module = loadedAssembly.GetPEFileOrNull(); - if (module == null) - continue; - CancellationToken cancellationToken = cts.Token; - searcher.Search(module); - } - } catch (OperationCanceledException) { - // ignore cancellation - } - // remove the 'Searching...' entry - dispatcher.BeginInvoke( - DispatcherPriority.Normal, - new Action(delegate { this.Results.RemoveAt(this.Results.Count - 1); })); - } - - void AddResult(SearchResult result) - { - if (++resultCount == 1000) { - result = new SearchResult { Name = "Search aborted, more than 1000 results found." }; - cts.Cancel(); - } - dispatcher.BeginInvoke( - DispatcherPriority.Normal, - new Action(delegate { InsertResult(this.Results, result); })); - cts.Token.ThrowIfCancellationRequested(); - } - - void InsertResult(ObservableCollection results, SearchResult result) + public async Task Run() { - if (Options.DisplaySettingsPanel.CurrentDisplaySettings.SortResults) - { - // Keep results collection sorted by "Fitness" by inserting result into correct place - // Inserts in the beginning shifts all elements, but there can be no more than 1000 items. - for (int i = 0; i < results.Count; i++) - { - if (results[i].Fitness < result.Fitness) - { - results.Insert(i, result); - return; + await Task.Factory.StartNew(() => { + var searcher = GetSearchStrategy(); + try { + foreach (var loadedAssembly in assemblies) { + var module = loadedAssembly.GetPEFileOrNull(); + if (module == null) + continue; + searcher.Search(module, cts.Token); } + } catch (OperationCanceledException) { + // ignore cancellation } - results.Insert(results.Count - 1, result); - } - else - { - // Original Code - int index = results.BinarySearch(result, 0, results.Count - 1, SearchResult.Comparer); - results.Insert(index < 0 ? ~index : index, result); - } + + }, cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false); } - AbstractSearchStrategy GetSearchStrategy(SearchMode mode, string[] terms) + AbstractSearchStrategy GetSearchStrategy() { - if (terms.Length == 1) { - if (terms[0].StartsWith("tm:", StringComparison.Ordinal)) - return new MemberSearchStrategy(language, AddResult, terms[0].Substring(3)); + if (searchTerm.Length == 1) { + if (searchTerm[0].StartsWith("tm:", StringComparison.Ordinal)) + return new MemberSearchStrategy(language, searchTerm[0].Substring(3), resultQueue); - if (terms[0].StartsWith("t:", StringComparison.Ordinal)) - return new MemberSearchStrategy(language, AddResult, terms[0].Substring(2), MemberSearchKind.Type); + if (searchTerm[0].StartsWith("t:", StringComparison.Ordinal)) + return new MemberSearchStrategy(language, searchTerm[0].Substring(2), resultQueue, MemberSearchKind.Type); - if (terms[0].StartsWith("m:", StringComparison.Ordinal)) - return new MemberSearchStrategy(language, AddResult, terms[0].Substring(2), MemberSearchKind.Member); + if (searchTerm[0].StartsWith("m:", StringComparison.Ordinal)) + return new MemberSearchStrategy(language, searchTerm[0].Substring(2), resultQueue, MemberSearchKind.Member); - if (terms[0].StartsWith("md:", StringComparison.Ordinal)) - return new MemberSearchStrategy(language, AddResult, terms[0].Substring(3), MemberSearchKind.Method); + if (searchTerm[0].StartsWith("md:", StringComparison.Ordinal)) + return new MemberSearchStrategy(language, searchTerm[0].Substring(3), resultQueue, MemberSearchKind.Method); - if (terms[0].StartsWith("f:", StringComparison.Ordinal)) - return new MemberSearchStrategy(language, AddResult, terms[0].Substring(2), MemberSearchKind.Field); + if (searchTerm[0].StartsWith("f:", StringComparison.Ordinal)) + return new MemberSearchStrategy(language, searchTerm[0].Substring(2), resultQueue, MemberSearchKind.Field); - if (terms[0].StartsWith("p:", StringComparison.Ordinal)) - return new MemberSearchStrategy(language, AddResult, terms[0].Substring(2), MemberSearchKind.Property); + if (searchTerm[0].StartsWith("p:", StringComparison.Ordinal)) + return new MemberSearchStrategy(language, searchTerm[0].Substring(2), resultQueue, MemberSearchKind.Property); - if (terms[0].StartsWith("e:", StringComparison.Ordinal)) - return new MemberSearchStrategy(language, AddResult, terms[0].Substring(2), MemberSearchKind.Event); + if (searchTerm[0].StartsWith("e:", StringComparison.Ordinal)) + return new MemberSearchStrategy(language, searchTerm[0].Substring(2), resultQueue, MemberSearchKind.Event); - if (terms[0].StartsWith("c:", StringComparison.Ordinal)) - return new LiteralSearchStrategy(language, AddResult, terms[0].Substring(2)); + if (searchTerm[0].StartsWith("c:", StringComparison.Ordinal)) + return new LiteralSearchStrategy(language, resultQueue, searchTerm[0].Substring(2)); - if (terms[0].StartsWith("@", StringComparison.Ordinal)) - return new MetadataTokenSearchStrategy(language, AddResult, terms[0].Substring(1)); + if (searchTerm[0].StartsWith("@", StringComparison.Ordinal)) + return new MetadataTokenSearchStrategy(language, resultQueue, searchTerm[0].Substring(1)); } - switch (mode) + switch (searchMode) { case SearchMode.TypeAndMember: - return new MemberSearchStrategy(language, AddResult, terms); + return new MemberSearchStrategy(language, resultQueue, searchTerm); case SearchMode.Type: - return new MemberSearchStrategy(language, AddResult, terms, MemberSearchKind.Type); + return new MemberSearchStrategy(language, resultQueue, searchTerm, MemberSearchKind.Type); case SearchMode.Member: - return new MemberSearchStrategy(language, AddResult, terms, MemberSearchKind.Member); + return new MemberSearchStrategy(language, resultQueue, searchTerm, MemberSearchKind.Member); case SearchMode.Literal: - return new LiteralSearchStrategy(language, AddResult, terms); + return new LiteralSearchStrategy(language, resultQueue, searchTerm); case SearchMode.Method: - return new MemberSearchStrategy(language, AddResult, terms, MemberSearchKind.Method); + return new MemberSearchStrategy(language, resultQueue, searchTerm, MemberSearchKind.Method); case SearchMode.Field: - return new MemberSearchStrategy(language, AddResult, terms, MemberSearchKind.Field); + return new MemberSearchStrategy(language, resultQueue, searchTerm, MemberSearchKind.Field); case SearchMode.Property: - return new MemberSearchStrategy(language, AddResult, terms, MemberSearchKind.Property); + return new MemberSearchStrategy(language, resultQueue, searchTerm, MemberSearchKind.Property); case SearchMode.Event: - return new MemberSearchStrategy(language, AddResult, terms, MemberSearchKind.Event); + return new MemberSearchStrategy(language, resultQueue, searchTerm, MemberSearchKind.Event); case SearchMode.Token: - return new MetadataTokenSearchStrategy(language, AddResult, terms); + return new MetadataTokenSearchStrategy(language, resultQueue, searchTerm); } return null; @@ -327,7 +350,7 @@ namespace ICSharpCode.ILSpy } } - sealed class SearchResult : IMemberTreeNode + public sealed class SearchResult : IMemberTreeNode { public static readonly System.Collections.Generic.IComparer Comparer = new SearchResultComparer(); diff --git a/ILSpy/Search/SearchPane.xaml b/ILSpy/Search/SearchPane.xaml index eca5a13a1..fcfc9c088 100644 --- a/ILSpy/Search/SearchPane.xaml +++ b/ILSpy/Search/SearchPane.xaml @@ -6,10 +6,11 @@ d:DesignHeight="300" d:DesignWidth="300"> - - + + + - + @@ -36,8 +37,11 @@ - + +