Skip to content

Commit

Permalink
Add support for Commandline Mode to the CmdPal (#7293)
Browse files Browse the repository at this point in the history
## Summary of the Pull Request

Adds support for "commandline mode" to the command palette. 
![cmdpal-commandline-mode](https://user-images.githubusercontent.com/18356694/90263053-bbd17500-de14-11ea-8726-fee48fec5888.gif)


This allows the user to start typing a `wt.exe` commandline directly in the command palette, to run that commandline directly in the current window. This allows the user input something like `> nt -p Ubuntu ; sp -p ssh` and open up a new tab and split it _in the current window_. 

## References

* cmdpal megathread: #5400
* Kinda related to #4472
* built with the `wt` action from #6537

## PR Checklist
* [x] Closes #6677
* [x] I work here
* [ ] Tests added/passed
* [ ] Requires documentation to be updated - sure does, when the cmdpal docs are written in the first place :P

## Validation Steps Performed

Tested manually
  • Loading branch information
zadjii-msft authored Aug 24, 2020
1 parent 55b6ace commit f897ce0
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 11 deletions.
154 changes: 145 additions & 9 deletions src/cascadia/TerminalApp/CommandPalette.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,25 +172,59 @@ namespace winrt::TerminalApp::implementation
}
else if (key == VirtualKey::Enter)
{
// Action Mode: Dispatch the action of the selected command.

if (const auto selectedItem = _filteredActionsView().SelectedItem())
// Action, TabSwitch or TabSearchMode Mode: Dispatch the action of the selected command.
if (_currentMode != CommandPaletteMode::CommandlineMode)
{
_dispatchCommand(selectedItem.try_as<TerminalApp::Command>());
if (const auto selectedItem = _filteredActionsView().SelectedItem())
{
_dispatchCommand(selectedItem.try_as<TerminalApp::Command>());
}
}
// Commandline Mode: Use the input to synthesize an ExecuteCommandline action
else if (_currentMode == CommandPaletteMode::CommandlineMode)
{
_dispatchCommandline();
}

e.Handled(true);
}
else if (key == VirtualKey::Escape)
{
// Action Mode: Dismiss the palette if the text is empty, otherwise clear the search string.
if (_searchBox().Text().empty())
// Action, TabSearch, TabSwitch Mode: Dismiss the palette if the
// text is empty, otherwise clear the search string.
if (_currentMode != CommandPaletteMode::CommandlineMode)
{
_dismissPalette();
if (_searchBox().Text().empty())
{
_dismissPalette();
}
else
{
_searchBox().Text(L"");
}
}
else
else if (_currentMode == CommandPaletteMode::CommandlineMode)
{
_searchBox().Text(L"");
const auto currentInput = _getPostPrefixInput();
if (currentInput.empty())
{
// The user's only input "> " so far. We should just dismiss
// the palette. This is like dismissing the Action mode with
// empty input.
_dismissPalette();
}
else
{
// Clear out the current input. We'll leave a ">" in the
// input (to stay in commandline mode), and a leading space
// (if they currently had one).
const bool hasLeadingSpace = (_searchBox().Text().size()) - (currentInput.size()) > 1;
_searchBox().Text(hasLeadingSpace ? L"> " : L">");

// This will conveniently move the cursor to the end of the
// text input for us.
_searchBox().Select(_searchBox().Text().size(), 0);
}
}

e.Handled(true);
Expand Down Expand Up @@ -362,6 +396,8 @@ namespace winrt::TerminalApp::implementation
case CommandPaletteMode::TabSearchMode:
case CommandPaletteMode::TabSwitchMode:
return _allTabActions;
case CommandPaletteMode::CommandlineMode:
return winrt::single_threaded_vector<TerminalApp::Command>();
default:
return _allCommands;
}
Expand Down Expand Up @@ -422,6 +458,72 @@ namespace winrt::TerminalApp::implementation
}
}

// Method Description:
// - Get all the input text in _searchBox that follows the prefix character
// and any whitespace following that prefix character. This can be used in
// commandline mode to get all the useful input that the user input after
// the leading ">" prefix.
// - Note that this will behave unexpectedly in Action Mode.
// Arguments:
// - <none>
// Return Value:
// - the string of input following the prefix character.
std::wstring CommandPalette::_getPostPrefixInput()
{
const std::wstring input{ _searchBox().Text() };
if (input.empty())
{
return input;
}

const auto rawCmdline{ input.substr(1) };

// Trim leading whitespace
const auto firstNonSpace = rawCmdline.find_first_not_of(L" ");
if (firstNonSpace == std::wstring::npos)
{
// All the following characters are whitespace.
return L"";
}

return rawCmdline.substr(firstNonSpace);
}

// Method Description:
// - Dispatch the current search text as a ExecuteCommandline action.
// Arguments:
// - <none>
// Return Value:
// - <none>
void CommandPalette::_dispatchCommandline()
{
const auto input = _getPostPrefixInput();
if (input.empty())
{
return;
}
winrt::hstring cmdline{ input };

// Build the ExecuteCommandline action from the values we've parsed on the commandline.
auto executeActionAndArgs = winrt::make_self<implementation::ActionAndArgs>();
executeActionAndArgs->Action(ShortcutAction::ExecuteCommandline);
auto args = winrt::make_self<implementation::ExecuteCommandlineArgs>();
args->Commandline(cmdline);
executeActionAndArgs->Args(*args);

TraceLoggingWrite(
g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider
"CommandPaletteDispatchedCommandline",
TraceLoggingDescription("Event emitted when the user runs a commandline in the Command Palette"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));

if (_dispatch.DoAction(*executeActionAndArgs))
{
_close();
}
}

// Method Description:
// - Helper method for closing the command palette, when the user has _not_
// selected an action. Also fires a tracelogging event indicating that the
Expand Down Expand Up @@ -452,12 +554,36 @@ namespace winrt::TerminalApp::implementation
void CommandPalette::_filterTextChanged(IInspectable const& /*sender*/,
Windows::UI::Xaml::RoutedEventArgs const& /*args*/)
{
if (_currentMode == CommandPaletteMode::CommandlineMode || _currentMode == CommandPaletteMode::ActionMode)
{
_evaluatePrefix();
}

_updateFilteredActions();
_filteredActionsView().SelectedIndex(0);

_noMatchesText().Visibility(_filteredActions.Size() > 0 ? Visibility::Collapsed : Visibility::Visible);
}

void CommandPalette::_evaluatePrefix()
{
auto newMode = CommandPaletteMode::ActionMode;

auto inputText = _searchBox().Text();
if (inputText.size() > 0)
{
if (inputText[0] == L'>')
{
newMode = CommandPaletteMode::CommandlineMode;
}
}

if (newMode != _currentMode)
{
_switchToMode(newMode);
}
}

Collections::IObservableVector<TerminalApp::Command> CommandPalette::FilteredActions()
{
return _filteredActions;
Expand Down Expand Up @@ -511,6 +637,10 @@ namespace winrt::TerminalApp::implementation
ControlName(RS_(L"TabSwitcherControlName"));
break;
}
case CommandPaletteMode::CommandlineMode:
NoMatchesText(RS_(L"CmdPalCommandlinePrompt"));
ControlName(RS_(L"CommandPaletteControlName"));
break;
case CommandPaletteMode::ActionMode:
default:
SearchBoxText(RS_(L"CommandPalette_SearchBox/PlaceholderText"));
Expand Down Expand Up @@ -666,6 +796,12 @@ namespace winrt::TerminalApp::implementation
// - <none>
void CommandPalette::_updateFilteredActions()
{
if (_currentMode == CommandPaletteMode::CommandlineMode)
{
_filteredActions.Clear();
return;
}

auto actions = _collectFilteredActions();

// Make _filteredActions look identical to actions, using only Insert and Remove.
Expand Down
8 changes: 6 additions & 2 deletions src/cascadia/TerminalApp/CommandPalette.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ namespace winrt::TerminalApp::implementation
{
ActionMode = 0,
TabSearchMode,
TabSwitchMode
TabSwitchMode,
CommandlineMode
};

struct CommandPalette : CommandPaletteT<CommandPalette>
Expand Down Expand Up @@ -80,6 +81,9 @@ namespace winrt::TerminalApp::implementation
CommandPaletteMode _currentMode;
void _switchToMode(CommandPaletteMode mode);

void _evaluatePrefix();
std::wstring _getPostPrefixInput();

Microsoft::Terminal::TerminalControl::IKeyBindings _bindings;

// Tab Switcher
Expand All @@ -92,7 +96,7 @@ namespace winrt::TerminalApp::implementation
winrt::Windows::UI::Xaml::Controls::ListView::SizeChanged_revoker _sizeChangedRevoker;

void _dispatchCommand(const TerminalApp::Command& command);

void _dispatchCommandline();
void _dismissPalette();
};
}
Expand Down
4 changes: 4 additions & 0 deletions src/cascadia/TerminalApp/Resources/en-US/Resources.resw
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,10 @@
<data name="TabSwitcher_NoMatchesText" xml:space="preserve">
<value>No matching tab name</value>
</data>
<data name="CmdPalCommandlinePrompt" xml:space="preserve">
<value>Enter a wt commandline to run</value>
<comment>{Locked="wt"} </comment>
</data>
<data name="CrimsonColorButton.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
<value>Crimson</value>
</data>
Expand Down

0 comments on commit f897ce0

Please sign in to comment.