feature: add support for Visual Studio as external tool (#648)

* feature: support Visual Studio external tool on Windows
* feature: when opening in Visual Studio, try to locate solution file
This commit is contained in:
Dmitrij D. Czarkoff 2024-11-04 01:22:16 +00:00 committed by GitHub
parent fba84c8297
commit 1d0098703e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 55 additions and 6 deletions

View file

@ -127,6 +127,7 @@ This app supports open repository in external tools listed in the table below.
| JetBrains Fleet | YES | YES | YES | FLEET | | JetBrains Fleet | YES | YES | YES | FLEET |
| Sublime Text | YES | YES | YES | SUBLIME_TEXT | | Sublime Text | YES | YES | YES | SUBLIME_TEXT |
| Zed | NO | YES | YES | ZED | | Zed | NO | YES | YES | ZED |
| Visual Studio | YES | YES | YES | VISUALSTUDIO |
> [!NOTE] > [!NOTE]
> This app will try to find those tools based on some pre-defined or expected locations automatically. If you are using one portable version of these tools, it will not be detected by this app. > This app will try to find those tools based on some pre-defined or expected locations automatically. If you are using one portable version of these tools, it will not be detected by this app.

View file

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
@ -16,12 +17,14 @@ namespace SourceGit.Models
public string Executable { get; private set; } public string Executable { get; private set; }
public string OpenCmdArgs { get; private set; } public string OpenCmdArgs { get; private set; }
public Bitmap IconImage { get; private set; } = null; public Bitmap IconImage { get; private set; } = null;
public Func<string, string> ArgTransform { get; private set; }
public ExternalTool(string name, string icon, string executable, string openCmdArgs) public ExternalTool(string name, string icon, string executable, string openCmdArgs, Func<string, string> argsTransform)
{ {
Name = name; Name = name;
Executable = executable; Executable = executable;
OpenCmdArgs = openCmdArgs; OpenCmdArgs = openCmdArgs;
ArgTransform = argsTransform ?? ((s) => s);
try try
{ {
@ -37,11 +40,16 @@ namespace SourceGit.Models
public void Open(string repo) public void Open(string repo)
{ {
string arguments = string.Format(OpenCmdArgs, repo);
if (ArgTransform != null)
arguments = ArgTransform.Invoke(arguments);
Process.Start(new ProcessStartInfo() Process.Start(new ProcessStartInfo()
{ {
WorkingDirectory = repo, WorkingDirectory = repo,
FileName = Executable, FileName = Executable,
Arguments = string.Format(OpenCmdArgs, repo), Arguments = arguments,
UseShellExecute = false, UseShellExecute = false,
}); });
} }
@ -110,17 +118,17 @@ namespace SourceGit.Models
_customPaths = new ExternalToolPaths(); _customPaths = new ExternalToolPaths();
} }
public void TryAdd(string name, string icon, string args, string key, Func<string> finder) public void TryAdd(string name, string icon, string args, string key, Func<string> finder, Func<string, string> argsTransform = null)
{ {
if (_customPaths.Tools.TryGetValue(key, out var customPath) && File.Exists(customPath)) if (_customPaths.Tools.TryGetValue(key, out var customPath) && File.Exists(customPath))
{ {
Founded.Add(new ExternalTool(name, icon, customPath, args)); Founded.Add(new ExternalTool(name, icon, customPath, args, argsTransform));
} }
else else
{ {
var path = finder(); var path = finder();
if (!string.IsNullOrEmpty(path) && File.Exists(path)) if (!string.IsNullOrEmpty(path) && File.Exists(path))
Founded.Add(new ExternalTool(name, icon, path, args)); Founded.Add(new ExternalTool(name, icon, path, args, argsTransform));
} }
} }
@ -154,6 +162,25 @@ namespace SourceGit.Models
TryAdd("Zed", "zed", "\"{0}\"", "ZED", platformFinder); TryAdd("Zed", "zed", "\"{0}\"", "ZED", platformFinder);
} }
public void VisualStudio(Func<string> platformFinder)
{
TryAdd("Visual Studio", "vs", "\"{0}\"", "VISUALSTUDIO", platformFinder, VisualStudioTryFindSolution);
}
private static string VisualStudioTryFindSolution(string path)
{
try
{
if (Directory.GetFiles(path.Trim('\"'), "*.sln", SearchOption.AllDirectories).FirstOrDefault() is string solutionPath)
return Path.GetFullPath(solutionPath);
}
catch
{
// do nothing
}
return path;
}
public void FindJetBrainsFromToolbox(Func<string> platformFinder) public void FindJetBrainsFromToolbox(Func<string> platformFinder)
{ {
var exclude = new List<string> { "fleet", "dotmemory", "dottrace", "resharper-u", "androidstudio" }; var exclude = new List<string> { "fleet", "dotmemory", "dottrace", "resharper-u", "androidstudio" };
@ -171,7 +198,8 @@ namespace SourceGit.Models
$"{tool.DisplayName} {tool.DisplayVersion}", $"{tool.DisplayName} {tool.DisplayVersion}",
supported_icons.Contains(tool.ProductCode) ? $"JetBrains/{tool.ProductCode}" : "JetBrains/JB", supported_icons.Contains(tool.ProductCode) ? $"JetBrains/{tool.ProductCode}" : "JetBrains/JB",
Path.Combine(tool.InstallLocation, tool.LaunchCommand), Path.Combine(tool.InstallLocation, tool.LaunchCommand),
"\"{0}\"")); "\"{0}\"",
null));
} }
} }
} }

View file

@ -134,6 +134,7 @@ namespace SourceGit.Native
finder.Fleet(() => $"{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}\\Programs\\Fleet\\Fleet.exe"); finder.Fleet(() => $"{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}\\Programs\\Fleet\\Fleet.exe");
finder.FindJetBrainsFromToolbox(() => $"{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}\\JetBrains\\Toolbox"); finder.FindJetBrainsFromToolbox(() => $"{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}\\JetBrains\\Toolbox");
finder.SublimeText(FindSublimeText); finder.SublimeText(FindSublimeText);
finder.VisualStudio(FindVisualStudio);
return finder.Founded; return finder.Founded;
} }
@ -313,6 +314,25 @@ namespace SourceGit.Native
return string.Empty; return string.Empty;
} }
private string FindVisualStudio()
{
var localMachine = Microsoft.Win32.RegistryKey.OpenBaseKey(
Microsoft.Win32.RegistryHive.LocalMachine,
Microsoft.Win32.RegistryView.Registry64);
// Get default class for VisualStudio.Launcher.sln - the handler for *.sln files
if (localMachine.OpenSubKey(@"SOFTWARE\Classes\VisualStudio.Launcher.sln\CLSID") is Microsoft.Win32.RegistryKey launcher)
{
// Get actual path to the executable
if (launcher.GetValue(string.Empty) is string CLSID && localMachine.OpenSubKey(@$"SOFTWARE\Classes\CLSID\{CLSID}\LocalServer32") is Microsoft.Win32.RegistryKey devenv && devenv.GetValue(string.Empty) is string localServer32)
{
return localServer32!.Trim('\"');
}
}
return string.Empty;
}
#endregion #endregion
private void OpenFolderAndSelectFile(string folderPath) private void OpenFolderAndSelectFile(string folderPath)