https://github.com/JDevlieghere updated https://github.com/llvm/llvm-project/pull/145823
>From 6dc1ff8270e603def516bae230a5fe7108645978 Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere <jo...@devlieghere.com> Date: Thu, 26 Jun 2025 08:33:07 -0700 Subject: [PATCH] [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 --- lldb/include/lldb/Core/Debugger.h | 2 ++ lldb/include/lldb/Core/IOHandler.h | 7 ++++++ lldb/include/lldb/Host/Editline.h | 3 +++ lldb/include/lldb/Host/Terminal.h | 5 +++++ lldb/source/Core/Debugger.cpp | 8 +++++++ lldb/source/Core/IOHandler.cpp | 8 +++++++ lldb/source/Core/Statusline.cpp | 33 +++++++++++++++++++++------- lldb/source/Host/common/Editline.cpp | 14 ++++++++++++ 8 files changed, 72 insertions(+), 8 deletions(-) diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h index 2087ef2a11562..46f70024fd65b 100644 --- a/lldb/include/lldb/Core/Debugger.h +++ b/lldb/include/lldb/Core/Debugger.h @@ -133,6 +133,8 @@ class Debugger : public std::enable_shared_from_this<Debugger>, void SetAsyncExecution(bool async); + CursorPosition GetIOHandlerCursorPosition(); + lldb::FileSP GetInputFileSP() { return m_input_file_sp; } File &GetInputFile() { return *m_input_file_sp; } diff --git a/lldb/include/lldb/Core/IOHandler.h b/lldb/include/lldb/Core/IOHandler.h index 2fb3d7a7c9cc3..9d17e3a45846a 100644 --- a/lldb/include/lldb/Core/IOHandler.h +++ b/lldb/include/lldb/Core/IOHandler.h @@ -10,6 +10,7 @@ #define LLDB_CORE_IOHANDLER_H #include "lldb/Host/Config.h" +#include "lldb/Host/Terminal.h" #include "lldb/Utility/CompletionRequest.h" #include "lldb/Utility/Flags.h" #include "lldb/Utility/Predicate.h" @@ -113,6 +114,10 @@ class IOHandler { virtual const char *GetHelpPrologue() { return nullptr; } + virtual CursorPosition GetCursorPosition() const { + return {std::nullopt, std::nullopt}; + } + int GetInputFD(); int GetOutputFD(); @@ -404,6 +409,8 @@ class IOHandlerEditline : public IOHandler { void PrintAsync(const char *s, size_t len, bool is_stdout) override; + virtual CursorPosition GetCursorPosition() const override; + private: #if LLDB_ENABLE_LIBEDIT bool IsInputCompleteCallback(Editline *editline, StringList &lines); diff --git a/lldb/include/lldb/Host/Editline.h b/lldb/include/lldb/Host/Editline.h index c202a76758e13..5672212421687 100644 --- a/lldb/include/lldb/Host/Editline.h +++ b/lldb/include/lldb/Host/Editline.h @@ -35,6 +35,7 @@ #include <vector> #include "lldb/Host/StreamFile.h" +#include "lldb/Host/Terminal.h" #include "lldb/lldb-private.h" #if !defined(_WIN32) && !defined(__ANDROID__) @@ -267,6 +268,8 @@ class Editline { size_t GetTerminalHeight() { return m_terminal_height; } + CursorPosition GetCursorPosition(); + private: /// Sets the lowest line number for multi-line editing sessions. A value of /// zero suppresses line number printing in the prompt. diff --git a/lldb/include/lldb/Host/Terminal.h b/lldb/include/lldb/Host/Terminal.h index da0d05e8bd265..787f97e66d267 100644 --- a/lldb/include/lldb/Host/Terminal.h +++ b/lldb/include/lldb/Host/Terminal.h @@ -169,6 +169,11 @@ class TerminalState { lldb::pid_t m_process_group = -1; ///< Cached process group information. }; +struct CursorPosition { + std::optional<unsigned> cols; + std::optional<unsigned> rows; +}; + } // namespace lldb_private #endif // LLDB_HOST_TERMINAL_H diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp index 445baf1f63785..bee2a6ac60396 100644 --- a/lldb/source/Core/Debugger.cpp +++ b/lldb/source/Core/Debugger.cpp @@ -1240,6 +1240,14 @@ void Debugger::DispatchInputEndOfFile() { reader_sp->GotEOF(); } +CursorPosition Debugger::GetIOHandlerCursorPosition() { + std::lock_guard<std::recursive_mutex> guard(m_io_handler_stack.GetMutex()); + IOHandlerSP reader_sp(m_io_handler_stack.Top()); + if (reader_sp) + return reader_sp->GetCursorPosition(); + return {std::nullopt, std::nullopt}; +} + void Debugger::ClearIOHandlers() { // The bottom input reader should be the main debugger input reader. We do // not want to close that one here. diff --git a/lldb/source/Core/IOHandler.cpp b/lldb/source/Core/IOHandler.cpp index 8aac507eaa0c2..b3f89def98701 100644 --- a/lldb/source/Core/IOHandler.cpp +++ b/lldb/source/Core/IOHandler.cpp @@ -634,6 +634,14 @@ void IOHandlerEditline::GotEOF() { #endif } +CursorPosition IOHandlerEditline::GetCursorPosition() const { +#if LLDB_ENABLE_LIBEDIT + if (m_editline_up) + return m_editline_up->GetCursorPosition(); +#endif + return {std::nullopt, std::nullopt}; +} + void IOHandlerEditline::PrintAsync(const char *s, size_t len, bool is_stdout) { #if LLDB_ENABLE_LIBEDIT if (m_editline_up) { diff --git a/lldb/source/Core/Statusline.cpp b/lldb/source/Core/Statusline.cpp index 8a8640805cac0..15a24adc3d7e8 100644 --- a/lldb/source/Core/Statusline.cpp +++ b/lldb/source/Core/Statusline.cpp @@ -28,6 +28,7 @@ #define ANSI_TO_START_OF_ROW ESCAPE "[%u;1f" #define ANSI_REVERSE_VIDEO ESCAPE "[7m" #define ANSI_UP_ROWS ESCAPE "[%dA" +#define ANSI_SET_COLUMN_N ESCAPE "[%uG" using namespace lldb; using namespace lldb_private; @@ -103,19 +104,35 @@ void Statusline::UpdateScrollWindow(ScrollWindowMode mode) { (mode == DisableStatusline) ? m_terminal_height : m_terminal_height - 1; LockedStreamFile locked_stream = stream_sp->Lock(); + + if (mode == EnableStatusline) { + // Get the cursor position before we potentially change the cursor position. + CursorPosition cursor_position = m_debugger.GetIOHandlerCursorPosition(); + + // Move everything on the screen up to make space for the statusline. This + // is going to move the cursor to the start of the next line which we need + // to undo. + locked_stream << '\n'; + + // First move the cursor back up. We can't use ANSI_SAVE/RESTORE_CURSOR + // here, because the old and new position differ if everything on the screen + // moved up. + locked_stream.Printf(ANSI_UP_ROWS, 1); + + // Finally move the cursor back to the correct column, if the IOHandler was + // able to tell us where that was. + if (cursor_position.cols) + locked_stream.Printf(ANSI_SET_COLUMN_N, *cursor_position.cols); + } + + // Adjust the scroll window. locked_stream << ANSI_SAVE_CURSOR; locked_stream.Printf(ANSI_SET_SCROLL_ROWS, scroll_height); locked_stream << ANSI_RESTORE_CURSOR; - switch (mode) { - case EnableStatusline: - // Move everything on the screen up. - locked_stream.Printf(ANSI_UP_ROWS, 1); - locked_stream << '\n'; - break; - case DisableStatusline: + + if (mode == DisableStatusline) { // Clear the screen below to hide the old statusline. locked_stream << ANSI_CLEAR_BELOW; - break; } } diff --git a/lldb/source/Host/common/Editline.cpp b/lldb/source/Host/common/Editline.cpp index b251ded6c3793..c2b8d23393440 100644 --- a/lldb/source/Host/common/Editline.cpp +++ b/lldb/source/Host/common/Editline.cpp @@ -398,6 +398,20 @@ int Editline::GetLineIndexForLocation(CursorLocation location, int cursor_row) { return line; } +CursorPosition Editline::GetCursorPosition() { + if (!m_editline) + return {}; + + const LineInfoW *info = el_wline(m_editline); + if (!info) + return {}; + + const size_t editline_cursor_col = + (int)((info->cursor - info->buffer) + GetPromptWidth()) + 1; + + return {editline_cursor_col, std::nullopt}; +} + void Editline::MoveCursor(CursorLocation from, CursorLocation to) { const LineInfoW *info = el_wline(m_editline); int editline_cursor_position = _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits