Add an exportBuffer action (#12097)

This adds an action for the context menu entry we added in #11062. That PR added support for exporting the buffer, exclusively through the tab item's context menu. This adds an action that can additionally be bound, which also can export the buffer to a file. This action accepts a `path` param. If empty/ommitted, then the Terminal will prompt for the file to export the buffer to. 

* Does a part of #9700
* Spec in #11090, but I doubt this is contentious
* [x] This will satisfy #12052
* [x] I work here
* [x] docs added: https://github.com/MicrosoftDocs/terminal/pull/479
This commit is contained in:
Mike Griese 2022-01-12 10:58:38 -06:00 committed by GitHub
parent bc97af701e
commit 862d8ed427
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 160 additions and 21 deletions

View File

@ -281,6 +281,7 @@
"ShortcutActionName": {
"enum": [
"adjustFontSize",
"clearBuffer",
"closeOtherTabs",
"closePane",
"closeTab",
@ -289,6 +290,7 @@
"commandPalette",
"copy",
"duplicateTab",
"exportBuffer",
"find",
"findMatch",
"focusPane",
@ -434,6 +436,14 @@
],
"type": "string"
},
"ClearBufferType": {
"enum": [
"all",
"screen",
"scrollback"
],
"type": "string"
},
"NewTerminalArgs": {
"properties": {
"commandline": {
@ -1266,6 +1276,48 @@
}
]
},
"ClearBufferAction": {
"description": "Arguments corresponding to a clearBuffer Action",
"allOf": [
{
"$ref": "#/$defs/ShortcutAction"
},
{
"properties": {
"action": {
"type": "string",
"const": "clearBuffer"
},
"clear": {
"$ref": "#/$defs/ClearBufferType",
"default": "all",
"description": "What to clear. Accepts one of `screen`, `scrollback` or `all` (for both)."
}
}
}
]
},
"ExportBufferAction": {
"description": "Arguments corresponding to a exportBuffer Action",
"allOf": [
{
"$ref": "#/$defs/ShortcutAction"
},
{
"properties": {
"action": {
"type": "string",
"const": "exportBuffer"
},
"path": {
"type": "string",
"default": "",
"description": "The path to export the text buffer to. If left blank, the Terminal will open a file picker to choose the path."
}
}
}
]
},
"GlobalSummonAction": {
"description": "This is a special action that works globally in the OS, rather than only in the context of the terminal window. When pressed, this action will summon the terminal window.",
"allOf": [
@ -1429,6 +1481,12 @@
{
"$ref": "#/$defs/FocusPaneAction"
},
{
"$ref": "#/$defs/ExportBufferAction"
},
{
"$ref": "#/$defs/ClearBufferAction"
},
{
"$ref": "#/$defs/GlobalSummonAction"
},

View File

@ -906,6 +906,27 @@ namespace winrt::TerminalApp::implementation
args.Handled(true);
}
void TerminalPage::_HandleExportBuffer(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (const auto activeTab{ _GetFocusedTabImpl() })
{
if (args)
{
if (const auto& realArgs = args.ActionArgs().try_as<ExportBufferArgs>())
{
_ExportTab(*activeTab, realArgs.Path());
args.Handled(true);
return;
}
}
// If we didn't have args, or the args weren't ExportBufferArgs (somehow)
_ExportTab(*activeTab, L"");
args.Handled(true);
}
}
void TerminalPage::_HandleClearBuffer(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{

View File

@ -194,7 +194,9 @@ namespace winrt::TerminalApp::implementation
if (page && tab)
{
page->_ExportTab(*tab);
// Passing null args to the ExportBuffer handler will default it
// to prompting for the path
page->_HandleExportBuffer(nullptr, nullptr);
}
});
@ -359,7 +361,7 @@ namespace winrt::TerminalApp::implementation
// - Exports the content of the Terminal Buffer inside the tab
// Arguments:
// - tab: tab to export
winrt::fire_and_forget TerminalPage::_ExportTab(const TerminalTab& tab)
winrt::fire_and_forget TerminalPage::_ExportTab(const TerminalTab& tab, winrt::hstring filepath)
{
// This will be used to set up the file picker "filter", to select .txt
// files by default.
@ -376,25 +378,38 @@ namespace winrt::TerminalApp::implementation
{
if (const auto control{ tab.GetActiveTerminalControl() })
{
// GH#11356 - we can't use the UWP apis for writing the file,
// because they don't work elevated (shocker) So just use the
// shell32 file picker manually.
auto path = co_await SaveFilePicker(*_hostingHwnd, [control](auto&& dialog) {
THROW_IF_FAILED(dialog->SetClientGuid(clientGuidExportFile));
try
{
// Default to the Downloads folder
auto folderShellItem{ winrt::capture<IShellItem>(&SHGetKnownFolderItem, FOLDERID_Downloads, KF_FLAG_DEFAULT, nullptr) };
dialog->SetDefaultFolder(folderShellItem.get());
}
CATCH_LOG(); // non-fatal
THROW_IF_FAILED(dialog->SetFileTypes(ARRAYSIZE(supportedFileTypes), supportedFileTypes));
THROW_IF_FAILED(dialog->SetFileTypeIndex(1)); // the array is 1-indexed
THROW_IF_FAILED(dialog->SetDefaultExtension(L"txt"));
auto path = filepath;
// Default to using the tab title as the file name
THROW_IF_FAILED(dialog->SetFileName((control.Title() + L".txt").c_str()));
});
if (path.empty())
{
// GH#11356 - we can't use the UWP apis for writing the file,
// because they don't work elevated (shocker) So just use the
// shell32 file picker manually.
path = co_await SaveFilePicker(*_hostingHwnd, [control](auto&& dialog) {
THROW_IF_FAILED(dialog->SetClientGuid(clientGuidExportFile));
try
{
// Default to the Downloads folder
auto folderShellItem{ winrt::capture<IShellItem>(&SHGetKnownFolderItem, FOLDERID_Downloads, KF_FLAG_DEFAULT, nullptr) };
dialog->SetDefaultFolder(folderShellItem.get());
}
CATCH_LOG(); // non-fatal
THROW_IF_FAILED(dialog->SetFileTypes(ARRAYSIZE(supportedFileTypes), supportedFileTypes));
THROW_IF_FAILED(dialog->SetFileTypeIndex(1)); // the array is 1-indexed
THROW_IF_FAILED(dialog->SetDefaultExtension(L"txt"));
// Default to using the tab title as the file name
THROW_IF_FAILED(dialog->SetFileName((control.Title() + L".txt").c_str()));
});
}
else
{
// The file picker isn't going to give us paths with
// environment variables, but the user might have set one in
// the settings. Expand those here.
path = { wil::ExpandEnvironmentStringsW<std::wstring>(path.c_str()) };
}
if (!path.empty())
{

View File

@ -256,7 +256,7 @@ namespace winrt::TerminalApp::implementation
void _DuplicateTab(const TerminalTab& tab);
void _SplitTab(TerminalTab& tab);
winrt::fire_and_forget _ExportTab(const TerminalTab& tab);
winrt::fire_and_forget _ExportTab(const TerminalTab& tab, winrt::hstring filepath);
winrt::Windows::Foundation::IAsyncAction _HandleCloseTabRequested(winrt::TerminalApp::TabBase tab);
void _CloseTabAtIndex(uint32_t index);

View File

@ -69,6 +69,7 @@ static constexpr std::string_view GlobalSummonKey{ "globalSummon" };
static constexpr std::string_view QuakeModeKey{ "quakeMode" };
static constexpr std::string_view FocusPaneKey{ "focusPane" };
static constexpr std::string_view OpenSystemMenuKey{ "openSystemMenu" };
static constexpr std::string_view ExportBufferKey{ "exportBuffer" };
static constexpr std::string_view ClearBufferKey{ "clearBuffer" };
static constexpr std::string_view MultipleActionsKey{ "multipleActions" };
static constexpr std::string_view QuitKey{ "quit" };
@ -378,6 +379,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{ ShortcutAction::QuakeMode, RS_(L"QuakeModeCommandKey") },
{ ShortcutAction::FocusPane, L"" }, // Intentionally omitted, must be generated by GenerateName
{ ShortcutAction::OpenSystemMenu, RS_(L"OpenSystemMenuCommandKey") },
{ ShortcutAction::ExportBuffer, L"" }, // Intentionally omitted, must be generated by GenerateName
{ ShortcutAction::ClearBuffer, L"" }, // Intentionally omitted, must be generated by GenerateName
{ ShortcutAction::MultipleActions, L"" }, // Intentionally omitted, must be generated by GenerateName
{ ShortcutAction::Quit, RS_(L"QuitCommandKey") },

View File

@ -37,6 +37,7 @@
#include "RenameWindowArgs.g.cpp"
#include "GlobalSummonArgs.g.cpp"
#include "FocusPaneArgs.g.cpp"
#include "ExportBufferArgs.g.cpp"
#include "ClearBufferArgs.g.cpp"
#include "MultipleActionsArgs.g.cpp"
@ -727,6 +728,24 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
Id())
};
}
winrt::hstring ExportBufferArgs::GenerateName() const
{
if (!Path().empty())
{
// "Export text to {path}"
return winrt::hstring{
fmt::format(std::wstring_view(RS_(L"ExportBufferToPathCommandKey")),
Path())
};
}
else
{
// "Export text"
return RS_(L"ExportBufferCommandKey");
}
}
winrt::hstring ClearBufferArgs::GenerateName() const
{
// "Clear Buffer"

View File

@ -39,6 +39,7 @@
#include "RenameWindowArgs.g.h"
#include "GlobalSummonArgs.g.h"
#include "FocusPaneArgs.g.h"
#include "ExportBufferArgs.g.h"
#include "ClearBufferArgs.g.h"
#include "MultipleActionsArgs.g.h"
@ -210,6 +211,10 @@ private:
#define FOCUS_PANE_ARGS(X) \
X(uint32_t, Id, "id", false, 0u)
////////////////////////////////////////////////////////////////////////////////
#define EXPORT_BUFFER_ARGS(X) \
X(winrt::hstring, Path, "path", false, L"")
////////////////////////////////////////////////////////////////////////////////
#define CLEAR_BUFFER_ARGS(X) \
X(winrt::Microsoft::Terminal::Control::ClearBufferType, Clear, "clear", false, winrt::Microsoft::Terminal::Control::ClearBufferType::All)
@ -631,6 +636,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
ACTION_ARGS_STRUCT(FocusPaneArgs, FOCUS_PANE_ARGS);
ACTION_ARGS_STRUCT(ExportBufferArgs, EXPORT_BUFFER_ARGS);
ACTION_ARGS_STRUCT(ClearBufferArgs, CLEAR_BUFFER_ARGS);
struct MultipleActionsArgs : public MultipleActionsArgsT<MultipleActionsArgs>
@ -713,6 +720,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
BASIC_FACTORY(FocusPaneArgs);
BASIC_FACTORY(PrevTabArgs);
BASIC_FACTORY(NextTabArgs);
BASIC_FACTORY(ExportBufferArgs);
BASIC_FACTORY(ClearBufferArgs);
BASIC_FACTORY(MultipleActionsArgs);
}

View File

@ -342,6 +342,12 @@ namespace Microsoft.Terminal.Settings.Model
UInt32 Id { get; };
};
[default_interface] runtimeclass ExportBufferArgs : IActionArgs
{
ExportBufferArgs(String path);
String Path { get; };
};
[default_interface] runtimeclass ClearBufferArgs : IActionArgs
{
ClearBufferArgs(Microsoft.Terminal.Control.ClearBufferType clear);

View File

@ -83,6 +83,7 @@
ON_ALL_ACTIONS(QuakeMode) \
ON_ALL_ACTIONS(FocusPane) \
ON_ALL_ACTIONS(OpenSystemMenu) \
ON_ALL_ACTIONS(ExportBuffer) \
ON_ALL_ACTIONS(ClearBuffer) \
ON_ALL_ACTIONS(MultipleActions) \
ON_ALL_ACTIONS(Quit) \
@ -121,5 +122,6 @@
ON_ALL_ACTIONS_WITH_ARGS(SwitchToTab) \
ON_ALL_ACTIONS_WITH_ARGS(ToggleCommandPalette) \
ON_ALL_ACTIONS_WITH_ARGS(FocusPane) \
ON_ALL_ACTIONS_WITH_ARGS(ExportBuffer) \
ON_ALL_ACTIONS_WITH_ARGS(ClearBuffer) \
ON_ALL_ACTIONS_WITH_ARGS(MultipleActions)

View File

@ -473,6 +473,13 @@
<value>Focus pane {0}</value>
<comment>{0} will be replaced with a user-specified number</comment>
</data>
<data name="ExportBufferToPathCommandKey" xml:space="preserve">
<value>Export text to {0}</value>
<comment>{0} will be replaced with a user-specified file path</comment>
</data>
<data name="ExportBufferCommandKey" xml:space="preserve">
<value>Export text</value>
</data>
<data name="ClearAllCommandKey" xml:space="preserve">
<value>Clear buffer</value>
<comment>A command to clear the entirety of the Terminal output buffer</comment>

View File

@ -393,6 +393,7 @@
{ "command": "scrollToTop", "keys": "ctrl+shift+home" },
{ "command": "scrollToBottom", "keys": "ctrl+shift+end" },
{ "command": { "action": "clearBuffer", "clear": "all" } },
{ "command": "exportBuffer" },
// Visual Adjustments
{ "command": { "action": "adjustFontSize", "delta": 1 }, "keys": "ctrl+plus" },