diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h index be72df2c68fae..35a41e419c9bf 100644 --- a/lldb/include/lldb/Core/Debugger.h +++ b/lldb/include/lldb/Core/Debugger.h @@ -232,6 +232,8 @@ class Debugger : public std::enable_shared_from_this, const char *GetIOHandlerHelpPrologue(); + void RefreshIOHandler(); + void ClearIOHandlers(); bool EnableLog(llvm::StringRef channel, diff --git a/lldb/include/lldb/Core/IOHandler.h b/lldb/include/lldb/Core/IOHandler.h index 6447d56cd4696..873bd2335365b 100644 --- a/lldb/include/lldb/Core/IOHandler.h +++ b/lldb/include/lldb/Core/IOHandler.h @@ -90,6 +90,8 @@ class IOHandler { virtual void TerminalSizeChanged() {} + virtual void Refresh() {} + virtual const char *GetPrompt() { // Prompt support isn't mandatory return nullptr; @@ -404,6 +406,8 @@ class IOHandlerEditline : public IOHandler { void PrintAsync(const char *s, size_t len, bool is_stdout) override; + void Refresh() override; + private: #if LLDB_ENABLE_LIBEDIT bool IsInputCompleteCallback(Editline *editline, StringList &lines); diff --git a/lldb/include/lldb/Core/Statusline.h b/lldb/include/lldb/Core/Statusline.h index 521b9f2526f6b..6bda153f822d2 100644 --- a/lldb/include/lldb/Core/Statusline.h +++ b/lldb/include/lldb/Core/Statusline.h @@ -36,12 +36,10 @@ class Statusline { /// Draw the statusline with the given text. void Draw(std::string msg); - /// Update terminal dimensions. - void UpdateTerminalProperties(); - enum ScrollWindowMode { EnableStatusline, DisableStatusline, + ResizeStatusline, }; /// Set the scroll window for the given mode. diff --git a/lldb/include/lldb/Host/Editline.h b/lldb/include/lldb/Host/Editline.h index c202a76758e13..947ad3bfe5ec6 100644 --- a/lldb/include/lldb/Host/Editline.h +++ b/lldb/include/lldb/Host/Editline.h @@ -267,6 +267,8 @@ class Editline { size_t GetTerminalHeight() { return m_terminal_height; } + void Refresh(); + 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/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp index 8c37b1c8d80ec..0efc9d9a4482f 100644 --- a/lldb/source/Core/Debugger.cpp +++ b/lldb/source/Core/Debugger.cpp @@ -1435,6 +1435,13 @@ bool Debugger::PopIOHandler(const IOHandlerSP &pop_reader_sp) { return true; } +void Debugger::RefreshIOHandler() { + std::lock_guard guard(m_io_handler_stack.GetMutex()); + IOHandlerSP reader_sp(m_io_handler_stack.Top()); + if (reader_sp) + reader_sp->Refresh(); +} + StreamUP Debugger::GetAsyncOutputStream() { return std::make_unique(*this, StreamAsynchronousIO::STDOUT); diff --git a/lldb/source/Core/IOHandler.cpp b/lldb/source/Core/IOHandler.cpp index 36bd9eb92110c..61de2f6ccc465 100644 --- a/lldb/source/Core/IOHandler.cpp +++ b/lldb/source/Core/IOHandler.cpp @@ -666,3 +666,10 @@ void IOHandlerEditline::PrintAsync(const char *s, size_t len, bool is_stdout) { #endif } } + +void IOHandlerEditline::Refresh() { +#if LLDB_ENABLE_LIBEDIT + if (m_editline_up) + m_editline_up->Refresh(); +#endif +} diff --git a/lldb/source/Core/Statusline.cpp b/lldb/source/Core/Statusline.cpp index dcfa84815dc03..393d427241021 100644 --- a/lldb/source/Core/Statusline.cpp +++ b/lldb/source/Core/Statusline.cpp @@ -24,10 +24,9 @@ #define ANSI_SAVE_CURSOR ESCAPE "7" #define ANSI_RESTORE_CURSOR ESCAPE "8" #define ANSI_CLEAR_BELOW ESCAPE "[J" -#define ANSI_CURSOR_DOWN ESCAPE "[B" -#define ANSI_CLEAR_LINE ESCAPE "[2K" -#define ANSI_SET_SCROLL_ROWS ESCAPE "[0;%ur" -#define ANSI_TO_START_OF_ROW ESCAPE "[%u;0f" +#define ANSI_CLEAR_SCREEN ESCAPE "[2J" +#define ANSI_SET_SCROLL_ROWS ESCAPE "[1;%ur" +#define ANSI_TO_START_OF_ROW ESCAPE "[%u;1f" #define ANSI_REVERSE_VIDEO ESCAPE "[7m" #define ANSI_UP_ROWS ESCAPE "[%dA" @@ -43,10 +42,12 @@ Statusline::Statusline(Debugger &debugger) Statusline::~Statusline() { Disable(); } void Statusline::TerminalSizeChanged() { - UpdateTerminalProperties(); + m_terminal_width = m_debugger.GetTerminalWidth(); + m_terminal_height = m_debugger.GetTerminalHeight(); + + UpdateScrollWindow(ResizeStatusline); - // This definitely isn't signal safe, but the best we can do, until we - // have proper signal-catching thread. + // Draw the old statusline. Redraw(/*update=*/false); } @@ -87,13 +88,6 @@ void Statusline::Draw(std::string str) { locked_stream << ANSI_RESTORE_CURSOR; } -void Statusline::UpdateTerminalProperties() { - UpdateScrollWindow(DisableStatusline); - m_terminal_width = m_debugger.GetTerminalWidth(); - m_terminal_height = m_debugger.GetTerminalHeight(); - UpdateScrollWindow(EnableStatusline); -} - void Statusline::UpdateScrollWindow(ScrollWindowMode mode) { assert(m_terminal_width != 0 && m_terminal_height != 0); @@ -101,24 +95,36 @@ void Statusline::UpdateScrollWindow(ScrollWindowMode mode) { if (!stream_sp) return; - const unsigned scroll_height = - (mode == DisableStatusline) ? m_terminal_height : m_terminal_height - 1; - + const unsigned reduced_scroll_window = m_terminal_height - 1; LockedStreamFile locked_stream = stream_sp->Lock(); - 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'; + locked_stream.Printf(ANSI_UP_ROWS, 1); + // Reduce the scroll window. + locked_stream << ANSI_SAVE_CURSOR; + locked_stream.Printf(ANSI_SET_SCROLL_ROWS, reduced_scroll_window); + locked_stream << ANSI_RESTORE_CURSOR; break; case DisableStatusline: + // Reset the scroll window. + locked_stream << ANSI_SAVE_CURSOR; + locked_stream.Printf(ANSI_SET_SCROLL_ROWS, 0); + locked_stream << ANSI_RESTORE_CURSOR; // Clear the screen below to hide the old statusline. locked_stream << ANSI_CLEAR_BELOW; break; + case ResizeStatusline: + // Clear the screen and update the scroll window. + // FIXME: Find a better solution (#146919). + locked_stream << ANSI_CLEAR_SCREEN; + locked_stream.Printf(ANSI_SET_SCROLL_ROWS, reduced_scroll_window); + break; } + + m_debugger.RefreshIOHandler(); } void Statusline::Redraw(bool update) { diff --git a/lldb/source/Host/common/Editline.cpp b/lldb/source/Host/common/Editline.cpp index b251ded6c3793..cbda2ef8012f8 100644 --- a/lldb/source/Host/common/Editline.cpp +++ b/lldb/source/Host/common/Editline.cpp @@ -1709,6 +1709,13 @@ void Editline::PrintAsync(lldb::LockableStreamFileSP stream_sp, const char *s, } } +void Editline::Refresh() { + if (!m_editline || !m_output_stream_sp) + return; + LockedStreamFile locked_stream = m_output_stream_sp->Lock(); + el_set(m_editline, EL_REFRESH); +} + bool Editline::CompleteCharacter(char ch, EditLineGetCharType &out) { #if !LLDB_EDITLINE_USE_WCHAR if (ch == (char)EOF) diff --git a/lldb/test/API/functionalities/statusline/TestStatusline.py b/lldb/test/API/functionalities/statusline/TestStatusline.py index 53ac7432f4ba1..087e62b387d77 100644 --- a/lldb/test/API/functionalities/statusline/TestStatusline.py +++ b/lldb/test/API/functionalities/statusline/TestStatusline.py @@ -27,10 +27,12 @@ def do_setup(self): self.expect("run", substrs=["stop reason"]) self.resize() - def resize(self): + def resize(self, height=None, width=None): + height = self.TERMINAL_HEIGHT if not height else height + width = self.TERMINAL_WIDTH if not width else width # Change the terminal dimensions. When we launch the tests, we reset # all the settings, leaving the terminal dimensions unset. - self.child.setwinsize(self.TERMINAL_HEIGHT, self.TERMINAL_WIDTH) + self.child.setwinsize(height, width) def test(self): """Basic test for the statusline.""" @@ -44,7 +46,7 @@ def test(self): self.expect( "set set show-statusline true", [ - "\x1b[0;{}r".format(self.TERMINAL_HEIGHT - 1), + "\x1b[1;{}r".format(self.TERMINAL_HEIGHT - 1), "a.out | main.c:2:11 | breakpoint 1.1 ", ], ) @@ -65,9 +67,7 @@ def test(self): self.expect('set set separator "| "') # Hide the statusline and check or the control character. - self.expect( - "set set show-statusline false", ["\x1b[0;{}r".format(self.TERMINAL_HEIGHT)] - ) + self.expect("set set show-statusline false", ["\x1b[1;0r"]) def test_no_color(self): """Basic test for the statusline with colors disabled.""" @@ -104,3 +104,14 @@ def test_no_target(self): self.resize() self.expect("set set show-statusline true", ["no target"]) + + @skipIfEditlineSupportMissing + def test_resize(self): + """Test that move the cursor when resizing.""" + self.launch(timeout=self.TIMEOUT) + self.resize() + self.expect("set set show-statusline true", ["no target"]) + self.resize(20, 60) + # Check for the escape code to resize the scroll window. + self.child.expect(re.escape("\x1b[1;19r")) + self.child.expect("(lldb)")