Compare commits

...

78 Commits

Author SHA1 Message Date
Mike Griese
a1043bac4b a bunch of notes before I abandon this 2023-08-10 11:22:24 -05:00
Mike Griese
33197c548d Merge branch 'dev/migrie/fhl-2023/pwsh-autocomplete-demo' into dev/migrie/f/3121-tooltips 2023-08-09 06:44:06 -05:00
Mike Griese
dff4ae3282 update my formatter so it stops blowing up 2023-08-09 06:43:48 -05:00
Mike Griese
53281b8284 Merge branch 'dev/migrie/fhl-2023/pwsh-autocomplete-demo' into dev/migrie/f/3121-tooltips 2023-08-09 06:29:35 -05:00
Mike Griese
2bceb92330 these are also vestigial 2023-08-09 06:29:25 -05:00
Mike Griese
ef7f563757 this would crash the palette if there were nested items 2023-08-09 06:18:02 -05:00
Mike Griese
6ee0d9f992 Merge branch 'dev/migrie/fhl-2023/pwsh-autocomplete-demo' into dev/migrie/f/3121-tooltips 2023-08-09 05:44:06 -05:00
Mike Griese
1759213233 oneliner mistakes 2023-08-09 05:43:26 -05:00
Mike Griese
6368a6d8f0 Merge branch 'dev/migrie/fhl-2023/pwsh-autocomplete-demo' into dev/migrie/f/3121-tooltips 2023-08-08 11:40:03 -05:00
Mike Griese
873d8d482c Merge remote-tracking branch 'origin/main' into dev/migrie/fhl-2023/pwsh-autocomplete-demo 2023-08-08 11:39:41 -05:00
Mike Griese
4de93031a2 runformat 2023-08-08 10:18:42 -05:00
Mike Griese
8f0b6f8a2b revert 1449088, d3b5533 and 0bda66f, because we didn't know what they did.
Maybe relevant to #12861
2023-08-08 10:10:35 -05:00
Mike Griese
902ee8eb33 raise the event relative to the sender 2023-08-08 10:09:58 -05:00
Mike Griese
0cf6475bd7 whole pile of PR nits 2023-08-08 08:42:04 -05:00
Mike Griese
a013020e10 Merge remote-tracking branch 'origin/main' into dev/migrie/fhl-2023/pwsh-autocomplete-demo 2023-08-08 05:05:15 -05:00
Mike Griese
f9a764e112 lots of load bearing code here 2023-08-02 15:53:43 -05:00
Mike Griese
f33a35b0d1 Merge branch 'dev/migrie/fhl-2023/pwsh-autocomplete-demo' into dev/migrie/f/3121-tooltips 2023-08-02 14:07:09 -05:00
Mike Griese
3ee1f9ba8a load bearing I guess 2023-08-02 14:04:20 -05:00
Mike Griese
7c3fd2aeec Remove input previewing. We can necro later. It's too hacky 2023-08-02 12:45:34 -05:00
Mike Griese
faa6665698 Merge branch 'dev/migrie/fhl-2023/pwsh-autocomplete-demo' into dev/migrie/f/3121-tooltips 2023-08-02 09:04:49 -05:00
Mike Griese
1720905027 last refactoring - Open() instead of Anchor() 2023-08-02 09:00:39 -05:00
Mike Griese
7ae471644c use less int math where possible 2023-08-01 14:45:02 -05:00
Mike Griese
1a8275e758 move code around 2023-08-01 14:09:27 -05:00
Mike Griese
f10f14c471 this is better 2023-08-01 13:51:09 -05:00
Mike Griese
12d216073d minor code movement 2023-08-01 13:01:48 -05:00
Mike Griese
434572a122 Merge branch 'main' into dev/migrie/fhl-2023/pwsh-autocomplete-demo 2023-08-01 11:44:11 -05:00
Mike Griese
741b7732a1 This needs to go way earlier in the stack of PRs
(cherry picked from commit d3e572d8fd)
2023-07-06 10:20:44 -05:00
Mike Griese
39c5b6372b Merge branch 'dev/migrie/fhl-2023/pwsh-autocomplete-demo' into dev/migrie/f/3121-tooltips 2023-07-05 13:34:05 -05:00
Mike Griese
2cf9f412d2 Merge branch 'main' into dev/migrie/fhl-2023/pwsh-autocomplete-demo 2023-07-05 13:33:45 -05:00
Mike Griese
56cc44d510 Merge branch 'dev/migrie/fhl-2023/pwsh-autocomplete-demo' into dev/migrie/f/3121-tooltips 2023-05-31 13:57:57 -05:00
Mike Griese
fb40b5d6b5 lazy loading for fun and profit 2023-05-31 13:54:28 -05:00
Mike Griese
3fc364cf8a Merge branch 'main' into dev/migrie/fhl-2023/pwsh-autocomplete-demo 2023-05-31 13:38:57 -05:00
Mike Griese
c49f765121 Merge branch 'dev/migrie/fhl-2023/pwsh-autocomplete-demo' into dev/migrie/f/3121-tooltips 2023-04-05 10:09:04 -05:00
Mike Griese
057098ea6f Merge remote-tracking branch 'origin/main' into dev/migrie/fhl-2023/pwsh-autocomplete-demo 2023-04-05 10:08:16 -05:00
Mike Griese
a010e23753 Merge branch 'dev/migrie/fhl-2023/pwsh-autocomplete-demo' into dev/migrie/f/3121-tooltips 2023-03-24 16:33:32 -05:00
Mike Griese
11916fd623 Merge remote-tracking branch 'origin/main' into dev/migrie/fhl-2023/pwsh-autocomplete-demo 2023-03-24 16:11:10 -05:00
Mike Griese
dd5458cf8c spell
(cherry picked from commit 62fb61d048)
2023-03-21 11:08:30 -05:00
Mike Griese
19c1ed90ed This fixes a previewing issue, all the way back in the first PR
(cherry picked from commit 0c19591d48)
2023-03-21 11:08:08 -05:00
Mike Griese
6c778a69cd yea no I always run the tests 2023-03-20 17:26:05 -05:00
Mike Griese
059fa07095 Merge branch 'dev/migrie/fhl-2023/pwsh-autocomplete-demo' into dev/migrie/f/3121-tooltips 2023-03-20 13:27:30 -05:00
Mike Griese
b0964692ed austin mode 2023-03-20 13:26:52 -05:00
Mike Griese
36b676a16c Add a setting to opt in, because feature flags are imprecise 2023-03-20 12:53:39 -05:00
Mike Griese
28555a6faa Merge remote-tracking branch 'origin/main' into dev/migrie/fhl-2023/pwsh-autocomplete-demo 2023-03-20 11:51:43 -05:00
Mike Griese
22c94bf10d spell 2023-03-10 14:57:56 -06:00
Mike Griese
cd2db82515 Merge branch 'main' into dev/migrie/fhl-2023/pwsh-autocomplete-demo 2023-03-10 14:57:31 -06:00
Mike Griese
110bb7da9c surprisingly load bearing 2023-03-01 13:28:11 -06:00
Mike Griese
de56d9e6c8 Merge branch 'dev/migrie/fhl-2023/pwsh-autocomplete-demo' into dev/migrie/f/3121-tooltips 2023-03-01 13:13:46 -06:00
Mike Griese
dcdb145500 Dismiss tooltip on close 2023-03-01 13:13:36 -06:00
Mike Griese
20f5651224 final cleanup for review 2023-03-01 11:07:24 -06:00
Mike Griese
e09379c64f Merge branch 'dev/migrie/fhl-2023/pwsh-autocomplete-demo' into dev/migrie/f/3121-tooltips 2023-03-01 08:35:35 -06:00
Mike Griese
173a830339 Finish detaching it from the popup
(cherry picked from commit a172f53597)
2023-03-01 08:23:39 -06:00
Mike Griese
e544871893 very confident we can get all the logic into the sxnui
(cherry picked from commit 57a53279ae)
2023-03-01 08:23:32 -06:00
Mike Griese
e2cc27889a POC: We can take it out of the Popup and have it still work
(cherry picked from commit 5defde545f)
2023-03-01 08:23:16 -06:00
Mike Griese
c839bfa2ca Cleanup for... review? 2023-02-24 11:30:12 -06:00
Mike Griese
06874ee321 dirty, but now with tooltips 2023-02-24 10:48:36 -06:00
Mike Griese
36b5759604 [PARENT] lmao I deleted the line that sends the inpu :facepalm" 2023-02-24 10:43:30 -06:00
Mike Griese
84a41b4504 cleanup _for review_? 2023-02-23 16:34:16 -06:00
Mike Griese
7988d89c08 code cleanup 2023-02-23 14:08:07 -06:00
Mike Griese
a3c4776355 clamp horizontally 2023-02-23 14:01:36 -06:00
Mike Griese
4516b4b1b3 Properly account for the position of panes when opening the menu 2023-02-22 14:51:47 -06:00
Mike Griese
f5909d9baa Open in different directions based on available space 2023-02-22 13:56:01 -06:00
Mike Griese
8feb9098b9 bottom-up mode is basically done 2023-02-21 15:52:39 -06:00
Mike Griese
d94183b897 icons are _slick_ 2023-02-21 14:25:58 -06:00
Mike Griese
2c66c32f97 bottoms up bottoms up 2023-02-21 11:20:03 -06:00
Mike Griese
ff5eead9ea Very important that the backspaces are trimmed from the preview 2023-02-21 10:25:55 -06:00
Mike Griese
026f342bc5 Merge remote-tracking branch 'origin/dev/migrie/f/3121-wE-dOnT-hAvE-dEv-DaYs' into dev/migrie/fhl-2023/pwsh-autocomplete-demo 2023-02-21 10:00:20 -06:00
Mike Griese
48e7348f5c small updates to the protocol 2023-02-16 13:04:13 -06:00
Mike Griese
e785bfc8bb Merge branch 'main' into dev/migrie/f/12861-preview-input 2023-02-16 13:02:40 -06:00
Mike Griese
985fcdbdb6 better UX for typing 2023-02-12 15:37:36 -06:00
Mike Griese
b0fa972ec1 make the menu mode compact, and remove the search box 2023-02-12 11:07:00 -06:00
Mike Griese
f361b6c879 lots of removal of dead code from the sxnui 2023-02-10 06:55:16 -06:00
Mike Griese
7404dc3d35 zhu li, do the thing 2023-02-10 06:02:03 -06:00
Mike Griese
c97ac66d5d resart with fresh plumbing 2023-02-09 06:31:44 -06:00
Dustin L. Howett
ccfc83443b Migrate spelling-0.0.21 changes from main 2022-04-29 06:55:15 -05:00
Mike Griese
0bda66fc2f a comment I missed 2022-04-29 06:55:15 -05:00
Mike Griese
d3b5533a1e fix remaining bugs 2022-04-29 06:22:48 -05:00
Mike Griese
1449088e80 bugfixes for the demo 2022-04-28 16:17:21 -05:00
Mike Griese
c96799c6e9 Preview the input via the TSF input control. This is awesome, and should go into main 2022-04-28 16:14:51 -05:00
40 changed files with 2121 additions and 5 deletions

View File

@@ -95,6 +95,7 @@ slnt
Sos
ssh
stakeholders
sxn
timeline
timelines
timestamped

View File

@@ -315,6 +315,7 @@ CPLINFO
cplusplus
CPPCORECHECK
cppcorecheckrules
cpprest
cpprestsdk
cppwinrt
CProc
@@ -1452,6 +1453,7 @@ PPEB
ppf
ppguid
ppidl
pplx
PPROC
ppropvar
ppsi

View File

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

View File

@@ -0,0 +1,136 @@
// 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);
winrt::fire_and_forget _selectedCommandChanged(const Windows::Foundation::IInspectable& sender,
const Windows::UI::Xaml::RoutedEventArgs& args);
winrt::fire_and_forget _openTooltip(Microsoft::Terminal::Settings::Model::Command cmd);
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);
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,223 @@
<!--
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="&#xE76C;" />
</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="&#xE76b;" />
</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" />
<mux:TeachingTip x:Name="DescriptionTip"
IsOpen="False"
PreferredPlacement="Right"
ShouldConstrainToRootBounds="False">
<ScrollViewer HorizontalScrollBarVisibility="Hidden"
HorizontalScrollMode="Enabled">
<TextBlock x:Name="_toolTipContent" />
</ScrollViewer>
</mux:TeachingTip>
</Grid>
</Grid>
</UserControl>

View File

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

View File

@@ -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*/)
{

View File

@@ -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);

View File

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

View File

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

View File

@@ -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() };

View File

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

View File

@@ -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;
};
}

View File

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

View File

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

View File

@@ -90,6 +90,12 @@ namespace Microsoft.Terminal.Control
Boolean ClearMarkers { get; };
}
runtimeclass CompletionsChangedEventArgs
{
String MenuJson { get; };
UInt32 ReplacementLength { get; };
}
runtimeclass KeySentEventArgs
{
UInt16 VKey { get; };

View File

@@ -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)
{

View File

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

View File

@@ -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();

View File

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

View File

@@ -136,6 +136,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();
@@ -209,6 +212,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;
@@ -306,6 +310,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;

View File

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

View File

@@ -27,6 +27,7 @@ static constexpr std::string_view ArgsKey{ "args" };
static constexpr std::string_view IterateOnKey{ "iterateOn" };
static constexpr std::string_view CommandsKey{ "commands" };
static constexpr std::string_view KeysKey{ "keys" };
static constexpr std::string_view DescriptionKey{ "description" };
static constexpr std::string_view ProfileNameToken{ "${profile.name}" };
static constexpr std::string_view ProfileIconToken{ "${profile.icon}" };
@@ -40,6 +41,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
auto command{ winrt::make_self<Command>() };
command->_name = _name;
command->_Description = _Description;
command->_ActionAndArgs = *get_self<implementation::ActionAndArgs>(_ActionAndArgs)->Copy();
command->_keyMappings = _keyMappings;
command->_iconPath = _iconPath;
@@ -254,7 +256,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
auto nested = false;
JsonUtils::GetValueForKey(json, IterateOnKey, result->_IterateOn);
JsonUtils::GetValueForKey(json, DescriptionKey, result->_Description);
// For iterable commands, we'll make another pass at parsing them once
// the json is patched. So ignore parsing sub-commands for now. Commands
// will only be marked iterable on the first pass.
@@ -406,7 +408,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
Json::Value cmdJson{ Json::ValueType::objectValue };
JsonUtils::SetValueForKey(cmdJson, IconKey, _iconPath);
JsonUtils::SetValueForKey(cmdJson, NameKey, _name);
JsonUtils::SetValueForKey(cmdJson, DescriptionKey, _Description);
if (_ActionAndArgs)
{
cmdJson[JsonKey(ActionKey)] = ActionAndArgs::ToJson(_ActionAndArgs);
@@ -426,6 +428,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// First iteration also writes icon and name
JsonUtils::SetValueForKey(cmdJson, IconKey, _iconPath);
JsonUtils::SetValueForKey(cmdJson, NameKey, _name);
JsonUtils::SetValueForKey(cmdJson, DescriptionKey, _Description);
}
if (_ActionAndArgs)
@@ -632,4 +635,105 @@ 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;
winrt::hstring tooltipText;
JsonUtils::GetValueForKey(element, "CompletionText", completionText);
JsonUtils::GetValueForKey(element, "ListItemText", listText);
JsonUtils::GetValueForKey(element, "ToolTip", tooltipText);
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;
c->_Description = tooltipText;
// 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));
}
}

View File

@@ -66,8 +66,11 @@ 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);
WINRT_PROPERTY(winrt::hstring, Description, L"");
private:
Json::Value _originalJson;

View File

@@ -36,6 +36,8 @@ namespace Microsoft.Terminal.Settings.Model
Command();
String Name { get; };
String Description { get; };
ActionAndArgs ActionAndArgs { get; };
Microsoft.Terminal.Control.KeyChord Keys { get; };
void RegisterKey(Microsoft.Terminal.Control.KeyChord keys);
@@ -45,5 +47,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);
}
}

View File

@@ -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);

View File

@@ -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) \

View File

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

View File

@@ -454,3 +454,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.
}

View File

@@ -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;
};

View File

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

View File

@@ -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;
};
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -214,6 +214,7 @@ namespace Microsoft::Console::VirtualTerminal
ResetBackgroundColor = 111, // Not implemented
ResetCursorColor = 112,
FinalTermAction = 133,
VsCodeAction = 633,
ITerm2Action = 1337,
};