27#include "llvm/Support/FileSystem.h"
28#include "llvm/Support/Threading.h"
42int setupterm(
char *term,
int fildes,
int *errret);
44#define USE_SETUPTERM_WORKAROUND
56#define ANSI_FAINT ESCAPE "[2m"
58#define ANSI_UNFAINT ESCAPE "[0m"
59#define ANSI_CLEAR_BELOW ESCAPE "[J"
60#define ANSI_CLEAR_RIGHT ESCAPE "[K"
61#define ANSI_SET_COLUMN_N ESCAPE "[%dG"
62#define ANSI_UP_N_ROWS ESCAPE "[%dA"
63#define ANSI_DOWN_N_ROWS ESCAPE "[%dB"
65#if LLDB_EDITLINE_USE_WCHAR
67#define EditLineConstString(str) L##str
68#define EditLineStringFormatSpec "%ls"
72#define EditLineConstString(str) str
73#define EditLineStringFormatSpec "%s"
77#define history_w history
78#define history_winit history_init
79#define history_wend history_end
80#define HistoryW History
81#define HistEventW HistEvent
82#define LineInfoW LineInfo
84#define el_wgets el_gets
85#define el_wgetc el_getc
86#define el_wpush el_push
87#define el_wparse el_parse
90#define el_wline el_line
91#define el_winsertstr el_insertstr
92#define el_wdeletestr el_deletestr
97 for (
wchar_t ch : content) {
98 if (ch != EditLineCharType(
' '))
122 case HistoryOperation::Oldest:
124 case HistoryOperation::Older:
126 case HistoryOperation::Current:
128 case HistoryOperation::Newer:
130 case HistoryOperation::Newest:
133 llvm_unreachable(
"Fully covered switch!");
137EditLineStringType
CombineLines(
const std::vector<EditLineStringType> &lines) {
138 EditLineStringStreamType combined_stream;
139 for (EditLineStringType line : lines) {
140 combined_stream << line.c_str() <<
"\n";
142 return combined_stream.str();
145std::vector<EditLineStringType>
SplitLines(
const EditLineStringType &input) {
146 std::vector<EditLineStringType> result;
148 while (start < input.length()) {
149 size_t end = input.find(
'\n', start);
150 if (end == std::string::npos) {
151 result.push_back(input.substr(start));
154 result.push_back(input.substr(start, end - start));
159 if (result.empty()) {
160 result.emplace_back();
166 int indent_correction) {
167 if (indent_correction == 0)
169 if (indent_correction < 0)
170 return line.substr(-indent_correction);
171 return EditLineStringType(indent_correction, EditLineCharType(
' ')) + line;
176 for (EditLineCharType ch : line) {
177 if (ch != EditLineCharType(
' '))
190 const int fd = fileno(file);
192 select_helper.
SetTimeout(std::chrono::microseconds(0));
198namespace line_editor {
219 llvm::SmallString<128> lldb_history_file;
221 llvm::sys::path::append(lldb_history_file,
".lldb");
225 if (!llvm::sys::fs::create_directory(lldb_history_file)) {
226#if LLDB_EDITLINE_USE_WCHAR
227 std::string filename =
m_prefix +
"-widehistory";
229 std::string filename =
m_prefix +
"-history";
231 llvm::sys::path::append(lldb_history_file, filename);
232 m_path = std::string(lldb_history_file.str());
252 static EditlineHistorySP
GetHistory(
const std::string &prefix) {
253 typedef std::map<std::string, EditlineHistoryWP> WeakHistoryMap;
254 static std::recursive_mutex g_mutex;
255 static WeakHistoryMap g_weak_map;
256 std::lock_guard<std::recursive_mutex> guard(g_mutex);
257 WeakHistoryMap::const_iterator pos = g_weak_map.find(prefix);
258 EditlineHistorySP history_sp;
259 if (pos != g_weak_map.end()) {
260 history_sp = pos->second.lock();
263 g_weak_map.erase(pos);
266 g_weak_map[prefix] = history_sp;
274 void Enter(
const EditLineCharType *line_cstr) {
317void Editline::SetBaseLineNumber(
int line_number) {
318 m_base_line_number = line_number;
319 m_line_number_digits =
320 std::max<int>(3, std::to_string(line_number).length() + 1);
323std::string Editline::PromptForIndex(
int line_index) {
324 bool use_line_numbers = m_multiline_enabled && m_base_line_number > 0;
325 std::string prompt = m_set_prompt;
326 if (use_line_numbers && prompt.length() == 0)
328 std::string continuation_prompt = prompt;
329 if (m_set_continuation_prompt.length() > 0) {
330 continuation_prompt = m_set_continuation_prompt;
333 while (continuation_prompt.length() < prompt.length()) {
334 continuation_prompt +=
' ';
336 while (prompt.length() < continuation_prompt.length()) {
341 if (use_line_numbers) {
344 "%*d%s", m_line_number_digits, m_base_line_number + line_index,
345 (line_index == 0) ? prompt.c_str() : continuation_prompt.c_str());
346 return std::string(std::move(prompt_stream.
GetString()));
348 return (line_index == 0) ? prompt : continuation_prompt;
351void Editline::SetCurrentLine(
int line_index) {
352 m_current_line_index = line_index;
353 m_current_prompt = PromptForIndex(line_index);
356int Editline::GetPromptWidth() {
return (
int)PromptForIndex(0).length(); }
358bool Editline::IsEmacs() {
360 el_get(m_editline, EL_EDITOR, &editor);
361 return editor[0] ==
'e';
364bool Editline::IsOnlySpaces() {
366 for (
const EditLineCharType *character = info->buffer;
367 character < info->lastchar; character++) {
368 if (*character !=
' ')
374int Editline::GetLineIndexForLocation(CursorLocation location,
int cursor_row) {
376 if (location == CursorLocation::EditingPrompt ||
377 location == CursorLocation::BlockEnd ||
378 location == CursorLocation::EditingCursor) {
379 for (
unsigned index = 0; index < m_current_line_index; index++) {
380 line += CountRowsForLine(m_input_lines[index]);
382 if (location == CursorLocation::EditingCursor) {
384 }
else if (location == CursorLocation::BlockEnd) {
385 for (
unsigned index = m_current_line_index; index < m_input_lines.size();
387 line += CountRowsForLine(m_input_lines[index]);
395void Editline::MoveCursor(CursorLocation from, CursorLocation to) {
397 int editline_cursor_position =
398 (int)((info->cursor - info->buffer) + GetPromptWidth());
399 int editline_cursor_row = editline_cursor_position / m_terminal_width;
402 int fromLine = GetLineIndexForLocation(from, editline_cursor_row);
403 int toLine = GetLineIndexForLocation(to, editline_cursor_row);
404 if (toLine != fromLine) {
405 fprintf(m_output_file,
407 std::abs(toLine - fromLine));
412 if (to == CursorLocation::EditingCursor) {
414 editline_cursor_position - (editline_cursor_row * m_terminal_width) + 1;
415 }
else if (to == CursorLocation::BlockEnd && !m_input_lines.empty()) {
417 ((m_input_lines[m_input_lines.size() - 1].length() + GetPromptWidth()) %
424void Editline::DisplayInput(
int firstIndex) {
426 int line_count = (int)m_input_lines.size();
427 const char *faint = m_color_prompts ?
ANSI_FAINT :
"";
428 const char *unfaint = m_color_prompts ?
ANSI_UNFAINT :
"";
430 for (
int index = firstIndex; index < line_count; index++) {
431 fprintf(m_output_file,
"%s"
434 faint, PromptForIndex(index).c_str(), unfaint,
435 m_input_lines[index].c_str());
436 if (index < line_count - 1)
437 fprintf(m_output_file,
"\n");
441int Editline::CountRowsForLine(
const EditLineStringType &content) {
444 int line_length = (int)(content.length() + prompt.length());
445 return (line_length / m_terminal_width) + 1;
448void Editline::SaveEditedLine() {
450 m_input_lines[m_current_line_index] =
451 EditLineStringType(info->buffer, info->lastchar - info->buffer);
454StringList Editline::GetInputAsStringList(
int line_count) {
456 for (EditLineStringType line : m_input_lines) {
459#if LLDB_EDITLINE_USE_WCHAR
469unsigned char Editline::RecallHistory(HistoryOperation op) {
470 assert(op == HistoryOperation::Older || op == HistoryOperation::Newer);
471 if (!m_history_sp || !m_history_sp->IsValid())
474 HistoryW *pHistory = m_history_sp->GetHistoryPtr();
476 std::vector<EditLineStringType> new_input_lines;
481 case HistoryOperation::Newer:
483 case HistoryOperation::Older: {
492 m_live_history_lines = m_input_lines;
496 llvm_unreachable(
"unsupported history direction");
501 case HistoryOperation::Older:
504 case HistoryOperation::Newer:
506 new_input_lines = m_live_history_lines;
507 m_in_history =
false;
510 llvm_unreachable(
"unsupported history direction");
517 new_input_lines =
SplitLines(history_event.str);
520 MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
521 m_input_lines = new_input_lines;
527 case HistoryOperation::Older:
528 m_current_line_index = (int)m_input_lines.size() - 1;
530 case HistoryOperation::Newer:
531 m_current_line_index = 0;
534 llvm_unreachable(
"unsupported history direction");
536 SetCurrentLine(m_current_line_index);
537 MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
541int Editline::GetCharacter(EditLineGetCharType *c) {
546 if (m_needs_prompt_repaint) {
547 MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
548 fprintf(m_output_file,
"%s"
552 MoveCursor(CursorLocation::EditingPrompt, CursorLocation::EditingCursor);
553 m_needs_prompt_repaint =
false;
556 if (m_multiline_enabled) {
559 int lineLength = (int)((info->lastchar - info->buffer) + GetPromptWidth());
560 int new_line_rows = (lineLength / m_terminal_width) + 1;
561 if (m_current_line_rows != -1 && new_line_rows != m_current_line_rows) {
563 MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
565 DisplayInput(m_current_line_index);
566 MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
568 m_current_line_rows = new_line_rows;
576 if (m_terminal_size_has_changed)
577 ApplyTerminalSizeChange();
584 m_output_mutex.unlock();
586 m_input_connection.Read(&ch, 1, std::nullopt, status,
nullptr);
587 m_output_mutex.lock();
588 if (m_editor_status == EditorStatus::Interrupted) {
591 m_input_connection.Read(&ch, 1, std::nullopt, status,
nullptr);
597 if (CompleteCharacter(ch, *c))
605 llvm_unreachable(
"Interrupts should have been handled above.");
614 m_editor_status = EditorStatus::EndOfInput;
621const char *Editline::Prompt() {
623 m_needs_prompt_repaint =
true;
624 return m_current_prompt.c_str();
627unsigned char Editline::BreakLineCommand(
int ch) {
631 EditLineStringType(info->buffer, info->cursor - info->buffer);
632 auto new_line_fragment =
633 EditLineStringType(info->cursor, info->lastchar - info->cursor);
634 m_input_lines[m_current_line_index] = current_line;
642 m_revert_cursor_index = 0;
647 if (m_fix_indentation_callback) {
648 StringList lines = GetInputAsStringList(m_current_line_index + 1);
649#if LLDB_EDITLINE_USE_WCHAR
650 lines.
AppendString(m_utf8conv.to_bytes(new_line_fragment));
655 int indent_correction = m_fix_indentation_callback(
this, lines, 0);
656 new_line_fragment =
FixIndentation(new_line_fragment, indent_correction);
662 m_input_lines.insert(m_input_lines.begin() + m_current_line_index + 1,
664 MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
665 DisplayInput(m_current_line_index);
668 SetCurrentLine(m_current_line_index + 1);
669 MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
673unsigned char Editline::EndOrAddLineCommand(
int ch) {
677 return BreakLineCommand(ch);
686 if (m_current_line_index == m_input_lines.size() - 1 &&
687 info->cursor == info->lastchar) {
688 if (m_is_input_complete_callback) {
689 auto lines = GetInputAsStringList();
690 if (!m_is_input_complete_callback(
this, lines)) {
691 return BreakLineCommand(ch);
695 m_input_lines.clear();
696 for (
unsigned index = 0; index < lines.
GetSize(); index++) {
697#if LLDB_EDITLINE_USE_WCHAR
698 m_input_lines.insert(m_input_lines.end(),
699 m_utf8conv.from_bytes(lines[index]));
701 m_input_lines.insert(m_input_lines.end(), lines[index]);
706 MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockEnd);
707 fprintf(m_output_file,
"\n");
708 m_editor_status = EditorStatus::Complete;
712unsigned char Editline::DeleteNextCharCommand(
int ch) {
716 if (info->cursor < info->lastchar) {
718 el_deletestr(m_editline, 1);
724 if (m_current_line_index == m_input_lines.size() - 1) {
725 if (ch == 4 && info->buffer == info->lastchar) {
726 fprintf(m_output_file,
"^D\n");
727 m_editor_status = EditorStatus::EndOfInput;
734 MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
737 const EditLineCharType *cursor = info->cursor;
738 el_winsertstr(m_editline, m_input_lines[m_current_line_index + 1].c_str());
739 info->cursor = cursor;
743 m_input_lines.erase(m_input_lines.begin() + m_current_line_index + 1);
746 DisplayInput(m_current_line_index);
747 MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
751unsigned char Editline::DeletePreviousCharCommand(
int ch) {
756 if (info->cursor > info->buffer) {
757 el_deletestr(m_editline, 1);
762 if (m_current_line_index == 0)
767 SetCurrentLine(m_current_line_index - 1);
768 auto priorLine = m_input_lines[m_current_line_index];
769 m_input_lines.erase(m_input_lines.begin() + m_current_line_index);
770 m_input_lines[m_current_line_index] =
771 priorLine + m_input_lines[m_current_line_index];
775 CountRowsForLine(priorLine), 1);
776 DisplayInput(m_current_line_index);
780 MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
785unsigned char Editline::PreviousLineCommand(
int ch) {
788 if (m_current_line_index == 0) {
789 return RecallHistory(HistoryOperation::Older);
793 MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
796 if (m_current_line_index == m_input_lines.size() - 1 &&
IsOnlySpaces()) {
797 m_input_lines.erase(m_input_lines.begin() + m_current_line_index);
801 SetCurrentLine(m_current_line_index - 1);
803 CountRowsForLine(m_input_lines[m_current_line_index]), 1);
807unsigned char Editline::NextLineCommand(
int ch) {
811 if (m_current_line_index == m_input_lines.size() - 1) {
815 return RecallHistory(HistoryOperation::Newer);
820 if (m_fix_indentation_callback) {
823 indentation = m_fix_indentation_callback(
this, lines, 0);
825 m_input_lines.insert(
827 EditLineStringType(indentation, EditLineCharType(
' ')));
832 SetCurrentLine(m_current_line_index + 1);
834 int cursor_position = (int)((info->cursor - info->buffer) + GetPromptWidth());
835 int cursor_row = cursor_position / m_terminal_width;
836 for (
int line_count = 0; line_count < m_current_line_rows - cursor_row;
838 fprintf(m_output_file,
"\n");
843unsigned char Editline::PreviousHistoryCommand(
int ch) {
846 return RecallHistory(HistoryOperation::Older);
849unsigned char Editline::NextHistoryCommand(
int ch) {
852 return RecallHistory(HistoryOperation::Newer);
855unsigned char Editline::FixIndentationCommand(
int ch) {
856 if (!m_fix_indentation_callback)
860 EditLineCharType inserted[] = {(EditLineCharType)ch, 0};
863 int cursor_position = info->cursor - info->buffer;
867 StringList lines = GetInputAsStringList(m_current_line_index + 1);
868 int indent_correction =
869 m_fix_indentation_callback(
this, lines, cursor_position);
872 if (indent_correction == 0)
877 if (indent_correction > 0) {
878 currentLine = currentLine.insert(0, indent_correction,
' ');
880 currentLine = currentLine.erase(0, -indent_correction);
882#if LLDB_EDITLINE_USE_WCHAR
883 m_input_lines[m_current_line_index] = m_utf8conv.from_bytes(currentLine);
885 m_input_lines[m_current_line_index] = currentLine;
889 MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
890 DisplayInput(m_current_line_index);
894 SetCurrentLine(m_current_line_index);
895 MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
896 m_revert_cursor_index = cursor_position + indent_correction;
900unsigned char Editline::RevertLineCommand(
int ch) {
901 el_winsertstr(m_editline, m_input_lines[m_current_line_index].c_str());
902 if (m_revert_cursor_index >= 0) {
904 info->cursor = info->buffer + m_revert_cursor_index;
905 if (info->cursor > info->lastchar) {
906 info->cursor = info->lastchar;
908 m_revert_cursor_index = -1;
913unsigned char Editline::BufferStartCommand(
int ch) {
915 MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
917 m_revert_cursor_index = 0;
921unsigned char Editline::BufferEndCommand(
int ch) {
923 MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockEnd);
924 SetCurrentLine((
int)m_input_lines.size() - 1);
925 MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
933 llvm::ArrayRef<CompletionResult::Completion> results,
936 fprintf(output_file,
"\t%-*s", (
int)max_len, c.GetCompletion().c_str());
937 if (!c.GetDescription().empty())
938 fprintf(output_file,
" -- %s", c.GetDescription().c_str());
939 fprintf(output_file,
"\n");
945 llvm::ArrayRef<CompletionResult::Completion> results) {
946 assert(!results.empty());
949 const size_t page_size = 40;
953 std::max_element(results.begin(), results.end(), [](
auto &c1,
auto &c2) {
954 return c1.GetCompletion().size() < c2.GetCompletion().size();
957 const size_t max_len = longest->GetCompletion().size();
959 if (results.size() < page_size) {
965 while (cur_pos < results.size()) {
966 size_t remaining = results.size() - cur_pos;
967 size_t next_size = all ? remaining : std::min(page_size, remaining);
969 PrintCompletion(output_file, results.slice(cur_pos, next_size), max_len);
971 cur_pos += next_size;
973 if (cur_pos >= results.size())
976 fprintf(output_file,
"More (Y/n/a): ");
978 int got_char = el_getc(editline, &reply);
979 fprintf(output_file,
"\n");
980 if (got_char == -1 || reply ==
'n')
987unsigned char Editline::TabCommand(
int ch) {
988 if (!m_completion_callback)
991 const LineInfo *line_info = el_line(m_editline);
993 llvm::StringRef line(line_info->buffer,
994 line_info->lastchar - line_info->buffer);
995 unsigned cursor_index = line_info->cursor - line_info->buffer;
999 m_completion_callback(request);
1001 llvm::ArrayRef<CompletionResult::Completion> results = result.
GetResults();
1006 if (results.size() == 0)
1009 if (results.size() == 1) {
1011 switch (completion.
GetMode()) {
1012 case CompletionMode::Normal: {
1015 if (!request.GetParsedLine().empty() && request.GetParsedArg().IsQuoted())
1016 to_add.push_back(request.GetParsedArg().GetQuoteChar());
1017 to_add.push_back(
' ');
1018 el_deletestr(m_editline, request.GetCursorArgumentPrefix().size());
1019 el_insertstr(m_editline, to_add.c_str());
1022 return CC_REDISPLAY;
1025 case CompletionMode::Partial: {
1027 to_add = to_add.substr(request.GetCursorArgumentPrefix().size());
1028 el_insertstr(m_editline, to_add.c_str());
1031 case CompletionMode::RewriteLine: {
1032 el_deletestr(m_editline, line_info->cursor - line_info->buffer);
1033 el_insertstr(m_editline, completion.
GetCompletion().c_str());
1037 return CC_REDISPLAY;
1042 if (!longest_prefix.empty())
1044 longest_prefix.substr(request.GetCursorArgumentPrefix().size());
1045 if (!longest_prefix.empty()) {
1046 el_insertstr(m_editline, longest_prefix.c_str());
1047 return CC_REDISPLAY;
1053 MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
1054 return CC_REDISPLAY;
1057unsigned char Editline::ApplyAutosuggestCommand(
int ch) {
1058 if (!m_suggestion_callback) {
1059 return CC_REDISPLAY;
1062 const LineInfo *line_info = el_line(m_editline);
1063 llvm::StringRef line(line_info->buffer,
1064 line_info->lastchar - line_info->buffer);
1066 if (std::optional<std::string> to_add = m_suggestion_callback(line))
1067 el_insertstr(m_editline, to_add->c_str());
1069 return CC_REDISPLAY;
1072unsigned char Editline::TypedCharacter(
int ch) {
1073 std::string typed = std::string(1, ch);
1074 el_insertstr(m_editline, typed.c_str());
1076 if (!m_suggestion_callback) {
1077 return CC_REDISPLAY;
1080 const LineInfo *line_info = el_line(m_editline);
1081 llvm::StringRef line(line_info->buffer,
1082 line_info->lastchar - line_info->buffer);
1084 const char *ansi_prefix =
1085 m_color_prompts ? m_suggestion_ansi_prefix.c_str() :
"";
1086 const char *ansi_suffix =
1087 m_color_prompts ? m_suggestion_ansi_suffix.c_str() :
"";
1089 if (std::optional<std::string> to_add = m_suggestion_callback(line)) {
1090 std::string to_add_color = ansi_prefix + to_add.value() + ansi_suffix;
1091 fputs(typed.c_str(), m_output_file);
1092 fputs(to_add_color.c_str(), m_output_file);
1093 size_t new_autosuggestion_size = line.size() + to_add->length();
1095 if (new_autosuggestion_size < m_previous_autosuggestion_size) {
1096 size_t spaces_to_print =
1097 m_previous_autosuggestion_size - new_autosuggestion_size;
1098 std::string spaces = std::string(spaces_to_print,
' ');
1099 fputs(spaces.c_str(), m_output_file);
1101 m_previous_autosuggestion_size = new_autosuggestion_size;
1103 int editline_cursor_position =
1104 (int)((line_info->cursor - line_info->buffer) + GetPromptWidth());
1105 int editline_cursor_row = editline_cursor_position / m_terminal_width;
1107 editline_cursor_position - (editline_cursor_row * m_terminal_width);
1112 return CC_REDISPLAY;
1115void Editline::AddFunctionToEditLine(
const EditLineCharType *command,
1116 const EditLineCharType *helptext,
1117 EditlineCommandCallbackType callbackFn) {
1118 el_wset(m_editline, EL_ADDFN, command, helptext, callbackFn);
1121void Editline::SetEditLinePromptCallback(
1122 EditlinePromptCallbackType callbackFn) {
1123 el_set(m_editline, EL_PROMPT, callbackFn);
1126void Editline::SetGetCharacterFunction(EditlineGetCharCallbackType callbackFn) {
1127 el_wset(m_editline, EL_GETCFN, callbackFn);
1130void Editline::ConfigureEditor(
bool multiline) {
1131 if (m_editline && m_multiline_enabled == multiline)
1133 m_multiline_enabled = multiline;
1139 el_set(m_editline, EL_EDITMODE, 0);
1144 el_init(m_editor_name.c_str(), m_input_file, m_output_file, m_error_file);
1145 ApplyTerminalSizeChange();
1147 if (m_history_sp && m_history_sp->IsValid()) {
1148 if (!m_history_sp->Load()) {
1149 fputs(
"Could not load history file\n.", m_output_file);
1151 el_wset(m_editline, EL_HIST, history, m_history_sp->GetHistoryPtr());
1153 el_set(m_editline, EL_CLIENTDATA,
this);
1154 el_set(m_editline, EL_SIGNAL, 0);
1155 el_set(m_editline, EL_EDITOR,
"emacs");
1157 SetGetCharacterFunction([](EditLine *editline, EditLineGetCharType *c) {
1158 return Editline::InstanceFor(editline)->GetCharacter(c);
1161 SetEditLinePromptCallback([](EditLine *editline) {
1162 return Editline::InstanceFor(editline)->Prompt();
1167 AddFunctionToEditLine(
1170 [](EditLine *editline,
int ch) {
1171 return Editline::InstanceFor(editline)->BreakLineCommand(ch);
1174 AddFunctionToEditLine(
1177 [](EditLine *editline,
int ch) {
1178 return Editline::InstanceFor(editline)->EndOrAddLineCommand(ch);
1180 AddFunctionToEditLine(
1183 [](EditLine *editline,
int ch) {
1184 return Editline::InstanceFor(editline)->DeleteNextCharCommand(ch);
1186 AddFunctionToEditLine(
1189 [](EditLine *editline,
int ch) {
1190 return Editline::InstanceFor(editline)->DeletePreviousCharCommand(ch);
1192 AddFunctionToEditLine(
1195 [](EditLine *editline,
int ch) {
1196 return Editline::InstanceFor(editline)->PreviousLineCommand(ch);
1198 AddFunctionToEditLine(
1201 return Editline::InstanceFor(editline)->NextLineCommand(ch);
1203 AddFunctionToEditLine(
1206 [](EditLine *editline,
int ch) {
1207 return Editline::InstanceFor(editline)->PreviousHistoryCommand(ch);
1209 AddFunctionToEditLine(
1212 [](EditLine *editline,
int ch) {
1213 return Editline::InstanceFor(editline)->NextHistoryCommand(ch);
1215 AddFunctionToEditLine(
1218 [](EditLine *editline,
int ch) {
1219 return Editline::InstanceFor(editline)->BufferStartCommand(ch);
1221 AddFunctionToEditLine(
1224 [](EditLine *editline,
int ch) {
1225 return Editline::InstanceFor(editline)->BufferEndCommand(ch);
1227 AddFunctionToEditLine(
1230 [](EditLine *editline,
int ch) {
1231 return Editline::InstanceFor(editline)->FixIndentationCommand(ch);
1239 EditlineCommandCallbackType complete_callback = [](EditLine *editline,
1241 return Editline::InstanceFor(editline)->TabCommand(ch);
1252 el_set(m_editline, EL_BIND,
"^r",
"em-inc-search-prev",
1255 if (m_suggestion_callback) {
1256 AddFunctionToEditLine(
1259 [](EditLine *editline,
int ch) {
1260 return Editline::InstanceFor(editline)->ApplyAutosuggestCommand(ch);
1263 el_set(m_editline, EL_BIND,
"^f",
"lldb-apply-complete",
1266 AddFunctionToEditLine(
1269 [](EditLine *editline,
int ch) {
1270 return Editline::InstanceFor(editline)->TypedCharacter(ch);
1273 char bind_key[2] = {0, 0};
1274 llvm::StringRef ascii_chars =
1275 "abcdefghijklmnopqrstuvwxzyABCDEFGHIJKLMNOPQRSTUVWXZY1234567890!\"#$%"
1276 "&'()*+,./:;<=>?@[]_`{|}~ ";
1277 for (
char c : ascii_chars) {
1279 el_set(m_editline, EL_BIND, bind_key,
"lldb-typed-character", NULL);
1281 el_set(m_editline, EL_BIND,
"\\-",
"lldb-typed-character", NULL);
1282 el_set(m_editline, EL_BIND,
"\\^",
"lldb-typed-character", NULL);
1283 el_set(m_editline, EL_BIND,
"\\\\",
"lldb-typed-character", NULL);
1287 el_set(m_editline, EL_BIND,
"^w",
"ed-delete-prev-word",
1289 el_set(m_editline, EL_BIND,
"\t",
"lldb-complete",
1294 el_set(m_editline, EL_BIND,
ESCAPE "[1;5C",
"em-next-word", NULL);
1295 el_set(m_editline, EL_BIND,
ESCAPE "[1;5D",
"ed-prev-word", NULL);
1296 el_set(m_editline, EL_BIND,
ESCAPE "[5C",
"em-next-word", NULL);
1297 el_set(m_editline, EL_BIND,
ESCAPE "[5D",
"ed-prev-word", NULL);
1298 el_set(m_editline, EL_BIND,
ESCAPE ESCAPE "[C",
"em-next-word", NULL);
1299 el_set(m_editline, EL_BIND,
ESCAPE ESCAPE "[D",
"ed-prev-word", NULL);
1303 el_source(m_editline,
nullptr);
1306 AddFunctionToEditLine(
1309 [](EditLine *editline,
int ch) {
1310 return Editline::InstanceFor(editline)->RevertLineCommand(ch);
1314 if (m_fix_indentation_callback && m_fix_indentation_callback_chars) {
1315 char bind_key[2] = {0, 0};
1316 const char *indent_chars = m_fix_indentation_callback_chars;
1317 while (*indent_chars) {
1318 bind_key[0] = *indent_chars;
1319 el_set(m_editline, EL_BIND, bind_key,
"lldb-fix-indentation", NULL);
1326 el_set(m_editline, EL_BIND,
"\n",
"lldb-end-or-add-line", NULL);
1327 el_set(m_editline, EL_BIND,
"\r",
"lldb-end-or-add-line", NULL);
1328 el_set(m_editline, EL_BIND,
ESCAPE "\n",
"lldb-break-line", NULL);
1329 el_set(m_editline, EL_BIND,
ESCAPE "\r",
"lldb-break-line", NULL);
1330 el_set(m_editline, EL_BIND,
"^p",
"lldb-previous-line", NULL);
1331 el_set(m_editline, EL_BIND,
"^n",
"lldb-next-line", NULL);
1332 el_set(m_editline, EL_BIND,
"^?",
"lldb-delete-previous-char", NULL);
1333 el_set(m_editline, EL_BIND,
"^d",
"lldb-delete-next-char", NULL);
1334 el_set(m_editline, EL_BIND,
ESCAPE "[3~",
"lldb-delete-next-char", NULL);
1335 el_set(m_editline, EL_BIND,
ESCAPE "[\\^",
"lldb-revert-line", NULL);
1339 el_set(m_editline, EL_BIND,
ESCAPE "<",
"lldb-buffer-start", NULL);
1340 el_set(m_editline, EL_BIND,
ESCAPE ">",
"lldb-buffer-end", NULL);
1341 el_set(m_editline, EL_BIND,
ESCAPE "[A",
"lldb-previous-line", NULL);
1342 el_set(m_editline, EL_BIND,
ESCAPE "[B",
"lldb-next-line", NULL);
1343 el_set(m_editline, EL_BIND,
ESCAPE ESCAPE "[A",
"lldb-previous-history",
1345 el_set(m_editline, EL_BIND,
ESCAPE ESCAPE "[B",
"lldb-next-history",
1347 el_set(m_editline, EL_BIND,
ESCAPE "[1;3A",
"lldb-previous-history",
1349 el_set(m_editline, EL_BIND,
ESCAPE "[1;3B",
"lldb-next-history", NULL);
1351 el_set(m_editline, EL_BIND,
"^H",
"lldb-delete-previous-char", NULL);
1353 el_set(m_editline, EL_BIND,
"-a",
ESCAPE "[A",
"lldb-previous-line",
1355 el_set(m_editline, EL_BIND,
"-a",
ESCAPE "[B",
"lldb-next-line", NULL);
1356 el_set(m_editline, EL_BIND,
"-a",
"x",
"lldb-delete-next-char", NULL);
1357 el_set(m_editline, EL_BIND,
"-a",
"^H",
"lldb-delete-previous-char",
1359 el_set(m_editline, EL_BIND,
"-a",
"^?",
"lldb-delete-previous-char",
1364 el_set(m_editline, EL_BIND,
"-a",
"[A",
"lldb-previous-line", NULL);
1365 el_set(m_editline, EL_BIND,
"-a",
"[B",
"lldb-next-line", NULL);
1366 el_set(m_editline, EL_BIND,
"-a",
"[\\^",
"lldb-revert-line", NULL);
1373Editline *Editline::InstanceFor(EditLine *editline) {
1375 el_get(editline, EL_CLIENTDATA, &editor);
1379Editline::Editline(
const char *editline_name, FILE *input_file,
1380 FILE *output_file, FILE *error_file,
1381 std::recursive_mutex &output_mutex,
bool color_prompts)
1382 : m_editor_status(EditorStatus::Complete), m_color_prompts(color_prompts),
1383 m_input_file(input_file), m_output_file(output_file),
1384 m_error_file(error_file), m_input_connection(fileno(input_file), false),
1385 m_output_mutex(output_mutex) {
1387 m_editor_name = (editline_name ==
nullptr) ?
"lldb-tmp" : editline_name;
1390#ifdef USE_SETUPTERM_WORKAROUND
1391 if (m_output_file) {
1392 const int term_fd = fileno(m_output_file);
1393 if (term_fd != -1) {
1394 static std::recursive_mutex *g_init_terminal_fds_mutex_ptr =
nullptr;
1395 static std::set<int> *g_init_terminal_fds_ptr =
nullptr;
1396 static llvm::once_flag g_once_flag;
1397 llvm::call_once(g_once_flag, [&]() {
1398 g_init_terminal_fds_mutex_ptr =
1399 new std::recursive_mutex();
1401 g_init_terminal_fds_ptr =
new std::set<int>();
1408 std::lock_guard<std::recursive_mutex> guard(
1409 *g_init_terminal_fds_mutex_ptr);
1410 if (g_init_terminal_fds_ptr->find(term_fd) ==
1411 g_init_terminal_fds_ptr->end()) {
1412 g_init_terminal_fds_ptr->insert(term_fd);
1413 setupterm((
char *)0, term_fd, (
int *)0);
1420Editline::~Editline() {
1425 el_set(m_editline, EL_EDITMODE, 0);
1427 m_editline =
nullptr;
1434 m_history_sp.reset();
1437void Editline::SetPrompt(
const char *prompt) {
1438 m_set_prompt = prompt ==
nullptr ?
"" : prompt;
1441void Editline::SetContinuationPrompt(
const char *continuation_prompt) {
1442 m_set_continuation_prompt =
1443 continuation_prompt ==
nullptr ?
"" : continuation_prompt;
1446void Editline::TerminalSizeChanged() { m_terminal_size_has_changed = 1; }
1448void Editline::ApplyTerminalSizeChange() {
1452 m_terminal_size_has_changed = 0;
1453 el_resize(m_editline);
1461 if (el_get(m_editline, EL_GETTC,
"co", &columns,
nullptr) == 0) {
1462 m_terminal_width = columns;
1463 if (m_current_line_rows != -1) {
1466 (int)((info->lastchar - info->buffer) + GetPromptWidth());
1467 m_current_line_rows = (lineLength / columns) + 1;
1470 m_terminal_width = INT_MAX;
1471 m_current_line_rows = 1;
1475const char *Editline::GetPrompt() {
return m_set_prompt.c_str(); }
1477uint32_t Editline::GetCurrentLine() {
return m_current_line_index; }
1479bool Editline::Interrupt() {
1481 std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
1482 if (m_editor_status == EditorStatus::Editing) {
1483 fprintf(m_output_file,
"^C\n");
1484 result = m_input_connection.InterruptRead();
1486 m_editor_status = EditorStatus::Interrupted;
1490bool Editline::Cancel() {
1492 std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
1493 if (m_editor_status == EditorStatus::Editing) {
1494 MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
1496 result = m_input_connection.InterruptRead();
1498 m_editor_status = EditorStatus::Interrupted;
1502bool Editline::GetLine(std::string &line,
bool &interrupted) {
1503 ConfigureEditor(
false);
1504 m_input_lines = std::vector<EditLineStringType>();
1507 std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
1509 lldbassert(m_editor_status != EditorStatus::Editing);
1510 if (m_editor_status == EditorStatus::Interrupted) {
1511 m_editor_status = EditorStatus::Complete;
1517 m_in_history =
false;
1518 m_editor_status = EditorStatus::Editing;
1519 m_revert_cursor_index = -1;
1522 auto input =
el_wgets(m_editline, &count);
1524 interrupted = m_editor_status == EditorStatus::Interrupted;
1526 if (input ==
nullptr) {
1527 fprintf(m_output_file,
"\n");
1528 m_editor_status = EditorStatus::EndOfInput;
1530 m_history_sp->Enter(input);
1531#if LLDB_EDITLINE_USE_WCHAR
1532 line = m_utf8conv.to_bytes(
SplitLines(input)[0]);
1536 m_editor_status = EditorStatus::Complete;
1539 return m_editor_status != EditorStatus::EndOfInput;
1542bool Editline::GetLines(
int first_line_number,
StringList &lines,
1543 bool &interrupted) {
1544 ConfigureEditor(
true);
1548 SetBaseLineNumber(first_line_number);
1549 m_input_lines = std::vector<EditLineStringType>();
1552 std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
1556 MoveCursor(CursorLocation::BlockEnd, CursorLocation::BlockStart);
1557 m_editor_status = EditorStatus::Editing;
1558 m_in_history =
false;
1560 m_revert_cursor_index = -1;
1561 while (m_editor_status == EditorStatus::Editing) {
1563 m_current_line_rows = -1;
1569 interrupted = m_editor_status == EditorStatus::Interrupted;
1573 if (!m_input_lines.empty())
1574 m_history_sp->Enter(
CombineLines(m_input_lines).c_str());
1576 lines = GetInputAsStringList();
1578 return m_editor_status != EditorStatus::EndOfInput;
1581void Editline::PrintAsync(
Stream *stream,
const char *s,
size_t len) {
1582 std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
1583 if (m_editor_status == EditorStatus::Editing) {
1584 MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
1587 stream->
Write(s, len);
1589 if (m_editor_status == EditorStatus::Editing) {
1591 MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
1595bool Editline::CompleteCharacter(
char ch, EditLineGetCharType &out) {
1596#if !LLDB_EDITLINE_USE_WCHAR
1597 if (ch == (
char)EOF)
1600 out = (
unsigned char)ch;
1603 std::codecvt_utf8<wchar_t> cvt;
1604 llvm::SmallString<4> input;
1606 const char *from_next;
1608 std::mbstate_t state = std::mbstate_t();
1609 input.push_back(ch);
1610 switch (cvt.in(state, input.begin(), input.end(), from_next, &out, &out + 1,
1612 case std::codecvt_base::ok:
1613 return out != (EditLineGetCharType)WEOF;
1615 case std::codecvt_base::error:
1616 case std::codecvt_base::noconv:
1619 case std::codecvt_base::partial:
1621 size_t read_count = m_input_connection.Read(
1622 &ch, 1, std::chrono::seconds(0), status,
nullptr);
1623 if (read_count == 0)
EditLineStringType CombineLines(const std::vector< EditLineStringType > &lines)
#define EditLineConstString(str)
static void PrintCompletion(FILE *output_file, llvm::ArrayRef< CompletionResult::Completion > results, size_t max_len)
Prints completions and their descriptions to the given file.
#define EditLineStringFormatSpec
#define ANSI_FAINT
Faint, decreased intensity or second colour.
EditLineStringType FixIndentation(const EditLineStringType &line, int indent_correction)
bool IsInputPending(FILE *file)
#define ANSI_UNFAINT
Normal colour or normal intensity (neither bold nor faint).
#define ANSI_SET_COLUMN_N
std::vector< EditLineStringType > SplitLines(const EditLineStringType &input)
bool IsOnlySpaces(const EditLineStringType &content)
int GetIndentation(const EditLineStringType &line)
static void DisplayCompletions(::EditLine *editline, FILE *output_file, llvm::ArrayRef< CompletionResult::Completion > results)
static int GetOperation(HistoryOperation op)
#define ESCAPE
https://www.ecma-international.org/publications/files/ECMA-ST/Ecma-048.pdf
lldb_private::Status Select()
void FDSetRead(lldb::socket_t fd)
void SetTimeout(const std::chrono::microseconds &timeout)
"lldb/Utility/ArgCompletionRequest.h"
A single completion and all associated data.
const std::string & GetCompletion() const
CompletionMode GetMode() const
llvm::ArrayRef< Completion > GetResults() const
void GetMatches(StringList &matches) const
Adds all collected completion matches to the given list.
bool GetHomeDirectory(llvm::SmallVectorImpl< char > &path) const
Get the user home directory.
static FileSystem & Instance()
bool Success() const
Test for success condition.
llvm::StringRef GetString() const
A stream class that can stream formatted output to a file.
size_t Write(const void *src, size_t src_len)
Output character bytes to the stream.
size_t Printf(const char *format,...) __attribute__((format(printf
Output printf formatted output to the stream.
virtual void Flush()=0
Flush the stream.
void AppendString(const std::string &s)
const char * GetStringAtIndex(size_t idx) const
std::string LongestCommonPrefix()
HistoryW * GetHistoryPtr()
void Enter(const EditLineCharType *line_cstr)
const char * GetHistoryFilePath()
std::string m_path
Path to the history file.
std::string m_prefix
The prefix name (usually the editline program name) to use when loading/saving history.
HistoryW * m_history
The history object.
EditlineHistory(const std::string &prefix, uint32_t size, bool unique_entries)
static EditlineHistorySP GetHistory(const std::string &prefix)
HistEventW m_event
The history event needed to contain all history events.
std::weak_ptr< EditlineHistory > EditlineHistoryWP
A class that represents a running process on the host machine.
ConnectionStatus
Connection Status Types.
@ eConnectionStatusError
Check GetError() for details.
@ eConnectionStatusInterrupted
Interrupted read.
@ eConnectionStatusTimedOut
Request timed out.
@ eConnectionStatusEndOfFile
End-of-file encountered.
@ eConnectionStatusSuccess
Success.
@ eConnectionStatusLostConnection
Lost connection while connected to a valid connection.
@ eConnectionStatusNoConnection
No connection.