From 1696028252a9f78fcd6bf21d69002ce6649328e4 Mon Sep 17 00:00:00 2001 From: OndrejPetrzilka Date: Mon, 28 Dec 2015 13:00:07 +0100 Subject: [PATCH 1/9] Added search result sorting by fitness (can be disabled in DisplaySettings, enabled by default) --- ILSpy/Options/DisplaySettings.cs | 16 ++++++++++ ILSpy/Options/DisplaySettingsPanel.xaml | 1 + ILSpy/Options/DisplaySettingsPanel.xaml.cs | 2 ++ ILSpy/SearchPane.cs | 25 ++++++++++++++- ILSpy/SearchStrategies.cs | 36 +++++++++++++++++++++- 5 files changed, 78 insertions(+), 2 deletions(-) diff --git a/ILSpy/Options/DisplaySettings.cs b/ILSpy/Options/DisplaySettings.cs index d1d703434..13e266223 100644 --- a/ILSpy/Options/DisplaySettings.cs +++ b/ILSpy/Options/DisplaySettings.cs @@ -110,6 +110,21 @@ namespace ICSharpCode.ILSpy.Options } } + bool sortResults; + + public bool SortResults + { + get { return sortResults; } + set + { + if (sortResults != value) + { + sortResults = value; + OnPropertyChanged("SortResults"); + } + } + } + public void CopyValues(DisplaySettings s) { this.SelectedFont = s.selectedFont; @@ -117,6 +132,7 @@ namespace ICSharpCode.ILSpy.Options this.ShowLineNumbers = s.showLineNumbers; this.ShowMetadataTokens = s.showMetadataTokens; this.EnableWordWrap = s.enableWordWrap; + this.SortResults = s.sortResults; } } } diff --git a/ILSpy/Options/DisplaySettingsPanel.xaml b/ILSpy/Options/DisplaySettingsPanel.xaml index d14bb0dd7..bbee08a9f 100644 --- a/ILSpy/Options/DisplaySettingsPanel.xaml +++ b/ILSpy/Options/DisplaySettingsPanel.xaml @@ -62,6 +62,7 @@ Show line numbers Show metadata tokens Enable word wrap + Sort results diff --git a/ILSpy/Options/DisplaySettingsPanel.xaml.cs b/ILSpy/Options/DisplaySettingsPanel.xaml.cs index 5ca096e7b..4dceee0e5 100644 --- a/ILSpy/Options/DisplaySettingsPanel.xaml.cs +++ b/ILSpy/Options/DisplaySettingsPanel.xaml.cs @@ -102,6 +102,7 @@ namespace ICSharpCode.ILSpy.Options s.ShowLineNumbers = (bool?)e.Attribute("ShowLineNumbers") ?? false; s.ShowMetadataTokens = (bool?) e.Attribute("ShowMetadataTokens") ?? false; s.EnableWordWrap = (bool?)e.Attribute("EnableWordWrap") ?? false; + s.SortResults = (bool?)e.Attribute("SortResults") ?? true; // Enabled by default return s; } @@ -118,6 +119,7 @@ namespace ICSharpCode.ILSpy.Options section.SetAttributeValue("ShowLineNumbers", s.ShowLineNumbers); section.SetAttributeValue("ShowMetadataTokens", s.ShowMetadataTokens); section.SetAttributeValue("EnableWordWrap", s.EnableWordWrap); + section.SetAttributeValue("SortResults", s.SortResults); XElement existingElement = root.Element("DisplaySettings"); if (existingElement != null) diff --git a/ILSpy/SearchPane.cs b/ILSpy/SearchPane.cs index 27177f7e7..37b3588e9 100644 --- a/ILSpy/SearchPane.cs +++ b/ILSpy/SearchPane.cs @@ -242,10 +242,32 @@ namespace ICSharpCode.ILSpy } dispatcher.BeginInvoke( DispatcherPriority.Normal, - new Action(delegate { this.Results.Insert(this.Results.Count - 1, result); })); + new Action(delegate { InsertResult(result); })); cts.Token.ThrowIfCancellationRequested(); } + void InsertResult(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 < this.Results.Count; i++) + { + if (this.Results[i].Fitness < result.Fitness) + { + this.Results.Insert(i, result); + break; + } + } + } + else + { + // Original code + this.Results.Insert(this.Results.Count - 1, result); + } + } + AbstractSearchStrategy GetSearchStrategy(SearchMode mode, string[] terms) { if (terms.Length == 1) { @@ -281,6 +303,7 @@ namespace ICSharpCode.ILSpy } public MemberReference Member { get; set; } + public float Fitness { get; set; } public string Location { get; set; } public string Name { get; set; } diff --git a/ILSpy/SearchStrategies.cs b/ILSpy/SearchStrategies.cs index 302f261db..519644cc8 100644 --- a/ILSpy/SearchStrategies.cs +++ b/ILSpy/SearchStrategies.cs @@ -30,6 +30,37 @@ namespace ICSharpCode.ILSpy searchTerm = terms; } + protected float GetFitness(string text) + { + // TODO: Is it possible to get fitness for regex? + if (regex != null) + return 1; + + float result = 0; + for (int i = 0; i < searchTerm.Length; ++i) { + // How to handle overlapping matches? + var term = searchTerm[i]; + switch (term[0]) + { + case '+': // must contain + term = term.Substring(1); + goto default; + case '-': // should not contain, ignore + break; + case '=': // exact match + term = term.Substring(1); + goto default; + default: + if (text.IndexOf(term, StringComparison.OrdinalIgnoreCase) >= 0) + { + result += term.Length / (float)text.Length; + } + break; + } + } + return result; + } + protected bool IsMatch(string text) { if (regex != null) @@ -93,6 +124,7 @@ namespace ICSharpCode.ILSpy addResult(new SearchResult { Member = item, + Fitness = GetFitness(item.Name), Image = image(item), Name = item.Name, LocationImage = TypeTreeNode.GetIcon(type), @@ -340,10 +372,12 @@ namespace ICSharpCode.ILSpy public override void Search(TypeDefinition type, Language language, Action addResult) { if (IsMatch(type.Name) || IsMatch(type.FullName)) { + string name = language.TypeToString(type, includeNamespace: false); addResult(new SearchResult { Member = type, + Fitness = GetFitness(name), Image = TypeTreeNode.GetIcon(type), - Name = language.TypeToString(type, includeNamespace: false), + Name = name, LocationImage = type.DeclaringType != null ? TypeTreeNode.GetIcon(type.DeclaringType) : Images.Namespace, Location = type.DeclaringType != null ? language.TypeToString(type.DeclaringType, includeNamespace: true) : type.Namespace }); From ccf6ddc89c3b601f1874e76ba7690ac0083435da Mon Sep 17 00:00:00 2001 From: OndrejPetrzilka Date: Mon, 28 Dec 2015 13:10:47 +0100 Subject: [PATCH 2/9] Sort results disabled by default --- ILSpy/Options/DisplaySettingsPanel.xaml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ILSpy/Options/DisplaySettingsPanel.xaml.cs b/ILSpy/Options/DisplaySettingsPanel.xaml.cs index 4dceee0e5..ba4763f08 100644 --- a/ILSpy/Options/DisplaySettingsPanel.xaml.cs +++ b/ILSpy/Options/DisplaySettingsPanel.xaml.cs @@ -102,7 +102,7 @@ namespace ICSharpCode.ILSpy.Options s.ShowLineNumbers = (bool?)e.Attribute("ShowLineNumbers") ?? false; s.ShowMetadataTokens = (bool?) e.Attribute("ShowMetadataTokens") ?? false; s.EnableWordWrap = (bool?)e.Attribute("EnableWordWrap") ?? false; - s.SortResults = (bool?)e.Attribute("SortResults") ?? true; // Enabled by default + s.SortResults = (bool?)e.Attribute("SortResults") ?? false; return s; } From 5261d73b9a9db7d751d5f7a1bf397abec894e342 Mon Sep 17 00:00:00 2001 From: Ondrej Petrzilka Date: Thu, 13 Oct 2016 08:09:29 +0200 Subject: [PATCH 3/9] fixed issue with missing results --- ILSpy/SearchPane.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ILSpy/SearchPane.cs b/ILSpy/SearchPane.cs index 37b3588e9..9068b304f 100644 --- a/ILSpy/SearchPane.cs +++ b/ILSpy/SearchPane.cs @@ -257,11 +257,12 @@ namespace ICSharpCode.ILSpy if (this.Results[i].Fitness < result.Fitness) { this.Results.Insert(i, result); - break; + return; } } - } - else + this.Results.Insert(this.Results.Count - 1, result); + } + else { // Original code this.Results.Insert(this.Results.Count - 1, result); From d6db2c07e8516e8f0a0ffb7c5c4fa0606c04c534 Mon Sep 17 00:00:00 2001 From: Ondrej Petrzilka Date: Thu, 13 Oct 2016 09:40:16 +0200 Subject: [PATCH 4/9] Changed way how fitness is calculated, now it's based purely on name length (ignoring generic arguments) --- ILSpy/SearchStrategies.cs | 48 ++++++++++++++------------------------- 1 file changed, 17 insertions(+), 31 deletions(-) diff --git a/ILSpy/SearchStrategies.cs b/ILSpy/SearchStrategies.cs index b171eb230..068ee1b0e 100644 --- a/ILSpy/SearchStrategies.cs +++ b/ILSpy/SearchStrategies.cs @@ -30,35 +30,21 @@ namespace ICSharpCode.ILSpy searchTerm = terms; } - protected float GetFitness(string text) - { - // TODO: Is it possible to get fitness for regex? - if (regex != null) - return 1; - - float result = 0; - for (int i = 0; i < searchTerm.Length; ++i) { - // How to handle overlapping matches? - var term = searchTerm[i]; - switch (term[0]) - { - case '+': // must contain - term = term.Substring(1); - goto default; - case '-': // should not contain, ignore - break; - case '=': // exact match - term = term.Substring(1); - goto default; - default: - if (text.IndexOf(term, StringComparison.OrdinalIgnoreCase) >= 0) - { - result += term.Length / (float)text.Length; - } - break; - } - } - return result; + protected float CalculateFitness(MemberReference member, string text) + { + // Ignore generic arguments, it not possible to search based on them either + int length = 0; + int generics = 0; + for (int i = 0; i < text.Length; i++) + { + if (text[i] == '<') + generics++; + else if (text[i] == '>') + generics--; + else if (generics == 0) + length++; + } + return 1.0f / length; } protected bool IsMatch(string text) @@ -125,7 +111,7 @@ namespace ICSharpCode.ILSpy addResult(new SearchResult { Member = item, - Fitness = GetFitness(item.Name), + Fitness = CalculateFitness(item, item.Name), Image = image(item), Name = item.Name, LocationImage = TypeTreeNode.GetIcon(type), @@ -393,7 +379,7 @@ namespace ICSharpCode.ILSpy string name = language.TypeToString(type, includeNamespace: false); addResult(new SearchResult { Member = type, - Fitness = GetFitness(name), + Fitness = CalculateFitness(type, name), Image = TypeTreeNode.GetIcon(type), Name = name, LocationImage = type.DeclaringType != null ? TypeTreeNode.GetIcon(type.DeclaringType) : Images.Namespace, From ecb0908805dc7d0e98fc4b5868a4461b9e63e325 Mon Sep 17 00:00:00 2001 From: Ondrej Petrzilka Date: Thu, 13 Oct 2016 09:43:20 +0200 Subject: [PATCH 5/9] Options, Sort results, better description --- ILSpy/Options/DisplaySettingsPanel.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ILSpy/Options/DisplaySettingsPanel.xaml b/ILSpy/Options/DisplaySettingsPanel.xaml index bbee08a9f..5a56bafbd 100644 --- a/ILSpy/Options/DisplaySettingsPanel.xaml +++ b/ILSpy/Options/DisplaySettingsPanel.xaml @@ -62,7 +62,7 @@ Show line numbers Show metadata tokens Enable word wrap - Sort results + Sort results by fitness From 8605454cba005040c7994e20c6f8a8ae22fd065d Mon Sep 17 00:00:00 2001 From: Ondrej Petrzilka Date: Thu, 13 Oct 2016 09:49:55 +0200 Subject: [PATCH 6/9] Used tabs instead of spaces --- ILSpy/SearchPane.cs | 6 +++--- ILSpy/SearchStrategies.cs | 26 +++++++++++++------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/ILSpy/SearchPane.cs b/ILSpy/SearchPane.cs index 2be51bc21..4fb1900e1 100644 --- a/ILSpy/SearchPane.cs +++ b/ILSpy/SearchPane.cs @@ -268,9 +268,9 @@ namespace ICSharpCode.ILSpy this.Results.Insert(this.Results.Count - 1, result); } else - { - // Original code - this.Results.Insert(this.Results.Count - 1, result); + { + // Original code + this.Results.Insert(this.Results.Count - 1, result); } } diff --git a/ILSpy/SearchStrategies.cs b/ILSpy/SearchStrategies.cs index 068ee1b0e..ef7570849 100644 --- a/ILSpy/SearchStrategies.cs +++ b/ILSpy/SearchStrategies.cs @@ -32,19 +32,19 @@ namespace ICSharpCode.ILSpy protected float CalculateFitness(MemberReference member, string text) { - // Ignore generic arguments, it not possible to search based on them either - int length = 0; - int generics = 0; - for (int i = 0; i < text.Length; i++) - { - if (text[i] == '<') - generics++; - else if (text[i] == '>') - generics--; - else if (generics == 0) - length++; - } - return 1.0f / length; + // Ignore generic arguments, it not possible to search based on them either + int length = 0; + int generics = 0; + for (int i = 0; i < text.Length; i++) + { + if (text[i] == '<') + generics++; + else if (text[i] == '>') + generics--; + else if (generics == 0) + length++; + } + return 1.0f / length; } protected bool IsMatch(string text) From 4c0a92727f92bb64debd89c1276afe9a94cc4446 Mon Sep 17 00:00:00 2001 From: Ondrej Petrzilka Date: Thu, 13 Oct 2016 10:01:35 +0200 Subject: [PATCH 7/9] Used tabs instead of spaces --- ILSpy/SearchPane.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ILSpy/SearchPane.cs b/ILSpy/SearchPane.cs index 4fb1900e1..787652db1 100644 --- a/ILSpy/SearchPane.cs +++ b/ILSpy/SearchPane.cs @@ -104,7 +104,7 @@ namespace ICSharpCode.ILSpy 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); } @@ -266,11 +266,11 @@ namespace ICSharpCode.ILSpy } } this.Results.Insert(this.Results.Count - 1, result); - } - else - { - // Original code - this.Results.Insert(this.Results.Count - 1, result); + } + else + { + // Original code + this.Results.Insert(this.Results.Count - 1, result); } } From 1cdcd95cb73503bea8c31f4fe4ee68760b1de069 Mon Sep 17 00:00:00 2001 From: Ondrej Petrzilka Date: Fri, 14 Oct 2016 14:44:07 +0200 Subject: [PATCH 8/9] Fixed missing fitness calculation when searching for types and members --- ILSpy/SearchStrategies.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ILSpy/SearchStrategies.cs b/ILSpy/SearchStrategies.cs index ef7570849..d8340deb3 100644 --- a/ILSpy/SearchStrategies.cs +++ b/ILSpy/SearchStrategies.cs @@ -404,11 +404,13 @@ namespace ICSharpCode.ILSpy { if (IsMatch(type.Name) || IsMatch(type.FullName)) { + string name = language.TypeToString(type, includeNamespace: false); addResult(new SearchResult { Member = type, Image = TypeTreeNode.GetIcon(type), - Name = language.TypeToString(type, includeNamespace: false), + Fitness = CalculateFitness(type, name), + Name = name, LocationImage = type.DeclaringType != null ? TypeTreeNode.GetIcon(type.DeclaringType) : Images.Namespace, Location = type.DeclaringType != null ? language.TypeToString(type.DeclaringType, includeNamespace: true) : type.Namespace }); From b66d9f436ec67adb46a5a61441111a66f1048990 Mon Sep 17 00:00:00 2001 From: Ondrej Petrzilka Date: Fri, 14 Oct 2016 14:44:50 +0200 Subject: [PATCH 9/9] Fitness for compiler generated types is now zero (show them last) --- ILSpy/SearchStrategies.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ILSpy/SearchStrategies.cs b/ILSpy/SearchStrategies.cs index d8340deb3..3556cbae3 100644 --- a/ILSpy/SearchStrategies.cs +++ b/ILSpy/SearchStrategies.cs @@ -32,6 +32,12 @@ namespace ICSharpCode.ILSpy protected float CalculateFitness(MemberReference member, string text) { + // Probably compiler generated types without meaningful names, show them last + if (text.StartsWith("<")) + { + return 0; + } + // Ignore generic arguments, it not possible to search based on them either int length = 0; int generics = 0;