virtual void Deactivate() { m_active = false; }
+ virtual void TerminalSizeChanged() {}
+
virtual const char *GetPrompt() {
// Prompt support isn't mandatory
return nullptr;
void Deactivate() override;
+ void TerminalSizeChanged() override;
+
ConstString GetControlSequence(char ch) override {
return m_delegate.IOHandlerGetControlSequence(ch);
}
// good amount of the text will
// disappear. It's still in the buffer, just invisible.
// b) The prompt printing logic for dealing with ANSI formatting characters is
-// broken, which is why we're
-// working around it here.
-// c) When resizing the terminal window, if the cursor moves between rows
-// libedit will get confused. d) The incremental search uses escape to cancel
-// input, so it's confused by
+// broken, which is why we're working around it here.
+// c) The incremental search uses escape to cancel input, so it's confused by
// ANSI sequences starting with escape.
-// e) Emoji support is fairly terrible, presumably it doesn't understand
+// d) Emoji support is fairly terrible, presumably it doesn't understand
// composed characters?
#ifndef LLDB_HOST_EDITLINE_H
#include <histedit.h>
#endif
+#include <csignal>
#include <mutex>
#include <string>
#include <vector>
/// editing scenarios.
void SetContinuationPrompt(const char *continuation_prompt);
- /// Required to update the width of the terminal registered for I/O. It is
- /// critical that this
- /// be correct at all times.
+ /// Call when the terminal size changes
void TerminalSizeChanged();
/// Returns the prompt established by SetPrompt()
bool CompleteCharacter(char ch, EditLineGetCharType &out);
+ void ApplyTerminalSizeChange();
+
private:
#if LLDB_EDITLINE_USE_WCHAR
std::wstring_convert<std::codecvt_utf8<wchar_t>> m_utf8conv;
std::string m_set_continuation_prompt;
std::string m_current_prompt;
bool m_needs_prompt_repaint = false;
+ volatile std::sig_atomic_t m_terminal_size_has_changed = 0;
std::string m_editor_name;
FILE *m_input_file;
FILE *m_output_file;
}
bool Debugger::SetTerminalWidth(uint32_t term_width) {
+ if (auto handler_sp = m_io_handler_stack.Top())
+ handler_sp->TerminalSizeChanged();
+
const uint32_t idx = ePropertyTerminalWidth;
return m_collection_sp->SetPropertyAtIndexAsSInt64(nullptr, idx, term_width);
}
m_delegate.IOHandlerDeactivated(*this);
}
+void IOHandlerEditline::TerminalSizeChanged() {
+#if LLDB_ENABLE_LIBEDIT
+ m_editline_up->TerminalSizeChanged();
+#endif
+}
+
// Split out a line from the buffer, if there is a full one to get.
static Optional<std::string> SplitLine(std::string &line_buffer) {
size_t pos = line_buffer.find('\n');
lldb::ConnectionStatus status = lldb::eConnectionStatusSuccess;
char ch = 0;
+ if (m_terminal_size_has_changed)
+ ApplyTerminalSizeChange();
+
// This mutex is locked by our caller (GetLine). Unlock it while we read a
// character (blocking operation), so we do not hold the mutex
// indefinitely. This gives a chance for someone to interrupt us. After
m_editline =
el_init(m_editor_name.c_str(), m_input_file, m_output_file, m_error_file);
- TerminalSizeChanged();
+ ApplyTerminalSizeChange();
if (m_history_sp && m_history_sp->IsValid()) {
if (!m_history_sp->Load()) {
continuation_prompt == nullptr ? "" : continuation_prompt;
}
-void Editline::TerminalSizeChanged() {
- if (m_editline != nullptr) {
- el_resize(m_editline);
- int columns;
- // This function is documenting as taking (const char *, void *) for the
- // vararg part, but in reality in was consuming arguments until the first
- // null pointer. This was fixed in libedit in April 2019
- // <http://mail-index.netbsd.org/source-changes/2019/04/26/msg105454.html>,
- // but we're keeping the workaround until a version with that fix is more
- // widely available.
- if (el_get(m_editline, EL_GETTC, "co", &columns, nullptr) == 0) {
- m_terminal_width = columns;
- if (m_current_line_rows != -1) {
- const LineInfoW *info = el_wline(m_editline);
- int lineLength =
- (int)((info->lastchar - info->buffer) + GetPromptWidth());
- m_current_line_rows = (lineLength / columns) + 1;
- }
- } else {
- m_terminal_width = INT_MAX;
- m_current_line_rows = 1;
+void Editline::TerminalSizeChanged() { m_terminal_size_has_changed = 1; }
+
+void Editline::ApplyTerminalSizeChange() {
+ if (!m_editline)
+ return;
+
+ m_terminal_size_has_changed = 0;
+ el_resize(m_editline);
+ int columns;
+ // This function is documenting as taking (const char *, void *) for the
+ // vararg part, but in reality in was consuming arguments until the first
+ // null pointer. This was fixed in libedit in April 2019
+ // <http://mail-index.netbsd.org/source-changes/2019/04/26/msg105454.html>,
+ // but we're keeping the workaround until a version with that fix is more
+ // widely available.
+ if (el_get(m_editline, EL_GETTC, "co", &columns, nullptr) == 0) {
+ m_terminal_width = columns;
+ if (m_current_line_rows != -1) {
+ const LineInfoW *info = el_wline(m_editline);
+ int lineLength =
+ (int)((info->lastchar - info->buffer) + GetPromptWidth());
+ m_current_line_rows = (lineLength / columns) + 1;
}
+ } else {
+ m_terminal_width = INT_MAX;
+ m_current_line_rows = 1;
}
}
--- /dev/null
+"""
+Test resizing in our IOHandlers.
+"""
+
+import os
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.lldbpexpect import PExpectTest
+
+class IOHandlerCompletionTest(PExpectTest):
+
+ mydir = TestBase.compute_mydir(__file__)
+
+ # PExpect uses many timeouts internally and doesn't play well
+ # under ASAN on a loaded machine..
+ @skipIfAsan
+ @skipIfEditlineSupportMissing
+ def test_resize(self):
+
+ # Start with a small window
+ self.launch(dimensions=(10,10))
+
+ self.child.send("his is a long sentence missing its first letter.")
+
+ # Now resize to something bigger
+ self.child.setwinsize(100,500)
+
+ # Hit "left" 60 times (to go to the beginning of the line) and insert
+ # a character.
+ self.child.send(60 * "\033[D")
+ self.child.send("T")
+
+ self.child.expect_exact("(lldb) This is a long sentence missing its first letter.")
+ self.quit()