-
Notifications
You must be signed in to change notification settings - Fork 9.2k
Expand file tree
/
Copy pathAppCommandlineArgs.cpp
More file actions
628 lines (573 loc) · 25.5 KB
/
AppCommandlineArgs.cpp
File metadata and controls
628 lines (573 loc) · 25.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "AppLogic.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 ; preceded 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();
}
// We're displaying an error message - we should always exit instead of
// actually starting the Terminal.
_shouldExitEarly = true;
return result;
}
// Method Description:
// - Add each subcommand and options to the commandline parser.
// Arguments:
// - <none>
// Return Value:
// - <none>
void AppCommandlineArgs::_buildParser()
{
// -v,--version
auto versionCallback = [this](int64_t /*count*/) {
if (const auto appLogic{ winrt::TerminalApp::implementation::AppLogic::Current() })
{
// Set our message to display the application name and the current version.
_exitMessage = fmt::format("{0}\n{1}",
til::u16u8(appLogic->ApplicationDisplayName()),
til::u16u8(appLogic->ApplicationVersion()));
// Theoretically, we don't need to exit now, since this isn't really
// an error case. However, in practice, it feels weird to have `wt
// -v` open a new tab, and makes enough sense that `wt -v ;
// split-pane` (or whatever) just displays the version and exits.
_shouldExitEarly = true;
}
};
_app.add_flag_function("-v,--version", versionCallback, RS_A(L"CmdVersionDesc"));
// Maximized and Fullscreen flags
auto maximizedCallback = [this](int64_t /*count*/) {
_launchMode = winrt::TerminalApp::LaunchMode::MaximizedMode;
};
auto fullscreenCallback = [this](int64_t /*count*/) {
_launchMode = winrt::TerminalApp::LaunchMode::FullscreenMode;
};
auto maximized = _app.add_flag_function("-M,--maximized", maximizedCallback, RS_A(L"CmdMaximizedDesc"));
auto fullscreen = _app.add_flag_function("-F,--fullscreen", fullscreenCallback, RS_A(L"CmdFullscreenDesc"));
maximized->excludes(fullscreen);
// Subcommands
_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]() {
// 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>();
// _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]() {
// Build 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))
{
if (_splitHorizontal)
{
args->SplitStyle(SplitState::Horizontal);
}
else if (_splitVertical)
{
args->SplitStyle(SplitState::Vertical);
}
}
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]() {
// Build 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;
// DON'T clear _launchMode here! This will get called once for every
// subcommand, so we don't want `wt -F new-tab ; split-pane` clearing out
// the "global" fullscreen flag (-F).
}
// 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
// separated 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
// separated 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 separated 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:
// - Returns true if we should exit the application before even starting the
// window. We might want to do this if we're displaying an error message or
// the version string, or if we want to open the settings file.
// Arguments:
// - <none>
// Return Value:
// - true iff we should exit the application before even starting the window
bool AppCommandlineArgs::ShouldExitEarly() const noexcept
{
return _shouldExitEarly;
}
// 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);
}
}
std::optional<winrt::TerminalApp::LaunchMode> AppCommandlineArgs::GetLaunchMode() const noexcept
{
return _launchMode;
}