-
Notifications
You must be signed in to change notification settings - Fork 8.6k
Preserve the cursor row during Clear Buffer #18976
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
f582ec0
8f6ebb9
267d075
57866f3
82f2bfc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2259,23 +2259,37 @@ namespace winrt::Microsoft::Terminal::Control::implementation | |
// - <none> | ||
void ControlCore::ClearBuffer(Control::ClearBufferType clearType) | ||
{ | ||
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->Write(command); | ||
// In absolute buffer coordinates, including the scrollback (= Y is offset by the scrollback height). | ||
const auto viewport = _terminal->GetViewport(); | ||
// The absolute cursor coordinate. | ||
const auto cursor = _terminal->GetViewportRelativeCursorPosition(); | ||
|
||
// GH#18732: Users want the row the cursor is on to be preserved across clears. | ||
std::wstring sequence; | ||
|
||
if (clearType == ClearBufferType::Scrollback || clearType == ClearBufferType::All) | ||
{ | ||
sequence.append(L"\x1b[3J"); | ||
} | ||
|
||
if (clearType == ClearBufferType::Screen || clearType == ClearBufferType::All) | ||
{ | ||
// Erase any viewport contents below (but not including) the cursor row. | ||
if (cursor.x < viewport.Height()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should assumedly be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's still an off-by-one error here. You can see that by doing a directory listing or something so that the prompt ends up on the last line of the screen, then try clearing the buffer. Note that it hasn't preserved the prompt line. |
||
{ | ||
fmt::format_to(std::back_inserter(sequence), FMT_COMPILE(L"\x1b[{};1H\x1b[J"), cursor.y + 2); | ||
} | ||
|
||
// Erase any viewport contents above (but not including) the cursor row. | ||
if (cursor.x > 0) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As above, should be |
||
{ | ||
fmt::format_to(std::back_inserter(sequence), FMT_COMPILE(L"\x1b[{}S\x1b[1;{}H"), cursor.y, cursor.x + 1); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not super important, but I'd prefer we didn't use an Also, and this is important, that final cursor positioning sequence should be applied outside the if, because it's required for both branches. If you're on the top line of the page, you're going to erase below, and not above, but you'll still need to reposition the cursor. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you for your review! Sorry that it took so long. It should be fixed now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The cursor repositioning still needs to be moved out so it applies to both branches. You can see the problem by clearing the buffer once, so the prompt ends up on the first line of the display, then immediately clear the buffer again. Note that cursor now ends up underneath the prompt. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Reflecting on this a little bit. The API between In truth, I think that means But also it means that we're using stringly typed magic between two components statically linked into the same executable image. ☹ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did consider moving the scroll logic to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Just on this point, note that our |
||
} | ||
} | ||
|
||
_terminal->Write(sequence); | ||
} | ||
|
||
if (clearType == Control::ClearBufferType::Screen || clearType == Control::ClearBufferType::All) | ||
|
@@ -2284,8 +2298,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation | |
{ | ||
// 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(); | ||
conpty.ClearBuffer(true); | ||
} | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
comment says "absolute" but method says "viewport relative" o_O
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, I changed the code and forgot to remove the comment.