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); }; };