|
2 | 2 | using System.Collections.Generic;
|
3 | 3 | using System.Diagnostics;
|
4 | 4 | using System.IO;
|
| 5 | +using System.Linq; |
5 | 6 | using System.Runtime.InteropServices;
|
6 | 7 | using System.Runtime.Versioning;
|
7 | 8 | using System.Text;
|
8 |
| - |
| 9 | +using System.Text.RegularExpressions; |
9 | 10 | using Avalonia;
|
10 | 11 | using Avalonia.Controls;
|
11 | 12 | using Avalonia.Platform;
|
|
14 | 15 | namespace SourceGit.Native
|
15 | 16 | {
|
16 | 17 | [SupportedOSPlatform("windows")]
|
17 |
| - internal class Windows : OS.IBackend |
| 18 | + internal partial class Windows : OS.IBackend |
18 | 19 | {
|
| 20 | + private const string VSWHERE_PATH = @"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe"; |
| 21 | + |
19 | 22 | internal struct RECT
|
20 | 23 | {
|
21 | 24 | public int left;
|
@@ -189,7 +192,13 @@ public string FindTerminal(Models.ShellOrTerminal shell)
|
189 | 192 | finder.Fleet(() => Path.Combine(localAppDataDir, @"Programs\Fleet\Fleet.exe"));
|
190 | 193 | finder.FindJetBrainsFromToolbox(() => Path.Combine(localAppDataDir, @"JetBrains\Toolbox"));
|
191 | 194 | 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 | + |
193 | 202 | return finder.Tools;
|
194 | 203 | }
|
195 | 204 |
|
@@ -371,25 +380,60 @@ private string FindSublimeText()
|
371 | 380 | return string.Empty;
|
372 | 381 | }
|
373 | 382 |
|
374 |
| - private string FindVisualStudio() |
| 383 | + private static (string Path, bool IsPreview)[] FindVisualStudioInstances() |
375 | 384 | {
|
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(); |
379 | 401 |
|
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) |
382 | 413 | {
|
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); |
388 | 419 | }
|
389 | 420 |
|
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(); |
391 | 432 | }
|
392 | 433 |
|
| 434 | + [GeneratedRegex(@"^(\w+):\s(.*)$")] |
| 435 | + private static partial Regex VsWhereLine(); |
| 436 | + |
393 | 437 | private string FindCursor()
|
394 | 438 | {
|
395 | 439 | var cursorPath = Path.Combine(
|
|
0 commit comments