.NET Decompiler with support for PDB generation, ReadyToRun, Metadata (&more) - cross-platform!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

384 lines
13 KiB

// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.TreeNodes;
namespace ICSharpCode.ILSpy
{
/// <summary>
/// Search pane
/// </summary>
public partial class SearchPane : UserControl, IPane
{
static SearchPane instance;
RunningSearch currentSearch;
public static SearchPane Instance {
get {
if (instance == null) {
App.Current.VerifyAccess();
instance = new SearchPane();
}
return instance;
}
}
private SearchPane()
{
InitializeComponent();
searchModeComboBox.Items.Add(new { Image = Images.Library, Name = "Types and Members" });
searchModeComboBox.Items.Add(new { Image = Images.Class, Name = "Type" });
searchModeComboBox.Items.Add(new { Image = Images.Property, Name = "Member" });
searchModeComboBox.Items.Add(new { Image = Images.Method, Name = "Method" });
searchModeComboBox.Items.Add(new { Image = Images.Field, Name = "Field" });
searchModeComboBox.Items.Add(new { Image = Images.Property, Name = "Property" });
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;
}
bool runSearchOnNextShow;
void MainWindow_Instance_CurrentAssemblyListChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (IsVisible) {
StartSearch(this.SearchTerm);
} else {
StartSearch(null);
runSearchOnNextShow = true;
}
}
public void Show()
{
if (!IsVisible) {
MainWindow.Instance.ShowInTopPane("Search", this);
if (runSearchOnNextShow) {
runSearchOnNextShow = false;
StartSearch(this.SearchTerm);
}
}
Dispatcher.BeginInvoke(
DispatcherPriority.Background,
new Action(
delegate {
searchBox.Focus();
searchBox.SelectAll();
}));
}
public static readonly DependencyProperty SearchTermProperty =
DependencyProperty.Register("SearchTerm", typeof(string), typeof(SearchPane),
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)
{
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;
}
void ListBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
JumpToSelectedItem();
e.Handled = true;
}
void ListBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Return) {
e.Handled = true;
JumpToSelectedItem();
}
}
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);
if (e.Key == Key.T && e.KeyboardDevice.Modifiers == ModifierKeys.Control) {
searchModeComboBox.SelectedIndex = (int)SearchMode.Type;
e.Handled = true;
} else if (e.Key == Key.M && e.KeyboardDevice.Modifiers == ModifierKeys.Control) {
searchModeComboBox.SelectedIndex = (int)SearchMode.Member;
e.Handled = true;
} else if (e.Key == Key.S && e.KeyboardDevice.Modifiers == ModifierKeys.Control) {
searchModeComboBox.SelectedIndex = (int)SearchMode.Literal;
e.Handled = true;
}
}
void SearchBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Down && listBox.HasItems) {
e.Handled = true;
listBox.MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
listBox.SelectedIndex = 0;
}
}
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;
public RunningSearch(LoadedAssembly[] assemblies, string searchTerm, SearchMode searchMode, Language language)
{
this.dispatcher = Dispatcher.CurrentDispatcher;
this.assemblies = assemblies;
this.searchTerm = searchTerm.Split(new char[] {' '}, StringSplitOptions.RemoveEmptyEntries);
this.language = language;
this.searchMode = searchMode;
this.Results.Add(new SearchResult { Name = "Searching..." });
}
public void Cancel()
{
cts.Cancel();
}
public void Run()
{
try {
var searcher = GetSearchStrategy(searchMode, searchTerm);
foreach (var loadedAssembly in assemblies) {
var typeSystem = loadedAssembly.GetTypeSystemOrNull();
if (typeSystem == null)
continue;
CancellationToken cancellationToken = cts.Token;
foreach (var type in typeSystem.MainModule.TopLevelTypeDefinitions) {
cancellationToken.ThrowIfCancellationRequested();
searcher.Search(type, language, AddResult);
}
}
} 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<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;
}
}
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);
}
}
AbstractSearchStrategy GetSearchStrategy(SearchMode mode, string[] terms)
{
if (terms.Length == 1) {
if (terms[0].StartsWith("tm:", StringComparison.Ordinal))
return new TypeAndMemberSearchStrategy(terms[0].Substring(3));
if (terms[0].StartsWith("t:", StringComparison.Ordinal))
return new TypeSearchStrategy(terms[0].Substring(2));
if (terms[0].StartsWith("m:", StringComparison.Ordinal))
return new MemberSearchStrategy(terms[0].Substring(2));
if (terms[0].StartsWith("md:", StringComparison.Ordinal))
return new MemberSearchStrategy(terms[0].Substring(3), MemberSearchKind.Method);
if (terms[0].StartsWith("f:", StringComparison.Ordinal))
return new MemberSearchStrategy(terms[0].Substring(2), MemberSearchKind.Field);
if (terms[0].StartsWith("p:", StringComparison.Ordinal))
return new MemberSearchStrategy(terms[0].Substring(2), MemberSearchKind.Property);
if (terms[0].StartsWith("e:", StringComparison.Ordinal))
return new MemberSearchStrategy(terms[0].Substring(2), MemberSearchKind.Event);
if (terms[0].StartsWith("c:", StringComparison.Ordinal))
return new LiteralSearchStrategy(terms[0].Substring(2));
if (terms[0].StartsWith("@", StringComparison.Ordinal))
return new MetadataTokenSearchStrategy(terms[0].Substring(1));
}
switch (mode)
{
case SearchMode.TypeAndMember:
return new TypeAndMemberSearchStrategy(terms);
case SearchMode.Type:
return new TypeSearchStrategy(terms);
case SearchMode.Member:
return new MemberSearchStrategy(terms);
case SearchMode.Literal:
return new LiteralSearchStrategy(terms);
case SearchMode.Method:
return new MemberSearchStrategy(terms, MemberSearchKind.Method);
case SearchMode.Field:
return new MemberSearchStrategy(terms, MemberSearchKind.Field);
case SearchMode.Property:
return new MemberSearchStrategy(terms, MemberSearchKind.Property);
case SearchMode.Event:
return new MemberSearchStrategy(terms, MemberSearchKind.Event);
case SearchMode.Token:
return new MetadataTokenSearchStrategy(terms);
}
return null;
}
}
}
sealed class SearchResult : IMemberTreeNode
{
public static readonly System.Collections.Generic.IComparer<SearchResult> Comparer = new SearchResultComparer();
public IEntity Member { get; set; }
public float Fitness { get; set; }
public string Location { get; set; }
public string Name { get; set; }
public ImageSource Image { get; set; }
public ImageSource LocationImage { get; set; }
public override string ToString()
{
return Name;
}
class SearchResultComparer : System.Collections.Generic.IComparer<SearchResult>
{
public int Compare(SearchResult x, SearchResult y)
{
return StringComparer.Ordinal.Compare(x?.Name ?? "", y?.Name ?? "");
}
}
}
[ExportMainMenuCommand(Menu = "_View", Header = "Search...", MenuIcon = "Images/Find.png", MenuCategory = "View", MenuOrder = 100)]
[ExportToolbarCommand(ToolTip = "Search (Ctrl+Shift+F or Ctrl+E)", ToolbarIcon = "Images/Find.png", ToolbarCategory = "View", ToolbarOrder = 100)]
sealed class ShowSearchCommand : CommandWrapper
{
public ShowSearchCommand()
: base(NavigationCommands.Search)
{
NavigationCommands.Search.InputGestures.Clear();
NavigationCommands.Search.InputGestures.Add(new KeyGesture(Key.F, ModifierKeys.Control | ModifierKeys.Shift));
NavigationCommands.Search.InputGestures.Add(new KeyGesture(Key.E, ModifierKeys.Control));
}
}
public enum SearchMode
{
TypeAndMember,
Type,
Member,
Method,
Field,
Property,
Event,
Literal,
Token
}
}