From 5fdeb223dde11dee57fac29e31dca37352af573d Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Wed, 24 Jul 2019 08:59:33 +0200 Subject: [PATCH] "Save Code" should only work with ILSpyTreeNodes. --- ILSpy/Commands/SaveCodeContextMenuEntry.cs | 105 +++++++++++++++++++-- ILSpy/MainWindow.xaml.cs | 77 +-------------- 2 files changed, 101 insertions(+), 81 deletions(-) diff --git a/ILSpy/Commands/SaveCodeContextMenuEntry.cs b/ILSpy/Commands/SaveCodeContextMenuEntry.cs index 85e5e301f..8ee945f0d 100644 --- a/ILSpy/Commands/SaveCodeContextMenuEntry.cs +++ b/ILSpy/Commands/SaveCodeContextMenuEntry.cs @@ -16,25 +16,118 @@ // 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.Generic; +using System.IO; +using System.Linq; +using System.Windows; using System.Windows.Input; using ICSharpCode.ILSpy.Properties; +using ICSharpCode.ILSpy.TreeNodes; +using ICSharpCode.TreeView; +using Microsoft.Win32; namespace ICSharpCode.ILSpy.TextView { [ExportContextMenuEntry(Header = nameof(Resources._SaveCode), Category = nameof(Resources.Save), Icon = "Images/SaveFile.png")] sealed class SaveCodeContextMenuEntry : IContextMenuEntry { - private readonly ICommand saveCommand; + public void Execute(TextViewContext context) + { + Execute(context.SelectedTreeNodes); + } + + public bool IsEnabled(TextViewContext context) => true; + + public bool IsVisible(TextViewContext context) + { + return CanExecute(context.SelectedTreeNodes); + } - public SaveCodeContextMenuEntry() + public static bool CanExecute(IReadOnlyList selectedNodes) { - saveCommand = ApplicationCommands.Save; + if (selectedNodes == null || selectedNodes.Any(n => !(n is ILSpyTreeNode))) + return false; + return selectedNodes.Count == 1 + || (selectedNodes.Count > 1 && (selectedNodes.All(n => n is AssemblyTreeNode) || selectedNodes.All(n => n is IMemberTreeNode))); } - public void Execute(TextViewContext context) => saveCommand.Execute(parameter: null); + public static void Execute(IReadOnlyList selectedNodes) + { + var currentLanguage = MainWindow.Instance.CurrentLanguage; + var textView = MainWindow.Instance.TextView; + if (selectedNodes.Count == 1 && selectedNodes[0] is ILSpyTreeNode singleSelection) { + // if there's only one treenode selected + // we will invoke the custom Save logic + if (singleSelection.Save(textView)) + return; + } else if (selectedNodes.Count > 1 && selectedNodes.All(n => n is AssemblyTreeNode)) { + var initialPath = Path.GetDirectoryName(((AssemblyTreeNode)selectedNodes[0]).LoadedAssembly.FileName); + var selectedPath = SelectSolutionFile(initialPath); - public bool IsEnabled(TextViewContext context) => true; + if (!string.IsNullOrEmpty(selectedPath)) { + var assemblies = selectedNodes.OfType() + .Select(n => n.LoadedAssembly) + .Where(a => !a.HasLoadError).ToArray(); + SolutionWriter.CreateSolution(textView, selectedPath, currentLanguage, assemblies); + } + return; + } - public bool IsVisible(TextViewContext context) => context.TreeView != null && saveCommand.CanExecute(parameter: null); + // Fallback: if nobody was able to handle the request, use default behavior. + // try to save all nodes to disk. + var options = new DecompilationOptions() { FullDecompilation = true }; + textView.SaveToDisk(currentLanguage, selectedNodes.OfType(), options); + } + + /// + /// Shows a File Selection dialog where the user can select the target file for the solution. + /// + /// The initial path to show in the dialog. If not specified, the 'Documents' directory + /// will be used. + /// + /// The full path of the selected target file, or null if the user canceled. + static string SelectSolutionFile(string path) + { + const string SolutionExtension = ".sln"; + const string DefaultSolutionName = "Solution"; + + if (string.IsNullOrWhiteSpace(path)) { + path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); + } + + SaveFileDialog dlg = new SaveFileDialog(); + dlg.InitialDirectory = path; + dlg.FileName = Path.Combine(path, DefaultSolutionName + SolutionExtension); + dlg.Filter = "Visual Studio Solution file|*" + SolutionExtension; + + bool targetInvalid; + do { + if (dlg.ShowDialog() != true) { + return null; + } + + string selectedPath = Path.GetDirectoryName(dlg.FileName); + try { + targetInvalid = Directory.EnumerateFileSystemEntries(selectedPath).Any(); + } catch (Exception e) when (e is IOException || e is UnauthorizedAccessException || e is System.Security.SecurityException) { + MessageBox.Show( + "The directory cannot be accessed. Please ensure it exists and you have sufficient rights to access it.", + "Solution directory not accessible", + MessageBoxButton.OK, MessageBoxImage.Error); + targetInvalid = true; + continue; + } + + if (targetInvalid) { + MessageBox.Show( + "The directory is not empty. Please select an empty directory.", + "Solution directory not empty", + MessageBoxButton.OK, MessageBoxImage.Warning); + } + } while (targetInvalid); + + return dlg.FileName; + } } } diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index 1fabedcd1..5732b5673 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -902,85 +902,12 @@ namespace ICSharpCode.ILSpy void SaveCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) { e.Handled = true; - var selectedNodes = SelectedNodes.ToList(); - e.CanExecute = selectedNodes.Count == 1 || (selectedNodes.Count > 1 && selectedNodes.All(n => n is AssemblyTreeNode)); + e.CanExecute = SaveCodeContextMenuEntry.CanExecute(SelectedNodes.ToList()); } void SaveCommandExecuted(object sender, ExecutedRoutedEventArgs e) { - var selectedNodes = SelectedNodes.ToList(); - if (selectedNodes.Count == 1) { - // if there's only one treenode selected - // we will invoke the custom Save logic - if (selectedNodes[0].Save(TextView)) - return; - } else if (selectedNodes.Count > 1 && selectedNodes.All(n => n is AssemblyTreeNode)) { - var initialPath = Path.GetDirectoryName(((AssemblyTreeNode)selectedNodes[0]).LoadedAssembly.FileName); - var selectedPath = SelectSolutionFile(initialPath); - - if (!string.IsNullOrEmpty(selectedPath)) { - var assemblies = selectedNodes.OfType() - .Select(n => n.LoadedAssembly) - .Where(a => !a.HasLoadError).ToArray(); - SolutionWriter.CreateSolution(TextView, selectedPath, CurrentLanguage, assemblies); - } - return; - } - - // Fallback: if nobody was able to handle the request, use default behavior. - // try to save all nodes to disk. - var options = new DecompilationOptions() { FullDecompilation = true }; - TextView.SaveToDisk(CurrentLanguage, selectedNodes, options); - } - - /// - /// Shows a File Selection dialog where the user can select the target file for the solution. - /// - /// The initial path to show in the dialog. If not specified, the 'Documents' directory - /// will be used. - /// - /// The full path of the selected target file, or null if the user canceled. - string SelectSolutionFile(string path) - { - const string SolutionExtension = ".sln"; - const string DefaultSolutionName = "Solution"; - - if (string.IsNullOrWhiteSpace(path)) { - path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); - } - - SaveFileDialog dlg = new SaveFileDialog(); - dlg.InitialDirectory = path; - dlg.FileName = Path.Combine(path, DefaultSolutionName + SolutionExtension); - dlg.Filter = "Visual Studio Solution file|*" + SolutionExtension; - - bool targetInvalid; - do { - if (dlg.ShowDialog() != true) { - return null; - } - - string selectedPath = Path.GetDirectoryName(dlg.FileName); - try { - targetInvalid = Directory.EnumerateFileSystemEntries(selectedPath).Any(); - } catch (Exception e) when (e is IOException || e is UnauthorizedAccessException || e is System.Security.SecurityException) { - MessageBox.Show( - "The directory cannot be accessed. Please ensure it exists and you have sufficient rights to access it.", - "Solution directory not accessible", - MessageBoxButton.OK, MessageBoxImage.Error); - targetInvalid = true; - continue; - } - - if (targetInvalid) { - MessageBox.Show( - "The directory is not empty. Please select an empty directory.", - "Solution directory not empty", - MessageBoxButton.OK, MessageBoxImage.Warning); - } - } while (targetInvalid); - - return dlg.FileName; + SaveCodeContextMenuEntry.Execute(SelectedNodes.ToList()); } public void RefreshDecompiledView()