mirror of
https://github.com/microsoft/terminal.git
synced 2025-12-15 16:40:12 -06:00
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:
parent
bc97af701e
commit
862d8ed427
@ -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"
|
||||
},
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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())
|
||||
{
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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") },
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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" },
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user