Browse Source

Merge pull request #1464 from MCpiroman/improve-search

Improve searching
pull/1476/head
Siegfried Pammer 6 years ago committed by GitHub
parent
commit
5d5de6e54e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 140
      ILSpy/Search/AbstractSearchStrategy.cs
  2. 17
      ILSpy/Search/LiteralSearchStrategy.cs
  3. 34
      ILSpy/Search/MemberSearchStrategy.cs
  4. 24
      ILSpy/Search/MetadataTokenSearchStrategy.cs
  5. 302
      ILSpy/Search/SearchPane.cs
  6. 14
      ILSpy/Search/SearchPane.xaml

140
ILSpy/Search/AbstractSearchStrategy.cs

@ -1,15 +1,11 @@ @@ -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,14 @@ namespace ICSharpCode.ILSpy.Search @@ -19,12 +15,14 @@ namespace ICSharpCode.ILSpy.Search
protected readonly Regex regex;
protected readonly bool fullNameSearch;
protected readonly Language language;
protected readonly Action<SearchResult> addResult;
protected readonly ApiVisibility apiVisibility;
private readonly IProducerConsumerCollection<SearchResult> resultQueue;
protected AbstractSearchStrategy(Language language, Action<SearchResult> addResult, params string[] terms)
protected AbstractSearchStrategy(Language language, ApiVisibility apiVisibility, IProducerConsumerCollection<SearchResult> resultQueue, params string[] terms)
{
this.language = language;
this.addResult = addResult;
this.apiVisibility = apiVisibility;
this.resultQueue = resultQueue;
if (terms.Length == 1 && terms[0].Length > 2) {
string search = terms[0];
@ -41,26 +39,7 @@ namespace ICSharpCode.ILSpy.Search @@ -41,26 +39,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)
{
@ -105,6 +84,28 @@ namespace ICSharpCode.ILSpy.Search @@ -105,6 +84,28 @@ namespace ICSharpCode.ILSpy.Search
return true;
}
protected bool CheckVisibility(IEntity entity)
{
if (apiVisibility == ApiVisibility.All)
return true;
do {
if (apiVisibility == ApiVisibility.PublicOnly) {
if (!(entity.Accessibility == Accessibility.Public ||
entity.Accessibility == Accessibility.Protected ||
entity.Accessibility == Accessibility.ProtectedOrInternal))
return false;
} else if (apiVisibility == ApiVisibility.PublicAndInternal) {
if (!language.ShowMember(entity))
return false;
}
entity = entity.DeclaringTypeDefinition;
}
while (entity != null);
return true;
}
bool IsNoncontiguousMatch(string text, string searchTerm)
{
if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(searchTerm)) {
@ -131,8 +132,58 @@ namespace ICSharpCode.ILSpy.Search @@ -131,8 +132,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;
}
}
protected string GetLanguageSpecificName(IEntity member)
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;
}
string GetLanguageSpecificName(IEntity member)
{
switch (member) {
case ITypeDefinition t:
@ -150,7 +201,7 @@ namespace ICSharpCode.ILSpy.Search @@ -150,7 +201,7 @@ namespace ICSharpCode.ILSpy.Search
}
}
protected ImageSource GetIcon(IEntity member)
ImageSource GetIcon(IEntity member)
{
switch (member) {
case ITypeDefinition t:
@ -167,30 +218,5 @@ namespace ICSharpCode.ILSpy.Search @@ -167,30 +218,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
};
}
}
}

17
ILSpy/Search/LiteralSearchStrategy.cs

@ -10,6 +10,8 @@ using ICSharpCode.Decompiler.TypeSystem; @@ -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 @@ -18,8 +20,8 @@ namespace ICSharpCode.ILSpy.Search
readonly TypeCode searchTermLiteralType;
readonly object searchTermLiteralValue;
public LiteralSearchStrategy(Language language, Action<SearchResult> addResult, params string[] terms)
: base(language, addResult, terms)
public LiteralSearchStrategy(Language language, ApiVisibility apiVisibility, IProducerConsumerCollection<SearchResult> resultQueue, params string[] terms)
: base(language, apiVisibility, resultQueue, terms)
{
if (terms.Length == 1) {
var lexer = new Lexer(new LATextReader(new System.IO.StringReader(terms[0])));
@ -50,20 +52,24 @@ namespace ICSharpCode.ILSpy.Search @@ -50,20 +52,24 @@ 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));
if (!CheckVisibility(method)) continue;
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 +81,8 @@ namespace ICSharpCode.ILSpy.Search @@ -75,7 +81,8 @@ namespace ICSharpCode.ILSpy.Search
if (!IsLiteralMatch(metadata, blob.ReadConstant(constant.TypeCode)))
continue;
IField field = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle);
addResult(ResultFromEntity(field));
if (!CheckVisibility(field)) continue;
OnFoundResult(field);
}
}

34
ILSpy/Search/MemberSearchStrategy.cs

@ -1,4 +1,6 @@ @@ -1,4 +1,6 @@
using System;
using System.Collections.Concurrent;
using System.Threading;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
@ -8,71 +10,81 @@ namespace ICSharpCode.ILSpy.Search @@ -8,71 +10,81 @@ namespace ICSharpCode.ILSpy.Search
{
readonly MemberSearchKind searchKind;
public MemberSearchStrategy(Language language, Action<SearchResult> addResult, string term, MemberSearchKind searchKind = MemberSearchKind.All)
: this(language, addResult, new[] { term }, searchKind)
public MemberSearchStrategy(Language language, ApiVisibility apiVisibility, string term, IProducerConsumerCollection<SearchResult> resultQueue, MemberSearchKind searchKind = MemberSearchKind.All)
: this(language, apiVisibility, resultQueue, new[] { term }, searchKind)
{
}
public MemberSearchStrategy(Language language, Action<SearchResult> addResult, string[] terms, MemberSearchKind searchKind = MemberSearchKind.All)
: base(language, addResult, terms)
public MemberSearchStrategy(Language language, ApiVisibility apiVisibility, IProducerConsumerCollection<SearchResult> resultQueue, string[] terms, MemberSearchKind searchKind = MemberSearchKind.All)
: base(language, apiVisibility, 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));
if (!CheckVisibility(type)) continue;
OnFoundResult(type);
}
}
if (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Member || searchKind == MemberSearchKind.Method) {
foreach (var handle in metadata.MethodDefinitions) {
// TODO use method semantics to skip accessors
cancellationToken.ThrowIfCancellationRequested();
string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch);
if (languageSpecificName != null && !IsMatch(languageSpecificName))
continue;
var method = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle);
addResult(ResultFromEntity(method));
if (!CheckVisibility(method)) continue;
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));
if (!CheckVisibility(field)) continue;
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));
if (!CheckVisibility(property)) continue;
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));
if (!CheckVisibility(@event)) continue;
OnFoundResult(@event);
}
}
}

24
ILSpy/Search/MetadataTokenSearchStrategy.cs

@ -1,7 +1,9 @@ @@ -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 @@ -11,8 +13,8 @@ namespace ICSharpCode.ILSpy.Search
{
readonly EntityHandle searchTermToken;
public MetadataTokenSearchStrategy(Language language, Action<SearchResult> addResult, params string[] terms)
: base(language, addResult, terms)
public MetadataTokenSearchStrategy(Language language, ApiVisibility apiVisibility, IProducerConsumerCollection<SearchResult> resultQueue, params string[] terms)
: base(language, apiVisibility, 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 @@ -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,36 @@ namespace ICSharpCode.ILSpy.Search @@ -33,31 +36,36 @@ namespace ICSharpCode.ILSpy.Search
if (row < 1 || row > module.Metadata.TypeDefinitions.Count)
break;
var type = metadataModule.GetDefinition((TypeDefinitionHandle)searchTermToken);
addResult(ResultFromEntity(type));
if (!CheckVisibility(type)) break;
OnFoundResult(type);
break;
case HandleKind.MethodDefinition:
if (row < 1 || row > module.Metadata.MethodDefinitions.Count)
break;
var method = metadataModule.GetDefinition((MethodDefinitionHandle)searchTermToken);
addResult(ResultFromEntity(method));
if (!CheckVisibility(method)) break;
OnFoundResult(method);
break;
case HandleKind.FieldDefinition:
if (row < 1 || row > module.Metadata.FieldDefinitions.Count)
break;
var field = metadataModule.GetDefinition((FieldDefinitionHandle)searchTermToken);
addResult(ResultFromEntity(field));
if (!CheckVisibility(field)) break;
OnFoundResult(field);
break;
case HandleKind.PropertyDefinition:
if (row < 1 || row > module.Metadata.PropertyDefinitions.Count)
break;
var property = metadataModule.GetDefinition((PropertyDefinitionHandle)searchTermToken);
addResult(ResultFromEntity(property));
if (!CheckVisibility(property)) break;
OnFoundResult(property);
break;
case HandleKind.EventDefinition:
if (row < 1 || row > module.Metadata.EventDefinitions.Count)
break;
var @event = metadataModule.GetDefinition((EventDefinitionHandle)searchTermToken);
addResult(ResultFromEntity(@event));
if (!CheckVisibility(@event)) break;
OnFoundResult(@event);
break;
}
}

302
ILSpy/Search/SearchPane.cs

@ -17,9 +17,14 @@ @@ -17,9 +17,14 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
@ -36,9 +41,19 @@ namespace ICSharpCode.ILSpy @@ -36,9 +41,19 @@ namespace ICSharpCode.ILSpy
/// </summary>
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<SearchResult>), typeof(SearchPane),
new PropertyMetadata(new ObservableCollection<SearchResult>()));
public ObservableCollection<SearchResult> Results {
get { return (ObservableCollection<SearchResult>)GetValue(ResultsProperty); }
}
public static SearchPane Instance {
get {
if (instance == null) {
@ -61,15 +76,16 @@ namespace ICSharpCode.ILSpy @@ -61,15 +76,16 @@ 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;
MainWindow.Instance.SessionSettings.FilterSettings.PropertyChanged += FilterSettings_PropertyChanged;
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 +95,20 @@ namespace ICSharpCode.ILSpy @@ -79,7 +95,20 @@ namespace ICSharpCode.ILSpy
runSearchOnNextShow = true;
}
}
void FilterSettings_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName != nameof(FilterSettings.ShowApiLevel))
return;
if (IsVisible) {
StartSearch(this.SearchTerm);
} else {
StartSearch(null);
runSearchOnNextShow = true;
}
}
public void Show()
{
if (!IsVisible) {
@ -97,43 +126,27 @@ namespace ICSharpCode.ILSpy @@ -97,43 +126,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 +163,13 @@ namespace ICSharpCode.ILSpy @@ -150,17 +163,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 +184,7 @@ namespace ICSharpCode.ILSpy @@ -175,7 +184,7 @@ namespace ICSharpCode.ILSpy
e.Handled = true;
}
}
void SearchBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Down && listBox.HasItems) {
@ -185,26 +194,96 @@ namespace ICSharpCode.ILSpy @@ -185,26 +194,96 @@ 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,
mainWindow.SessionSettings.FilterSettings.ShowApiLevel);
currentSearch = startedSearch;
await startedSearch.Run();
}
if (currentSearch == startedSearch) { //are we still running the same search
searchProgressBar.IsIndeterminate = false;
}
}
void InsertResult(IList<SearchResult> 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<SearchResult> Results = new ObservableCollection<SearchResult>();
int resultCount;
readonly ApiVisibility apiVisibility;
public readonly IProducerConsumerCollection<SearchResult> resultQueue = new ConcurrentQueue<SearchResult>();
public RunningSearch(LoadedAssembly[] assemblies, string searchTerm, SearchMode searchMode, Language language)
public RunningSearch(LoadedAssembly[] assemblies, string searchTerm, SearchMode searchMode, Language language, ApiVisibility apiVisibility)
{
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..." });
this.apiVisibility = apiVisibility;
}
public void Cancel()
@ -212,114 +291,75 @@ namespace ICSharpCode.ILSpy @@ -212,114 +291,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)
public async Task Run()
{
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<SearchResult> results, SearchResult result)
{
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, apiVisibility, 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, apiVisibility, 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, apiVisibility, 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, apiVisibility, 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, apiVisibility, 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, apiVisibility, 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, apiVisibility, 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, apiVisibility, 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, apiVisibility, resultQueue, searchTerm[0].Substring(1));
}
switch (mode)
switch (searchMode)
{
case SearchMode.TypeAndMember:
return new MemberSearchStrategy(language, AddResult, terms);
return new MemberSearchStrategy(language, apiVisibility, resultQueue, searchTerm);
case SearchMode.Type:
return new MemberSearchStrategy(language, AddResult, terms, MemberSearchKind.Type);
return new MemberSearchStrategy(language, apiVisibility, resultQueue, searchTerm, MemberSearchKind.Type);
case SearchMode.Member:
return new MemberSearchStrategy(language, AddResult, terms, MemberSearchKind.Member);
return new MemberSearchStrategy(language, apiVisibility, resultQueue, searchTerm, MemberSearchKind.Member);
case SearchMode.Literal:
return new LiteralSearchStrategy(language, AddResult, terms);
return new LiteralSearchStrategy(language, apiVisibility, resultQueue, searchTerm);
case SearchMode.Method:
return new MemberSearchStrategy(language, AddResult, terms, MemberSearchKind.Method);
return new MemberSearchStrategy(language, apiVisibility, resultQueue, searchTerm, MemberSearchKind.Method);
case SearchMode.Field:
return new MemberSearchStrategy(language, AddResult, terms, MemberSearchKind.Field);
return new MemberSearchStrategy(language, apiVisibility, resultQueue, searchTerm, MemberSearchKind.Field);
case SearchMode.Property:
return new MemberSearchStrategy(language, AddResult, terms, MemberSearchKind.Property);
return new MemberSearchStrategy(language, apiVisibility, resultQueue, searchTerm, MemberSearchKind.Property);
case SearchMode.Event:
return new MemberSearchStrategy(language, AddResult, terms, MemberSearchKind.Event);
return new MemberSearchStrategy(language, apiVisibility, resultQueue, searchTerm, MemberSearchKind.Event);
case SearchMode.Token:
return new MetadataTokenSearchStrategy(language, AddResult, terms);
return new MetadataTokenSearchStrategy(language, apiVisibility, resultQueue, searchTerm);
}
return null;
@ -327,7 +367,7 @@ namespace ICSharpCode.ILSpy @@ -327,7 +367,7 @@ namespace ICSharpCode.ILSpy
}
}
sealed class SearchResult : IMemberTreeNode
public sealed class SearchResult : IMemberTreeNode
{
public static readonly System.Collections.Generic.IComparer<SearchResult> Comparer = new SearchResultComparer();

14
ILSpy/Search/SearchPane.xaml

@ -6,10 +6,11 @@ @@ -6,10 +6,11 @@
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto"/>
<RowDefinition Height="3" />
<RowDefinition />
</Grid.RowDefinitions>
<Border BorderThickness="0,0,0,1" BorderBrush="{DynamicResource {x:Static SystemColors.ControlLightBrushKey}}" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}">
<Border BorderBrush="{DynamicResource {x:Static SystemColors.ControlLightBrushKey}}" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" Margin="0">
<Grid Height="23">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
@ -36,8 +37,11 @@ @@ -36,8 +37,11 @@
</StackPanel>
</Grid>
</Border>
<ListView Grid.Row="1" BorderThickness="0,0,0,0" HorizontalContentAlignment="Stretch" KeyDown="ListBox_KeyDown"
MouseDoubleClick="ListBox_MouseDoubleClick" Name="listBox" SelectionMode="Single" controls:SortableGridViewColumn.SortMode="Automatic" controls:GridViewColumnAutoSize.AutoWidth="50%;50%">
<ProgressBar x:Name="searchProgressBar" Grid.Row="1" BorderThickness="0" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
<ListView Grid.Row="2" BorderThickness="0,1,0,0" HorizontalContentAlignment="Stretch" KeyDown="ListBox_KeyDown"
MouseDoubleClick="ListBox_MouseDoubleClick" Name="listBox" SelectionMode="Single" controls:SortableGridViewColumn.SortMode="Automatic"
controls:GridViewColumnAutoSize.AutoWidth="50%;50%" BorderBrush="{DynamicResource {x:Static SystemColors.ControlLightBrushKey}}"
ItemsSource="{Binding Results, ElementName=self}">
<ListView.View>
<GridView AllowsColumnReorder="False">
<controls:SortableGridViewColumn Header="Name" SortBy="Name">

Loading…
Cancel
Save