Add Suggestions UI & experimental shell completions support (#14938)
There's two parts to this PR that should be considered _separately_. 1. The Suggestions UI, a new graphical menu for displaying suggestions / completions to the user in the context of the terminal the user is working in. 2. The VsCode shell completions protocol. This enables the shell to invoke this UI via a VT sequence. These are being introduced at the same time, because they both require one another. However, I need to absolutely emphasize: ### THE FORMAT OF THE COMPLETION PROTOCOL IS EXPERIMENTAL AND SUBJECT TO CHANGE This is what we've prototyped with VsCode, but we're still working on how we want to conclusively define that protocol. However, we can also refine the Suggestions UI independently of how the protocol is actually implemented. This will let us rev the Suggestions UI to support other things like tooltips, recent commands, tasks, INDEPENDENTLY of us rev'ing the completion protocol. So yes, they're both here, but let's not nitpick that protocol for now. ### Checklist * Doesn't actually close anything * Heavily related to #3121, but I'm not gonna say that's closed till we settle on the protocol * See also: * #1595 * #14779 * https://github.com/microsoft/vscode/pull/171648 ### Detailed Description #### Suggestions UI The Suggestions UI is spec'ed over in #14864, so go read that. It's basically a transient Command Palette, that floats by the user's cursor. It's heavily forked from the Command Palette code, with all the business about switching modes removed. The major bit of new code is `SuggestionsControl::Anchor`. It also supports two "modes": * A "palette", which is like the command palette - a list with a text box * A "menu", which is more like the intellisense flyout. No text box. This is the mode that the shell completions use #### Shell Completions Protocol I literally cannot say this enough times - this protocol is experimental and subject to change. Build on it at your own peril. It's disabled in Release builds (but available in preview behind `globals.experimental.enableShellCompletionMenu`), so that when it ships, no one can take a dependency on it accidentally. Right now we're just taking a blob of JSON, passing that up to the App layer, who asks `Command` to parse it and build a list of `sendInput` actions to populate the menu with. It's not a particularly elegant solution, but it's good enough to prototype with. #### How do I test this? I've been testing this in two parts. You'll need a snippet in your powershell profile, and a keybinding in the Terminal settings to trigger it. The work together by binding <kbd>Ctrl+space</kbd> to _essentially_ send <kbd>F12</kbd><kbd>b</kbd>. Wacky, but it works. ```json { "command": { "action": "sendInput","input": "\u001b[24~b" }, "keys": "ctrl+space" }, ``` ```ps1 function Send-Completions2 { $commandLine = "" $cursorIndex = 0 # TODO: Since fuzzy matching exists, should completions be provided only for character after the # last space and then filter on the client side? That would let you trigger ctrl+space # anywhere on a word and have full completions available [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$commandLine, [ref]$cursorIndex) $completionPrefix = $commandLine # Get completions $result = "`e]633;Completions" if ($completionPrefix.Length -gt 0) { # Get and send completions $completions = TabExpansion2 -inputScript $completionPrefix -cursorColumn $cursorIndex if ($null -ne $completions.CompletionMatches) { $result += ";$($completions.ReplacementIndex);$($completions.ReplacementLength);$($cursorIndex);" $result += $completions.CompletionMatches | ConvertTo-Json -Compress } } $result += "`a" Write-Host -NoNewLine $result } function Set-MappedKeyHandlers { # VS Code send completions request (may override Ctrl+Spacebar) Set-PSReadLineKeyHandler -Chord 'F12,b' -ScriptBlock { Send-Completions2 } } # Register key handlers if PSReadLine is available if (Get-Module -Name PSReadLine) { Set-MappedKeyHandlers } ``` ### TODO * [x] `(prompt | format-hex).`<kbd>Ctrl+space</kbd> -> This always throws an exception. Seems like the payload is always clipped to ```{"CompletionText":"Ascii","ListItemText":"Ascii","ResultType":5,"ToolTip":"string Ascii { get``` and that ain't JSON. Investigate on the pwsh side?
This commit is contained in:
parent
69eff7e9fd
commit
a0c88bb511
|
@ -95,6 +95,7 @@ slnt
|
|||
Sos
|
||||
ssh
|
||||
stakeholders
|
||||
sxn
|
||||
timeline
|
||||
timelines
|
||||
timestamped
|
||||
|
|
|
@ -315,6 +315,7 @@ CPLINFO
|
|||
cplusplus
|
||||
CPPCORECHECK
|
||||
cppcorecheckrules
|
||||
cpprest
|
||||
cpprestsdk
|
||||
cppwinrt
|
||||
CProc
|
||||
|
@ -1452,6 +1453,7 @@ PPEB
|
|||
ppf
|
||||
ppguid
|
||||
ppidl
|
||||
pplx
|
||||
PPROC
|
||||
ppropvar
|
||||
ppsi
|
||||
|
|
|
@ -74,3 +74,5 @@ Author(s):
|
|||
#include "../../inc/DefaultSettings.h"
|
||||
|
||||
#include <cppwinrt_utils.h>
|
||||
|
||||
#include <til/winrt.h>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,132 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "FilteredCommand.h"
|
||||
#include "SuggestionsControl.g.h"
|
||||
#include "AppCommandlineArgs.h"
|
||||
|
||||
#include <til/hash.h>
|
||||
|
||||
// fwdecl unittest classes
|
||||
namespace TerminalAppLocalTests
|
||||
{
|
||||
class TabTests;
|
||||
};
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
struct SuggestionsControl : SuggestionsControlT<SuggestionsControl>
|
||||
{
|
||||
SuggestionsControl();
|
||||
|
||||
Windows::Foundation::Collections::IObservableVector<winrt::TerminalApp::FilteredCommand> FilteredActions();
|
||||
|
||||
void SetCommands(const Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::Command>& actions);
|
||||
void SetActionMap(const Microsoft::Terminal::Settings::Model::IActionMapView& actionMap);
|
||||
|
||||
bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down);
|
||||
|
||||
void SelectNextItem(const bool moveDown);
|
||||
|
||||
void ScrollPageUp();
|
||||
void ScrollPageDown();
|
||||
void ScrollToTop();
|
||||
void ScrollToBottom();
|
||||
|
||||
Windows::UI::Xaml::FrameworkElement SelectedItem();
|
||||
|
||||
TerminalApp::SuggestionsMode Mode() const;
|
||||
void Mode(TerminalApp::SuggestionsMode mode);
|
||||
|
||||
void Open(TerminalApp::SuggestionsMode mode,
|
||||
const Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::Command>& commands,
|
||||
Windows::Foundation::Point anchor,
|
||||
Windows::Foundation::Size space,
|
||||
float characterHeight);
|
||||
|
||||
til::typed_event<winrt::TerminalApp::SuggestionsControl, Microsoft::Terminal::Settings::Model::Command> DispatchCommandRequested;
|
||||
til::typed_event<Windows::Foundation::IInspectable, Microsoft::Terminal::Settings::Model::Command> PreviewAction;
|
||||
|
||||
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, NoMatchesText, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, SearchBoxPlaceholderText, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, ControlName, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, ParentCommandName, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, ParsedCommandLineText, _PropertyChangedHandlers);
|
||||
|
||||
private:
|
||||
struct winrt_object_hash
|
||||
{
|
||||
size_t operator()(const auto& value) const noexcept
|
||||
{
|
||||
return til::hash(winrt::get_abi(value));
|
||||
}
|
||||
};
|
||||
friend struct SuggestionsControlT<SuggestionsControl>; // for Xaml to bind events
|
||||
|
||||
Windows::Foundation::Collections::IVector<winrt::TerminalApp::FilteredCommand> _allCommands{ nullptr };
|
||||
Windows::Foundation::Collections::IVector<winrt::TerminalApp::FilteredCommand> _currentNestedCommands{ nullptr };
|
||||
Windows::Foundation::Collections::IObservableVector<winrt::TerminalApp::FilteredCommand> _filteredActions{ nullptr };
|
||||
Windows::Foundation::Collections::IVector<winrt::TerminalApp::FilteredCommand> _nestedActionStack{ nullptr };
|
||||
|
||||
TerminalApp::SuggestionsMode _mode{ TerminalApp::SuggestionsMode::Palette };
|
||||
TerminalApp::SuggestionsDirection _direction{ TerminalApp::SuggestionsDirection::TopDown };
|
||||
|
||||
bool _lastFilterTextWasEmpty{ true };
|
||||
Windows::Foundation::Point _anchor;
|
||||
Windows::Foundation::Size _space;
|
||||
|
||||
Microsoft::Terminal::Settings::Model::IActionMapView _actionMap{ nullptr };
|
||||
|
||||
winrt::Windows::UI::Xaml::Controls::ListView::SizeChanged_revoker _sizeChangedRevoker;
|
||||
|
||||
winrt::TerminalApp::PaletteItemTemplateSelector _itemTemplateSelector{ nullptr };
|
||||
std::unordered_map<Windows::UI::Xaml::DataTemplate, std::unordered_set<Windows::UI::Xaml::Controls::Primitives::SelectorItem, winrt_object_hash>, winrt_object_hash> _listViewItemsCache;
|
||||
Windows::UI::Xaml::DataTemplate _listItemTemplate;
|
||||
|
||||
void _switchToMode();
|
||||
void _setDirection(TerminalApp::SuggestionsDirection direction);
|
||||
|
||||
void _scrollToIndex(uint32_t index);
|
||||
|
||||
void _updateUIForStackChange();
|
||||
void _updateFilteredActions();
|
||||
|
||||
void _dispatchCommand(const winrt::TerminalApp::FilteredCommand& command);
|
||||
void _close();
|
||||
void _dismissPalette();
|
||||
|
||||
void _filterTextChanged(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args);
|
||||
void _previewKeyDownHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::KeyRoutedEventArgs& e);
|
||||
void _keyUpHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::KeyRoutedEventArgs& e);
|
||||
|
||||
void _rootPointerPressed(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e);
|
||||
|
||||
void _lostFocusHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args);
|
||||
void _backdropPointerPressed(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e);
|
||||
|
||||
void _listItemClicked(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Controls::ItemClickEventArgs& e);
|
||||
void _listItemSelectionChanged(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Controls::SelectionChangedEventArgs& e);
|
||||
void _selectedCommandChanged(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args);
|
||||
|
||||
void _moveBackButtonClicked(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs&);
|
||||
void _updateCurrentNestedCommands(const winrt::Microsoft::Terminal::Settings::Model::Command& parentCommand);
|
||||
|
||||
void _choosingItemContainer(const Windows::UI::Xaml::Controls::ListViewBase& sender, const Windows::UI::Xaml::Controls::ChoosingItemContainerEventArgs& args);
|
||||
void _containerContentChanging(const Windows::UI::Xaml::Controls::ListViewBase& sender, const Windows::UI::Xaml::Controls::ContainerContentChangingEventArgs& args);
|
||||
|
||||
std::vector<winrt::TerminalApp::FilteredCommand> _collectFilteredActions();
|
||||
Windows::Foundation::Collections::IVector<winrt::TerminalApp::FilteredCommand> _commandsToFilter();
|
||||
std::wstring _getTrimmedInput();
|
||||
uint32_t _getNumVisibleItems();
|
||||
|
||||
friend class TerminalAppLocalTests::TabTests;
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::TerminalApp::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(SuggestionsControl);
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import "TabBase.idl";
|
||||
import "IDirectKeyListener.idl";
|
||||
import "HighlightedTextControl.idl";
|
||||
import "FilteredCommand.idl";
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
|
||||
enum SuggestionsMode
|
||||
{
|
||||
Palette = 0,
|
||||
Menu,
|
||||
// Inline,
|
||||
};
|
||||
|
||||
enum SuggestionsDirection {
|
||||
TopDown,
|
||||
BottomUp
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass SuggestionsControl : Windows.UI.Xaml.Controls.UserControl, Windows.UI.Xaml.Data.INotifyPropertyChanged, IDirectKeyListener
|
||||
{
|
||||
SuggestionsControl();
|
||||
|
||||
String NoMatchesText { get; };
|
||||
String SearchBoxPlaceholderText { get; };
|
||||
String ControlName { get; };
|
||||
String ParentCommandName { get; };
|
||||
|
||||
Windows.UI.Xaml.FrameworkElement SelectedItem { get; };
|
||||
|
||||
Windows.Foundation.Collections.IObservableVector<FilteredCommand> FilteredActions { get; };
|
||||
|
||||
SuggestionsMode Mode { get; set; };
|
||||
|
||||
void SetCommands(Windows.Foundation.Collections.IVector<Microsoft.Terminal.Settings.Model.Command> actions);
|
||||
void SetActionMap(Microsoft.Terminal.Settings.Model.IActionMapView actionMap);
|
||||
void SelectNextItem(Boolean moveDown);
|
||||
|
||||
void Open(SuggestionsMode mode, IVector<Microsoft.Terminal.Settings.Model.Command> commands, Windows.Foundation.Point anchor, Windows.Foundation.Size space, Single characterHeight);
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<SuggestionsControl, Microsoft.Terminal.Settings.Model.Command> DispatchCommandRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Settings.Model.Command> PreviewAction;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,214 @@
|
|||
<!--
|
||||
Copyright (c) Microsoft Corporation. All rights reserved. Licensed under
|
||||
the MIT License. See LICENSE in the project root for license information.
|
||||
-->
|
||||
<UserControl x:Class="TerminalApp.SuggestionsControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:SettingsModel="using:Microsoft.Terminal.Settings.Model"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:TerminalApp"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:model="using:Microsoft.Terminal.Settings.Model"
|
||||
xmlns:mux="using:Microsoft.UI.Xaml.Controls"
|
||||
AllowFocusOnInteraction="True"
|
||||
AutomationProperties.Name="{x:Bind ControlName, Mode=OneWay}"
|
||||
IsTabStop="True"
|
||||
LostFocus="_lostFocusHandler"
|
||||
PointerPressed="_rootPointerPressed"
|
||||
PreviewKeyDown="_previewKeyDownHandler"
|
||||
PreviewKeyUp="_keyUpHandler"
|
||||
TabNavigation="Cycle"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<local:EmptyStringVisibilityConverter x:Key="ParentCommandVisibilityConverter" />
|
||||
<model:IconPathConverter x:Key="IconSourceConverter" />
|
||||
|
||||
<DataTemplate x:Key="ListItemTemplate"
|
||||
x:DataType="local:FilteredCommand">
|
||||
<ListViewItem Height="32"
|
||||
MinHeight="0"
|
||||
Padding="16,0,12,0"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
AutomationProperties.AcceleratorKey="{x:Bind Item.KeyChordText, Mode=OneWay}"
|
||||
AutomationProperties.Name="{x:Bind Item.Name, Mode=OneWay}"
|
||||
FontSize="12" />
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="GeneralItemTemplate"
|
||||
x:DataType="local:FilteredCommand">
|
||||
<Grid HorizontalAlignment="Stretch"
|
||||
ColumnSpacing="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="16" />
|
||||
<!-- icon -->
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<!-- command label -->
|
||||
<ColumnDefinition Width="*" />
|
||||
<!-- key chord -->
|
||||
<ColumnDefinition Width="16" />
|
||||
<!-- gutter for scrollbar -->
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<ContentPresenter Grid.Column="0"
|
||||
Width="16"
|
||||
Height="16"
|
||||
Content="{x:Bind Item.ResolvedIcon, Mode=OneWay}" />
|
||||
|
||||
<local:HighlightedTextControl Grid.Column="1"
|
||||
HorizontalAlignment="Left"
|
||||
Text="{x:Bind HighlightedName, Mode=OneWay}" />
|
||||
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="NestedItemTemplate"
|
||||
x:DataType="local:FilteredCommand">
|
||||
<Grid HorizontalAlignment="Stretch"
|
||||
ColumnSpacing="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="16" />
|
||||
<!-- icon -->
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<!-- command label -->
|
||||
<ColumnDefinition Width="*" />
|
||||
<!-- key chord -->
|
||||
<ColumnDefinition Width="16" />
|
||||
<!-- gutter for scrollbar -->
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<ContentPresenter Grid.Column="0"
|
||||
Width="16"
|
||||
Height="16"
|
||||
Content="{x:Bind Item.ResolvedIcon, Mode=OneWay}" />
|
||||
|
||||
<local:HighlightedTextControl Grid.Column="1"
|
||||
HorizontalAlignment="Left"
|
||||
Text="{x:Bind HighlightedName, Mode=OneWay}" />
|
||||
|
||||
<FontIcon Grid.Column="2"
|
||||
HorizontalAlignment="Right"
|
||||
FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||
FontSize="12"
|
||||
Glyph="" />
|
||||
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
|
||||
<local:PaletteItemTemplateSelector x:Key="PaletteItemTemplateSelector"
|
||||
GeneralItemTemplate="{StaticResource GeneralItemTemplate}"
|
||||
NestedItemTemplate="{StaticResource NestedItemTemplate}" />
|
||||
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="2*" />
|
||||
<ColumnDefinition Width="6*" />
|
||||
<ColumnDefinition Width="2*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="8*" />
|
||||
<RowDefinition Height="2*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid x:Name="_backdrop"
|
||||
Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="3"
|
||||
MaxWidth="300"
|
||||
MaxHeight="300"
|
||||
Margin="0"
|
||||
Padding="0,8,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Background="{ThemeResource FlyoutPresenterBackground}"
|
||||
BorderBrush="{ThemeResource FlyoutBorderThemeBrush}"
|
||||
BorderThickness="{ThemeResource FlyoutBorderThemeThickness}"
|
||||
CornerRadius="{ThemeResource OverlayCornerRadius}"
|
||||
PointerPressed="_backdropPointerPressed"
|
||||
Shadow="{StaticResource SharedShadow}"
|
||||
Translation="0,0,32">
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<!-- Top-down _searchBox -->
|
||||
<RowDefinition Height="Auto" />
|
||||
<!-- Top-down ParentCommandName -->
|
||||
<RowDefinition Height="Auto" />
|
||||
<!-- Top-down UNUSED???????? -->
|
||||
<RowDefinition Height="*" />
|
||||
<!-- _filteredActionsView -->
|
||||
<RowDefinition Height="Auto" />
|
||||
<!-- bottom-up _searchBox -->
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBox x:Name="_searchBox"
|
||||
Grid.Row="0"
|
||||
Margin="8,0,8,8"
|
||||
Padding="18,8,8,8"
|
||||
IsSpellCheckEnabled="False"
|
||||
PlaceholderText="{x:Bind SearchBoxPlaceholderText, Mode=OneWay}"
|
||||
Text=""
|
||||
TextChanged="_filterTextChanged"
|
||||
Visibility="Collapsed" />
|
||||
|
||||
<StackPanel Grid.Row="1"
|
||||
Margin="8,0,8,8"
|
||||
Orientation="Horizontal"
|
||||
Visibility="{x:Bind ParentCommandName, Mode=OneWay, Converter={StaticResource ParentCommandVisibilityConverter}}">
|
||||
|
||||
<Button x:Name="_parentCommandBackButton"
|
||||
x:Uid="ParentCommandBackButton"
|
||||
VerticalAlignment="Center"
|
||||
Click="_moveBackButtonClicked"
|
||||
ClickMode="Press">
|
||||
<FontIcon FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||
FontSize="11"
|
||||
Glyph="" />
|
||||
</Button>
|
||||
|
||||
<TextBlock x:Name="_parentCommandText"
|
||||
Padding="16,4"
|
||||
VerticalAlignment="Center"
|
||||
FontStyle="Italic"
|
||||
Text="{x:Bind ParentCommandName, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
|
||||
|
||||
<Border x:Name="_noMatchesText"
|
||||
Grid.Row="3"
|
||||
Height="36"
|
||||
Margin="8,0,8,8"
|
||||
Visibility="Collapsed">
|
||||
<TextBlock Padding="12,0"
|
||||
VerticalAlignment="Center"
|
||||
FontStyle="Italic"
|
||||
Text="{x:Bind NoMatchesText, Mode=OneWay}" />
|
||||
</Border>
|
||||
|
||||
<ListView x:Name="_filteredActionsView"
|
||||
Grid.Row="3"
|
||||
Padding="4,-2,4,6"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
AllowDrop="False"
|
||||
CanReorderItems="False"
|
||||
ChoosingItemContainer="_choosingItemContainer"
|
||||
ContainerContentChanging="_containerContentChanging"
|
||||
IsItemClickEnabled="True"
|
||||
ItemClick="_listItemClicked"
|
||||
ItemsSource="{x:Bind FilteredActions}"
|
||||
SelectionChanged="_listItemSelectionChanged"
|
||||
SelectionMode="Single" />
|
||||
|
||||
</Grid>
|
||||
|
||||
|
||||
</Grid>
|
||||
</UserControl>
|
|
@ -68,6 +68,9 @@
|
|||
<Page Include="CommandPalette.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="SuggestionsControl.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<!-- ========================= Headers ======================== -->
|
||||
<ItemGroup>
|
||||
|
@ -159,6 +162,9 @@
|
|||
<DependentUpon>TerminalWindow.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Toast.h" />
|
||||
<ClInclude Include="SuggestionsControl.h">
|
||||
<DependentUpon>SuggestionsControl.xaml</DependentUpon>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<!-- ========================= Cpp Files ======================== -->
|
||||
<ItemGroup>
|
||||
|
@ -262,6 +268,9 @@
|
|||
</ClCompile>
|
||||
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
|
||||
<ClCompile Include="Toast.cpp" />
|
||||
<ClCompile Include="SuggestionsControl.cpp">
|
||||
<DependentUpon>SuggestionsControl.xaml</DependentUpon>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<!-- ========================= idl Files ======================== -->
|
||||
<ItemGroup>
|
||||
|
@ -325,6 +334,10 @@
|
|||
<DependentUpon>CommandPalette.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Midl>
|
||||
<Midl Include="SuggestionsControl.idl">
|
||||
<DependentUpon>SuggestionsControl.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Midl>
|
||||
<Midl Include="FilteredCommand.idl" />
|
||||
<Midl Include="EmptyStringVisibilityConverter.idl" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -1466,6 +1466,11 @@ namespace winrt::TerminalApp::implementation
|
|||
{
|
||||
CommandPaletteElement().Visibility(Visibility::Collapsed);
|
||||
}
|
||||
if (_suggestionsControlIs(Visibility::Visible) &&
|
||||
cmd.ActionAndArgs().Action() != ShortcutAction::ToggleCommandPalette)
|
||||
{
|
||||
SuggestionsElement().Visibility(Visibility::Collapsed);
|
||||
}
|
||||
|
||||
// Let's assume the user has bound the dead key "^" to a sendInput command that sends "b".
|
||||
// If the user presses the two keys "^a" it'll produce "bâ", despite us marking the key event as handled.
|
||||
|
@ -1654,6 +1659,12 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
term.ShowWindowChanged({ get_weak(), &TerminalPage::_ShowWindowChangedHandler });
|
||||
|
||||
// Don't even register for the event if the feature is compiled off.
|
||||
if constexpr (Feature_ShellCompletions::IsEnabled())
|
||||
{
|
||||
term.CompletionsChanged({ get_weak(), &TerminalPage::_ControlCompletionsChangedHandler });
|
||||
}
|
||||
|
||||
term.ContextMenu().Opening({ this, &TerminalPage::_ContextMenuOpened });
|
||||
term.SelectionContextMenu().Opening({ this, &TerminalPage::_SelectionMenuOpened });
|
||||
}
|
||||
|
@ -1825,6 +1836,37 @@ namespace winrt::TerminalApp::implementation
|
|||
return p;
|
||||
}
|
||||
|
||||
SuggestionsControl TerminalPage::LoadSuggestionsUI()
|
||||
{
|
||||
if (const auto p = SuggestionsElement())
|
||||
{
|
||||
return p;
|
||||
}
|
||||
|
||||
return _loadSuggestionsElementSlowPath();
|
||||
}
|
||||
bool TerminalPage::_suggestionsControlIs(WUX::Visibility visibility)
|
||||
{
|
||||
const auto p = SuggestionsElement();
|
||||
return p && p.Visibility() == visibility;
|
||||
}
|
||||
|
||||
SuggestionsControl TerminalPage::_loadSuggestionsElementSlowPath()
|
||||
{
|
||||
const auto p = FindName(L"SuggestionsElement").as<SuggestionsControl>();
|
||||
|
||||
p.RegisterPropertyChangedCallback(UIElement::VisibilityProperty(), [this](auto&&, auto&&) {
|
||||
if (SuggestionsElement().Visibility() == Visibility::Collapsed)
|
||||
{
|
||||
_FocusActiveControl(nullptr, nullptr);
|
||||
}
|
||||
});
|
||||
p.DispatchCommandRequested({ this, &TerminalPage::_OnDispatchCommandRequested });
|
||||
p.PreviewAction({ this, &TerminalPage::_PreviewActionHandler });
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Warn the user that they are about to close all open windows, then
|
||||
// signal that we want to close everything.
|
||||
|
@ -2787,7 +2829,7 @@ namespace winrt::TerminalApp::implementation
|
|||
// Arguments:
|
||||
// - sender (not used)
|
||||
// - args: the arguments specifying how to set the display status to ShowWindow for our window handle
|
||||
void TerminalPage::_ShowWindowChangedHandler(const IInspectable& /*sender*/, const Microsoft::Terminal::Control::ShowWindowArgs args)
|
||||
void TerminalPage::_ShowWindowChangedHandler(const IInspectable /*sender*/, const Microsoft::Terminal::Control::ShowWindowArgs args)
|
||||
{
|
||||
_ShowWindowChangedHandlers(*this, args);
|
||||
}
|
||||
|
@ -4649,6 +4691,79 @@ namespace winrt::TerminalApp::implementation
|
|||
_updateThemeColors();
|
||||
}
|
||||
|
||||
winrt::fire_and_forget TerminalPage::_ControlCompletionsChangedHandler(const IInspectable sender,
|
||||
const CompletionsChangedEventArgs args)
|
||||
{
|
||||
// This will come in on a background (not-UI, not output) thread.
|
||||
|
||||
// This won't even get hit if the velocity flag is disabled - we gate
|
||||
// registering for the event based off of
|
||||
// Feature_ShellCompletions::IsEnabled back in _RegisterTerminalEvents
|
||||
|
||||
// User must explicitly opt-in on Preview builds
|
||||
if (!_settings.GlobalSettings().EnableShellCompletionMenu())
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
|
||||
// Parse the json string into a collection of actions
|
||||
try
|
||||
{
|
||||
auto commandsCollection = Command::ParsePowerShellMenuComplete(args.MenuJson(),
|
||||
args.ReplacementLength());
|
||||
|
||||
auto weakThis{ get_weak() };
|
||||
Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [weakThis, commandsCollection, sender]() {
|
||||
// On the UI thread...
|
||||
if (const auto& page{ weakThis.get() })
|
||||
{
|
||||
// Open the Suggestions UI with the commands from the control
|
||||
page->_OpenSuggestions(sender.try_as<TermControl>(), commandsCollection, SuggestionsMode::Menu);
|
||||
}
|
||||
});
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
|
||||
void TerminalPage::_OpenSuggestions(
|
||||
const TermControl& sender,
|
||||
IVector<Command> commandsCollection,
|
||||
winrt::TerminalApp::SuggestionsMode mode)
|
||||
{
|
||||
// ON THE UI THREAD
|
||||
assert(Dispatcher().HasThreadAccess());
|
||||
|
||||
if (commandsCollection == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (commandsCollection.Size() == 0)
|
||||
{
|
||||
if (const auto p = SuggestionsElement())
|
||||
{
|
||||
p.Visibility(Visibility::Collapsed);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& control{ sender ? sender : _GetActiveControl() };
|
||||
if (!control)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& sxnUi{ LoadSuggestionsUI() };
|
||||
|
||||
const auto characterSize{ control.CharacterDimensions() };
|
||||
// This is in control-relative space. We'll need to convert it to page-relative space.
|
||||
const auto cursorPos{ control.CursorPositionInDips() };
|
||||
const auto controlTransform = control.TransformToVisual(this->Root());
|
||||
const auto realCursorPos{ controlTransform.TransformPoint({ cursorPos.X, cursorPos.Y }) }; // == controlTransform + cursorPos
|
||||
const Windows::Foundation::Size windowDimensions{ gsl::narrow_cast<float>(ActualWidth()), gsl::narrow_cast<float>(ActualHeight()) };
|
||||
|
||||
sxnUi.Open(mode, commandsCollection, realCursorPos, windowDimensions, characterSize.Height);
|
||||
}
|
||||
|
||||
void TerminalPage::_ContextMenuOpened(const IInspectable& sender,
|
||||
const IInspectable& /*args*/)
|
||||
{
|
||||
|
|
|
@ -117,6 +117,8 @@ namespace winrt::TerminalApp::implementation
|
|||
winrt::hstring ApplicationVersion();
|
||||
|
||||
CommandPalette LoadCommandPalette();
|
||||
SuggestionsControl LoadSuggestionsUI();
|
||||
|
||||
winrt::fire_and_forget RequestQuit();
|
||||
winrt::fire_and_forget CloseWindow(bool bypassDialog);
|
||||
|
||||
|
@ -280,6 +282,8 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
__declspec(noinline) CommandPalette _loadCommandPaletteSlowPath();
|
||||
bool _commandPaletteIs(winrt::Windows::UI::Xaml::Visibility visibility);
|
||||
__declspec(noinline) SuggestionsControl _loadSuggestionsElementSlowPath();
|
||||
bool _suggestionsControlIs(winrt::Windows::UI::Xaml::Visibility visibility);
|
||||
|
||||
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> _ShowDialogHelper(const std::wstring_view& name);
|
||||
|
||||
|
@ -481,6 +485,7 @@ namespace winrt::TerminalApp::implementation
|
|||
void _RunRestorePreviews();
|
||||
void _PreviewColorScheme(const Microsoft::Terminal::Settings::Model::SetColorSchemeArgs& args);
|
||||
void _PreviewAdjustOpacity(const Microsoft::Terminal::Settings::Model::AdjustOpacityArgs& args);
|
||||
|
||||
winrt::Microsoft::Terminal::Settings::Model::ActionAndArgs _lastPreviewedAction{ nullptr };
|
||||
std::vector<std::function<void()>> _restorePreviewFuncs{};
|
||||
|
||||
|
@ -513,7 +518,11 @@ namespace winrt::TerminalApp::implementation
|
|||
void _updateAllTabCloseButtons(const winrt::TerminalApp::TabBase& focusedTab);
|
||||
void _updatePaneResources(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme);
|
||||
|
||||
void _ShowWindowChangedHandler(const IInspectable& sender, const winrt::Microsoft::Terminal::Control::ShowWindowArgs args);
|
||||
winrt::fire_and_forget _ControlCompletionsChangedHandler(const winrt::Windows::Foundation::IInspectable sender, const winrt::Microsoft::Terminal::Control::CompletionsChangedEventArgs args);
|
||||
void _OpenSuggestions(const Microsoft::Terminal::Control::TermControl& sender, Windows::Foundation::Collections::IVector<winrt::Microsoft::Terminal::Settings::Model::Command> commandsCollection, winrt::TerminalApp::SuggestionsMode mode);
|
||||
|
||||
void _ShowWindowChangedHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::ShowWindowArgs args);
|
||||
|
||||
winrt::fire_and_forget _windowPropertyChanged(const IInspectable& sender, const winrt::Windows::UI::Xaml::Data::PropertyChangedEventArgs& args);
|
||||
|
||||
void _onTabDragStarting(const winrt::Microsoft::UI::Xaml::Controls::TabView& sender, const winrt::Microsoft::UI::Xaml::Controls::TabViewTabDragStartingEventArgs& e);
|
||||
|
|
|
@ -175,6 +175,14 @@
|
|||
PreviewKeyDown="_KeyDownHandler"
|
||||
Visibility="Collapsed" />
|
||||
|
||||
<local:SuggestionsControl x:Name="SuggestionsElement"
|
||||
Grid.Row="2"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
x:Load="False"
|
||||
PreviewKeyDown="_KeyDownHandler"
|
||||
Visibility="Collapsed" />
|
||||
|
||||
<!--
|
||||
A TeachingTip with IsLightDismissEnabled="True" will immediately
|
||||
dismiss itself if the window is unfocused (In Xaml Islands). This is
|
||||
|
|
|
@ -84,3 +84,5 @@ TRACELOGGING_DECLARE_PROVIDER(g_hTerminalAppProvider);
|
|||
|
||||
#include <cppwinrt_utils.h>
|
||||
#include <wil/cppwinrt_helpers.h> // must go after the CoreDispatcher type is defined
|
||||
|
||||
#include <til/winrt.h>
|
||||
|
|
|
@ -119,6 +119,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
auto pfnPlayMidiNote = std::bind(&ControlCore::_terminalPlayMidiNote, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
|
||||
_terminal->SetPlayMidiNoteCallback(pfnPlayMidiNote);
|
||||
|
||||
auto pfnCompletionsChanged = [=](auto&& menuJson, auto&& replaceLength) { _terminalCompletionsChanged(menuJson, replaceLength); };
|
||||
_terminal->CompletionsChangedCallback(pfnCompletionsChanged);
|
||||
|
||||
// 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
|
||||
|
@ -2228,6 +2231,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
}
|
||||
}
|
||||
|
||||
winrt::fire_and_forget ControlCore::_terminalCompletionsChanged(std::wstring_view menuJson,
|
||||
unsigned int replaceLength)
|
||||
{
|
||||
auto args = winrt::make_self<CompletionsChangedEventArgs>(winrt::hstring{ menuJson },
|
||||
replaceLength);
|
||||
|
||||
co_await winrt::resume_background();
|
||||
|
||||
_CompletionsChangedHandlers(*this, *args);
|
||||
}
|
||||
void ControlCore::_selectSpan(til::point_span s)
|
||||
{
|
||||
const auto bufferSize{ _terminal->GetTextBuffer().GetSize() };
|
||||
|
|
|
@ -258,6 +258,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
TYPED_EVENT(ShowWindowChanged, IInspectable, Control::ShowWindowArgs);
|
||||
TYPED_EVENT(UpdateSelectionMarkers, IInspectable, Control::UpdateSelectionMarkersEventArgs);
|
||||
TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs);
|
||||
TYPED_EVENT(CompletionsChanged, IInspectable, Control::CompletionsChangedEventArgs);
|
||||
|
||||
TYPED_EVENT(CloseTerminalRequested, IInspectable, IInspectable);
|
||||
TYPED_EVENT(RestartTerminalRequested, IInspectable, IInspectable);
|
||||
|
||||
|
@ -347,6 +349,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
void _terminalPlayMidiNote(const int noteNumber,
|
||||
const int velocity,
|
||||
const std::chrono::microseconds duration);
|
||||
|
||||
winrt::fire_and_forget _terminalCompletionsChanged(std::wstring_view menuJson, unsigned int replaceLength);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
MidiAudio _midiAudio;
|
||||
|
|
|
@ -175,5 +175,8 @@ namespace Microsoft.Terminal.Control
|
|||
event Windows.Foundation.TypedEventHandler<Object, Object> RestartTerminalRequested;
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> Attached;
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, CompletionsChangedEventArgs> CompletionsChanged;
|
||||
|
||||
};
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "FoundResultsArgs.g.cpp"
|
||||
#include "ShowWindowArgs.g.cpp"
|
||||
#include "UpdateSelectionMarkersEventArgs.g.cpp"
|
||||
#include "CompletionsChangedEventArgs.g.cpp"
|
||||
#include "KeySentEventArgs.g.cpp"
|
||||
#include "CharSentEventArgs.g.cpp"
|
||||
#include "StringSentEventArgs.g.cpp"
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "FoundResultsArgs.g.h"
|
||||
#include "ShowWindowArgs.g.h"
|
||||
#include "UpdateSelectionMarkersEventArgs.g.h"
|
||||
#include "CompletionsChangedEventArgs.g.h"
|
||||
#include "KeySentEventArgs.g.h"
|
||||
#include "CharSentEventArgs.g.h"
|
||||
#include "StringSentEventArgs.g.h"
|
||||
|
@ -183,6 +184,19 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
WINRT_PROPERTY(bool, ClearMarkers, false);
|
||||
};
|
||||
|
||||
struct CompletionsChangedEventArgs : public CompletionsChangedEventArgsT<CompletionsChangedEventArgs>
|
||||
{
|
||||
public:
|
||||
CompletionsChangedEventArgs(const winrt::hstring menuJson, const unsigned int replaceLength) :
|
||||
_MenuJson(menuJson),
|
||||
_ReplacementLength(replaceLength)
|
||||
{
|
||||
}
|
||||
|
||||
WINRT_PROPERTY(winrt::hstring, MenuJson, L"");
|
||||
WINRT_PROPERTY(uint32_t, ReplacementLength, 0);
|
||||
};
|
||||
|
||||
struct KeySentEventArgs : public KeySentEventArgsT<KeySentEventArgs>
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -90,6 +90,12 @@ namespace Microsoft.Terminal.Control
|
|||
Boolean ClearMarkers { get; };
|
||||
}
|
||||
|
||||
runtimeclass CompletionsChangedEventArgs
|
||||
{
|
||||
String MenuJson { get; };
|
||||
UInt32 ReplacementLength { get; };
|
||||
}
|
||||
|
||||
runtimeclass KeySentEventArgs
|
||||
{
|
||||
UInt16 VKey { get; };
|
||||
|
|
|
@ -104,6 +104,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
_revokers.ConnectionStateChanged = _core.ConnectionStateChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleConnectionStateChanged });
|
||||
_revokers.ShowWindowChanged = _core.ShowWindowChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleShowWindowChanged });
|
||||
_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.PasteFromClipboard = _interactivity.PasteFromClipboard(winrt::auto_revoke, { get_weak(), &TermControl::_bubblePasteFromClipboard });
|
||||
|
@ -2349,7 +2350,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
|
||||
// Method Description:
|
||||
// - Get the size of a single character of this control. The size is in
|
||||
// DIPs. If you need it in _pixels_, you'll need to multiply by the
|
||||
// _pixels_. If you want it in DIPs, you'll need to DIVIDE by the
|
||||
// current display scaling.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
|
@ -3471,6 +3472,34 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
_core.ColorSelection(fg, bg, matchMode);
|
||||
}
|
||||
|
||||
// Returns the text cursor's position relative to our origin, in DIPs.
|
||||
Windows::Foundation::Point TermControl::CursorPositionInDips()
|
||||
{
|
||||
const til::point cursorPos{ _core.CursorPosition() };
|
||||
|
||||
// CharacterDimensions returns a font size in pixels.
|
||||
const auto fontSize{ CharacterDimensions() };
|
||||
|
||||
// Convert text buffer cursor position to client coordinate position
|
||||
// within the window. This point is in _pixels_
|
||||
const Windows::Foundation::Point clientCursorPos{ cursorPos.x * fontSize.Width,
|
||||
cursorPos.y * fontSize.Height };
|
||||
|
||||
// Get scale factor for view
|
||||
const double scaleFactor = DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel();
|
||||
|
||||
// Adjust to DIPs
|
||||
const til::point clientCursorInDips{ til::math::rounding, clientCursorPos.X / scaleFactor, clientCursorPos.Y / scaleFactor };
|
||||
|
||||
// Account for the margins, which are in DIPs
|
||||
auto padding{ GetPadding() };
|
||||
til::point relativeToOrigin{ til::math::flooring,
|
||||
clientCursorInDips.x + padding.Left,
|
||||
clientCursorInDips.y + padding.Top };
|
||||
|
||||
return relativeToOrigin.to_winrt_point();
|
||||
}
|
||||
|
||||
void TermControl::_contextMenuHandler(IInspectable /*sender*/,
|
||||
Control::ContextMenuRequestedEventArgs args)
|
||||
{
|
||||
|
|
|
@ -51,6 +51,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
Windows::Foundation::Size MinimumSize();
|
||||
float SnapDimensionToGrid(const bool widthOrHeight, const float dimension);
|
||||
|
||||
Windows::Foundation::Point CursorPositionInDips();
|
||||
|
||||
void WindowVisibilityChanged(const bool showOrHide);
|
||||
|
||||
void ColorSelection(Control::SelectionColor fg, Control::SelectionColor bg, Core::MatchMode matchMode);
|
||||
|
@ -172,6 +174,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
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);
|
||||
|
@ -394,7 +397,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
Control::ControlCore::ConnectionStateChanged_revoker ConnectionStateChanged;
|
||||
Control::ControlCore::ShowWindowChanged_revoker ShowWindowChanged;
|
||||
Control::ControlCore::CloseTerminalRequested_revoker CloseTerminalRequested;
|
||||
Control::ControlCore::CompletionsChanged_revoker CompletionsChanged;
|
||||
Control::ControlCore::RestartTerminalRequested_revoker RestartTerminalRequested;
|
||||
|
||||
// These are set up in _InitializeTerminal
|
||||
Control::ControlCore::RendererWarning_revoker RendererWarning;
|
||||
Control::ControlCore::SwapChainChanged_revoker SwapChainChanged;
|
||||
|
|
|
@ -53,10 +53,14 @@ namespace Microsoft.Terminal.Control
|
|||
event Windows.Foundation.TypedEventHandler<Object, Object> TabColorChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> ReadOnlyChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> FocusFollowMouseRequested;
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, CompletionsChangedEventArgs> CompletionsChanged;
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, KeySentEventArgs> KeySent;
|
||||
event Windows.Foundation.TypedEventHandler<Object, CharSentEventArgs> CharSent;
|
||||
event Windows.Foundation.TypedEventHandler<Object, StringSentEventArgs> StringSent;
|
||||
|
||||
|
||||
Microsoft.UI.Xaml.Controls.CommandBarFlyout ContextMenu { get; };
|
||||
Microsoft.UI.Xaml.Controls.CommandBarFlyout SelectionContextMenu { get; };
|
||||
|
||||
|
@ -123,6 +127,8 @@ namespace Microsoft.Terminal.Control
|
|||
|
||||
void ColorSelection(SelectionColor fg, SelectionColor bg, Microsoft.Terminal.Core.MatchMode matchMode);
|
||||
|
||||
Windows.Foundation.Point CursorPositionInDips { get; };
|
||||
|
||||
void ShowContextMenu();
|
||||
|
||||
void Detach();
|
||||
|
|
|
@ -1259,6 +1259,11 @@ const size_t Microsoft::Terminal::Core::Terminal::GetTaskbarProgress() const noe
|
|||
return _taskbarProgress;
|
||||
}
|
||||
|
||||
void Microsoft::Terminal::Core::Terminal::CompletionsChangedCallback(std::function<void(std::wstring_view, unsigned int)> pfn) noexcept
|
||||
{
|
||||
_pfnCompletionsChanged.swap(pfn);
|
||||
}
|
||||
|
||||
Scheme Terminal::GetColorScheme() const
|
||||
{
|
||||
Scheme s;
|
||||
|
|
|
@ -152,6 +152,9 @@ public:
|
|||
bool IsVtInputEnabled() const noexcept override;
|
||||
void NotifyAccessibilityChange(const til::rect& changedRect) noexcept override;
|
||||
void NotifyBufferRotation(const int delta) override;
|
||||
|
||||
void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) override;
|
||||
|
||||
#pragma endregion
|
||||
|
||||
void ClearMark();
|
||||
|
@ -225,6 +228,7 @@ public:
|
|||
void TaskbarProgressChangedCallback(std::function<void()> pfn) noexcept;
|
||||
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 SetCursorOn(const bool isOn);
|
||||
bool IsCursorBlinkingAllowed() const noexcept;
|
||||
|
@ -322,6 +326,7 @@ private:
|
|||
std::function<void()> _pfnTaskbarProgressChanged;
|
||||
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;
|
||||
|
||||
RenderSettings _renderSettings;
|
||||
std::unique_ptr<::Microsoft::Console::VirtualTerminal::StateMachine> _stateMachine;
|
||||
|
|
|
@ -431,6 +431,14 @@ void Terminal::NotifyAccessibilityChange(const til::rect& /*changedRect*/) noexc
|
|||
// This is only needed in conhost. Terminal handles accessibility in another way.
|
||||
}
|
||||
|
||||
void Terminal::InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength)
|
||||
{
|
||||
if (_pfnCompletionsChanged)
|
||||
{
|
||||
_pfnCompletionsChanged(menuJson, replaceLength);
|
||||
}
|
||||
}
|
||||
|
||||
void Terminal::NotifyBufferRotation(const int delta)
|
||||
{
|
||||
// Update our selection, so it doesn't move as the buffer is cycled
|
||||
|
|
|
@ -632,4 +632,102 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
|
||||
return newCommands;
|
||||
}
|
||||
|
||||
winrt::Windows::Foundation::Collections::IVector<Model::Command> Command::ParsePowerShellMenuComplete(winrt::hstring json, int32_t replaceLength)
|
||||
{
|
||||
if (json.empty())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
auto data = winrt::to_string(json);
|
||||
|
||||
std::string errs;
|
||||
static std::unique_ptr<Json::CharReader> reader{ Json::CharReaderBuilder{}.newCharReader() };
|
||||
Json::Value root;
|
||||
if (!reader->parse(data.data(), data.data() + data.size(), &root, &errs))
|
||||
{
|
||||
throw winrt::hresult_error(WEB_E_INVALID_JSON_STRING, winrt::to_hstring(errs));
|
||||
}
|
||||
|
||||
std::vector<Model::Command> result;
|
||||
|
||||
const auto parseElement = [&](const auto& element) {
|
||||
winrt::hstring completionText;
|
||||
winrt::hstring listText;
|
||||
JsonUtils::GetValueForKey(element, "CompletionText", completionText);
|
||||
JsonUtils::GetValueForKey(element, "ListItemText", listText);
|
||||
|
||||
auto args = winrt::make_self<SendInputArgs>(
|
||||
winrt::hstring{ fmt::format(FMT_COMPILE(L"{:\x7f^{}}{}"),
|
||||
L"",
|
||||
replaceLength,
|
||||
static_cast<std::wstring_view>(completionText)) });
|
||||
|
||||
Model::ActionAndArgs actionAndArgs{ ShortcutAction::SendInput, *args };
|
||||
|
||||
auto c = winrt::make_self<Command>();
|
||||
c->_name = listText;
|
||||
c->_ActionAndArgs = actionAndArgs;
|
||||
|
||||
// Try to assign a sensible icon based on the result type. These are
|
||||
// roughly chosen to align with the icons in
|
||||
// https://github.com/PowerShell/PowerShellEditorServices/pull/1738
|
||||
// as best as possible.
|
||||
if (const auto resultType{ JsonUtils::GetValueForKey<int>(element, "ResultType") })
|
||||
{
|
||||
// PowerShell completion result -> Segoe Fluent icon value & name
|
||||
switch (resultType)
|
||||
{
|
||||
case 1: // History -> 0xe81c History
|
||||
c->_iconPath = L"\ue81c";
|
||||
break;
|
||||
case 2: // Command -> 0xecaa AppIconDefault
|
||||
c->_iconPath = L"\uecaa";
|
||||
break;
|
||||
case 3: // ProviderItem -> 0xe8e4 AlignLeft
|
||||
c->_iconPath = L"\ue8e4";
|
||||
break;
|
||||
case 4: // ProviderContainer -> 0xe838 FolderOpen
|
||||
c->_iconPath = L"\ue838";
|
||||
break;
|
||||
case 5: // Property -> 0xe7c1 Flag
|
||||
c->_iconPath = L"\ue7c1";
|
||||
break;
|
||||
case 6: // Method -> 0xecaa AppIconDefault
|
||||
c->_iconPath = L"\uecaa";
|
||||
break;
|
||||
case 7: // ParameterName -> 0xe7c1 Flag
|
||||
c->_iconPath = L"\ue7c1";
|
||||
break;
|
||||
case 8: // ParameterValue -> 0xf000 KnowledgeArticle
|
||||
c->_iconPath = L"\uf000";
|
||||
break;
|
||||
case 10: // Namespace -> 0xe943 Code
|
||||
c->_iconPath = L"\ue943";
|
||||
break;
|
||||
case 13: // DynamicKeyword -> 0xe945 LightningBolt
|
||||
c->_iconPath = L"\ue945";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
result.push_back(*c);
|
||||
};
|
||||
|
||||
if (root.isArray())
|
||||
{
|
||||
// If we got a whole array of suggestions, parse each one.
|
||||
for (const auto& element : root)
|
||||
{
|
||||
parseElement(element);
|
||||
}
|
||||
}
|
||||
else if (root.isObject())
|
||||
{
|
||||
// If we instead only got a single element back, just parse the root element.
|
||||
parseElement(root);
|
||||
}
|
||||
|
||||
return winrt::single_threaded_vector<Model::Command>(std::move(result));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,6 +66,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
hstring IconPath() const noexcept;
|
||||
void IconPath(const hstring& val);
|
||||
|
||||
static Windows::Foundation::Collections::IVector<Model::Command> ParsePowerShellMenuComplete(winrt::hstring json, int32_t replaceLength);
|
||||
|
||||
WINRT_PROPERTY(ExpandCommandType, IterateOn, ExpandCommandType::None);
|
||||
WINRT_PROPERTY(Model::ActionAndArgs, ActionAndArgs);
|
||||
|
||||
|
|
|
@ -45,5 +45,7 @@ namespace Microsoft.Terminal.Settings.Model
|
|||
|
||||
Boolean HasNestedCommands { get; };
|
||||
Windows.Foundation.Collections.IMapView<String, Command> NestedCommands { get; };
|
||||
|
||||
static IVector<Command> ParsePowerShellMenuComplete(String json, Int32 replaceLength);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -98,6 +98,7 @@ namespace Microsoft.Terminal.Settings.Model
|
|||
INHERITABLE_SETTING(Boolean, ShowAdminShield);
|
||||
INHERITABLE_SETTING(IVector<NewTabMenuEntry>, NewTabMenu);
|
||||
INHERITABLE_SETTING(Boolean, EnableColorSelection);
|
||||
INHERITABLE_SETTING(Boolean, EnableShellCompletionMenu);
|
||||
INHERITABLE_SETTING(Boolean, IsolatedMode);
|
||||
INHERITABLE_SETTING(Boolean, AllowHeadless);
|
||||
INHERITABLE_SETTING(String, SearchWebDefaultQueryUrl);
|
||||
|
|
|
@ -63,6 +63,7 @@ Author(s):
|
|||
X(bool, ShowAdminShield, "showAdminShield", true) \
|
||||
X(bool, TrimPaste, "trimPaste", true) \
|
||||
X(bool, EnableColorSelection, "experimental.enableColorSelection", false) \
|
||||
X(bool, EnableShellCompletionMenu, "experimental.enableShellCompletionMenu", false) \
|
||||
X(winrt::Windows::Foundation::Collections::IVector<Model::NewTabMenuEntry>, NewTabMenu, "newTabMenu", winrt::single_threaded_vector<Model::NewTabMenuEntry>({ Model::RemainingProfilesEntry{} })) \
|
||||
X(bool, AllowHeadless, "compatibility.allowHeadless", false) \
|
||||
X(bool, IsolatedMode, "compatibility.isolatedMode", false) \
|
||||
|
|
|
@ -163,6 +163,17 @@
|
|||
</alwaysEnabledBrandingTokens>
|
||||
</feature>
|
||||
|
||||
<feature>
|
||||
<name>Feature_ShellCompletions</name>
|
||||
<description>An experimental escape sequence for client applications to request the Terminal display a list of suggestions.</description>
|
||||
<id>3121</id>
|
||||
<stage>AlwaysDisabled</stage>
|
||||
<alwaysEnabledBrandingTokens>
|
||||
<brandingToken>Dev</brandingToken>
|
||||
<brandingToken>Preview</brandingToken>
|
||||
</alwaysEnabledBrandingTokens>
|
||||
</feature>
|
||||
|
||||
<feature>
|
||||
<name>Feature_VtChecksumReport</name>
|
||||
<description>Enables the DECRQCRA checksum report, which can be used to read the screen contents</description>
|
||||
|
|
|
@ -453,3 +453,7 @@ void ConhostInternalGetSet::MarkCommandFinish(std::optional<unsigned int> /*erro
|
|||
{
|
||||
// Not implemented for conhost.
|
||||
}
|
||||
void ConhostInternalGetSet::InvokeCompletions(std::wstring_view /*menuJson*/, unsigned int /*replaceLength*/)
|
||||
{
|
||||
// Not implemented for conhost.
|
||||
}
|
||||
|
|
|
@ -74,6 +74,8 @@ public:
|
|||
void MarkOutputStart() override;
|
||||
void MarkCommandFinish(std::optional<unsigned int> error) override;
|
||||
|
||||
void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) override;
|
||||
|
||||
private:
|
||||
Microsoft::Console::IIoProvider& _io;
|
||||
};
|
||||
|
|
|
@ -137,6 +137,8 @@ public:
|
|||
|
||||
virtual bool DoFinalTermAction(const std::wstring_view string) = 0;
|
||||
|
||||
virtual bool DoVsCodeAction(const std::wstring_view string) = 0;
|
||||
|
||||
virtual StringHandler DownloadDRCS(const VTInt fontNumber,
|
||||
const VTParameter startChar,
|
||||
const DispatchTypes::DrcsEraseControl eraseControl,
|
||||
|
|
|
@ -85,5 +85,7 @@ namespace Microsoft::Console::VirtualTerminal
|
|||
virtual void MarkCommandStart() = 0;
|
||||
virtual void MarkOutputStart() = 0;
|
||||
virtual void MarkCommandFinish(std::optional<unsigned int> error) = 0;
|
||||
|
||||
virtual void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) = 0;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -3738,6 +3738,86 @@ bool AdaptDispatch::DoFinalTermAction(const std::wstring_view string)
|
|||
// modify the state of that mark as we go.
|
||||
return false;
|
||||
}
|
||||
// Method Description:
|
||||
// - Performs a VsCode action
|
||||
// - Currently, the actions we support are:
|
||||
// * Completions: An experimental protocol for passing shell completion
|
||||
// information from the shell to the terminal. This sequence is still under
|
||||
// active development, and subject to change.
|
||||
// - Not actually used in conhost
|
||||
// Arguments:
|
||||
// - string: contains the parameters that define which action we do
|
||||
// Return Value:
|
||||
// - false in conhost, true for the SetMark action, otherwise false.
|
||||
bool AdaptDispatch::DoVsCodeAction(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;
|
||||
}
|
||||
|
||||
if constexpr (!Feature_ShellCompletions::IsEnabled())
|
||||
{
|
||||
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"Completions")
|
||||
{
|
||||
// The structure of the message is as follows:
|
||||
// `e]633;
|
||||
// 0: Completions;
|
||||
// 1: $($completions.ReplacementIndex);
|
||||
// 2: $($completions.ReplacementLength);
|
||||
// 3: $($cursorIndex);
|
||||
// 4: $completions.CompletionMatches | ConvertTo-Json
|
||||
unsigned int replacementIndex = 0;
|
||||
unsigned int replacementLength = 0;
|
||||
unsigned int cursorIndex = 0;
|
||||
|
||||
bool succeeded = (parts.size() >= 2) &&
|
||||
(Utils::StringToUint(til::at(parts, 1), replacementIndex));
|
||||
succeeded &= (parts.size() >= 3) &&
|
||||
(Utils::StringToUint(til::at(parts, 2), replacementLength));
|
||||
succeeded &= (parts.size() >= 4) &&
|
||||
(Utils::StringToUint(til::at(parts, 3), cursorIndex));
|
||||
|
||||
// VsCode is using cursorIndex and replacementIndex, but we aren't currently.
|
||||
if (succeeded)
|
||||
{
|
||||
// Get the combined lengths of parts 0-3, plus the semicolons. We
|
||||
// need this so that we can just pass the remainder of the string.
|
||||
const auto prefixLength = til::at(parts, 0).size() + 1 +
|
||||
til::at(parts, 1).size() + 1 +
|
||||
til::at(parts, 2).size() + 1 +
|
||||
til::at(parts, 3).size() + 1;
|
||||
if (prefixLength > string.size())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// Get the remainder of the string
|
||||
const auto remainder = string.substr(prefixLength);
|
||||
|
||||
_api.InvokeCompletions(parts.size() < 5 ? L"" : remainder,
|
||||
replacementLength);
|
||||
}
|
||||
|
||||
// If it's poorly formatted, just eat it
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - DECDLD - Downloads one or more characters of a dynamically redefinable
|
||||
|
|
|
@ -139,6 +139,8 @@ namespace Microsoft::Console::VirtualTerminal
|
|||
|
||||
bool DoFinalTermAction(const std::wstring_view string) override;
|
||||
|
||||
bool DoVsCodeAction(const std::wstring_view string) override;
|
||||
|
||||
StringHandler DownloadDRCS(const VTInt fontNumber,
|
||||
const VTParameter startChar,
|
||||
const DispatchTypes::DrcsEraseControl eraseControl,
|
||||
|
|
|
@ -130,6 +130,8 @@ public:
|
|||
|
||||
bool DoFinalTermAction(const std::wstring_view /*string*/) override { return false; }
|
||||
|
||||
bool DoVsCodeAction(const std::wstring_view /*string*/) override { return false; }
|
||||
|
||||
StringHandler DownloadDRCS(const VTInt /*fontNumber*/,
|
||||
const VTParameter /*startChar*/,
|
||||
const DispatchTypes::DrcsEraseControl /*eraseControl*/,
|
||||
|
|
|
@ -231,6 +231,12 @@ public:
|
|||
{
|
||||
Log::Comment(L"MarkCommandFinish MOCK called...");
|
||||
}
|
||||
void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) override
|
||||
{
|
||||
Log::Comment(L"InvokeCompletions MOCK called...");
|
||||
VERIFY_ARE_EQUAL(_expectedMenuJson, menuJson);
|
||||
VERIFY_ARE_EQUAL(_expectedReplaceLength, replaceLength);
|
||||
}
|
||||
|
||||
void PrepData()
|
||||
{
|
||||
|
@ -378,6 +384,9 @@ public:
|
|||
bool _getConsoleOutputCPResult = false;
|
||||
bool _expectedShowWindow = false;
|
||||
|
||||
std::wstring _expectedMenuJson{};
|
||||
unsigned int _expectedReplaceLength = 0;
|
||||
|
||||
private:
|
||||
HANDLE _hCon;
|
||||
};
|
||||
|
@ -3005,6 +3014,33 @@ public:
|
|||
_testGetSet->ValidateInputEvent(expectedResponse.c_str());
|
||||
}
|
||||
|
||||
TEST_METHOD(MenuCompletionsTests)
|
||||
{
|
||||
_testGetSet->PrepData();
|
||||
|
||||
Log::Comment(L"Not enough parameters");
|
||||
VERIFY_IS_FALSE(_pDispatch->DoVsCodeAction(LR"(garbage)"));
|
||||
|
||||
Log::Comment(L"Not enough parameters");
|
||||
VERIFY_IS_TRUE(_pDispatch->DoVsCodeAction(LR"(Completions)"));
|
||||
VERIFY_IS_TRUE(_pDispatch->DoVsCodeAction(LR"(Completions;)"));
|
||||
VERIFY_IS_TRUE(_pDispatch->DoVsCodeAction(LR"(Completions;10;)"));
|
||||
VERIFY_IS_TRUE(_pDispatch->DoVsCodeAction(LR"(Completions;10;20)"));
|
||||
VERIFY_IS_TRUE(_pDispatch->DoVsCodeAction(LR"(Completions;10;20;)"));
|
||||
Log::Comment(L"No trailing semicolon");
|
||||
VERIFY_IS_TRUE(_pDispatch->DoVsCodeAction(LR"(Completions;10;20;3)"));
|
||||
|
||||
Log::Comment(L"Normal, good case");
|
||||
_testGetSet->_expectedMenuJson = LR"({ "foo": 1, "bar": 2 })";
|
||||
_testGetSet->_expectedReplaceLength = 2;
|
||||
VERIFY_IS_TRUE(_pDispatch->DoVsCodeAction(LR"(Completions;1;2;3;{ "foo": 1, "bar": 2 })"));
|
||||
|
||||
Log::Comment(L"JSON has a semicolon in it");
|
||||
_testGetSet->_expectedMenuJson = LR"({ "foo": "what;ever", "bar": 2 })";
|
||||
_testGetSet->_expectedReplaceLength = 20;
|
||||
VERIFY_IS_TRUE(_pDispatch->DoVsCodeAction(LR"(Completions;10;20;30;{ "foo": "what;ever", "bar": 2 })"));
|
||||
}
|
||||
|
||||
private:
|
||||
TerminalInput _terminalInput;
|
||||
std::unique_ptr<TestGetSet> _testGetSet;
|
||||
|
|
|
@ -868,6 +868,11 @@ bool OutputStateMachineEngine::ActionOscDispatch(const wchar_t /*wch*/,
|
|||
success = _dispatch->DoFinalTermAction(string);
|
||||
break;
|
||||
}
|
||||
case OscActionCodes::VsCodeAction:
|
||||
{
|
||||
success = _dispatch->DoVsCodeAction(string);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// If no functions to call, overall dispatch was a failure.
|
||||
success = false;
|
||||
|
|
|
@ -214,6 +214,7 @@ namespace Microsoft::Console::VirtualTerminal
|
|||
ResetBackgroundColor = 111, // Not implemented
|
||||
ResetCursorColor = 112,
|
||||
FinalTermAction = 133,
|
||||
VsCodeAction = 633,
|
||||
ITerm2Action = 1337,
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue