Skip to content

Commit 6dc1ff8

Browse files
committed
[lldb] Correctly restore the cursor column after resizing the statusline
This PR ensures we correctly restore the cursor column after resizing the statusline. To ensure we have space for the statusline, we have to emit a newline to move up everything on screen. The newline causes the cursor to move to the start of the next line, which needs to be undone. Normally, we would use escape codes to save & restore the cursor position, but that doesn't work here, as the cursor position may have (purposely) changed. Instead, we move the cursor up one line using an escape code, but we weren't restoring the column. Interestingly, Editline was able to recover from this issue through the LineInfo struct which contains the buffer and the cursor location, which allows us to compute the column. This PR addresses the bug by relying on the active IOHandler to report its cursor position and if available, use that to restore the cursor column position. Fixes #134064
1 parent 00f6d6a commit 6dc1ff8

File tree

8 files changed

+72
-8
lines changed

8 files changed

+72
-8
lines changed

lldb/include/lldb/Core/Debugger.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
133133

134134
void SetAsyncExecution(bool async);
135135

136+
CursorPosition GetIOHandlerCursorPosition();
137+
136138
lldb::FileSP GetInputFileSP() { return m_input_file_sp; }
137139
File &GetInputFile() { return *m_input_file_sp; }
138140

lldb/include/lldb/Core/IOHandler.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#define LLDB_CORE_IOHANDLER_H
1111

1212
#include "lldb/Host/Config.h"
13+
#include "lldb/Host/Terminal.h"
1314
#include "lldb/Utility/CompletionRequest.h"
1415
#include "lldb/Utility/Flags.h"
1516
#include "lldb/Utility/Predicate.h"
@@ -113,6 +114,10 @@ class IOHandler {
113114

114115
virtual const char *GetHelpPrologue() { return nullptr; }
115116

117+
virtual CursorPosition GetCursorPosition() const {
118+
return {std::nullopt, std::nullopt};
119+
}
120+
116121
int GetInputFD();
117122

118123
int GetOutputFD();
@@ -404,6 +409,8 @@ class IOHandlerEditline : public IOHandler {
404409

405410
void PrintAsync(const char *s, size_t len, bool is_stdout) override;
406411

412+
virtual CursorPosition GetCursorPosition() const override;
413+
407414
private:
408415
#if LLDB_ENABLE_LIBEDIT
409416
bool IsInputCompleteCallback(Editline *editline, StringList &lines);

lldb/include/lldb/Host/Editline.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include <vector>
3636

3737
#include "lldb/Host/StreamFile.h"
38+
#include "lldb/Host/Terminal.h"
3839
#include "lldb/lldb-private.h"
3940

4041
#if !defined(_WIN32) && !defined(__ANDROID__)
@@ -267,6 +268,8 @@ class Editline {
267268

268269
size_t GetTerminalHeight() { return m_terminal_height; }
269270

271+
CursorPosition GetCursorPosition();
272+
270273
private:
271274
/// Sets the lowest line number for multi-line editing sessions. A value of
272275
/// zero suppresses line number printing in the prompt.

lldb/include/lldb/Host/Terminal.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,11 @@ class TerminalState {
169169
lldb::pid_t m_process_group = -1; ///< Cached process group information.
170170
};
171171

172+
struct CursorPosition {
173+
std::optional<unsigned> cols;
174+
std::optional<unsigned> rows;
175+
};
176+
172177
} // namespace lldb_private
173178

174179
#endif // LLDB_HOST_TERMINAL_H

lldb/source/Core/Debugger.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1240,6 +1240,14 @@ void Debugger::DispatchInputEndOfFile() {
12401240
reader_sp->GotEOF();
12411241
}
12421242

1243+
CursorPosition Debugger::GetIOHandlerCursorPosition() {
1244+
std::lock_guard<std::recursive_mutex> guard(m_io_handler_stack.GetMutex());
1245+
IOHandlerSP reader_sp(m_io_handler_stack.Top());
1246+
if (reader_sp)
1247+
return reader_sp->GetCursorPosition();
1248+
return {std::nullopt, std::nullopt};
1249+
}
1250+
12431251
void Debugger::ClearIOHandlers() {
12441252
// The bottom input reader should be the main debugger input reader. We do
12451253
// not want to close that one here.

lldb/source/Core/IOHandler.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,14 @@ void IOHandlerEditline::GotEOF() {
634634
#endif
635635
}
636636

637+
CursorPosition IOHandlerEditline::GetCursorPosition() const {
638+
#if LLDB_ENABLE_LIBEDIT
639+
if (m_editline_up)
640+
return m_editline_up->GetCursorPosition();
641+
#endif
642+
return {std::nullopt, std::nullopt};
643+
}
644+
637645
void IOHandlerEditline::PrintAsync(const char *s, size_t len, bool is_stdout) {
638646
#if LLDB_ENABLE_LIBEDIT
639647
if (m_editline_up) {

lldb/source/Core/Statusline.cpp

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#define ANSI_TO_START_OF_ROW ESCAPE "[%u;1f"
2929
#define ANSI_REVERSE_VIDEO ESCAPE "[7m"
3030
#define ANSI_UP_ROWS ESCAPE "[%dA"
31+
#define ANSI_SET_COLUMN_N ESCAPE "[%uG"
3132

3233
using namespace lldb;
3334
using namespace lldb_private;
@@ -103,19 +104,35 @@ void Statusline::UpdateScrollWindow(ScrollWindowMode mode) {
103104
(mode == DisableStatusline) ? m_terminal_height : m_terminal_height - 1;
104105

105106
LockedStreamFile locked_stream = stream_sp->Lock();
107+
108+
if (mode == EnableStatusline) {
109+
// Get the cursor position before we potentially change the cursor position.
110+
CursorPosition cursor_position = m_debugger.GetIOHandlerCursorPosition();
111+
112+
// Move everything on the screen up to make space for the statusline. This
113+
// is going to move the cursor to the start of the next line which we need
114+
// to undo.
115+
locked_stream << '\n';
116+
117+
// First move the cursor back up. We can't use ANSI_SAVE/RESTORE_CURSOR
118+
// here, because the old and new position differ if everything on the screen
119+
// moved up.
120+
locked_stream.Printf(ANSI_UP_ROWS, 1);
121+
122+
// Finally move the cursor back to the correct column, if the IOHandler was
123+
// able to tell us where that was.
124+
if (cursor_position.cols)
125+
locked_stream.Printf(ANSI_SET_COLUMN_N, *cursor_position.cols);
126+
}
127+
128+
// Adjust the scroll window.
106129
locked_stream << ANSI_SAVE_CURSOR;
107130
locked_stream.Printf(ANSI_SET_SCROLL_ROWS, scroll_height);
108131
locked_stream << ANSI_RESTORE_CURSOR;
109-
switch (mode) {
110-
case EnableStatusline:
111-
// Move everything on the screen up.
112-
locked_stream.Printf(ANSI_UP_ROWS, 1);
113-
locked_stream << '\n';
114-
break;
115-
case DisableStatusline:
132+
133+
if (mode == DisableStatusline) {
116134
// Clear the screen below to hide the old statusline.
117135
locked_stream << ANSI_CLEAR_BELOW;
118-
break;
119136
}
120137
}
121138

lldb/source/Host/common/Editline.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,20 @@ int Editline::GetLineIndexForLocation(CursorLocation location, int cursor_row) {
398398
return line;
399399
}
400400

401+
CursorPosition Editline::GetCursorPosition() {
402+
if (!m_editline)
403+
return {};
404+
405+
const LineInfoW *info = el_wline(m_editline);
406+
if (!info)
407+
return {};
408+
409+
const size_t editline_cursor_col =
410+
(int)((info->cursor - info->buffer) + GetPromptWidth()) + 1;
411+
412+
return {editline_cursor_col, std::nullopt};
413+
}
414+
401415
void Editline::MoveCursor(CursorLocation from, CursorLocation to) {
402416
const LineInfoW *info = el_wline(m_editline);
403417
int editline_cursor_position =

0 commit comments

Comments
 (0)