diff --git a/src/cascadia/TerminalSettingsEditor/Extensions.cpp b/src/cascadia/TerminalSettingsEditor/Extensions.cpp index 1347c070d2f..1792785f93a 100644 --- a/src/cascadia/TerminalSettingsEditor/Extensions.cpp +++ b/src/cascadia/TerminalSettingsEditor/Extensions.cpp @@ -111,14 +111,25 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation _extensionSources.clear(); _CurrentExtensionSource.clear(); - std::vector fragmentExtensions; - fragmentExtensions.reserve(settings.FragmentExtensions().Size()); + std::vector extensions; + extensions.reserve(settings.FragmentExtensions().Size() + settings.DynamicProfileGenerators().Size()); + for (auto ext : settings.FragmentExtensions()) + { + extensions.push_back(ext); + } + for (auto ext : settings.DynamicProfileGenerators()) + { + extensions.push_back(ext); + } + + std::vector extensionVMs; + extensionVMs.reserve(extensions.size()); // these vectors track components all extensions successfully added std::vector profilesModifiedTotal; std::vector profilesAddedTotal; std::vector colorSchemesAddedTotal; - for (const auto&& fragExt : settings.FragmentExtensions()) + for (const auto& fragExt : extensions) { const auto extensionEnabled = GetExtensionState(fragExt.Source()); @@ -173,10 +184,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation } _extensionSources.insert(fragExt.Source()); - fragmentExtensions.push_back(winrt::make(fragExt, currentProfilesModified, currentProfilesAdded, currentColorSchemesAdded)); + extensionVMs.push_back(winrt::make(fragExt, currentProfilesModified, currentProfilesAdded, currentColorSchemesAdded)); } - _fragmentExtensions = single_threaded_observable_vector(std::move(fragmentExtensions)); + _fragmentExtensions = single_threaded_observable_vector(std::move(extensionVMs)); _profilesModifiedView = single_threaded_observable_vector(std::move(profilesModifiedTotal)); _profilesAddedView = single_threaded_observable_vector(std::move(profilesAddedTotal)); _colorSchemesAddedView = single_threaded_observable_vector(std::move(colorSchemesAddedTotal)); diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp index 8d76d42dd65..c7a5f8d2928 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp @@ -124,6 +124,17 @@ Model::CascadiaSettings CascadiaSettings::Copy() const } settings->_fragmentExtensions = winrt::single_threaded_vector(std::move(fragmentExtensions)); } + + // copy dynamic profile generators + { + std::vector dynamicProfileGenerators; + dynamicProfileGenerators.reserve(_dynamicProfileGeneratorExtensions.Size()); + for (const auto& fragment : _dynamicProfileGeneratorExtensions) + { + dynamicProfileGenerators.emplace_back(get_self(fragment)->Copy()); + } + settings->_dynamicProfileGeneratorExtensions = winrt::single_threaded_vector(std::move(dynamicProfileGenerators)); + } } // load errors @@ -220,6 +231,11 @@ IVectorView CascadiaSettings::FragmentExtensions() cons return _fragmentExtensions.GetView(); } +IVectorView CascadiaSettings::DynamicProfileGenerators() const noexcept +{ + return _dynamicProfileGeneratorExtensions.GetView(); +} + // Method Description: // - Returns the globally configured keybindings // Arguments: diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.h b/src/cascadia/TerminalSettingsModel/CascadiaSettings.h index f772303cbc8..22238092bb7 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.h +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.h @@ -74,6 +74,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation ParsedSettings inboxSettings; ParsedSettings userSettings; std::vector fragmentExtensions; + std::vector dynamicProfileGeneratorExtensions; bool duplicateProfile = false; private: @@ -132,6 +133,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::Windows::Foundation::Collections::IObservableVector ActiveProfiles() const noexcept; Model::ActionMap ActionMap() const noexcept; winrt::Windows::Foundation::Collections::IVectorView FragmentExtensions() const noexcept; + winrt::Windows::Foundation::Collections::IVectorView DynamicProfileGenerators() const noexcept; void WriteSettingsToDisk(); Json::Value ToJson() const; Model::Profile ProfileDefaults() const; @@ -190,6 +192,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::Windows::Foundation::Collections::IObservableVector _allProfiles = winrt::single_threaded_observable_vector(); winrt::Windows::Foundation::Collections::IObservableVector _activeProfiles = winrt::single_threaded_observable_vector(); winrt::Windows::Foundation::Collections::IVector _fragmentExtensions = winrt::single_threaded_vector(); + winrt::Windows::Foundation::Collections::IVector _dynamicProfileGeneratorExtensions = winrt::single_threaded_vector(); std::set _themesChangeLog{}; // load errors diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.idl b/src/cascadia/TerminalSettingsModel/CascadiaSettings.idl index 25b65e1ad18..5671ed21d42 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.idl +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.idl @@ -45,6 +45,7 @@ namespace Microsoft.Terminal.Settings.Model ActionMap ActionMap { get; }; Windows.Foundation.Collections.IVectorView FragmentExtensions { get; }; + Windows.Foundation.Collections.IVectorView DynamicProfileGenerators { get; }; IVectorView Warnings { get; }; Windows.Foundation.IReference GetLoadingError { get; }; diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp index 0997a37a966..9ec727253a8 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp @@ -991,31 +991,51 @@ bool SettingsLoader::_addOrMergeUserColorScheme(const winrt::com_ptr> generatedProfiles; try { - generator.GenerateProfiles(inboxSettings.profiles); + generator.GenerateProfiles(generatedProfiles); } CATCH_LOG_MSG("Dynamic Profile Namespace: \"%.*s\"", gsl::narrow(generatorNamespace.size()), generatorNamespace.data()) + // These are needed for the FragmentSettings object + std::vector profileEntries; + Json::Value profilesListJson{ Json::ValueType::arrayValue }; + Json::StreamWriterBuilder styledWriter; + styledWriter["indentation"] = " "; + // If the generator produced some profiles we're going to give them default attributes. // By setting the Origin/Source/etc. here, we deduplicate some code and ensure they aren't missing accidentally. - if (inboxSettings.profiles.size() > previousSize) + const winrt::hstring source{ generatorNamespace }; + for (const auto& profile : generatedProfiles) { - const winrt::hstring source{ generatorNamespace }; + profile->Origin(OriginTag::Generated); + profile->Source(source); + + const auto profileJson = profile->ToJson(); + profilesListJson.append(profileJson); + profileEntries.push_back(winrt::make(profile->Guid(), hstring{ til::u8u16(Json::writeString(styledWriter, profileJson)) })); + } - for (const auto& profile : std::span(inboxSettings.profiles).subspan(previousSize)) + if (!_ignoredNamespaces.contains(generatorNamespace)) + { + // Add generated profiles to the user settings + for (auto& profile : generatedProfiles) { - profile->Origin(OriginTag::Generated); - profile->Source(source); + inboxSettings.profiles.push_back(profile); } } + + // Manually construct the JSON for the FragmentSettings object + Json::Value json{ Json::ValueType::objectValue }; + json[JsonKey(ProfilesKey)] = profilesListJson; + + auto generatorExtension = winrt::make_self(hstring{ generatorNamespace }, hstring{ til::u8u16(Json::writeString(styledWriter, json)) }, hstring{ L"settings.json" }, FragmentScope::Machine); + for (const auto& entry : profileEntries) + { + generatorExtension->NewProfiles().Append(entry); + } + dynamicProfileGeneratorExtensions.emplace_back(*generatorExtension); } // Method Description: @@ -1308,6 +1328,7 @@ CascadiaSettings::CascadiaSettings(SettingsLoader&& loader) : _warnings = winrt::single_threaded_vector(std::move(warnings)); _themesChangeLog = std::move(loader.userSettings.themesChangeLog); _fragmentExtensions = winrt::single_threaded_vector(std::move(loader.fragmentExtensions)); + _dynamicProfileGeneratorExtensions = winrt::single_threaded_vector(std::move(loader.dynamicProfileGeneratorExtensions)); _resolveDefaultProfile(); _resolveNewTabMenuProfiles();