mirror of
https://github.com/microsoft/terminal.git
synced 2026-02-06 21:29:10 +00:00
Add the ability to select a whole command (or its output) (#14807)
Adds two new commands, `selectOutput` and `selectCommand`. These don't
do much without shell integration enabled, unfortunately. If you do
enable it, however, you can use these commands to quickly navigate the
history to select whole commands (or their output).
Some sample JSON:
```json
{ "keys": "ctrl+shift+<", "command": { "action": "selectCommand", "direction": "prev" } },
{ "keys": "ctrl+shift+>", "command": { "action": "selectCommand", "direction": "next" } },
{ "keys": "ctrl+shift+[", "command": { "action": "selectOutput", "direction": "prev" } },
{ "keys": "ctrl+shift+]", "command": { "action": "selectOutput", "direction": "next" } },
```
**Demo gifs** in
https://github.com/microsoft/terminal/issues/4588#issuecomment-1352042789
closes #4588
Tested manually.
<details>
<summary>CMD.exe user? It's dangerous to go alone! Take this.</summary>
Surely, there's a simpler way to do it, this is adapted from my own
script.
```cmd
prompt $e]133;D$e\$e]133;A$e\$e\$e]9;9;$P$e\$e[30;107m[$T]$e[97;46m$g$P$e[36;49m$g$e[0m$e[K$_$e[0m$e[94m%username%$e[0m@$e[32m%computername%$e[0m$G$e]133;B$e\
```
</details>
This commit is contained in:
@@ -1145,6 +1145,35 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleSelectCommand(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
if (args)
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<SelectCommandArgs>())
|
||||
{
|
||||
const auto res = _ApplyToActiveControls([&](auto& control) {
|
||||
control.SelectCommand(realArgs.Direction() == Settings::Model::SelectOutputDirection::Previous);
|
||||
});
|
||||
args.Handled(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
void TerminalPage::_HandleSelectOutput(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
if (args)
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<SelectOutputArgs>())
|
||||
{
|
||||
const auto res = _ApplyToActiveControls([&](auto& control) {
|
||||
control.SelectOutput(realArgs.Direction() == Settings::Model::SelectOutputDirection::Previous);
|
||||
});
|
||||
args.Handled(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleMarkMode(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
|
||||
@@ -2182,6 +2182,90 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
}
|
||||
|
||||
void ControlCore::SelectCommand(const bool goUp)
|
||||
{
|
||||
const til::point start = HasSelection() ? (goUp ? _terminal->GetSelectionAnchor() : _terminal->GetSelectionEnd()) :
|
||||
_terminal->GetTextBuffer().GetCursor().GetPosition();
|
||||
std::optional<DispatchTypes::ScrollMark> nearest{ std::nullopt };
|
||||
const auto& marks{ _terminal->GetScrollMarks() };
|
||||
|
||||
// Early return so we don't have to check for the validity of `nearest` below after the loop exits.
|
||||
if (marks.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static constexpr til::point worst{ til::CoordTypeMax, til::CoordTypeMax };
|
||||
til::point bestDistance{ worst };
|
||||
|
||||
for (const auto& m : marks)
|
||||
{
|
||||
if (!m.HasCommand())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto distance = goUp ? start - m.end : m.end - start;
|
||||
if ((distance > til::point{ 0, 0 }) && distance < bestDistance)
|
||||
{
|
||||
nearest = m;
|
||||
bestDistance = distance;
|
||||
}
|
||||
}
|
||||
|
||||
if (nearest.has_value())
|
||||
{
|
||||
const auto start = nearest->end;
|
||||
auto end = *nearest->commandEnd;
|
||||
|
||||
const auto bufferSize{ _terminal->GetTextBuffer().GetSize() };
|
||||
bufferSize.DecrementInBounds(end);
|
||||
|
||||
auto lock = _terminal->LockForWriting();
|
||||
_terminal->SelectNewRegion(start, end);
|
||||
_renderer->TriggerSelection();
|
||||
}
|
||||
}
|
||||
|
||||
void ControlCore::SelectOutput(const bool goUp)
|
||||
{
|
||||
const til::point start = HasSelection() ? (goUp ? _terminal->GetSelectionAnchor() : _terminal->GetSelectionEnd()) :
|
||||
_terminal->GetTextBuffer().GetCursor().GetPosition();
|
||||
std::optional<DispatchTypes::ScrollMark> nearest{ std::nullopt };
|
||||
const auto& marks{ _terminal->GetScrollMarks() };
|
||||
|
||||
static constexpr til::point worst{ til::CoordTypeMax, til::CoordTypeMax };
|
||||
til::point bestDistance{ worst };
|
||||
|
||||
for (const auto& m : marks)
|
||||
{
|
||||
if (!m.HasOutput())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto distance = goUp ? start - *m.commandEnd : *m.commandEnd - start;
|
||||
if ((distance > til::point{ 0, 0 }) && distance < bestDistance)
|
||||
{
|
||||
nearest = m;
|
||||
bestDistance = distance;
|
||||
}
|
||||
}
|
||||
|
||||
if (nearest.has_value())
|
||||
{
|
||||
const auto start = *nearest->commandEnd;
|
||||
auto end = *nearest->outputEnd;
|
||||
|
||||
const auto bufferSize{ _terminal->GetTextBuffer().GetSize() };
|
||||
bufferSize.DecrementInBounds(end);
|
||||
|
||||
auto lock = _terminal->LockForWriting();
|
||||
_terminal->SelectNewRegion(start, end);
|
||||
_renderer->TriggerSelection();
|
||||
}
|
||||
}
|
||||
|
||||
void ControlCore::ColorSelection(const Control::SelectionColor& fg, const Control::SelectionColor& bg, Core::MatchMode matchMode)
|
||||
{
|
||||
if (HasSelection())
|
||||
|
||||
@@ -152,7 +152,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
void ClearMark();
|
||||
void ClearAllMarks();
|
||||
void ScrollToMark(const Control::ScrollToMarkDirection& direction);
|
||||
|
||||
void SelectCommand(const bool goUp);
|
||||
void SelectOutput(const bool goUp);
|
||||
#pragma endregion
|
||||
|
||||
#pragma region ITerminalInput
|
||||
|
||||
@@ -57,6 +57,8 @@ namespace Microsoft.Terminal.Control
|
||||
void ClearMark();
|
||||
void ClearAllMarks();
|
||||
void ScrollToMark(ScrollToMarkDirection direction);
|
||||
void SelectCommand(Boolean goUp);
|
||||
void SelectOutput(Boolean goUp);
|
||||
IVector<ScrollMark> ScrollMarks { get; };
|
||||
|
||||
};
|
||||
|
||||
@@ -3316,6 +3316,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
return _core.ScrollMarks();
|
||||
}
|
||||
|
||||
void TermControl::SelectCommand(const bool goUp)
|
||||
{
|
||||
_core.SelectCommand(goUp);
|
||||
}
|
||||
|
||||
void TermControl::SelectOutput(const bool goUp)
|
||||
{
|
||||
_core.SelectOutput(goUp);
|
||||
}
|
||||
|
||||
void TermControl::ColorSelection(Control::SelectionColor fg, Control::SelectionColor bg, Core::MatchMode matchMode)
|
||||
{
|
||||
_core.ColorSelection(fg, bg, matchMode);
|
||||
|
||||
@@ -81,6 +81,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
void ClearMark();
|
||||
void ClearAllMarks();
|
||||
void ScrollToMark(const Control::ScrollToMarkDirection& direction);
|
||||
void SelectCommand(const bool goUp);
|
||||
void SelectOutput(const bool goUp);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
|
||||
@@ -83,6 +83,8 @@ static constexpr std::string_view QuitKey{ "quit" };
|
||||
static constexpr std::string_view AdjustOpacityKey{ "adjustOpacity" };
|
||||
static constexpr std::string_view RestoreLastClosedKey{ "restoreLastClosed" };
|
||||
static constexpr std::string_view SelectAllKey{ "selectAll" };
|
||||
static constexpr std::string_view SelectCommandKey{ "selectCommand" };
|
||||
static constexpr std::string_view SelectOutputKey{ "selectOutput" };
|
||||
static constexpr std::string_view MarkModeKey{ "markMode" };
|
||||
static constexpr std::string_view ToggleBlockSelectionKey{ "toggleBlockSelection" };
|
||||
static constexpr std::string_view SwitchSelectionEndpointKey{ "switchSelectionEndpoint" };
|
||||
@@ -329,22 +331,24 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
|
||||
winrt::hstring ActionAndArgs::GenerateName() const
|
||||
{
|
||||
// Sentinel used to indicate this command must ALWAYS be generated by GenerateName
|
||||
static const winrt::hstring MustGenerate{ L"" };
|
||||
// Use a magic static to initialize this map, because we won't be able
|
||||
// to load the resources at _init_, only at runtime.
|
||||
static const auto GeneratedActionNames = []() {
|
||||
return std::unordered_map<ShortcutAction, winrt::hstring>{
|
||||
{ ShortcutAction::AdjustFontSize, RS_(L"AdjustFontSizeCommandKey") },
|
||||
{ ShortcutAction::CloseOtherPanes, RS_(L"CloseOtherPanesCommandKey") },
|
||||
{ ShortcutAction::CloseOtherTabs, L"" }, // Intentionally omitted, must be generated by GenerateName
|
||||
{ ShortcutAction::CloseOtherTabs, MustGenerate },
|
||||
{ ShortcutAction::ClosePane, RS_(L"ClosePaneCommandKey") },
|
||||
{ ShortcutAction::CloseTab, L"" }, // Intentionally omitted, must be generated by GenerateName
|
||||
{ ShortcutAction::CloseTabsAfter, L"" }, // Intentionally omitted, must be generated by GenerateName
|
||||
{ ShortcutAction::CloseTab, MustGenerate },
|
||||
{ ShortcutAction::CloseTabsAfter, MustGenerate },
|
||||
{ ShortcutAction::CloseWindow, RS_(L"CloseWindowCommandKey") },
|
||||
{ ShortcutAction::CopyText, RS_(L"CopyTextCommandKey") },
|
||||
{ ShortcutAction::DuplicateTab, RS_(L"DuplicateTabCommandKey") },
|
||||
{ ShortcutAction::ExecuteCommandline, RS_(L"ExecuteCommandlineCommandKey") },
|
||||
{ ShortcutAction::Find, RS_(L"FindCommandKey") },
|
||||
{ ShortcutAction::Invalid, L"" },
|
||||
{ ShortcutAction::Invalid, MustGenerate },
|
||||
{ ShortcutAction::MoveFocus, RS_(L"MoveFocusCommandKey") },
|
||||
{ ShortcutAction::MovePane, RS_(L"MovePaneCommandKey") },
|
||||
{ ShortcutAction::SwapPane, RS_(L"SwapPaneCommandKey") },
|
||||
@@ -369,25 +373,25 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
{ ShortcutAction::AddMark, RS_(L"AddMarkCommandKey") },
|
||||
{ ShortcutAction::ClearMark, RS_(L"ClearMarkCommandKey") },
|
||||
{ ShortcutAction::ClearAllMarks, RS_(L"ClearAllMarksCommandKey") },
|
||||
{ ShortcutAction::SendInput, L"" },
|
||||
{ ShortcutAction::SetColorScheme, L"" },
|
||||
{ ShortcutAction::SendInput, MustGenerate },
|
||||
{ ShortcutAction::SetColorScheme, MustGenerate },
|
||||
{ ShortcutAction::SetTabColor, RS_(L"ResetTabColorCommandKey") },
|
||||
{ ShortcutAction::SplitPane, RS_(L"SplitPaneCommandKey") },
|
||||
{ ShortcutAction::SwitchToTab, RS_(L"SwitchToTabCommandKey") },
|
||||
{ ShortcutAction::TabSearch, RS_(L"TabSearchCommandKey") },
|
||||
{ ShortcutAction::ToggleAlwaysOnTop, RS_(L"ToggleAlwaysOnTopCommandKey") },
|
||||
{ ShortcutAction::ToggleCommandPalette, L"" },
|
||||
{ ShortcutAction::ToggleCommandPalette, MustGenerate },
|
||||
{ ShortcutAction::ToggleFocusMode, RS_(L"ToggleFocusModeCommandKey") },
|
||||
{ ShortcutAction::SetFocusMode, L"" },
|
||||
{ ShortcutAction::SetFocusMode, MustGenerate },
|
||||
{ ShortcutAction::ToggleFullscreen, RS_(L"ToggleFullscreenCommandKey") },
|
||||
{ ShortcutAction::SetFullScreen, L"" },
|
||||
{ ShortcutAction::SetMaximized, L"" },
|
||||
{ ShortcutAction::SetFullScreen, MustGenerate },
|
||||
{ ShortcutAction::SetMaximized, MustGenerate },
|
||||
{ ShortcutAction::TogglePaneZoom, RS_(L"TogglePaneZoomCommandKey") },
|
||||
{ ShortcutAction::ToggleSplitOrientation, RS_(L"ToggleSplitOrientationCommandKey") },
|
||||
{ ShortcutAction::ToggleShaderEffects, RS_(L"ToggleShaderEffectsCommandKey") },
|
||||
{ ShortcutAction::MoveTab, L"" }, // Intentionally omitted, must be generated by GenerateName
|
||||
{ ShortcutAction::MoveTab, MustGenerate },
|
||||
{ ShortcutAction::BreakIntoDebugger, RS_(L"BreakIntoDebuggerCommandKey") },
|
||||
{ ShortcutAction::FindMatch, L"" }, // Intentionally omitted, must be generated by GenerateName
|
||||
{ ShortcutAction::FindMatch, MustGenerate },
|
||||
{ ShortcutAction::TogglePaneReadOnly, RS_(L"TogglePaneReadOnlyCommandKey") },
|
||||
{ ShortcutAction::EnablePaneReadOnly, RS_(L"EnablePaneReadOnlyCommandKey") },
|
||||
{ ShortcutAction::DisablePaneReadOnly, RS_(L"DisablePaneReadOnlyCommandKey") },
|
||||
@@ -396,21 +400,23 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
{ ShortcutAction::IdentifyWindows, RS_(L"IdentifyWindowsCommandKey") },
|
||||
{ ShortcutAction::RenameWindow, RS_(L"ResetWindowNameCommandKey") },
|
||||
{ ShortcutAction::OpenWindowRenamer, RS_(L"OpenWindowRenamerCommandKey") },
|
||||
{ ShortcutAction::GlobalSummon, L"" }, // Intentionally omitted, must be generated by GenerateName
|
||||
{ ShortcutAction::GlobalSummon, MustGenerate },
|
||||
{ ShortcutAction::QuakeMode, RS_(L"QuakeModeCommandKey") },
|
||||
{ ShortcutAction::FocusPane, L"" }, // Intentionally omitted, must be generated by GenerateName
|
||||
{ ShortcutAction::FocusPane, MustGenerate },
|
||||
{ ShortcutAction::OpenSystemMenu, RS_(L"OpenSystemMenuCommandKey") },
|
||||
{ ShortcutAction::ExportBuffer, L"" }, // Intentionally omitted, must be generated by GenerateName
|
||||
{ ShortcutAction::ClearBuffer, L"" }, // Intentionally omitted, must be generated by GenerateName
|
||||
{ ShortcutAction::MultipleActions, L"" }, // Intentionally omitted, must be generated by GenerateName
|
||||
{ ShortcutAction::ExportBuffer, MustGenerate },
|
||||
{ ShortcutAction::ClearBuffer, MustGenerate },
|
||||
{ ShortcutAction::MultipleActions, MustGenerate },
|
||||
{ ShortcutAction::Quit, RS_(L"QuitCommandKey") },
|
||||
{ ShortcutAction::AdjustOpacity, L"" }, // Intentionally omitted, must be generated by GenerateName
|
||||
{ ShortcutAction::AdjustOpacity, MustGenerate },
|
||||
{ ShortcutAction::RestoreLastClosed, RS_(L"RestoreLastClosedCommandKey") },
|
||||
{ ShortcutAction::SelectCommand, MustGenerate },
|
||||
{ ShortcutAction::SelectOutput, MustGenerate },
|
||||
{ ShortcutAction::SelectAll, RS_(L"SelectAllCommandKey") },
|
||||
{ ShortcutAction::MarkMode, RS_(L"MarkModeCommandKey") },
|
||||
{ ShortcutAction::ToggleBlockSelection, RS_(L"ToggleBlockSelectionCommandKey") },
|
||||
{ ShortcutAction::SwitchSelectionEndpoint, RS_(L"SwitchSelectionEndpointCommandKey") },
|
||||
{ ShortcutAction::ColorSelection, L"" }, // Intentionally omitted, must be generated by GenerateName
|
||||
{ ShortcutAction::ColorSelection, MustGenerate },
|
||||
{ ShortcutAction::ExpandSelectionToWord, RS_(L"ExpandSelectionToWordCommandKey") },
|
||||
};
|
||||
}();
|
||||
|
||||
@@ -43,6 +43,8 @@
|
||||
#include "ClearBufferArgs.g.cpp"
|
||||
#include "MultipleActionsArgs.g.cpp"
|
||||
#include "AdjustOpacityArgs.g.cpp"
|
||||
#include "SelectCommandArgs.g.cpp"
|
||||
#include "SelectOutputArgs.g.cpp"
|
||||
#include "ColorSelectionArgs.g.cpp"
|
||||
|
||||
#include <LibraryResources.h>
|
||||
@@ -970,4 +972,27 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
winrt::hstring SelectOutputArgs::GenerateName() const
|
||||
{
|
||||
switch (Direction())
|
||||
{
|
||||
case SelectOutputDirection::Next:
|
||||
return RS_(L"SelectOutputNextCommandKey");
|
||||
case SelectOutputDirection::Previous:
|
||||
return RS_(L"SelectOutputPreviousCommandKey");
|
||||
}
|
||||
return L"";
|
||||
}
|
||||
winrt::hstring SelectCommandArgs::GenerateName() const
|
||||
{
|
||||
switch (Direction())
|
||||
{
|
||||
case SelectOutputDirection::Next:
|
||||
return RS_(L"SelectCommandNextCommandKey");
|
||||
case SelectOutputDirection::Previous:
|
||||
return RS_(L"SelectCommandPreviousCommandKey");
|
||||
}
|
||||
return L"";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,8 @@
|
||||
#include "ClearBufferArgs.g.h"
|
||||
#include "MultipleActionsArgs.g.h"
|
||||
#include "AdjustOpacityArgs.g.h"
|
||||
#include "SelectCommandArgs.g.h"
|
||||
#include "SelectOutputArgs.g.h"
|
||||
#include "ColorSelectionArgs.g.h"
|
||||
|
||||
#include "JsonUtils.h"
|
||||
@@ -250,6 +252,14 @@ private: \
|
||||
X(int32_t, Opacity, "opacity", false, 0) \
|
||||
X(bool, Relative, "relative", false, true)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#define SELECT_COMMAND_ARGS(X) \
|
||||
X(SelectOutputDirection, Direction, "direction", false, SelectOutputDirection::Previous)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#define SELECT_OUTPUT_ARGS(X) \
|
||||
X(SelectOutputDirection, Direction, "direction", false, SelectOutputDirection::Previous)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#define COLOR_SELECTION_ARGS(X) \
|
||||
X(winrt::Microsoft::Terminal::Control::SelectionColor, Foreground, "foreground", false, nullptr) \
|
||||
@@ -778,6 +788,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
|
||||
ACTION_ARGS_STRUCT(AdjustOpacityArgs, ADJUST_OPACITY_ARGS);
|
||||
|
||||
ACTION_ARGS_STRUCT(SelectCommandArgs, SELECT_COMMAND_ARGS);
|
||||
ACTION_ARGS_STRUCT(SelectOutputArgs, SELECT_OUTPUT_ARGS);
|
||||
|
||||
ACTION_ARGS_STRUCT(ColorSelectionArgs, COLOR_SELECTION_ARGS);
|
||||
|
||||
}
|
||||
@@ -814,4 +827,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
|
||||
BASIC_FACTORY(ClearBufferArgs);
|
||||
BASIC_FACTORY(MultipleActionsArgs);
|
||||
BASIC_FACTORY(AdjustOpacityArgs);
|
||||
BASIC_FACTORY(SelectCommandArgs);
|
||||
BASIC_FACTORY(SelectOutputArgs);
|
||||
}
|
||||
|
||||
@@ -80,6 +80,12 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
Previous
|
||||
};
|
||||
|
||||
enum SelectOutputDirection
|
||||
{
|
||||
Previous = 0,
|
||||
Next,
|
||||
};
|
||||
|
||||
enum CommandPaletteLaunchMode
|
||||
{
|
||||
Action = 0,
|
||||
@@ -388,4 +394,17 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
Microsoft.Terminal.Control.SelectionColor Background;
|
||||
Microsoft.Terminal.Core.MatchMode MatchMode { get; };
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass SelectCommandArgs : IActionArgs
|
||||
{
|
||||
SelectCommandArgs(SelectOutputDirection direction);
|
||||
SelectOutputDirection Direction { get; };
|
||||
}
|
||||
[default_interface] runtimeclass SelectOutputArgs : IActionArgs
|
||||
{
|
||||
SelectOutputArgs(SelectOutputDirection direction);
|
||||
SelectOutputDirection Direction { get; };
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -96,6 +96,8 @@
|
||||
ON_ALL_ACTIONS(AdjustOpacity) \
|
||||
ON_ALL_ACTIONS(RestoreLastClosed) \
|
||||
ON_ALL_ACTIONS(SelectAll) \
|
||||
ON_ALL_ACTIONS(SelectCommand) \
|
||||
ON_ALL_ACTIONS(SelectOutput) \
|
||||
ON_ALL_ACTIONS(MarkMode) \
|
||||
ON_ALL_ACTIONS(ToggleBlockSelection) \
|
||||
ON_ALL_ACTIONS(SwitchSelectionEndpoint) \
|
||||
@@ -142,4 +144,6 @@
|
||||
ON_ALL_ACTIONS_WITH_ARGS(ClearBuffer) \
|
||||
ON_ALL_ACTIONS_WITH_ARGS(MultipleActions) \
|
||||
ON_ALL_ACTIONS_WITH_ARGS(AdjustOpacity) \
|
||||
ON_ALL_ACTIONS_WITH_ARGS(SelectCommand) \
|
||||
ON_ALL_ACTIONS_WITH_ARGS(SelectOutput) \
|
||||
ON_ALL_ACTIONS_WITH_ARGS(ColorSelection)
|
||||
|
||||
@@ -675,4 +675,16 @@
|
||||
<data name="CloseOtherPanesCommandKey" xml:space="preserve">
|
||||
<value>Close all other panes</value>
|
||||
</data>
|
||||
<data name="SelectOutputNextCommandKey" xml:space="preserve">
|
||||
<value>Select next command output</value>
|
||||
</data>
|
||||
<data name="SelectOutputPreviousCommandKey" xml:space="preserve">
|
||||
<value>Select previous command output</value>
|
||||
</data>
|
||||
<data name="SelectCommandNextCommandKey" xml:space="preserve">
|
||||
<value>Select next command</value>
|
||||
</data>
|
||||
<data name="SelectCommandPreviousCommandKey" xml:space="preserve">
|
||||
<value>Select previous command</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -672,6 +672,14 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::FolderEntryInlin
|
||||
};
|
||||
};
|
||||
|
||||
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::SelectOutputDirection)
|
||||
{
|
||||
JSON_MAPPINGS(2) = {
|
||||
pair_type{ "prev", ValueType::Previous },
|
||||
pair_type{ "next", ValueType::Next },
|
||||
};
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ::Microsoft::Terminal::Settings::Model::JsonUtils::ConversionTrait<::winrt::Microsoft::Terminal::Control::SelectionColor>
|
||||
{
|
||||
|
||||
@@ -38,6 +38,9 @@ namespace ControlUnitTests
|
||||
TEST_METHOD(TestClearAll);
|
||||
TEST_METHOD(TestReadEntireBuffer);
|
||||
|
||||
TEST_METHOD(TestSelectCommandSimple);
|
||||
TEST_METHOD(TestSelectOutputSimple);
|
||||
|
||||
TEST_CLASS_SETUP(ModuleSetup)
|
||||
{
|
||||
winrt::init_apartment(winrt::apartment_type::single_threaded);
|
||||
@@ -358,5 +361,142 @@ namespace ControlUnitTests
|
||||
VERIFY_ARE_EQUAL(L"This is some text\r\nwith varying amounts\r\nof whitespace\r\n",
|
||||
core->ReadEntireBuffer());
|
||||
}
|
||||
void _writePrompt(const winrt::com_ptr<MockConnection>& conn, const auto& path)
|
||||
{
|
||||
conn->WriteInput(L"\x1b]133;D\x7");
|
||||
conn->WriteInput(L"\x1b]133;A\x7");
|
||||
conn->WriteInput(L"\x1b]9;9;");
|
||||
conn->WriteInput(path);
|
||||
conn->WriteInput(L"\x7");
|
||||
conn->WriteInput(L"PWSH ");
|
||||
conn->WriteInput(path);
|
||||
conn->WriteInput(L"> ");
|
||||
conn->WriteInput(L"\x1b]133;B\x7");
|
||||
}
|
||||
|
||||
void ControlCoreTests::TestSelectCommandSimple()
|
||||
{
|
||||
auto [settings, conn] = _createSettingsAndConnection();
|
||||
Log::Comment(L"Create ControlCore object");
|
||||
auto core = createCore(*settings, *conn);
|
||||
VERIFY_IS_NOT_NULL(core);
|
||||
_standardInit(core);
|
||||
|
||||
Log::Comment(L"Print some text");
|
||||
|
||||
_writePrompt(conn, L"C:\\Windows");
|
||||
conn->WriteInput(L"Foo-bar");
|
||||
conn->WriteInput(L"\x1b]133;C\x7");
|
||||
|
||||
conn->WriteInput(L"\r\n");
|
||||
conn->WriteInput(L"This is some text \r\n");
|
||||
conn->WriteInput(L"with varying amounts \r\n");
|
||||
conn->WriteInput(L"of whitespace \r\n");
|
||||
|
||||
_writePrompt(conn, L"C:\\Windows");
|
||||
|
||||
Log::Comment(L"Check the buffer contents");
|
||||
const auto& buffer = core->_terminal->GetTextBuffer();
|
||||
const auto& cursor = buffer.GetCursor();
|
||||
|
||||
{
|
||||
const til::point expectedCursor{ 17, 4 };
|
||||
VERIFY_ARE_EQUAL(expectedCursor, cursor.GetPosition());
|
||||
}
|
||||
|
||||
VERIFY_IS_FALSE(core->HasSelection());
|
||||
core->SelectCommand(true);
|
||||
VERIFY_IS_TRUE(core->HasSelection());
|
||||
{
|
||||
const auto& start = core->_terminal->GetSelectionAnchor();
|
||||
const auto& end = core->_terminal->GetSelectionEnd();
|
||||
const til::point expectedStart{ 17, 0 };
|
||||
const til::point expectedEnd{ 23, 0 };
|
||||
VERIFY_ARE_EQUAL(expectedStart, start);
|
||||
VERIFY_ARE_EQUAL(expectedEnd, end);
|
||||
}
|
||||
|
||||
core->_terminal->ClearSelection();
|
||||
conn->WriteInput(L"Boo-far");
|
||||
conn->WriteInput(L"\x1b]133;C\x7");
|
||||
|
||||
VERIFY_IS_FALSE(core->HasSelection());
|
||||
{
|
||||
const til::point expectedCursor{ 24, 4 };
|
||||
VERIFY_ARE_EQUAL(expectedCursor, cursor.GetPosition());
|
||||
}
|
||||
VERIFY_IS_FALSE(core->HasSelection());
|
||||
core->SelectCommand(true);
|
||||
VERIFY_IS_TRUE(core->HasSelection());
|
||||
{
|
||||
const auto& start = core->_terminal->GetSelectionAnchor();
|
||||
const auto& end = core->_terminal->GetSelectionEnd();
|
||||
const til::point expectedStart{ 17, 4 };
|
||||
const til::point expectedEnd{ 23, 4 };
|
||||
VERIFY_ARE_EQUAL(expectedStart, start);
|
||||
VERIFY_ARE_EQUAL(expectedEnd, end);
|
||||
}
|
||||
core->SelectCommand(true);
|
||||
VERIFY_IS_TRUE(core->HasSelection());
|
||||
{
|
||||
const auto& start = core->_terminal->GetSelectionAnchor();
|
||||
const auto& end = core->_terminal->GetSelectionEnd();
|
||||
const til::point expectedStart{ 17, 0 };
|
||||
const til::point expectedEnd{ 23, 0 };
|
||||
VERIFY_ARE_EQUAL(expectedStart, start);
|
||||
VERIFY_ARE_EQUAL(expectedEnd, end);
|
||||
}
|
||||
core->SelectCommand(false);
|
||||
VERIFY_IS_TRUE(core->HasSelection());
|
||||
{
|
||||
const auto& start = core->_terminal->GetSelectionAnchor();
|
||||
const auto& end = core->_terminal->GetSelectionEnd();
|
||||
const til::point expectedStart{ 17, 4 };
|
||||
const til::point expectedEnd{ 23, 4 };
|
||||
VERIFY_ARE_EQUAL(expectedStart, start);
|
||||
VERIFY_ARE_EQUAL(expectedEnd, end);
|
||||
}
|
||||
}
|
||||
void ControlCoreTests::TestSelectOutputSimple()
|
||||
{
|
||||
auto [settings, conn] = _createSettingsAndConnection();
|
||||
Log::Comment(L"Create ControlCore object");
|
||||
auto core = createCore(*settings, *conn);
|
||||
VERIFY_IS_NOT_NULL(core);
|
||||
_standardInit(core);
|
||||
|
||||
Log::Comment(L"Print some text");
|
||||
|
||||
_writePrompt(conn, L"C:\\Windows");
|
||||
conn->WriteInput(L"Foo-bar");
|
||||
conn->WriteInput(L"\x1b]133;C\x7");
|
||||
|
||||
conn->WriteInput(L"\r\n");
|
||||
conn->WriteInput(L"This is some text \r\n");
|
||||
conn->WriteInput(L"with varying amounts \r\n");
|
||||
conn->WriteInput(L"of whitespace \r\n");
|
||||
|
||||
_writePrompt(conn, L"C:\\Windows");
|
||||
|
||||
Log::Comment(L"Check the buffer contents");
|
||||
const auto& buffer = core->_terminal->GetTextBuffer();
|
||||
const auto& cursor = buffer.GetCursor();
|
||||
|
||||
{
|
||||
const til::point expectedCursor{ 17, 4 };
|
||||
VERIFY_ARE_EQUAL(expectedCursor, cursor.GetPosition());
|
||||
}
|
||||
|
||||
VERIFY_IS_FALSE(core->HasSelection());
|
||||
core->SelectOutput(true);
|
||||
VERIFY_IS_TRUE(core->HasSelection());
|
||||
{
|
||||
const auto& start = core->_terminal->GetSelectionAnchor();
|
||||
const auto& end = core->_terminal->GetSelectionEnd();
|
||||
const til::point expectedStart{ 24, 0 }; // The character after the prompt
|
||||
const til::point expectedEnd{ 29, 3 }; // x = buffer.right
|
||||
VERIFY_ARE_EQUAL(expectedStart, start);
|
||||
VERIFY_ARE_EQUAL(expectedEnd, end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -580,5 +580,14 @@ namespace Microsoft::Console::VirtualTerminal::DispatchTypes
|
||||
MarkCategory category{ MarkCategory::Info };
|
||||
// Other things we may want to think about in the future are listed in
|
||||
// GH#11000
|
||||
|
||||
bool HasCommand() const noexcept
|
||||
{
|
||||
return commandEnd.has_value() && *commandEnd != end;
|
||||
}
|
||||
bool HasOutput() const noexcept
|
||||
{
|
||||
return outputEnd.has_value() && *outputEnd != *commandEnd;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3173,6 +3173,18 @@ bool AdaptDispatch::DoConEmuAction(const std::wstring_view string)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// 12: "Let ConEmu treat current cursor position as prompt start"
|
||||
//
|
||||
// Based on the official conemu docs:
|
||||
// * https://conemu.github.io/en/ShellWorkDir.html#connector-ps1
|
||||
// * https://conemu.github.io/en/ShellWorkDir.html#PowerShell
|
||||
//
|
||||
// This seems like basically the same as 133;B - the end of the prompt, the start of the commandline.
|
||||
else if (subParam == 12)
|
||||
{
|
||||
_api.MarkCommandStart();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user