aboutsummaryrefslogtreecommitdiff
path: root/source/Host/common/Editline.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/Host/common/Editline.cpp')
-rw-r--r--source/Host/common/Editline.cpp246
1 files changed, 154 insertions, 92 deletions
diff --git a/source/Host/common/Editline.cpp b/source/Host/common/Editline.cpp
index 4640154c6cb1..d7209feb46db 100644
--- a/source/Host/common/Editline.cpp
+++ b/source/Host/common/Editline.cpp
@@ -19,7 +19,6 @@
#include "lldb/Host/FileSpec.h"
#include "lldb/Host/FileSystem.h"
#include "lldb/Host/Host.h"
-#include "lldb/Host/Mutex.h"
#include "lldb/Utility/LLDBAssert.h"
using namespace lldb_private;
@@ -227,9 +226,9 @@ namespace lldb_private
GetHistory (const std::string &prefix)
{
typedef std::map<std::string, EditlineHistoryWP> WeakHistoryMap;
- static Mutex g_mutex (Mutex::eMutexTypeRecursive);
+ static std::recursive_mutex g_mutex;
static WeakHistoryMap g_weak_map;
- Mutex::Locker locker (g_mutex);
+ std::lock_guard<std::recursive_mutex> guard(g_mutex);
WeakHistoryMap::const_iterator pos = g_weak_map.find (prefix);
EditlineHistorySP history_sp;
if (pos != g_weak_map.end())
@@ -587,9 +586,9 @@ Editline::GetCharacter (EditLineCharType * c)
// (blocking operation), so we do not hold the mutex indefinitely. This gives a chance
// for someone to interrupt us. After Read returns, immediately lock the mutex again and
// check if we were interrupted.
- m_output_mutex.Unlock();
+ m_output_mutex.unlock();
int read_count = m_input_connection.Read(&ch, 1, UINT32_MAX, status, NULL);
- m_output_mutex.Lock();
+ m_output_mutex.lock();
if (m_editor_status == EditorStatus::Interrupted)
{
while (read_count > 0 && status == lldb::eConnectionStatusSuccess)
@@ -658,41 +657,9 @@ Editline::BreakLineCommand (int ch)
// Establish the new cursor position at the start of a line when inserting a line break
m_revert_cursor_index = 0;
- // Don't perform end of input detection or automatic formatting when pasting
+ // Don't perform automatic formatting when pasting
if (!IsInputPending (m_input_file))
{
- // If this is the end of the last line, treat this as a potential exit
- if (m_current_line_index == m_input_lines.size() - 1 && new_line_fragment.length() == 0)
- {
- bool end_of_input = true;
- if (m_is_input_complete_callback)
- {
- SaveEditedLine();
- auto lines = GetInputAsStringList();
- end_of_input = m_is_input_complete_callback (this, lines, m_is_input_complete_callback_baton);
-
- // The completion test is allowed to change the input lines when complete
- if (end_of_input)
- {
- m_input_lines.clear();
- for (unsigned index = 0; index < lines.GetSize(); index++)
- {
-#if LLDB_EDITLINE_USE_WCHAR
- m_input_lines.insert (m_input_lines.end(), m_utf8conv.from_bytes (lines[index]));
-#else
- m_input_lines.insert (m_input_lines.end(), lines[index]);
-#endif
- }
- }
- }
- if (end_of_input)
- {
- fprintf (m_output_file, "\n");
- m_editor_status = EditorStatus::Complete;
- return CC_NEWLINE;
- }
- }
-
// Apply smart indentation
if (m_fix_indentation_callback)
{
@@ -721,7 +688,49 @@ Editline::BreakLineCommand (int ch)
}
unsigned char
-Editline::DeleteNextCharCommand (int ch)
+Editline::EndOrAddLineCommand(int ch)
+{
+ // Don't perform end of input detection when pasting, always treat this as a line break
+ if (IsInputPending(m_input_file))
+ {
+ return BreakLineCommand(ch);
+ }
+
+ // Save any edits to this line
+ SaveEditedLine();
+
+ // If this is the end of the last line, consider whether to add a line instead
+ const LineInfoW *info = el_wline(m_editline);
+ if (m_current_line_index == m_input_lines.size() - 1 && info->cursor == info->lastchar)
+ {
+ if (m_is_input_complete_callback)
+ {
+ auto lines = GetInputAsStringList();
+ if (!m_is_input_complete_callback(this, lines, m_is_input_complete_callback_baton))
+ {
+ return BreakLineCommand(ch);
+ }
+
+ // The completion test is allowed to change the input lines when complete
+ m_input_lines.clear();
+ for (unsigned index = 0; index < lines.GetSize(); index++)
+ {
+#if LLDB_EDITLINE_USE_WCHAR
+ m_input_lines.insert(m_input_lines.end(), m_utf8conv.from_bytes(lines[index]));
+#else
+ m_input_lines.insert(m_input_lines.end(), lines[index]);
+#endif
+ }
+ }
+ }
+ MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockEnd);
+ fprintf(m_output_file, "\n");
+ m_editor_status = EditorStatus::Complete;
+ return CC_NEWLINE;
+}
+
+unsigned char
+Editline::DeleteNextCharCommand(int ch)
{
LineInfoW * info = const_cast<LineInfoW *>(el_wline (m_editline));
@@ -861,7 +870,23 @@ Editline::NextLineCommand (int ch)
}
unsigned char
-Editline::FixIndentationCommand (int ch)
+Editline::PreviousHistoryCommand(int ch)
+{
+ SaveEditedLine();
+
+ return RecallHistory(true);
+}
+
+unsigned char
+Editline::NextHistoryCommand(int ch)
+{
+ SaveEditedLine();
+
+ return RecallHistory(false);
+}
+
+unsigned char
+Editline::FixIndentationCommand(int ch)
{
if (!m_fix_indentation_callback)
return CC_NORM;
@@ -1075,36 +1100,46 @@ Editline::ConfigureEditor (bool multiline)
}));
// Commands used for multiline support, registered whether or not they're used
- el_set (m_editline, EL_ADDFN, "lldb-break-line", "Insert a line break",
- (EditlineCommandCallbackType)([] (EditLine * editline, int ch) {
- return Editline::InstanceFor (editline)->BreakLineCommand (ch);
- }));
- el_set (m_editline, EL_ADDFN, "lldb-delete-next-char", "Delete next character",
- (EditlineCommandCallbackType)([] (EditLine * editline, int ch) {
- return Editline::InstanceFor (editline)->DeleteNextCharCommand (ch);
- }));
- el_set (m_editline, EL_ADDFN, "lldb-delete-previous-char", "Delete previous character",
- (EditlineCommandCallbackType)([] (EditLine * editline, int ch) {
- return Editline::InstanceFor (editline)->DeletePreviousCharCommand (ch);
- }));
- el_set (m_editline, EL_ADDFN, "lldb-previous-line", "Move to previous line",
- (EditlineCommandCallbackType)([] (EditLine * editline, int ch) {
- return Editline::InstanceFor (editline)->PreviousLineCommand (ch);
- }));
- el_set (m_editline, EL_ADDFN, "lldb-next-line", "Move to next line",
- (EditlineCommandCallbackType)([] (EditLine * editline, int ch) {
- return Editline::InstanceFor (editline)->NextLineCommand (ch);
- }));
- el_set (m_editline, EL_ADDFN, "lldb-buffer-start", "Move to start of buffer",
- (EditlineCommandCallbackType)([] (EditLine * editline, int ch) {
- return Editline::InstanceFor (editline)->BufferStartCommand (ch);
- }));
- el_set (m_editline, EL_ADDFN, "lldb-buffer-end", "Move to end of buffer",
- (EditlineCommandCallbackType)([] (EditLine * editline, int ch) {
- return Editline::InstanceFor (editline)->BufferEndCommand (ch);
+ el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-break-line"), EditLineConstString("Insert a line break"),
+ (EditlineCommandCallbackType)(
+ [](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->BreakLineCommand(ch); }));
+ el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-end-or-add-line"),
+ EditLineConstString("End editing or continue when incomplete"),
+ (EditlineCommandCallbackType)(
+ [](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->EndOrAddLineCommand(ch); }));
+ el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-delete-next-char"),
+ EditLineConstString("Delete next character"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
+ return Editline::InstanceFor(editline)->DeleteNextCharCommand(ch);
+ }));
+ el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-delete-previous-char"),
+ EditLineConstString("Delete previous character"),
+ (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
+ return Editline::InstanceFor(editline)->DeletePreviousCharCommand(ch);
}));
- el_set (m_editline, EL_ADDFN, "lldb-fix-indentation", "Fix line indentation",
- (EditlineCommandCallbackType)([] (EditLine * editline, int ch) {
+ el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-previous-line"),
+ EditLineConstString("Move to previous line"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
+ return Editline::InstanceFor(editline)->PreviousLineCommand(ch);
+ }));
+ el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-next-line"), EditLineConstString("Move to next line"),
+ (EditlineCommandCallbackType)(
+ [](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->NextLineCommand(ch); }));
+ el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-previous-history"),
+ EditLineConstString("Move to previous history"),
+ (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
+ return Editline::InstanceFor(editline)->PreviousHistoryCommand(ch);
+ }));
+ el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-next-history"), EditLineConstString("Move to next history"),
+ (EditlineCommandCallbackType)(
+ [](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->NextHistoryCommand(ch); }));
+ el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-buffer-start"),
+ EditLineConstString("Move to start of buffer"),
+ (EditlineCommandCallbackType)(
+ [](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->BufferStartCommand(ch); }));
+ el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-buffer-end"), EditLineConstString("Move to end of buffer"),
+ (EditlineCommandCallbackType)(
+ [](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->BufferEndCommand(ch); }));
+ el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-fix-indentation"),
+ EditLineConstString("Fix line indentation"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
return Editline::InstanceFor (editline)->FixIndentationCommand (ch);
}));
@@ -1115,9 +1150,11 @@ Editline::ConfigureEditor (bool multiline)
EditlineCommandCallbackType complete_callback = [] (EditLine * editline, int ch) {
return Editline::InstanceFor (editline)->TabCommand (ch);
};
- el_set (m_editline, EL_ADDFN, "lldb-complete", "Invoke completion", complete_callback);
- el_set (m_editline, EL_ADDFN, "lldb_complete", "Invoke completion", complete_callback);
-
+ el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-complete"), EditLineConstString("Invoke completion"),
+ complete_callback);
+ el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb_complete"), EditLineConstString("Invoke completion"),
+ complete_callback);
+
// General bindings we don't mind being overridden
if (!multiline) {
el_set (m_editline, EL_BIND, "^r", "em-inc-search-prev", NULL); // Cycle through backwards search, entering string
@@ -1129,10 +1166,10 @@ Editline::ConfigureEditor (bool multiline)
el_source (m_editline, NULL);
// Register an internal binding that external developers shouldn't use
- el_set (m_editline, EL_ADDFN, "lldb-revert-line", "Revert line to saved state",
- (EditlineCommandCallbackType)([] (EditLine * editline, int ch) {
- return Editline::InstanceFor (editline)->RevertLineCommand (ch);
- }));
+ el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-revert-line"),
+ EditLineConstString("Revert line to saved state"),
+ (EditlineCommandCallbackType)(
+ [](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->RevertLineCommand(ch); }));
// Register keys that perform auto-indent correction
if (m_fix_indentation_callback && m_fix_indentation_callback_chars)
@@ -1150,8 +1187,10 @@ Editline::ConfigureEditor (bool multiline)
// Multi-line editor bindings
if (multiline)
{
- el_set (m_editline, EL_BIND, "\n", "lldb-break-line", NULL);
- el_set (m_editline, EL_BIND, "\r", "lldb-break-line", NULL);
+ el_set(m_editline, EL_BIND, "\n", "lldb-end-or-add-line", NULL);
+ el_set(m_editline, EL_BIND, "\r", "lldb-end-or-add-line", NULL);
+ el_set(m_editline, EL_BIND, ESCAPE "\n", "lldb-break-line", NULL);
+ el_set(m_editline, EL_BIND, ESCAPE "\r", "lldb-break-line", NULL);
el_set (m_editline, EL_BIND, "^p", "lldb-previous-line", NULL);
el_set (m_editline, EL_BIND, "^n", "lldb-next-line", NULL);
el_set (m_editline, EL_BIND, "^?", "lldb-delete-previous-char", NULL);
@@ -1166,6 +1205,10 @@ Editline::ConfigureEditor (bool multiline)
el_set (m_editline, EL_BIND, ESCAPE ">", "lldb-buffer-end", NULL);
el_set (m_editline, EL_BIND, ESCAPE "[A", "lldb-previous-line", NULL);
el_set (m_editline, EL_BIND, ESCAPE "[B", "lldb-next-line", NULL);
+ el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[A", "lldb-previous-history", NULL);
+ el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[B", "lldb-next-history", NULL);
+ el_set(m_editline, EL_BIND, ESCAPE "[1;3A", "lldb-previous-history", NULL);
+ el_set(m_editline, EL_BIND, ESCAPE "[1;3B", "lldb-next-history", NULL);
}
else
{
@@ -1209,6 +1252,32 @@ Editline::Editline (const char * editline_name, FILE * input_file, FILE * output
// Get a shared history instance
m_editor_name = (editline_name == nullptr) ? "lldb-tmp" : editline_name;
m_history_sp = EditlineHistory::GetHistory (m_editor_name);
+
+#ifdef USE_SETUPTERM_WORKAROUND
+ if (m_output_file)
+ {
+ const int term_fd = fileno(m_output_file);
+ if (term_fd != -1)
+ {
+ static std::mutex *g_init_terminal_fds_mutex_ptr = nullptr;
+ static std::set<int> *g_init_terminal_fds_ptr = nullptr;
+ static std::once_flag g_once_flag;
+ std::call_once(g_once_flag, [&]() {
+ g_init_terminal_fds_mutex_ptr = new std::mutex(); // NOTE: Leak to avoid C++ destructor chain issues
+ g_init_terminal_fds_ptr = new std::set<int>(); // NOTE: Leak to avoid C++ destructor chain issues
+ });
+
+ // We must make sure to initialize the terminal a given file descriptor
+ // only once. If we do this multiple times, we start leaking memory.
+ std::lock_guard<std::mutex> guard(*g_init_terminal_fds_mutex_ptr);
+ if (g_init_terminal_fds_ptr->find(term_fd) == g_init_terminal_fds_ptr->end())
+ {
+ g_init_terminal_fds_ptr->insert(term_fd);
+ setupterm((char *)0, term_fd, (int *)0);
+ }
+ }
+ }
+#endif
}
Editline::~Editline()
@@ -1284,7 +1353,7 @@ bool
Editline::Interrupt()
{
bool result = true;
- Mutex::Locker locker(m_output_mutex);
+ std::lock_guard<std::mutex> guard(m_output_mutex);
if (m_editor_status == EditorStatus::Editing) {
fprintf(m_output_file, "^C\n");
result = m_input_connection.InterruptRead();
@@ -1297,7 +1366,7 @@ bool
Editline::Cancel()
{
bool result = true;
- Mutex::Locker locker(m_output_mutex);
+ std::lock_guard<std::mutex> guard(m_output_mutex);
if (m_editor_status == EditorStatus::Editing) {
MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
fprintf(m_output_file, ANSI_CLEAR_BELOW);
@@ -1338,8 +1407,8 @@ Editline::GetLine (std::string &line, bool &interrupted)
ConfigureEditor (false);
m_input_lines = std::vector<EditLineStringType>();
m_input_lines.insert (m_input_lines.begin(), EditLineConstString(""));
-
- Mutex::Locker locker(m_output_mutex);
+
+ std::lock_guard<std::mutex> guard(m_output_mutex);
lldbassert(m_editor_status != EditorStatus::Editing);
if (m_editor_status == EditorStatus::Interrupted)
@@ -1354,10 +1423,6 @@ Editline::GetLine (std::string &line, bool &interrupted)
m_editor_status = EditorStatus::Editing;
m_revert_cursor_index = -1;
-#ifdef USE_SETUPTERM_WORKAROUND
- setupterm((char *)0, fileno(m_output_file), (int *)0);
-#endif
-
int count;
auto input = el_wgets (m_editline, &count);
@@ -1392,8 +1457,8 @@ Editline::GetLines (int first_line_number, StringList &lines, bool &interrupted)
SetBaseLineNumber (first_line_number);
m_input_lines = std::vector<EditLineStringType>();
m_input_lines.insert (m_input_lines.begin(), EditLineConstString(""));
-
- Mutex::Locker locker(m_output_mutex);
+
+ std::lock_guard<std::mutex> guard(m_output_mutex);
// Begin the line editing loop
DisplayInput();
SetCurrentLine (0);
@@ -1404,9 +1469,6 @@ Editline::GetLines (int first_line_number, StringList &lines, bool &interrupted)
m_revert_cursor_index = -1;
while (m_editor_status == EditorStatus::Editing)
{
-#ifdef USE_SETUPTERM_WORKAROUND
- setupterm((char *)0, fileno(m_output_file), (int *)0);
-#endif
int count;
m_current_line_rows = -1;
el_wpush (m_editline, EditLineConstString("\x1b[^")); // Revert to the existing line content
@@ -1427,7 +1489,7 @@ Editline::GetLines (int first_line_number, StringList &lines, bool &interrupted)
void
Editline::PrintAsync (Stream *stream, const char *s, size_t len)
{
- Mutex::Locker locker(m_output_mutex);
+ std::lock_guard<std::mutex> guard(m_output_mutex);
if (m_editor_status == EditorStatus::Editing)
{
MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);