1 //===-- Editline.cpp --------------------------------------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
14 #include "lldb/Host/ConnectionFileDescriptor.h"
15 #include "lldb/Host/Editline.h"
16 #include "lldb/Host/Host.h"
17 #include "lldb/Utility/Error.h"
18 #include "lldb/Utility/FileSpec.h"
19 #include "lldb/Utility/LLDBAssert.h"
20 #include "lldb/Utility/SelectHelper.h"
21 #include "lldb/Utility/StreamString.h"
22 #include "lldb/Utility/StringList.h"
23 #include "lldb/Utility/Timeout.h"
25 #include "llvm/Support/FileSystem.h"
26 #include "llvm/Support/Threading.h"
28 using namespace lldb_private;
29 using namespace lldb_private::line_editor;
31 // Workaround for what looks like an OS X-specific issue, but other platforms
32 // may benefit from something similar if issues arise. The libedit library
33 // doesn't explicitly initialize the curses termcap library, which it gets away
34 // with until TERM is set to VT100 where it stumbles over an implementation
35 // assumption that may not exist on other platforms. The setupterm() function
36 // would normally require headers that don't work gracefully in this context, so
37 // the function declaraction has been hoisted here.
38 #if defined(__APPLE__)
40 int setupterm(char *term, int fildes, int *errret);
42 #define USE_SETUPTERM_WORKAROUND
45 // Editline uses careful cursor management to achieve the illusion of editing a
46 // multi-line block of text
47 // with a single line editor. Preserving this illusion requires fairly careful
48 // management of cursor
49 // state. Read and understand the relationship between DisplayInput(),
50 // MoveCursor(), SetCurrentLine(),
51 // and SaveEditedLine() before making changes.
54 #define ANSI_FAINT ESCAPE "[2m"
55 #define ANSI_UNFAINT ESCAPE "[22m"
56 #define ANSI_CLEAR_BELOW ESCAPE "[J"
57 #define ANSI_CLEAR_RIGHT ESCAPE "[K"
58 #define ANSI_SET_COLUMN_N ESCAPE "[%dG"
59 #define ANSI_UP_N_ROWS ESCAPE "[%dA"
60 #define ANSI_DOWN_N_ROWS ESCAPE "[%dB"
62 #if LLDB_EDITLINE_USE_WCHAR
64 #define EditLineConstString(str) L##str
65 #define EditLineStringFormatSpec "%ls"
69 #define EditLineConstString(str) str
70 #define EditLineStringFormatSpec "%s"
72 // use #defines so wide version functions and structs will resolve to old
74 // for case of libedit not built with wide char support
75 #define history_w history
76 #define history_winit history_init
77 #define history_wend history_end
78 #define HistoryW History
79 #define HistEventW HistEvent
80 #define LineInfoW LineInfo
82 #define el_wgets el_gets
83 #define el_wgetc el_getc
84 #define el_wpush el_push
85 #define el_wparse el_parse
86 #define el_wset el_set
87 #define el_wget el_get
88 #define el_wline el_line
89 #define el_winsertstr el_insertstr
90 #define el_wdeletestr el_deletestr
92 #endif // #if LLDB_EDITLINE_USE_WCHAR
94 bool IsOnlySpaces(const EditLineStringType &content) {
95 for (wchar_t ch : content) {
96 if (ch != EditLineCharType(' '))
102 EditLineStringType CombineLines(const std::vector<EditLineStringType> &lines) {
103 EditLineStringStreamType combined_stream;
104 for (EditLineStringType line : lines) {
105 combined_stream << line.c_str() << "\n";
107 return combined_stream.str();
110 std::vector<EditLineStringType> SplitLines(const EditLineStringType &input) {
111 std::vector<EditLineStringType> result;
113 while (start < input.length()) {
114 size_t end = input.find('\n', start);
115 if (end == std::string::npos) {
116 result.insert(result.end(), input.substr(start));
119 result.insert(result.end(), input.substr(start, end - start));
125 EditLineStringType FixIndentation(const EditLineStringType &line,
126 int indent_correction) {
127 if (indent_correction == 0)
129 if (indent_correction < 0)
130 return line.substr(-indent_correction);
131 return EditLineStringType(indent_correction, EditLineCharType(' ')) + line;
134 int GetIndentation(const EditLineStringType &line) {
136 for (EditLineCharType ch : line) {
137 if (ch != EditLineCharType(' '))
144 bool IsInputPending(FILE *file) {
145 // FIXME: This will be broken on Windows if we ever re-enable Editline. You
147 // on something that isn't a socket. This will have to be re-written to not
149 // instead use some kind of yet-to-be-created abstraction that select-like
151 // non-socket objects.
152 const int fd = fileno(file);
153 SelectHelper select_helper;
154 select_helper.SetTimeout(std::chrono::microseconds(0));
155 select_helper.FDSetRead(fd);
156 return select_helper.Select().Success();
159 namespace lldb_private {
160 namespace line_editor {
161 typedef std::weak_ptr<EditlineHistory> EditlineHistoryWP;
163 // EditlineHistory objects are sometimes shared between multiple
164 // Editline instances with the same program name.
166 class EditlineHistory {
168 // Use static GetHistory() function to get a EditlineHistorySP to one of these
170 EditlineHistory(const std::string &prefix, uint32_t size, bool unique_entries)
171 : m_history(NULL), m_event(), m_prefix(prefix), m_path() {
172 m_history = history_winit();
173 history_w(m_history, &m_event, H_SETSIZE, size);
175 history_w(m_history, &m_event, H_SETUNIQUE, 1);
178 const char *GetHistoryFilePath() {
179 if (m_path.empty() && m_history && !m_prefix.empty()) {
180 FileSpec parent_path{"~/.lldb", true};
181 char history_path[PATH_MAX];
182 if (!llvm::sys::fs::create_directory(parent_path.GetPath())) {
183 snprintf(history_path, sizeof(history_path), "~/.lldb/%s-history",
186 snprintf(history_path, sizeof(history_path), "~/%s-widehistory",
189 m_path = FileSpec(history_path, true).GetPath();
193 return m_path.c_str();
201 history_wend(m_history);
206 static EditlineHistorySP GetHistory(const std::string &prefix) {
207 typedef std::map<std::string, EditlineHistoryWP> WeakHistoryMap;
208 static std::recursive_mutex g_mutex;
209 static WeakHistoryMap g_weak_map;
210 std::lock_guard<std::recursive_mutex> guard(g_mutex);
211 WeakHistoryMap::const_iterator pos = g_weak_map.find(prefix);
212 EditlineHistorySP history_sp;
213 if (pos != g_weak_map.end()) {
214 history_sp = pos->second.lock();
217 g_weak_map.erase(pos);
219 history_sp.reset(new EditlineHistory(prefix, 800, true));
220 g_weak_map[prefix] = history_sp;
224 bool IsValid() const { return m_history != NULL; }
226 HistoryW *GetHistoryPtr() { return m_history; }
228 void Enter(const EditLineCharType *line_cstr) {
230 history_w(m_history, &m_event, H_ENTER, line_cstr);
235 const char *path = GetHistoryFilePath();
237 history_w(m_history, &m_event, H_LOAD, path);
246 const char *path = GetHistoryFilePath();
248 history_w(m_history, &m_event, H_SAVE, path);
256 HistoryW *m_history; // The history object
257 HistEventW m_event; // The history event needed to contain all history events
258 std::string m_prefix; // The prefix name (usually the editline program name)
259 // to use when loading/saving history
260 std::string m_path; // Path to the history file
265 //------------------------------------------------------------------
266 // Editline private methods
267 //------------------------------------------------------------------
269 void Editline::SetBaseLineNumber(int line_number) {
270 std::stringstream line_number_stream;
271 line_number_stream << line_number;
272 m_base_line_number = line_number;
273 m_line_number_digits =
274 std::max(3, (int)line_number_stream.str().length() + 1);
277 std::string Editline::PromptForIndex(int line_index) {
278 bool use_line_numbers = m_multiline_enabled && m_base_line_number > 0;
279 std::string prompt = m_set_prompt;
280 if (use_line_numbers && prompt.length() == 0) {
283 std::string continuation_prompt = prompt;
284 if (m_set_continuation_prompt.length() > 0) {
285 continuation_prompt = m_set_continuation_prompt;
287 // Ensure that both prompts are the same length through space padding
288 while (continuation_prompt.length() < prompt.length()) {
289 continuation_prompt += ' ';
291 while (prompt.length() < continuation_prompt.length()) {
296 if (use_line_numbers) {
297 StreamString prompt_stream;
298 prompt_stream.Printf(
299 "%*d%s", m_line_number_digits, m_base_line_number + line_index,
300 (line_index == 0) ? prompt.c_str() : continuation_prompt.c_str());
301 return std::move(prompt_stream.GetString());
303 return (line_index == 0) ? prompt : continuation_prompt;
306 void Editline::SetCurrentLine(int line_index) {
307 m_current_line_index = line_index;
308 m_current_prompt = PromptForIndex(line_index);
311 int Editline::GetPromptWidth() { return (int)PromptForIndex(0).length(); }
313 bool Editline::IsEmacs() {
315 el_get(m_editline, EL_EDITOR, &editor);
316 return editor[0] == 'e';
319 bool Editline::IsOnlySpaces() {
320 const LineInfoW *info = el_wline(m_editline);
321 for (const EditLineCharType *character = info->buffer;
322 character < info->lastchar; character++) {
323 if (*character != ' ')
329 int Editline::GetLineIndexForLocation(CursorLocation location, int cursor_row) {
331 if (location == CursorLocation::EditingPrompt ||
332 location == CursorLocation::BlockEnd ||
333 location == CursorLocation::EditingCursor) {
334 for (unsigned index = 0; index < m_current_line_index; index++) {
335 line += CountRowsForLine(m_input_lines[index]);
337 if (location == CursorLocation::EditingCursor) {
339 } else if (location == CursorLocation::BlockEnd) {
340 for (unsigned index = m_current_line_index; index < m_input_lines.size();
342 line += CountRowsForLine(m_input_lines[index]);
350 void Editline::MoveCursor(CursorLocation from, CursorLocation to) {
351 const LineInfoW *info = el_wline(m_editline);
352 int editline_cursor_position =
353 (int)((info->cursor - info->buffer) + GetPromptWidth());
354 int editline_cursor_row = editline_cursor_position / m_terminal_width;
356 // Determine relative starting and ending lines
357 int fromLine = GetLineIndexForLocation(from, editline_cursor_row);
358 int toLine = GetLineIndexForLocation(to, editline_cursor_row);
359 if (toLine != fromLine) {
360 fprintf(m_output_file,
361 (toLine > fromLine) ? ANSI_DOWN_N_ROWS : ANSI_UP_N_ROWS,
362 std::abs(toLine - fromLine));
365 // Determine target column
367 if (to == CursorLocation::EditingCursor) {
369 editline_cursor_position - (editline_cursor_row * m_terminal_width) + 1;
370 } else if (to == CursorLocation::BlockEnd) {
372 ((m_input_lines[m_input_lines.size() - 1].length() + GetPromptWidth()) %
376 fprintf(m_output_file, ANSI_SET_COLUMN_N, toColumn);
379 void Editline::DisplayInput(int firstIndex) {
380 fprintf(m_output_file, ANSI_SET_COLUMN_N ANSI_CLEAR_BELOW, 1);
381 int line_count = (int)m_input_lines.size();
382 const char *faint = m_color_prompts ? ANSI_FAINT : "";
383 const char *unfaint = m_color_prompts ? ANSI_UNFAINT : "";
385 for (int index = firstIndex; index < line_count; index++) {
386 fprintf(m_output_file, "%s"
388 "%s" EditLineStringFormatSpec " ",
389 faint, PromptForIndex(index).c_str(), unfaint,
390 m_input_lines[index].c_str());
391 if (index < line_count - 1)
392 fprintf(m_output_file, "\n");
396 int Editline::CountRowsForLine(const EditLineStringType &content) {
398 PromptForIndex(0); // Prompt width is constant during an edit session
399 int line_length = (int)(content.length() + prompt.length());
400 return (line_length / m_terminal_width) + 1;
403 void Editline::SaveEditedLine() {
404 const LineInfoW *info = el_wline(m_editline);
405 m_input_lines[m_current_line_index] =
406 EditLineStringType(info->buffer, info->lastchar - info->buffer);
409 StringList Editline::GetInputAsStringList(int line_count) {
411 for (EditLineStringType line : m_input_lines) {
414 #if LLDB_EDITLINE_USE_WCHAR
415 lines.AppendString(m_utf8conv.to_bytes(line));
417 lines.AppendString(line);
424 unsigned char Editline::RecallHistory(bool earlier) {
425 if (!m_history_sp || !m_history_sp->IsValid())
428 HistoryW *pHistory = m_history_sp->GetHistoryPtr();
429 HistEventW history_event;
430 std::vector<EditLineStringType> new_input_lines;
432 // Treat moving from the "live" entry differently
434 if (earlier == false)
435 return CC_ERROR; // Can't go newer than the "live" entry
436 if (history_w(pHistory, &history_event, H_FIRST) == -1)
439 // Save any edits to the "live" entry in case we return by moving forward in
441 // (it would be more bash-like to save over any current entry, but libedit
443 // offer the ability to add entries anywhere except the end.)
445 m_live_history_lines = m_input_lines;
448 if (history_w(pHistory, &history_event, earlier ? H_NEXT : H_PREV) == -1) {
449 // Can't move earlier than the earliest entry
453 // ... but moving to newer than the newest yields the "live" entry
454 new_input_lines = m_live_history_lines;
455 m_in_history = false;
459 // If we're pulling the lines from history, split them apart
461 new_input_lines = SplitLines(history_event.str);
463 // Erase the current edit session and replace it with a new one
464 MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
465 m_input_lines = new_input_lines;
468 // Prepare to edit the last line when moving to previous entry, or the first
470 // when moving to next entry
471 SetCurrentLine(m_current_line_index =
472 earlier ? (int)m_input_lines.size() - 1 : 0);
473 MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
477 int Editline::GetCharacter(EditLineCharType *c) {
478 const LineInfoW *info = el_wline(m_editline);
480 // Paint a faint version of the desired prompt over the version libedit draws
481 // (will only be requested if colors are supported)
482 if (m_needs_prompt_repaint) {
483 MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
484 fprintf(m_output_file, "%s"
487 ANSI_FAINT, Prompt(), ANSI_UNFAINT);
488 MoveCursor(CursorLocation::EditingPrompt, CursorLocation::EditingCursor);
489 m_needs_prompt_repaint = false;
492 if (m_multiline_enabled) {
493 // Detect when the number of rows used for this input line changes due to an
495 int lineLength = (int)((info->lastchar - info->buffer) + GetPromptWidth());
496 int new_line_rows = (lineLength / m_terminal_width) + 1;
497 if (m_current_line_rows != -1 && new_line_rows != m_current_line_rows) {
498 // Respond by repainting the current state from this line on
499 MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
501 DisplayInput(m_current_line_index);
502 MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
504 m_current_line_rows = new_line_rows;
507 // Read an actual character
509 lldb::ConnectionStatus status = lldb::eConnectionStatusSuccess;
512 // This mutex is locked by our caller (GetLine). Unlock it while we read a
514 // (blocking operation), so we do not hold the mutex indefinitely. This
516 // for someone to interrupt us. After Read returns, immediately lock the
518 // check if we were interrupted.
519 m_output_mutex.unlock();
520 int read_count = m_input_connection.Read(&ch, 1, llvm::None, status, NULL);
521 m_output_mutex.lock();
522 if (m_editor_status == EditorStatus::Interrupted) {
523 while (read_count > 0 && status == lldb::eConnectionStatusSuccess)
524 read_count = m_input_connection.Read(&ch, 1, llvm::None, status, NULL);
525 lldbassert(status == lldb::eConnectionStatusInterrupted);
530 if (CompleteCharacter(ch, *c))
534 case lldb::eConnectionStatusSuccess: // Success
537 case lldb::eConnectionStatusInterrupted:
538 lldbassert(0 && "Interrupts should have been handled above.");
540 case lldb::eConnectionStatusError: // Check GetError() for details
541 case lldb::eConnectionStatusTimedOut: // Request timed out
542 case lldb::eConnectionStatusEndOfFile: // End-of-file encountered
543 case lldb::eConnectionStatusNoConnection: // No connection
544 case lldb::eConnectionStatusLostConnection: // Lost connection while
545 // connected to a valid
547 m_editor_status = EditorStatus::EndOfInput;
554 const char *Editline::Prompt() {
556 m_needs_prompt_repaint = true;
557 return m_current_prompt.c_str();
560 unsigned char Editline::BreakLineCommand(int ch) {
561 // Preserve any content beyond the cursor, truncate and save the current line
562 const LineInfoW *info = el_wline(m_editline);
564 EditLineStringType(info->buffer, info->cursor - info->buffer);
565 auto new_line_fragment =
566 EditLineStringType(info->cursor, info->lastchar - info->cursor);
567 m_input_lines[m_current_line_index] = current_line;
569 // Ignore whitespace-only extra fragments when breaking a line
570 if (::IsOnlySpaces(new_line_fragment))
571 new_line_fragment = EditLineConstString("");
573 // Establish the new cursor position at the start of a line when inserting a
575 m_revert_cursor_index = 0;
577 // Don't perform automatic formatting when pasting
578 if (!IsInputPending(m_input_file)) {
579 // Apply smart indentation
580 if (m_fix_indentation_callback) {
581 StringList lines = GetInputAsStringList(m_current_line_index + 1);
582 #if LLDB_EDITLINE_USE_WCHAR
583 lines.AppendString(m_utf8conv.to_bytes(new_line_fragment));
585 lines.AppendString(new_line_fragment);
588 int indent_correction = m_fix_indentation_callback(
589 this, lines, 0, m_fix_indentation_callback_baton);
590 new_line_fragment = FixIndentation(new_line_fragment, indent_correction);
591 m_revert_cursor_index = GetIndentation(new_line_fragment);
595 // Insert the new line and repaint everything from the split line on down
596 m_input_lines.insert(m_input_lines.begin() + m_current_line_index + 1,
598 MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
599 DisplayInput(m_current_line_index);
601 // Reposition the cursor to the right line and prepare to edit the new line
602 SetCurrentLine(m_current_line_index + 1);
603 MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
607 unsigned char Editline::EndOrAddLineCommand(int ch) {
608 // Don't perform end of input detection when pasting, always treat this as a
610 if (IsInputPending(m_input_file)) {
611 return BreakLineCommand(ch);
614 // Save any edits to this line
617 // If this is the end of the last line, consider whether to add a line instead
618 const LineInfoW *info = el_wline(m_editline);
619 if (m_current_line_index == m_input_lines.size() - 1 &&
620 info->cursor == info->lastchar) {
621 if (m_is_input_complete_callback) {
622 auto lines = GetInputAsStringList();
623 if (!m_is_input_complete_callback(this, lines,
624 m_is_input_complete_callback_baton)) {
625 return BreakLineCommand(ch);
628 // The completion test is allowed to change the input lines when complete
629 m_input_lines.clear();
630 for (unsigned index = 0; index < lines.GetSize(); index++) {
631 #if LLDB_EDITLINE_USE_WCHAR
632 m_input_lines.insert(m_input_lines.end(),
633 m_utf8conv.from_bytes(lines[index]));
635 m_input_lines.insert(m_input_lines.end(), lines[index]);
640 MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockEnd);
641 fprintf(m_output_file, "\n");
642 m_editor_status = EditorStatus::Complete;
646 unsigned char Editline::DeleteNextCharCommand(int ch) {
647 LineInfoW *info = const_cast<LineInfoW *>(el_wline(m_editline));
649 // Just delete the next character normally if possible
650 if (info->cursor < info->lastchar) {
652 el_deletestr(m_editline, 1);
656 // Fail when at the end of the last line, except when ^D is pressed on
657 // the line is empty, in which case it is treated as EOF
658 if (m_current_line_index == m_input_lines.size() - 1) {
659 if (ch == 4 && info->buffer == info->lastchar) {
660 fprintf(m_output_file, "^D\n");
661 m_editor_status = EditorStatus::EndOfInput;
667 // Prepare to combine this line with the one below
668 MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
670 // Insert the next line of text at the cursor and restore the cursor position
671 const EditLineCharType *cursor = info->cursor;
672 el_winsertstr(m_editline, m_input_lines[m_current_line_index + 1].c_str());
673 info->cursor = cursor;
676 // Delete the extra line
677 m_input_lines.erase(m_input_lines.begin() + m_current_line_index + 1);
679 // Clear and repaint from this line on down
680 DisplayInput(m_current_line_index);
681 MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
685 unsigned char Editline::DeletePreviousCharCommand(int ch) {
686 LineInfoW *info = const_cast<LineInfoW *>(el_wline(m_editline));
688 // Just delete the previous character normally when not at the start of a line
689 if (info->cursor > info->buffer) {
690 el_deletestr(m_editline, 1);
694 // No prior line and no prior character? Let the user know
695 if (m_current_line_index == 0)
698 // No prior character, but prior line? Combine with the line above
700 SetCurrentLine(m_current_line_index - 1);
701 auto priorLine = m_input_lines[m_current_line_index];
702 m_input_lines.erase(m_input_lines.begin() + m_current_line_index);
703 m_input_lines[m_current_line_index] =
704 priorLine + m_input_lines[m_current_line_index];
706 // Repaint from the new line down
707 fprintf(m_output_file, ANSI_UP_N_ROWS ANSI_SET_COLUMN_N,
708 CountRowsForLine(priorLine), 1);
709 DisplayInput(m_current_line_index);
711 // Put the cursor back where libedit expects it to be before returning to
713 // by telling libedit about the newly inserted text
714 MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
715 el_winsertstr(m_editline, priorLine.c_str());
719 unsigned char Editline::PreviousLineCommand(int ch) {
722 if (m_current_line_index == 0) {
723 return RecallHistory(true);
726 // Start from a known location
727 MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
729 // Treat moving up from a blank last line as a deletion of that line
730 if (m_current_line_index == m_input_lines.size() - 1 && IsOnlySpaces()) {
731 m_input_lines.erase(m_input_lines.begin() + m_current_line_index);
732 fprintf(m_output_file, ANSI_CLEAR_BELOW);
735 SetCurrentLine(m_current_line_index - 1);
736 fprintf(m_output_file, ANSI_UP_N_ROWS ANSI_SET_COLUMN_N,
737 CountRowsForLine(m_input_lines[m_current_line_index]), 1);
741 unsigned char Editline::NextLineCommand(int ch) {
744 // Handle attempts to move down from the last line
745 if (m_current_line_index == m_input_lines.size() - 1) {
746 // Don't add an extra line if the existing last line is blank, move through
748 if (IsOnlySpaces()) {
749 return RecallHistory(false);
752 // Determine indentation for the new line
754 if (m_fix_indentation_callback) {
755 StringList lines = GetInputAsStringList();
756 lines.AppendString("");
757 indentation = m_fix_indentation_callback(
758 this, lines, 0, m_fix_indentation_callback_baton);
760 m_input_lines.insert(
762 EditLineStringType(indentation, EditLineCharType(' ')));
765 // Move down past the current line using newlines to force scrolling if needed
766 SetCurrentLine(m_current_line_index + 1);
767 const LineInfoW *info = el_wline(m_editline);
768 int cursor_position = (int)((info->cursor - info->buffer) + GetPromptWidth());
769 int cursor_row = cursor_position / m_terminal_width;
770 for (int line_count = 0; line_count < m_current_line_rows - cursor_row;
772 fprintf(m_output_file, "\n");
777 unsigned char Editline::PreviousHistoryCommand(int ch) {
780 return RecallHistory(true);
783 unsigned char Editline::NextHistoryCommand(int ch) {
786 return RecallHistory(false);
789 unsigned char Editline::FixIndentationCommand(int ch) {
790 if (!m_fix_indentation_callback)
793 // Insert the character typed before proceeding
794 EditLineCharType inserted[] = {(EditLineCharType)ch, 0};
795 el_winsertstr(m_editline, inserted);
796 LineInfoW *info = const_cast<LineInfoW *>(el_wline(m_editline));
797 int cursor_position = info->cursor - info->buffer;
799 // Save the edits and determine the correct indentation level
801 StringList lines = GetInputAsStringList(m_current_line_index + 1);
802 int indent_correction = m_fix_indentation_callback(
803 this, lines, cursor_position, m_fix_indentation_callback_baton);
805 // If it is already correct no special work is needed
806 if (indent_correction == 0)
809 // Change the indentation level of the line
810 std::string currentLine = lines.GetStringAtIndex(m_current_line_index);
811 if (indent_correction > 0) {
812 currentLine = currentLine.insert(0, indent_correction, ' ');
814 currentLine = currentLine.erase(0, -indent_correction);
816 #if LLDB_EDITLINE_USE_WCHAR
817 m_input_lines[m_current_line_index] = m_utf8conv.from_bytes(currentLine);
819 m_input_lines[m_current_line_index] = currentLine;
822 // Update the display to reflect the change
823 MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
824 DisplayInput(m_current_line_index);
826 // Reposition the cursor back on the original line and prepare to restart
828 // with a new cursor position
829 SetCurrentLine(m_current_line_index);
830 MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
831 m_revert_cursor_index = cursor_position + indent_correction;
835 unsigned char Editline::RevertLineCommand(int ch) {
836 el_winsertstr(m_editline, m_input_lines[m_current_line_index].c_str());
837 if (m_revert_cursor_index >= 0) {
838 LineInfoW *info = const_cast<LineInfoW *>(el_wline(m_editline));
839 info->cursor = info->buffer + m_revert_cursor_index;
840 if (info->cursor > info->lastchar) {
841 info->cursor = info->lastchar;
843 m_revert_cursor_index = -1;
848 unsigned char Editline::BufferStartCommand(int ch) {
850 MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
852 m_revert_cursor_index = 0;
856 unsigned char Editline::BufferEndCommand(int ch) {
858 MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockEnd);
859 SetCurrentLine((int)m_input_lines.size() - 1);
860 MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
864 unsigned char Editline::TabCommand(int ch) {
865 if (m_completion_callback == nullptr)
868 const LineInfo *line_info = el_line(m_editline);
869 StringList completions;
872 const int num_completions = m_completion_callback(
873 line_info->buffer, line_info->cursor, line_info->lastchar,
874 0, // Don't skip any matches (start at match zero)
875 -1, // Get all the matches
876 completions, m_completion_callback_baton);
878 if (num_completions == 0)
880 // if (num_completions == -1)
882 // el_insertstr (m_editline, m_completion_key);
883 // return CC_REDISPLAY;
886 if (num_completions == -2) {
887 // Replace the entire line with the first string...
888 el_deletestr(m_editline, line_info->cursor - line_info->buffer);
889 el_insertstr(m_editline, completions.GetStringAtIndex(0));
893 // If we get a longer match display that first.
894 const char *completion_str = completions.GetStringAtIndex(0);
895 if (completion_str != nullptr && *completion_str != '\0') {
896 el_insertstr(m_editline, completion_str);
900 if (num_completions > 1) {
901 int num_elements = num_completions + 1;
902 fprintf(m_output_file, "\n" ANSI_CLEAR_BELOW "Available completions:");
903 if (num_completions < page_size) {
904 for (int i = 1; i < num_elements; i++) {
905 completion_str = completions.GetStringAtIndex(i);
906 fprintf(m_output_file, "\n\t%s", completion_str);
908 fprintf(m_output_file, "\n");
913 while (cur_pos < num_elements) {
914 int endpoint = cur_pos + page_size;
915 if (endpoint > num_elements)
916 endpoint = num_elements;
917 for (; cur_pos < endpoint; cur_pos++) {
918 completion_str = completions.GetStringAtIndex(cur_pos);
919 fprintf(m_output_file, "\n\t%s", completion_str);
922 if (cur_pos >= num_elements) {
923 fprintf(m_output_file, "\n");
927 fprintf(m_output_file, "\nMore (Y/n/a): ");
929 got_char = el_getc(m_editline, &reply);
930 if (got_char == -1 || reply == 'n')
933 page_size = num_elements - cur_pos;
937 MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
942 void Editline::ConfigureEditor(bool multiline) {
943 if (m_editline && m_multiline_enabled == multiline)
945 m_multiline_enabled = multiline;
948 // Disable edit mode to stop the terminal from flushing all input
949 // during the call to el_end() since we expect to have multiple editline
950 // instances in this program.
951 el_set(m_editline, EL_EDITMODE, 0);
956 el_init(m_editor_name.c_str(), m_input_file, m_output_file, m_error_file);
957 TerminalSizeChanged();
959 if (m_history_sp && m_history_sp->IsValid()) {
960 m_history_sp->Load();
961 el_wset(m_editline, EL_HIST, history, m_history_sp->GetHistoryPtr());
963 el_set(m_editline, EL_CLIENTDATA, this);
964 el_set(m_editline, EL_SIGNAL, 0);
965 el_set(m_editline, EL_EDITOR, "emacs");
966 el_set(m_editline, EL_PROMPT,
967 (EditlinePromptCallbackType)([](EditLine *editline) {
968 return Editline::InstanceFor(editline)->Prompt();
971 el_wset(m_editline, EL_GETCFN, (EditlineGetCharCallbackType)([](
972 EditLine *editline, EditLineCharType *c) {
973 return Editline::InstanceFor(editline)->GetCharacter(c);
976 // Commands used for multiline support, registered whether or not they're used
977 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-break-line"),
978 EditLineConstString("Insert a line break"),
979 (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
980 return Editline::InstanceFor(editline)->BreakLineCommand(ch);
982 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-end-or-add-line"),
983 EditLineConstString("End editing or continue when incomplete"),
984 (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
985 return Editline::InstanceFor(editline)->EndOrAddLineCommand(ch);
987 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-delete-next-char"),
988 EditLineConstString("Delete next character"),
989 (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
990 return Editline::InstanceFor(editline)->DeleteNextCharCommand(ch);
993 m_editline, EL_ADDFN, EditLineConstString("lldb-delete-previous-char"),
994 EditLineConstString("Delete previous character"),
995 (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
996 return Editline::InstanceFor(editline)->DeletePreviousCharCommand(ch);
998 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-previous-line"),
999 EditLineConstString("Move to previous line"),
1000 (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
1001 return Editline::InstanceFor(editline)->PreviousLineCommand(ch);
1003 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-next-line"),
1004 EditLineConstString("Move to next line"),
1005 (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
1006 return Editline::InstanceFor(editline)->NextLineCommand(ch);
1008 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-previous-history"),
1009 EditLineConstString("Move to previous history"),
1010 (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
1011 return Editline::InstanceFor(editline)->PreviousHistoryCommand(ch);
1013 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-next-history"),
1014 EditLineConstString("Move to next history"),
1015 (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
1016 return Editline::InstanceFor(editline)->NextHistoryCommand(ch);
1018 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-buffer-start"),
1019 EditLineConstString("Move to start of buffer"),
1020 (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
1021 return Editline::InstanceFor(editline)->BufferStartCommand(ch);
1023 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-buffer-end"),
1024 EditLineConstString("Move to end of buffer"),
1025 (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
1026 return Editline::InstanceFor(editline)->BufferEndCommand(ch);
1028 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-fix-indentation"),
1029 EditLineConstString("Fix line indentation"),
1030 (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
1031 return Editline::InstanceFor(editline)->FixIndentationCommand(ch);
1034 // Register the complete callback under two names for compatibility with older
1036 // custom .editrc files (largely because libedit has a bad bug where if you
1037 // have a bind command
1038 // that tries to bind to a function name that doesn't exist, it can corrupt
1040 // crash your process later.)
1041 EditlineCommandCallbackType complete_callback = [](EditLine *editline,
1043 return Editline::InstanceFor(editline)->TabCommand(ch);
1045 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-complete"),
1046 EditLineConstString("Invoke completion"), complete_callback);
1047 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb_complete"),
1048 EditLineConstString("Invoke completion"), complete_callback);
1050 // General bindings we don't mind being overridden
1052 el_set(m_editline, EL_BIND, "^r", "em-inc-search-prev",
1053 NULL); // Cycle through backwards search, entering string
1055 el_set(m_editline, EL_BIND, "^w", "ed-delete-prev-word",
1056 NULL); // Delete previous word, behave like bash in emacs mode
1057 el_set(m_editline, EL_BIND, "\t", "lldb-complete",
1058 NULL); // Bind TAB to auto complete
1060 // Allow user-specific customization prior to registering bindings we
1061 // absolutely require
1062 el_source(m_editline, NULL);
1064 // Register an internal binding that external developers shouldn't use
1065 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-revert-line"),
1066 EditLineConstString("Revert line to saved state"),
1067 (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
1068 return Editline::InstanceFor(editline)->RevertLineCommand(ch);
1071 // Register keys that perform auto-indent correction
1072 if (m_fix_indentation_callback && m_fix_indentation_callback_chars) {
1073 char bind_key[2] = {0, 0};
1074 const char *indent_chars = m_fix_indentation_callback_chars;
1075 while (*indent_chars) {
1076 bind_key[0] = *indent_chars;
1077 el_set(m_editline, EL_BIND, bind_key, "lldb-fix-indentation", NULL);
1082 // Multi-line editor bindings
1084 el_set(m_editline, EL_BIND, "\n", "lldb-end-or-add-line", NULL);
1085 el_set(m_editline, EL_BIND, "\r", "lldb-end-or-add-line", NULL);
1086 el_set(m_editline, EL_BIND, ESCAPE "\n", "lldb-break-line", NULL);
1087 el_set(m_editline, EL_BIND, ESCAPE "\r", "lldb-break-line", NULL);
1088 el_set(m_editline, EL_BIND, "^p", "lldb-previous-line", NULL);
1089 el_set(m_editline, EL_BIND, "^n", "lldb-next-line", NULL);
1090 el_set(m_editline, EL_BIND, "^?", "lldb-delete-previous-char", NULL);
1091 el_set(m_editline, EL_BIND, "^d", "lldb-delete-next-char", NULL);
1092 el_set(m_editline, EL_BIND, ESCAPE "[3~", "lldb-delete-next-char", NULL);
1093 el_set(m_editline, EL_BIND, ESCAPE "[\\^", "lldb-revert-line", NULL);
1095 // Editor-specific bindings
1097 el_set(m_editline, EL_BIND, ESCAPE "<", "lldb-buffer-start", NULL);
1098 el_set(m_editline, EL_BIND, ESCAPE ">", "lldb-buffer-end", NULL);
1099 el_set(m_editline, EL_BIND, ESCAPE "[A", "lldb-previous-line", NULL);
1100 el_set(m_editline, EL_BIND, ESCAPE "[B", "lldb-next-line", NULL);
1101 el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[A", "lldb-previous-history",
1103 el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[B", "lldb-next-history",
1105 el_set(m_editline, EL_BIND, ESCAPE "[1;3A", "lldb-previous-history",
1107 el_set(m_editline, EL_BIND, ESCAPE "[1;3B", "lldb-next-history", NULL);
1109 el_set(m_editline, EL_BIND, "^H", "lldb-delete-previous-char", NULL);
1111 el_set(m_editline, EL_BIND, "-a", ESCAPE "[A", "lldb-previous-line",
1113 el_set(m_editline, EL_BIND, "-a", ESCAPE "[B", "lldb-next-line", NULL);
1114 el_set(m_editline, EL_BIND, "-a", "x", "lldb-delete-next-char", NULL);
1115 el_set(m_editline, EL_BIND, "-a", "^H", "lldb-delete-previous-char",
1117 el_set(m_editline, EL_BIND, "-a", "^?", "lldb-delete-previous-char",
1120 // Escape is absorbed exiting edit mode, so re-register important
1122 // without the prefix
1123 el_set(m_editline, EL_BIND, "-a", "[A", "lldb-previous-line", NULL);
1124 el_set(m_editline, EL_BIND, "-a", "[B", "lldb-next-line", NULL);
1125 el_set(m_editline, EL_BIND, "-a", "[\\^", "lldb-revert-line", NULL);
1130 //------------------------------------------------------------------
1131 // Editline public methods
1132 //------------------------------------------------------------------
1134 Editline *Editline::InstanceFor(EditLine *editline) {
1136 el_get(editline, EL_CLIENTDATA, &editor);
1140 Editline::Editline(const char *editline_name, FILE *input_file,
1141 FILE *output_file, FILE *error_file, bool color_prompts)
1142 : m_editor_status(EditorStatus::Complete), m_color_prompts(color_prompts),
1143 m_input_file(input_file), m_output_file(output_file),
1144 m_error_file(error_file), m_input_connection(fileno(input_file), false) {
1145 // Get a shared history instance
1146 m_editor_name = (editline_name == nullptr) ? "lldb-tmp" : editline_name;
1147 m_history_sp = EditlineHistory::GetHistory(m_editor_name);
1149 #ifdef USE_SETUPTERM_WORKAROUND
1150 if (m_output_file) {
1151 const int term_fd = fileno(m_output_file);
1152 if (term_fd != -1) {
1153 static std::mutex *g_init_terminal_fds_mutex_ptr = nullptr;
1154 static std::set<int> *g_init_terminal_fds_ptr = nullptr;
1155 static llvm::once_flag g_once_flag;
1156 llvm::call_once(g_once_flag, [&]() {
1157 g_init_terminal_fds_mutex_ptr =
1158 new std::mutex(); // NOTE: Leak to avoid C++ destructor chain issues
1159 g_init_terminal_fds_ptr = new std::set<int>(); // NOTE: Leak to avoid
1160 // C++ destructor chain
1164 // We must make sure to initialize the terminal a given file descriptor
1165 // only once. If we do this multiple times, we start leaking memory.
1166 std::lock_guard<std::mutex> guard(*g_init_terminal_fds_mutex_ptr);
1167 if (g_init_terminal_fds_ptr->find(term_fd) ==
1168 g_init_terminal_fds_ptr->end()) {
1169 g_init_terminal_fds_ptr->insert(term_fd);
1170 setupterm((char *)0, term_fd, (int *)0);
1177 Editline::~Editline() {
1179 // Disable edit mode to stop the terminal from flushing all input
1180 // during the call to el_end() since we expect to have multiple editline
1181 // instances in this program.
1182 el_set(m_editline, EL_EDITMODE, 0);
1184 m_editline = nullptr;
1187 // EditlineHistory objects are sometimes shared between multiple
1188 // Editline instances with the same program name. So just release
1189 // our shared pointer and if we are the last owner, it will save the
1190 // history to the history save file automatically.
1191 m_history_sp.reset();
1194 void Editline::SetPrompt(const char *prompt) {
1195 m_set_prompt = prompt == nullptr ? "" : prompt;
1198 void Editline::SetContinuationPrompt(const char *continuation_prompt) {
1199 m_set_continuation_prompt =
1200 continuation_prompt == nullptr ? "" : continuation_prompt;
1203 void Editline::TerminalSizeChanged() {
1204 if (m_editline != nullptr) {
1205 el_resize(m_editline);
1207 // Despite the man page claiming non-zero indicates success, it's actually
1209 if (el_get(m_editline, EL_GETTC, "co", &columns) == 0) {
1210 m_terminal_width = columns;
1211 if (m_current_line_rows != -1) {
1212 const LineInfoW *info = el_wline(m_editline);
1214 (int)((info->lastchar - info->buffer) + GetPromptWidth());
1215 m_current_line_rows = (lineLength / columns) + 1;
1218 m_terminal_width = INT_MAX;
1219 m_current_line_rows = 1;
1224 const char *Editline::GetPrompt() { return m_set_prompt.c_str(); }
1226 uint32_t Editline::GetCurrentLine() { return m_current_line_index; }
1228 bool Editline::Interrupt() {
1230 std::lock_guard<std::mutex> guard(m_output_mutex);
1231 if (m_editor_status == EditorStatus::Editing) {
1232 fprintf(m_output_file, "^C\n");
1233 result = m_input_connection.InterruptRead();
1235 m_editor_status = EditorStatus::Interrupted;
1239 bool Editline::Cancel() {
1241 std::lock_guard<std::mutex> guard(m_output_mutex);
1242 if (m_editor_status == EditorStatus::Editing) {
1243 MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
1244 fprintf(m_output_file, ANSI_CLEAR_BELOW);
1245 result = m_input_connection.InterruptRead();
1247 m_editor_status = EditorStatus::Interrupted;
1251 void Editline::SetAutoCompleteCallback(CompleteCallbackType callback,
1253 m_completion_callback = callback;
1254 m_completion_callback_baton = baton;
1257 void Editline::SetIsInputCompleteCallback(IsInputCompleteCallbackType callback,
1259 m_is_input_complete_callback = callback;
1260 m_is_input_complete_callback_baton = baton;
1263 bool Editline::SetFixIndentationCallback(FixIndentationCallbackType callback,
1265 const char *indent_chars) {
1266 m_fix_indentation_callback = callback;
1267 m_fix_indentation_callback_baton = baton;
1268 m_fix_indentation_callback_chars = indent_chars;
1272 bool Editline::GetLine(std::string &line, bool &interrupted) {
1273 ConfigureEditor(false);
1274 m_input_lines = std::vector<EditLineStringType>();
1275 m_input_lines.insert(m_input_lines.begin(), EditLineConstString(""));
1277 std::lock_guard<std::mutex> guard(m_output_mutex);
1279 lldbassert(m_editor_status != EditorStatus::Editing);
1280 if (m_editor_status == EditorStatus::Interrupted) {
1281 m_editor_status = EditorStatus::Complete;
1287 m_in_history = false;
1288 m_editor_status = EditorStatus::Editing;
1289 m_revert_cursor_index = -1;
1292 auto input = el_wgets(m_editline, &count);
1294 interrupted = m_editor_status == EditorStatus::Interrupted;
1296 if (input == nullptr) {
1297 fprintf(m_output_file, "\n");
1298 m_editor_status = EditorStatus::EndOfInput;
1300 m_history_sp->Enter(input);
1301 #if LLDB_EDITLINE_USE_WCHAR
1302 line = m_utf8conv.to_bytes(SplitLines(input)[0]);
1304 line = SplitLines(input)[0];
1306 m_editor_status = EditorStatus::Complete;
1309 return m_editor_status != EditorStatus::EndOfInput;
1312 bool Editline::GetLines(int first_line_number, StringList &lines,
1313 bool &interrupted) {
1314 ConfigureEditor(true);
1316 // Print the initial input lines, then move the cursor back up to the start of
1318 SetBaseLineNumber(first_line_number);
1319 m_input_lines = std::vector<EditLineStringType>();
1320 m_input_lines.insert(m_input_lines.begin(), EditLineConstString(""));
1322 std::lock_guard<std::mutex> guard(m_output_mutex);
1323 // Begin the line editing loop
1326 MoveCursor(CursorLocation::BlockEnd, CursorLocation::BlockStart);
1327 m_editor_status = EditorStatus::Editing;
1328 m_in_history = false;
1330 m_revert_cursor_index = -1;
1331 while (m_editor_status == EditorStatus::Editing) {
1333 m_current_line_rows = -1;
1334 el_wpush(m_editline, EditLineConstString(
1335 "\x1b[^")); // Revert to the existing line content
1336 el_wgets(m_editline, &count);
1339 interrupted = m_editor_status == EditorStatus::Interrupted;
1341 // Save the completed entry in history before returning
1342 m_history_sp->Enter(CombineLines(m_input_lines).c_str());
1344 lines = GetInputAsStringList();
1346 return m_editor_status != EditorStatus::EndOfInput;
1349 void Editline::PrintAsync(Stream *stream, const char *s, size_t len) {
1350 std::lock_guard<std::mutex> guard(m_output_mutex);
1351 if (m_editor_status == EditorStatus::Editing) {
1352 MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
1353 fprintf(m_output_file, ANSI_CLEAR_BELOW);
1355 stream->Write(s, len);
1357 if (m_editor_status == EditorStatus::Editing) {
1359 MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
1363 bool Editline::CompleteCharacter(char ch, EditLineCharType &out) {
1364 #if !LLDB_EDITLINE_USE_WCHAR
1365 if (ch == (char)EOF)
1371 std::codecvt_utf8<wchar_t> cvt;
1372 llvm::SmallString<4> input;
1374 const char *from_next;
1376 std::mbstate_t state = std::mbstate_t();
1377 input.push_back(ch);
1378 switch (cvt.in(state, input.begin(), input.end(), from_next, &out, &out + 1,
1380 case std::codecvt_base::ok:
1383 case std::codecvt_base::error:
1384 case std::codecvt_base::noconv:
1387 case std::codecvt_base::partial:
1388 lldb::ConnectionStatus status;
1389 size_t read_count = m_input_connection.Read(
1390 &ch, 1, std::chrono::seconds(0), status, nullptr);
1391 if (read_count == 0)