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, 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 #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 cols; + std::optional 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 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 =