Allow exporting terminal buffer into file via tab context menu (#11062)
## Summary of the Pull Request **Naive implementation** of exporting the text buffer of the current pane into a text file triggered from the tab context menu. **Disclaimer: this is not an export of the command history,** but rather just a text buffer dumped into a file when asked explicitly. ## References Should provide partial solution for #642. ## Detailed Description of the Pull Request / Additional comments The logic is following: * Open a file save picker * The location is Downloads folder (should be always accessible) * The suggest name of the file equals to the pane's title * The allowed file formats list contains .txt only * If no file selected stop * Lock terminal * Read all lines till the cursor * Format each line by removing trailing white-spaces and adding CRLF if not wrapped * Asynchronously write to selected file * Show confirmation As the action is relatively fast didn't add a progress bar or any other UX. As the buffer is relatively small, holding it entirely in the memory rather than writing line by line to disk.
This commit is contained in:
parent
8d81497eb7
commit
c089ae0c57
|
@ -685,4 +685,16 @@
|
|||
<data name="DropPathTabSplit.Text" xml:space="preserve">
|
||||
<value>Split the window and start in given directory</value>
|
||||
</data>
|
||||
<data name="ExportTabText" xml:space="preserve">
|
||||
<value>Export Text</value>
|
||||
</data>
|
||||
<data name="ExportFailure" xml:space="preserve">
|
||||
<value>Failed to export terminal content</value>
|
||||
</data>
|
||||
<data name="ExportSuccess" xml:space="preserve">
|
||||
<value>Successfully exported terminal content</value>
|
||||
</data>
|
||||
<data name="PlainText" xml:space="preserve">
|
||||
<value>Plain Text</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
|
@ -28,6 +28,9 @@ using namespace winrt::Windows::UI::Core;
|
|||
using namespace winrt::Windows::System;
|
||||
using namespace winrt::Windows::ApplicationModel::DataTransfer;
|
||||
using namespace winrt::Windows::UI::Text;
|
||||
using namespace winrt::Windows::Storage;
|
||||
using namespace winrt::Windows::Storage::Pickers;
|
||||
using namespace winrt::Windows::Storage::Provider;
|
||||
using namespace winrt::Microsoft::Terminal;
|
||||
using namespace winrt::Microsoft::Terminal::Control;
|
||||
using namespace winrt::Microsoft::Terminal::TerminalConnection;
|
||||
|
@ -171,6 +174,16 @@ namespace winrt::TerminalApp::implementation
|
|||
}
|
||||
});
|
||||
|
||||
newTabImpl->ExportTabRequested([weakTab, weakThis{ get_weak() }]() {
|
||||
auto page{ weakThis.get() };
|
||||
auto tab{ weakTab.get() };
|
||||
|
||||
if (page && tab)
|
||||
{
|
||||
page->_ExportTab(*tab);
|
||||
}
|
||||
});
|
||||
|
||||
auto tabViewItem = newTabImpl->TabViewItem();
|
||||
_tabView.TabItems().Append(tabViewItem);
|
||||
|
||||
|
@ -391,6 +404,45 @@ namespace winrt::TerminalApp::implementation
|
|||
CATCH_LOG();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Exports the content of the Terminal Buffer inside the tab
|
||||
// Arguments:
|
||||
// - tab: tab to export
|
||||
winrt::fire_and_forget TerminalPage::_ExportTab(const TerminalTab& tab)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (const auto control{ tab.GetActiveTerminalControl() })
|
||||
{
|
||||
const FileSavePicker savePicker;
|
||||
savePicker.as<IInitializeWithWindow>()->Initialize(*_hostingHwnd);
|
||||
savePicker.SuggestedStartLocation(PickerLocationId::Downloads);
|
||||
const auto fileChoices = single_threaded_vector<hstring>({ L".txt" });
|
||||
savePicker.FileTypeChoices().Insert(RS_(L"PlainText"), fileChoices);
|
||||
savePicker.SuggestedFileName(control.Title());
|
||||
|
||||
const StorageFile file = co_await savePicker.PickSaveFileAsync();
|
||||
if (file != nullptr)
|
||||
{
|
||||
const auto buffer = control.ReadEntireBuffer();
|
||||
CachedFileManager::DeferUpdates(file);
|
||||
co_await FileIO::WriteTextAsync(file, buffer);
|
||||
const auto status = co_await CachedFileManager::CompleteUpdatesAsync(file);
|
||||
switch (status)
|
||||
{
|
||||
case FileUpdateStatus::Complete:
|
||||
case FileUpdateStatus::CompleteAndRenamed:
|
||||
_ShowControlNoticeDialog(RS_(L"NoticeInfo"), RS_(L"ExportSuccess"));
|
||||
break;
|
||||
default:
|
||||
_ShowControlNoticeDialog(RS_(L"NoticeError"), RS_(L"ExportFailure"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Removes the tab (both TerminalControl and XAML) after prompting for approval
|
||||
// Arguments:
|
||||
|
|
|
@ -220,6 +220,7 @@ namespace winrt::TerminalApp::implementation
|
|||
void _DuplicateTab(const TerminalTab& tab);
|
||||
|
||||
void _SplitTab(TerminalTab& tab);
|
||||
winrt::fire_and_forget _ExportTab(const TerminalTab& tab);
|
||||
|
||||
winrt::Windows::Foundation::IAsyncAction _HandleCloseTabRequested(winrt::TerminalApp::TabBase tab);
|
||||
void _CloseTabAtIndex(uint32_t index);
|
||||
|
|
|
@ -1211,6 +1211,23 @@ namespace winrt::TerminalApp::implementation
|
|||
splitTabMenuItem.Icon(splitTabSymbol);
|
||||
}
|
||||
|
||||
Controls::MenuFlyoutItem exportTabMenuItem;
|
||||
{
|
||||
// "Split Tab"
|
||||
Controls::FontIcon exportTabSymbol;
|
||||
exportTabSymbol.FontFamily(Media::FontFamily{ L"Segoe MDL2 Assets" });
|
||||
exportTabSymbol.Glyph(L"\xE74E"); // Save
|
||||
|
||||
exportTabMenuItem.Click([weakThis](auto&&, auto&&) {
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
tab->_ExportTabRequestedHandlers();
|
||||
}
|
||||
});
|
||||
exportTabMenuItem.Text(RS_(L"ExportTabText"));
|
||||
exportTabMenuItem.Icon(exportTabSymbol);
|
||||
}
|
||||
|
||||
// Build the menu
|
||||
Controls::MenuFlyout contextMenuFlyout;
|
||||
Controls::MenuFlyoutSeparator menuSeparator;
|
||||
|
@ -1218,6 +1235,7 @@ namespace winrt::TerminalApp::implementation
|
|||
contextMenuFlyout.Items().Append(renameTabMenuItem);
|
||||
contextMenuFlyout.Items().Append(duplicateTabMenuItem);
|
||||
contextMenuFlyout.Items().Append(splitTabMenuItem);
|
||||
contextMenuFlyout.Items().Append(exportTabMenuItem);
|
||||
contextMenuFlyout.Items().Append(menuSeparator);
|
||||
|
||||
// GH#5750 - When the context menu is dismissed with ESC, toss the focus
|
||||
|
@ -1592,4 +1610,5 @@ namespace winrt::TerminalApp::implementation
|
|||
DEFINE_EVENT(TerminalTab, TabRaiseVisualBell, _TabRaiseVisualBellHandlers, winrt::delegate<>);
|
||||
DEFINE_EVENT(TerminalTab, DuplicateRequested, _DuplicateRequestedHandlers, winrt::delegate<>);
|
||||
DEFINE_EVENT(TerminalTab, SplitTabRequested, _SplitTabRequestedHandlers, winrt::delegate<>);
|
||||
DEFINE_EVENT(TerminalTab, ExportTabRequested, _ExportTabRequestedHandlers, winrt::delegate<>);
|
||||
}
|
||||
|
|
|
@ -103,6 +103,7 @@ namespace winrt::TerminalApp::implementation
|
|||
DECLARE_EVENT(TabRaiseVisualBell, _TabRaiseVisualBellHandlers, winrt::delegate<>);
|
||||
DECLARE_EVENT(DuplicateRequested, _DuplicateRequestedHandlers, winrt::delegate<>);
|
||||
DECLARE_EVENT(SplitTabRequested, _SplitTabRequestedHandlers, winrt::delegate<>);
|
||||
DECLARE_EVENT(ExportTabRequested, _ExportTabRequestedHandlers, winrt::delegate<>);
|
||||
TYPED_EVENT(TaskbarProgressChanged, IInspectable, IInspectable);
|
||||
|
||||
private:
|
||||
|
|
|
@ -56,6 +56,9 @@
|
|||
#include <winrt/Microsoft.Terminal.TerminalConnection.h>
|
||||
#include <winrt/Microsoft.Terminal.Settings.Editor.h>
|
||||
#include <winrt/Microsoft.Terminal.Settings.Model.h>
|
||||
#include <winrt/Windows.Storage.h>
|
||||
#include <winrt/Windows.Storage.Provider.h>
|
||||
#include <winrt/Windows.Storage.Pickers.h>
|
||||
|
||||
#include <windows.ui.xaml.media.dxinterop.h>
|
||||
|
||||
|
|
|
@ -1499,4 +1499,32 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
_updatePatternLocations->Run();
|
||||
}
|
||||
|
||||
hstring ControlCore::ReadEntireBuffer() const
|
||||
{
|
||||
auto terminalLock = _terminal->LockForWriting();
|
||||
|
||||
const auto& textBuffer = _terminal->GetTextBuffer();
|
||||
|
||||
std::wstringstream ss;
|
||||
const auto lastRow = textBuffer.GetLastNonSpaceCharacter().Y;
|
||||
for (auto rowIndex = 0; rowIndex <= lastRow; rowIndex++)
|
||||
{
|
||||
const auto& row = textBuffer.GetRowByOffset(rowIndex);
|
||||
auto rowText = row.GetText();
|
||||
const auto strEnd = rowText.find_last_not_of(UNICODE_SPACE);
|
||||
if (strEnd != std::string::npos)
|
||||
{
|
||||
rowText.erase(strEnd + 1);
|
||||
ss << rowText;
|
||||
}
|
||||
|
||||
if (!row.WasWrapForced())
|
||||
{
|
||||
ss << UNICODE_CARRIAGERETURN << UNICODE_LINEFEED;
|
||||
}
|
||||
}
|
||||
|
||||
return hstring(ss.str());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -144,6 +144,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
bool IsInReadOnlyMode() const;
|
||||
void ToggleReadOnlyMode();
|
||||
|
||||
hstring ReadEntireBuffer() const;
|
||||
|
||||
// -------------------------------- WinRT Events ---------------------------------
|
||||
// clang-format off
|
||||
WINRT_CALLBACK(FontSizeChanged, Control::FontSizeChangedEventArgs);
|
||||
|
|
|
@ -81,6 +81,8 @@ namespace Microsoft.Terminal.Control
|
|||
Boolean CursorOn;
|
||||
void EnablePainting();
|
||||
|
||||
String ReadEntireBuffer();
|
||||
|
||||
event FontSizeChangedEventArgs FontSizeChanged;
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, CopyToClipboardEventArgs> CopyToClipboard;
|
||||
|
|
|
@ -2586,4 +2586,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
{
|
||||
_playWarningBell->Run();
|
||||
}
|
||||
|
||||
hstring TermControl::ReadEntireBuffer() const
|
||||
{
|
||||
return _core.ReadEntireBuffer();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,6 +107,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
static unsigned int GetPointerUpdateKind(const winrt::Windows::UI::Input::PointerPoint point);
|
||||
static Windows::UI::Xaml::Thickness ParseThicknessFromPadding(const hstring padding);
|
||||
|
||||
hstring ReadEntireBuffer() const;
|
||||
|
||||
// -------------------------------- WinRT Events ---------------------------------
|
||||
// clang-format off
|
||||
WINRT_CALLBACK(FontSizeChanged, Control::FontSizeChangedEventArgs);
|
||||
|
|
|
@ -67,5 +67,7 @@ namespace Microsoft.Terminal.Control
|
|||
|
||||
Boolean ReadOnly { get; };
|
||||
void ToggleReadOnly();
|
||||
|
||||
String ReadEntireBuffer();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue