From 3c044f20cfbb52b4cc78943a51790d902f6fd5de Mon Sep 17 00:00:00 2001 From: Don-Vito Date: Fri, 15 Jan 2021 20:30:11 +0200 Subject: [PATCH] Introduce startupActions in settings (#8770) Procedural solution for https://github.com/microsoft/terminal/issues/756. Introduces a `startupActions` global setting. This setting is as string with the same format as actions in command line arguments. It is used only if command line arguments were not provided (aka running pure wt.exe). The setting allows implicit new-tabs. In the case of invalid syntax we show the warning dialog and ignore the setting. The documentation PR is here: https://github.com/MicrosoftDocs/terminal/pull/217 --- doc/cascadia/profiles.schema.json | 4 ++ src/cascadia/TerminalApp/AppLogic.cpp | 46 +++++++++++++++++-- src/cascadia/TerminalApp/AppLogic.h | 5 ++ .../Resources/en-US/Resources.resw | 4 ++ .../GlobalAppSettings.cpp | 5 ++ .../TerminalSettingsModel/GlobalAppSettings.h | 1 + .../GlobalAppSettings.idl | 4 ++ .../TerminalWarnings.idl | 1 + .../TerminalSettingsModel/defaults.json | 1 + 9 files changed, 67 insertions(+), 4 deletions(-) diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index ed7136625..886e4cb5c 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -656,6 +656,10 @@ "description": "Sets the default profile. Opens by clicking the \"+\" icon or typing the key binding assigned to \"newTab\".", "type": "string" }, + "startupActions": { + "description": "Sets the list of actions to apply if no command line is provided. Uses the same format as command line arguments", + "type": "string" + }, "disabledProfileSources": { "description": "Disables all the dynamic profile generators in this list, preventing them from adding their profiles to the list of profiles on startup.", "items": { diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 12a6223dc..234a19cc5 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -43,7 +43,8 @@ static const std::array(SettingsLoadWar USES_RESOURCE(L"FailedToParseCommandJson"), USES_RESOURCE(L"FailedToWriteToSettings"), USES_RESOURCE(L"InvalidColorSchemeInCmd"), - USES_RESOURCE(L"InvalidSplitSize") + USES_RESOURCE(L"InvalidSplitSize"), + USES_RESOURCE(L"FailedToParseStartupActions") }; static const std::array(SettingsLoadErrors::ERRORS_SIZE)> settingsLoadErrorsLabels { USES_RESOURCE(L"NoProfilesText"), @@ -263,6 +264,14 @@ namespace winrt::TerminalApp::implementation _settings.GlobalSettings().ShowTabsInTitlebar(false); } + // Pay attention, that even if some command line arguments were parsed (like launch mode), + // we will not use the startup actions from settings. + // While this simplifies the logic, we might want to reconsider this behavior in the future. + if (!_hasCommandLineArguments && _hasSettingsStartupActions) + { + _root->SetStartupActions(_settingsAppArgs.GetStartupActions()); + } + _root->SetSettings(_settings, false); _root->Loaded({ this, &AppLogic::_OnLoaded }); _root->Initialized([this](auto&&, auto&&) { @@ -426,8 +435,7 @@ namespace winrt::TerminalApp::implementation // Make sure the lines of text wrap warningsTextBlock.TextWrapping(TextWrapping::Wrap); - const auto warnings = _settings.Warnings(); - for (const auto& warning : warnings) + for (const auto& warning : _warnings) { // Try looking up the warning message key for each warning. const auto warningText = _GetWarningText(warning); @@ -716,7 +724,34 @@ namespace winrt::TerminalApp::implementation return E_INVALIDARG; } - hr = _settings.Warnings().Size() == 0 ? S_OK : S_FALSE; + _warnings.clear(); + for (uint32_t i = 0; i < _settings.Warnings().Size(); i++) + { + _warnings.push_back(_settings.Warnings().GetAt(i)); + } + + _hasSettingsStartupActions = false; + const auto startupActions = _settings.GlobalSettings().StartupActions(); + if (!startupActions.empty()) + { + _settingsAppArgs.FullResetState(); + + ExecuteCommandlineArgs args{ _settings.GlobalSettings().StartupActions() }; + auto result = _settingsAppArgs.ParseArgs(args); + if (result == 0) + { + _hasSettingsStartupActions = true; + + // Validation also injects new-tab command if implicit new-tab was provided. + _settingsAppArgs.ValidateStartupCommands(); + } + else + { + _warnings.push_back(SettingsLoadWarnings::FailedToParseStartupActions); + } + } + + hr = _warnings.empty() ? S_OK : S_FALSE; } catch (const winrt::hresult_error& e) { @@ -1105,6 +1140,9 @@ namespace winrt::TerminalApp::implementation const auto result = _appArgs.ParseArgs(args); if (result == 0) { + // If the size of the arguments list is 1, + // then it contains only the executable name and no other arguments. + _hasCommandLineArguments = args.size() > 1; _appArgs.ValidateStartupCommands(); _root->SetStartupActions(_appArgs.GetStartupActions()); } diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index 1a93abcf5..02c1029ac 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -86,6 +86,7 @@ namespace winrt::TerminalApp::implementation std::atomic _settingsReloadQueued{ false }; ::TerminalApp::AppCommandlineArgs _appArgs; + ::TerminalApp::AppCommandlineArgs _settingsAppArgs; int _ParseArgs(winrt::array_view& args); void _ShowLoadErrorsDialog(const winrt::hstring& titleKey, const winrt::hstring& contentKey, HRESULT settingsLoadedResult); @@ -107,6 +108,10 @@ namespace winrt::TerminalApp::implementation void _ApplyTheme(const Windows::UI::Xaml::ElementTheme& newTheme); + bool _hasCommandLineArguments{ false }; + bool _hasSettingsStartupActions{ false }; + std::vector _warnings; + // These are events that are handled by the TerminalPage, but are // exposed through the AppLogic. This macro is used to forward the event // directly to them. diff --git a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw index 1d72c98c1..b430676ad 100644 --- a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw @@ -253,6 +253,10 @@ Found a "splitPane" command with an invalid "size". This command will be ignored. Make sure the size is between 0 and 1, exclusive. {Locked="\"splitPane\"","\"size\""} + + Failed to parse "startupActions". + {Locked="\"startupActions\""} + An optional command, with arguments, to be spawned in the new tab or pane diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp index 928f00ad4..4d2d904f8 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp @@ -40,6 +40,7 @@ static constexpr std::string_view AlwaysOnTopKey{ "alwaysOnTop" }; static constexpr std::string_view LegacyUseTabSwitcherModeKey{ "useTabSwitcher" }; static constexpr std::string_view TabSwitcherModeKey{ "tabSwitcherMode" }; static constexpr std::string_view DisableAnimationsKey{ "disableAnimations" }; +static constexpr std::string_view StartupActionsKey{ "startupActions" }; static constexpr std::string_view DebugFeaturesKey{ "debugFeatures" }; @@ -114,6 +115,7 @@ winrt::com_ptr GlobalAppSettings::Copy() const globals->_AlwaysOnTop = _AlwaysOnTop; globals->_TabSwitcherMode = _TabSwitcherMode; globals->_DisableAnimations = _DisableAnimations; + globals->_StartupActions = _StartupActions; globals->_UnparsedDefaultProfile = _UnparsedDefaultProfile; globals->_validDefaultProfile = _validDefaultProfile; @@ -300,6 +302,8 @@ void GlobalAppSettings::LayerJson(const Json::Value& json) JsonUtils::GetValueForKey(json, DisableAnimationsKey, _DisableAnimations); + JsonUtils::GetValueForKey(json, StartupActionsKey, _StartupActions); + // This is a helper lambda to get the keybindings and commands out of both // and array of objects. We'll use this twice, once on the legacy // `keybindings` key, and again on the newer `bindings` key. @@ -394,6 +398,7 @@ Json::Value GlobalAppSettings::ToJson() const JsonUtils::SetValueForKey(json, AlwaysOnTopKey, _AlwaysOnTop); JsonUtils::SetValueForKey(json, TabSwitcherModeKey, _TabSwitcherMode); JsonUtils::SetValueForKey(json, DisableAnimationsKey, _DisableAnimations); + JsonUtils::SetValueForKey(json, StartupActionsKey, _StartupActions); // clang-format on // TODO GH#8100: keymap needs to be serialized here diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h index d4e6c23df..8e1e65942 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h @@ -86,6 +86,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation GETSET_SETTING(bool, AlwaysOnTop, false); GETSET_SETTING(Model::TabSwitcherMode, TabSwitcherMode, Model::TabSwitcherMode::InOrder); GETSET_SETTING(bool, DisableAnimations, false); + GETSET_SETTING(hstring, StartupActions, L""); private: guid _defaultProfile; diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl index 52c22f9b5..6f0fc41da 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl @@ -142,5 +142,9 @@ namespace Microsoft.Terminal.Settings.Model KeyMapping KeyMap(); Windows.Foundation.Collections.IMapView Commands(); + + Boolean HasStartupActions(); + void ClearStartupActions(); + String StartupActions(); } } diff --git a/src/cascadia/TerminalSettingsModel/TerminalWarnings.idl b/src/cascadia/TerminalSettingsModel/TerminalWarnings.idl index ff8b96369..8e808d57e 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalWarnings.idl +++ b/src/cascadia/TerminalSettingsModel/TerminalWarnings.idl @@ -20,6 +20,7 @@ namespace Microsoft.Terminal.Settings.Model FailedToWriteToSettings = 10, InvalidColorSchemeInCmd = 11, InvalidSplitSize = 12, + FailedToParseStartupActions = 13, WARNINGS_SIZE // IMPORTANT: This MUST be the last value in this enum. It's an unused placeholder. }; diff --git a/src/cascadia/TerminalSettingsModel/defaults.json b/src/cascadia/TerminalSettingsModel/defaults.json index 1d3226f27..372205ab0 100644 --- a/src/cascadia/TerminalSettingsModel/defaults.json +++ b/src/cascadia/TerminalSettingsModel/defaults.json @@ -26,6 +26,7 @@ "theme": "system", "snapToGridOnResize": true, "disableAnimations": false, + "startupActions": "", "profiles": [