b6ec670bd8
Fixes #4155. ## Validation steps ``` Summary: Total=23, Passed=22, Failed=1, Blocked=0, Not Run=0, Skipped=0 ``` The failing test is the same one as before. It is not germane to this pull request.
572 lines
23 KiB
C++
572 lines
23 KiB
C++
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT license.
|
|
|
|
#include "pch.h"
|
|
#include "AppCommandlineArgs.h"
|
|
#include "ActionArgs.h"
|
|
#include <LibraryResources.h>
|
|
|
|
using namespace winrt::TerminalApp;
|
|
using namespace TerminalApp;
|
|
|
|
// Either a ; at the start of a line, or a ; preceeded by any non-\ char.
|
|
const std::wregex AppCommandlineArgs::_commandDelimiterRegex{ LR"(^;|[^\\];)" };
|
|
|
|
AppCommandlineArgs::AppCommandlineArgs()
|
|
{
|
|
_buildParser();
|
|
_resetStateToDefault();
|
|
}
|
|
|
|
// Method Description:
|
|
// - Attempt to parse a given command as a single commandline. If the command
|
|
// doesn't have a subcommand, we'll try parsing the commandline again, as a
|
|
// new-tab command.
|
|
// - Actions generated by this command are added to our _startupActions list.
|
|
// Arguments:
|
|
// - command: The individual commandline to parse as a command.
|
|
// Return Value:
|
|
// - 0 if the commandline was successfully parsed
|
|
// - nonzero return values are defined in CLI::ExitCodes
|
|
int AppCommandlineArgs::ParseCommand(const Commandline& command)
|
|
{
|
|
const int argc = static_cast<int>(command.Argc());
|
|
|
|
// Stash a pointer to the current Commandline instance we're parsing.
|
|
// When we're trying to parse the commandline for a new-tab/split-pane
|
|
// subcommand, we'll need to inspect the original Args from this
|
|
// Commandline to find the entirety of the commandline args for the new
|
|
// terminal instance. Discard the pointer when we leave this method. The
|
|
// pointer will be safe for usage, since the parse callback will be
|
|
// executed on the same thread, higher on the stack.
|
|
_currentCommandline = &command;
|
|
auto clearPointer = wil::scope_exit([this]() { _currentCommandline = nullptr; });
|
|
try
|
|
{
|
|
// CLI11 needs a mutable vector<string>, so copy out the args here.
|
|
// * When we're using the vector<string> parse(), it also expects that
|
|
// there isn't a leading executable name in the args, so slice that
|
|
// out.
|
|
// - In AppCommandlineArgs::BuildCommands, we'll make sure each
|
|
// subsequent command in a single commandline starts with a wt.exe.
|
|
// Our very first argument might not be "wt.exe", it could be `wt`,
|
|
// or `wtd.exe`, etc. Regardless, we want to ignore the first arg of
|
|
// every Commandline
|
|
// * Not only that, but this particular overload of parse() wants the
|
|
// args _reversed_ here.
|
|
std::vector<std::string> args{ command.Args().begin() + 1, command.Args().end() };
|
|
std::reverse(args.begin(), args.end());
|
|
|
|
// Revert our state to the initial state. As this function can be called
|
|
// multiple times during the parsing of a single commandline (once for each
|
|
// sub-command), we don't want the leftover state from previous calls to
|
|
// pollute this run's state.
|
|
_resetStateToDefault();
|
|
|
|
// Manually check for the "/?" or "-?" flags, to manually trigger the help text.
|
|
if (argc == 2 && (NixHelpFlag == til::at(command.Args(), 1) || WindowsHelpFlag == til::at(command.Args(), 1)))
|
|
{
|
|
throw CLI::CallForHelp();
|
|
}
|
|
// Clear the parser's internal state
|
|
_app.clear();
|
|
|
|
// attempt to parse the commandline
|
|
_app.parse(args);
|
|
|
|
// If we parsed the commandline, and _no_ subcommands were provided, try
|
|
// parsing again as a "new-tab" command.
|
|
|
|
if (_noCommandsProvided())
|
|
{
|
|
_newTabCommand.subcommand->clear();
|
|
_newTabCommand.subcommand->parse(args);
|
|
}
|
|
}
|
|
catch (const CLI::CallForHelp& e)
|
|
{
|
|
return _handleExit(_app, e);
|
|
}
|
|
catch (const CLI::ParseError& e)
|
|
{
|
|
// If we parsed the commandline, and _no_ subcommands were provided, try
|
|
// parsing again as a "new-tab" command.
|
|
if (_noCommandsProvided())
|
|
{
|
|
try
|
|
{
|
|
// CLI11 mutated the original vector the first time it tried to
|
|
// parse the args. Reconstruct it the way CLI11 wants here.
|
|
// "See above for why it's begin() + 1"
|
|
std::vector<std::string> args{ command.Args().begin() + 1, command.Args().end() };
|
|
std::reverse(args.begin(), args.end());
|
|
_newTabCommand.subcommand->clear();
|
|
_newTabCommand.subcommand->parse(args);
|
|
}
|
|
catch (const CLI::ParseError& e)
|
|
{
|
|
return _handleExit(*_newTabCommand.subcommand, e);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return _handleExit(_app, e);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Method Description:
|
|
// - Calls App::exit() for the provided command, and collects it's output into
|
|
// our _exitMessage buffer.
|
|
// Arguments:
|
|
// - command: Either the root App object, or a subcommand for which to call exit() on.
|
|
// - e: the CLI::Error to process as the exit reason for parsing.
|
|
// Return Value:
|
|
// - 0 if the command exited successfully
|
|
// - nonzero return values are defined in CLI::ExitCodes
|
|
int AppCommandlineArgs::_handleExit(const CLI::App& command, const CLI::Error& e)
|
|
{
|
|
// Create some streams to collect the output that would otherwise go to stdout.
|
|
std::ostringstream out;
|
|
std::ostringstream err;
|
|
const auto result = command.exit(e, out, err);
|
|
// I believe only CallForHelp will return 0
|
|
if (result == 0)
|
|
{
|
|
_exitMessage = out.str();
|
|
}
|
|
else
|
|
{
|
|
_exitMessage = err.str();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// Method Description:
|
|
// - Add each subcommand and options to the commandline parser.
|
|
// Arguments:
|
|
// - <none>
|
|
// Return Value:
|
|
// - <none>
|
|
void AppCommandlineArgs::_buildParser()
|
|
{
|
|
_buildNewTabParser();
|
|
_buildSplitPaneParser();
|
|
_buildFocusTabParser();
|
|
}
|
|
|
|
// Method Description:
|
|
// - Adds the `new-tab` subcommand and related options to the commandline parser.
|
|
// Arguments:
|
|
// - <none>
|
|
// Return Value:
|
|
// - <none>
|
|
void AppCommandlineArgs::_buildNewTabParser()
|
|
{
|
|
_newTabCommand.subcommand = _app.add_subcommand("new-tab", RS_A(L"CmdNewTabDesc"));
|
|
_addNewTerminalArgs(_newTabCommand);
|
|
|
|
// When ParseCommand is called, if this subcommand was provided, this
|
|
// callback function will be triggered on the same thread. We can be sure
|
|
// that `this` will still be safe - this function just lets us know this
|
|
// command was parsed.
|
|
_newTabCommand.subcommand->callback([&, this]() {
|
|
// Buld the NewTab action from the values we've parsed on the commandline.
|
|
auto newTabAction = winrt::make_self<implementation::ActionAndArgs>();
|
|
newTabAction->Action(ShortcutAction::NewTab);
|
|
auto args = winrt::make_self<implementation::NewTabArgs>();
|
|
// _getNewTerminalArgs MUST be called before parsing any other options,
|
|
// as it might clear those options while finding the commandline
|
|
args->TerminalArgs(_getNewTerminalArgs(_newTabCommand));
|
|
newTabAction->Args(*args);
|
|
_startupActions.push_back(*newTabAction);
|
|
});
|
|
}
|
|
|
|
// Method Description:
|
|
// - Adds the `split-pane` subcommand and related options to the commandline parser.
|
|
// Arguments:
|
|
// - <none>
|
|
// Return Value:
|
|
// - <none>
|
|
void AppCommandlineArgs::_buildSplitPaneParser()
|
|
{
|
|
_newPaneCommand.subcommand = _app.add_subcommand("split-pane", RS_A(L"CmdSplitPaneDesc"));
|
|
_addNewTerminalArgs(_newPaneCommand);
|
|
_horizontalOption = _newPaneCommand.subcommand->add_flag("-H,--horizontal",
|
|
_splitHorizontal,
|
|
RS_A(L"CmdSplitPaneHorizontalArgDesc"));
|
|
_verticalOption = _newPaneCommand.subcommand->add_flag("-V,--vertical",
|
|
_splitVertical,
|
|
RS_A(L"CmdSplitPaneVerticalArgDesc"));
|
|
_verticalOption->excludes(_horizontalOption);
|
|
|
|
// When ParseCommand is called, if this subcommand was provided, this
|
|
// callback function will be triggered on the same thread. We can be sure
|
|
// that `this` will still be safe - this function just lets us know this
|
|
// command was parsed.
|
|
_newPaneCommand.subcommand->callback([&, this]() {
|
|
// Buld the SplitPane action from the values we've parsed on the commandline.
|
|
auto splitPaneActionAndArgs = winrt::make_self<implementation::ActionAndArgs>();
|
|
splitPaneActionAndArgs->Action(ShortcutAction::SplitPane);
|
|
auto args = winrt::make_self<implementation::SplitPaneArgs>();
|
|
// _getNewTerminalArgs MUST be called before parsing any other options,
|
|
// as it might clear those options while finding the commandline
|
|
args->TerminalArgs(_getNewTerminalArgs(_newPaneCommand));
|
|
args->SplitStyle(SplitState::Automatic);
|
|
// Make sure to use the `Option`s here to check if they were set -
|
|
// _getNewTerminalArgs might reset them while parsing a commandline
|
|
if ((*_horizontalOption || *_verticalOption) && (_splitHorizontal))
|
|
{
|
|
if (_splitHorizontal)
|
|
{
|
|
args->SplitStyle(SplitState::Horizontal);
|
|
}
|
|
else if (_splitVertical)
|
|
{
|
|
args->SplitStyle(SplitState::Horizontal);
|
|
}
|
|
}
|
|
|
|
splitPaneActionAndArgs->Args(*args);
|
|
_startupActions.push_back(*splitPaneActionAndArgs);
|
|
});
|
|
}
|
|
|
|
// Method Description:
|
|
// - Adds the `new-tab` subcommand and related options to the commandline parser.
|
|
// Arguments:
|
|
// - <none>
|
|
// Return Value:
|
|
// - <none>
|
|
void AppCommandlineArgs::_buildFocusTabParser()
|
|
{
|
|
_focusTabCommand = _app.add_subcommand("focus-tab", RS_A(L"CmdFocusTabDesc"));
|
|
auto* indexOpt = _focusTabCommand->add_option("-t,--target", _focusTabIndex, RS_A(L"CmdFocusTabTargetArgDesc"));
|
|
auto* nextOpt = _focusTabCommand->add_flag("-n,--next",
|
|
_focusNextTab,
|
|
RS_A(L"CmdFocusTabNextArgDesc"));
|
|
auto* prevOpt = _focusTabCommand->add_flag("-p,--previous",
|
|
_focusPrevTab,
|
|
RS_A(L"CmdFocusTabPrevArgDesc"));
|
|
nextOpt->excludes(prevOpt);
|
|
indexOpt->excludes(prevOpt);
|
|
indexOpt->excludes(nextOpt);
|
|
|
|
// When ParseCommand is called, if this subcommand was provided, this
|
|
// callback function will be triggered on the same thread. We can be sure
|
|
// that `this` will still be safe - this function just lets us know this
|
|
// command was parsed.
|
|
_focusTabCommand->callback([&, this]() {
|
|
// Buld the action from the values we've parsed on the commandline.
|
|
auto focusTabAction = winrt::make_self<implementation::ActionAndArgs>();
|
|
|
|
if (_focusTabIndex >= 0)
|
|
{
|
|
focusTabAction->Action(ShortcutAction::SwitchToTab);
|
|
auto args = winrt::make_self<implementation::SwitchToTabArgs>();
|
|
args->TabIndex(_focusTabIndex);
|
|
focusTabAction->Args(*args);
|
|
_startupActions.push_back(*focusTabAction);
|
|
}
|
|
else if (_focusNextTab || _focusPrevTab)
|
|
{
|
|
focusTabAction->Action(_focusNextTab ? ShortcutAction::NextTab : ShortcutAction::PrevTab);
|
|
_startupActions.push_back(*focusTabAction);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Method Description:
|
|
// - Add the `NewTerminalArgs` parameters to the given subcommand. This enables
|
|
// that subcommand to support all the properties in a NewTerminalArgs.
|
|
// Arguments:
|
|
// - subcommand: the command to add the args to.
|
|
// Return Value:
|
|
// - <none>
|
|
void AppCommandlineArgs::_addNewTerminalArgs(AppCommandlineArgs::NewTerminalSubcommand& subcommand)
|
|
{
|
|
subcommand.profileNameOption = subcommand.subcommand->add_option("-p,--profile",
|
|
_profileName,
|
|
RS_A(L"CmdProfileArgDesc"));
|
|
subcommand.startingDirectoryOption = subcommand.subcommand->add_option("-d,--startingDirectory",
|
|
_startingDirectory,
|
|
RS_A(L"CmdStartingDirArgDesc"));
|
|
|
|
// Using positionals_at_end allows us to support "wt new-tab -d wsl -d Ubuntu"
|
|
// without CLI11 thinking that we've specified -d twice.
|
|
// There's an alternate construction where we make all subcommands "prefix commands",
|
|
// which lets us get all remaining non-option args provided at the end, but that
|
|
// doesn't support "wt new-tab -- wsl -d Ubuntu -- sleep 10" because the first
|
|
// -- breaks out of the subcommand (instead of the subcommand options).
|
|
// See https://github.com/CLIUtils/CLI11/issues/417 for more info.
|
|
subcommand.commandlineOption = subcommand.subcommand->add_option("command", _commandline, RS_A(L"CmdCommandArgDesc"));
|
|
subcommand.subcommand->positionals_at_end(true);
|
|
}
|
|
|
|
// Method Description:
|
|
// - Build a NewTerminalArgs instance from the data we've parsed
|
|
// Arguments:
|
|
// - <none>
|
|
// Return Value:
|
|
// - A fully initialized NewTerminalArgs corresponding to values we've currently parsed.
|
|
NewTerminalArgs AppCommandlineArgs::_getNewTerminalArgs(AppCommandlineArgs::NewTerminalSubcommand& subcommand)
|
|
{
|
|
auto args = winrt::make_self<implementation::NewTerminalArgs>();
|
|
|
|
if (!_commandline.empty())
|
|
{
|
|
std::ostringstream cmdlineBuffer;
|
|
|
|
for (const auto& arg : _commandline)
|
|
{
|
|
if (cmdlineBuffer.tellp() != 0)
|
|
{
|
|
// If there's already something in here, prepend a space
|
|
cmdlineBuffer << ' ';
|
|
}
|
|
|
|
if (arg.find(" ") != std::string::npos)
|
|
{
|
|
cmdlineBuffer << '"' << arg << '"';
|
|
}
|
|
else
|
|
{
|
|
cmdlineBuffer << arg;
|
|
}
|
|
}
|
|
|
|
args->Commandline(winrt::to_hstring(cmdlineBuffer.str()));
|
|
}
|
|
|
|
if (*subcommand.profileNameOption)
|
|
{
|
|
args->Profile(winrt::to_hstring(_profileName));
|
|
}
|
|
|
|
if (*subcommand.startingDirectoryOption)
|
|
{
|
|
args->StartingDirectory(winrt::to_hstring(_startingDirectory));
|
|
}
|
|
|
|
return *args;
|
|
}
|
|
|
|
// Method Description:
|
|
// - This function should return true if _no_ subcommands were parsed from the
|
|
// given commandline. In that case, we'll fall back to trying the commandline
|
|
// as a new tab command.
|
|
// Arguments:
|
|
// - <none>
|
|
// Return Value:
|
|
// - true if no sub commands were parsed.
|
|
bool AppCommandlineArgs::_noCommandsProvided()
|
|
{
|
|
return !(*_newTabCommand.subcommand ||
|
|
*_focusTabCommand ||
|
|
*_newPaneCommand.subcommand);
|
|
}
|
|
|
|
// Method Description:
|
|
// - Reset any state we might have accumulated back to its default values. Since
|
|
// we'll be re-using these members across the parsing of many commandlines, we
|
|
// need to make sure the state from one run doesn't pollute the following one.
|
|
// Arguments:
|
|
// - <none>
|
|
// Return Value:
|
|
// - <none>
|
|
void AppCommandlineArgs::_resetStateToDefault()
|
|
{
|
|
_profileName.clear();
|
|
_startingDirectory.clear();
|
|
_commandline.clear();
|
|
|
|
_splitVertical = false;
|
|
_splitHorizontal = false;
|
|
|
|
_focusTabIndex = -1;
|
|
_focusNextTab = false;
|
|
_focusPrevTab = false;
|
|
}
|
|
|
|
// Function Description:
|
|
// - Builds a list of Commandline objects for the given argc,argv. Each
|
|
// Commandline represents a single command to parse. These commands can be
|
|
// seperated by ";", which indicates the start of the next commandline. If the
|
|
// user would like to provide ';' in the text of the commandline, they can
|
|
// escape it as "\;".
|
|
// Arguments:
|
|
// - args: an array of arguments to parse into Commandlines
|
|
// Return Value:
|
|
// - a list of Commandline objects, where each one represents a single
|
|
// commandline to parse.
|
|
std::vector<Commandline> AppCommandlineArgs::BuildCommands(winrt::array_view<const winrt::hstring>& args)
|
|
{
|
|
std::vector<Commandline> commands;
|
|
commands.emplace_back(Commandline{});
|
|
|
|
// For each arg in argv:
|
|
// Check the string for a delimiter.
|
|
// * If there isn't a delimiter, add the arg to the current commandline.
|
|
// * If there is a delimiter, split the string at that delimiter. Add the
|
|
// first part of the string to the current command, and start a new
|
|
// command with the second bit.
|
|
for (const auto& arg : args)
|
|
{
|
|
_addCommandsForArg(commands, { arg });
|
|
}
|
|
|
|
return commands;
|
|
}
|
|
|
|
// Function Description:
|
|
// - Builds a list of Commandline objects for the given argc,argv. Each
|
|
// Commandline represents a single command to parse. These commands can be
|
|
// seperated by ";", which indicates the start of the next commandline. If the
|
|
// user would like to provide ';' in the text of the commandline, they can
|
|
// escape it as "\;".
|
|
// Arguments:
|
|
// - argc: the number of arguments provided in argv
|
|
// - argv: a c-style array of wchar_t strings. These strings can include spaces in them.
|
|
// Return Value:
|
|
// - a list of Commandline objects, where each one represents a single
|
|
// commandline to parse.
|
|
std::vector<Commandline> AppCommandlineArgs::BuildCommands(const std::vector<const wchar_t*>& args)
|
|
{
|
|
std::vector<Commandline> commands;
|
|
// Initialize a first Commandline without a leading `wt.exe` argument. When
|
|
// we're run from the commandline, `wt.exe` (or whatever the exe's name is)
|
|
// will be the first argument passed to us
|
|
commands.resize(1);
|
|
|
|
// For each arg in argv:
|
|
// Check the string for a delimiter.
|
|
// * If there isn't a delimiter, add the arg to the current commandline.
|
|
// * If there is a delimiter, split the string at that delimiter. Add the
|
|
// first part of the string to the current command, ansd start a new
|
|
// command with the second bit.
|
|
for (const auto& arg : args)
|
|
{
|
|
_addCommandsForArg(commands, { arg });
|
|
}
|
|
|
|
return commands;
|
|
}
|
|
|
|
// Function Description:
|
|
// - Update and append Commandline objects for the given arg to the given list
|
|
// of commands. Each Commandline represents a single command to parse. These
|
|
// commands can be seperated by ";", which indicates the start of the next
|
|
// commandline. If the user would like to provide ';' in the text of the
|
|
// commandline, they can escape it as "\;".
|
|
// - As we parse arg, if it doesn't contain a delimiter in it, we'll add it to
|
|
// the last command in commands. Otherwise, we'll generate a new Commandline
|
|
// object for each command in arg.
|
|
// Arguments:
|
|
// - commands: a list of Commandline objects to modify and append to
|
|
// - arg: a single argument that should be parsed into args to append to the
|
|
// current command, or create more Commandlines
|
|
// Return Value:
|
|
// <none>
|
|
void AppCommandlineArgs::_addCommandsForArg(std::vector<Commandline>& commands, std::wstring_view arg)
|
|
{
|
|
std::wstring remaining{ arg };
|
|
std::wsmatch match;
|
|
// Keep looking for matches until we've found no unescaped delimiters,
|
|
// or we've hit the end of the string.
|
|
std::regex_search(remaining, match, AppCommandlineArgs::_commandDelimiterRegex);
|
|
do
|
|
{
|
|
if (match.empty())
|
|
{
|
|
// Easy case: no delimiter. Add it to the current command.
|
|
commands.back().AddArg(remaining);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// Harder case: There was a match.
|
|
const bool matchedFirstChar = match.position(0) == 0;
|
|
// If the match was at the beginning of the string, then the
|
|
// next arg should be "", since there was no content before the
|
|
// delimiter. Otherwise, add one, since the regex will include
|
|
// the last character of the string before the delimiter.
|
|
const auto delimiterPosition = matchedFirstChar ? match.position(0) : match.position(0) + 1;
|
|
const auto nextArg = remaining.substr(0, delimiterPosition);
|
|
|
|
if (!nextArg.empty())
|
|
{
|
|
commands.back().AddArg(nextArg);
|
|
}
|
|
|
|
// Create a new commandline
|
|
commands.emplace_back(Commandline{});
|
|
// Initialize it with "wt.exe" as the first arg, as if that command
|
|
// was passed individually by the user on the commandline.
|
|
commands.back().AddArg(std::wstring{ AppCommandlineArgs::PlaceholderExeName });
|
|
|
|
// Look for the next match in the string, but updating our
|
|
// remaining to be the text after the match.
|
|
remaining = match.suffix().str();
|
|
std::regex_search(remaining, match, AppCommandlineArgs::_commandDelimiterRegex);
|
|
}
|
|
} while (!remaining.empty());
|
|
}
|
|
|
|
// Method Description:
|
|
// - Returns the deque of actions we've buffered as a result of parsing commands.
|
|
// Arguments:
|
|
// - <none>
|
|
// Return Value:
|
|
// - the deque of actions we've buffered as a result of parsing commands.
|
|
std::deque<winrt::TerminalApp::ActionAndArgs>& AppCommandlineArgs::GetStartupActions()
|
|
{
|
|
return _startupActions;
|
|
}
|
|
|
|
// Method Description:
|
|
// - Get the string of text that should be displayed to the user on exit. This
|
|
// is usually helpful for cases where the user entered some sort of invalid
|
|
// commandline. It's additionally also used when the user has requested the
|
|
// help text.
|
|
// Arguments:
|
|
// - <none>
|
|
// Return Value:
|
|
// - The help text, or an error message, generated from parsing the input
|
|
// provided by the user.
|
|
const std::string& AppCommandlineArgs::GetExitMessage()
|
|
{
|
|
return _exitMessage;
|
|
}
|
|
|
|
// Method Description:
|
|
// - Ensure that the first command in our list of actions is a NewTab action.
|
|
// This makes sure that if the user passes a commandline like "wt split-pane
|
|
// -H", we _first_ create a new tab, so there's always at least one tab.
|
|
// - If the first command in our queue of actions is a NewTab action, this does
|
|
// nothing.
|
|
// - This should only be called once - if the first NewTab action is popped from
|
|
// our _startupActions, calling this again will add another.
|
|
// Arguments:
|
|
// - <none>
|
|
// Return Value:
|
|
// - <none>
|
|
void AppCommandlineArgs::ValidateStartupCommands()
|
|
{
|
|
// If we parsed no commands, or the first command we've parsed is not a new
|
|
// tab action, prepend a new-tab command to the front of the list.
|
|
if (_startupActions.empty() ||
|
|
_startupActions.front().Action() != ShortcutAction::NewTab)
|
|
{
|
|
// Build the NewTab action from the values we've parsed on the commandline.
|
|
auto newTabAction = winrt::make_self<implementation::ActionAndArgs>();
|
|
newTabAction->Action(ShortcutAction::NewTab);
|
|
auto args = winrt::make_self<implementation::NewTabArgs>();
|
|
auto newTerminalArgs = winrt::make_self<implementation::NewTerminalArgs>();
|
|
args->TerminalArgs(*newTerminalArgs);
|
|
newTabAction->Args(*args);
|
|
_startupActions.push_front(*newTabAction);
|
|
}
|
|
}
|