Add an action to search the web for selected text (#15539)

This PR adds a `searchWeb` command to search the selected text on the web.
Arguments:
- `queryUrl`: URL of the web page to launch (the selected text will be
inserted where the first `%s` is found in the query string)

To make the search text more "compact" and handle multi-line selections,
I'm concatenating the selected lines and replacing consecutive
whitespaces with a single space (we may change this with something more
clever in case).

## Validation Steps Performed
Manual testing with single, multi-line, block selections.

Closes #10175

---------

Co-authored-by: Marco Pelagatti <marco.pelagatti@iongroup.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
This commit is contained in:
MPela 2023-06-21 19:24:45 +02:00 committed by GitHub
parent 2386abb8df
commit 40963c7b18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 143 additions and 7 deletions

View File

@ -421,6 +421,7 @@
"scrollToMark",
"clearMark",
"clearAllMarks",
"searchWeb",
"experimental.colorSelection",
"unbound"
],
@ -1709,6 +1710,29 @@
}
]
},
"SearchWebAction": {
"description": "Search the web for selected text",
"allOf": [
{
"$ref": "#/$defs/ShortcutAction"
},
{
"properties": {
"action": {
"type": "string",
"const": "searchWeb"
},
"queryUrl": {
"type": "string",
"description": "URL of the web page to launch, %s is replaced with the selected text"
}
}
}
],
"required": [
"queryUrl"
]
},
"AdjustOpacityAction": {
"description": "Changes the opacity of the active Terminal window. If `relative` is specified, then this action will increase/decrease relative to the current opacity.",
"allOf": [
@ -2004,6 +2028,9 @@
{
"$ref": "#/$defs/ColorSelectionAction"
},
{
"$ref": "#/$defs/SearchWebAction"
},
{
"type": "null"
}

View File

@ -1021,6 +1021,49 @@ namespace winrt::TerminalApp::implementation
args.Handled(true);
}
void TerminalPage::_HandleSearchForText(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (const auto termControl{ _GetActiveControl() })
{
if (termControl.HasSelection())
{
const auto selections{ termControl.SelectedText(true) };
// concatenate the selection into a single line
auto searchText = std::accumulate(selections.begin(), selections.end(), std::wstring());
// make it compact by replacing consecutive whitespaces with a single space
searchText = std::regex_replace(searchText, std::wregex(LR"(\s+)"), L" ");
std::wstring queryUrl;
if (args)
{
if (const auto& realArgs = args.ActionArgs().try_as<SearchForTextArgs>())
{
queryUrl = realArgs.QueryUrl().c_str();
}
}
// use global default if query URL is unspecified
if (queryUrl.empty())
{
queryUrl = _settings.GlobalSettings().SearchWebDefaultQueryUrl().c_str();
}
constexpr std::wstring_view queryToken{ L"%s" };
if (const auto pos{ queryUrl.find(queryToken) }; pos != std::wstring_view::npos)
{
queryUrl.replace(pos, queryToken.length(), Windows::Foundation::Uri::EscapeComponent(searchText));
}
winrt::Microsoft::Terminal::Control::OpenHyperlinkEventArgs shortcut{ queryUrl };
_OpenHyperlinkHandler(termControl, shortcut);
args.Handled(true);
}
}
}
void TerminalPage::_HandleGlobalSummon(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{

View File

@ -214,6 +214,9 @@
<data name="SplitPaneText" xml:space="preserve">
<value>Split Pane</value>
</data>
<data name="SearchWebText" xml:space="preserve">
<value>Web Search</value>
</data>
<data name="TabColorChoose" xml:space="preserve">
<value>Color...</value>
</data>

View File

@ -4581,7 +4581,7 @@ namespace winrt::TerminalApp::implementation
}
void TerminalPage::_PopulateContextMenu(const IInspectable& sender,
const bool /*withSelection*/)
const bool withSelection)
{
// withSelection can be used to add actions that only appear if there's
// selected text, like "search the web". In this initial draft, it's not
@ -4638,6 +4638,11 @@ namespace winrt::TerminalApp::implementation
makeItem(RS_(L"PaneClose"), L"\xE89F", ActionAndArgs{ ShortcutAction::ClosePane, nullptr });
}
if (withSelection)
{
makeItem(RS_(L"SearchWebText"), L"\xF6FA", ActionAndArgs{ ShortcutAction::SearchForText, nullptr });
}
makeItem(RS_(L"TabClose"), L"\xE711", ActionAndArgs{ ShortcutAction::CloseTab, CloseTabArgs{ _GetFocusedTabIndex().value() } });
}

View File

@ -145,6 +145,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
int ViewHeight() const;
int BufferHeight() const;
bool HasSelection() const;
Windows::Foundation::Collections::IVector<winrt::hstring> SelectedText(bool trimTrailingWhitespace) const;
bool BracketedPasteEnabled() const noexcept;
Windows::Foundation::Collections::IVector<Control::ScrollMark> ScrollMarks() const;
@ -188,9 +191,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool ShouldSendAlternateScroll(const unsigned int uiButton, const int32_t delta) const;
Core::Point CursorPosition() const;
bool HasSelection() const;
bool CopyOnSelect() const;
Windows::Foundation::Collections::IVector<winrt::hstring> SelectedText(bool trimTrailingWhitespace) const;
Control::SelectionData SelectionInfo() const;
void SetSelectionAnchor(const til::point position);
void SetEndSelectionPoint(const til::point position);

View File

@ -124,8 +124,6 @@ namespace Microsoft.Terminal.Control
void Search(String text, Boolean goForward, Boolean caseSensitive);
Microsoft.Terminal.Core.Color BackgroundColor { get; };
Boolean HasSelection { get; };
IVector<String> SelectedText(Boolean trimTrailingWhitespace);
SelectionData SelectionInfo { get; };
SelectionInteractionMode SelectionMode();

View File

@ -180,3 +180,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
WINRT_PROPERTY(bool, ClearMarkers, false);
};
}
namespace winrt::Microsoft::Terminal::Control::factory_implementation
{
BASIC_FACTORY(OpenHyperlinkEventArgs);
}

View File

@ -40,6 +40,7 @@ namespace Microsoft.Terminal.Control
runtimeclass OpenHyperlinkEventArgs
{
OpenHyperlinkEventArgs(String uri);
String Uri { get; };
}

View File

@ -45,6 +45,9 @@ namespace Microsoft.Terminal.Control
Int32 ViewHeight { get; };
Int32 BufferHeight { get; };
Boolean HasSelection { get; };
IVector<String> SelectedText(Boolean trimTrailingWhitespace);
Boolean BracketedPasteEnabled { get; };
Microsoft.Terminal.TerminalConnection.ConnectionState ConnectionState { get; };

View File

@ -3335,6 +3335,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return _core.Opacity();
}
bool TermControl::HasSelection() const
{
return _core.HasSelection();
}
Windows::Foundation::Collections::IVector<winrt::hstring> TermControl::SelectedText(bool trimTrailingWhitespace) const
{
return _core.SelectedText(trimTrailingWhitespace);
}
// Method Description:
// - Called when the core raises a FoundMatch event. That's done in response
// to us starting a search query with ControlCore::Search.

View File

@ -69,6 +69,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
int ViewHeight() const;
int BufferHeight() const;
bool HasSelection() const;
Windows::Foundation::Collections::IVector<winrt::hstring> SelectedText(bool trimTrailingWhitespace) const;
bool BracketedPasteEnabled() const noexcept;
double BackgroundOpacity() const;

View File

@ -72,6 +72,7 @@ static constexpr std::string_view IdentifyWindowKey{ "identifyWindow" };
static constexpr std::string_view IdentifyWindowsKey{ "identifyWindows" };
static constexpr std::string_view RenameWindowKey{ "renameWindow" };
static constexpr std::string_view OpenWindowRenamerKey{ "openWindowRenamer" };
static constexpr std::string_view SearchForTextKey{ "searchWeb" };
static constexpr std::string_view GlobalSummonKey{ "globalSummon" };
static constexpr std::string_view QuakeModeKey{ "quakeMode" };
static constexpr std::string_view FocusPaneKey{ "focusPane" };
@ -403,6 +404,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{ ShortcutAction::RenameWindow, RS_(L"ResetWindowNameCommandKey") },
{ ShortcutAction::OpenWindowRenamer, RS_(L"OpenWindowRenamerCommandKey") },
{ ShortcutAction::GlobalSummon, MustGenerate },
{ ShortcutAction::SearchForText, MustGenerate },
{ ShortcutAction::QuakeMode, RS_(L"QuakeModeCommandKey") },
{ ShortcutAction::FocusPane, MustGenerate },
{ ShortcutAction::OpenSystemMenu, RS_(L"OpenSystemMenuCommandKey") },

View File

@ -37,6 +37,7 @@
#include "PrevTabArgs.g.cpp"
#include "NextTabArgs.g.cpp"
#include "RenameWindowArgs.g.cpp"
#include "SearchForTextArgs.g.cpp"
#include "GlobalSummonArgs.g.cpp"
#include "FocusPaneArgs.g.cpp"
#include "ExportBufferArgs.g.cpp"
@ -765,6 +766,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
return RS_(L"ResetWindowNameCommandKey");
}
winrt::hstring SearchForTextArgs::GenerateName() const
{
return winrt::hstring{
fmt::format(std::wstring_view(RS_(L"SearchForTextCommandKey")),
Windows::Foundation::Uri(QueryUrl()).Domain().c_str())
};
}
winrt::hstring GlobalSummonArgs::GenerateName() const
{
// GH#10210 - Is this action literally the same thing as the `quakeMode`

View File

@ -39,6 +39,7 @@
#include "PrevTabArgs.g.h"
#include "NextTabArgs.g.h"
#include "RenameWindowArgs.g.h"
#include "SearchForTextArgs.g.h"
#include "GlobalSummonArgs.g.h"
#include "FocusPaneArgs.g.h"
#include "ExportBufferArgs.g.h"
@ -227,6 +228,10 @@ private: \
#define RENAME_WINDOW_ARGS(X) \
X(winrt::hstring, Name, "name", false, L"")
////////////////////////////////////////////////////////////////////////////////
#define SEARCH_FOR_TEXT_ARGS(X) \
X(winrt::hstring, QueryUrl, "queryUrl", false, L"")
////////////////////////////////////////////////////////////////////////////////
#define GLOBAL_SUMMON_ARGS(X) \
X(winrt::hstring, Name, "name", false, L"") \
@ -711,6 +716,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
ACTION_ARGS_STRUCT(RenameWindowArgs, RENAME_WINDOW_ARGS);
ACTION_ARGS_STRUCT(SearchForTextArgs, SEARCH_FOR_TEXT_ARGS);
struct GlobalSummonArgs : public GlobalSummonArgsT<GlobalSummonArgs>
{
ACTION_ARG_BODY(GlobalSummonArgs, GLOBAL_SUMMON_ARGS)

View File

@ -349,6 +349,11 @@ namespace Microsoft.Terminal.Settings.Model
String Name { get; };
};
[default_interface] runtimeclass SearchForTextArgs : IActionArgs
{
String QueryUrl { get; };
};
[default_interface] runtimeclass GlobalSummonArgs : IActionArgs
{
String Name { get; };

View File

@ -85,6 +85,7 @@
ON_ALL_ACTIONS(IdentifyWindows) \
ON_ALL_ACTIONS(RenameWindow) \
ON_ALL_ACTIONS(OpenWindowRenamer) \
ON_ALL_ACTIONS(SearchForText) \
ON_ALL_ACTIONS(GlobalSummon) \
ON_ALL_ACTIONS(QuakeMode) \
ON_ALL_ACTIONS(FocusPane) \
@ -115,6 +116,7 @@
ON_ALL_ACTIONS_WITH_ARGS(CopyText) \
ON_ALL_ACTIONS_WITH_ARGS(ExecuteCommandline) \
ON_ALL_ACTIONS_WITH_ARGS(FindMatch) \
ON_ALL_ACTIONS_WITH_ARGS(SearchForText) \
ON_ALL_ACTIONS_WITH_ARGS(GlobalSummon) \
ON_ALL_ACTIONS_WITH_ARGS(MoveFocus) \
ON_ALL_ACTIONS_WITH_ARGS(MovePane) \

View File

@ -100,6 +100,7 @@ namespace Microsoft.Terminal.Settings.Model
INHERITABLE_SETTING(Boolean, EnableColorSelection);
INHERITABLE_SETTING(Boolean, IsolatedMode);
INHERITABLE_SETTING(Boolean, AllowHeadless);
INHERITABLE_SETTING(String, SearchWebDefaultQueryUrl);
Windows.Foundation.Collections.IMapView<String, ColorScheme> ColorSchemes();
void AddColorScheme(ColorScheme scheme);

View File

@ -65,7 +65,8 @@ Author(s):
X(bool, EnableColorSelection, "experimental.enableColorSelection", 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)
X(bool, IsolatedMode, "compatibility.isolatedMode", false) \
X(hstring, SearchWebDefaultQueryUrl, "searchWebDefaultQueryUrl", L"https://www.bing.com/search?q=%22%s%22")
#define MTSM_PROFILE_SETTINGS(X) \
X(int32_t, HistorySize, "historySize", DEFAULT_HISTORY_SIZE) \

View File

@ -699,4 +699,12 @@
<data name="SelectCommandPreviousCommandKey" xml:space="preserve">
<value>Select previous command</value>
</data>
<data name="SearchForTextCommandKey" xml:space="preserve">
<value>Search {0}</value>
<comment>{0} will be replaced with a user-specified URL to a web page</comment>
</data>
<data name="SearchWebCommandKey" xml:space="preserve">
<value>Search the web for selected text</value>
<comment>This will open a web browser to search for some user-selected text</comment>
</data>
</root>

View File

@ -466,6 +466,9 @@
{ "command": "expandSelectionToWord" },
{ "command": "showContextMenu", "keys": "menu" },
// Web Search
{ "command": { "action": "searchWeb" }, "name": { "key": "SearchWebCommandKey" } },
// Scrollback
{ "command": "scrollDown", "keys": "ctrl+shift+down" },
{ "command": "scrollDownPage", "keys": "ctrl+shift+pgdn" },