PRE-MERGE #16848 Add support for custom CommandNotFound OSC

This commit is contained in:
Mike Griese
2024-06-03 16:18:06 -05:00
36 changed files with 516 additions and 42 deletions

View File

@@ -174,6 +174,23 @@
</ItemGroup>
</Target>
<!-- We need to include some additional files in the package for things like WinGet's Command Not Found -->
<Target Name="IncludeAdditionalFilesInPackage">
<PropertyGroup>
<WinGetAdditionalPackageFileRoot>$(SolutionDir)\src\cascadia\TerminalControl</WinGetAdditionalPackageFileRoot>
</PropertyGroup>
<ItemGroup>
<WinGetAdditionalPackageFile Include="$(WinGetAdditionalPackageFileRoot)\Microsoft.Management.Deployment.winmd">
<PackagePath>Microsoft.Management.Deployment.winmd</PackagePath>
</WinGetAdditionalPackageFile>
</ItemGroup>
<ItemGroup>
<AppxPackagePayload Include="%(WinGetAdditionalPackageFile.Identity))">
<TargetPath>%(WinGetAdditionalPackageFile.PackagePath)</TargetPath>
</AppxPackagePayload>
</ItemGroup>
</Target>
<!-- This is required to get the package dependency in the AppXManifest. -->
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />

View File

@@ -1388,7 +1388,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() })
@@ -1421,7 +1421,19 @@ 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)
{
// \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

@@ -1743,6 +1743,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())
{
@@ -1761,6 +1763,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:
@@ -2993,6 +3001,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(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.ShowQuickFixMenu();
}
// Method Description:
// - Paste text from the Windows Clipboard to the focused terminal
void TerminalPage::_PasteText()
@@ -4982,6 +5014,62 @@ 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();
}
}
};
};
auto makeItem = [&menu, &makeCallback](const winrt::hstring& label,
const winrt::hstring& icon,
const winrt::hstring& suggestion) {
MenuFlyoutItem item{};
if (!icon.empty())
{
auto iconElement = UI::IconPathConverter::IconWUX(icon);
Automation::AutomationProperties::SetAccessibilityView(iconElement, Automation::Peers::AccessibilityView::Raw);
item.Icon(iconElement);
}
item.Text(label);
item.Click(makeCallback(suggestion));
menu.Items().Append(item);
};
// 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)
{
makeItem(qf, L"\ue74c", qf);
}
}
// 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

@@ -529,6 +529,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);
@@ -546,6 +547,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

@@ -13,6 +13,7 @@
#include <unicode.hpp>
#include <utils.hpp>
#include <WinUser.h>
//#include <winrt/Microsoft.Management.Deployment.h>
#include "EventArgs.h"
#include "../../renderer/atlas/AtlasEngine.h"
@@ -23,6 +24,7 @@
#include "ControlCore.g.cpp"
#include "SelectionColor.g.cpp"
//using namespace winrt::Microsoft::Management::Deployment;
using namespace ::Microsoft::Console::Types;
using namespace ::Microsoft::Console::VirtualTerminal;
using namespace ::Microsoft::Terminal::Core;
@@ -128,6 +130,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] { _terminalClearQuickFix(); };
_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 +1635,16 @@ 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::_terminalClearQuickFix()
{
ClearQuickFix.raise(*this, nullptr);
}
bool ControlCore::HasSelection() const
{
const auto lock = _terminal->LockForReading();
@@ -2295,11 +2313,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation
commands.pop_back();
}
auto context = winrt::make_self<CommandHistoryContext>(std::move(commands));
context->CurrentCommandline(trimmedCurrentCommand);
// TODO CARLOS: should we delete this after a new command is run? Or delete it after a suggestion is used? Or just after the next winget suggestion (current impl)?
// No clue which we should do. Thoughts?
context->QuickFixes(_cachedQuickFixes);
return *context;
}
void ControlCore::UpdateQuickFixes(const Windows::Foundation::Collections::IVector<hstring>& quickFixes)
{
_cachedQuickFixes = quickFixes;
}
Core::Scheme ControlCore::ColorScheme() const noexcept
{
Core::Scheme s;

View File

@@ -68,6 +68,7 @@ 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)
{
@@ -241,6 +242,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
hstring ReadEntireBuffer() const;
Control::CommandHistoryContext CommandHistory() const;
void UpdateQuickFixes(const Windows::Foundation::Collections::IVector<hstring>& quickFixes);
void AdjustOpacity(const float opacity, const bool relative);
@@ -283,6 +285,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<> ClearQuickFix;
til::typed_event<> CloseTerminalRequested;
til::typed_event<> RestartTerminalRequested;
@@ -352,6 +356,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
til::point _contextMenuBufferPosition{ 0, 0 };
Windows::Foundation::Collections::IVector<int32_t> _cachedSearchResultRows{ nullptr };
Windows::Foundation::Collections::IVector<hstring> _cachedQuickFixes{ nullptr };
void _setupDispatcherAndCallbacks();
bool _setFontSizeUnderLock(float fontSize);
@@ -375,6 +382,8 @@ 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);
void _terminalClearQuickFix();
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
@@ -174,6 +175,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> ClearQuickFix;
// 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) {}
WINRT_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

@@ -296,6 +296,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>

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,12 @@ 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.ClearQuickFix = _core.ClearQuickFix(winrt::auto_revoke, { get_weak(), &TermControl::_clearQuickFix });
// 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*/) {
@@ -332,6 +338,29 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
}
});
if (Feature_QuickFix::IsEnabled())
{
auto quickFixBtn{ QuickFixButton() };
quickFixBtn.PointerEntered({ get_weak(), &TermControl::QuickFixButton_PointerEntered });
quickFixBtn.PointerExited({ get_weak(), &TermControl::QuickFixButton_PointerExited });
}
}
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:
@@ -808,6 +837,12 @@ 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.
if (Feature_QuickFix::IsEnabled())
{
// update the position of the quick fix menu (in case we changed the padding)
ShowQuickFixMenu();
}
}
// Method Description:
@@ -2313,6 +2348,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
_updateSelectionMarkers(nullptr, winrt::make<UpdateSelectionMarkersEventArgs>(false));
}
ShowQuickFixMenu();
}
hstring TermControl::Title()
@@ -3472,7 +3509,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;
@@ -3488,6 +3526,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
};
scaleMarker(SelectionStartMarker());
scaleMarker(SelectionEndMarker());
if (Feature_QuickFix::IsEnabled())
{
auto quickFixBtn = QuickFixButton();
quickFixBtn.Height(args.Height() / dpiScale);
QuickFixIcon().FontSize(std::min(static_cast<double>(args.Width() / dpiScale), GetPadding().Left));
ShowQuickFixMenu();
}
}
void TermControl::_coreRaisedNotice(const IInspectable& /*sender*/,
@@ -3556,6 +3602,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();
@@ -3770,6 +3821,67 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_showContextMenuAt(_toControlOrigin(cursorPos));
}
double TermControl::CalculateQuickFixButtonWidth()
{
return GetPadding().Left;
}
double TermControl::CalculateQuickFixButtonCollapsedWidth()
{
return GetPadding().Left / 3.0;
}
void TermControl::ShowQuickFixMenu()
{
if (!_quickFixesAvailable)
{
QuickFixButton().Visibility(Visibility::Collapsed);
return;
}
auto quickFixBtn{ QuickFixButton() };
// If the gutter is narrow, display the collapsed version
const auto& termPadding = GetPadding();
_quickFixButtonCollapsible = termPadding.Left < CharacterDimensions().Width;
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);
quickFixBtn.Visibility(Visibility::Visible);
}
void TermControl::_bubbleSearchMissingCommand(const IInspectable& /*sender*/, const Control::SearchMissingCommandEventArgs& args)
{
_quickFixesAvailable = true;
SearchMissingCommand.raise(*this, args);
}
void TermControl::_clearQuickFix(const IInspectable& /*sender*/, const IInspectable& /*args*/)
{
_quickFixesAvailable = false;
ShowQuickFixMenu();
}
void TermControl::ClearQuickFix()
{
_clearQuickFix(nullptr, nullptr);
}
void TermControl::_PasteCommandHandler(const IInspectable& /*sender*/,
const IInspectable& /*args*/)
{

View File

@@ -75,6 +75,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
float SnapDimensionToGrid(const bool widthOrHeight, const float dimension);
Windows::Foundation::Point CursorPositionInDips();
double CalculateQuickFixButtonWidth();
double CalculateQuickFixButtonCollapsedWidth();
void WindowVisibilityChanged(const bool showOrHide);
@@ -167,6 +169,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;
@@ -178,6 +181,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void RawWriteString(const winrt::hstring& text);
void ShowContextMenu();
void ShowQuickFixMenu();
void ClearQuickFix();
void Detach();
@@ -202,17 +207,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);
@@ -242,6 +248,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;
@@ -332,6 +340,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);
@@ -394,6 +405,9 @@ 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 _clearQuickFix(const IInspectable& sender, const IInspectable& 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);
@@ -423,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::ClearQuickFix_revoker ClearQuickFix;
// 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);
@@ -141,8 +144,12 @@ namespace Microsoft.Terminal.Control
void ColorSelection(SelectionColor fg, SelectionColor bg, Microsoft.Terminal.Core.MatchMode matchMode);
Windows.Foundation.Point CursorPositionInDips { get; };
Double CalculateQuickFixButtonWidth { get; };
Double CalculateQuickFixButtonCollapsedWidth { get; };
void ShowContextMenu();
void ShowQuickFixMenu();
void ClearQuickFix();
void Detach();
}

View File

@@ -1279,6 +1279,28 @@
</ToolTip>
</ToolTipService.ToolTip>
</Border>
<Button x:Name="QuickFixButton"
x:Uid="QuickFixButton"
Width="{x:Bind CalculateQuickFixButtonWidth}"
Padding="0"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
AllowFocusOnInteraction="False"
CornerRadius="0,5,5,0"
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" />
</Button.Flyout>
</Button>
</Canvas>
<Canvas x:Name="SelectionCanvas"
@@ -1353,6 +1375,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 CalculateQuickFixButtonCollapsedWidth}" />
<Setter Target="QuickFixButton.Background" Value="{ThemeResource SystemAccentColor}" />
<Setter Target="QuickFixIcon.Visibility" Value="Collapsed" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</UserControl>

View File

@@ -171,6 +171,13 @@
<Private>false</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Reference Include="$(OpenConsoleDir)src\cascadia\TerminalControl\Microsoft.Management.Deployment.winmd">
<IsWinMDFile>true</IsWinMDFile>
<Private>false</Private>
<CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
</Reference>
</ItemGroup>
<!-- ====================== Compiler & Linker Flags ===================== -->
<ItemDefinitionGroup>
<ClCompile>

View File

@@ -699,34 +699,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();
}
}
}
@@ -1227,6 +1237,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);
@@ -338,6 +342,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

@@ -937,6 +937,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

@@ -665,7 +665,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,
hstring iconPath)
{
std::wstring cdText = directories ? L"cd " : L"";
auto result = std::vector<Model::Command>();
@@ -697,9 +698,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

@@ -78,7 +78,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

@@ -45,7 +45,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

@@ -166,4 +166,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

@@ -3996,6 +3996,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)
{
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

@@ -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,