Clear buffer: option to clear up to the current cursor line #23231

Closed
opened 2026-01-31 08:36:04 +00:00 by claunia · 4 comments
Owner

Originally created by @elibarzilay on GitHub (May 5, 2025).

Originally assigned to: @lhecker on GitHub.

Description of the new feature

This is a followup to #18732, which is locked. The request there is to have an option to preserve the current line in clearBuffer.

I don't have VS installed, so I can't play with the code, but I think that adding something like the below case to ControlCore::ClearBuffer should be very close to implementing this.

[It will fail in case of editing multi-line inputs, but as I described in the other issue, this is still hugely useful as a default over the current always-remove-everything option. The one thing that bothers me more about this is that it uses the beginning of the current physical line rather than the logical line. If there's a way to get that information, it would be much better.]

Proposed technical implementation details

Disclaimer: mostly made-up code based on what I see in the file. Also assumes that there's a .x and .y fields, and that they're 0-based.

        case ClearBufferType::AllToCursor:
            const til::point pos = _terminal->GetTextBuffer().GetCursor().GetPosition();
            if (til.y == 0)
            {
                // cursor at the top of the screen: just remove the scrollback
                command = L"\x1b[3J";
            }
            else
            {
                // otherwise: clear scrallback, move to top at the same column, delete rows up to cursor position
                command = fmt::format(FMT_COMPILE(L"\x1b[3J\x1b[H\x1b[{}M\x1b[1;{}H"), pos.y, pos.x + 1);
            }
            break;

For reference, a bash version of this:

clearToCursor() {
  # read cursor position
  IFS='[;' read -s -d R -p $'\e[6n' -r -u 0 _ ROW COL
  if [[ $ROW -eq 1 ]]; then
    # on top row => just clear the scrollback
    printf '\e[3J'
  else
    # clear scrollback, goto top, delete ROW-1 lines
    printf '\e[3J\e[H\e[%dM\e[1;%dH' $((ROW-1)) $COL
  fi
}
Originally created by @elibarzilay on GitHub (May 5, 2025). Originally assigned to: @lhecker on GitHub. ### Description of the new feature This is a followup to #18732, which is locked. The request there is to have an option to preserve the current line in `clearBuffer`. I don't have VS installed, so I can't play with the code, but I think that adding something like the below case to `ControlCore::ClearBuffer` should be very close to implementing this. [It will fail in case of editing multi-line inputs, but as I described in the other issue, this is still hugely useful as a default over the current always-remove-everything option. The one thing that bothers me more about this is that it uses the beginning of the current physical line rather than the logical line. If there's a way to get that information, it would be much better.] ### Proposed technical implementation details Disclaimer: mostly made-up code based on what I see in the file. Also assumes that there's a `.x` and `.y` fields, and that they're 0-based. ```c++ case ClearBufferType::AllToCursor: const til::point pos = _terminal->GetTextBuffer().GetCursor().GetPosition(); if (til.y == 0) { // cursor at the top of the screen: just remove the scrollback command = L"\x1b[3J"; } else { // otherwise: clear scrallback, move to top at the same column, delete rows up to cursor position command = fmt::format(FMT_COMPILE(L"\x1b[3J\x1b[H\x1b[{}M\x1b[1;{}H"), pos.y, pos.x + 1); } break; ``` For reference, a bash version of this: ```bash clearToCursor() { # read cursor position IFS='[;' read -s -d R -p $'\e[6n' -r -u 0 _ ROW COL if [[ $ROW -eq 1 ]]; then # on top row => just clear the scrollback printf '\e[3J' else # clear scrollback, goto top, delete ROW-1 lines printf '\e[3J\e[H\e[%dM\e[1;%dH' $((ROW-1)) $COL fi } ```
claunia added the Needs-Tag-FixProduct-TerminalArea-TerminalControl labels 2026-01-31 08:36:05 +00:00
Author
Owner

@elibarzilay commented on GitHub (May 10, 2025):

Two more bits of relevant information:

  • Now that #18885 is merged, this could use the same logic to make it even better and preserve the last logical line rather than the physical one. (But I added a comment there that might be considered WRT a logical line that is partially visible.)

  • By complete chance, I was using CMD today, and tried clearing the buffer. The result is strange in a way that looks like things are complicated: it works as expected when nothing is type (ie, after an enter) -- but if I start typing stuff, then clear the buffer, then type more, the editing continues from the same place. Looks like once typing starts, whatever is CMD's idea of a line-editor does not get reset. I also tried powershell, and it's the same as CMD, except that even after an enter it behaves in the same broken way. Screen-recording follows:

https://github.com/user-attachments/assets/392fb0b8-967a-47b0-b64c-dcacd5ae5bf9

Sidenote: I only mention this for completeness, since whatever was the original issue that triggered the new behavior might have been related to how things work in CMD/PS, which I almost never use (and therefore how it's broken now is not something that I care much about :).

@elibarzilay commented on GitHub (May 10, 2025): Two more bits of relevant information: * Now that #18885 is merged, this could use the same logic to make it even better and preserve the last logical line rather than the physical one. (But I added a comment there that might be considered WRT a logical line that is partially visible.) --- * By complete chance, I was using CMD today, and tried clearing the buffer. The result is strange in a way that looks like things are complicated: it works as expected when nothing is type (ie, after an enter) -- but if I start typing stuff, then clear the buffer, then type more, the editing continues from the same place. Looks like once typing starts, whatever is CMD's idea of a line-editor does not get reset. I also tried powershell, and it's the same as CMD, except that even after an enter it behaves in the same broken way. Screen-recording follows: https://github.com/user-attachments/assets/392fb0b8-967a-47b0-b64c-dcacd5ae5bf9 Sidenote: I only mention this for completeness, since whatever was the original issue that triggered the new behavior might have been related to how things work in CMD/PS, which I almost never use (and therefore how it's broken now is not something that I care much about :).
Author
Owner

@elibarzilay commented on GitHub (May 12, 2025):

I was able to kind of install VS in a VM, not really in a productive way (since each recompilation took around 30m), but I did manage to verify that the following code seem to work:

            {
                const auto lock = _terminal->LockForWriting();
                const til::point pos = _terminal->GetCursorPosition();
                int ofs = ScrollOffset();
                if (pos.y - ofs == 0)
                {
                    // cursor is already at the top of the screen: just remove the scrollback
                    command = L"\x1b[3J";
                }
                else
                {
                    command = fmt::format(L"\x1b[3J\x1b[H\x1b[{}M\x1b[1;{}H", pos.y - ofs, pos.x + 1);
                }
            }

It's not ideal in releasing the lock just to re-grab it later when sending the string, but it does seem to do the trick.

Re my last comment, it would be nice to have that working too (clearing up to the beginning of the current logical line), but given the 30m recompilation cycle I gave up. For reference, there is a control sequence for scrolling up, eg: \x1b[2T to scroll two rows up.

@elibarzilay commented on GitHub (May 12, 2025): I was able to kind of install VS in a VM, not really in a productive way (since each recompilation took around 30m), but I did manage to verify that the following code seem to work: ```c++ { const auto lock = _terminal->LockForWriting(); const til::point pos = _terminal->GetCursorPosition(); int ofs = ScrollOffset(); if (pos.y - ofs == 0) { // cursor is already at the top of the screen: just remove the scrollback command = L"\x1b[3J"; } else { command = fmt::format(L"\x1b[3J\x1b[H\x1b[{}M\x1b[1;{}H", pos.y - ofs, pos.x + 1); } } ``` It's not ideal in releasing the lock just to re-grab it later when sending the string, but it does seem to do the trick. Re my last comment, it would be nice to have that working too (clearing up to the beginning of the current logical line), but given the 30m recompilation cycle I gave up. For reference, there is a control sequence for scrolling up, eg: `\x1b[2T` to scroll two rows up.
Author
Owner

@lhecker commented on GitHub (May 27, 2025):

Your suggested fix doesn't take into account that the buffers between ConPTY and Windows Terminal should stay mostly synchronized. ClearBuffer clears the buffer fully which has useful properties. This puts the cursor at position 0,0. Since your approach only clears the Windows Terminal side, the cursors aren't synchronized anymore.

This is the entire crux why this behavior can't be changed trivially.
Edit: While on a walk I think I had an idea how to fix this "simply" and somewhat elegantly.
Edit 2: Doesn't work. Fuck it. Shitty solution it is.

@lhecker commented on GitHub (May 27, 2025): Your suggested fix doesn't take into account that the buffers between ConPTY and Windows Terminal should stay mostly synchronized. `ClearBuffer` clears the buffer fully which has useful properties. This puts the cursor at position `0,0`. Since your approach only clears the Windows Terminal side, the cursors aren't synchronized anymore. This is the entire crux why this behavior can't be changed trivially. Edit: While on a walk I think I had an idea how to fix this "simply" and somewhat elegantly. Edit 2: Doesn't work. Fuck it. Shitty solution it is.
Author
Owner

@elibarzilay commented on GitHub (May 27, 2025):

  1. My suggestion uses only control sequences -- are they not implemented properly somehow?

  2. See the second part in my earlier comment: it is currently already broken in CMD/PS, which makes me think that the answer to the above is "yes".

  3. Regardless, it seems to me like a good direction to implement a control-sequence-only solution independently of fixing whatever's broken. More so given that it's broken in CMD/PS anyway.

@elibarzilay commented on GitHub (May 27, 2025): 1. My suggestion uses only control sequences -- are they not implemented properly somehow? 2. See the second part in [my earlier comment](https://github.com/microsoft/terminal/issues/18878#issuecomment-2868170232): it is *currently* already broken in CMD/PS, which makes me think that the answer to the above is "yes". 3. Regardless, it seems to me like a good direction to implement a control-sequence-only solution independently of fixing whatever's broken. More so given that it's broken in CMD/PS anyway.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#23231