Add Quick Fix UI and support for custom CommandNotFound OSC (#16848)

### `OSC 9001; CmdNotFound; <missingCmd>`
Adds support for custom OSC "command not found" sequence `OSC 9001;
CmdNotFound; <missingCmd>`. Upon receiving the "CmdNotFound" variant
with the missing command payload, we send the missing command up to the
Quick Fix menu and add it in as `winget install <missingCmd>`.

### Quick Fix UI
The Quick Fix UI is a new UI surface that lives in the gutter (left
padding) of your terminal. The button appears if quick fixes are
available. When clicked, a list of suggestions appears in a flyout. If
there is not enough space in the gutter, the button will be presented in
a collapsed version that expands to a normal size upon hovering over it.

The Quick Fix UI was implemented similar to the context menu. The UI
itself lives in TermControl, but it can be populated by other layers
(i.e. TermApp layer).

Quick Fix suggestions are also automatically loaded into the Suggestions
UI.

If a quick fix is available and a screen reader is attached, we dispatch
an announcement that quick fixes are available to notify the user that
that's the case.

Spec: #17005
#16599

### Follow-ups
- #17377: Add a key binding for quick fix
- #17378: Use winget to search for packages using `missingCmd`

---------

Co-authored-by: Dustin L. Howett <duhowett@microsoft.com>
Co-authored-by: Dustin L. Howett <dustin@howett.net>
This commit is contained in:
Carlos Zamora 2024-06-28 16:27:31 -07:00 committed by GitHub
parent 024837c50f
commit 6589957d4d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
34 changed files with 517 additions and 43 deletions

View File

@ -1340,7 +1340,7 @@ namespace winrt::TerminalApp::implementation
// requires context from the control)
// then get that here.
const bool shouldGetContext = realArgs.UseCommandline() ||
WI_IsFlagSet(source, SuggestionsSource::CommandHistory);
WI_IsAnyFlagSet(source, SuggestionsSource::CommandHistory | SuggestionsSource::QuickFixes);
if (shouldGetContext)
{
if (const auto& control{ _GetActiveControl() })
@ -1373,7 +1373,20 @@ namespace winrt::TerminalApp::implementation
if (WI_IsFlagSet(source, SuggestionsSource::CommandHistory) &&
context != nullptr)
{
const auto recentCommands = Command::HistoryToCommands(context.History(), currentCommandline, false);
// \ue81c --> History icon
const auto recentCommands = Command::HistoryToCommands(context.History(), currentCommandline, false, hstring{ L"\ue81c" });
for (const auto& t : recentCommands)
{
commandsCollection.push_back(t);
}
}
if (WI_IsFlagSet(source, SuggestionsSource::QuickFixes) &&
context != nullptr &&
context.QuickFixes() != nullptr)
{
// \ue74c --> OEM icon
const auto recentCommands = Command::HistoryToCommands(context.QuickFixes(), hstring{ L"" }, false, hstring{ L"\ue74c" });
for (const auto& t : recentCommands)
{
commandsCollection.push_back(t);

View File

@ -1735,6 +1735,8 @@ namespace winrt::TerminalApp::implementation
term.ShowWindowChanged({ get_weak(), &TerminalPage::_ShowWindowChangedHandler });
term.SearchMissingCommand({ get_weak(), &TerminalPage::_SearchMissingCommandHandler });
// Don't even register for the event if the feature is compiled off.
if constexpr (Feature_ShellCompletions::IsEnabled())
{
@ -1753,6 +1755,12 @@ namespace winrt::TerminalApp::implementation
page->_PopulateContextMenu(weakTerm.get(), sender.try_as<MUX::Controls::CommandBarFlyout>(), true);
}
});
term.QuickFixMenu().Opening([weak = get_weak(), weakTerm](auto&& sender, auto&& /*args*/) {
if (const auto& page{ weak.get() })
{
page->_PopulateQuickFixMenu(weakTerm.get(), sender.try_as<Controls::MenuFlyout>());
}
});
}
// Method Description:
@ -2985,6 +2993,30 @@ namespace winrt::TerminalApp::implementation
ShowWindowChanged.raise(*this, args);
}
winrt::fire_and_forget TerminalPage::_SearchMissingCommandHandler(const IInspectable /*sender*/, const Microsoft::Terminal::Control::SearchMissingCommandEventArgs args)
{
assert(!Dispatcher().HasThreadAccess());
if (!Feature_QuickFix::IsEnabled())
{
co_return;
}
std::vector<hstring> suggestions;
suggestions.reserve(1);
suggestions.emplace_back(fmt::format(FMT_COMPILE(L"winget install {}"), args.MissingCommand()));
co_await wil::resume_foreground(Dispatcher());
auto term = _GetActiveControl();
if (!term)
{
co_return;
}
term.UpdateWinGetSuggestions(single_threaded_vector<hstring>(std::move(suggestions)));
term.RefreshQuickFixMenu();
}
// Method Description:
// - Paste text from the Windows Clipboard to the focused terminal
void TerminalPage::_PasteText()
@ -4905,6 +4937,53 @@ namespace winrt::TerminalApp::implementation
makeItem(RS_(L"TabClose"), L"\xE711", ActionAndArgs{ ShortcutAction::CloseTab, CloseTabArgs{ _GetFocusedTabIndex().value() } });
}
void TerminalPage::_PopulateQuickFixMenu(const TermControl& control,
const Controls::MenuFlyout& menu)
{
if (!control || !menu)
{
return;
}
// Helper lambda for dispatching a SendInput ActionAndArgs onto the
// ShortcutActionDispatch. Used below to wire up each menu entry to the
// respective action. Then clear the quick fix menu.
auto weak = get_weak();
auto makeCallback = [weak](const hstring& suggestion) {
return [weak, suggestion](auto&&, auto&&) {
if (auto page{ weak.get() })
{
const auto actionAndArgs = ActionAndArgs{ ShortcutAction::SendInput, SendInputArgs{ hstring{ L"\u0003" } + suggestion } };
page->_actionDispatch->DoAction(actionAndArgs);
if (auto ctrl = page->_GetActiveControl())
{
ctrl.ClearQuickFix();
}
}
};
};
// Wire up each item to the action that should be performed. By actually
// connecting these to actions, we ensure the implementation is
// consistent. This also leaves room for customizing this menu with
// actions in the future.
menu.Items().Clear();
const auto quickFixes = control.CommandHistory().QuickFixes();
for (const auto& qf : quickFixes)
{
MenuFlyoutItem item{};
auto iconElement = UI::IconPathConverter::IconWUX(L"\ue74c");
Automation::AutomationProperties::SetAccessibilityView(iconElement, Automation::Peers::AccessibilityView::Raw);
item.Icon(iconElement);
item.Text(qf);
item.Click(makeCallback(qf));
menu.Items().Append(item);
}
}
// Handler for our WindowProperties's PropertyChanged event. We'll use this
// to pop the "Identify Window" toast when the user renames our window.
winrt::fire_and_forget TerminalPage::_windowPropertyChanged(const IInspectable& /*sender*/,

View File

@ -522,6 +522,7 @@ namespace winrt::TerminalApp::implementation
void _OpenSuggestions(const Microsoft::Terminal::Control::TermControl& sender, Windows::Foundation::Collections::IVector<winrt::Microsoft::Terminal::Settings::Model::Command> commandsCollection, winrt::TerminalApp::SuggestionsMode mode, winrt::hstring filterText);
void _ShowWindowChangedHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::ShowWindowArgs args);
winrt::fire_and_forget _SearchMissingCommandHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::SearchMissingCommandEventArgs args);
winrt::fire_and_forget _windowPropertyChanged(const IInspectable& sender, const winrt::Windows::UI::Xaml::Data::PropertyChangedEventArgs& args);
@ -539,6 +540,7 @@ namespace winrt::TerminalApp::implementation
void _sendDraggedTabToWindow(const winrt::hstring& windowId, const uint32_t tabIndex, std::optional<til::point> dragPoint);
void _PopulateContextMenu(const Microsoft::Terminal::Control::TermControl& control, const Microsoft::UI::Xaml::Controls::CommandBarFlyout& sender, const bool withSelection);
void _PopulateQuickFixMenu(const Microsoft::Terminal::Control::TermControl& control, const Windows::UI::Xaml::Controls::MenuFlyout& sender);
winrt::Windows::UI::Xaml::Controls::MenuFlyout _CreateRunAsAdminFlyout(int profileIndex);
winrt::Microsoft::Terminal::Control::TermControl _senderOrActiveControl(const winrt::Windows::Foundation::IInspectable& sender);

View File

@ -128,6 +128,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
auto pfnCompletionsChanged = [=](auto&& menuJson, auto&& replaceLength) { _terminalCompletionsChanged(menuJson, replaceLength); };
_terminal->CompletionsChangedCallback(pfnCompletionsChanged);
auto pfnSearchMissingCommand = [this](auto&& PH1) { _terminalSearchMissingCommand(std::forward<decltype(PH1)>(PH1)); };
_terminal->SetSearchMissingCommandCallback(pfnSearchMissingCommand);
auto pfnClearQuickFix = [this] { ClearQuickFix(); };
_terminal->SetClearQuickFixCallback(pfnClearQuickFix);
// MSFT 33353327: Initialize the renderer in the ctor instead of Initialize().
// We need the renderer to be ready to accept new engines before the SwapChainPanel is ready to go.
// If we wait, a screen reader may try to get the AutomationPeer (aka the UIA Engine), and we won't be able to attach
@ -1627,6 +1633,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_midiAudio.PlayNote(reinterpret_cast<HWND>(_owningHwnd), noteNumber, velocity, std::chrono::duration_cast<std::chrono::milliseconds>(duration));
}
void ControlCore::_terminalSearchMissingCommand(std::wstring_view missingCommand)
{
SearchMissingCommand.raise(*this, make<implementation::SearchMissingCommandEventArgs>(hstring{ missingCommand }));
}
void ControlCore::ClearQuickFix()
{
_cachedQuickFixes = nullptr;
RefreshQuickFixUI.raise(*this, nullptr);
}
bool ControlCore::HasSelection() const
{
const auto lock = _terminal->LockForReading();
@ -2297,9 +2314,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation
auto context = winrt::make_self<CommandHistoryContext>(std::move(commands));
context->CurrentCommandline(trimmedCurrentCommand);
context->QuickFixes(_cachedQuickFixes);
return *context;
}
bool ControlCore::QuickFixesAvailable() const noexcept
{
return _cachedQuickFixes && _cachedQuickFixes.Size() > 0;
}
void ControlCore::UpdateQuickFixes(const Windows::Foundation::Collections::IVector<hstring>& quickFixes)
{
_cachedQuickFixes = quickFixes;
}
Core::Scheme ControlCore::ColorScheme() const noexcept
{
Core::Scheme s;

View File

@ -68,8 +68,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
til::property<Windows::Foundation::Collections::IVector<winrt::hstring>> History;
til::property<winrt::hstring> CurrentCommandline;
til::property<Windows::Foundation::Collections::IVector<winrt::hstring>> QuickFixes;
CommandHistoryContext(std::vector<winrt::hstring>&& history)
CommandHistoryContext(std::vector<winrt::hstring>&& history) :
QuickFixes(winrt::single_threaded_vector<winrt::hstring>())
{
History(winrt::single_threaded_vector<winrt::hstring>(std::move(history)));
}
@ -153,6 +155,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void PersistToPath(const wchar_t* path) const;
void RestoreFromPath(const wchar_t* path) const;
void ClearQuickFix();
#pragma region ICoreState
const size_t TaskbarState() const noexcept;
const size_t TaskbarProgress() const noexcept;
@ -241,6 +245,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
hstring ReadEntireBuffer() const;
Control::CommandHistoryContext CommandHistory() const;
bool QuickFixesAvailable() const noexcept;
void UpdateQuickFixes(const Windows::Foundation::Collections::IVector<hstring>& quickFixes);
void AdjustOpacity(const float opacity, const bool relative);
@ -285,6 +291,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
til::typed_event<IInspectable, Control::UpdateSelectionMarkersEventArgs> UpdateSelectionMarkers;
til::typed_event<IInspectable, Control::OpenHyperlinkEventArgs> OpenHyperlink;
til::typed_event<IInspectable, Control::CompletionsChangedEventArgs> CompletionsChanged;
til::typed_event<IInspectable, Control::SearchMissingCommandEventArgs> SearchMissingCommand;
til::typed_event<> RefreshQuickFixUI;
til::typed_event<> CloseTerminalRequested;
til::typed_event<> RestartTerminalRequested;
@ -354,6 +362,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
til::point _contextMenuBufferPosition{ 0, 0 };
Windows::Foundation::Collections::IVector<hstring> _cachedQuickFixes{ nullptr };
void _setupDispatcherAndCallbacks();
bool _setFontSizeUnderLock(float fontSize);
@ -377,6 +387,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void _terminalPlayMidiNote(const int noteNumber,
const int velocity,
const std::chrono::microseconds duration);
void _terminalSearchMissingCommand(std::wstring_view missingCommand);
winrt::fire_and_forget _terminalCompletionsChanged(std::wstring_view menuJson, unsigned int replaceLength);

View File

@ -70,6 +70,7 @@ namespace Microsoft.Terminal.Control
{
IVector<String> History { get; };
String CurrentCommandline { get; };
IVector<String> QuickFixes { get; };
};
[default_interface] runtimeclass ControlCore : ICoreState
@ -155,6 +156,7 @@ namespace Microsoft.Terminal.Control
String ReadEntireBuffer();
CommandHistoryContext CommandHistory();
Boolean QuickFixesAvailable { get; };
void AdjustOpacity(Single Opacity, Boolean relative);
void WindowVisibilityChanged(Boolean showOrHide);
@ -166,6 +168,8 @@ namespace Microsoft.Terminal.Control
Boolean ShouldShowSelectCommand();
Boolean ShouldShowSelectOutput();
void ClearQuickFix();
// These events are called from some background thread
event Windows.Foundation.TypedEventHandler<Object, TitleChangedEventArgs> TitleChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> WarningBell;
@ -174,6 +178,8 @@ namespace Microsoft.Terminal.Control
event Windows.Foundation.TypedEventHandler<Object, Object> TaskbarProgressChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> RendererEnteredErrorState;
event Windows.Foundation.TypedEventHandler<Object, ShowWindowArgs> ShowWindowChanged;
event Windows.Foundation.TypedEventHandler<Object, SearchMissingCommandEventArgs> SearchMissingCommand;
event Windows.Foundation.TypedEventHandler<Object, Object> RefreshQuickFixUI;
// These events are always called from the UI thread (bugs aside)
event Windows.Foundation.TypedEventHandler<Object, FontSizeChangedArgs> FontSizeChanged;

View File

@ -18,3 +18,4 @@
#include "KeySentEventArgs.g.cpp"
#include "CharSentEventArgs.g.cpp"
#include "StringSentEventArgs.g.cpp"
#include "SearchMissingCommandEventArgs.g.cpp"

View File

@ -18,6 +18,7 @@
#include "KeySentEventArgs.g.h"
#include "CharSentEventArgs.g.h"
#include "StringSentEventArgs.g.h"
#include "SearchMissingCommandEventArgs.g.h"
namespace winrt::Microsoft::Terminal::Control::implementation
{
@ -211,6 +212,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
WINRT_PROPERTY(winrt::hstring, Text);
};
struct SearchMissingCommandEventArgs : public SearchMissingCommandEventArgsT<SearchMissingCommandEventArgs>
{
public:
SearchMissingCommandEventArgs(const winrt::hstring& missingCommand) :
MissingCommand(missingCommand) {}
til::property<winrt::hstring> MissingCommand;
};
}
namespace winrt::Microsoft::Terminal::Control::factory_implementation

View File

@ -126,4 +126,9 @@ namespace Microsoft.Terminal.Control
{
String Text { get; };
}
runtimeclass SearchMissingCommandEventArgs
{
String MissingCommand { get; };
}
}

View File

@ -301,6 +301,12 @@ Please either install the missing font or choose another one.</value>
<value>Select output</value>
<comment>The tooltip for a button for selecting all of a command's output</comment>
</data>
<data name="QuickFixButton.ToolTipService.ToolTip" xml:space="preserve">
<value>Quick fix</value>
</data>
<data name="QuickFixButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Quick fix</value>
</data>
<data name="SessionRestoreMessage" xml:space="preserve">
<value>Restored</value>
<comment>"Restored" as in "This content was restored"</comment>
@ -317,6 +323,13 @@ Please either install the missing font or choose another one.</value>
<value>invalid</value>
<comment>This brief message is displayed when a regular expression is invalid.</comment>
</data>
<data name="QuickFixAvailable" xml:space="preserve">
<value>Quick fix available</value>
<comment>"Quick fix" is referencing the same feature as "QuickFixButton.ToolTipService.ToolTip".</comment>
</data>
<data name="QuickFixMenu.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Quick fix</value>
</data>
<data name="PreviewTextAnnouncement" xml:space="preserve">
<value>Suggested input: {0}</value>
<comment>{Locked="{0}"} {0} will be replaced with a string of input that is suggested for the user to input</comment>

View File

@ -40,6 +40,9 @@ constexpr const auto UpdatePatternLocationsInterval = std::chrono::milliseconds(
// The minimum delay between emitting warning bells
constexpr const auto TerminalWarningBellInterval = std::chrono::milliseconds(1000);
constexpr std::wstring_view StateNormal{ L"Normal" };
constexpr std::wstring_view StateCollapsed{ L"Collapsed" };
DEFINE_ENUM_FLAG_OPERATORS(winrt::Microsoft::Terminal::Control::CopyFormat);
DEFINE_ENUM_FLAG_OPERATORS(winrt::Microsoft::Terminal::Control::MouseButtonState);
@ -220,9 +223,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_revokers.CloseTerminalRequested = _core.CloseTerminalRequested(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleCloseTerminalRequested });
_revokers.CompletionsChanged = _core.CompletionsChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleCompletionsChanged });
_revokers.RestartTerminalRequested = _core.RestartTerminalRequested(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleRestartTerminalRequested });
_revokers.SearchMissingCommand = _core.SearchMissingCommand(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleSearchMissingCommand });
_revokers.PasteFromClipboard = _interactivity.PasteFromClipboard(winrt::auto_revoke, { get_weak(), &TermControl::_bubblePasteFromClipboard });
_revokers.RefreshQuickFixUI = _core.RefreshQuickFixUI(winrt::auto_revoke, [this](auto /*s*/, auto /*e*/) {
RefreshQuickFixMenu();
});
// Initialize the terminal only once the swapchainpanel is loaded - that
// way, we'll be able to query the real pixel size it got on layout
_layoutUpdatedRevoker = SwapChainPanel().LayoutUpdated(winrt::auto_revoke, [this](auto /*s*/, auto /*e*/) {
@ -334,6 +342,22 @@ namespace winrt::Microsoft::Terminal::Control::implementation
});
}
void TermControl::_QuickFixButton_PointerEntered(const IInspectable& /*sender*/, const PointerRoutedEventArgs& /*e*/)
{
if (!_IsClosing() && _quickFixButtonCollapsible)
{
VisualStateManager::GoToState(*this, StateNormal, false);
}
}
void TermControl::_QuickFixButton_PointerExited(const IInspectable& /*sender*/, const PointerRoutedEventArgs& /*e*/)
{
if (!_IsClosing() && _quickFixButtonCollapsible)
{
VisualStateManager::GoToState(*this, StateCollapsed, false);
}
}
// Function Description:
// - Static helper for building a new TermControl from an already existing
// content. We'll attach the existing swapchain to this new control's
@ -832,6 +856,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// When we hot reload the settings, the core will send us a scrollbar
// update. If we enabled scrollbar marks, then great, when we handle
// that message, we'll redraw them.
// update the position of the quick fix menu (in case we changed the padding)
RefreshQuickFixMenu();
}
// Method Description:
@ -2347,6 +2374,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
_updateSelectionMarkers(nullptr, winrt::make<UpdateSelectionMarkersEventArgs>(false));
}
RefreshQuickFixMenu();
}
hstring TermControl::Title()
@ -3506,7 +3535,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const Control::FontSizeChangedArgs& args)
{
// scale the selection markers to be the size of a cell
auto scaleMarker = [args, dpiScale{ SwapChainPanel().CompositionScaleX() }](const Windows::UI::Xaml::Shapes::Path& shape) {
const auto dpiScale = SwapChainPanel().CompositionScaleX();
auto scaleMarker = [&args, &dpiScale](const Windows::UI::Xaml::Shapes::Path& shape) {
// The selection markers were designed to be 5x14 in size,
// so use those dimensions below for the scaling
const auto scaleX = args.Width() / 5.0 / dpiScale;
@ -3522,6 +3552,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
};
scaleMarker(SelectionStartMarker());
scaleMarker(SelectionEndMarker());
if (Feature_QuickFix::IsEnabled())
{
auto quickFixBtn = FindName(L"QuickFixButton").as<Controls::Button>();
quickFixBtn.Height(args.Height() / dpiScale);
QuickFixIcon().FontSize(static_cast<double>(args.Width() / dpiScale));
RefreshQuickFixMenu();
}
}
void TermControl::_coreRaisedNotice(const IInspectable& /*sender*/,
@ -3590,6 +3628,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return _core.CommandHistory();
}
void TermControl::UpdateWinGetSuggestions(Windows::Foundation::Collections::IVector<hstring> suggestions)
{
get_self<ControlCore>(_core)->UpdateQuickFixes(suggestions);
}
Core::Scheme TermControl::ColorScheme() const noexcept
{
return _core.ColorScheme();
@ -3829,6 +3872,86 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_showContextMenuAt(_toControlOrigin(cursorPos));
}
double TermControl::QuickFixButtonWidth()
{
const auto leftPadding = GetPadding().Left;
if (_quickFixButtonCollapsible)
{
const auto cellWidth = CharacterDimensions().Width;
if (leftPadding == 0)
{
return cellWidth;
}
return leftPadding + (cellWidth / 2.0);
}
return leftPadding;
}
double TermControl::QuickFixButtonCollapsedWidth()
{
return std::max(CharacterDimensions().Width * 2.0 / 3.0, GetPadding().Left);
}
void TermControl::RefreshQuickFixMenu()
{
if (!Feature_QuickFix::IsEnabled())
{
return;
}
auto quickFixBtn = FindName(L"QuickFixButton").as<Controls::Button>();
if (!_core.QuickFixesAvailable())
{
quickFixBtn.Visibility(Visibility::Collapsed);
return;
}
// If the gutter is narrow, display the collapsed version
const auto& termPadding = GetPadding();
// Make sure to update _quickFixButtonCollapsible and QuickFix button widths BEFORE updating the VisualState
_quickFixButtonCollapsible = termPadding.Left < CharacterDimensions().Width;
PropertyChanged.raise(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"QuickFixButtonWidth" });
PropertyChanged.raise(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"QuickFixButtonCollapsedWidth" });
VisualStateManager::GoToState(*this, !_quickFixButtonCollapsible ? StateNormal : StateCollapsed, false);
const auto rd = get_self<ControlCore>(_core)->GetRenderData();
rd->LockConsole();
const auto viewportBufferPosition = rd->GetViewport();
const auto cursorBufferPosition = rd->GetCursorPosition();
rd->UnlockConsole();
if (cursorBufferPosition.y < viewportBufferPosition.Top() || cursorBufferPosition.y > viewportBufferPosition.BottomExclusive())
{
quickFixBtn.Visibility(Visibility::Collapsed);
return;
}
// draw the button in the gutter
const auto& cursorPosInDips = CursorPositionInDips();
Controls::Canvas::SetLeft(quickFixBtn, -termPadding.Left);
Controls::Canvas::SetTop(quickFixBtn, cursorPosInDips.Y - termPadding.Top);
quickFixBtn.Visibility(Visibility::Visible);
if (auto automationPeer{ FrameworkElementAutomationPeer::FromElement(*this) })
{
automationPeer.RaiseNotificationEvent(
AutomationNotificationKind::ItemAdded,
AutomationNotificationProcessing::ImportantMostRecent,
RS_(L"QuickFixAvailable"),
L"QuickFixAvailableAnnouncement" /* unique name for this group of notifications */);
}
}
void TermControl::_bubbleSearchMissingCommand(const IInspectable& /*sender*/, const Control::SearchMissingCommandEventArgs& args)
{
SearchMissingCommand.raise(*this, args);
}
void TermControl::ClearQuickFix()
{
_core.ClearQuickFix();
}
void TermControl::_PasteCommandHandler(const IInspectable& /*sender*/,
const IInspectable& /*args*/)
{

View File

@ -76,6 +76,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void PreviewInput(const winrt::hstring& text);
Windows::Foundation::Point CursorPositionInDips();
double QuickFixButtonWidth();
double QuickFixButtonCollapsedWidth();
void WindowVisibilityChanged(const bool showOrHide);
@ -168,6 +170,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
hstring ReadEntireBuffer() const;
Control::CommandHistoryContext CommandHistory() const;
void UpdateWinGetSuggestions(Windows::Foundation::Collections::IVector<hstring> suggestions);
winrt::Microsoft::Terminal::Core::Scheme ColorScheme() const noexcept;
void ColorScheme(const winrt::Microsoft::Terminal::Core::Scheme& scheme) const noexcept;
@ -179,6 +182,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void RawWriteString(const winrt::hstring& text);
void ShowContextMenu();
void RefreshQuickFixMenu();
void ClearQuickFix();
void Detach();
@ -203,17 +208,18 @@ namespace winrt::Microsoft::Terminal::Control::implementation
til::typed_event<IInspectable, Control::KeySentEventArgs> KeySent;
til::typed_event<IInspectable, Control::CharSentEventArgs> CharSent;
til::typed_event<IInspectable, Control::StringSentEventArgs> StringSent;
til::typed_event<IInspectable, Control::SearchMissingCommandEventArgs> SearchMissingCommand;
// UNDER NO CIRCUMSTANCES SHOULD YOU ADD A (PROJECTED_)FORWARDED_TYPED_EVENT HERE
// Those attach the handler to the core directly, and will explode if
// the core ever gets detached & reattached to another window.
BUBBLED_FORWARDED_TYPED_EVENT(TitleChanged, IInspectable, Control::TitleChangedEventArgs);
BUBBLED_FORWARDED_TYPED_EVENT(TabColorChanged, IInspectable, IInspectable);
BUBBLED_FORWARDED_TYPED_EVENT(SetTaskbarProgress, IInspectable, IInspectable);
BUBBLED_FORWARDED_TYPED_EVENT(ConnectionStateChanged, IInspectable, IInspectable);
BUBBLED_FORWARDED_TYPED_EVENT(ShowWindowChanged, IInspectable, Control::ShowWindowArgs);
BUBBLED_FORWARDED_TYPED_EVENT(CloseTerminalRequested, IInspectable, IInspectable);
BUBBLED_FORWARDED_TYPED_EVENT(CompletionsChanged, IInspectable, Control::CompletionsChangedEventArgs);
BUBBLED_FORWARDED_TYPED_EVENT(TitleChanged, IInspectable, Control::TitleChangedEventArgs);
BUBBLED_FORWARDED_TYPED_EVENT(TabColorChanged, IInspectable, IInspectable);
BUBBLED_FORWARDED_TYPED_EVENT(SetTaskbarProgress, IInspectable, IInspectable);
BUBBLED_FORWARDED_TYPED_EVENT(ConnectionStateChanged, IInspectable, IInspectable);
BUBBLED_FORWARDED_TYPED_EVENT(ShowWindowChanged, IInspectable, Control::ShowWindowArgs);
BUBBLED_FORWARDED_TYPED_EVENT(CloseTerminalRequested, IInspectable, IInspectable);
BUBBLED_FORWARDED_TYPED_EVENT(CompletionsChanged, IInspectable, Control::CompletionsChangedEventArgs);
BUBBLED_FORWARDED_TYPED_EVENT(RestartTerminalRequested, IInspectable, IInspectable);
BUBBLED_FORWARDED_TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs);
@ -243,6 +249,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool _closing{ false };
bool _focused{ false };
bool _initializedTerminal{ false };
bool _quickFixButtonCollapsible{ false };
bool _quickFixesAvailable{ false };
std::shared_ptr<ThrottledFuncLeading> _playWarningBell;
@ -333,6 +341,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void _MouseWheelHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e);
void _ScrollbarChangeHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Controls::Primitives::RangeBaseValueChangedEventArgs& e);
void _QuickFixButton_PointerEntered(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e);
void _QuickFixButton_PointerExited(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e);
void _GotFocusHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e);
void _LostFocusHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e);
@ -395,6 +406,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void _contextMenuHandler(IInspectable sender, Control::ContextMenuRequestedEventArgs args);
void _showContextMenuAt(const til::point& controlRelativePos);
void _bubbleSearchMissingCommand(const IInspectable& sender, const Control::SearchMissingCommandEventArgs& args);
void _PasteCommandHandler(const IInspectable& sender, const IInspectable& args);
void _CopyCommandHandler(const IInspectable& sender, const IInspectable& args);
void _SearchCommandHandler(const IInspectable& sender, const IInspectable& args);
@ -424,6 +437,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
Control::ControlCore::CloseTerminalRequested_revoker CloseTerminalRequested;
Control::ControlCore::CompletionsChanged_revoker CompletionsChanged;
Control::ControlCore::RestartTerminalRequested_revoker RestartTerminalRequested;
Control::ControlCore::SearchMissingCommand_revoker SearchMissingCommand;
Control::ControlCore::RefreshQuickFixUI_revoker RefreshQuickFixUI;
// These are set up in _InitializeTerminal
Control::ControlCore::RendererWarning_revoker RendererWarning;

View File

@ -69,10 +69,12 @@ namespace Microsoft.Terminal.Control
event Windows.Foundation.TypedEventHandler<Object, KeySentEventArgs> KeySent;
event Windows.Foundation.TypedEventHandler<Object, CharSentEventArgs> CharSent;
event Windows.Foundation.TypedEventHandler<Object, StringSentEventArgs> StringSent;
event Windows.Foundation.TypedEventHandler<Object, SearchMissingCommandEventArgs> SearchMissingCommand;
Microsoft.UI.Xaml.Controls.CommandBarFlyout ContextMenu { get; };
Microsoft.UI.Xaml.Controls.CommandBarFlyout SelectionContextMenu { get; };
Windows.UI.Xaml.Controls.MenuFlyout QuickFixMenu { get; };
event Windows.Foundation.TypedEventHandler<TermControl, Windows.UI.Xaml.RoutedEventArgs> Initialized;
// This is an event handler forwarder for the underlying connection.
@ -125,6 +127,7 @@ namespace Microsoft.Terminal.Control
String ReadEntireBuffer();
CommandHistoryContext CommandHistory();
void UpdateWinGetSuggestions(Windows.Foundation.Collections.IVector<String> suggestions);
void AdjustOpacity(Single Opacity, Boolean relative);
void PreviewInput(String text);
@ -142,8 +145,12 @@ namespace Microsoft.Terminal.Control
void ColorSelection(SelectionColor fg, SelectionColor bg, Microsoft.Terminal.Core.MatchMode matchMode);
Windows.Foundation.Point CursorPositionInDips { get; };
Double QuickFixButtonWidth { get; };
Double QuickFixButtonCollapsedWidth { get; };
void ShowContextMenu();
void RefreshQuickFixMenu();
void ClearQuickFix();
void Detach();
}

View File

@ -1279,6 +1279,32 @@
</ToolTip>
</ToolTipService.ToolTip>
</Border>
<Button x:Name="QuickFixButton"
x:Uid="QuickFixButton"
Width="{x:Bind QuickFixButtonWidth, Mode=OneWay}"
Padding="0"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
x:Load="False"
AllowFocusOnInteraction="False"
CornerRadius="0,5,5,0"
PointerEntered="_QuickFixButton_PointerEntered"
PointerExited="_QuickFixButton_PointerExited"
Style="{StaticResource AccentButtonStyle}"
Visibility="Collapsed">
<Button.Content>
<FontIcon x:Name="QuickFixIcon"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
Glyph="&#xEA80;" />
</Button.Content>
<Button.Flyout>
<MenuFlyout x:Name="QuickFixMenu"
x:Uid="QuickFixMenu" />
</Button.Flyout>
</Button>
</Canvas>
<Canvas x:Name="SelectionCanvas"
@ -1353,6 +1379,18 @@
</Border>
</Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="QuickFixButtonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Collapsed">
<VisualState.Setters>
<Setter Target="QuickFixButton.Width" Value="{x:Bind QuickFixButtonCollapsedWidth, Mode=OneWay}" />
<Setter Target="QuickFixButton.Background" Value="{ThemeResource SystemAccentColor}" />
<Setter Target="QuickFixIcon.Visibility" Value="Collapsed" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</UserControl>

View File

@ -700,34 +700,44 @@ TerminalInput::OutputType Terminal::SendCharEvent(const wchar_t ch, const WORD s
vkey = _VirtualKeyFromCharacter(ch);
}
// GH#1527: When the user has auto mark prompts enabled, we're going to try
// and heuristically detect if this was the line the prompt was on.
// * If the key was an Enter keypress (Terminal.app also marks ^C keypresses
// as prompts. That's omitted for now.)
// * AND we're not in the alt buffer
//
// Then treat this line like it's a prompt mark.
if (_autoMarkPrompts && vkey == VK_RETURN && !_inAltBuffer())
if (vkey == VK_RETURN && !_inAltBuffer())
{
// We need to be a little tricky here, to try and support folks that are
// auto-marking prompts, but don't necessarily have the rest of shell
// integration enabled.
//
// We'll set the current attributes to Output, so that the output after
// here is marked as the command output. But we also need to make sure
// that a mark was started.
// We can't just check if the current row has a mark - there could be a
// multiline prompt.
//
// (TextBuffer::_createPromptMarkIfNeeded does that work for us)
const bool createdMark = _activeBuffer().StartOutput();
if (createdMark)
// Treat VK_RETURN as a new prompt,
// so we should clear the quick fix UI if it's visible.
if (_pfnClearQuickFix)
{
_activeBuffer().ManuallyMarkRowAsPrompt(_activeBuffer().GetCursor().GetPosition().y);
_pfnClearQuickFix();
}
// This changed the scrollbar marks - raise a notification to update them
_NotifyScrollEvent();
// GH#1527: When the user has auto mark prompts enabled, we're going to try
// and heuristically detect if this was the line the prompt was on.
// * If the key was an Enter keypress (Terminal.app also marks ^C keypresses
// as prompts. That's omitted for now.)
// * AND we're not in the alt buffer
//
// Then treat this line like it's a prompt mark.
if (_autoMarkPrompts)
{
// We need to be a little tricky here, to try and support folks that are
// auto-marking prompts, but don't necessarily have the rest of shell
// integration enabled.
//
// We'll set the current attributes to Output, so that the output after
// here is marked as the command output. But we also need to make sure
// that a mark was started.
// We can't just check if the current row has a mark - there could be a
// multiline prompt.
//
// (TextBuffer::_createPromptMarkIfNeeded does that work for us)
const bool createdMark = _activeBuffer().StartOutput();
if (createdMark)
{
_activeBuffer().ManuallyMarkRowAsPrompt(_activeBuffer().GetCursor().GetPosition().y);
// This changed the scrollbar marks - raise a notification to update them
_NotifyScrollEvent();
}
}
}
@ -1228,6 +1238,16 @@ void Microsoft::Terminal::Core::Terminal::CompletionsChangedCallback(std::functi
_pfnCompletionsChanged.swap(pfn);
}
void Microsoft::Terminal::Core::Terminal::SetSearchMissingCommandCallback(std::function<void(std::wstring_view)> pfn) noexcept
{
_pfnSearchMissingCommand.swap(pfn);
}
void Microsoft::Terminal::Core::Terminal::SetClearQuickFixCallback(std::function<void()> pfn) noexcept
{
_pfnClearQuickFix.swap(pfn);
}
// Method Description:
// - Stores the search highlighted regions in the terminal
void Terminal::SetSearchHighlights(const std::vector<til::point_span>& highlights) noexcept

View File

@ -157,6 +157,8 @@ public:
void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) override;
void SearchMissingCommand(const std::wstring_view command) override;
#pragma endregion
void ClearMark();
@ -229,6 +231,8 @@ public:
void SetShowWindowCallback(std::function<void(bool)> pfn) noexcept;
void SetPlayMidiNoteCallback(std::function<void(const int, const int, const std::chrono::microseconds)> pfn) noexcept;
void CompletionsChangedCallback(std::function<void(std::wstring_view, unsigned int)> pfn) noexcept;
void SetSearchMissingCommandCallback(std::function<void(std::wstring_view)> pfn) noexcept;
void SetClearQuickFixCallback(std::function<void()> pfn) noexcept;
void SetSearchHighlights(const std::vector<til::point_span>& highlights) noexcept;
void SetSearchHighlightFocused(const size_t focusedIdx);
@ -339,6 +343,8 @@ private:
std::function<void(bool)> _pfnShowWindowChanged;
std::function<void(const int, const int, const std::chrono::microseconds)> _pfnPlayMidiNote;
std::function<void(std::wstring_view, unsigned int)> _pfnCompletionsChanged;
std::function<void(std::wstring_view)> _pfnSearchMissingCommand;
std::function<void()> _pfnClearQuickFix;
RenderSettings _renderSettings;
std::unique_ptr<::Microsoft::Console::VirtualTerminal::StateMachine> _stateMachine;

View File

@ -333,6 +333,14 @@ void Terminal::InvokeCompletions(std::wstring_view menuJson, unsigned int replac
}
}
void Terminal::SearchMissingCommand(const std::wstring_view command)
{
if (_pfnSearchMissingCommand)
{
_pfnSearchMissingCommand(command);
}
}
void Terminal::NotifyBufferRotation(const int delta)
{
// Update our selection, so it doesn't move as the buffer is cycled

View File

@ -928,6 +928,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
BASIC_FACTORY(SetTabColorArgs);
BASIC_FACTORY(RenameTabArgs);
BASIC_FACTORY(SwapPaneArgs);
BASIC_FACTORY(SendInputArgs);
BASIC_FACTORY(SplitPaneArgs);
BASIC_FACTORY(SetFocusModeArgs);
BASIC_FACTORY(SetFullScreenArgs);

View File

@ -119,6 +119,7 @@ namespace Microsoft.Terminal.Settings.Model
Tasks = 0x1,
CommandHistory = 0x2,
DirectoryHistory = 0x4,
QuickFixes = 0x8,
All = 0xffffffff,
};
@ -225,6 +226,8 @@ namespace Microsoft.Terminal.Settings.Model
[default_interface] runtimeclass SendInputArgs : IActionArgs
{
SendInputArgs(String input);
String Input { get; };
};

View File

@ -673,7 +673,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// the command will be run as a directory change instead.
IVector<Model::Command> Command::HistoryToCommands(IVector<winrt::hstring> history,
winrt::hstring currentCommandline,
bool directories)
bool directories,
winrt::hstring iconPath)
{
std::wstring cdText = directories ? L"cd " : L"";
auto result = std::vector<Model::Command>();
@ -705,9 +706,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
auto command = winrt::make_self<Command>();
command->_ActionAndArgs = actionAndArgs;
command->_name = winrt::hstring{ line };
command->_iconPath = directories ?
L"\ue8da" : // OpenLocal (a folder with an arrow pointing up)
L"\ue81c"; // History icon
command->_iconPath = iconPath;
result.push_back(*command);
foundCommands[line] = true;
}

View File

@ -79,7 +79,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
static Windows::Foundation::Collections::IVector<Model::Command> ParsePowerShellMenuComplete(winrt::hstring json, int32_t replaceLength);
static Windows::Foundation::Collections::IVector<Model::Command> HistoryToCommands(Windows::Foundation::Collections::IVector<winrt::hstring> history,
winrt::hstring currentCommandline,
bool directories);
bool directories,
hstring iconPath);
WINRT_PROPERTY(ExpandCommandType, IterateOn, ExpandCommandType::None);
WINRT_PROPERTY(Model::ActionAndArgs, ActionAndArgs);

View File

@ -48,7 +48,7 @@ namespace Microsoft.Terminal.Settings.Model
Windows.Foundation.Collections.IMapView<String, Command> NestedCommands { get; };
static IVector<Command> ParsePowerShellMenuComplete(String json, Int32 replaceLength);
static IVector<Command> HistoryToCommands(IVector<String> commandHistory, String commandline, Boolean directories);
static IVector<Command> HistoryToCommands(IVector<String> commandHistory, String commandline, Boolean directories, String iconPath);
}
}

View File

@ -501,11 +501,12 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::FindMatchDirecti
JSON_FLAG_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::SuggestionsSource)
{
static constexpr std::array<pair_type, 5> mappings = {
static constexpr std::array<pair_type, 6> mappings = {
pair_type{ "none", AllClear },
pair_type{ "tasks", ValueType::Tasks },
pair_type{ "commandHistory", ValueType::CommandHistory },
pair_type{ "directoryHistory", ValueType::DirectoryHistory },
pair_type{ "quickFix", ValueType::QuickFixes },
pair_type{ "all", AllSet },
};
};

View File

@ -155,4 +155,15 @@
</alwaysEnabledBrandingTokens>
</feature>
<feature>
<name>Feature_QuickFix</name>
<description>Enables the Quick Fix menu</description>
<id>16599</id>
<stage>AlwaysDisabled</stage>
<alwaysEnabledBrandingTokens>
<brandingToken>Dev</brandingToken>
<brandingToken>Canary</brandingToken>
</alwaysEnabledBrandingTokens>
</feature>
</featureStaging>

View File

@ -419,3 +419,7 @@ void ConhostInternalGetSet::InvokeCompletions(std::wstring_view /*menuJson*/, un
{
// Not implemented for conhost.
}
void ConhostInternalGetSet::SearchMissingCommand(std::wstring_view /*missingCommand*/)
{
// Not implemented for conhost.
}

View File

@ -70,6 +70,8 @@ public:
void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) override;
void SearchMissingCommand(std::wstring_view missingCommand) override;
private:
Microsoft::Console::IIoProvider& _io;
};

View File

@ -147,6 +147,8 @@ public:
virtual bool DoVsCodeAction(const std::wstring_view string) = 0;
virtual bool DoWTAction(const std::wstring_view string) = 0;
virtual StringHandler DownloadDRCS(const VTInt fontNumber,
const VTParameter startChar,
const DispatchTypes::DrcsEraseControl eraseControl,

View File

@ -88,5 +88,7 @@ namespace Microsoft::Console::VirtualTerminal
virtual void NotifyBufferRotation(const int delta) = 0;
virtual void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) = 0;
virtual void SearchMissingCommand(const std::wstring_view command) = 0;
};
}

View File

@ -3995,6 +3995,54 @@ bool AdaptDispatch::DoVsCodeAction(const std::wstring_view string)
return false;
}
// Method Description:
// - Performs a Windows Terminal action
// - Currently, the actions we support are:
// * CmdNotFound: A protocol for passing commands that the shell couldn't resolve
// to the terminal. The command is then shared with WinGet to see if it can
// find a package that provides that command, which is then displayed to the
// user.
// - Not actually used in conhost
// Arguments:
// - string: contains the parameters that define which action we do
// Return Value:
// - false in conhost, true for the CmdNotFound action, otherwise false.
bool AdaptDispatch::DoWTAction(const std::wstring_view string)
{
// This is not implemented in conhost.
if (_api.IsConsolePty())
{
// Flush the frame manually to make sure this action happens at the right time.
_renderer->TriggerFlush(false);
return false;
}
const auto parts = Utils::SplitString(string, L';');
if (parts.size() < 1)
{
return false;
}
const auto action = til::at(parts, 0);
if (action == L"CmdNotFound")
{
// The structure of the message is as follows:
// `e]9001;
// 0: CmdNotFound;
// 1: $($cmdNotFound.missingCmd);
if (parts.size() >= 2)
{
const std::wstring_view missingCmd = til::at(parts, 1);
_api.SearchMissingCommand(missingCmd);
}
return true;
}
return false;
}
// Method Description:
// - DECDLD - Downloads one or more characters of a dynamically redefinable
// character set (DRCS) with a specified pixel pattern. The pixel array is

View File

@ -150,6 +150,8 @@ namespace Microsoft::Console::VirtualTerminal
bool DoVsCodeAction(const std::wstring_view string) override;
bool DoWTAction(const std::wstring_view string) override;
StringHandler DownloadDRCS(const VTInt fontNumber,
const VTParameter startChar,
const DispatchTypes::DrcsEraseControl eraseControl,

View File

@ -140,6 +140,8 @@ public:
bool DoVsCodeAction(const std::wstring_view /*string*/) override { return false; }
bool DoWTAction(const std::wstring_view /*string*/) override { return false; }
StringHandler DownloadDRCS(const VTInt /*fontNumber*/,
const VTParameter /*startChar*/,
const DispatchTypes::DrcsEraseControl /*eraseControl*/,

View File

@ -218,6 +218,11 @@ public:
VERIFY_ARE_EQUAL(_expectedReplaceLength, replaceLength);
}
void SearchMissingCommand(const std::wstring_view /*command*/) override
{
Log::Comment(L"SearchMissingCommand MOCK called...");
}
void PrepData()
{
PrepData(CursorDirection::UP); // if called like this, the cursor direction doesn't matter.

View File

@ -913,6 +913,11 @@ bool OutputStateMachineEngine::ActionOscDispatch(const size_t parameter, const s
success = _dispatch->DoVsCodeAction(string);
break;
}
case OscActionCodes::WTAction:
{
success = _dispatch->DoWTAction(string);
break;
}
default:
// If no functions to call, overall dispatch was a failure.
success = false;

View File

@ -229,6 +229,7 @@ namespace Microsoft::Console::VirtualTerminal
FinalTermAction = 133,
VsCodeAction = 633,
ITerm2Action = 1337,
WTAction = 9001,
};
bool _GetOscSetColorTable(const std::wstring_view string,