xref: /freebsd/contrib/llvm-project/lldb/source/Host/common/Editline.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 //===-- Editline.cpp ------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include <climits>
10 #include <iomanip>
11 #include <optional>
12 
13 #include "lldb/Host/ConnectionFileDescriptor.h"
14 #include "lldb/Host/Editline.h"
15 #include "lldb/Host/FileSystem.h"
16 #include "lldb/Host/Host.h"
17 #include "lldb/Host/StreamFile.h"
18 #include "lldb/Utility/AnsiTerminal.h"
19 #include "lldb/Utility/CompletionRequest.h"
20 #include "lldb/Utility/FileSpec.h"
21 #include "lldb/Utility/LLDBAssert.h"
22 #include "lldb/Utility/SelectHelper.h"
23 #include "lldb/Utility/Status.h"
24 #include "lldb/Utility/StreamString.h"
25 #include "lldb/Utility/StringList.h"
26 #include "lldb/Utility/Timeout.h"
27 #include "lldb/lldb-forward.h"
28 #include "llvm/Support/ConvertUTF.h"
29 
30 #include "llvm/Support/FileSystem.h"
31 #include "llvm/Support/Locale.h"
32 #include "llvm/Support/Threading.h"
33 
34 using namespace lldb_private;
35 using namespace lldb_private::line_editor;
36 
37 // Editline uses careful cursor management to achieve the illusion of editing a
38 // multi-line block of text with a single line editor.  Preserving this
39 // illusion requires fairly careful management of cursor state.  Read and
40 // understand the relationship between DisplayInput(), MoveCursor(),
41 // SetCurrentLine(), and SaveEditedLine() before making changes.
42 
43 /// https://www.ecma-international.org/publications/files/ECMA-ST/Ecma-048.pdf
44 #define ESCAPE "\x1b"
45 #define ANSI_CLEAR_BELOW ESCAPE "[J"
46 #define ANSI_CLEAR_RIGHT ESCAPE "[K"
47 #define ANSI_SET_COLUMN_N ESCAPE "[%dG"
48 #define ANSI_UP_N_ROWS ESCAPE "[%dA"
49 #define ANSI_DOWN_N_ROWS ESCAPE "[%dB"
50 
51 #if LLDB_EDITLINE_USE_WCHAR
52 
53 #define EditLineConstString(str) L##str
54 #define EditLineStringFormatSpec "%ls"
55 
56 #else
57 
58 #define EditLineConstString(str) str
59 #define EditLineStringFormatSpec "%s"
60 
61 // use #defines so wide version functions and structs will resolve to old
62 // versions for case of libedit not built with wide char support
63 #define history_w history
64 #define history_winit history_init
65 #define history_wend history_end
66 #define HistoryW History
67 #define HistEventW HistEvent
68 #define LineInfoW LineInfo
69 
70 #define el_wgets el_gets
71 #define el_wgetc el_getc
72 #define el_wpush el_push
73 #define el_wparse el_parse
74 #define el_wset el_set
75 #define el_wget el_get
76 #define el_wline el_line
77 #define el_winsertstr el_insertstr
78 #define el_wdeletestr el_deletestr
79 
80 #endif // #if LLDB_EDITLINE_USE_WCHAR
81 
82 template <typename T> class ScopedOptional {
83 public:
84   template <typename... Args>
ScopedOptional(std::optional<T> & optional,Args &&...args)85   ScopedOptional(std::optional<T> &optional, Args &&...args)
86       : m_optional(optional) {
87     m_optional.emplace(std::forward<Args>(args)...);
88   }
~ScopedOptional()89   ~ScopedOptional() { m_optional.reset(); }
90 
91 private:
92   std::optional<T> &m_optional;
93 };
94 
IsOnlySpaces(const EditLineStringType & content)95 bool IsOnlySpaces(const EditLineStringType &content) {
96   for (wchar_t ch : content) {
97     if (ch != EditLineCharType(' '))
98       return false;
99   }
100   return true;
101 }
102 
ColumnWidth(llvm::StringRef str)103 static size_t ColumnWidth(llvm::StringRef str) {
104   std::string stripped = ansi::StripAnsiTerminalCodes(str);
105   return llvm::sys::locale::columnWidth(stripped);
106 }
107 
GetOperation(HistoryOperation op)108 static int GetOperation(HistoryOperation op) {
109   // The naming used by editline for the history operations is counter
110   // intuitive to how it's used in LLDB's editline implementation.
111   //
112   //  - The H_LAST returns the oldest entry in the history.
113   //
114   //  - The H_PREV operation returns the previous element in the history, which
115   //    is newer than the current one.
116   //
117   //  - The H_CURR returns the current entry in the history.
118   //
119   //  - The H_NEXT operation returns the next element in the history, which is
120   //    older than the current one.
121   //
122   //  - The H_FIRST returns the most recent entry in the history.
123   //
124   // The naming of the enum entries match the semantic meaning.
125   switch (op) {
126   case HistoryOperation::Oldest:
127     return H_LAST;
128   case HistoryOperation::Older:
129     return H_NEXT;
130   case HistoryOperation::Current:
131     return H_CURR;
132   case HistoryOperation::Newer:
133     return H_PREV;
134   case HistoryOperation::Newest:
135     return H_FIRST;
136   }
137   llvm_unreachable("Fully covered switch!");
138 }
139 
CombineLines(const std::vector<EditLineStringType> & lines)140 EditLineStringType CombineLines(const std::vector<EditLineStringType> &lines) {
141   EditLineStringStreamType combined_stream;
142   for (EditLineStringType line : lines) {
143     combined_stream << line.c_str() << "\n";
144   }
145   return combined_stream.str();
146 }
147 
SplitLines(const EditLineStringType & input)148 std::vector<EditLineStringType> SplitLines(const EditLineStringType &input) {
149   std::vector<EditLineStringType> result;
150   size_t start = 0;
151   while (start < input.length()) {
152     size_t end = input.find('\n', start);
153     if (end == std::string::npos) {
154       result.push_back(input.substr(start));
155       break;
156     }
157     result.push_back(input.substr(start, end - start));
158     start = end + 1;
159   }
160   // Treat an empty history session as a single command of zero-length instead
161   // of returning an empty vector.
162   if (result.empty()) {
163     result.emplace_back();
164   }
165   return result;
166 }
167 
FixIndentation(const EditLineStringType & line,int indent_correction)168 EditLineStringType FixIndentation(const EditLineStringType &line,
169                                   int indent_correction) {
170   if (indent_correction == 0)
171     return line;
172   if (indent_correction < 0)
173     return line.substr(-indent_correction);
174   return EditLineStringType(indent_correction, EditLineCharType(' ')) + line;
175 }
176 
GetIndentation(const EditLineStringType & line)177 int GetIndentation(const EditLineStringType &line) {
178   int space_count = 0;
179   for (EditLineCharType ch : line) {
180     if (ch != EditLineCharType(' '))
181       break;
182     ++space_count;
183   }
184   return space_count;
185 }
186 
IsInputPending(FILE * file)187 bool IsInputPending(FILE *file) {
188   // FIXME: This will be broken on Windows if we ever re-enable Editline.  You
189   // can't use select
190   // on something that isn't a socket.  This will have to be re-written to not
191   // use a FILE*, but instead use some kind of yet-to-be-created abstraction
192   // that select-like functionality on non-socket objects.
193   const int fd = fileno(file);
194   SelectHelper select_helper;
195   select_helper.SetTimeout(std::chrono::microseconds(0));
196   select_helper.FDSetRead(fd);
197   return select_helper.Select().Success();
198 }
199 
200 namespace lldb_private {
201 namespace line_editor {
202 typedef std::weak_ptr<EditlineHistory> EditlineHistoryWP;
203 
204 // EditlineHistory objects are sometimes shared between multiple Editline
205 // instances with the same program name.
206 
207 class EditlineHistory {
208 private:
209   // Use static GetHistory() function to get a EditlineHistorySP to one of
210   // these objects
EditlineHistory(const std::string & prefix,uint32_t size,bool unique_entries)211   EditlineHistory(const std::string &prefix, uint32_t size, bool unique_entries)
212       : m_prefix(prefix) {
213     m_history = history_winit();
214     history_w(m_history, &m_event, H_SETSIZE, size);
215     if (unique_entries)
216       history_w(m_history, &m_event, H_SETUNIQUE, 1);
217   }
218 
GetHistoryFilePath()219   const char *GetHistoryFilePath() {
220     // Compute the history path lazily.
221     if (m_path.empty() && m_history && !m_prefix.empty()) {
222       llvm::SmallString<128> lldb_history_file;
223       FileSystem::Instance().GetHomeDirectory(lldb_history_file);
224       llvm::sys::path::append(lldb_history_file, ".lldb");
225 
226       // LLDB stores its history in ~/.lldb/. If for some reason this directory
227       // isn't writable or cannot be created, history won't be available.
228       if (!llvm::sys::fs::create_directory(lldb_history_file)) {
229 #if LLDB_EDITLINE_USE_WCHAR
230         std::string filename = m_prefix + "-widehistory";
231 #else
232         std::string filename = m_prefix + "-history";
233 #endif
234         llvm::sys::path::append(lldb_history_file, filename);
235         m_path = std::string(lldb_history_file.str());
236       }
237     }
238 
239     if (m_path.empty())
240       return nullptr;
241 
242     return m_path.c_str();
243   }
244 
245 public:
~EditlineHistory()246   ~EditlineHistory() {
247     Save();
248 
249     if (m_history) {
250       history_wend(m_history);
251       m_history = nullptr;
252     }
253   }
254 
GetHistory(const std::string & prefix)255   static EditlineHistorySP GetHistory(const std::string &prefix) {
256     typedef std::map<std::string, EditlineHistoryWP> WeakHistoryMap;
257     static std::recursive_mutex g_mutex;
258     static WeakHistoryMap g_weak_map;
259     std::lock_guard<std::recursive_mutex> guard(g_mutex);
260     WeakHistoryMap::const_iterator pos = g_weak_map.find(prefix);
261     EditlineHistorySP history_sp;
262     if (pos != g_weak_map.end()) {
263       history_sp = pos->second.lock();
264       if (history_sp)
265         return history_sp;
266       g_weak_map.erase(pos);
267     }
268     history_sp.reset(new EditlineHistory(prefix, 800, true));
269     g_weak_map[prefix] = history_sp;
270     return history_sp;
271   }
272 
IsValid() const273   bool IsValid() const { return m_history != nullptr; }
274 
GetHistoryPtr()275   HistoryW *GetHistoryPtr() { return m_history; }
276 
Enter(const EditLineCharType * line_cstr)277   void Enter(const EditLineCharType *line_cstr) {
278     if (m_history)
279       history_w(m_history, &m_event, H_ENTER, line_cstr);
280   }
281 
Load()282   bool Load() {
283     if (m_history) {
284       const char *path = GetHistoryFilePath();
285       if (path) {
286         history_w(m_history, &m_event, H_LOAD, path);
287         return true;
288       }
289     }
290     return false;
291   }
292 
Save()293   bool Save() {
294     if (m_history) {
295       const char *path = GetHistoryFilePath();
296       if (path) {
297         history_w(m_history, &m_event, H_SAVE, path);
298         return true;
299       }
300     }
301     return false;
302   }
303 
304 protected:
305   /// The history object.
306   HistoryW *m_history = nullptr;
307   /// The history event needed to contain all history events.
308   HistEventW m_event;
309   /// The prefix name (usually the editline program name) to use when
310   /// loading/saving history.
311   std::string m_prefix;
312   /// Path to the history file.
313   std::string m_path;
314 };
315 } // namespace line_editor
316 } // namespace lldb_private
317 
318 // Editline private methods
319 
SetBaseLineNumber(int line_number)320 void Editline::SetBaseLineNumber(int line_number) {
321   m_base_line_number = line_number;
322   m_line_number_digits =
323       std::max<int>(3, std::to_string(line_number).length() + 1);
324 }
325 
PromptForIndex(int line_index)326 std::string Editline::PromptForIndex(int line_index) {
327   bool use_line_numbers = m_multiline_enabled && m_base_line_number > 0;
328   std::string prompt = m_set_prompt;
329   if (use_line_numbers && prompt.length() == 0)
330     prompt = ": ";
331   std::string continuation_prompt = prompt;
332   if (m_set_continuation_prompt.length() > 0) {
333     continuation_prompt = m_set_continuation_prompt;
334     // Ensure that both prompts are the same length through space padding
335     const size_t prompt_width = ColumnWidth(prompt);
336     const size_t cont_prompt_width = ColumnWidth(continuation_prompt);
337     const size_t padded_prompt_width =
338         std::max(prompt_width, cont_prompt_width);
339     if (prompt_width < padded_prompt_width)
340       prompt += std::string(padded_prompt_width - prompt_width, ' ');
341     else if (cont_prompt_width < padded_prompt_width)
342       continuation_prompt +=
343           std::string(padded_prompt_width - cont_prompt_width, ' ');
344   }
345 
346   if (use_line_numbers) {
347     StreamString prompt_stream;
348     prompt_stream.Printf(
349         "%*d%s", m_line_number_digits, m_base_line_number + line_index,
350         (line_index == 0) ? prompt.c_str() : continuation_prompt.c_str());
351     return std::string(std::move(prompt_stream.GetString()));
352   }
353   return (line_index == 0) ? prompt : continuation_prompt;
354 }
355 
SetCurrentLine(int line_index)356 void Editline::SetCurrentLine(int line_index) {
357   m_current_line_index = line_index;
358   m_current_prompt = PromptForIndex(line_index);
359 }
360 
GetPromptWidth()361 size_t Editline::GetPromptWidth() { return ColumnWidth(PromptForIndex(0)); }
362 
IsEmacs()363 bool Editline::IsEmacs() {
364   const char *editor;
365   el_get(m_editline, EL_EDITOR, &editor);
366   return editor[0] == 'e';
367 }
368 
IsOnlySpaces()369 bool Editline::IsOnlySpaces() {
370   const LineInfoW *info = el_wline(m_editline);
371   for (const EditLineCharType *character = info->buffer;
372        character < info->lastchar; character++) {
373     if (*character != ' ')
374       return false;
375   }
376   return true;
377 }
378 
GetLineIndexForLocation(CursorLocation location,int cursor_row)379 int Editline::GetLineIndexForLocation(CursorLocation location, int cursor_row) {
380   int line = 0;
381   if (location == CursorLocation::EditingPrompt ||
382       location == CursorLocation::BlockEnd ||
383       location == CursorLocation::EditingCursor) {
384     for (unsigned index = 0; index < m_current_line_index; index++) {
385       line += CountRowsForLine(m_input_lines[index]);
386     }
387     if (location == CursorLocation::EditingCursor) {
388       line += cursor_row;
389     } else if (location == CursorLocation::BlockEnd) {
390       for (unsigned index = m_current_line_index; index < m_input_lines.size();
391            index++) {
392         line += CountRowsForLine(m_input_lines[index]);
393       }
394       --line;
395     }
396   }
397   return line;
398 }
399 
MoveCursor(CursorLocation from,CursorLocation to)400 void Editline::MoveCursor(CursorLocation from, CursorLocation to) {
401   const LineInfoW *info = el_wline(m_editline);
402   int editline_cursor_position =
403       (int)((info->cursor - info->buffer) + GetPromptWidth());
404   int editline_cursor_row = editline_cursor_position / m_terminal_width;
405 
406   LockedStreamFile locked_stream = m_output_stream_sp->Lock();
407 
408   // Determine relative starting and ending lines
409   int fromLine = GetLineIndexForLocation(from, editline_cursor_row);
410   int toLine = GetLineIndexForLocation(to, editline_cursor_row);
411   if (toLine != fromLine) {
412     fprintf(locked_stream.GetFile().GetStream(),
413             (toLine > fromLine) ? ANSI_DOWN_N_ROWS : ANSI_UP_N_ROWS,
414             std::abs(toLine - fromLine));
415   }
416 
417   // Determine target column
418   int toColumn = 1;
419   if (to == CursorLocation::EditingCursor) {
420     toColumn =
421         editline_cursor_position - (editline_cursor_row * m_terminal_width) + 1;
422   } else if (to == CursorLocation::BlockEnd && !m_input_lines.empty()) {
423     toColumn =
424         ((m_input_lines[m_input_lines.size() - 1].length() + GetPromptWidth()) %
425          80) +
426         1;
427   }
428   fprintf(locked_stream.GetFile().GetStream(), ANSI_SET_COLUMN_N, toColumn);
429 }
430 
DisplayInput(int firstIndex)431 void Editline::DisplayInput(int firstIndex) {
432   LockedStreamFile locked_stream = m_output_stream_sp->Lock();
433   fprintf(locked_stream.GetFile().GetStream(),
434           ANSI_SET_COLUMN_N ANSI_CLEAR_BELOW, 1);
435   int line_count = (int)m_input_lines.size();
436   for (int index = firstIndex; index < line_count; index++) {
437     fprintf(locked_stream.GetFile().GetStream(),
438             "%s"
439             "%s"
440             "%s" EditLineStringFormatSpec " ",
441             m_prompt_ansi_prefix.c_str(), PromptForIndex(index).c_str(),
442             m_prompt_ansi_suffix.c_str(), m_input_lines[index].c_str());
443     if (index < line_count - 1)
444       fprintf(locked_stream.GetFile().GetStream(), "\n");
445   }
446 }
447 
CountRowsForLine(const EditLineStringType & content)448 int Editline::CountRowsForLine(const EditLineStringType &content) {
449   std::string prompt =
450       PromptForIndex(0); // Prompt width is constant during an edit session
451   int line_length = (int)(content.length() + ColumnWidth(prompt));
452   return (line_length / m_terminal_width) + 1;
453 }
454 
SaveEditedLine()455 void Editline::SaveEditedLine() {
456   const LineInfoW *info = el_wline(m_editline);
457   m_input_lines[m_current_line_index] =
458       EditLineStringType(info->buffer, info->lastchar - info->buffer);
459 }
460 
GetInputAsStringList(int line_count)461 StringList Editline::GetInputAsStringList(int line_count) {
462   StringList lines;
463   for (EditLineStringType line : m_input_lines) {
464     if (line_count == 0)
465       break;
466 #if LLDB_EDITLINE_USE_WCHAR
467     std::string buffer;
468     llvm::convertWideToUTF8(line, buffer);
469     lines.AppendString(buffer);
470 #else
471     lines.AppendString(line);
472 #endif
473     --line_count;
474   }
475   return lines;
476 }
477 
RecallHistory(HistoryOperation op)478 unsigned char Editline::RecallHistory(HistoryOperation op) {
479   assert(op == HistoryOperation::Older || op == HistoryOperation::Newer);
480   if (!m_history_sp || !m_history_sp->IsValid())
481     return CC_ERROR;
482 
483   HistoryW *pHistory = m_history_sp->GetHistoryPtr();
484   HistEventW history_event;
485   std::vector<EditLineStringType> new_input_lines;
486 
487   // Treat moving from the "live" entry differently
488   if (!m_in_history) {
489     switch (op) {
490     case HistoryOperation::Newer:
491       return CC_ERROR; // Can't go newer than the "live" entry
492     case HistoryOperation::Older: {
493       if (history_w(pHistory, &history_event,
494                     GetOperation(HistoryOperation::Newest)) == -1)
495         return CC_ERROR;
496       // Save any edits to the "live" entry in case we return by moving forward
497       // in history (it would be more bash-like to save over any current entry,
498       // but libedit doesn't offer the ability to add entries anywhere except
499       // the end.)
500       SaveEditedLine();
501       m_live_history_lines = m_input_lines;
502       m_in_history = true;
503     } break;
504     default:
505       llvm_unreachable("unsupported history direction");
506     }
507   } else {
508     if (history_w(pHistory, &history_event, GetOperation(op)) == -1) {
509       switch (op) {
510       case HistoryOperation::Older:
511         // Can't move earlier than the earliest entry.
512         return CC_ERROR;
513       case HistoryOperation::Newer:
514         // Moving to newer-than-the-newest entry yields the "live" entry.
515         new_input_lines = m_live_history_lines;
516         m_in_history = false;
517         break;
518       default:
519         llvm_unreachable("unsupported history direction");
520       }
521     }
522   }
523 
524   // If we're pulling the lines from history, split them apart
525   if (m_in_history)
526     new_input_lines = SplitLines(history_event.str);
527 
528   // Erase the current edit session and replace it with a new one
529   MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
530   m_input_lines = new_input_lines;
531   DisplayInput();
532 
533   // Prepare to edit the last line when moving to previous entry, or the first
534   // line when moving to next entry
535   switch (op) {
536   case HistoryOperation::Older:
537     m_current_line_index = (int)m_input_lines.size() - 1;
538     break;
539   case HistoryOperation::Newer:
540     m_current_line_index = 0;
541     break;
542   default:
543     llvm_unreachable("unsupported history direction");
544   }
545   SetCurrentLine(m_current_line_index);
546   MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
547   return CC_NEWLINE;
548 }
549 
GetCharacter(EditLineGetCharType * c)550 int Editline::GetCharacter(EditLineGetCharType *c) {
551   const LineInfoW *info = el_wline(m_editline);
552 
553   // Paint a ANSI formatted version of the desired prompt over the version
554   // libedit draws. (will only be requested if colors are supported)
555   if (m_needs_prompt_repaint) {
556     ScopedOptional<LockedStreamFile> scope(m_locked_output,
557                                            m_output_stream_sp->Lock());
558     MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
559     fprintf(m_locked_output->GetFile().GetStream(),
560             "%s"
561             "%s"
562             "%s",
563             m_prompt_ansi_prefix.c_str(), Prompt(),
564             m_prompt_ansi_suffix.c_str());
565     MoveCursor(CursorLocation::EditingPrompt, CursorLocation::EditingCursor);
566     m_needs_prompt_repaint = false;
567   }
568 
569   if (m_multiline_enabled) {
570     // Detect when the number of rows used for this input line changes due to
571     // an edit
572     int lineLength = (int)((info->lastchar - info->buffer) + GetPromptWidth());
573     int new_line_rows = (lineLength / m_terminal_width) + 1;
574     if (m_current_line_rows != -1 && new_line_rows != m_current_line_rows) {
575       // Respond by repainting the current state from this line on
576       MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
577       SaveEditedLine();
578       DisplayInput(m_current_line_index);
579       MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
580     }
581     m_current_line_rows = new_line_rows;
582   }
583 
584   if (m_terminal_size_has_changed)
585     ApplyTerminalSizeChange();
586 
587   // This mutex is locked by our caller (GetLine). Unlock it while we read a
588   // character (blocking operation), so we do not hold the mutex
589   // indefinitely. This gives a chance for someone to interrupt us. After
590   // Read returns, immediately lock the mutex again and check if we were
591   // interrupted.
592   m_locked_output.reset();
593 
594   if (m_redraw_callback)
595     m_redraw_callback();
596 
597   // Read an actual character
598   lldb::ConnectionStatus status = lldb::eConnectionStatusSuccess;
599   char ch = 0;
600   int read_count =
601       m_input_connection.Read(&ch, 1, std::nullopt, status, nullptr);
602 
603   // Re-lock the output mutex to protected m_editor_status here and in the
604   // switch below.
605   m_locked_output.emplace(m_output_stream_sp->Lock());
606   if (m_editor_status == EditorStatus::Interrupted) {
607     while (read_count > 0 && status == lldb::eConnectionStatusSuccess)
608       read_count =
609           m_input_connection.Read(&ch, 1, std::nullopt, status, nullptr);
610     lldbassert(status == lldb::eConnectionStatusInterrupted);
611     return 0;
612   }
613 
614   if (read_count) {
615     if (CompleteCharacter(ch, *c))
616       return 1;
617     return 0;
618   }
619 
620   switch (status) {
621   case lldb::eConnectionStatusSuccess:
622     llvm_unreachable("Success should have resulted in positive read_count.");
623   case lldb::eConnectionStatusInterrupted:
624     llvm_unreachable("Interrupts should have been handled above.");
625   case lldb::eConnectionStatusError:
626   case lldb::eConnectionStatusTimedOut:
627   case lldb::eConnectionStatusEndOfFile:
628   case lldb::eConnectionStatusNoConnection:
629   case lldb::eConnectionStatusLostConnection:
630     m_editor_status = EditorStatus::EndOfInput;
631   }
632 
633   return 0;
634 }
635 
Prompt()636 const char *Editline::Prompt() {
637   if (m_color)
638     m_needs_prompt_repaint = true;
639   return m_current_prompt.c_str();
640 }
641 
BreakLineCommand(int ch)642 unsigned char Editline::BreakLineCommand(int ch) {
643   // Preserve any content beyond the cursor, truncate and save the current line
644   const LineInfoW *info = el_wline(m_editline);
645   auto current_line =
646       EditLineStringType(info->buffer, info->cursor - info->buffer);
647   auto new_line_fragment =
648       EditLineStringType(info->cursor, info->lastchar - info->cursor);
649   m_input_lines[m_current_line_index] = current_line;
650 
651   // Ignore whitespace-only extra fragments when breaking a line
652   if (::IsOnlySpaces(new_line_fragment))
653     new_line_fragment = EditLineConstString("");
654 
655   // Establish the new cursor position at the start of a line when inserting a
656   // line break
657   m_revert_cursor_index = 0;
658 
659   // Don't perform automatic formatting when pasting
660   if (!IsInputPending(m_input_file)) {
661     // Apply smart indentation
662     if (m_fix_indentation_callback) {
663       StringList lines = GetInputAsStringList(m_current_line_index + 1);
664 #if LLDB_EDITLINE_USE_WCHAR
665       std::string buffer;
666       llvm::convertWideToUTF8(new_line_fragment, buffer);
667       lines.AppendString(buffer);
668 #else
669       lines.AppendString(new_line_fragment);
670 #endif
671 
672       int indent_correction = m_fix_indentation_callback(this, lines, 0);
673       new_line_fragment = FixIndentation(new_line_fragment, indent_correction);
674       m_revert_cursor_index = GetIndentation(new_line_fragment);
675     }
676   }
677 
678   // Insert the new line and repaint everything from the split line on down
679   m_input_lines.insert(m_input_lines.begin() + m_current_line_index + 1,
680                        new_line_fragment);
681   MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
682   DisplayInput(m_current_line_index);
683 
684   // Reposition the cursor to the right line and prepare to edit the new line
685   SetCurrentLine(m_current_line_index + 1);
686   MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
687   return CC_NEWLINE;
688 }
689 
EndOrAddLineCommand(int ch)690 unsigned char Editline::EndOrAddLineCommand(int ch) {
691   // Don't perform end of input detection when pasting, always treat this as a
692   // line break
693   if (IsInputPending(m_input_file)) {
694     return BreakLineCommand(ch);
695   }
696 
697   // Save any edits to this line
698   SaveEditedLine();
699 
700   // If this is the end of the last line, consider whether to add a line
701   // instead
702   const LineInfoW *info = el_wline(m_editline);
703   if (m_current_line_index == m_input_lines.size() - 1 &&
704       info->cursor == info->lastchar) {
705     if (m_is_input_complete_callback) {
706       auto lines = GetInputAsStringList();
707       if (!m_is_input_complete_callback(this, lines)) {
708         return BreakLineCommand(ch);
709       }
710 
711       // The completion test is allowed to change the input lines when complete
712       m_input_lines.clear();
713       for (unsigned index = 0; index < lines.GetSize(); index++) {
714 #if LLDB_EDITLINE_USE_WCHAR
715         std::wstring wbuffer;
716         llvm::ConvertUTF8toWide(lines[index], wbuffer);
717         m_input_lines.insert(m_input_lines.end(), wbuffer);
718 #else
719         m_input_lines.insert(m_input_lines.end(), lines[index]);
720 #endif
721       }
722     }
723   }
724   MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockEnd);
725   LockedStreamFile locked_stream = m_output_stream_sp->Lock();
726   fprintf(locked_stream.GetFile().GetStream(), "\n");
727   m_editor_status = EditorStatus::Complete;
728   return CC_NEWLINE;
729 }
730 
DeleteNextCharCommand(int ch)731 unsigned char Editline::DeleteNextCharCommand(int ch) {
732   LockedStreamFile locked_stream = m_output_stream_sp->Lock();
733   LineInfoW *info = const_cast<LineInfoW *>(el_wline(m_editline));
734 
735   // Just delete the next character normally if possible
736   if (info->cursor < info->lastchar) {
737     info->cursor++;
738     el_deletestr(m_editline, 1);
739     return CC_REFRESH;
740   }
741 
742   // Fail when at the end of the last line, except when ^D is pressed on the
743   // line is empty, in which case it is treated as EOF
744   if (m_current_line_index == m_input_lines.size() - 1) {
745     if (ch == 4 && info->buffer == info->lastchar) {
746       fprintf(locked_stream.GetFile().GetStream(), "^D\n");
747       m_editor_status = EditorStatus::EndOfInput;
748       return CC_EOF;
749     }
750     return CC_ERROR;
751   }
752 
753   // Prepare to combine this line with the one below
754   MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
755 
756   // Insert the next line of text at the cursor and restore the cursor position
757   const EditLineCharType *cursor = info->cursor;
758   el_winsertstr(m_editline, m_input_lines[m_current_line_index + 1].c_str());
759   info->cursor = cursor;
760   SaveEditedLine();
761 
762   // Delete the extra line
763   m_input_lines.erase(m_input_lines.begin() + m_current_line_index + 1);
764 
765   // Clear and repaint from this line on down
766   DisplayInput(m_current_line_index);
767   MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
768   return CC_REFRESH;
769 }
770 
DeletePreviousCharCommand(int ch)771 unsigned char Editline::DeletePreviousCharCommand(int ch) {
772   LineInfoW *info = const_cast<LineInfoW *>(el_wline(m_editline));
773 
774   // Just delete the previous character normally when not at the start of a
775   // line
776   if (info->cursor > info->buffer) {
777     el_deletestr(m_editline, 1);
778     return CC_REFRESH;
779   }
780 
781   // No prior line and no prior character?  Let the user know
782   if (m_current_line_index == 0)
783     return CC_ERROR;
784 
785   // No prior character, but prior line?  Combine with the line above
786   SaveEditedLine();
787   SetCurrentLine(m_current_line_index - 1);
788   auto priorLine = m_input_lines[m_current_line_index];
789   m_input_lines.erase(m_input_lines.begin() + m_current_line_index);
790   m_input_lines[m_current_line_index] =
791       priorLine + m_input_lines[m_current_line_index];
792 
793   // Repaint from the new line down
794   LockedStreamFile locked_stream = m_output_stream_sp->Lock();
795   fprintf(locked_stream.GetFile().GetStream(), ANSI_UP_N_ROWS ANSI_SET_COLUMN_N,
796           CountRowsForLine(priorLine), 1);
797   DisplayInput(m_current_line_index);
798 
799   // Put the cursor back where libedit expects it to be before returning to
800   // editing by telling libedit about the newly inserted text
801   MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
802   el_winsertstr(m_editline, priorLine.c_str());
803   return CC_REDISPLAY;
804 }
805 
PreviousLineCommand(int ch)806 unsigned char Editline::PreviousLineCommand(int ch) {
807   SaveEditedLine();
808 
809   if (m_current_line_index == 0) {
810     return RecallHistory(HistoryOperation::Older);
811   }
812 
813   LockedStreamFile locked_stream = m_output_stream_sp->Lock();
814 
815   // Start from a known location
816   MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
817 
818   // Treat moving up from a blank last line as a deletion of that line
819   if (m_current_line_index == m_input_lines.size() - 1 && IsOnlySpaces()) {
820     m_input_lines.erase(m_input_lines.begin() + m_current_line_index);
821     fprintf(locked_stream.GetFile().GetStream(), ANSI_CLEAR_BELOW);
822   }
823 
824   SetCurrentLine(m_current_line_index - 1);
825   fprintf(locked_stream.GetFile().GetStream(), ANSI_UP_N_ROWS ANSI_SET_COLUMN_N,
826           CountRowsForLine(m_input_lines[m_current_line_index]), 1);
827   return CC_NEWLINE;
828 }
829 
NextLineCommand(int ch)830 unsigned char Editline::NextLineCommand(int ch) {
831   SaveEditedLine();
832 
833   // Handle attempts to move down from the last line
834   if (m_current_line_index == m_input_lines.size() - 1) {
835     // Don't add an extra line if the existing last line is blank, move through
836     // history instead
837     if (IsOnlySpaces()) {
838       return RecallHistory(HistoryOperation::Newer);
839     }
840 
841     // Determine indentation for the new line
842     int indentation = 0;
843     if (m_fix_indentation_callback) {
844       StringList lines = GetInputAsStringList();
845       lines.AppendString("");
846       indentation = m_fix_indentation_callback(this, lines, 0);
847     }
848     m_input_lines.insert(
849         m_input_lines.end(),
850         EditLineStringType(indentation, EditLineCharType(' ')));
851   }
852 
853   // Move down past the current line using newlines to force scrolling if
854   // needed
855   SetCurrentLine(m_current_line_index + 1);
856   const LineInfoW *info = el_wline(m_editline);
857   int cursor_position = (int)((info->cursor - info->buffer) + GetPromptWidth());
858   int cursor_row = cursor_position / m_terminal_width;
859 
860   LockedStreamFile locked_stream = m_output_stream_sp->Lock();
861   for (int line_count = 0; line_count < m_current_line_rows - cursor_row;
862        line_count++) {
863     fprintf(locked_stream.GetFile().GetStream(), "\n");
864   }
865   return CC_NEWLINE;
866 }
867 
PreviousHistoryCommand(int ch)868 unsigned char Editline::PreviousHistoryCommand(int ch) {
869   SaveEditedLine();
870 
871   return RecallHistory(HistoryOperation::Older);
872 }
873 
NextHistoryCommand(int ch)874 unsigned char Editline::NextHistoryCommand(int ch) {
875   SaveEditedLine();
876 
877   return RecallHistory(HistoryOperation::Newer);
878 }
879 
FixIndentationCommand(int ch)880 unsigned char Editline::FixIndentationCommand(int ch) {
881   if (!m_fix_indentation_callback)
882     return CC_NORM;
883 
884   // Insert the character typed before proceeding
885   EditLineCharType inserted[] = {(EditLineCharType)ch, 0};
886   el_winsertstr(m_editline, inserted);
887   LineInfoW *info = const_cast<LineInfoW *>(el_wline(m_editline));
888   int cursor_position = info->cursor - info->buffer;
889 
890   // Save the edits and determine the correct indentation level
891   SaveEditedLine();
892   StringList lines = GetInputAsStringList(m_current_line_index + 1);
893   int indent_correction =
894       m_fix_indentation_callback(this, lines, cursor_position);
895 
896   // If it is already correct no special work is needed
897   if (indent_correction == 0)
898     return CC_REFRESH;
899 
900   // Change the indentation level of the line
901   std::string currentLine = lines.GetStringAtIndex(m_current_line_index);
902   if (indent_correction > 0) {
903     currentLine = currentLine.insert(0, indent_correction, ' ');
904   } else {
905     currentLine = currentLine.erase(0, -indent_correction);
906   }
907 #if LLDB_EDITLINE_USE_WCHAR
908   std::wstring wbuffer;
909   llvm::ConvertUTF8toWide(currentLine, wbuffer);
910   m_input_lines[m_current_line_index] = wbuffer;
911 #else
912   m_input_lines[m_current_line_index] = currentLine;
913 #endif
914 
915   // Update the display to reflect the change
916   MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
917   DisplayInput(m_current_line_index);
918 
919   // Reposition the cursor back on the original line and prepare to restart
920   // editing with a new cursor position
921   SetCurrentLine(m_current_line_index);
922   MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
923   m_revert_cursor_index = cursor_position + indent_correction;
924   return CC_NEWLINE;
925 }
926 
RevertLineCommand(int ch)927 unsigned char Editline::RevertLineCommand(int ch) {
928   el_winsertstr(m_editline, m_input_lines[m_current_line_index].c_str());
929   if (m_revert_cursor_index >= 0) {
930     LineInfoW *info = const_cast<LineInfoW *>(el_wline(m_editline));
931     info->cursor = info->buffer + m_revert_cursor_index;
932     if (info->cursor > info->lastchar) {
933       info->cursor = info->lastchar;
934     }
935     m_revert_cursor_index = -1;
936   }
937   return CC_REFRESH;
938 }
939 
BufferStartCommand(int ch)940 unsigned char Editline::BufferStartCommand(int ch) {
941   SaveEditedLine();
942   MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
943   SetCurrentLine(0);
944   m_revert_cursor_index = 0;
945   return CC_NEWLINE;
946 }
947 
BufferEndCommand(int ch)948 unsigned char Editline::BufferEndCommand(int ch) {
949   SaveEditedLine();
950   MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockEnd);
951   SetCurrentLine((int)m_input_lines.size() - 1);
952   MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
953   return CC_NEWLINE;
954 }
955 
956 /// Prints completions and their descriptions to the given file. Only the
957 /// completions in the interval [start, end) are printed.
958 static size_t
PrintCompletion(FILE * output_file,llvm::ArrayRef<CompletionResult::Completion> results,size_t max_completion_length,size_t max_length,std::optional<size_t> max_height=std::nullopt)959 PrintCompletion(FILE *output_file,
960                 llvm::ArrayRef<CompletionResult::Completion> results,
961                 size_t max_completion_length, size_t max_length,
962                 std::optional<size_t> max_height = std::nullopt) {
963   constexpr size_t ellipsis_length = 3;
964   constexpr size_t padding_length = 8;
965   constexpr size_t separator_length = 4;
966 
967   const size_t description_col =
968       std::min(max_completion_length + padding_length, max_length);
969 
970   size_t lines_printed = 0;
971   size_t results_printed = 0;
972   for (const CompletionResult::Completion &c : results) {
973     if (max_height && lines_printed >= *max_height)
974       break;
975 
976     results_printed++;
977 
978     if (c.GetCompletion().empty())
979       continue;
980 
981     // Print the leading padding.
982     fprintf(output_file, "        ");
983 
984     // Print the completion with trailing padding to the description column if
985     // that fits on the screen. Otherwise print whatever fits on the screen
986     // followed by ellipsis.
987     const size_t completion_length = c.GetCompletion().size();
988     if (padding_length + completion_length < max_length) {
989       fprintf(output_file, "%-*s",
990               static_cast<int>(description_col - padding_length),
991               c.GetCompletion().c_str());
992     } else {
993       // If the completion doesn't fit on the screen, print ellipsis and don't
994       // bother with the description.
995       fprintf(output_file, "%.*s...\n",
996               static_cast<int>(max_length - padding_length - ellipsis_length),
997               c.GetCompletion().c_str());
998       lines_printed++;
999       continue;
1000     }
1001 
1002     // If we don't have a description, or we don't have enough space left to
1003     // print the separator followed by the ellipsis, we're done.
1004     if (c.GetDescription().empty() ||
1005         description_col + separator_length + ellipsis_length >= max_length) {
1006       fprintf(output_file, "\n");
1007       lines_printed++;
1008       continue;
1009     }
1010 
1011     // Print the separator.
1012     fprintf(output_file, " -- ");
1013 
1014     // Descriptions can contain newlines. We want to print them below each
1015     // other, aligned after the separator. For example, foo has a
1016     // two-line description:
1017     //
1018     // foo   -- Something that fits on the line.
1019     //          More information below.
1020     //
1021     // However, as soon as a line exceed the available screen width and
1022     // print ellipsis, we don't print the next line. For example, foo has a
1023     // three-line description:
1024     //
1025     // foo   -- Something that fits on the line.
1026     //          Something much longer  that doesn't fit...
1027     //
1028     // Because we had to print ellipsis on line two, we don't print the
1029     // third line.
1030     bool first = true;
1031     for (llvm::StringRef line : llvm::split(c.GetDescription(), '\n')) {
1032       if (line.empty())
1033         break;
1034       if (max_height && lines_printed >= *max_height)
1035         break;
1036       if (!first)
1037         fprintf(output_file, "%*s",
1038                 static_cast<int>(description_col + separator_length), "");
1039 
1040       first = false;
1041       const size_t position = description_col + separator_length;
1042       const size_t description_length = line.size();
1043       if (position + description_length < max_length) {
1044         fprintf(output_file, "%.*s\n", static_cast<int>(description_length),
1045                 line.data());
1046         lines_printed++;
1047       } else {
1048         fprintf(output_file, "%.*s...\n",
1049                 static_cast<int>(max_length - position - ellipsis_length),
1050                 line.data());
1051         lines_printed++;
1052         continue;
1053       }
1054     }
1055   }
1056   return results_printed;
1057 }
1058 
DisplayCompletions(Editline & editline,llvm::ArrayRef<CompletionResult::Completion> results)1059 void Editline::DisplayCompletions(
1060     Editline &editline, llvm::ArrayRef<CompletionResult::Completion> results) {
1061   assert(!results.empty());
1062 
1063   LockedStreamFile locked_stream = editline.m_output_stream_sp->Lock();
1064 
1065   fprintf(locked_stream.GetFile().GetStream(),
1066           "\n" ANSI_CLEAR_BELOW "Available completions:\n");
1067 
1068   /// Account for the current line, the line showing "Available completions"
1069   /// before and the line saying "More" after.
1070   const size_t page_size = editline.GetTerminalHeight() - 3;
1071 
1072   bool all = false;
1073 
1074   auto longest =
1075       std::max_element(results.begin(), results.end(), [](auto &c1, auto &c2) {
1076         return c1.GetCompletion().size() < c2.GetCompletion().size();
1077       });
1078 
1079   const size_t max_len = longest->GetCompletion().size();
1080 
1081   size_t cur_pos = 0;
1082   while (cur_pos < results.size()) {
1083     cur_pos += PrintCompletion(
1084         locked_stream.GetFile().GetStream(), results.slice(cur_pos), max_len,
1085         editline.GetTerminalWidth(),
1086         all ? std::nullopt : std::optional<size_t>(page_size));
1087 
1088     if (cur_pos >= results.size())
1089       break;
1090 
1091     fprintf(locked_stream.GetFile().GetStream(), "More (Y/n/a): ");
1092     // The type for the output and the type for the parameter are different,
1093     // to allow interoperability with older versions of libedit. The container
1094     // for the reply must be as wide as what our implementation is using,
1095     // but libedit may use a narrower type depending on the build
1096     // configuration.
1097     EditLineGetCharType reply = L'n';
1098     int got_char = el_wgetc(editline.m_editline,
1099                             reinterpret_cast<EditLineCharType *>(&reply));
1100     // Check for a ^C or other interruption.
1101     if (editline.m_editor_status == EditorStatus::Interrupted) {
1102       editline.m_editor_status = EditorStatus::Editing;
1103       fprintf(locked_stream.GetFile().GetStream(), "^C\n");
1104       break;
1105     }
1106 
1107     fprintf(locked_stream.GetFile().GetStream(), "\n");
1108     if (got_char == -1 || reply == 'n')
1109       break;
1110     if (reply == 'a')
1111       all = true;
1112   }
1113 }
1114 
UseColor(bool use_color)1115 void Editline::UseColor(bool use_color) { m_color = use_color; }
1116 
TabCommand(int ch)1117 unsigned char Editline::TabCommand(int ch) {
1118   if (!m_completion_callback)
1119     return CC_ERROR;
1120 
1121   const LineInfo *line_info = el_line(m_editline);
1122 
1123   llvm::StringRef line(line_info->buffer,
1124                        line_info->lastchar - line_info->buffer);
1125   unsigned cursor_index = line_info->cursor - line_info->buffer;
1126   CompletionResult result;
1127   CompletionRequest request(line, cursor_index, result);
1128 
1129   m_completion_callback(request);
1130 
1131   llvm::ArrayRef<CompletionResult::Completion> results = result.GetResults();
1132 
1133   StringList completions;
1134   result.GetMatches(completions);
1135 
1136   if (results.size() == 0)
1137     return CC_ERROR;
1138 
1139   if (results.size() == 1) {
1140     CompletionResult::Completion completion = results.front();
1141     switch (completion.GetMode()) {
1142     case CompletionMode::Normal: {
1143       std::string to_add = completion.GetCompletion();
1144       // Terminate the current argument with a quote if it started with a quote.
1145       Args &parsedLine = request.GetParsedLine();
1146       if (!parsedLine.empty() && request.GetCursorIndex() < parsedLine.size() &&
1147           request.GetParsedArg().IsQuoted()) {
1148         to_add.push_back(request.GetParsedArg().GetQuoteChar());
1149       }
1150       to_add.push_back(' ');
1151       el_deletestr(m_editline, request.GetCursorArgumentPrefix().size());
1152       el_insertstr(m_editline, to_add.c_str());
1153       // Clear all the autosuggestion parts if the only single space can be
1154       // completed.
1155       if (to_add == " ")
1156         return CC_REDISPLAY;
1157       return CC_REFRESH;
1158     }
1159     case CompletionMode::Partial: {
1160       std::string to_add = completion.GetCompletion();
1161       to_add = to_add.substr(request.GetCursorArgumentPrefix().size());
1162       el_insertstr(m_editline, to_add.c_str());
1163       break;
1164     }
1165     case CompletionMode::RewriteLine: {
1166       el_deletestr(m_editline, line_info->cursor - line_info->buffer);
1167       el_insertstr(m_editline, completion.GetCompletion().c_str());
1168       break;
1169     }
1170     }
1171     return CC_REDISPLAY;
1172   }
1173 
1174   // If we get a longer match display that first.
1175   std::string longest_prefix = completions.LongestCommonPrefix();
1176   if (!longest_prefix.empty())
1177     longest_prefix =
1178         longest_prefix.substr(request.GetCursorArgumentPrefix().size());
1179   if (!longest_prefix.empty()) {
1180     el_insertstr(m_editline, longest_prefix.c_str());
1181     return CC_REDISPLAY;
1182   }
1183 
1184   DisplayCompletions(*this, results);
1185 
1186   DisplayInput();
1187   MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
1188   return CC_REDISPLAY;
1189 }
1190 
ApplyAutosuggestCommand(int ch)1191 unsigned char Editline::ApplyAutosuggestCommand(int ch) {
1192   if (!m_suggestion_callback) {
1193     return CC_REDISPLAY;
1194   }
1195 
1196   const LineInfo *line_info = el_line(m_editline);
1197   llvm::StringRef line(line_info->buffer,
1198                        line_info->lastchar - line_info->buffer);
1199 
1200   if (std::optional<std::string> to_add = m_suggestion_callback(line))
1201     el_insertstr(m_editline, to_add->c_str());
1202 
1203   return CC_REDISPLAY;
1204 }
1205 
TypedCharacter(int ch)1206 unsigned char Editline::TypedCharacter(int ch) {
1207   std::string typed = std::string(1, ch);
1208   el_insertstr(m_editline, typed.c_str());
1209 
1210   if (!m_suggestion_callback) {
1211     return CC_REDISPLAY;
1212   }
1213 
1214   const LineInfo *line_info = el_line(m_editline);
1215   llvm::StringRef line(line_info->buffer,
1216                        line_info->lastchar - line_info->buffer);
1217 
1218   if (std::optional<std::string> to_add = m_suggestion_callback(line)) {
1219     LockedStreamFile locked_stream = m_output_stream_sp->Lock();
1220     std::string to_add_color =
1221         m_suggestion_ansi_prefix + to_add.value() + m_suggestion_ansi_suffix;
1222     fputs(typed.c_str(), locked_stream.GetFile().GetStream());
1223     fputs(to_add_color.c_str(), locked_stream.GetFile().GetStream());
1224     size_t new_autosuggestion_size = line.size() + to_add->length();
1225     // Print spaces to hide any remains of a previous longer autosuggestion.
1226     if (new_autosuggestion_size < m_previous_autosuggestion_size) {
1227       size_t spaces_to_print =
1228           m_previous_autosuggestion_size - new_autosuggestion_size;
1229       std::string spaces = std::string(spaces_to_print, ' ');
1230       fputs(spaces.c_str(), locked_stream.GetFile().GetStream());
1231     }
1232     m_previous_autosuggestion_size = new_autosuggestion_size;
1233 
1234     int editline_cursor_position =
1235         (int)((line_info->cursor - line_info->buffer) + GetPromptWidth());
1236     int editline_cursor_row = editline_cursor_position / m_terminal_width;
1237     int toColumn =
1238         editline_cursor_position - (editline_cursor_row * m_terminal_width);
1239     fprintf(locked_stream.GetFile().GetStream(), ANSI_SET_COLUMN_N, toColumn);
1240     return CC_REFRESH;
1241   }
1242 
1243   return CC_REDISPLAY;
1244 }
1245 
AddFunctionToEditLine(const EditLineCharType * command,const EditLineCharType * helptext,EditlineCommandCallbackType callbackFn)1246 void Editline::AddFunctionToEditLine(const EditLineCharType *command,
1247                                      const EditLineCharType *helptext,
1248                                      EditlineCommandCallbackType callbackFn) {
1249   el_wset(m_editline, EL_ADDFN, command, helptext, callbackFn);
1250 }
1251 
SetEditLinePromptCallback(EditlinePromptCallbackType callbackFn)1252 void Editline::SetEditLinePromptCallback(
1253     EditlinePromptCallbackType callbackFn) {
1254   el_set(m_editline, EL_PROMPT, callbackFn);
1255 }
1256 
SetGetCharacterFunction(EditlineGetCharCallbackType callbackFn)1257 void Editline::SetGetCharacterFunction(EditlineGetCharCallbackType callbackFn) {
1258   el_wset(m_editline, EL_GETCFN, callbackFn);
1259 }
1260 
ConfigureEditor(bool multiline)1261 void Editline::ConfigureEditor(bool multiline) {
1262   if (m_editline && m_multiline_enabled == multiline)
1263     return;
1264   m_multiline_enabled = multiline;
1265 
1266   if (m_editline) {
1267     // Disable edit mode to stop the terminal from flushing all input during
1268     // the call to el_end() since we expect to have multiple editline instances
1269     // in this program.
1270     el_set(m_editline, EL_EDITMODE, 0);
1271     el_end(m_editline);
1272   }
1273 
1274   LockedStreamFile locked_output_stream = m_output_stream_sp->Lock();
1275   LockedStreamFile locked_error_stream = m_output_stream_sp->Lock();
1276   m_editline = el_init(m_editor_name.c_str(), m_input_file,
1277                        locked_output_stream.GetFile().GetStream(),
1278                        locked_error_stream.GetFile().GetStream());
1279   ApplyTerminalSizeChange();
1280 
1281   if (m_history_sp && m_history_sp->IsValid()) {
1282     if (!m_history_sp->Load()) {
1283       fputs("Could not load history file\n.",
1284             locked_output_stream.GetFile().GetStream());
1285     }
1286     el_wset(m_editline, EL_HIST, history, m_history_sp->GetHistoryPtr());
1287   }
1288   el_set(m_editline, EL_CLIENTDATA, this);
1289   el_set(m_editline, EL_SIGNAL, 0);
1290   el_set(m_editline, EL_EDITOR, "emacs");
1291 
1292   SetGetCharacterFunction([](EditLine *editline, EditLineGetCharType *c) {
1293     return Editline::InstanceFor(editline)->GetCharacter(c);
1294   });
1295 
1296   SetEditLinePromptCallback([](EditLine *editline) {
1297     return Editline::InstanceFor(editline)->Prompt();
1298   });
1299 
1300   // Commands used for multiline support, registered whether or not they're
1301   // used
1302   AddFunctionToEditLine(
1303       EditLineConstString("lldb-break-line"),
1304       EditLineConstString("Insert a line break"),
1305       [](EditLine *editline, int ch) {
1306         return Editline::InstanceFor(editline)->BreakLineCommand(ch);
1307       });
1308 
1309   AddFunctionToEditLine(
1310       EditLineConstString("lldb-end-or-add-line"),
1311       EditLineConstString("End editing or continue when incomplete"),
1312       [](EditLine *editline, int ch) {
1313         return Editline::InstanceFor(editline)->EndOrAddLineCommand(ch);
1314       });
1315   AddFunctionToEditLine(
1316       EditLineConstString("lldb-delete-next-char"),
1317       EditLineConstString("Delete next character"),
1318       [](EditLine *editline, int ch) {
1319         return Editline::InstanceFor(editline)->DeleteNextCharCommand(ch);
1320       });
1321   AddFunctionToEditLine(
1322       EditLineConstString("lldb-delete-previous-char"),
1323       EditLineConstString("Delete previous character"),
1324       [](EditLine *editline, int ch) {
1325         return Editline::InstanceFor(editline)->DeletePreviousCharCommand(ch);
1326       });
1327   AddFunctionToEditLine(
1328       EditLineConstString("lldb-previous-line"),
1329       EditLineConstString("Move to previous line"),
1330       [](EditLine *editline, int ch) {
1331         return Editline::InstanceFor(editline)->PreviousLineCommand(ch);
1332       });
1333   AddFunctionToEditLine(
1334       EditLineConstString("lldb-next-line"),
1335       EditLineConstString("Move to next line"), [](EditLine *editline, int ch) {
1336         return Editline::InstanceFor(editline)->NextLineCommand(ch);
1337       });
1338   AddFunctionToEditLine(
1339       EditLineConstString("lldb-previous-history"),
1340       EditLineConstString("Move to previous history"),
1341       [](EditLine *editline, int ch) {
1342         return Editline::InstanceFor(editline)->PreviousHistoryCommand(ch);
1343       });
1344   AddFunctionToEditLine(
1345       EditLineConstString("lldb-next-history"),
1346       EditLineConstString("Move to next history"),
1347       [](EditLine *editline, int ch) {
1348         return Editline::InstanceFor(editline)->NextHistoryCommand(ch);
1349       });
1350   AddFunctionToEditLine(
1351       EditLineConstString("lldb-buffer-start"),
1352       EditLineConstString("Move to start of buffer"),
1353       [](EditLine *editline, int ch) {
1354         return Editline::InstanceFor(editline)->BufferStartCommand(ch);
1355       });
1356   AddFunctionToEditLine(
1357       EditLineConstString("lldb-buffer-end"),
1358       EditLineConstString("Move to end of buffer"),
1359       [](EditLine *editline, int ch) {
1360         return Editline::InstanceFor(editline)->BufferEndCommand(ch);
1361       });
1362   AddFunctionToEditLine(
1363       EditLineConstString("lldb-fix-indentation"),
1364       EditLineConstString("Fix line indentation"),
1365       [](EditLine *editline, int ch) {
1366         return Editline::InstanceFor(editline)->FixIndentationCommand(ch);
1367       });
1368 
1369   // Register the complete callback under two names for compatibility with
1370   // older clients using custom .editrc files (largely because libedit has a
1371   // bad bug where if you have a bind command that tries to bind to a function
1372   // name that doesn't exist, it can corrupt the heap and crash your process
1373   // later.)
1374   EditlineCommandCallbackType complete_callback = [](EditLine *editline,
1375                                                      int ch) {
1376     return Editline::InstanceFor(editline)->TabCommand(ch);
1377   };
1378   AddFunctionToEditLine(EditLineConstString("lldb-complete"),
1379                         EditLineConstString("Invoke completion"),
1380                         complete_callback);
1381   AddFunctionToEditLine(EditLineConstString("lldb_complete"),
1382                         EditLineConstString("Invoke completion"),
1383                         complete_callback);
1384 
1385   // General bindings we don't mind being overridden
1386   if (!multiline) {
1387     el_set(m_editline, EL_BIND, "^r", "em-inc-search-prev",
1388            NULL); // Cycle through backwards search, entering string
1389 
1390     if (m_suggestion_callback) {
1391       AddFunctionToEditLine(
1392           EditLineConstString("lldb-apply-complete"),
1393           EditLineConstString("Adopt autocompletion"),
1394           [](EditLine *editline, int ch) {
1395             return Editline::InstanceFor(editline)->ApplyAutosuggestCommand(ch);
1396           });
1397 
1398       el_set(m_editline, EL_BIND, "^f", "lldb-apply-complete",
1399              NULL); // Apply a part that is suggested automatically
1400 
1401       AddFunctionToEditLine(
1402           EditLineConstString("lldb-typed-character"),
1403           EditLineConstString("Typed character"),
1404           [](EditLine *editline, int ch) {
1405             return Editline::InstanceFor(editline)->TypedCharacter(ch);
1406           });
1407 
1408       char bind_key[2] = {0, 0};
1409       llvm::StringRef ascii_chars =
1410           "abcdefghijklmnopqrstuvwxzyABCDEFGHIJKLMNOPQRSTUVWXZY1234567890!\"#$%"
1411           "&'()*+,./:;<=>?@[]_`{|}~ ";
1412       for (char c : ascii_chars) {
1413         bind_key[0] = c;
1414         el_set(m_editline, EL_BIND, bind_key, "lldb-typed-character", NULL);
1415       }
1416       el_set(m_editline, EL_BIND, "\\-", "lldb-typed-character", NULL);
1417       el_set(m_editline, EL_BIND, "\\^", "lldb-typed-character", NULL);
1418       el_set(m_editline, EL_BIND, "\\\\", "lldb-typed-character", NULL);
1419     }
1420   }
1421 
1422   el_set(m_editline, EL_BIND, "^w", "ed-delete-prev-word",
1423          NULL); // Delete previous word, behave like bash in emacs mode
1424   el_set(m_editline, EL_BIND, "\t", "lldb-complete",
1425          NULL); // Bind TAB to auto complete
1426 
1427   // Allow ctrl-left-arrow and ctrl-right-arrow for navigation, behave like
1428   // bash in emacs mode.
1429   el_set(m_editline, EL_BIND, ESCAPE "[1;5C", "em-next-word", NULL);
1430   el_set(m_editline, EL_BIND, ESCAPE "[1;5D", "ed-prev-word", NULL);
1431   el_set(m_editline, EL_BIND, ESCAPE "[5C", "em-next-word", NULL);
1432   el_set(m_editline, EL_BIND, ESCAPE "[5D", "ed-prev-word", NULL);
1433   el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[C", "em-next-word", NULL);
1434   el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[D", "ed-prev-word", NULL);
1435 
1436   // Allow user-specific customization prior to registering bindings we
1437   // absolutely require
1438   el_source(m_editline, nullptr);
1439 
1440   // Register an internal binding that external developers shouldn't use
1441   AddFunctionToEditLine(
1442       EditLineConstString("lldb-revert-line"),
1443       EditLineConstString("Revert line to saved state"),
1444       [](EditLine *editline, int ch) {
1445         return Editline::InstanceFor(editline)->RevertLineCommand(ch);
1446       });
1447 
1448   // Register keys that perform auto-indent correction
1449   if (m_fix_indentation_callback && m_fix_indentation_callback_chars) {
1450     char bind_key[2] = {0, 0};
1451     const char *indent_chars = m_fix_indentation_callback_chars;
1452     while (*indent_chars) {
1453       bind_key[0] = *indent_chars;
1454       el_set(m_editline, EL_BIND, bind_key, "lldb-fix-indentation", NULL);
1455       ++indent_chars;
1456     }
1457   }
1458 
1459   // Multi-line editor bindings
1460   if (multiline) {
1461     el_set(m_editline, EL_BIND, "\n", "lldb-end-or-add-line", NULL);
1462     el_set(m_editline, EL_BIND, "\r", "lldb-end-or-add-line", NULL);
1463     el_set(m_editline, EL_BIND, ESCAPE "\n", "lldb-break-line", NULL);
1464     el_set(m_editline, EL_BIND, ESCAPE "\r", "lldb-break-line", NULL);
1465     el_set(m_editline, EL_BIND, "^p", "lldb-previous-line", NULL);
1466     el_set(m_editline, EL_BIND, "^n", "lldb-next-line", NULL);
1467     el_set(m_editline, EL_BIND, "^?", "lldb-delete-previous-char", NULL);
1468     el_set(m_editline, EL_BIND, "^d", "lldb-delete-next-char", NULL);
1469     el_set(m_editline, EL_BIND, ESCAPE "[3~", "lldb-delete-next-char", NULL);
1470     el_set(m_editline, EL_BIND, ESCAPE "[\\^", "lldb-revert-line", NULL);
1471 
1472     // Editor-specific bindings
1473     if (IsEmacs()) {
1474       el_set(m_editline, EL_BIND, ESCAPE "<", "lldb-buffer-start", NULL);
1475       el_set(m_editline, EL_BIND, ESCAPE ">", "lldb-buffer-end", NULL);
1476       el_set(m_editline, EL_BIND, ESCAPE "[A", "lldb-previous-line", NULL);
1477       el_set(m_editline, EL_BIND, ESCAPE "[B", "lldb-next-line", NULL);
1478       el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[A", "lldb-previous-history",
1479              NULL);
1480       el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[B", "lldb-next-history",
1481              NULL);
1482       el_set(m_editline, EL_BIND, ESCAPE "[1;3A", "lldb-previous-history",
1483              NULL);
1484       el_set(m_editline, EL_BIND, ESCAPE "[1;3B", "lldb-next-history", NULL);
1485     } else {
1486       el_set(m_editline, EL_BIND, "^H", "lldb-delete-previous-char", NULL);
1487 
1488       el_set(m_editline, EL_BIND, "-a", ESCAPE "[A", "lldb-previous-line",
1489              NULL);
1490       el_set(m_editline, EL_BIND, "-a", ESCAPE "[B", "lldb-next-line", NULL);
1491       el_set(m_editline, EL_BIND, "-a", "x", "lldb-delete-next-char", NULL);
1492       el_set(m_editline, EL_BIND, "-a", "^H", "lldb-delete-previous-char",
1493              NULL);
1494       el_set(m_editline, EL_BIND, "-a", "^?", "lldb-delete-previous-char",
1495              NULL);
1496 
1497       // Escape is absorbed exiting edit mode, so re-register important
1498       // sequences without the prefix
1499       el_set(m_editline, EL_BIND, "-a", "[A", "lldb-previous-line", NULL);
1500       el_set(m_editline, EL_BIND, "-a", "[B", "lldb-next-line", NULL);
1501       el_set(m_editline, EL_BIND, "-a", "[\\^", "lldb-revert-line", NULL);
1502     }
1503   }
1504 }
1505 
1506 // Editline public methods
1507 
InstanceFor(EditLine * editline)1508 Editline *Editline::InstanceFor(EditLine *editline) {
1509   Editline *editor;
1510   el_get(editline, EL_CLIENTDATA, &editor);
1511   return editor;
1512 }
1513 
Editline(const char * editline_name,FILE * input_file,lldb::LockableStreamFileSP output_stream_sp,lldb::LockableStreamFileSP error_stream_sp,bool color)1514 Editline::Editline(const char *editline_name, FILE *input_file,
1515                    lldb::LockableStreamFileSP output_stream_sp,
1516                    lldb::LockableStreamFileSP error_stream_sp, bool color)
1517     : m_editor_status(EditorStatus::Complete), m_input_file(input_file),
1518       m_output_stream_sp(output_stream_sp), m_error_stream_sp(error_stream_sp),
1519       m_input_connection(fileno(input_file), false), m_color(color) {
1520   assert(output_stream_sp && error_stream_sp);
1521   // Get a shared history instance
1522   m_editor_name = (editline_name == nullptr) ? "lldb-tmp" : editline_name;
1523   m_history_sp = EditlineHistory::GetHistory(m_editor_name);
1524 }
1525 
~Editline()1526 Editline::~Editline() {
1527   if (m_editline) {
1528     // Disable edit mode to stop the terminal from flushing all input during
1529     // the call to el_end() since we expect to have multiple editline instances
1530     // in this program.
1531     el_set(m_editline, EL_EDITMODE, 0);
1532     el_end(m_editline);
1533     m_editline = nullptr;
1534   }
1535 
1536   // EditlineHistory objects are sometimes shared between multiple Editline
1537   // instances with the same program name. So just release our shared pointer
1538   // and if we are the last owner, it will save the history to the history save
1539   // file automatically.
1540   m_history_sp.reset();
1541 }
1542 
SetPrompt(const char * prompt)1543 void Editline::SetPrompt(const char *prompt) {
1544   m_set_prompt = prompt == nullptr ? "" : prompt;
1545 }
1546 
SetContinuationPrompt(const char * continuation_prompt)1547 void Editline::SetContinuationPrompt(const char *continuation_prompt) {
1548   m_set_continuation_prompt =
1549       continuation_prompt == nullptr ? "" : continuation_prompt;
1550 }
1551 
TerminalSizeChanged()1552 void Editline::TerminalSizeChanged() { m_terminal_size_has_changed = 1; }
1553 
ApplyTerminalSizeChange()1554 void Editline::ApplyTerminalSizeChange() {
1555   if (!m_editline)
1556     return;
1557 
1558   m_terminal_size_has_changed = 0;
1559   el_resize(m_editline);
1560   int columns;
1561   // This function is documenting as taking (const char *, void *) for the
1562   // vararg part, but in reality in was consuming arguments until the first
1563   // null pointer. This was fixed in libedit in April 2019
1564   // <http://mail-index.netbsd.org/source-changes/2019/04/26/msg105454.html>,
1565   // but we're keeping the workaround until a version with that fix is more
1566   // widely available.
1567   if (el_get(m_editline, EL_GETTC, "co", &columns, nullptr) == 0) {
1568     m_terminal_width = columns;
1569     if (m_current_line_rows != -1) {
1570       const LineInfoW *info = el_wline(m_editline);
1571       int lineLength =
1572           (int)((info->lastchar - info->buffer) + GetPromptWidth());
1573       m_current_line_rows = (lineLength / columns) + 1;
1574     }
1575   } else {
1576     m_terminal_width = INT_MAX;
1577     m_current_line_rows = 1;
1578   }
1579 
1580   int rows;
1581   if (el_get(m_editline, EL_GETTC, "li", &rows, nullptr) == 0) {
1582     m_terminal_height = rows;
1583   } else {
1584     m_terminal_height = INT_MAX;
1585   }
1586 }
1587 
GetPrompt()1588 const char *Editline::GetPrompt() { return m_set_prompt.c_str(); }
1589 
GetCurrentLine()1590 uint32_t Editline::GetCurrentLine() { return m_current_line_index; }
1591 
Interrupt()1592 bool Editline::Interrupt() {
1593   bool result = true;
1594   LockedStreamFile locked_stream = m_output_stream_sp->Lock();
1595   if (m_editor_status == EditorStatus::Editing) {
1596     fprintf(locked_stream.GetFile().GetStream(), "^C\n");
1597     result = m_input_connection.InterruptRead();
1598   }
1599   m_editor_status = EditorStatus::Interrupted;
1600   return result;
1601 }
1602 
Cancel()1603 bool Editline::Cancel() {
1604   bool result = true;
1605   LockedStreamFile locked_stream = m_output_stream_sp->Lock();
1606   if (m_editor_status == EditorStatus::Editing) {
1607     MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
1608     fprintf(locked_stream.GetFile().GetStream(), ANSI_CLEAR_BELOW);
1609     result = m_input_connection.InterruptRead();
1610   }
1611   m_editor_status = EditorStatus::Interrupted;
1612   return result;
1613 }
1614 
GetLine(std::string & line,bool & interrupted)1615 bool Editline::GetLine(std::string &line, bool &interrupted) {
1616   ConfigureEditor(false);
1617   m_input_lines = std::vector<EditLineStringType>();
1618   m_input_lines.insert(m_input_lines.begin(), EditLineConstString(""));
1619 
1620   ScopedOptional<LockedStreamFile> scope(m_locked_output,
1621                                          m_output_stream_sp->Lock());
1622 
1623   lldbassert(m_editor_status != EditorStatus::Editing);
1624   if (m_editor_status == EditorStatus::Interrupted) {
1625     m_editor_status = EditorStatus::Complete;
1626     interrupted = true;
1627     return true;
1628   }
1629 
1630   SetCurrentLine(0);
1631   m_in_history = false;
1632   m_editor_status = EditorStatus::Editing;
1633   m_revert_cursor_index = -1;
1634 
1635   int count;
1636   auto input = el_wgets(m_editline, &count);
1637 
1638   interrupted = m_editor_status == EditorStatus::Interrupted;
1639   if (!interrupted) {
1640     if (input == nullptr) {
1641       fprintf(m_locked_output->GetFile().GetStream(), "\n");
1642       m_editor_status = EditorStatus::EndOfInput;
1643     } else {
1644       m_history_sp->Enter(input);
1645 #if LLDB_EDITLINE_USE_WCHAR
1646       llvm::convertWideToUTF8(SplitLines(input)[0], line);
1647 #else
1648       line = SplitLines(input)[0];
1649 #endif
1650       m_editor_status = EditorStatus::Complete;
1651     }
1652   }
1653   return m_editor_status != EditorStatus::EndOfInput;
1654 }
1655 
GetLines(int first_line_number,StringList & lines,bool & interrupted)1656 bool Editline::GetLines(int first_line_number, StringList &lines,
1657                         bool &interrupted) {
1658   ConfigureEditor(true);
1659 
1660   // Print the initial input lines, then move the cursor back up to the start
1661   // of input
1662   SetBaseLineNumber(first_line_number);
1663   m_input_lines = std::vector<EditLineStringType>();
1664   m_input_lines.insert(m_input_lines.begin(), EditLineConstString(""));
1665 
1666   ScopedOptional<LockedStreamFile> scope(m_locked_output,
1667                                          m_output_stream_sp->Lock());
1668 
1669   // Begin the line editing loop
1670   DisplayInput();
1671   SetCurrentLine(0);
1672   MoveCursor(CursorLocation::BlockEnd, CursorLocation::BlockStart);
1673   m_editor_status = EditorStatus::Editing;
1674   m_in_history = false;
1675 
1676   m_revert_cursor_index = -1;
1677   while (m_editor_status == EditorStatus::Editing) {
1678     int count;
1679     m_current_line_rows = -1;
1680     el_wpush(m_editline, EditLineConstString(
1681                              "\x1b[^")); // Revert to the existing line content
1682     el_wgets(m_editline, &count);
1683   }
1684 
1685   interrupted = m_editor_status == EditorStatus::Interrupted;
1686   if (!interrupted) {
1687     // Save the completed entry in history before returning. Don't save empty
1688     // input as that just clutters the command history.
1689     if (!m_input_lines.empty())
1690       m_history_sp->Enter(CombineLines(m_input_lines).c_str());
1691 
1692     lines = GetInputAsStringList();
1693   }
1694   return m_editor_status != EditorStatus::EndOfInput;
1695 }
1696 
PrintAsync(lldb::LockableStreamFileSP stream_sp,const char * s,size_t len)1697 void Editline::PrintAsync(lldb::LockableStreamFileSP stream_sp, const char *s,
1698                           size_t len) {
1699   LockedStreamFile locked_stream = m_output_stream_sp->Lock();
1700   if (m_editor_status == EditorStatus::Editing) {
1701     SaveEditedLine();
1702     MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
1703     fprintf(locked_stream.GetFile().GetStream(), ANSI_CLEAR_BELOW);
1704   }
1705   locked_stream.Write(s, len);
1706   if (m_editor_status == EditorStatus::Editing) {
1707     DisplayInput();
1708     MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
1709   }
1710 }
1711 
Refresh()1712 void Editline::Refresh() {
1713   if (!m_editline || !m_output_stream_sp)
1714     return;
1715   LockedStreamFile locked_stream = m_output_stream_sp->Lock();
1716   el_set(m_editline, EL_REFRESH);
1717 }
1718 
CompleteCharacter(char ch,EditLineGetCharType & out)1719 bool Editline::CompleteCharacter(char ch, EditLineGetCharType &out) {
1720 #if !LLDB_EDITLINE_USE_WCHAR
1721   if (ch == (char)EOF)
1722     return false;
1723 
1724   out = (unsigned char)ch;
1725   return true;
1726 #else
1727   llvm::SmallString<4> input;
1728   for (;;) {
1729     input.push_back(ch);
1730     auto *cur_ptr = reinterpret_cast<const llvm::UTF8 *>(input.begin());
1731     auto *end_ptr = reinterpret_cast<const llvm::UTF8 *>(input.end());
1732     llvm::UTF32 code_point = 0;
1733     llvm::ConversionResult cr = llvm::convertUTF8Sequence(
1734         &cur_ptr, end_ptr, &code_point, llvm::lenientConversion);
1735     switch (cr) {
1736     case llvm::conversionOK:
1737       out = code_point;
1738       return out != (EditLineGetCharType)WEOF;
1739     case llvm::targetExhausted:
1740     case llvm::sourceIllegal:
1741       return false;
1742     case llvm::sourceExhausted:
1743       lldb::ConnectionStatus status;
1744       size_t read_count = m_input_connection.Read(
1745           &ch, 1, std::chrono::seconds(0), status, nullptr);
1746       if (read_count == 0)
1747         return false;
1748       break;
1749     }
1750   }
1751 #endif
1752 }
1753