From 37e8769b3706402706d62f19202554080796b3e3 Mon Sep 17 00:00:00 2001 From: Heath Stewart Date: Wed, 29 Sep 2021 15:03:05 -0700 Subject: [PATCH] Show only latest VS, VC prompts by default (#11326) ## Summary of the Pull Request Similar to `vswhere -latest`, show only the latest Visual Studio command prompts / developer PowerShell. This was tested by deleting the local package state and testing against fresh state with both VS2019 and VS2022 Preview installed, and indeed VS2022 Preview (both cmd and powershell) show. The other profiles were generated but hidden by default. ## References Modification of PR #7774 ## PR Checklist * [x] Closes #11307 * [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA * [x] Tests added/passed * [ ] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx * [ ] Schema updated. * [x] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #xxx ## Detailed Description of the Pull Request / Additional comments The sort algorithm is the same basic algorithm I used in https://github.com/microsoft/vswhere. It sorts first by installation version with a secondary sort based on the install date in case the installation versions are the same. ## Validation Steps Performed With both VS2019 and VS2022 Preview installed, I made sure the initial state was expected, and tried different combinations of hiding and unhiding generated entries, and restarted Terminal to make sure my settings "stuck". --- .github/actions/spelling/allow/microsoft.txt | 1 + .../actions/spelling/patterns/patterns.txt | 1 + .../BaseVisualStudioGenerator.cpp | 36 ------- .../BaseVisualStudioGenerator.h | 35 ------- .../CascadiaSettingsSerialization.cpp | 6 +- ...crosoft.Terminal.Settings.ModelLib.vcxproj | 9 +- ...Terminal.Settings.ModelLib.vcxproj.filters | 12 ++- .../VcDevCmdGenerator.cpp | 94 +++++++++++++++++++ .../TerminalSettingsModel/VcDevCmdGenerator.h | 40 ++++++++ .../VisualStudioGenerator.cpp | 36 +++++++ .../VisualStudioGenerator.h | 38 ++++++++ .../VsDevCmdGenerator.cpp | 30 +++++- .../TerminalSettingsModel/VsDevCmdGenerator.h | 23 ++--- .../VsDevShellGenerator.cpp | 36 ++++++- .../VsDevShellGenerator.h | 23 ++--- .../VsSetupConfiguration.cpp | 20 ++++ .../VsSetupConfiguration.h | 61 ++++++++---- 17 files changed, 375 insertions(+), 126 deletions(-) delete mode 100644 src/cascadia/TerminalSettingsModel/BaseVisualStudioGenerator.cpp delete mode 100644 src/cascadia/TerminalSettingsModel/BaseVisualStudioGenerator.h create mode 100644 src/cascadia/TerminalSettingsModel/VcDevCmdGenerator.cpp create mode 100644 src/cascadia/TerminalSettingsModel/VcDevCmdGenerator.h create mode 100644 src/cascadia/TerminalSettingsModel/VisualStudioGenerator.cpp create mode 100644 src/cascadia/TerminalSettingsModel/VisualStudioGenerator.h diff --git a/.github/actions/spelling/allow/microsoft.txt b/.github/actions/spelling/allow/microsoft.txt index c82254bbb..a96139e3c 100644 --- a/.github/actions/spelling/allow/microsoft.txt +++ b/.github/actions/spelling/allow/microsoft.txt @@ -24,6 +24,7 @@ DTDs DWINRT enablewttlogging Intelli +IVisual LKG LOCKFILE Lxss diff --git a/.github/actions/spelling/patterns/patterns.txt b/.github/actions/spelling/patterns/patterns.txt index 882243396..47f902f90 100644 --- a/.github/actions/spelling/patterns/patterns.txt +++ b/.github/actions/spelling/patterns/patterns.txt @@ -24,3 +24,4 @@ VERIFY_ARE_EQUAL\(L"[^"]+" std::memory_order_[\w]+ D2DERR_SHADER_COMPILE_FAILED TIL_FEATURE_[0-9A-Z_]+ +vcvars\w* diff --git a/src/cascadia/TerminalSettingsModel/BaseVisualStudioGenerator.cpp b/src/cascadia/TerminalSettingsModel/BaseVisualStudioGenerator.cpp deleted file mode 100644 index cb351935f..000000000 --- a/src/cascadia/TerminalSettingsModel/BaseVisualStudioGenerator.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "pch.h" -#include "BaseVisualStudioGenerator.h" -#include "DynamicProfileUtils.h" - -using namespace winrt::Microsoft::Terminal::Settings::Model; - -void BaseVisualStudioGenerator::GenerateProfiles(std::vector>& profiles) const -{ - // There's no point in enumerating valid Visual Studio instances more than once, - // so cache them for use by both Visual Studio profile generators. - static const auto instances = VsSetupConfiguration::QueryInstances(); - - for (auto const& instance : instances) - { - try - { - if (!IsInstanceValid(instance)) - { - continue; - } - - const auto seed = GetProfileGuidSeed(instance); - const winrt::guid profileGuid{ ::Microsoft::Console::Utils::CreateV5Uuid(TERMINAL_PROFILE_NAMESPACE_GUID, gsl::as_bytes(gsl::make_span(seed))) }; - auto profile = winrt::make_self(profileGuid); - profile->Name(winrt::hstring{ GetProfileName(instance) }); - profile->Commandline(winrt::hstring{ GetProfileCommandLine(instance) }); - profile->StartingDirectory(winrt::hstring{ instance.GetInstallationPath() }); - profile->Icon(winrt::hstring{ GetProfileIconPath() }); - profiles.emplace_back(std::move(profile)); - } - CATCH_LOG(); - } -} diff --git a/src/cascadia/TerminalSettingsModel/BaseVisualStudioGenerator.h b/src/cascadia/TerminalSettingsModel/BaseVisualStudioGenerator.h deleted file mode 100644 index e2ea620b8..000000000 --- a/src/cascadia/TerminalSettingsModel/BaseVisualStudioGenerator.h +++ /dev/null @@ -1,35 +0,0 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. - -Module Name: -- BaseVisualStudioGenerator - -Abstract: -- Base generator for Visual Studio Developer shell profiles - -Author(s): -- Charles Willis - October 2020 - ---*/ - -#pragma once - -#include "IDynamicProfileGenerator.h" -#include "VsSetupConfiguration.h" - -namespace winrt::Microsoft::Terminal::Settings::Model -{ - class BaseVisualStudioGenerator : public IDynamicProfileGenerator - { - public: - void GenerateProfiles(std::vector>& profiles) const override; - - protected: - virtual bool IsInstanceValid(const VsSetupConfiguration::VsSetupInstance& instance) const = 0; - virtual std::wstring GetProfileName(const VsSetupConfiguration::VsSetupInstance& instance) const = 0; - virtual std::wstring GetProfileCommandLine(const VsSetupConfiguration::VsSetupInstance& instance) const = 0; - virtual std::wstring GetProfileGuidSeed(const VsSetupConfiguration::VsSetupInstance& instance) const = 0; - virtual std::wstring GetProfileIconPath() const = 0; - }; -}; diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp index 431a54eff..8720ad8c3 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp @@ -11,8 +11,7 @@ #include "AzureCloudShellGenerator.h" #include "PowershellCoreProfileGenerator.h" -#include "VsDevCmdGenerator.h" -#include "VsDevShellGenerator.h" +#include "VisualStudioGenerator.h" #include "WslDistroGenerator.h" // The following files are generated at build time into the "Generated Files" directory. @@ -131,8 +130,7 @@ void SettingsLoader::GenerateProfiles() _executeGenerator(PowershellCoreProfileGenerator{}); _executeGenerator(WslDistroGenerator{}); _executeGenerator(AzureCloudShellGenerator{}); - _executeGenerator(VsDevCmdGenerator{}); - _executeGenerator(VsDevShellGenerator{}); + _executeGenerator(VisualStudioGenerator{}); } // A new settings.json gets a special treatment: diff --git a/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj b/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj index 8f5ab68c7..a5d82e34d 100644 --- a/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj +++ b/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj @@ -14,7 +14,8 @@ - + + DefaultTerminal.idl @@ -84,7 +85,8 @@ - + + DefaultTerminal.idl @@ -241,7 +243,6 @@ - @@ -266,4 +267,4 @@ - + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj.filters b/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj.filters index 149d48af1..873dcb28c 100644 --- a/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj.filters +++ b/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj.filters @@ -34,7 +34,7 @@ - + profileGeneration @@ -46,6 +46,9 @@ profileGeneration + + profileGeneration + @@ -80,7 +83,7 @@ - + profileGeneration @@ -92,6 +95,9 @@ profileGeneration + + profileGeneration + @@ -123,4 +129,4 @@ {81a6314f-aa5b-4533-a499-13bc3a5c4af0} - + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsModel/VcDevCmdGenerator.cpp b/src/cascadia/TerminalSettingsModel/VcDevCmdGenerator.cpp new file mode 100644 index 000000000..633e5b94c --- /dev/null +++ b/src/cascadia/TerminalSettingsModel/VcDevCmdGenerator.cpp @@ -0,0 +1,94 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "DynamicProfileUtils.h" +#include "VcDevCmdGenerator.h" + +using namespace winrt::Microsoft::Terminal::Settings::Model; + +void VcDevCmdGenerator::GenerateProfiles(const VsSetupConfiguration::VsSetupInstance& instance, bool hidden, std::vector>& profiles) const +{ + try + { + const std::filesystem::path root{ GetVcCmdScriptDirectory(instance) }; + if (!std::filesystem::exists(root)) + { + return; + } + +// x64 environments only installed on 64-bit machines. +#ifdef _WIN64 + const auto vcvars64 = root / L"vcvars64.bat"; + if (std::filesystem::exists(vcvars64)) + { + auto profile = CreateProfile(instance, L"x64", vcvars64, hidden); + profiles.emplace_back(std::move(profile)); + + // Only the VC environment for the matching architecture should be shown by default. + hidden = true; + } + + const auto vcvarsamd64_x86 = root / L"vcvarsamd64_x86.bat"; + if (std::filesystem::exists(vcvarsamd64_x86)) + { + auto profile = CreateProfile(instance, L"x64_x86", vcvarsamd64_x86, true); + profiles.emplace_back(std::move(profile)); + } + + const auto vcvarsx86_amd64 = root / L"vcvarsx86_amd64.bat"; + if (std::filesystem::exists(vcvarsx86_amd64)) + { + auto profile = CreateProfile(instance, L"x86_x64", vcvarsx86_amd64, true); + profiles.emplace_back(std::move(profile)); + } +#endif // _WIN64 + + const auto vcvars32 = root / L"vcvars32.bat"; + if (std::filesystem::exists(vcvars32)) + { + auto profile = CreateProfile(instance, L"x86", vcvars32, hidden); + profiles.emplace_back(std::move(profile)); + } + } + CATCH_LOG(); +} + +winrt::com_ptr VcDevCmdGenerator::CreateProfile(const VsSetupConfiguration::VsSetupInstance& instance, const std::wstring_view& prefix, const std::filesystem::path& path, bool hidden) const +{ + const auto seed = GetProfileGuidSeed(instance, path); + const winrt::guid profileGuid{ ::Microsoft::Console::Utils::CreateV5Uuid(TERMINAL_PROFILE_NAMESPACE_GUID, gsl::as_bytes(gsl::make_span(seed))) }; + + auto profile = winrt::make_self(profileGuid); + profile->Name(winrt::hstring{ GetProfileName(instance, prefix) }); + profile->Commandline(winrt::hstring{ GetProfileCommandLine(path) }); + profile->StartingDirectory(winrt::hstring{ instance.GetInstallationPath() }); + profile->Icon(winrt::hstring{ GetProfileIconPath() }); + profile->Hidden(hidden); + + return profile; +} + +std::wstring VcDevCmdGenerator::GetProfileGuidSeed(const VsSetupConfiguration::VsSetupInstance& instance, const std::filesystem::path& path) const +{ + return L"VsDevCmd" + instance.GetInstanceId() + path.native(); +} + +std::wstring VcDevCmdGenerator::GetProfileName(const VsSetupConfiguration::VsSetupInstance& instance, const std::wstring_view& prefix) const +{ + std::wstring name{ prefix + L" Native Tools Command Prompt for VS " }; + name.append(instance.GetProfileNameSuffix()); + return name; +} + +std::wstring VcDevCmdGenerator::GetProfileCommandLine(const std::filesystem::path& path) const +{ + std::wstring commandLine{ L"cmd.exe /k \"" + path.native() + L"\"" }; + + return commandLine; +} + +std::wstring VcDevCmdGenerator::GetVcCmdScriptDirectory(const VsSetupConfiguration::VsSetupInstance& instance) const +{ + return instance.ResolvePath(L"VC\\Auxiliary\\Build\\"); +} diff --git a/src/cascadia/TerminalSettingsModel/VcDevCmdGenerator.h b/src/cascadia/TerminalSettingsModel/VcDevCmdGenerator.h new file mode 100644 index 000000000..8e30cf28b --- /dev/null +++ b/src/cascadia/TerminalSettingsModel/VcDevCmdGenerator.h @@ -0,0 +1,40 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Module Name: +- VcDevCmdGenerator + +Abstract: +- Dynamic profile generator for Visual C++ development environments. + +Author(s): +- Heath Stewart - September 2021 + +--*/ + +#pragma once +#include "VisualStudioGenerator.h" +#include "VsSetupConfiguration.h" + +namespace winrt::Microsoft::Terminal::Settings::Model +{ + class VcDevCmdGenerator final : public VisualStudioGenerator::IVisualStudioProfileGenerator + { + public: + void GenerateProfiles(const VsSetupConfiguration::VsSetupInstance& instance, bool hidden, std::vector>& profiles) const override; + + private: + winrt::com_ptr CreateProfile(const VsSetupConfiguration::VsSetupInstance& instance, const std::wstring_view& prefix, const std::filesystem::path& path, bool hidden) const; + + std::wstring GetProfileIconPath() const + { + return L"ms-appx:///ProfileIcons/{0caa0dad-35be-5f56-a8ff-afceeeaa6101}.png"; + } + + std::wstring GetProfileGuidSeed(const VsSetupConfiguration::VsSetupInstance& instance, const std::filesystem::path& path) const; + std::wstring GetProfileName(const VsSetupConfiguration::VsSetupInstance& instance, const std::wstring_view& prefix) const; + std::wstring GetProfileCommandLine(const std::filesystem::path& path) const; + std::wstring GetVcCmdScriptDirectory(const VsSetupConfiguration::VsSetupInstance& instance) const; + }; +}; diff --git a/src/cascadia/TerminalSettingsModel/VisualStudioGenerator.cpp b/src/cascadia/TerminalSettingsModel/VisualStudioGenerator.cpp new file mode 100644 index 000000000..9e883205e --- /dev/null +++ b/src/cascadia/TerminalSettingsModel/VisualStudioGenerator.cpp @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "DynamicProfileUtils.h" +#include "VisualStudioGenerator.h" +#include "VcDevCmdGenerator.h" +#include "VsDevCmdGenerator.h" +#include "VsDevShellGenerator.h" + +using namespace winrt::Microsoft::Terminal::Settings::Model; + +std::wstring_view VisualStudioGenerator::GetNamespace() const noexcept +{ + return std::wstring_view{ L"Windows.Terminal.VisualStudio" }; +} + +void VisualStudioGenerator::GenerateProfiles(std::vector>& profiles) const +{ + const auto instances = VsSetupConfiguration::QueryInstances(); + + VsDevCmdGenerator devCmdGenerator; + VcDevCmdGenerator vcCmdGenerator; + VsDevShellGenerator devShellGenerator; + + // Instances are ordered from latest to oldest. Hide all but the profiles for the latest instance. + bool hidden = false; + for (auto const& instance : instances) + { + devCmdGenerator.GenerateProfiles(instance, hidden, profiles); + vcCmdGenerator.GenerateProfiles(instance, hidden, profiles); + devShellGenerator.GenerateProfiles(instance, hidden, profiles); + + hidden = true; + } +} diff --git a/src/cascadia/TerminalSettingsModel/VisualStudioGenerator.h b/src/cascadia/TerminalSettingsModel/VisualStudioGenerator.h new file mode 100644 index 000000000..766d2cd77 --- /dev/null +++ b/src/cascadia/TerminalSettingsModel/VisualStudioGenerator.h @@ -0,0 +1,38 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Module Name: +- VisualStudioGenerator + +Abstract: +- Generator for Visual Studio shell profiles. Actual profile generation is delegated + to separate classes that encapsulate different logic for cmd- and powershell-based shells, + as well as VC startup scripts specific to the current processor architecture. + +Author(s): +- Charles Willis - October 2020 +- Heath Stewart - September 2021 + +--*/ + +#pragma once + +#include "IDynamicProfileGenerator.h" +#include "VsSetupConfiguration.h" + +namespace winrt::Microsoft::Terminal::Settings::Model +{ + class VisualStudioGenerator : public IDynamicProfileGenerator + { + public: + std::wstring_view GetNamespace() const noexcept override; + void GenerateProfiles(std::vector>& profiles) const override; + + class IVisualStudioProfileGenerator + { + public: + virtual void GenerateProfiles(const VsSetupConfiguration::VsSetupInstance& instance, bool hidden, std::vector>& profiles) const = 0; + }; + }; +}; diff --git a/src/cascadia/TerminalSettingsModel/VsDevCmdGenerator.cpp b/src/cascadia/TerminalSettingsModel/VsDevCmdGenerator.cpp index d21ae8ff2..a5327a300 100644 --- a/src/cascadia/TerminalSettingsModel/VsDevCmdGenerator.cpp +++ b/src/cascadia/TerminalSettingsModel/VsDevCmdGenerator.cpp @@ -2,10 +2,33 @@ // Licensed under the MIT license. #include "pch.h" +#include "DynamicProfileUtils.h" #include "VsDevCmdGenerator.h" using namespace winrt::Microsoft::Terminal::Settings::Model; +void VsDevCmdGenerator::GenerateProfiles(const VsSetupConfiguration::VsSetupInstance& instance, bool hidden, std::vector>& profiles) const +{ + try + { + if (!IsInstanceValid(instance)) + { + return; + } + + const auto seed = GetProfileGuidSeed(instance); + const winrt::guid profileGuid{ ::Microsoft::Console::Utils::CreateV5Uuid(TERMINAL_PROFILE_NAMESPACE_GUID, gsl::as_bytes(gsl::make_span(seed))) }; + auto profile = winrt::make_self(profileGuid); + profile->Name(winrt::hstring{ GetProfileName(instance) }); + profile->Commandline(winrt::hstring{ GetProfileCommandLine(instance) }); + profile->StartingDirectory(winrt::hstring{ instance.GetInstallationPath() }); + profile->Icon(winrt::hstring{ GetProfileIconPath() }); + profile->Hidden(hidden); + profiles.emplace_back(std::move(profile)); + } + CATCH_LOG(); +} + std::wstring VsDevCmdGenerator::GetProfileName(const VsSetupConfiguration::VsSetupInstance& instance) const { std::wstring name{ L"Developer Command Prompt for VS " }; @@ -15,7 +38,12 @@ std::wstring VsDevCmdGenerator::GetProfileName(const VsSetupConfiguration::VsSet std::wstring VsDevCmdGenerator::GetProfileCommandLine(const VsSetupConfiguration::VsSetupInstance& instance) const { - std::wstring commandLine{ L"cmd.exe /k \"" + instance.GetDevCmdScriptPath() + L"\"" }; + std::wstring commandLine{ L"cmd.exe /k \"" + GetDevCmdScriptPath(instance) + L"\"" }; return commandLine; } + +std::wstring VsDevCmdGenerator::GetDevCmdScriptPath(const VsSetupConfiguration::VsSetupInstance& instance) const +{ + return instance.ResolvePath(L"Common7\\Tools\\VsDevCmd.bat"); +} diff --git a/src/cascadia/TerminalSettingsModel/VsDevCmdGenerator.h b/src/cascadia/TerminalSettingsModel/VsDevCmdGenerator.h index 7545bf079..dd5d42dba 100644 --- a/src/cascadia/TerminalSettingsModel/VsDevCmdGenerator.h +++ b/src/cascadia/TerminalSettingsModel/VsDevCmdGenerator.h @@ -10,23 +10,23 @@ Abstract: Author(s): - Charles Willis - October 2020 +- Heath Stewart - September 2021 --*/ #pragma once -#include "BaseVisualStudioGenerator.h" +#include "VisualStudioGenerator.h" +#include "VsSetupConfiguration.h" namespace winrt::Microsoft::Terminal::Settings::Model { - class VsDevCmdGenerator final : public BaseVisualStudioGenerator + class VsDevCmdGenerator final : public VisualStudioGenerator::IVisualStudioProfileGenerator { public: - std::wstring_view GetNamespace() const noexcept override - { - return std::wstring_view{ L"Windows.Terminal.VisualStudio.CommandPrompt" }; - } + void GenerateProfiles(const VsSetupConfiguration::VsSetupInstance& instance, bool hidden, std::vector>& profiles) const override; - bool IsInstanceValid(const VsSetupConfiguration::VsSetupInstance&) const override + private: + bool IsInstanceValid(const VsSetupConfiguration::VsSetupInstance&) const { // We only support version of VS from 15.0. // Per heaths: The [ISetupConfiguration] COM server only supports Visual Studio 15.0 and newer anyway. @@ -34,17 +34,18 @@ namespace winrt::Microsoft::Terminal::Settings::Model return true; } - std::wstring GetProfileGuidSeed(const VsSetupConfiguration::VsSetupInstance& instance) const override + std::wstring GetProfileGuidSeed(const VsSetupConfiguration::VsSetupInstance& instance) const { return L"VsDevCmd" + instance.GetInstanceId(); } - std::wstring GetProfileIconPath() const override + std::wstring GetProfileIconPath() const { return L"ms-appx:///ProfileIcons/{0caa0dad-35be-5f56-a8ff-afceeeaa6101}.png"; } - std::wstring GetProfileName(const VsSetupConfiguration::VsSetupInstance& instance) const override; - std::wstring GetProfileCommandLine(const VsSetupConfiguration::VsSetupInstance& instance) const override; + std::wstring GetProfileName(const VsSetupConfiguration::VsSetupInstance& instance) const; + std::wstring GetProfileCommandLine(const VsSetupConfiguration::VsSetupInstance& instance) const; + std::wstring GetDevCmdScriptPath(const VsSetupConfiguration::VsSetupInstance& instance) const; }; }; diff --git a/src/cascadia/TerminalSettingsModel/VsDevShellGenerator.cpp b/src/cascadia/TerminalSettingsModel/VsDevShellGenerator.cpp index 5849e7dfd..60d4b711c 100644 --- a/src/cascadia/TerminalSettingsModel/VsDevShellGenerator.cpp +++ b/src/cascadia/TerminalSettingsModel/VsDevShellGenerator.cpp @@ -2,11 +2,34 @@ // Licensed under the MIT license. #include "pch.h" +#include "DynamicProfileUtils.h" #include "VsDevShellGenerator.h" #include "VsSetupConfiguration.h" using namespace winrt::Microsoft::Terminal::Settings::Model; +void VsDevShellGenerator::GenerateProfiles(const VsSetupConfiguration::VsSetupInstance& instance, bool hidden, std::vector>& profiles) const +{ + try + { + if (!IsInstanceValid(instance)) + { + return; + } + + const auto seed = GetProfileGuidSeed(instance); + const winrt::guid profileGuid{ ::Microsoft::Console::Utils::CreateV5Uuid(TERMINAL_PROFILE_NAMESPACE_GUID, gsl::as_bytes(gsl::make_span(seed))) }; + auto profile = winrt::make_self(profileGuid); + profile->Name(winrt::hstring{ GetProfileName(instance) }); + profile->Commandline(winrt::hstring{ GetProfileCommandLine(instance) }); + profile->StartingDirectory(winrt::hstring{ instance.GetInstallationPath() }); + profile->Icon(winrt::hstring{ GetProfileIconPath() }); + profile->Hidden(hidden); + profiles.emplace_back(std::move(profile)); + } + CATCH_LOG(); +} + std::wstring VsDevShellGenerator::GetProfileName(const VsSetupConfiguration::VsSetupInstance& instance) const { std::wstring name{ L"Developer PowerShell for VS " }; @@ -20,9 +43,20 @@ std::wstring VsDevShellGenerator::GetProfileCommandLine(const VsSetupConfigurati // The "SkipAutomaticLocation" parameter will prevent "Enter-VsDevShell" from automatically setting the shell path // so the path in the profile will be used instead. std::wstring commandLine{ L"powershell.exe -NoExit -Command \"& {" }; - commandLine.append(L"Import-Module \"\"\"" + instance.GetDevShellModulePath() + L"\"\"\";"); + commandLine.append(L"Import-Module \"\"\"" + GetDevShellModulePath(instance) + L"\"\"\";"); commandLine.append(L"Enter-VsDevShell " + instance.GetInstanceId() + L" -SkipAutomaticLocation"); commandLine.append(L"}\""); return commandLine; } + +std::wstring VsDevShellGenerator::GetDevShellModulePath(const VsSetupConfiguration::VsSetupInstance& instance) const +{ + // The path of Microsoft.VisualStudio.DevShell.dll changed in 16.3 + if (instance.VersionInRange(L"[16.3,")) + { + return instance.ResolvePath(L"Common7\\Tools\\Microsoft.VisualStudio.DevShell.dll"); + } + + return instance.ResolvePath(L"Common7\\Tools\\vsdevshell\\Microsoft.VisualStudio.DevShell.dll"); +} diff --git a/src/cascadia/TerminalSettingsModel/VsDevShellGenerator.h b/src/cascadia/TerminalSettingsModel/VsDevShellGenerator.h index d8c027dd4..b7d14e8b9 100644 --- a/src/cascadia/TerminalSettingsModel/VsDevShellGenerator.h +++ b/src/cascadia/TerminalSettingsModel/VsDevShellGenerator.h @@ -10,38 +10,39 @@ Abstract: Author(s): - Charles Willis - October 2020 +- Heath Stewart - September 2021 --*/ #pragma once -#include "BaseVisualStudioGenerator.h" +#include "VisualStudioGenerator.h" +#include "VsSetupConfiguration.h" namespace winrt::Microsoft::Terminal::Settings::Model { - class VsDevShellGenerator final : public BaseVisualStudioGenerator + class VsDevShellGenerator final : public VisualStudioGenerator::IVisualStudioProfileGenerator { public: - std::wstring_view GetNamespace() const noexcept override - { - return std::wstring_view{ L"Windows.Terminal.VisualStudio.Powershell" }; - } + void GenerateProfiles(const VsSetupConfiguration::VsSetupInstance& instance, bool hidden, std::vector>& profiles) const override; - bool IsInstanceValid(const VsSetupConfiguration::VsSetupInstance& instance) const override + private: + bool IsInstanceValid(const VsSetupConfiguration::VsSetupInstance& instance) const { return instance.VersionInRange(L"[16.2,)"); } - std::wstring GetProfileGuidSeed(const VsSetupConfiguration::VsSetupInstance& instance) const override + std::wstring GetProfileGuidSeed(const VsSetupConfiguration::VsSetupInstance& instance) const { return L"VsDevShell" + instance.GetInstanceId(); } - std::wstring GetProfileIconPath() const override + std::wstring GetProfileIconPath() const { return L"ms-appx:///ProfileIcons/{61c54bbd-c2c6-5271-96e7-009a87ff44bf}.png"; } - std::wstring GetProfileName(const VsSetupConfiguration::VsSetupInstance& instance) const override; - std::wstring GetProfileCommandLine(const VsSetupConfiguration::VsSetupInstance& instance) const override; + std::wstring GetProfileName(const VsSetupConfiguration::VsSetupInstance& instance) const; + std::wstring GetProfileCommandLine(const VsSetupConfiguration::VsSetupInstance& instance) const; + std::wstring GetDevShellModulePath(const VsSetupConfiguration::VsSetupInstance& instance) const; }; }; diff --git a/src/cascadia/TerminalSettingsModel/VsSetupConfiguration.cpp b/src/cascadia/TerminalSettingsModel/VsSetupConfiguration.cpp index 47e965757..1b92e77ea 100644 --- a/src/cascadia/TerminalSettingsModel/VsSetupConfiguration.cpp +++ b/src/cascadia/TerminalSettingsModel/VsSetupConfiguration.cpp @@ -32,6 +32,19 @@ std::vector VsSetupConfiguration::QueryIn THROW_IF_FAILED(result); + // Sort instances based on version and install date from latest to oldest. + std::sort(instances.begin(), instances.end(), [](const VsSetupInstance& a, const VsSetupInstance& b) { + auto const aVersion = a.GetComparableVersion(); + auto const bVersion = b.GetComparableVersion(); + + if (aVersion == bVersion) + { + return a.GetComparableInstallDate() > b.GetComparableInstallDate(); + } + + return aVersion > bVersion; + }); + return instances; } @@ -95,6 +108,13 @@ std::wstring VsSetupConfiguration::GetInstanceId(ISetupInstance* pInst) return bstrInstanceId.get(); } +unsigned long long VsSetupConfiguration::GetInstallDate(ISetupInstance* pInst) +{ + FILETIME ftInstallDate{ 0 }; + THROW_IF_FAILED(pInst->GetInstallDate(&ftInstallDate)); + return wil::filetime::to_int64(ftInstallDate); +} + std::wstring VsSetupConfiguration::GetStringProperty(ISetupPropertyStore* pProps, std::wstring_view name) { if (pProps == nullptr) diff --git a/src/cascadia/TerminalSettingsModel/VsSetupConfiguration.h b/src/cascadia/TerminalSettingsModel/VsSetupConfiguration.h index 849b91b10..f3a318af0 100644 --- a/src/cascadia/TerminalSettingsModel/VsSetupConfiguration.h +++ b/src/cascadia/TerminalSettingsModel/VsSetupConfiguration.h @@ -10,6 +10,7 @@ Abstract: Author(s): - Charles Willis - October 2020 +- Heath Stewart - September 2021 --*/ @@ -37,27 +38,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model public: struct VsSetupInstance { + VsSetupInstance(VsSetupInstance&& other) = default; + VsSetupInstance& operator=(VsSetupInstance&&) = default; + std::wstring ResolvePath(std::wstring_view relativePath) const { return VsSetupConfiguration::ResolvePath(inst.get(), relativePath); } - std::wstring GetDevShellModulePath() const - { - // The path of Microsoft.VisualStudio.DevShell.dll changed in 16.3 - if (VersionInRange(L"[16.3,")) - { - return ResolvePath(L"Common7\\Tools\\Microsoft.VisualStudio.DevShell.dll"); - } - - return ResolvePath(L"Common7\\Tools\\vsdevshell\\Microsoft.VisualStudio.DevShell.dll"); - } - - std::wstring GetDevCmdScriptPath() const - { - return ResolvePath(L"Common7\\Tools\\VsDevCmd.bat"); - } - bool VersionInRange(std::wstring_view range) const { return InstallationVersionInRange(query.get(), inst.get(), range); @@ -65,7 +53,17 @@ namespace winrt::Microsoft::Terminal::Settings::Model std::wstring GetVersion() const { - return GetInstallationVersion(inst.get()); + return VsSetupConfiguration::GetInstallationVersion(inst.get()); + } + + unsigned long long GetComparableInstallDate() const + { + return installDate; + } + + unsigned long long GetComparableVersion() const + { + return version; } std::wstring GetInstallationPath() const @@ -120,9 +118,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model friend class VsSetupConfiguration; VsSetupInstance(ComPtrSetupQuery pQuery, ComPtrSetupInstance pInstance) : - query(std::move(pQuery)), - inst(std::move(pInstance)), - profileNameSuffix(BuildProfileNameSuffix()) + query(pQuery), // Copy and AddRef the query object. + inst(std::move(pInstance)), // Take ownership of the instance object. + profileNameSuffix(BuildProfileNameSuffix()), + installDate(GetInstallDate()), + version(GetInstallationVersion()) { } @@ -131,6 +131,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model std::wstring profileNameSuffix; + // Cache oft-accessed properties used in sorting. + unsigned long long installDate; + unsigned long long version; + std::wstring BuildProfileNameSuffix() const { ComPtrCatalogPropertyStore catalogProperties = GetCatalogPropertyStore(); @@ -167,6 +171,22 @@ namespace winrt::Microsoft::Terminal::Settings::Model return GetVersion(); } + unsigned long long GetInstallDate() const + { + return VsSetupConfiguration::GetInstallDate(inst.get()); + } + + unsigned long long GetInstallationVersion() const + { + const auto helper = wil::com_query(query); + + std::wstring versionString{ GetVersion() }; + unsigned long long version{ 0 }; + + THROW_IF_FAILED(helper->ParseVersion(versionString.data(), &version)); + return version; + } + static std::wstring GetChannelNameSuffixTag(ISetupPropertyStore* instanceProperties) { std::wstring tag; @@ -229,6 +249,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model static std::wstring GetInstallationVersion(ISetupInstance* pInst); static std::wstring GetInstallationPath(ISetupInstance* pInst); static std::wstring GetInstanceId(ISetupInstance* pInst); + static unsigned long long GetInstallDate(ISetupInstance* pInst); static std::wstring GetStringProperty(ISetupPropertyStore* pProps, std::wstring_view name); }; };