Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce parsed command line text to command palette #8515

Merged
8 commits merged into from
Dec 16, 2020
72 changes: 72 additions & 0 deletions src/cascadia/TerminalApp/AppCommandlineArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -720,3 +720,75 @@ int AppCommandlineArgs::ParseArgs(winrt::array_view<const winrt::hstring>& args)
// built in _appArgs, which we'll use when the application starts up.
return 0;
}

// Method Description:
// - Attempts to parse an array of commandline args into a list of
// commands to execute, and then parses these commands. As commands are
// successfully parsed, they will generate ShortcutActions for us to be
// able to execute. If we fail to parse any commands, we'll return the
// error code from the failure to parse that command, and stop processing
// additional commands.
// - The first arg in args should be the program name "wt" (or some variant). It
// will be ignored during parsing.
// Arguments:
// - args: ExecuteCommandlineArgs describing the command line to parse
// Return Value:
// - 0 if the commandline was successfully parsed
int AppCommandlineArgs::ParseArgs(const winrt::Microsoft::Terminal::Settings::Model::ExecuteCommandlineArgs& args)
{
if (!args || args.Commandline().empty())
{
return 0;
}

// Convert the commandline into an array of args with
// CommandLineToArgvW, similar to how the app typically does when
// called from the commandline.
int argc = 0;
wil::unique_any<LPWSTR*, decltype(&::LocalFree), ::LocalFree> argv{ CommandLineToArgvW(args.Commandline().c_str(), &argc) };
if (argv)
{
std::vector<winrt::hstring> args;

// Make sure the first argument is wt.exe, because ParseArgs will
// always skip the program name. The particular value of this first
// string doesn't terribly matter.
args.emplace_back(L"wt.exe");
for (auto& elem : wil::make_range(argv.get(), argc))
{
args.emplace_back(elem);
}
winrt::array_view<const winrt::hstring> argsView{ args };
return ParseArgs(argsView);
}
return 0;
}

// Method Description:
// - Allows disabling addition of help-related info in the exit message
// Arguments:
// - <none>
// Return Value:
// - <none>
void AppCommandlineArgs::DisableHelpInExitMessage()
{
_app.set_help_flag();
_app.set_help_all_flag();
}

// Method Description:
// - Resets the state to allow external consumers to reuse this instance
// Arguments:
// - <none>
// Return Value:
// - <none>
void AppCommandlineArgs::FullResetState()
{
_resetStateToDefault();

_currentCommandline = nullptr;
_launchMode = std::nullopt;
_startupActions.clear();
_exitMessage = "";
_shouldExitEarly = false;
}
11 changes: 7 additions & 4 deletions src/cascadia/TerminalApp/AppCommandlineArgs.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ class TerminalApp::AppCommandlineArgs final

std::optional<winrt::Microsoft::Terminal::Settings::Model::LaunchMode> GetLaunchMode() const noexcept;

int ParseArgs(const winrt::Microsoft::Terminal::Settings::Model::ExecuteCommandlineArgs& args);
void DisableHelpInExitMessage();
void FullResetState();

private:
static const std::wregex _commandDelimiterRegex;

Expand Down Expand Up @@ -80,21 +84,20 @@ class TerminalApp::AppCommandlineArgs final
// _commandline will contain the command line with which we'll be spawning a new terminal
std::vector<std::string> _commandline;

const Commandline* _currentCommandline{ nullptr };

bool _splitVertical{ false };
bool _splitHorizontal{ false };

int _focusTabIndex{ -1 };
bool _focusNextTab{ false };
bool _focusPrevTab{ false };

std::optional<winrt::Microsoft::Terminal::Settings::Model::LaunchMode> _launchMode{ std::nullopt };
// Are you adding more args here? Make sure to reset them in _resetStateToDefault

const Commandline* _currentCommandline{ nullptr };
std::optional<winrt::Microsoft::Terminal::Settings::Model::LaunchMode> _launchMode{ std::nullopt };
std::vector<winrt::Microsoft::Terminal::Settings::Model::ActionAndArgs> _startupActions;
std::string _exitMessage;
bool _shouldExitEarly{ false };
// Are you adding more args or attributes here? If they are not reset in _resetStateToDefault, make sure to reset them in FullResetState

winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs _getNewTerminalArgs(NewTerminalSubcommand& subcommand);
void _addNewTerminalArgs(NewTerminalSubcommand& subcommand);
Expand Down
34 changes: 33 additions & 1 deletion src/cascadia/TerminalApp/CommandPalette.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
#include "TabPaletteItem.h"
#include "CommandLinePaletteItem.h"
#include "CommandPalette.h"

#include <LibraryResources.h>

#include "CommandPalette.g.cpp"
Expand Down Expand Up @@ -99,6 +98,8 @@ namespace winrt::TerminalApp::implementation
});

_filteredActionsView().SelectionChanged({ this, &CommandPalette::_selectedCommandChanged });

_appArgs.DisableHelpInExitMessage();
}

// Method Description:
Expand Down Expand Up @@ -793,6 +794,36 @@ namespace winrt::TerminalApp::implementation
{
_noMatchesText().Visibility(Visibility::Collapsed);
}

if (_currentMode == CommandPaletteMode::CommandlineMode)
{
ParsedCommandLineText(L"");

const auto commandLine = _getTrimmedInput();
if (!commandLine.empty())
{
ExecuteCommandlineArgs args{ commandLine };
_appArgs.FullResetState();
if (_appArgs.ParseArgs(args) == 0)
{
const auto& commands = _appArgs.GetStartupActions();
if (commands.size() > 0)
{
std::wstring commandDescription{ RS_(L"CommandPalette_ParsedCommandLine") };
for (const auto& command : commands)
{
commandDescription += L"\n\t" + command.Args().GenerateName();
}
ParsedCommandLineText(commandDescription.data());
}
}
else
{
ParsedCommandLineText(RS_(L"CommandPalette_FailedParsingCommandLine") + L"\n\t" + til::u8u16(_appArgs.GetExitMessage()));
}
_noMatchesText().Visibility(Visibility::Visible);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious -- we're showing the no matches string all the time now? What's this look like?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DHowett - I am afraid this might be a leftover.. 🤦 which is hidden by the ParsedCommandLineText... 🤔
Fixing it immediately, after I am finalizing the live search for initial review. Hopefully today (it is already 2am for me 😄)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. It is a leftover. Thanks!

}
}
}

void CommandPalette::_evaluatePrefix()
Expand Down Expand Up @@ -893,6 +924,7 @@ namespace winrt::TerminalApp::implementation
}
}

ParsedCommandLineText(L"");
_searchBox().Text(L"");
_searchBox().Select(_searchBox().Text().size(), 0);
// Leaving this block of code outside the above if-statement
Expand Down
3 changes: 3 additions & 0 deletions src/cascadia/TerminalApp/CommandPalette.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include "FilteredCommand.h"
#include "CommandPalette.g.h"
#include "AppCommandlineArgs.h"
#include "../../cascadia/inc/cppwinrt_utils.h"

// fwdecl unittest classes
Expand Down Expand Up @@ -54,6 +55,7 @@ namespace winrt::TerminalApp::implementation
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, PrefixCharacter, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, ControlName, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, ParentCommandName, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, ParsedCommandLineText, _PropertyChangedHandlers);

TYPED_EVENT(SwitchToTabRequested, winrt::TerminalApp::CommandPalette, winrt::TerminalApp::TabBase);
TYPED_EVENT(CommandLineExecutionRequested, winrt::TerminalApp::CommandPalette, winrt::hstring);
Expand Down Expand Up @@ -128,6 +130,7 @@ namespace winrt::TerminalApp::implementation

static constexpr int CommandLineHistoryLength = 10;
Windows::Foundation::Collections::IVector<winrt::TerminalApp::FilteredCommand> _commandLineHistory{ nullptr };
::TerminalApp::AppCommandlineArgs _appArgs;

friend class TerminalAppLocalTests::TabTests;
};
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalApp/CommandPalette.idl
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ namespace TerminalApp
String PrefixCharacter { get; };
String ControlName { get; };
String ParentCommandName { get; };
String ParsedCommandLineText { get; };

Windows.Foundation.Collections.IObservableVector<FilteredCommand> FilteredActions { get; };

Expand Down
38 changes: 38 additions & 0 deletions src/cascadia/TerminalApp/CommandPalette.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ the MIT License. See LICENSE in the project root for license information. -->

<!-- This creates an instance of our CommandKeyChordVisibilityConverter we can reference below -->
<local:EmptyStringVisibilityConverter x:Key="CommandKeyChordVisibilityConverter"/>
<local:EmptyStringVisibilityConverter x:Key="ParsedCommandLineTextVisibilityConverter"/>
<local:EmptyStringVisibilityConverter x:Key="ParentCommandVisibilityConverter"/>
<local:HasNestedCommandsVisibilityConverter x:Key="HasNestedCommandsVisibilityConverter"/>
<model:IconPathConverter x:Key="IconSourceConverter"/>
Expand Down Expand Up @@ -69,6 +70,16 @@ the MIT License. See LICENSE in the project root for license information. -->
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
</Style>

<!-- ParsedCommandLineText styles -->
<Style x:Key="ParsedCommandLineBorderStyle" TargetType="Border">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="1" />
<Setter Property="Background" Value="{ThemeResource SystemAltMediumLowColor}" />
<Setter Property="BorderBrush" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
</Style>
<Style x:Key="ParsedCommandLineTextBlockStyle" TargetType="TextBlock">
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
</Style>
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<Style x:Key="CommandPaletteBackground" TargetType="Grid">
Expand Down Expand Up @@ -100,6 +111,16 @@ the MIT License. See LICENSE in the project root for license information. -->
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
</Style>

<!-- ParsedCommandLineText styles -->
<Style x:Key="ParsedCommandLineBorderStyle" TargetType="Border">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="1" />
<Setter Property="Background" Value="{ThemeResource SystemAltMediumLowColor}" />
<Setter Property="BorderBrush" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
</Style>
<Style x:Key="ParsedCommandLineTextBlockStyle" TargetType="TextBlock">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spacing looks strange around here.. Sup?

<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
</Style>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<Style x:Key="CommandPaletteBackground" TargetType="Grid">
Expand All @@ -110,6 +131,9 @@ the MIT License. See LICENSE in the project root for license information. -->
<Style x:Key="KeyChordBorderStyle" TargetType="Border"/>
<Style x:Key="KeyChordTextBlockStyle" TargetType="TextBlock"/>

<!-- ParsedCommandLineText styles (use XAML defaults for High Contrast theme) -->
<Style x:Key="ParsedCommandLineBoderStyle" TargetType="Border"/>
<Style x:Key="ParsedCommandLineTextBlockStyle" TargetType="TextBlock"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
Expand Down Expand Up @@ -228,6 +252,20 @@ the MIT License. See LICENSE in the project root for license information. -->
Text="{x:Bind NoMatchesText, Mode=OneWay}">
</TextBlock>

<Border
Grid.Row="1"
Visibility="{x:Bind ParsedCommandLineText, Mode=OneWay, Converter={StaticResource ParsedCommandLineTextVisibilityConverter}}"
Style="{ThemeResource ParsedCommandLineBorderStyle}"
Padding="16"
HorizontalAlignment="Stretch"
VerticalAlignment="Center">

<TextBlock
FontStyle="Italic"
TextWrapping="Wrap"
Text="{x:Bind ParsedCommandLineText, Mode=OneWay}"/>
</Border>

<ListView
Grid.Row="2"
x:Name="_filteredActionsView"
Expand Down
9 changes: 8 additions & 1 deletion src/cascadia/TerminalApp/Resources/en-US/Resources.resw
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,13 @@
<data name="CommandPalette_NoMatchesText.Text" xml:space="preserve">
<value>No matching commands</value>
</data>
<data name="CommandPalette_ParsedCommandLine" xml:space="preserve">
<value>Executing command line will invoke the following commands:</value>
<comment>Will be followed by a list of strings describing parsed commands</comment>
</data>
<data name="CommandPalette_FailedParsingCommandLine" xml:space="preserve">
<value>Failed parsing command line:</value>
</data>
<data name="CommandPaletteControlName" xml:space="preserve">
<value>Command Palette</value>
</data>
Expand Down Expand Up @@ -513,4 +520,4 @@
<data name="NoticeWarning" xml:space="preserve">
<value>Warning</value>
</data>
</root>
</root>
29 changes: 3 additions & 26 deletions src/cascadia/TerminalApp/TerminalPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2698,35 +2698,12 @@ namespace winrt::TerminalApp::implementation
// - an empty list if we failed to parse, otherwise a list of actions to execute.
std::vector<ActionAndArgs> TerminalPage::ConvertExecuteCommandlineToActions(const ExecuteCommandlineArgs& args)
{
if (!args || args.Commandline().empty())
::TerminalApp::AppCommandlineArgs appArgs;
if (appArgs.ParseArgs(args) == 0)
{
return {};
return appArgs.GetStartupActions();
}
// Convert the commandline into an array of args with
// CommandLineToArgvW, similar to how the app typically does when
// called from the commandline.
int argc = 0;
wil::unique_any<LPWSTR*, decltype(&::LocalFree), ::LocalFree> argv{ CommandLineToArgvW(args.Commandline().c_str(), &argc) };
if (argv)
{
std::vector<winrt::hstring> args;

// Make sure the first argument is wt.exe, because ParseArgs will
// always skip the program name. The particular value of this first
// string doesn't terribly matter.
args.emplace_back(L"wt.exe");
for (auto& elem : wil::make_range(argv.get(), argc))
{
args.emplace_back(elem);
}
winrt::array_view<const winrt::hstring> argsView{ args };

::TerminalApp::AppCommandlineArgs appArgs;
if (appArgs.ParseArgs(argsView) == 0)
{
return appArgs.GetStartupActions();
}
}
return {};
}

Expand Down