diff --git a/README.md b/README.md index aa21ee55..99928589 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,6 @@ You can download the latest stable from [Releases](https://github.com/sourcegit- For **Windows** users: * **MSYS Git is NOT supported**. Please use official [Git for Windows](https://git-scm.com/download/win) instead. -* You can use `Visual Studio Code Insiders` as the same way as `Visual Studio Code` in this software. For **macOS** users: @@ -54,10 +53,17 @@ For **Linux** users: * Maybe you need to set environment variable `AVALONIA_SCREEN_SCALE_FACTORS`. See https://github.com/AvaloniaUI/Avalonia/wiki/Configuring-X11-per-monitor-DPI. * Modify `SourceGit.desktop.template` (replace SOURCEGIT_LOCAL_FOLDER with real path) and move it into `~/.local/share/applications`. -Other tips: +## External Editors -* You can set `VSCODE_PATH` environment variable if VSCode can NOT be found when you click `Open In Visual Studio Code`. -* You can set `FLEET_PATH` environment variable if JetBrains Fleet can NOT be found when you click `Open In Fleet`. +This app supports open repository in external editors listed in the table below. + +| Editor | Windows | macOS | Linux | Environment Variable | +| --- | --- | --- | --- | --- | +| Visual Studio Code | YES | YES | YES | VSCODE_PATH | +| Visual Studio Code - Insiders | YES | YES | YES | VSCODE_INSIDERS_PATH | +| JetBrains Fleet | YES | YES | YES | FLEET_PATH | + +You can set the given environment variable for special editor if it can NOT be found by this app automatically. ## Screen Shots diff --git a/src/Commands/Command.cs b/src/Commands/Command.cs index 8667346e..c30a3d45 100644 --- a/src/Commands/Command.cs +++ b/src/Commands/Command.cs @@ -32,7 +32,7 @@ namespace SourceGit.Commands public bool Exec() { var start = new ProcessStartInfo(); - start.FileName = Native.OS.GitInstallPath; + start.FileName = Native.OS.GitExecutable; start.Arguments = "--no-pager -c core.quotepath=off " + Args; start.UseShellExecute = false; start.CreateNoWindow = true; @@ -144,7 +144,7 @@ namespace SourceGit.Commands public ReadToEndResult ReadToEnd() { var start = new ProcessStartInfo(); - start.FileName = Native.OS.GitInstallPath; + start.FileName = Native.OS.GitExecutable; start.Arguments = "--no-pager -c core.quotepath=off " + Args; start.UseShellExecute = false; start.CreateNoWindow = true; diff --git a/src/Commands/GetImageFileAsBitmap.cs b/src/Commands/GetImageFileAsBitmap.cs index e145d67f..981b33ba 100644 --- a/src/Commands/GetImageFileAsBitmap.cs +++ b/src/Commands/GetImageFileAsBitmap.cs @@ -12,7 +12,7 @@ namespace SourceGit.Commands { var starter = new ProcessStartInfo(); starter.WorkingDirectory = repo; - starter.FileName = Native.OS.GitInstallPath; + starter.FileName = Native.OS.GitExecutable; starter.Arguments = $"show {revision}:\"{file}\""; starter.UseShellExecute = false; starter.CreateNoWindow = true; diff --git a/src/Commands/SaveChangesAsPatch.cs b/src/Commands/SaveChangesAsPatch.cs index f15cc2f2..409127ba 100644 --- a/src/Commands/SaveChangesAsPatch.cs +++ b/src/Commands/SaveChangesAsPatch.cs @@ -27,7 +27,7 @@ namespace SourceGit.Commands { var starter = new ProcessStartInfo(); starter.WorkingDirectory = repo; - starter.FileName = Native.OS.GitInstallPath; + starter.FileName = Native.OS.GitExecutable; starter.Arguments = $"diff --ignore-cr-at-eol --unified=4 {opt}"; starter.UseShellExecute = false; starter.CreateNoWindow = true; diff --git a/src/Commands/SaveRevisionFile.cs b/src/Commands/SaveRevisionFile.cs index 41575efc..6c200940 100644 --- a/src/Commands/SaveRevisionFile.cs +++ b/src/Commands/SaveRevisionFile.cs @@ -30,7 +30,7 @@ namespace SourceGit.Commands { var starter = new ProcessStartInfo(); starter.WorkingDirectory = repo; - starter.FileName = Native.OS.GitInstallPath; + starter.FileName = Native.OS.GitExecutable; starter.Arguments = args; starter.UseShellExecute = false; starter.CreateNoWindow = true; diff --git a/src/Models/ExternalEditor.cs b/src/Models/ExternalEditor.cs new file mode 100644 index 00000000..8ae16132 --- /dev/null +++ b/src/Models/ExternalEditor.cs @@ -0,0 +1,24 @@ +using System; +using System.Diagnostics; + +namespace SourceGit.Models +{ + public class ExternalEditor + { + public string Name { get; set; } = string.Empty; + public Uri Icon { get; set; } = null; + public string Executable { get; set; } = string.Empty; + public string OpenCmdArgs { get; set; } = string.Empty; + + public void Open(string repo) + { + Process.Start(new ProcessStartInfo() + { + WorkingDirectory = repo, + FileName = Executable, + Arguments = string.Format(OpenCmdArgs, repo), + UseShellExecute = false, + }); + } + } +} diff --git a/src/Models/ExternalMergeTools.cs b/src/Models/ExternalMergeTools.cs index 1871faf2..4baab460 100644 --- a/src/Models/ExternalMergeTools.cs +++ b/src/Models/ExternalMergeTools.cs @@ -20,12 +20,13 @@ namespace SourceGit.Models { Supported = new List() { new ExternalMergeTools(0, "Custom", "", "", ""), - new ExternalMergeTools(1, "Visual Studio Code", "Code.exe;Code - Insiders.exe", "-n --wait \"$MERGED\"", "-n --wait --diff \"$LOCAL\" \"$REMOTE\""), - new ExternalMergeTools(2, "Visual Studio 2017/2019", "vsDiffMerge.exe", "\"$REMOTE\" \"$LOCAL\" \"$BASE\" \"$MERGED\" /m", "\"$LOCAL\" \"$REMOTE\""), - new ExternalMergeTools(3, "Tortoise Merge", "TortoiseMerge.exe;TortoiseGitMerge.exe", "-base:\"$BASE\" -theirs:\"$REMOTE\" -mine:\"$LOCAL\" -merged:\"$MERGED\"", "-base:\"$LOCAL\" -theirs:\"$REMOTE\""), - new ExternalMergeTools(4, "KDiff3", "kdiff3.exe", "\"$REMOTE\" -b \"$BASE\" \"$LOCAL\" -o \"$MERGED\"", "\"$LOCAL\" \"$REMOTE\""), - new ExternalMergeTools(5, "Beyond Compare 4", "BComp.exe", "\"$REMOTE\" \"$LOCAL\" \"$BASE\" \"$MERGED\"", "\"$LOCAL\" \"$REMOTE\""), - new ExternalMergeTools(6, "WinMerge", "WinMergeU.exe", "-u -e \"$REMOTE\" \"$LOCAL\" \"$MERGED\"", "-u -e \"$LOCAL\" \"$REMOTE\""), + new ExternalMergeTools(1, "Visual Studio Code", "Code.exe", "-n --wait \"$MERGED\"", "-n --wait --diff \"$LOCAL\" \"$REMOTE\""), + new ExternalMergeTools(2, "Visual Studio Code - Insiders", "Code - Insiders.exe", "-n --wait \"$MERGED\"", "-n --wait --diff \"$LOCAL\" \"$REMOTE\""), + new ExternalMergeTools(3, "Visual Studio 2017/2019", "vsDiffMerge.exe", "\"$REMOTE\" \"$LOCAL\" \"$BASE\" \"$MERGED\" /m", "\"$LOCAL\" \"$REMOTE\""), + new ExternalMergeTools(4, "Tortoise Merge", "TortoiseMerge.exe;TortoiseGitMerge.exe", "-base:\"$BASE\" -theirs:\"$REMOTE\" -mine:\"$LOCAL\" -merged:\"$MERGED\"", "-base:\"$LOCAL\" -theirs:\"$REMOTE\""), + new ExternalMergeTools(5, "KDiff3", "kdiff3.exe", "\"$REMOTE\" -b \"$BASE\" \"$LOCAL\" -o \"$MERGED\"", "\"$LOCAL\" \"$REMOTE\""), + new ExternalMergeTools(6, "Beyond Compare 4", "BComp.exe", "\"$REMOTE\" \"$LOCAL\" \"$BASE\" \"$MERGED\"", "\"$LOCAL\" \"$REMOTE\""), + new ExternalMergeTools(7, "WinMerge", "WinMergeU.exe", "-u -e \"$REMOTE\" \"$LOCAL\" \"$MERGED\"", "-u -e \"$LOCAL\" \"$REMOTE\""), }; } else if (OperatingSystem.IsMacOS()) @@ -34,8 +35,9 @@ namespace SourceGit.Models new ExternalMergeTools(0, "Custom", "", "", ""), new ExternalMergeTools(1, "FileMerge", "/usr/bin/opendiff", "\"$BASE\" \"$LOCAL\" \"$REMOTE\" -ancestor \"$MERGED\"", "\"$LOCAL\" \"$REMOTE\""), new ExternalMergeTools(2, "Visual Studio Code", "/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code", "-n --wait \"$MERGED\"", "-n --wait --diff \"$LOCAL\" \"$REMOTE\""), - new ExternalMergeTools(3, "KDiff3", "/Applications/kdiff3.app/Contents/MacOS/kdiff3", "\"$REMOTE\" -b \"$BASE\" \"$LOCAL\" -o \"$MERGED\"", "\"$LOCAL\" \"$REMOTE\""), - new ExternalMergeTools(4, "Beyond Compare 4", "/Applications/Beyond Compare.app/Contents/MacOS/bcomp", "\"$REMOTE\" \"$LOCAL\" \"$BASE\" \"$MERGED\"", "\"$LOCAL\" \"$REMOTE\""), + new ExternalMergeTools(3, "Visual Studio Code - Insiders", "/Applications/Visual Studio Code - Insiders.app/Contents/Resources/app/bin/code", "-n --wait \"$MERGED\"", "-n --wait --diff \"$LOCAL\" \"$REMOTE\""), + new ExternalMergeTools(4, "KDiff3", "/Applications/kdiff3.app/Contents/MacOS/kdiff3", "\"$REMOTE\" -b \"$BASE\" \"$LOCAL\" -o \"$MERGED\"", "\"$LOCAL\" \"$REMOTE\""), + new ExternalMergeTools(5, "Beyond Compare 4", "/Applications/Beyond Compare.app/Contents/MacOS/bcomp", "\"$REMOTE\" \"$LOCAL\" \"$BASE\" \"$MERGED\"", "\"$LOCAL\" \"$REMOTE\""), }; } else if (OperatingSystem.IsLinux()) @@ -43,8 +45,9 @@ namespace SourceGit.Models Supported = new List() { new ExternalMergeTools(0, "Custom", "", "", ""), new ExternalMergeTools(1, "Visual Studio Code", "/usr/share/code/code", "-n --wait \"$MERGED\"", "-n --wait --diff \"$LOCAL\" \"$REMOTE\""), - new ExternalMergeTools(2, "KDiff3", "/usr/bin/kdiff3", "\"$REMOTE\" -b \"$BASE\" \"$LOCAL\" -o \"$MERGED\"", "\"$LOCAL\" \"$REMOTE\""), - new ExternalMergeTools(3, "Beyond Compare 4", "/usr/bin/bcomp", "\"$REMOTE\" \"$LOCAL\" \"$BASE\" \"$MERGED\"", "\"$LOCAL\" \"$REMOTE\""), + new ExternalMergeTools(2, "Visual Studio Code - Insiders", "/usr/share/code-insiders/code-insiders", "-n --wait \"$MERGED\"", "-n --wait --diff \"$LOCAL\" \"$REMOTE\""), + new ExternalMergeTools(3, "KDiff3", "/usr/bin/kdiff3", "\"$REMOTE\" -b \"$BASE\" \"$LOCAL\" -o \"$MERGED\"", "\"$LOCAL\" \"$REMOTE\""), + new ExternalMergeTools(4, "Beyond Compare 4", "/usr/bin/bcomp", "\"$REMOTE\" \"$LOCAL\" \"$BASE\" \"$MERGED\"", "\"$LOCAL\" \"$REMOTE\""), }; } else diff --git a/src/Native/Linux.cs b/src/Native/Linux.cs index 9c358cb4..5b7c02e8 100644 --- a/src/Native/Linux.cs +++ b/src/Native/Linux.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Runtime.Versioning; @@ -30,20 +31,47 @@ namespace SourceGit.Native return string.Empty; } - public string FindVSCode() + public List FindExternalEditors() { - var toolPath = "/usr/share/code/code"; - if (File.Exists(toolPath)) - return toolPath; - return string.Empty; - } + var editors = new List(); - public string FindFleet() - { - var toolPath = $"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}/.local/share/JetBrains/Toolbox/apps/fleet/bin/Fleet"; - if (File.Exists(toolPath)) - return toolPath; - return string.Empty; + var vscode = FindVSCode(); + if (!string.IsNullOrEmpty(vscode) && File.Exists(vscode)) + { + editors.Add(new Models.ExternalEditor + { + Name = "Visual Studio Code", + Icon = new Uri("avares://SourceGit/Resources/ExternalToolIcons/vscode.png", UriKind.Absolute), + Executable = vscode, + OpenCmdArgs = "\"{0}\"", + }); + } + + var vscodeInsiders = FindVSCodeInsiders(); + if (!string.IsNullOrEmpty(vscodeInsiders) && File.Exists(vscodeInsiders)) + { + editors.Add(new Models.ExternalEditor + { + Name = "Visual Studio Code - Insiders", + Icon = new Uri("avares://SourceGit/Resources/ExternalToolIcons/vscode_insiders.png", UriKind.Absolute), + Executable = vscodeInsiders, + OpenCmdArgs = "\"{0}\"", + }); + } + + var fleet = FindFleet(); + if (!string.IsNullOrEmpty(fleet) && File.Exists(fleet)) + { + editors.Add(new Models.ExternalEditor + { + Name = "JetBrains Fleet", + Icon = new Uri("avares://SourceGit/Resources/ExternalToolIcons/fleet.png", UriKind.Absolute), + Executable = fleet, + OpenCmdArgs = "\"{0}\"", + }); + } + + return editors; } public void OpenBrowser(string url) @@ -119,5 +147,46 @@ namespace SourceGit.Native proc.Close(); } + + #region EXTERNAL_EDITORS_FINDER + private string FindVSCode() + { + var toolPath = "/usr/share/code/code"; + if (File.Exists(toolPath)) + return toolPath; + + var customPath = Environment.GetEnvironmentVariable("VSCODE_PATH"); + if (!string.IsNullOrEmpty(customPath)) + return customPath; + + return string.Empty; + } + + private string FindVSCodeInsiders() + { + var toolPath = "/usr/share/code/code"; + if (File.Exists(toolPath)) + return toolPath; + + var customPath = Environment.GetEnvironmentVariable("VSCODE_INSIDERS_PATH"); + if (!string.IsNullOrEmpty(customPath)) + return customPath; + + return string.Empty; + } + + private string FindFleet() + { + var toolPath = $"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}/.local/share/JetBrains/Toolbox/apps/fleet/bin/Fleet"; + if (File.Exists(toolPath)) + return toolPath; + + var customPath = Environment.GetEnvironmentVariable("FLEET_PATH"); + if (!string.IsNullOrEmpty(customPath)) + return customPath; + + return string.Empty; + } + #endregion } } diff --git a/src/Native/MacOS.cs b/src/Native/MacOS.cs index e19ad328..bc632ca1 100644 --- a/src/Native/MacOS.cs +++ b/src/Native/MacOS.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Runtime.Versioning; @@ -27,20 +28,47 @@ namespace SourceGit.Native return string.Empty; } - public string FindVSCode() + public List FindExternalEditors() { - var toolPath = "/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code"; - if (File.Exists(toolPath)) - return toolPath; - return string.Empty; - } + var editors = new List(); - public string FindFleet() - { - var toolPath = $"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}/Applications/Fleet.app/Contents/MacOS/Fleet"; - if (File.Exists(toolPath)) - return toolPath; - return string.Empty; + var vscode = FindVSCode(); + if (!string.IsNullOrEmpty(vscode) && File.Exists(vscode)) + { + editors.Add(new Models.ExternalEditor + { + Name = "Visual Studio Code", + Icon = new Uri("avares://SourceGit/Resources/ExternalToolIcons/vscode.png", UriKind.Absolute), + Executable = vscode, + OpenCmdArgs = "\"{0}\"", + }); + } + + var vscodeInsiders = FindVSCodeInsiders(); + if (!string.IsNullOrEmpty(vscodeInsiders) && File.Exists(vscodeInsiders)) + { + editors.Add(new Models.ExternalEditor + { + Name = "Visual Studio Code - Insiders", + Icon = new Uri("avares://SourceGit/Resources/ExternalToolIcons/vscode_insiders.png", UriKind.Absolute), + Executable = vscodeInsiders, + OpenCmdArgs = "\"{0}\"", + }); + } + + var fleet = FindFleet(); + if (!string.IsNullOrEmpty(fleet) && File.Exists(fleet)) + { + editors.Add(new Models.ExternalEditor + { + Name = "JetBrains Fleet", + Icon = new Uri("avares://SourceGit/Resources/ExternalToolIcons/fleet.png", UriKind.Absolute), + Executable = fleet, + OpenCmdArgs = "\"{0}\"", + }); + } + + return editors; } public void OpenBrowser(string url) @@ -82,5 +110,46 @@ namespace SourceGit.Native { Process.Start("open", file); } + + #region EXTERNAL_EDITORS_FINDER + private string FindVSCode() + { + var toolPath = "/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code"; + if (File.Exists(toolPath)) + return toolPath; + + var customPath = Environment.GetEnvironmentVariable("VSCODE_PATH"); + if (!string.IsNullOrEmpty(customPath)) + return customPath; + + return string.Empty; + } + + private string FindVSCodeInsiders() + { + var toolPath = "/Applications/Visual Studio Code - Insiders.app/Contents/Resources/app/bin/code"; + if (File.Exists(toolPath)) + return toolPath; + + var customPath = Environment.GetEnvironmentVariable("VSCODE_INSIDERS_PATH"); + if (!string.IsNullOrEmpty(customPath)) + return customPath; + + return string.Empty; + } + + private string FindFleet() + { + var toolPath = $"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}/Applications/Fleet.app/Contents/MacOS/Fleet"; + if (File.Exists(toolPath)) + return toolPath; + + var customPath = Environment.GetEnvironmentVariable("FLEET_PATH"); + if (!string.IsNullOrEmpty(customPath)) + return customPath; + + return string.Empty; + } + #endregion } } diff --git a/src/Native/OS.cs b/src/Native/OS.cs index 004cfc62..61a33ed6 100644 --- a/src/Native/OS.cs +++ b/src/Native/OS.cs @@ -1,6 +1,5 @@ using System; -using System.Diagnostics; -using System.IO; +using System.Collections.Generic; using Avalonia; @@ -13,8 +12,7 @@ namespace SourceGit.Native void SetupApp(AppBuilder builder); string FindGitExecutable(); - string FindVSCode(); - string FindFleet(); + List FindExternalEditors(); void OpenTerminal(string workdir); void OpenInFileManager(string path, bool select); @@ -22,11 +20,8 @@ namespace SourceGit.Native void OpenWithDefaultEditor(string file); } - public static string GitInstallPath { get; set; } - - public static string VSCodeExecutableFile { get; set; } - - public static string FleetExecutableFile { get; set; } + public static string GitExecutable { get; set; } = string.Empty; + public static List ExternalEditors { get; set; } = new List(); static OS() { @@ -47,13 +42,7 @@ namespace SourceGit.Native throw new Exception("Platform unsupported!!!"); } - VSCodeExecutableFile = _backend.FindVSCode(); - if (string.IsNullOrEmpty(VSCodeExecutableFile)) - VSCodeExecutableFile = GetPathFromEnvironmentVar("VSCODE_PATH"); - - FleetExecutableFile = _backend.FindFleet(); - if (string.IsNullOrEmpty(FleetExecutableFile)) - FleetExecutableFile = GetPathFromEnvironmentVar("FLEET_PATH"); + ExternalEditors = _backend.FindExternalEditors(); } public static void SetupApp(AppBuilder builder) @@ -86,51 +75,6 @@ namespace SourceGit.Native _backend.OpenWithDefaultEditor(file); } - public static void OpenInVSCode(string repo) - { - if (string.IsNullOrEmpty(VSCodeExecutableFile)) - { - App.RaiseException(repo, "Visual Studio Code can NOT be found in your system!!!"); - return; - } - - Process.Start(new ProcessStartInfo() - { - WorkingDirectory = repo, - FileName = VSCodeExecutableFile, - Arguments = $"\"{repo}\"", - UseShellExecute = false, - }); - } - - public static void OpenInFleet(string repo) - { - if (string.IsNullOrEmpty(FleetExecutableFile)) - { - App.RaiseException(repo, "Fleet can NOT be found in your system!!!"); - return; - } - - Process.Start(new ProcessStartInfo() - { - WorkingDirectory = repo, - FileName = FleetExecutableFile, - Arguments = $"\"{repo}\"", - UseShellExecute = false, - }); - } - - private static string GetPathFromEnvironmentVar(string key) - { - var customPath = Environment.GetEnvironmentVariable(key); - if (!string.IsNullOrEmpty(customPath) && File.Exists(customPath)) - { - return customPath; - } - - return string.Empty; - } - - private static readonly IBackend _backend; + private static IBackend _backend = null; } } diff --git a/src/Native/Windows.cs b/src/Native/Windows.cs index f026852b..829f85c7 100644 --- a/src/Native/Windows.cs +++ b/src/Native/Windows.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; @@ -113,54 +114,47 @@ namespace SourceGit.Native return null; } - public string FindVSCode() + public List FindExternalEditors() { - var localMachine = Microsoft.Win32.RegistryKey.OpenBaseKey( - Microsoft.Win32.RegistryHive.LocalMachine, - Microsoft.Win32.RegistryView.Registry64); + var editors = new List(); - // VSCode (system) - var systemVScode = localMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{EA457B21-F73E-494C-ACAB-524FDE069978}_is1"); - if (systemVScode != null) + var vscode = FindVSCode(); + if (!string.IsNullOrEmpty(vscode) && File.Exists(vscode)) { - return systemVScode.GetValue("DisplayIcon") as string; + editors.Add(new Models.ExternalEditor + { + Name = "Visual Studio Code", + Icon = new Uri("avares://SourceGit/Resources/ExternalToolIcons/vscode.png", UriKind.Absolute), + Executable = vscode, + OpenCmdArgs = "\"{0}\"", + }); } - // VSCode - Insiders (system) - var systemVScodeInsiders = localMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{1287CAD5-7C8D-410D-88B9-0D1EE4A83FF2}_is1"); - if (systemVScodeInsiders != null) + var vscodeInsiders = FindVSCodeInsiders(); + if (!string.IsNullOrEmpty(vscodeInsiders) && File.Exists(vscodeInsiders)) { - return systemVScodeInsiders.GetValue("DisplayIcon") as string; + editors.Add(new Models.ExternalEditor + { + Name = "Visual Studio Code - Insiders", + Icon = new Uri("avares://SourceGit/Resources/ExternalToolIcons/vscode_insiders.png", UriKind.Absolute), + Executable = vscodeInsiders, + OpenCmdArgs = "\"{0}\"", + }); } - var currentUser = Microsoft.Win32.RegistryKey.OpenBaseKey( - Microsoft.Win32.RegistryHive.CurrentUser, - Microsoft.Win32.RegistryView.Registry64); - - // VSCode (user) - var vscode = currentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{771FD6B0-FA20-440A-A002-3B3BAC16DC50}_is1"); - if (vscode != null) + var fleet = FindFleet(); + if (!string.IsNullOrEmpty(fleet) && File.Exists(fleet)) { - return vscode.GetValue("DisplayIcon") as string; + editors.Add(new Models.ExternalEditor + { + Name = "JetBrains Fleet", + Icon = new Uri("avares://SourceGit/Resources/ExternalToolIcons/fleet.png", UriKind.Absolute), + Executable = fleet, + OpenCmdArgs = "\"{0}\"", + }); } - // VSCode - Insiders (user) - var vscodeInsiders = currentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{217B4C08-948D-4276-BFBB-BEE930AE5A2C}_is1"); - if (vscodeInsiders != null) - { - return vscodeInsiders.GetValue("DisplayIcon") as string; - } - - return string.Empty; - } - - public string FindFleet() - { - var toolPath = Environment.ExpandEnvironmentVariables($"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}\\AppData\\Local\\Programs\\Fleet\\Fleet.exe"); - if (File.Exists(toolPath)) - return toolPath; - - return string.Empty; + return editors; } public void OpenBrowser(string url) @@ -172,10 +166,11 @@ namespace SourceGit.Native public void OpenTerminal(string workdir) { - var bash = Path.Combine(Path.GetDirectoryName(OS.GitInstallPath), "bash.exe"); + var binDir = Path.GetDirectoryName(OS.GitExecutable); + var bash = Path.Combine(binDir, "bash.exe"); if (!File.Exists(bash)) { - App.RaiseException(string.IsNullOrEmpty(workdir) ? "" : workdir, $"Can NOT found bash.exe under '{Path.GetDirectoryName(OS.GitInstallPath)}'"); + App.RaiseException(string.IsNullOrEmpty(workdir) ? "" : workdir, $"Can NOT found bash.exe under '{binDir}'"); return; } @@ -227,6 +222,89 @@ namespace SourceGit.Native Process.Start(start); } + #region EXTERNAL_EDITOR_FINDER + private string FindVSCode() + { + var localMachine = Microsoft.Win32.RegistryKey.OpenBaseKey( + Microsoft.Win32.RegistryHive.LocalMachine, + Microsoft.Win32.RegistryView.Registry64); + + // VSCode (system) + var systemVScode = localMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{EA457B21-F73E-494C-ACAB-524FDE069978}_is1"); + if (systemVScode != null) + { + return systemVScode.GetValue("DisplayIcon") as string; + } + + var currentUser = Microsoft.Win32.RegistryKey.OpenBaseKey( + Microsoft.Win32.RegistryHive.CurrentUser, + Microsoft.Win32.RegistryView.Registry64); + + // VSCode (user) + var vscode = currentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{771FD6B0-FA20-440A-A002-3B3BAC16DC50}_is1"); + if (vscode != null) + { + return vscode.GetValue("DisplayIcon") as string; + } + + // ENV + var customPath = Environment.GetEnvironmentVariable("VSCODE_PATH"); + if (!string.IsNullOrEmpty(customPath)) + { + return customPath; + } + + return string.Empty; + } + + private string FindVSCodeInsiders() + { + var localMachine = Microsoft.Win32.RegistryKey.OpenBaseKey( + Microsoft.Win32.RegistryHive.LocalMachine, + Microsoft.Win32.RegistryView.Registry64); + + // VSCode - Insiders (system) + var systemVScodeInsiders = localMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{1287CAD5-7C8D-410D-88B9-0D1EE4A83FF2}_is1"); + if (systemVScodeInsiders != null) + { + return systemVScodeInsiders.GetValue("DisplayIcon") as string; + } + + var currentUser = Microsoft.Win32.RegistryKey.OpenBaseKey( + Microsoft.Win32.RegistryHive.CurrentUser, + Microsoft.Win32.RegistryView.Registry64); + + // VSCode - Insiders (user) + var vscodeInsiders = currentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{217B4C08-948D-4276-BFBB-BEE930AE5A2C}_is1"); + if (vscodeInsiders != null) + { + return vscodeInsiders.GetValue("DisplayIcon") as string; + } + + // ENV + var customPath = Environment.GetEnvironmentVariable("VSCODE_INSIDERS_PATH"); + if (!string.IsNullOrEmpty(customPath)) + { + return customPath; + } + + return string.Empty; + } + + private string FindFleet() + { + var toolPath = Environment.ExpandEnvironmentVariables($"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}\\AppData\\Local\\Programs\\Fleet\\Fleet.exe"); + if (File.Exists(toolPath)) + return toolPath; + + var customPath = Environment.GetEnvironmentVariable("FLEET_PATH"); + if (!string.IsNullOrEmpty(customPath)) + return customPath; + + return string.Empty; + } + #endregion + private void OpenFolderAndSelectFile(string folderPath) { var pidl = ILCreateFromPathW(folderPath); diff --git a/src/Resources/ExternalToolIcons/vscode_insiders.png b/src/Resources/ExternalToolIcons/vscode_insiders.png new file mode 100644 index 00000000..ee11c151 Binary files /dev/null and b/src/Resources/ExternalToolIcons/vscode_insiders.png differ diff --git a/src/Resources/Locales.Designer.cs b/src/Resources/Locales.Designer.cs index 1ac0e291..1fc9dfd0 100644 --- a/src/Resources/Locales.Designer.cs +++ b/src/Resources/Locales.Designer.cs @@ -1600,7 +1600,7 @@ namespace SourceGit.Resources { } /// - /// Looks up a localized string similar to GIT FLOW. + /// Looks up a localized string similar to Git-Flow. /// public static string Text_GitFlow { get { @@ -2985,15 +2985,6 @@ namespace SourceGit.Resources { } } - /// - /// Looks up a localized string similar to Open In Fleet. - /// - public static string Text_Repository_Fleet { - get { - return ResourceManager.GetString("Text.Repository.Fleet", resourceCulture); - } - } - /// /// Looks up a localized string similar to LOCAL BRANCHES. /// @@ -3013,7 +3004,7 @@ namespace SourceGit.Resources { } /// - /// Looks up a localized string similar to NEW BRANCH. + /// Looks up a localized string similar to Create Branch. /// public static string Text_Repository_NewBranch { get { @@ -3021,12 +3012,21 @@ namespace SourceGit.Resources { } } + /// + /// Looks up a localized string similar to Open In {0}. + /// + public static string Text_Repository_OpenIn { + get { + return ResourceManager.GetString("Text.Repository.OpenIn", resourceCulture); + } + } + /// /// Looks up a localized string similar to Open In External Tools. /// - public static string Text_Repository_OpenWith { + public static string Text_Repository_OpenWithExternalTools { get { - return ResourceManager.GetString("Text.Repository.OpenWith", resourceCulture); + return ResourceManager.GetString("Text.Repository.OpenWithExternalTools", resourceCulture); } } @@ -3147,15 +3147,6 @@ namespace SourceGit.Resources { } } - /// - /// Looks up a localized string similar to Open In Visual Studio Code. - /// - public static string Text_Repository_VSCode { - get { - return ResourceManager.GetString("Text.Repository.VSCode", resourceCulture); - } - } - /// /// Looks up a localized string similar to WORKSPACE. /// diff --git a/src/Resources/Locales.en.resx b/src/Resources/Locales.en.resx index fc8f7760..4f2b0263 100644 --- a/src/Resources/Locales.en.resx +++ b/src/Resources/Locales.en.resx @@ -339,11 +339,8 @@ Open In File Browser - - Open In Visual Studio Code - - - Open In Fleet + + Open In {0} Open In Terminal @@ -376,7 +373,7 @@ LOCAL BRANCHES - NEW BRANCH + Create Branch REMOTES @@ -409,7 +406,7 @@ ABORT - GIT FLOW + Git-Flow Initialize Git-Flow @@ -1293,7 +1290,7 @@ APPEARANCE - + Open In External Tools diff --git a/src/Resources/Locales.resx b/src/Resources/Locales.resx index 441988ec..970752ab 100644 --- a/src/Resources/Locales.resx +++ b/src/Resources/Locales.resx @@ -339,11 +339,8 @@ Open In File Browser - - Open In Visual Studio Code - - - Open In Fleet + + Open In {0} Open In Terminal @@ -376,7 +373,7 @@ LOCAL BRANCHES - NEW BRANCH + Create Branch REMOTES @@ -409,7 +406,7 @@ ABORT - GIT FLOW + Git-Flow Initialize Git-Flow @@ -1293,7 +1290,7 @@ Appearance - + Open In External Tools diff --git a/src/Resources/Locales.zh.resx b/src/Resources/Locales.zh.resx index b4f350ba..939e99db 100644 --- a/src/Resources/Locales.zh.resx +++ b/src/Resources/Locales.zh.resx @@ -339,8 +339,8 @@ 在文件浏览器中打开 - - 在 Visual Studio Code 中打开 + + 在 {0} 中打开 在终端中打开 @@ -1317,15 +1317,12 @@ 开源免费的Git客户端 - + 使用外部工具打开 • 项目源代码地址 - - 在 Fleet 中打开 - 挑选(Cherry-Pick)操作进行中。点击【终止】回滚到操作前的状态。 diff --git a/src/ViewModels/Preference.cs b/src/ViewModels/Preference.cs index d5a6f3be..8be7f9e2 100644 --- a/src/ViewModels/Preference.cs +++ b/src/ViewModels/Preference.cs @@ -189,12 +189,12 @@ namespace SourceGit.ViewModels public string GitInstallPath { - get => Native.OS.GitInstallPath; + get => Native.OS.GitExecutable; set { - if (Native.OS.GitInstallPath != value) + if (Native.OS.GitExecutable != value) { - Native.OS.GitInstallPath = value; + Native.OS.GitExecutable = value; OnPropertyChanged(nameof(GitInstallPath)); } } diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index 5ba26ca7..64b211b0 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -6,6 +6,9 @@ using System.Threading.Tasks; using Avalonia.Collections; using Avalonia.Controls; +using Avalonia.Media; +using Avalonia.Media.Imaging; +using Avalonia.Platform; using Avalonia.Threading; using CommunityToolkit.Mvvm.ComponentModel; @@ -279,21 +282,43 @@ namespace SourceGit.ViewModels Native.OS.OpenInFileManager(_fullpath); } - public void OpenInVSCode() - { - Native.OS.OpenInVSCode(_fullpath); - } - - public void OpenInFleet() - { - Native.OS.OpenInFleet(_fullpath); - } - public void OpenInTerminal() { Native.OS.OpenTerminal(_fullpath); } + public ContextMenu CreateContextMenuForExternalEditors() + { + var editors = Native.OS.ExternalEditors; + if (editors.Count == 0) + { + App.RaiseException(_fullpath, "No available external editors found!"); + return null; + } + + var menu = new ContextMenu(); + menu.Placement = PlacementMode.BottomEdgeAlignedLeft; + RenderOptions.SetBitmapInterpolationMode(menu, BitmapInterpolationMode.HighQuality); + + foreach (var editor in editors) + { + var dupEditor = editor; + var icon = AssetLoader.Open(dupEditor.Icon); + var item = new MenuItem(); + item.Header = App.Text("Repository.OpenIn", dupEditor.Name); + item.Icon = new Image { Width = 16, Height = 16, Source = new Bitmap(icon) }; + item.Click += (o, e) => + { + dupEditor.Open(_fullpath); + e.Handled = true; + }; + + menu.Items.Add(item); + } + + return menu; + } + public void Fetch() { if (!PopupHost.CanCreatePopup()) diff --git a/src/Views/Repository.axaml b/src/Views/Repository.axaml index 83fc4d20..4bfb98a6 100644 --- a/src/Views/Repository.axaml +++ b/src/Views/Repository.axaml @@ -22,22 +22,8 @@ -