feature: add powershell support for Windows

This commit is contained in:
leo 2024-04-08 17:39:52 +08:00
parent 8f70778ec2
commit 4ac705f8ca
13 changed files with 88 additions and 46 deletions

View file

@ -53,20 +53,20 @@ 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`.
## External Editors
## External Tools
This app supports open repository in external editors listed in the table below.
This app supports open repository in external tools listed in the table below.
| Editor | Windows | macOS | Linux | Environment Variable |
| Tool | 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 |
| Sublime Text | YES | YES | YES | SUBLIME_TEXT_PATH |
You can set the given environment variable for special editor if it can NOT be found by this app automatically.
You can set the given environment variable for special tool if it can NOT be found by this app automatically.
## Screen Shots
## Screenshots
* Dark Theme

View file

@ -5,7 +5,7 @@ using System.IO;
namespace SourceGit.Models
{
public class ExternalEditor
public class ExternalTool
{
public string Name { get; set; } = string.Empty;
public string Icon { get; set; } = string.Empty;
@ -24,13 +24,13 @@ namespace SourceGit.Models
}
}
public class ExternalEditorFinder
public class ExternalToolsFinder
{
public List<ExternalEditor> Editors
public List<ExternalTool> Founded
{
get;
private set;
} = new List<ExternalEditor>();
} = new List<ExternalTool>();
public void VSCode(Func<string> platform_finder)
{
@ -52,7 +52,7 @@ namespace SourceGit.Models
TryAdd("Sublime Text", "sublime_text.png", "\"{0}\"", "SUBLIME_TEXT_PATH", platform_finder);
}
private void TryAdd(string name, string icon, string args, string env, Func<string> finder)
public void TryAdd(string name, string icon, string args, string env, Func<string> finder)
{
var path = Environment.GetEnvironmentVariable(env);
if (string.IsNullOrEmpty(path) || !File.Exists(path))
@ -62,7 +62,7 @@ namespace SourceGit.Models
return;
}
Editors.Add(new ExternalEditor
Founded.Add(new ExternalTool
{
Name = name,
Icon = icon,

View file

@ -31,14 +31,14 @@ namespace SourceGit.Native
return string.Empty;
}
public List<Models.ExternalEditor> FindExternalEditors()
public List<Models.ExternalTool> FindExternalTools()
{
var finder = new Models.ExternalEditorFinder();
var finder = new Models.ExternalToolsFinder();
finder.VSCode(() => "/usr/share/code/code");
finder.VSCodeInsiders(() => "/usr/share/code-insiders/code-insiders");
finder.Fleet(() => $"{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}/JetBrains/Toolbox/apps/fleet/bin/Fleet");
finder.SublimeText(() => File.Exists("/usr/bin/subl") ? "/usr/bin/subl" : "/usr/local/bin/subl");
return finder.Editors;
return finder.Founded;
}
public void OpenBrowser(string url)

View file

@ -28,14 +28,14 @@ namespace SourceGit.Native
return string.Empty;
}
public List<Models.ExternalEditor> FindExternalEditors()
public List<Models.ExternalTool> FindExternalTools()
{
var finder = new Models.ExternalEditorFinder();
var finder = new Models.ExternalToolsFinder();
finder.VSCode(() => "/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code");
finder.VSCodeInsiders(() => "/Applications/Visual Studio Code - Insiders.app/Contents/Resources/app/bin/code");
finder.Fleet(() => $"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}/Applications/Fleet.app/Contents/MacOS/Fleet");
finder.SublimeText(() => "/Applications/Sublime Text.app/Contents/SharedSupport/bin");
return finder.Editors;
return finder.Founded;
}
public void OpenBrowser(string url)

View file

@ -12,7 +12,7 @@ namespace SourceGit.Native
void SetupApp(AppBuilder builder);
string FindGitExecutable();
List<Models.ExternalEditor> FindExternalEditors();
List<Models.ExternalTool> FindExternalTools();
void OpenTerminal(string workdir);
void OpenInFileManager(string path, bool select);
@ -21,7 +21,8 @@ namespace SourceGit.Native
}
public static string GitExecutable { get; set; } = string.Empty;
public static List<Models.ExternalEditor> ExternalEditors { get; set; } = new List<Models.ExternalEditor>();
public static bool UsePowershellOnWindows { get; set; } = false;
public static List<Models.ExternalTool> ExternalTools { get; set; } = new List<Models.ExternalTool>();
static OS()
{
@ -42,7 +43,7 @@ namespace SourceGit.Native
throw new Exception("Platform unsupported!!!");
}
ExternalEditors = _backend.FindExternalEditors();
ExternalTools = _backend.FindExternalTools();
}
public static void SetupApp(AppBuilder builder)

View file

@ -54,6 +54,16 @@ namespace SourceGit.Native
[DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = false)]
private static extern int SHOpenFolderAndSelectItems(IntPtr pidlFolder, int cild, IntPtr apidl, int dwFlags);
public Windows()
{
var localMachine = Microsoft.Win32.RegistryKey.OpenBaseKey(
Microsoft.Win32.RegistryHive.LocalMachine,
Microsoft.Win32.RegistryView.Registry64);
var pwsh = localMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\pwsh.exe");
_powershellPath = pwsh != null ? pwsh.GetValue(null) as string : "powershell";
}
public void SetupApp(AppBuilder builder)
{
builder.With(new FontManagerOptions()
@ -114,14 +124,14 @@ namespace SourceGit.Native
return null;
}
public List<Models.ExternalEditor> FindExternalEditors()
public List<Models.ExternalTool> FindExternalTools()
{
var finder = new Models.ExternalEditorFinder();
var finder = new Models.ExternalToolsFinder();
finder.VSCode(FindVSCode);
finder.VSCodeInsiders(FindVSCodeInsiders);
finder.Fleet(() => $"{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}\\Programs\\Fleet\\Fleet.exe");
finder.SublimeText(FindSublimeText);
return finder.Editors;
return finder.Founded;
}
public void OpenBrowser(string url)
@ -133,19 +143,27 @@ namespace SourceGit.Native
public void OpenTerminal(string workdir)
{
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 '{binDir}'");
return;
}
var startInfo = new ProcessStartInfo();
startInfo.UseShellExecute = true;
startInfo.FileName = bash;
var startInfo = new ProcessStartInfo() { UseShellExecute = true };
if (!string.IsNullOrEmpty(workdir) && Path.Exists(workdir))
startInfo.WorkingDirectory = workdir;
if (OS.UsePowershellOnWindows)
{
startInfo.FileName = _powershellPath;
}
else
{
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 '{binDir}'");
return;
}
startInfo.FileName = bash;
}
Process.Start(startInfo);
}
@ -281,5 +299,7 @@ namespace SourceGit.Native
ILFree(pidl);
}
}
private string _powershellPath = string.Empty;
}
}

View file

@ -268,6 +268,7 @@
<x:String x:Key="Text.Preference.Git.Path" xml:space="preserve">Install Path</x:String>
<x:String x:Key="Text.Preference.Git.User" xml:space="preserve">User Name</x:String>
<x:String x:Key="Text.Preference.Git.User.Placeholder" xml:space="preserve">Global git user name</x:String>
<x:String x:Key="Text.Preference.Git.UsePowershellOnWindows" xml:space="preserve">Use Powershell instead of Git bash</x:String>
<x:String x:Key="Text.Preference.Git.Version" xml:space="preserve">Git version</x:String>
<x:String x:Key="Text.Preference.GPG" xml:space="preserve">GPG SIGNING</x:String>
<x:String x:Key="Text.Preference.GPG.Enabled" xml:space="preserve">Commit GPG signing</x:String>

View file

@ -268,6 +268,7 @@
<x:String x:Key="Text.Preference.Git.Path" xml:space="preserve">安装路径</x:String>
<x:String x:Key="Text.Preference.Git.User" xml:space="preserve">用户名</x:String>
<x:String x:Key="Text.Preference.Git.User.Placeholder" xml:space="preserve">默认GIT用户名</x:String>
<x:String x:Key="Text.Preference.Git.UsePowershellOnWindows" xml:space="preserve">使用PowerShell替代Git Bash</x:String>
<x:String x:Key="Text.Preference.Git.Version" xml:space="preserve">Git 版本</x:String>
<x:String x:Key="Text.Preference.GPG" xml:space="preserve">GPG签名</x:String>
<x:String x:Key="Text.Preference.GPG.Enabled" xml:space="preserve">启用提交签名</x:String>

View file

@ -219,6 +219,19 @@ namespace SourceGit.ViewModels
}
}
public bool UsePowershellOnWindows
{
get => Native.OS.UsePowershellOnWindows;
set
{
if (Native.OS.UsePowershellOnWindows != value)
{
Native.OS.UsePowershellOnWindows = value;
OnPropertyChanged(nameof(UsePowershellOnWindows));
}
}
}
public int ExternalMergeToolType
{
get => _externalMergeToolType;

View file

@ -287,10 +287,10 @@ namespace SourceGit.ViewModels
Native.OS.OpenTerminal(_fullpath);
}
public ContextMenu CreateContextMenuForExternalEditors()
public ContextMenu CreateContextMenuForExternalTools()
{
var editors = Native.OS.ExternalEditors;
if (editors.Count == 0)
var tools = Native.OS.ExternalTools;
if (tools.Count == 0)
{
App.RaiseException(_fullpath, "No available external editors found!");
return null;
@ -300,16 +300,16 @@ namespace SourceGit.ViewModels
menu.Placement = PlacementMode.BottomEdgeAlignedLeft;
RenderOptions.SetBitmapInterpolationMode(menu, BitmapInterpolationMode.HighQuality);
foreach (var editor in editors)
foreach (var tool in tools)
{
var dupEditor = editor;
var icon = AssetLoader.Open(new Uri($"avares://SourceGit/Resources/ExternalToolIcons/{dupEditor.Icon}", UriKind.RelativeOrAbsolute));
var dupTool = tool;
var icon = AssetLoader.Open(new Uri($"avares://SourceGit/Resources/ExternalToolIcons/{dupTool.Icon}", UriKind.RelativeOrAbsolute));
var item = new MenuItem();
item.Header = App.Text("Repository.OpenIn", dupEditor.Name);
item.Header = App.Text("Repository.OpenIn", dupTool.Name);
item.Icon = new Image { Width = 16, Height = 16, Source = new Bitmap(icon) };
item.Click += (o, e) =>
{
dupEditor.Open(_fullpath);
dupTool.Open(_fullpath);
e.Handled = true;
};

View file

@ -231,7 +231,7 @@
<TextBlock Classes="tab_header" Text="{DynamicResource Text.Preference.Git}"/>
</TabItem.Header>
<Grid Margin="8" RowDefinitions="32,32,32,32,32,32,32" ColumnDefinitions="Auto,*">
<Grid Margin="8" RowDefinitions="32,32,32,32,32,32,Auto,32" ColumnDefinitions="Auto,*">
<TextBlock Grid.Row="0" Grid.Column="0"
Text="{DynamicResource Text.Preference.Git.Path}"
HorizontalAlignment="Right"
@ -310,6 +310,12 @@
</ComboBox>
<CheckBox Grid.Row="6" Grid.Column="1"
Height="32"
Content="{DynamicResource Text.Preference.Git.UsePowershellOnWindows}"
IsChecked="{Binding UsePowershellOnWindows, Mode=TwoWay}"
IsVisible="{OnPlatform False, Windows=True}"/>
<CheckBox Grid.Row="7" Grid.Column="1"
Content="{DynamicResource Text.Preference.Git.AutoFetch}"
IsChecked="{Binding GitAutoFetch, Mode=TwoWay}"/>
</Grid>

View file

@ -22,7 +22,7 @@
<Path Width="13" Height="13" Data="{StaticResource Icons.Terminal}"/>
</Button>
<Button Classes="icon_button" Width="32" Click="OnOpenWithExternalEditor" ToolTip.Tip="{DynamicResource Text.Repository.OpenWithExternalTools}">
<Button Classes="icon_button" Width="32" Click="OnOpenWithExternalTools" ToolTip.Tip="{DynamicResource Text.Repository.OpenWithExternalTools}">
<Path Width="13" Height="13" Data="{StaticResource Icons.OpenWith}"/>
</Button>

View file

@ -61,11 +61,11 @@ namespace SourceGit.Views
InitializeComponent();
}
private void OnOpenWithExternalEditor(object sender, RoutedEventArgs e)
private void OnOpenWithExternalTools(object sender, RoutedEventArgs e)
{
if (sender is Button button && DataContext is ViewModels.Repository repo)
{
var menu = repo.CreateContextMenuForExternalEditors();
var menu = repo.CreateContextMenuForExternalTools();
if (menu != null)
{
menu.Open(button);