Skip to content

Commit 4485e50

Browse files
committed
PRE-MERGE #16848 Add support for custom CommandNotFound OSC
2 parents ab25a1e + ec1fbc2 commit 4485e50

36 files changed

+515
-41
lines changed

src/cascadia/CascadiaPackage/CascadiaPackage.wapproj

+17
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,23 @@
174174
</ItemGroup>
175175
</Target>
176176

177+
<!-- We need to include some additional files in the package for things like WinGet's Command Not Found -->
178+
<Target Name="IncludeAdditionalFilesInPackage">
179+
<PropertyGroup>
180+
<WinGetAdditionalPackageFileRoot>$(SolutionDir)\src\cascadia\TerminalControl</WinGetAdditionalPackageFileRoot>
181+
</PropertyGroup>
182+
<ItemGroup>
183+
<WinGetAdditionalPackageFile Include="$(WinGetAdditionalPackageFileRoot)\Microsoft.Management.Deployment.winmd">
184+
<PackagePath>Microsoft.Management.Deployment.winmd</PackagePath>
185+
</WinGetAdditionalPackageFile>
186+
</ItemGroup>
187+
<ItemGroup>
188+
<AppxPackagePayload Include="%(WinGetAdditionalPackageFile.Identity))">
189+
<TargetPath>%(WinGetAdditionalPackageFile.PackagePath)</TargetPath>
190+
</AppxPackagePayload>
191+
</ItemGroup>
192+
</Target>
193+
177194
<!-- This is required to get the package dependency in the AppXManifest. -->
178195
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
179196

src/cascadia/TerminalApp/AppActionHandlers.cpp

+14-2
Original file line numberDiff line numberDiff line change
@@ -1388,7 +1388,7 @@ namespace winrt::TerminalApp::implementation
13881388
// requires context from the control)
13891389
// then get that here.
13901390
const bool shouldGetContext = realArgs.UseCommandline() ||
1391-
WI_IsFlagSet(source, SuggestionsSource::CommandHistory);
1391+
WI_IsAnyFlagSet(source, SuggestionsSource::CommandHistory | SuggestionsSource::QuickFixes);
13921392
if (shouldGetContext)
13931393
{
13941394
if (const auto& control{ _GetActiveControl() })
@@ -1421,7 +1421,19 @@ namespace winrt::TerminalApp::implementation
14211421
if (WI_IsFlagSet(source, SuggestionsSource::CommandHistory) &&
14221422
context != nullptr)
14231423
{
1424-
const auto recentCommands = Command::HistoryToCommands(context.History(), currentCommandline, false);
1424+
// \ue81c --> History icon
1425+
const auto recentCommands = Command::HistoryToCommands(context.History(), currentCommandline, false, hstring{ L"\ue81c" });
1426+
for (const auto& t : recentCommands)
1427+
{
1428+
commandsCollection.push_back(t);
1429+
}
1430+
}
1431+
1432+
if (WI_IsFlagSet(source, SuggestionsSource::QuickFixes) &&
1433+
context != nullptr)
1434+
{
1435+
// \ue74c --> OEM icon
1436+
const auto recentCommands = Command::HistoryToCommands(context.QuickFixes(), hstring{ L"" }, false, hstring{ L"\ue74c" });
14251437
for (const auto& t : recentCommands)
14261438
{
14271439
commandsCollection.push_back(t);

src/cascadia/TerminalApp/TerminalPage.cpp

+88
Original file line numberDiff line numberDiff line change
@@ -1743,6 +1743,8 @@ namespace winrt::TerminalApp::implementation
17431743

17441744
term.ShowWindowChanged({ get_weak(), &TerminalPage::_ShowWindowChangedHandler });
17451745

1746+
term.SearchMissingCommand({ get_weak(), &TerminalPage::_SearchMissingCommandHandler });
1747+
17461748
// Don't even register for the event if the feature is compiled off.
17471749
if constexpr (Feature_ShellCompletions::IsEnabled())
17481750
{
@@ -1761,6 +1763,12 @@ namespace winrt::TerminalApp::implementation
17611763
page->_PopulateContextMenu(weakTerm.get(), sender.try_as<MUX::Controls::CommandBarFlyout>(), true);
17621764
}
17631765
});
1766+
term.QuickFixMenu().Opening([weak = get_weak(), weakTerm](auto&& sender, auto&& /*args*/) {
1767+
if (const auto& page{ weak.get() })
1768+
{
1769+
page->_PopulateQuickFixMenu(weakTerm.get(), sender.try_as<Controls::MenuFlyout>());
1770+
}
1771+
});
17641772
}
17651773

17661774
// Method Description:
@@ -2993,6 +3001,30 @@ namespace winrt::TerminalApp::implementation
29933001
ShowWindowChanged.raise(*this, args);
29943002
}
29953003

3004+
winrt::fire_and_forget TerminalPage::_SearchMissingCommandHandler(const IInspectable /*sender*/, const Microsoft::Terminal::Control::SearchMissingCommandEventArgs args)
3005+
{
3006+
assert(!Dispatcher().HasThreadAccess());
3007+
3008+
if (!Feature_QuickFix::IsEnabled())
3009+
{
3010+
co_return;
3011+
}
3012+
3013+
std::vector<hstring> suggestions;
3014+
suggestions.reserve(1);
3015+
suggestions.emplace_back(fmt::format(L"winget install {}", args.MissingCommand()));
3016+
3017+
co_await wil::resume_foreground(Dispatcher());
3018+
3019+
auto term = _GetActiveControl();
3020+
if (!term)
3021+
{
3022+
co_return;
3023+
}
3024+
term.UpdateWinGetSuggestions(single_threaded_vector<hstring>(std::move(suggestions)));
3025+
term.ShowQuickFixMenu();
3026+
}
3027+
29963028
// Method Description:
29973029
// - Paste text from the Windows Clipboard to the focused terminal
29983030
void TerminalPage::_PasteText()
@@ -4982,6 +5014,62 @@ namespace winrt::TerminalApp::implementation
49825014
makeItem(RS_(L"TabClose"), L"\xE711", ActionAndArgs{ ShortcutAction::CloseTab, CloseTabArgs{ _GetFocusedTabIndex().value() } });
49835015
}
49845016

5017+
void TerminalPage::_PopulateQuickFixMenu(const TermControl& control,
5018+
const Controls::MenuFlyout& menu)
5019+
{
5020+
if (!control || !menu)
5021+
{
5022+
return;
5023+
}
5024+
5025+
// Helper lambda for dispatching a SendInput ActionAndArgs onto the
5026+
// ShortcutActionDispatch. Used below to wire up each menu entry to the
5027+
// respective action. Then clear the quick fix menu.
5028+
auto weak = get_weak();
5029+
auto makeCallback = [weak](const hstring& suggestion) {
5030+
return [weak, suggestion](auto&&, auto&&) {
5031+
if (auto page{ weak.get() })
5032+
{
5033+
const auto actionAndArgs = ActionAndArgs{ ShortcutAction::SendInput, SendInputArgs{ hstring{ L"\u0003" } + suggestion } };
5034+
page->_actionDispatch->DoAction(actionAndArgs);
5035+
if (auto ctrl = page->_GetActiveControl())
5036+
{
5037+
ctrl.ClearQuickFix();
5038+
}
5039+
}
5040+
};
5041+
};
5042+
5043+
auto makeItem = [&menu, &makeCallback](const winrt::hstring& label,
5044+
const winrt::hstring& icon,
5045+
const winrt::hstring& suggestion) {
5046+
MenuFlyoutItem item{};
5047+
5048+
if (!icon.empty())
5049+
{
5050+
auto iconElement = UI::IconPathConverter::IconWUX(icon);
5051+
Automation::AutomationProperties::SetAccessibilityView(iconElement, Automation::Peers::AccessibilityView::Raw);
5052+
item.Icon(iconElement);
5053+
}
5054+
5055+
item.Text(label);
5056+
item.Click(makeCallback(suggestion));
5057+
menu.Items().Append(item);
5058+
};
5059+
5060+
// Wire up each item to the action that should be performed. By actually
5061+
// connecting these to actions, we ensure the implementation is
5062+
// consistent. This also leaves room for customizing this menu with
5063+
// actions in the future.
5064+
5065+
menu.Items().Clear();
5066+
const auto quickFixes = control.CommandHistory().QuickFixes();
5067+
for (const auto& qf : quickFixes)
5068+
{
5069+
makeItem(qf, L"\ue74c", qf);
5070+
}
5071+
}
5072+
49855073
// Handler for our WindowProperties's PropertyChanged event. We'll use this
49865074
// to pop the "Identify Window" toast when the user renames our window.
49875075
winrt::fire_and_forget TerminalPage::_windowPropertyChanged(const IInspectable& /*sender*/,

src/cascadia/TerminalApp/TerminalPage.h

+2
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,7 @@ namespace winrt::TerminalApp::implementation
529529
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);
530530

531531
void _ShowWindowChangedHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::ShowWindowArgs args);
532+
winrt::fire_and_forget _SearchMissingCommandHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::SearchMissingCommandEventArgs args);
532533

533534
winrt::fire_and_forget _windowPropertyChanged(const IInspectable& sender, const winrt::Windows::UI::Xaml::Data::PropertyChangedEventArgs& args);
534535

@@ -546,6 +547,7 @@ namespace winrt::TerminalApp::implementation
546547
void _sendDraggedTabToWindow(const winrt::hstring& windowId, const uint32_t tabIndex, std::optional<til::point> dragPoint);
547548

548549
void _PopulateContextMenu(const Microsoft::Terminal::Control::TermControl& control, const Microsoft::UI::Xaml::Controls::CommandBarFlyout& sender, const bool withSelection);
550+
void _PopulateQuickFixMenu(const Microsoft::Terminal::Control::TermControl& control, const Windows::UI::Xaml::Controls::MenuFlyout& sender);
549551
winrt::Windows::UI::Xaml::Controls::MenuFlyout _CreateRunAsAdminFlyout(int profileIndex);
550552

551553
winrt::Microsoft::Terminal::Control::TermControl _senderOrActiveControl(const winrt::Windows::Foundation::IInspectable& sender);

src/cascadia/TerminalControl/ControlCore.cpp

+27
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <unicode.hpp>
1414
#include <utils.hpp>
1515
#include <WinUser.h>
16+
//#include <winrt/Microsoft.Management.Deployment.h>
1617

1718
#include "EventArgs.h"
1819
#include "../../renderer/atlas/AtlasEngine.h"
@@ -23,6 +24,7 @@
2324
#include "ControlCore.g.cpp"
2425
#include "SelectionColor.g.cpp"
2526

27+
//using namespace winrt::Microsoft::Management::Deployment;
2628
using namespace ::Microsoft::Console::Types;
2729
using namespace ::Microsoft::Console::VirtualTerminal;
2830
using namespace ::Microsoft::Terminal::Core;
@@ -128,6 +130,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
128130
auto pfnCompletionsChanged = [=](auto&& menuJson, auto&& replaceLength) { _terminalCompletionsChanged(menuJson, replaceLength); };
129131
_terminal->CompletionsChangedCallback(pfnCompletionsChanged);
130132

133+
auto pfnSearchMissingCommand = [this](auto&& PH1) { _terminalSearchMissingCommand(std::forward<decltype(PH1)>(PH1)); };
134+
_terminal->SetSearchMissingCommandCallback(pfnSearchMissingCommand);
135+
136+
auto pfnClearQuickFix = [this] { _terminalClearQuickFix(); };
137+
_terminal->SetClearQuickFixCallback(pfnClearQuickFix);
138+
131139
// MSFT 33353327: Initialize the renderer in the ctor instead of Initialize().
132140
// We need the renderer to be ready to accept new engines before the SwapChainPanel is ready to go.
133141
// 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
16271635
_midiAudio.PlayNote(reinterpret_cast<HWND>(_owningHwnd), noteNumber, velocity, std::chrono::duration_cast<std::chrono::milliseconds>(duration));
16281636
}
16291637

1638+
void ControlCore::_terminalSearchMissingCommand(std::wstring_view missingCommand)
1639+
{
1640+
SearchMissingCommand.raise(*this, make<implementation::SearchMissingCommandEventArgs>(hstring{ missingCommand }));
1641+
}
1642+
1643+
void ControlCore::_terminalClearQuickFix()
1644+
{
1645+
ClearQuickFix.raise(*this, nullptr);
1646+
}
1647+
16301648
bool ControlCore::HasSelection() const
16311649
{
16321650
const auto lock = _terminal->LockForReading();
@@ -2295,11 +2313,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation
22952313
commands.pop_back();
22962314
}
22972315

2316+
22982317
auto context = winrt::make_self<CommandHistoryContext>(std::move(commands));
22992318
context->CurrentCommandline(trimmedCurrentCommand);
2319+
// 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)?
2320+
// No clue which we should do. Thoughts?
2321+
context->QuickFixes(_cachedQuickFixes);
23002322
return *context;
23012323
}
23022324

2325+
void ControlCore::UpdateQuickFixes(const Windows::Foundation::Collections::IVector<hstring>& quickFixes)
2326+
{
2327+
_cachedQuickFixes = quickFixes;
2328+
}
2329+
23032330
Core::Scheme ControlCore::ColorScheme() const noexcept
23042331
{
23052332
Core::Scheme s;

src/cascadia/TerminalControl/ControlCore.h

+9
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
6868
{
6969
til::property<Windows::Foundation::Collections::IVector<winrt::hstring>> History;
7070
til::property<winrt::hstring> CurrentCommandline;
71+
til::property<Windows::Foundation::Collections::IVector<winrt::hstring>> QuickFixes;
7172

7273
CommandHistoryContext(std::vector<winrt::hstring>&& history)
7374
{
@@ -241,6 +242,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
241242

242243
hstring ReadEntireBuffer() const;
243244
Control::CommandHistoryContext CommandHistory() const;
245+
void UpdateQuickFixes(const Windows::Foundation::Collections::IVector<hstring>& quickFixes);
244246

245247
void AdjustOpacity(const float opacity, const bool relative);
246248

@@ -283,6 +285,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
283285
til::typed_event<IInspectable, Control::UpdateSelectionMarkersEventArgs> UpdateSelectionMarkers;
284286
til::typed_event<IInspectable, Control::OpenHyperlinkEventArgs> OpenHyperlink;
285287
til::typed_event<IInspectable, Control::CompletionsChangedEventArgs> CompletionsChanged;
288+
til::typed_event<IInspectable, Control::SearchMissingCommandEventArgs> SearchMissingCommand;
289+
til::typed_event<> ClearQuickFix;
286290

287291
til::typed_event<> CloseTerminalRequested;
288292
til::typed_event<> RestartTerminalRequested;
@@ -352,6 +356,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
352356

353357
til::point _contextMenuBufferPosition{ 0, 0 };
354358

359+
Windows::Foundation::Collections::IVector<int32_t> _cachedSearchResultRows{ nullptr };
360+
Windows::Foundation::Collections::IVector<hstring> _cachedQuickFixes{ nullptr };
361+
355362
void _setupDispatcherAndCallbacks();
356363

357364
bool _setFontSizeUnderLock(float fontSize);
@@ -375,6 +382,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
375382
void _terminalPlayMidiNote(const int noteNumber,
376383
const int velocity,
377384
const std::chrono::microseconds duration);
385+
void _terminalSearchMissingCommand(std::wstring_view missingCommand);
386+
void _terminalClearQuickFix();
378387

379388
winrt::fire_and_forget _terminalCompletionsChanged(std::wstring_view menuJson, unsigned int replaceLength);
380389

src/cascadia/TerminalControl/ControlCore.idl

+3
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ namespace Microsoft.Terminal.Control
7070
{
7171
IVector<String> History { get; };
7272
String CurrentCommandline { get; };
73+
IVector<String> QuickFixes { get; };
7374
};
7475

7576
[default_interface] runtimeclass ControlCore : ICoreState
@@ -174,6 +175,8 @@ namespace Microsoft.Terminal.Control
174175
event Windows.Foundation.TypedEventHandler<Object, Object> TaskbarProgressChanged;
175176
event Windows.Foundation.TypedEventHandler<Object, Object> RendererEnteredErrorState;
176177
event Windows.Foundation.TypedEventHandler<Object, ShowWindowArgs> ShowWindowChanged;
178+
event Windows.Foundation.TypedEventHandler<Object, SearchMissingCommandEventArgs> SearchMissingCommand;
179+
event Windows.Foundation.TypedEventHandler<Object, Object> ClearQuickFix;
177180

178181
// These events are always called from the UI thread (bugs aside)
179182
event Windows.Foundation.TypedEventHandler<Object, FontSizeChangedArgs> FontSizeChanged;

src/cascadia/TerminalControl/EventArgs.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@
1818
#include "KeySentEventArgs.g.cpp"
1919
#include "CharSentEventArgs.g.cpp"
2020
#include "StringSentEventArgs.g.cpp"
21+
#include "SearchMissingCommandEventArgs.g.cpp"

src/cascadia/TerminalControl/EventArgs.h

+10
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "KeySentEventArgs.g.h"
1919
#include "CharSentEventArgs.g.h"
2020
#include "StringSentEventArgs.g.h"
21+
#include "SearchMissingCommandEventArgs.g.h"
2122

2223
namespace winrt::Microsoft::Terminal::Control::implementation
2324
{
@@ -211,6 +212,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
211212

212213
WINRT_PROPERTY(winrt::hstring, Text);
213214
};
215+
216+
struct SearchMissingCommandEventArgs : public SearchMissingCommandEventArgsT<SearchMissingCommandEventArgs>
217+
{
218+
public:
219+
SearchMissingCommandEventArgs(const winrt::hstring& missingCommand) :
220+
_MissingCommand(missingCommand) {}
221+
222+
WINRT_PROPERTY(winrt::hstring, MissingCommand);
223+
};
214224
}
215225

216226
namespace winrt::Microsoft::Terminal::Control::factory_implementation

src/cascadia/TerminalControl/EventArgs.idl

+5
Original file line numberDiff line numberDiff line change
@@ -126,4 +126,9 @@ namespace Microsoft.Terminal.Control
126126
{
127127
String Text { get; };
128128
}
129+
130+
runtimeclass SearchMissingCommandEventArgs
131+
{
132+
String MissingCommand { get; };
133+
}
129134
}
Binary file not shown.

src/cascadia/TerminalControl/Resources/en-US/Resources.resw

+6
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,12 @@ Please either install the missing font or choose another one.</value>
296296
<value>Select output</value>
297297
<comment>The tooltip for a button for selecting all of a command's output</comment>
298298
</data>
299+
<data name="QuickFixButton.ToolTipService.ToolTip" xml:space="preserve">
300+
<value>Quick fix</value>
301+
</data>
302+
<data name="QuickFixButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
303+
<value>Quick fix</value>
304+
</data>
299305
<data name="SessionRestoreMessage" xml:space="preserve">
300306
<value>Restored</value>
301307
<comment>"Restored" as in "This content was restored"</comment>

0 commit comments

Comments
 (0)