13 changed files with 362 additions and 21 deletions
@ -0,0 +1,133 @@ |
|||||||
|
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||||
|
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.ComponentModel; |
||||||
|
using System.Linq; |
||||||
|
using System.Threading; |
||||||
|
using System.Threading.Tasks; |
||||||
|
|
||||||
|
using CSharpBinding.Parser; |
||||||
|
using ICSharpCode.AvalonEdit.Highlighting; |
||||||
|
using ICSharpCode.Core; |
||||||
|
using ICSharpCode.NRefactory; |
||||||
|
using ICSharpCode.NRefactory.Editor; |
||||||
|
using ICSharpCode.SharpDevelop; |
||||||
|
using ICSharpCode.SharpDevelop.Editor.Search; |
||||||
|
using ICSharpCode.SharpDevelop.Project; |
||||||
|
|
||||||
|
namespace CSharpBinding.Refactoring |
||||||
|
{ |
||||||
|
public class SearchForIssuesCommand : SimpleCommand |
||||||
|
{ |
||||||
|
public override void Execute(object parameter) |
||||||
|
{ |
||||||
|
SearchForIssuesDialog dlg = new SearchForIssuesDialog(); |
||||||
|
dlg.Owner = SD.Workbench.MainWindow; |
||||||
|
if (dlg.ShowDialog() == true) { |
||||||
|
var providers = dlg.SelectedProviders.ToList(); |
||||||
|
var fileNames = GetFilesToSearch(dlg.Target).ToList(); |
||||||
|
var monitor = SD.StatusBar.CreateProgressMonitor(); |
||||||
|
var observable = ReactiveExtensions.CreateObservable<SearchedFile>( |
||||||
|
(m, c) => SearchForIssuesAsync(fileNames, providers, c, m), |
||||||
|
monitor); |
||||||
|
SearchResultsPad.Instance.ShowSearchResults("Issue Search", observable); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
IEnumerable<FileName> GetFilesToSearch(SearchForIssuesTarget target) |
||||||
|
{ |
||||||
|
SD.MainThread.VerifyAccess(); |
||||||
|
switch (target) { |
||||||
|
case SearchForIssuesTarget.CurrentDocument: |
||||||
|
if (SD.Workbench.ActiveViewContent != null) { |
||||||
|
FileName fileName = SD.Workbench.ActiveViewContent.PrimaryFileName; |
||||||
|
if (fileName != null) |
||||||
|
return new[] { fileName }; |
||||||
|
} |
||||||
|
break; |
||||||
|
case SearchForIssuesTarget.WholeProject: |
||||||
|
return GetFilesFromProject(ProjectService.CurrentProject); |
||||||
|
case SearchForIssuesTarget.WholeSolution: |
||||||
|
if (ProjectService.OpenSolution != null) { |
||||||
|
return ProjectService.OpenSolution.Projects.SelectMany(GetFilesFromProject).Distinct(); |
||||||
|
} |
||||||
|
break; |
||||||
|
default: |
||||||
|
throw new Exception("Invalid value for SearchForIssuesTarget"); |
||||||
|
} |
||||||
|
return Enumerable.Empty<FileName>(); |
||||||
|
} |
||||||
|
|
||||||
|
IEnumerable<FileName> GetFilesFromProject(IProject project) |
||||||
|
{ |
||||||
|
if (project == null) |
||||||
|
return Enumerable.Empty<FileName>(); |
||||||
|
return from item in project.GetItemsOfType(ItemType.Compile) |
||||||
|
where item.FileName.EndsWith(".cs", StringComparison.OrdinalIgnoreCase) |
||||||
|
select FileName.Create(item.FileName); |
||||||
|
} |
||||||
|
|
||||||
|
Task SearchForIssuesAsync(List<FileName> fileNames, IEnumerable<IssueManager.IssueProvider> providers, Action<SearchedFile> callback, IProgressMonitor monitor) |
||||||
|
{ |
||||||
|
return Task.Run(() => SearchForIssues(fileNames, providers, callback, monitor)); |
||||||
|
} |
||||||
|
|
||||||
|
void SearchForIssues(List<FileName> fileNames, IEnumerable<IssueManager.IssueProvider> providers, Action<SearchedFile> callback, IProgressMonitor monitor) |
||||||
|
{ |
||||||
|
ParseableFileContentFinder contentFinder = new ParseableFileContentFinder(); |
||||||
|
int filesProcessed = 0; |
||||||
|
Parallel.ForEach( |
||||||
|
fileNames, |
||||||
|
delegate (FileName fileName) { |
||||||
|
var fileContent = contentFinder.Create(fileName); |
||||||
|
var resultForFile = SearchForIssues(fileName, fileContent, providers, monitor.CancellationToken); |
||||||
|
if (resultForFile != null) { |
||||||
|
callback(resultForFile); |
||||||
|
} |
||||||
|
monitor.Progress = (double)Interlocked.Increment(ref filesProcessed) / fileNames.Count; |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
SearchedFile SearchForIssues(FileName fileName, ITextSource fileContent, IEnumerable<IssueManager.IssueProvider> providers, CancellationToken cancellationToken) |
||||||
|
{ |
||||||
|
cancellationToken.ThrowIfCancellationRequested(); |
||||||
|
var parseInfo = SD.ParserService.Parse(fileName, fileContent, cancellationToken: cancellationToken) as CSharpFullParseInformation; |
||||||
|
if (parseInfo == null) |
||||||
|
return null; |
||||||
|
var compilation = SD.ParserService.GetCompilationForFile(fileName); |
||||||
|
var resolver = parseInfo.GetResolver(compilation); |
||||||
|
var context = new SDRefactoringContext(fileContent, resolver, new TextLocation(0, 0), 0, 0, cancellationToken); |
||||||
|
ReadOnlyDocument document = null; |
||||||
|
IHighlighter highlighter = null; |
||||||
|
var results = new List<SearchResultMatch>(); |
||||||
|
foreach (var provider in providers) { |
||||||
|
cancellationToken.ThrowIfCancellationRequested(); |
||||||
|
foreach (var issue in provider.GetIssues(context)) { |
||||||
|
if (document == null) { |
||||||
|
document = new ReadOnlyDocument(fileContent, fileName); |
||||||
|
highlighter = SD.EditorControlService.CreateHighlighter(document); |
||||||
|
} |
||||||
|
results.Add(SearchResultMatch.Create(document, issue.Start, issue.End, highlighter)); |
||||||
|
} |
||||||
|
} |
||||||
|
if (results.Count > 0) |
||||||
|
return new SearchedFile(fileName, results); |
||||||
|
else |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public enum SearchForIssuesTarget |
||||||
|
{ |
||||||
|
[Description("${res:Dialog.NewProject.SearchReplace.LookIn.CurrentDocument}")] |
||||||
|
CurrentDocument, |
||||||
|
//[Description("${res:Dialog.NewProject.SearchReplace.LookIn.AllOpenDocuments}")]
|
||||||
|
//AllOpenFiles,
|
||||||
|
[Description("${res:Dialog.NewProject.SearchReplace.LookIn.WholeProject}")] |
||||||
|
WholeProject, |
||||||
|
[Description("${res:Dialog.NewProject.SearchReplace.LookIn.WholeSolution}")] |
||||||
|
WholeSolution |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,38 @@ |
|||||||
|
<Window x:Class="CSharpBinding.Refactoring.SearchForIssuesDialog" |
||||||
|
x:ClassModifier="internal" |
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||||
|
xmlns:tv="http://icsharpcode.net/sharpdevelop/treeview" |
||||||
|
xmlns:core="http://icsharpcode.net/sharpdevelop/core" |
||||||
|
xmlns:widgets="http://icsharpcode.net/sharpdevelop/widgets" |
||||||
|
xmlns:gui="clr-namespace:ICSharpCode.SharpDevelop.Gui;assembly=ICSharpCode.SharpDevelop" |
||||||
|
xmlns:local="clr-namespace:CSharpBinding.Refactoring" |
||||||
|
Style="{x:Static core:GlobalStyles.DialogWindowStyle}" |
||||||
|
WindowStartupLocation="CenterOwner" |
||||||
|
MinHeight="250" MinWidth="200" |
||||||
|
Height="450" Width="400" Title="Search For Issues"> |
||||||
|
<Grid Margin="6"> |
||||||
|
<Grid.RowDefinitions> |
||||||
|
<RowDefinition Height="Auto" /> |
||||||
|
<RowDefinition Height="Auto" /> |
||||||
|
<RowDefinition Height="*" /> |
||||||
|
<RowDefinition Height="Auto" /> |
||||||
|
<RowDefinition Height="Auto" /> |
||||||
|
</Grid.RowDefinitions> |
||||||
|
<Grid.ColumnDefinitions> |
||||||
|
<ColumnDefinition Width="*" /> |
||||||
|
</Grid.ColumnDefinitions> |
||||||
|
<StackPanel Orientation="Horizontal"> |
||||||
|
<Label Content="Search In:" /> |
||||||
|
<widgets:RadioButtonGroup Name="searchInRBG" Margin="0,6,0,0" gui:EnumBinding.EnumType="local:SearchForIssuesTarget" /> |
||||||
|
</StackPanel> |
||||||
|
<Label Content="Issues:" Grid.Row="1" HorizontalAlignment="Left" /> |
||||||
|
<tv:SharpTreeView Name="treeView" Grid.Row="2" Margin="4,0,4,4" /> |
||||||
|
<CheckBox Name="fixCheckBox" Grid.Row="3" Margin="8,0,0,0" Checked="FixCheckBox_Checked" Unchecked="FixCheckBox_Unchecked" |
||||||
|
HorizontalAlignment="Left" Content="Automatically fix issues if possible" /> |
||||||
|
<StackPanel Grid.Row="4" Margin="4" HorizontalAlignment="Right" Orientation="Horizontal"> |
||||||
|
<Button Content="Search" Style="{x:Static core:GlobalStyles.ButtonStyle}" Name="searchButton" IsDefault="True" Click="searchButton_Click" Margin="0,0,5,0" /> |
||||||
|
<Button Content="{core:Localize Global.CloseButtonText}" Style="{x:Static core:GlobalStyles.ButtonStyle}" IsCancel="True" /> |
||||||
|
</StackPanel> |
||||||
|
</Grid> |
||||||
|
</Window> |
@ -0,0 +1,128 @@ |
|||||||
|
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||||
|
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Linq; |
||||||
|
using System.Text; |
||||||
|
using System.Windows; |
||||||
|
using System.Windows.Controls; |
||||||
|
using System.Windows.Data; |
||||||
|
using System.Windows.Documents; |
||||||
|
using System.Windows.Input; |
||||||
|
using System.Windows.Media; |
||||||
|
using ICSharpCode.NRefactory.CSharp; |
||||||
|
using ICSharpCode.TreeView; |
||||||
|
|
||||||
|
namespace CSharpBinding.Refactoring |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for SearchForIssues.xaml
|
||||||
|
/// </summary>
|
||||||
|
internal partial class SearchForIssuesDialog : Window |
||||||
|
{ |
||||||
|
public SearchForIssuesDialog() |
||||||
|
{ |
||||||
|
InitializeComponent(); |
||||||
|
FixCheckBox_Unchecked(null, null); |
||||||
|
treeView.Root = new RootTreeNode(IssueManager.IssueProviders); |
||||||
|
} |
||||||
|
|
||||||
|
public SearchForIssuesTarget Target { |
||||||
|
get { |
||||||
|
return (SearchForIssuesTarget)searchInRBG.SelectedValue; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public IEnumerable<IssueManager.IssueProvider> SelectedProviders { |
||||||
|
get { |
||||||
|
return treeView.Root.Descendants().OfType<IssueTreeNode>() |
||||||
|
.Where(n => n.IsChecked == true).Select(n => n.Provider); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public bool FixIssues { |
||||||
|
get { |
||||||
|
return fixCheckBox.IsChecked == true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void searchButton_Click(object sender, RoutedEventArgs e) |
||||||
|
{ |
||||||
|
DialogResult = true; |
||||||
|
Close(); |
||||||
|
} |
||||||
|
|
||||||
|
void FixCheckBox_Unchecked(object sender, RoutedEventArgs e) |
||||||
|
{ |
||||||
|
searchButton.Content = "Search"; |
||||||
|
} |
||||||
|
|
||||||
|
void FixCheckBox_Checked(object sender, RoutedEventArgs e) |
||||||
|
{ |
||||||
|
searchButton.Content = "Search and Fix"; |
||||||
|
} |
||||||
|
|
||||||
|
sealed class RootTreeNode : SharpTreeNode |
||||||
|
{ |
||||||
|
internal RootTreeNode(IEnumerable<IssueManager.IssueProvider> providers) |
||||||
|
{ |
||||||
|
this.Children.AddRange(providers.GroupBy(p => p.Attribute.Category, (key, g) => new CategoryTreeNode(key, g))); |
||||||
|
this.IsChecked = false; |
||||||
|
this.IsExpanded = true; |
||||||
|
} |
||||||
|
|
||||||
|
public override object Text { |
||||||
|
get { return "C# Issues"; } |
||||||
|
} |
||||||
|
|
||||||
|
public override bool IsCheckable { |
||||||
|
get { return true; } |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
sealed class CategoryTreeNode : SharpTreeNode |
||||||
|
{ |
||||||
|
readonly string categoryName; |
||||||
|
|
||||||
|
internal CategoryTreeNode(string categoryName, IEnumerable<IssueManager.IssueProvider> providers) |
||||||
|
{ |
||||||
|
this.categoryName = categoryName; |
||||||
|
this.Children.AddRange(providers.Select(p => new IssueTreeNode(p))); |
||||||
|
this.IsExpanded = true; |
||||||
|
} |
||||||
|
|
||||||
|
public override object Text { |
||||||
|
get { return categoryName; } |
||||||
|
} |
||||||
|
|
||||||
|
public override bool IsCheckable { |
||||||
|
get { return true; } |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
sealed class IssueTreeNode : SharpTreeNode |
||||||
|
{ |
||||||
|
internal readonly IssueManager.IssueProvider Provider; |
||||||
|
readonly IssueDescriptionAttribute attribute; |
||||||
|
|
||||||
|
internal IssueTreeNode(IssueManager.IssueProvider provider) |
||||||
|
{ |
||||||
|
this.Provider = provider; |
||||||
|
this.attribute = provider.Attribute; |
||||||
|
} |
||||||
|
|
||||||
|
public override bool IsCheckable { |
||||||
|
get { return true; } |
||||||
|
} |
||||||
|
|
||||||
|
public override object Text { |
||||||
|
get { return attribute.Title; } |
||||||
|
} |
||||||
|
|
||||||
|
public override object ToolTip { |
||||||
|
get { return attribute.Description; } |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue