From 1f965d8583cb6230ffc48ef562549b998481247b Mon Sep 17 00:00:00 2001 From: Filippo Ferrario <102259289+ferrariofilippo@users.noreply.github.com> Date: Wed, 22 Jan 2025 17:26:30 +0100 Subject: [PATCH 01/12] Feature: Add a setting to change the default IDE --- src/Files.App/Actions/Open/OpenInIDEAction.cs | 54 +++++++++ .../Actions/Open/OpenInVSCodeAction.cs | 44 ------- .../Actions/Open/OpenRepoInIDEAction.cs | 51 ++++++++ .../Actions/Open/OpenRepoInVSCodeAction.cs | 45 ------- .../Data/Commands/Manager/CommandCodes.cs | 4 +- .../Data/Commands/Manager/CommandManager.cs | 8 +- .../Contracts/IDevToolsSettingsService.cs | 13 +- .../Helpers/Environment/SoftwareHelpers.cs | 80 ------------- .../Settings/DevToolsSettingsService.cs | 14 +++ src/Files.App/Strings/en-US/Resources.resw | 25 ++-- .../ViewModels/Settings/DevToolsViewModel.cs | 63 ++++++++++ .../Views/Settings/DevToolsPage.xaml | 111 ++++++++++++++++++ 12 files changed, 326 insertions(+), 186 deletions(-) create mode 100644 src/Files.App/Actions/Open/OpenInIDEAction.cs delete mode 100644 src/Files.App/Actions/Open/OpenInVSCodeAction.cs create mode 100644 src/Files.App/Actions/Open/OpenRepoInIDEAction.cs delete mode 100644 src/Files.App/Actions/Open/OpenRepoInVSCodeAction.cs delete mode 100644 src/Files.App/Helpers/Environment/SoftwareHelpers.cs diff --git a/src/Files.App/Actions/Open/OpenInIDEAction.cs b/src/Files.App/Actions/Open/OpenInIDEAction.cs new file mode 100644 index 000000000000..724750ceddf4 --- /dev/null +++ b/src/Files.App/Actions/Open/OpenInIDEAction.cs @@ -0,0 +1,54 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Actions +{ + internal sealed class OpenInIDEAction : ObservableObject, IAction + { + private readonly IDevToolsSettingsService _devToolsSettingsService; + + private readonly IContentPageContext _context; + + public string Label + => string.Format( + "OpenInIDE".GetLocalizedResource(), + _devToolsSettingsService.FriendlyIDEName); + + public string Description + => string.Format( + "OpenInIDEDescription".GetLocalizedResource(), + _devToolsSettingsService.FriendlyIDEName); + + public bool IsExecutable => + _context.Folder is not null && + !string.IsNullOrWhiteSpace(_devToolsSettingsService.IDEPath); + + public OpenInIDEAction() + { + _devToolsSettingsService = Ioc.Default.GetRequiredService(); + _context = Ioc.Default.GetRequiredService(); + _context.PropertyChanged += Context_PropertyChanged; + _devToolsSettingsService.PropertyChanged += DevSettings_PropertyChanged; + } + + public Task ExecuteAsync(object? parameter = null) + { + return Win32Helper.RunPowershellCommandAsync( + $"& \'{_devToolsSettingsService.IDEPath}\' \'{_context.ShellPage?.ShellViewModel.WorkingDirectory}\'", + PowerShellExecutionOptions.Hidden + ); + } + + private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(IContentPageContext.Folder)) + OnPropertyChanged(nameof(IsExecutable)); + } + + private void DevSettings_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(IDevToolsSettingsService.IDEPath)) + OnPropertyChanged(nameof(IsExecutable)); + } + } +} diff --git a/src/Files.App/Actions/Open/OpenInVSCodeAction.cs b/src/Files.App/Actions/Open/OpenInVSCodeAction.cs deleted file mode 100644 index ffb26efd988d..000000000000 --- a/src/Files.App/Actions/Open/OpenInVSCodeAction.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) Files Community -// Licensed under the MIT License. - -using Files.App.Utils.Shell; - -namespace Files.App.Actions -{ - internal sealed partial class OpenInVSCodeAction : ObservableObject, IAction - { - private readonly IContentPageContext _context; - - private readonly bool _isVSCodeInstalled; - - public string Label - => "OpenInVSCode".GetLocalizedResource(); - - public string Description - => "OpenInVSCodeDescription".GetLocalizedResource(); - - public bool IsExecutable => - _isVSCodeInstalled && - _context.Folder is not null; - - public OpenInVSCodeAction() - { - _context = Ioc.Default.GetRequiredService(); - - _isVSCodeInstalled = SoftwareHelpers.IsVSCodeInstalled(); - if (_isVSCodeInstalled) - _context.PropertyChanged += Context_PropertyChanged; - } - - public Task ExecuteAsync(object? parameter = null) - { - return Win32Helper.RunPowershellCommandAsync($"code \'{_context.ShellPage?.ShellViewModel.WorkingDirectory}\'", PowerShellExecutionOptions.Hidden); - } - - private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) - { - if (e.PropertyName == nameof(IContentPageContext.Folder)) - OnPropertyChanged(nameof(IsExecutable)); - } - } -} diff --git a/src/Files.App/Actions/Open/OpenRepoInIDEAction.cs b/src/Files.App/Actions/Open/OpenRepoInIDEAction.cs new file mode 100644 index 000000000000..8306eb3e7092 --- /dev/null +++ b/src/Files.App/Actions/Open/OpenRepoInIDEAction.cs @@ -0,0 +1,51 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Actions +{ + internal sealed class OpenRepoInIDEAction : ObservableObject, IAction + { + private readonly IDevToolsSettingsService _devToolsSettingsService; + + private readonly IContentPageContext _context; + + public string Label + => string.Format("OpenRepoInIDE".GetLocalizedResource(), _devToolsSettingsService.FriendlyIDEName); + + public string Description + => string.Format("OpenRepoInIDEDescription".GetLocalizedResource(), _devToolsSettingsService.FriendlyIDEName); + + public bool IsExecutable => + _context.Folder is not null && + _context.ShellPage!.InstanceViewModel.IsGitRepository && + !string.IsNullOrWhiteSpace(_devToolsSettingsService.IDEPath); + + public OpenRepoInIDEAction() + { + _context = Ioc.Default.GetRequiredService(); + _devToolsSettingsService = Ioc.Default.GetRequiredService(); + _context.PropertyChanged += Context_PropertyChanged; + _devToolsSettingsService.PropertyChanged += DevSettings_PropertyChanged; + } + + public Task ExecuteAsync(object? parameter = null) + { + return Win32Helper.RunPowershellCommandAsync( + $"& \'{_devToolsSettingsService.IDEPath}\' \'{_context.ShellPage!.InstanceViewModel.GitRepositoryPath}\'", + PowerShellExecutionOptions.Hidden + ); + } + + private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(IContentPageContext.Folder)) + OnPropertyChanged(nameof(IsExecutable)); + } + + private void DevSettings_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(IDevToolsSettingsService.IDEPath)) + OnPropertyChanged(nameof(IsExecutable)); + } + } +} diff --git a/src/Files.App/Actions/Open/OpenRepoInVSCodeAction.cs b/src/Files.App/Actions/Open/OpenRepoInVSCodeAction.cs deleted file mode 100644 index abe1e14a5399..000000000000 --- a/src/Files.App/Actions/Open/OpenRepoInVSCodeAction.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) Files Community -// Licensed under the MIT License. - -using Files.App.Utils.Shell; - -namespace Files.App.Actions -{ - internal sealed partial class OpenRepoInVSCodeAction : ObservableObject, IAction - { - private readonly IContentPageContext _context; - - private readonly bool _isVSCodeInstalled; - - public string Label - => "OpenRepoInVSCode".GetLocalizedResource(); - - public string Description - => "OpenRepoInVSCodeDescription".GetLocalizedResource(); - - public bool IsExecutable => - _isVSCodeInstalled && - _context.Folder is not null && - _context.ShellPage!.InstanceViewModel.IsGitRepository; - - public OpenRepoInVSCodeAction() - { - _context = Ioc.Default.GetRequiredService(); - - _isVSCodeInstalled = SoftwareHelpers.IsVSCodeInstalled(); - if (_isVSCodeInstalled) - _context.PropertyChanged += Context_PropertyChanged; - } - - public Task ExecuteAsync(object? parameter = null) - { - return Win32Helper.RunPowershellCommandAsync($"code \'{_context.ShellPage!.InstanceViewModel.GitRepositoryPath}\'", PowerShellExecutionOptions.Hidden); - } - - private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) - { - if (e.PropertyName == nameof(IContentPageContext.Folder)) - OnPropertyChanged(nameof(IsExecutable)); - } - } -} diff --git a/src/Files.App/Data/Commands/Manager/CommandCodes.cs b/src/Files.App/Data/Commands/Manager/CommandCodes.cs index 19c2158a0a29..b00898587aaf 100644 --- a/src/Files.App/Data/Commands/Manager/CommandCodes.cs +++ b/src/Files.App/Data/Commands/Manager/CommandCodes.cs @@ -112,8 +112,8 @@ public enum CommandCodes RotateRight, // Open - OpenInVSCode, - OpenRepoInVSCode, + OpenInIDE, + OpenRepoInIDE, OpenProperties, OpenReleaseNotes, OpenClassicProperties, diff --git a/src/Files.App/Data/Commands/Manager/CommandManager.cs b/src/Files.App/Data/Commands/Manager/CommandManager.cs index f0e656b11943..6dc09ae4fa3f 100644 --- a/src/Files.App/Data/Commands/Manager/CommandManager.cs +++ b/src/Files.App/Data/Commands/Manager/CommandManager.cs @@ -113,8 +113,8 @@ public IRichCommand this[HotKey hotKey] public IRichCommand OpenItem => commands[CommandCodes.OpenItem]; public IRichCommand OpenItemWithApplicationPicker => commands[CommandCodes.OpenItemWithApplicationPicker]; public IRichCommand OpenParentFolder => commands[CommandCodes.OpenParentFolder]; - public IRichCommand OpenInVSCode => commands[CommandCodes.OpenInVSCode]; - public IRichCommand OpenRepoInVSCode => commands[CommandCodes.OpenRepoInVSCode]; + public IRichCommand OpenInVSCode => commands[CommandCodes.OpenInIDE]; + public IRichCommand OpenRepoInVSCode => commands[CommandCodes.OpenRepoInIDE]; public IRichCommand OpenProperties => commands[CommandCodes.OpenProperties]; public IRichCommand OpenReleaseNotes => commands[CommandCodes.OpenReleaseNotes]; public IRichCommand OpenClassicProperties => commands[CommandCodes.OpenClassicProperties]; @@ -317,8 +317,8 @@ public IEnumerator GetEnumerator() => [CommandCodes.OpenItem] = new OpenItemAction(), [CommandCodes.OpenItemWithApplicationPicker] = new OpenItemWithApplicationPickerAction(), [CommandCodes.OpenParentFolder] = new OpenParentFolderAction(), - [CommandCodes.OpenInVSCode] = new OpenInVSCodeAction(), - [CommandCodes.OpenRepoInVSCode] = new OpenRepoInVSCodeAction(), + [CommandCodes.OpenInIDE] = new OpenInIDEAction(), + [CommandCodes.OpenRepoInIDE] = new OpenRepoInIDEAction(), [CommandCodes.OpenProperties] = new OpenPropertiesAction(), [CommandCodes.OpenReleaseNotes] = new OpenReleaseNotesAction(), [CommandCodes.OpenClassicProperties] = new OpenClassicPropertiesAction(), diff --git a/src/Files.App/Data/Contracts/IDevToolsSettingsService.cs b/src/Files.App/Data/Contracts/IDevToolsSettingsService.cs index 113ff6732888..9b28ad70be22 100644 --- a/src/Files.App/Data/Contracts/IDevToolsSettingsService.cs +++ b/src/Files.App/Data/Contracts/IDevToolsSettingsService.cs @@ -1,9 +1,6 @@ // Copyright (c) Files Community // Licensed under the MIT License. -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Media; - namespace Files.App.Data.Contracts { public interface IDevToolsSettingsService : IBaseSettingsService, INotifyPropertyChanged @@ -12,5 +9,15 @@ public interface IDevToolsSettingsService : IBaseSettingsService, INotifyPropert /// Gets or sets a value when the Open in IDE button should be displayed on the status bar. /// OpenInIDEOption OpenInIDEOption { get; set; } + + /// + /// Gets or sets the path of the chosen IDE. + /// + string IDEPath { get; set; } + + /// + /// Gets or sets the friendly name of the chosen IDE. + /// + string FriendlyIDEName { get; set; } } } diff --git a/src/Files.App/Helpers/Environment/SoftwareHelpers.cs b/src/Files.App/Helpers/Environment/SoftwareHelpers.cs deleted file mode 100644 index 4b674564ba89..000000000000 --- a/src/Files.App/Helpers/Environment/SoftwareHelpers.cs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) Files Community -// Licensed under the MIT License. - -using Microsoft.Win32; -using System.Security; - -namespace Files.App.Helpers -{ - internal static class SoftwareHelpers - { - private const string UninstallRegistryKey = @"Software\Microsoft\Windows\CurrentVersion\Uninstall"; - private const string VsRegistryKey = @"SOFTWARE\Microsoft\VisualStudio"; - - private const string VsCodeName = "Microsoft Visual Studio Code"; - - - public static bool IsVSCodeInstalled() - { - try - { - return - ContainsName(Registry.CurrentUser.OpenSubKey(UninstallRegistryKey), VsCodeName) || - ContainsName(Registry.LocalMachine.OpenSubKey(UninstallRegistryKey), VsCodeName); - } - catch (SecurityException) - { - // Handle edge case where OpenSubKey results in SecurityException - return false; - } - } - - public static bool IsVSInstalled() - { - try - { - var key = Registry.LocalMachine.OpenSubKey(VsRegistryKey); - if (key is null) - return false; - - key.Close(); - - return true; - } - catch (SecurityException) - { - // Handle edge case where OpenSubKey results in SecurityException - return false; - } - } - - private static bool ContainsName(RegistryKey? key, string find) - { - if (key is null) - return false; - - try - { - foreach (var subKey in key.GetSubKeyNames().Select(key.OpenSubKey)) - { - var displayName = subKey?.GetValue("DisplayName") as string; - if (!string.IsNullOrWhiteSpace(displayName) && displayName.StartsWith(find)) - { - key.Close(); - - return true; - } - } - - key.Close(); - - return false; - } - catch (SecurityException) - { - // Handle edge case where OpenSubKey results in SecurityException - return false; - } - } - } -} diff --git a/src/Files.App/Services/Settings/DevToolsSettingsService.cs b/src/Files.App/Services/Settings/DevToolsSettingsService.cs index 7d2fb980f29d..eb1fbc79372d 100644 --- a/src/Files.App/Services/Settings/DevToolsSettingsService.cs +++ b/src/Files.App/Services/Settings/DevToolsSettingsService.cs @@ -18,6 +18,20 @@ public OpenInIDEOption OpenInIDEOption set => Set(value); } + /// + public string IDEPath + { + get => Get("") ?? ""; + set => Set(value); + } + + /// + public string FriendlyIDEName + { + get => Get("") ?? ""; + set => Set(value); + } + protected override void RaiseOnSettingChangedEvent(object sender, SettingChangedEventArgs e) { base.RaiseOnSettingChangedEvent(sender, e); diff --git a/src/Files.App/Strings/en-US/Resources.resw b/src/Files.App/Strings/en-US/Resources.resw index 119345bacf36..b4225bc52f35 100644 --- a/src/Files.App/Strings/en-US/Resources.resw +++ b/src/Files.App/Strings/en-US/Resources.resw @@ -3214,17 +3214,17 @@ Files cannot access GitHub right now. - - Open folder in VS Code + + Open folder in {0} - - Open the current directory in Visual Studio Code + + Open the current directory in {0} - - Open repo in VS Code + + Open repo in {0} - - Open the root of the Git repo in Visual Studio Code + + Open the root of the Git repo in {0} Copy code @@ -4043,4 +4043,13 @@ Add to shelf Tooltip that displays when dragging items to the Shelf Pane + + Default IDE + + + IDE path or alias + + + e.g. "code" or "C:\...\code.exe" + \ No newline at end of file diff --git a/src/Files.App/ViewModels/Settings/DevToolsViewModel.cs b/src/Files.App/ViewModels/Settings/DevToolsViewModel.cs index cc80c13b490d..9fbb02769d34 100644 --- a/src/Files.App/ViewModels/Settings/DevToolsViewModel.cs +++ b/src/Files.App/ViewModels/Settings/DevToolsViewModel.cs @@ -13,6 +13,10 @@ public sealed partial class DevToolsViewModel : ObservableObject public Dictionary OpenInIDEOptions { get; private set; } = []; public ICommand RemoveCredentialsCommand { get; } public ICommand ConnectToGitHubCommand { get; } + public ICommand StartEditingIDECommand { get; } + public ICommand CancelIDEChangesCommand { get; } + public ICommand SaveIDEChangesCommand { get; } + public ICommand OpenFilePickerForIDECommand { get; } // Enabled when there are saved credentials private bool _IsLogoutEnabled; @@ -22,6 +26,34 @@ public bool IsLogoutEnabled set => SetProperty(ref _IsLogoutEnabled, value); } + private bool _IsEditingIDEConfig; + public bool IsEditingIDEConfig + { + get => _IsEditingIDEConfig; + set => SetProperty(ref _IsEditingIDEConfig, value); + } + + private bool _CanSaveIDEChanges; + public bool CanSaveIDEChanges + { + get => _CanSaveIDEChanges; + set => SetProperty(ref _CanSaveIDEChanges, value); + } + + private string _IDEPath; + public string IDEPath + { + get => _IDEPath; + set => SetProperty(ref _IDEPath, value); + } + + private string _IDEFriendlyName; + public string IDEFriendlyName + { + get => _IDEFriendlyName; + set => SetProperty(ref _IDEFriendlyName, value); + } + public DevToolsViewModel() { // Open in IDE options @@ -29,10 +61,17 @@ public DevToolsViewModel() OpenInIDEOptions.Add(OpenInIDEOption.AllLocations, "AllLocations".GetLocalizedResource()); SelectedOpenInIDEOption = OpenInIDEOptions[DevToolsSettingsService.OpenInIDEOption]; + IDEPath = DevToolsSettingsService.IDEPath; + IDEFriendlyName = DevToolsSettingsService.FriendlyIDEName; + IsLogoutEnabled = GitHelpers.GetSavedCredentials() != string.Empty; RemoveCredentialsCommand = new RelayCommand(DoRemoveCredentials); ConnectToGitHubCommand = new RelayCommand(DoConnectToGitHubAsync); + CancelIDEChangesCommand = new RelayCommand(DoCancelIDEChanges); + SaveIDEChangesCommand = new RelayCommand(DoSaveIDEChanges); + StartEditingIDECommand = new RelayCommand(DoStartEditingIDE); + OpenFilePickerForIDECommand = new RelayCommand(DoOpenFilePickerForIDE); } private string selectedOpenInIDEOption; @@ -62,5 +101,29 @@ public async void DoConnectToGitHubAsync() await GitHelpers.RequireGitAuthenticationAsync(); } + + private void DoCancelIDEChanges() + { + IsEditingIDEConfig = false; + IDEPath = DevToolsSettingsService.IDEPath; + IDEFriendlyName = DevToolsSettingsService.FriendlyIDEName; + } + + private void DoSaveIDEChanges() + { + IsEditingIDEConfig = false; + DevToolsSettingsService.IDEPath = IDEPath; + DevToolsSettingsService.FriendlyIDEName = IDEFriendlyName; + } + + private void DoStartEditingIDE() + { + IsEditingIDEConfig = true; + } + + private void DoOpenFilePickerForIDE() + { + + } } } diff --git a/src/Files.App/Views/Settings/DevToolsPage.xaml b/src/Files.App/Views/Settings/DevToolsPage.xaml index 6a98a5ebb51e..5d340d6dbc3b 100644 --- a/src/Files.App/Views/Settings/DevToolsPage.xaml +++ b/src/Files.App/Views/Settings/DevToolsPage.xaml @@ -11,6 +11,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:uc="using:Files.App.UserControls" xmlns:vm="using:Files.App.ViewModels.Settings" + xmlns:wctconverters="using:CommunityToolkit.WinUI.UI.Converters" mc:Ignorable="d"> @@ -20,6 +21,14 @@ + + @@ -49,6 +58,108 @@ AutomationProperties.Name="{helpers:ResourceString Name=DisplayOpenIDE}" ItemsSource="{x:Bind ViewModel.OpenInIDEOptions.Values}" SelectedItem="{x:Bind ViewModel.SelectedOpenInIDEOption, Mode=TwoWay}" /> + + + + + + + + + + + + + + + + +