Repository: microsoft/terminal (language: cpp)

Issue:
Clear Buffer is broken in Windows Terminal Preview (1.22.2362.0)
### Windows Terminal version

1.22.2362.0

### Windows build number

10.0.22631.4037

### Steps to reproduce

* Settings -> Actions -> Add New -> Add "Ctrl+l" (lowercase L) for "Clear Buffer". Press Save.
* Open tab with "Command Prompt".
* Execute "cd C:\Windows". Execute "dir" several times to fill the screen.
* Press Ctrl+l (lowercase L).

### Expected Behavior

Clear Buffer works fine in Windows Terminal v.1.20.11781.0.

### Actual Behavior

Clear Buffer does not work in Windows Terminal Preview v.1.22.2362.0.

The same problem can be reproduced with "Ubuntu 24.04 LTS" tab (WSL2).

I've just noticed that pressing Ctrl+Shift+L works differently from Ctrl+L (but still broken). And I didn't assign Ctrl+Shift+L to anything.


Discussion / hints:
@lhecker Didn't we fix this during the original #17510? I could have swore that I filed that, but I can't find it now
@valiko-ua it's not the key binding. In `1.22.2362` the `Clear buffer` doesn't work. Fill the buffer with data and invoke the command  via `ctrl+shift+p`. It won't clear the buffer!
I think the problem is explained by the comment here:

https://github.com/microsoft/terminal/blob/544452dad41d05b865f1fa1d665397befaa9ec2b/src/cascadia/TerminalControl/ControlCore.cpp#L2232-L2238

In the past when you asked conpty to clear its buffer, the vtengine would have pushed back an update that also cleared the buffer in the connected terminal, but that's not happening anymore. On the conhost side, the `ClearBuffer` signal is handled here:

https://github.com/microsoft/terminal/blob/544452dad41d05b865f1fa1d665397befaa9ec2b/src/host/PtySignalInputThread.cpp#L185-L201

Which just calls the `SCREEN_INFORMATION` class here:

https://github.com/microsoft/terminal/blob/544452dad41d05b865f1fa1d665397befaa9ec2b/src/host/screenInfo.cpp#L2142-L2162

That's just going to clear the conhost buffer. As far as I can see, there's nothing in that code that will emit sequences to sync it up with the terminal buffer.

Fix patch:
diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp
index cd4f5f58b81..5c96b5c805c 100644
--- a/src/cascadia/TerminalControl/ControlCore.cpp
+++ b/src/cascadia/TerminalControl/ControlCore.cpp
@@ -2221,19 +2221,32 @@ namespace winrt::Microsoft::Terminal::Control::implementation
     // - <none>
     void ControlCore::ClearBuffer(Control::ClearBufferType clearType)
     {
-        if (clearType == Control::ClearBufferType::Scrollback || clearType == Control::ClearBufferType::All)
+        std::wstring_view command;
+        switch (clearType)
+        {
+        case ClearBufferType::Screen:
+            command = L"\x1b[H\x1b[2J";
+            break;
+        case ClearBufferType::Scrollback:
+            command = L"\x1b[3J";
+            break;
+        case ClearBufferType::All:
+            command = L"\x1b[H\x1b[2J\x1b[3J";
+            break;
+        }
+
         {
             const auto lock = _terminal->LockForWriting();
-            _terminal->EraseScrollback();
+            _terminal->Write(command);
         }
 
         if (clearType == Control::ClearBufferType::Screen || clearType == Control::ClearBufferType::All)
         {
-            // Send a signal to conpty to clear the buffer.
             if (auto conpty{ _connection.try_as<TerminalConnection::ConptyConnection>() })
             {
-                // ConPTY will emit sequences to sync up our buffer with its new
-                // contents.
+                // Since the clearing of ConPTY occurs asynchronously, this call can result weird issues,
+                // where a console application still sees contents that we've already deleted, etc.
+                // The correct way would be for ConPTY to emit the appropriate CSI n J sequences.
                 conpty.ClearBuffer();
             }
         }
diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp
index c5876e1497c..4b855b02f6c 100644
--- a/src/cascadia/TerminalCore/Terminal.cpp
+++ b/src/cascadia/TerminalCore/Terminal.cpp
@@ -209,12 +209,6 @@ void Terminal::SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle)
     engine.Dispatch().SetCursorStyle(cursorStyle);
 }
 
-void Terminal::EraseScrollback()
-{
-    auto& engine = reinterpret_cast<OutputStateMachineEngine&>(_stateMachine->Engine());
-    engine.Dispatch().EraseInDisplay(DispatchTypes::EraseType::Scrollback);
-}
-
 bool Terminal::IsXtermBracketedPasteModeEnabled() const noexcept
 {
     return _systemMode.test(Mode::BracketedPaste);
diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp
index e9d3f1abd0a..ae7c2dbe168 100644
--- a/src/cascadia/TerminalCore/Terminal.hpp
+++ b/src/cascadia/TerminalCore/Terminal.hpp
@@ -94,7 +94,6 @@ class Microsoft::Terminal::Core::Terminal final :
     void UpdateAppearance(const winrt::Microsoft::Terminal::Core::ICoreAppearance& appearance);
     void SetFontInfo(const FontInfo& fontInfo);
     void SetCursorStyle(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::CursorStyle cursorStyle);
-    void EraseScrollback();
     bool IsXtermBracketedPasteModeEnabled() const noexcept;
     std::wstring_view GetWorkingDirectory() noexcept;
 
diff --git a/src/cascadia/UnitTests_Control/ControlCoreTests.cpp b/src/cascadia/UnitTests_Control/ControlCoreTests.cpp
index 4620afae09a..e018e1fd2ad 100644
--- a/src/cascadia/UnitTests_Control/ControlCoreTests.cpp
+++ b/src/cascadia/UnitTests_Control/ControlCoreTests.cpp
@@ -304,9 +304,9 @@ namespace ControlUnitTests
 
         Log::Comment(L"Check the buffer after the clear");
         VERIFY_ARE_EQUAL(20, core->_terminal->GetViewport().Height());
-        VERIFY_ARE_EQUAL(21, core->ScrollOffset());
+        VERIFY_ARE_EQUAL(41, core->ScrollOffset());
         VERIFY_ARE_EQUAL(20, core->ViewHeight());
-        VERIFY_ARE_EQUAL(41, core->BufferHeight());
+        VERIFY_ARE_EQUAL(61, core->BufferHeight());
 
         // In this test, we can't actually check if we cleared the buffer
         // contents. ConPTY will handle the actual clearing of the buffer
diff --git a/src/host/PtySignalInputThread.cpp b/src/host/PtySignalInputThread.cpp
index 45860f9c1a0..fa3893b4dd0 100644
--- a/src/host/PtySignalInputThread.cpp
+++ b/src/host/PtySignalInputThread.cpp
@@ -7,6 +7,7 @@
 
 #include "output.h"
 #include "handle.h"
+#include "_stream.h"
 #include "../interactivity/inc/ServiceLocator.hpp"
 
 using namespace Microsoft::Console;
@@ -197,7 +198,9 @@ void PtySignalInputThread::_DoClearBuffer() const
     }
 
     auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
-    THROW_IF_FAILED(gci.GetActiveOutputBuffer().ClearBuffer());
+    auto& screenInfo = gci.GetActiveOutputBuffer();
+    auto& stateMachine = screenInfo.GetStateMachine();
+    stateMachine.ProcessString(L"\x1b[H\x1b[2J");
 }
 
 void PtySignalInputThread::_DoShowHide(const ShowHideData& data)
diff --git a/src/host/screenInfo.cpp b/src/host/screenInfo.cpp
index 8d5e9217246..3e1ae49d4b3 100644
--- a/src/host/screenInfo.cpp
+++ b/src/host/screenInfo.cpp
@@ -2129,38 +2129,6 @@ void SCREEN_INFORMATION::SetViewport(const Viewport& newViewport,
     Tracing::s_TraceWindowViewport(_viewport);
 }
 
-// Method Description:
-// - Clear the entire contents of the viewport, except for the cursor's row,
-//   which is moved to the top line of the viewport.
-// - This is used exclusively by ConPTY to support GH#1193, GH#1882. This allows
-//   a terminal to clear the contents of the ConPTY buffer, which is important
-//   if the user would like to be able to clear the terminal-side buffer.
-// Arguments:
-// - <none>
-// Return Value:
-// - S_OK
-[[nodiscard]] HRESULT SCREEN_INFORMATION::ClearBuffer()
-{
-    // Rotate the buffer to bring the cursor row to the top of the viewport.
-    const auto cursorPos = _textBuffer->GetCursor().GetPosition();
-    for (auto i = 0; i < cursorPos.y; i++)
-    {
-        _textBuffer->IncrementCircularBuffer();
-    }
-
-    // Erase everything below that point.
-    RETURN_IF_FAILED(SetCursorPosition({ 0, 1 }, false));
-    auto& engine = reinterpret_cast<OutputStateMachineEngine&>(_stateMachine->Engine());
-    engine.Dispatch().EraseInDisplay(DispatchTypes::EraseType::ToEnd);
-
-    // Restore the original cursor x offset, but now on the first row.
-    RETURN_IF_FAILED(SetCursorPosition({ cursorPos.x, 0 }, false));
-
-    _textBuffer->TriggerRedrawAll();
-
-    return S_OK;
-}
-
 // Routine Description:
 // - Writes cells to the output buffer at the cursor position.
 // Arguments:
diff --git a/src/host/screenInfo.hpp b/src/host/screenInfo.hpp
index 4ecd1a4e3e9..c7685cb1c06 100644
--- a/src/host/screenInfo.hpp
+++ b/src/host/screenInfo.hpp
@@ -203,8 +203,6 @@ class SCREEN_INFORMATION : public ConsoleObjectHeader, public Microsoft::Console
     void SetDefaultAttributes(const TextAttribute& attributes,
                               const TextAttribute& popupAttributes);
 
-    [[nodiscard]] HRESULT ClearBuffer();
-
     void UpdateBottom();
 
     FontInfo& GetCurrentFont() noexcept;


Test patch: