13 changed files with 362 additions and 21 deletions
@ -0,0 +1,133 @@
@@ -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 @@
@@ -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 @@
@@ -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