Skip to content

Commit fd43f8d

Browse files
committed
feat: detect visual studio preview as external tools
1 parent d5745b4 commit fd43f8d

File tree

1 file changed

+59
-15
lines changed

1 file changed

+59
-15
lines changed

src/Native/Windows.cs

Lines changed: 59 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
using System.Collections.Generic;
33
using System.Diagnostics;
44
using System.IO;
5+
using System.Linq;
56
using System.Runtime.InteropServices;
67
using System.Runtime.Versioning;
78
using System.Text;
8-
9+
using System.Text.RegularExpressions;
910
using Avalonia;
1011
using Avalonia.Controls;
1112
using Avalonia.Platform;
@@ -14,8 +15,10 @@
1415
namespace SourceGit.Native
1516
{
1617
[SupportedOSPlatform("windows")]
17-
internal class Windows : OS.IBackend
18+
internal partial class Windows : OS.IBackend
1819
{
20+
private const string VSWHERE_PATH = @"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe";
21+
1922
internal struct RECT
2023
{
2124
public int left;
@@ -189,7 +192,13 @@ public string FindTerminal(Models.ShellOrTerminal shell)
189192
finder.Fleet(() => Path.Combine(localAppDataDir, @"Programs\Fleet\Fleet.exe"));
190193
finder.FindJetBrainsFromToolbox(() => Path.Combine(localAppDataDir, @"JetBrains\Toolbox"));
191194
finder.SublimeText(FindSublimeText);
192-
finder.TryAdd("Visual Studio", "vs", FindVisualStudio, GenerateCommandlineArgsForVisualStudio);
195+
196+
foreach (var instance in FindVisualStudioInstances().OrderBy(q => q.IsPreview))
197+
{
198+
var name = instance.IsPreview ? "Visual Studio - Preview" : "Visual Studio";
199+
finder.TryAdd(name, "vs", () => instance.Path, GenerateCommandlineArgsForVisualStudio);
200+
}
201+
193202
return finder.Tools;
194203
}
195204

@@ -371,25 +380,60 @@ private string FindSublimeText()
371380
return string.Empty;
372381
}
373382

374-
private string FindVisualStudio()
383+
private static (string Path, bool IsPreview)[] FindVisualStudioInstances()
375384
{
376-
var localMachine = Microsoft.Win32.RegistryKey.OpenBaseKey(
377-
Microsoft.Win32.RegistryHive.LocalMachine,
378-
Microsoft.Win32.RegistryView.Registry64);
385+
static string ExecuteAndGetOutput(string programPath, string arguments)
386+
{
387+
using var process = new Process
388+
{
389+
StartInfo = new ProcessStartInfo
390+
{
391+
FileName = programPath,
392+
Arguments = arguments,
393+
UseShellExecute = false,
394+
RedirectStandardOutput = true,
395+
}
396+
};
397+
398+
process.Start();
399+
var stdOut = process.StandardOutput.ReadToEnd();
400+
process.WaitForExit();
379401

380-
// Get default class for VisualStudio.Launcher.sln - the handler for *.sln files
381-
if (localMachine.OpenSubKey(@"SOFTWARE\Classes\VisualStudio.Launcher.sln\CLSID") is { } launcher)
402+
if (process.ExitCode > 0)
403+
{
404+
return null;
405+
}
406+
else
407+
{
408+
return stdOut;
409+
}
410+
}
411+
412+
static Dictionary<string, string> InterpretVsWhereChunk(string chunk)
382413
{
383-
// Get actual path to the executable
384-
if (launcher.GetValue(string.Empty) is string CLSID &&
385-
localMachine.OpenSubKey(@$"SOFTWARE\Classes\CLSID\{CLSID}\LocalServer32") is { } devenv &&
386-
devenv.GetValue(string.Empty) is string localServer32)
387-
return localServer32!.Trim('\"');
414+
var lines = chunk.Split(Environment.NewLine);
415+
416+
return lines.Select(l => VsWhereLine().Match(l))
417+
.Where(regexResult => regexResult.Success)
418+
.ToDictionary(regexResult => regexResult.Groups[1].Value, regexResult => regexResult.Groups[2].Value);
388419
}
389420

390-
return string.Empty;
421+
var vswherePath = Environment.ExpandEnvironmentVariables(VSWHERE_PATH);
422+
if (!File.Exists(vswherePath))
423+
{
424+
return [];
425+
}
426+
427+
var outputChunks = ExecuteAndGetOutput(vswherePath, "-nologo -nocolor -prerelease").Split(Environment.NewLine + Environment.NewLine);
428+
429+
return outputChunks.Select(InterpretVsWhereChunk)
430+
.Select(q => (q["productPath"], q["isPrerelease"] == "1"))
431+
.ToArray();
391432
}
392433

434+
[GeneratedRegex(@"^(\w+):\s(.*)$")]
435+
private static partial Regex VsWhereLine();
436+
393437
private string FindCursor()
394438
{
395439
var cursorPath = Path.Combine(

0 commit comments

Comments
 (0)