xref: /NextBSD/contrib/llvm/tools/lldb/source/Host/common/Editline.cpp (revision 84d351007654069f9643c8e4b4802a7f5f08ee42)
1 //===-- Editline.cpp --------------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include <iomanip>
11 #include <iostream>
12 #include <limits.h>
13 
14 #include "lldb/Host/Editline.h"
15 #include "lldb/Host/ConnectionFileDescriptor.h"
16 #include "lldb/Core/Error.h"
17 #include "lldb/Core/StringList.h"
18 #include "lldb/Core/StreamString.h"
19 #include "lldb/Host/FileSpec.h"
20 #include "lldb/Host/FileSystem.h"
21 #include "lldb/Host/Host.h"
22 #include "lldb/Host/Mutex.h"
23 #include "lldb/Utility/LLDBAssert.h"
24 
25 using namespace lldb_private;
26 using namespace lldb_private::line_editor;
27 
28 // Workaround for what looks like an OS X-specific issue, but other platforms
29 // may benefit from something similar if issues arise.  The libedit library
30 // doesn't explicitly initialize the curses termcap library, which it gets away
31 // with until TERM is set to VT100 where it stumbles over an implementation
32 // assumption that may not exist on other platforms.  The setupterm() function
33 // would normally require headers that don't work gracefully in this context, so
34 // the function declaraction has been hoisted here.
35 #if defined(__APPLE__)
36 extern "C" {
37     int setupterm(char *term, int fildes, int *errret);
38 }
39 #define USE_SETUPTERM_WORKAROUND
40 #endif
41 
42 // Editline uses careful cursor management to achieve the illusion of editing a multi-line block of text
43 // with a single line editor.  Preserving this illusion requires fairly careful management of cursor
44 // state.  Read and understand the relationship between DisplayInput(), MoveCursor(), SetCurrentLine(),
45 // and SaveEditedLine() before making changes.
46 
47 #define ESCAPE "\x1b"
48 #define ANSI_FAINT ESCAPE "[2m"
49 #define ANSI_UNFAINT ESCAPE "[22m"
50 #define ANSI_CLEAR_BELOW ESCAPE "[J"
51 #define ANSI_CLEAR_RIGHT ESCAPE "[K"
52 #define ANSI_SET_COLUMN_N ESCAPE "[%dG"
53 #define ANSI_UP_N_ROWS ESCAPE "[%dA"
54 #define ANSI_DOWN_N_ROWS ESCAPE "[%dB"
55 
56 #if LLDB_EDITLINE_USE_WCHAR
57 
58 #define EditLineConstString(str) L##str
59 #define EditLineStringFormatSpec "%ls"
60 
61 #else
62 
63 #define EditLineConstString(str) str
64 #define EditLineStringFormatSpec "%s"
65 
66 // use #defines so wide version functions and structs will resolve to old versions
67 // for case of libedit not built with wide char support
68 #define history_w history
69 #define history_winit history_init
70 #define history_wend history_end
71 #define HistoryW History
72 #define HistEventW HistEvent
73 #define LineInfoW LineInfo
74 
75 #define el_wgets el_gets
76 #define el_wgetc el_getc
77 #define el_wpush el_push
78 #define el_wparse el_parse
79 #define el_wset  el_set
80 #define el_wget  el_get
81 #define el_wline el_line
82 #define el_winsertstr el_insertstr
83 #define  el_wdeletestr el_deletestr
84 
85 #endif // #if LLDB_EDITLINE_USE_WCHAR
86 
87 bool
IsOnlySpaces(const EditLineStringType & content)88 IsOnlySpaces (const EditLineStringType & content)
89 {
90     for (wchar_t ch : content)
91     {
92         if (ch != EditLineCharType(' '))
93             return false;
94     }
95     return true;
96 }
97 
98 EditLineStringType
CombineLines(const std::vector<EditLineStringType> & lines)99 CombineLines (const std::vector<EditLineStringType> & lines)
100 {
101     EditLineStringStreamType combined_stream;
102     for (EditLineStringType line : lines)
103     {
104         combined_stream << line.c_str() << "\n";
105     }
106     return combined_stream.str();
107 }
108 
109 std::vector<EditLineStringType>
SplitLines(const EditLineStringType & input)110 SplitLines (const EditLineStringType & input)
111 {
112     std::vector<EditLineStringType> result;
113     size_t start = 0;
114     while (start < input.length())
115     {
116         size_t end = input.find ('\n', start);
117         if (end == std::string::npos)
118         {
119             result.insert (result.end(), input.substr (start));
120             break;
121         }
122         result.insert (result.end(), input.substr (start, end - start));
123         start = end + 1;
124     }
125     return result;
126 }
127 
128 EditLineStringType
FixIndentation(const EditLineStringType & line,int indent_correction)129 FixIndentation (const EditLineStringType & line, int indent_correction)
130 {
131     if (indent_correction == 0)
132         return line;
133     if (indent_correction < 0)
134         return line.substr (-indent_correction);
135     return EditLineStringType (indent_correction, EditLineCharType(' ')) + line;
136 }
137 
138 int
GetIndentation(const EditLineStringType & line)139 GetIndentation (const EditLineStringType & line)
140 {
141     int space_count = 0;
142     for (EditLineCharType ch : line)
143     {
144         if (ch != EditLineCharType(' '))
145             break;
146         ++space_count;
147     }
148     return space_count;
149 }
150 
151 bool
IsInputPending(FILE * file)152 IsInputPending (FILE * file)
153 {
154     // FIXME: This will be broken on Windows if we ever re-enable Editline.  You can't use select
155     // on something that isn't a socket.  This will have to be re-written to not use a FILE*, but
156     // instead use some kind of yet-to-be-created abstraction that select-like functionality on
157     // non-socket objects.
158     const int fd = fileno (file);
159     fd_set fds;
160     FD_ZERO (&fds);
161     FD_SET (fd, &fds);
162     timeval timeout = { 0, 0 };
163     return select (fd + 1, &fds, NULL, NULL, &timeout);
164 }
165 
166 namespace lldb_private
167 {
168     namespace line_editor
169     {
170         typedef std::weak_ptr<EditlineHistory> EditlineHistoryWP;
171 
172         // EditlineHistory objects are sometimes shared between multiple
173         // Editline instances with the same program name.
174 
175         class EditlineHistory
176         {
177         private:
178             // Use static GetHistory() function to get a EditlineHistorySP to one of these objects
EditlineHistory(const std::string & prefix,uint32_t size,bool unique_entries)179             EditlineHistory (const std::string &prefix, uint32_t size, bool unique_entries) :
180                 m_history (NULL),
181                 m_event (),
182                 m_prefix (prefix),
183                 m_path ()
184             {
185                 m_history = history_winit();
186                 history_w (m_history, &m_event, H_SETSIZE, size);
187                 if (unique_entries)
188                     history_w (m_history, &m_event, H_SETUNIQUE, 1);
189             }
190 
191             const char *
GetHistoryFilePath()192             GetHistoryFilePath()
193             {
194                 if (m_path.empty() && m_history && !m_prefix.empty())
195                 {
196                     FileSpec parent_path{"~/.lldb", true};
197                     char history_path[PATH_MAX];
198                     if (FileSystem::MakeDirectory(parent_path, lldb::eFilePermissionsDirectoryDefault).Success())
199                     {
200                         snprintf (history_path, sizeof (history_path), "~/.lldb/%s-history", m_prefix.c_str());
201                     }
202                     else
203                     {
204                         snprintf (history_path, sizeof (history_path), "~/%s-widehistory", m_prefix.c_str());
205                     }
206                     m_path = std::move (FileSpec (history_path, true).GetPath());
207                 }
208                 if (m_path.empty())
209                     return NULL;
210                 return m_path.c_str();
211             }
212 
213         public:
214 
~EditlineHistory()215             ~EditlineHistory()
216             {
217                 Save();
218 
219                 if (m_history)
220                 {
221                     history_wend (m_history);
222                     m_history = NULL;
223                 }
224             }
225 
226             static EditlineHistorySP
GetHistory(const std::string & prefix)227             GetHistory (const std::string &prefix)
228             {
229                 typedef std::map<std::string, EditlineHistoryWP> WeakHistoryMap;
230                 static Mutex g_mutex (Mutex::eMutexTypeRecursive);
231                 static WeakHistoryMap g_weak_map;
232                 Mutex::Locker locker (g_mutex);
233                 WeakHistoryMap::const_iterator pos = g_weak_map.find (prefix);
234                 EditlineHistorySP history_sp;
235                 if (pos != g_weak_map.end())
236                 {
237                     history_sp = pos->second.lock();
238                     if (history_sp)
239                         return history_sp;
240                     g_weak_map.erase (pos);
241                 }
242                 history_sp.reset (new EditlineHistory (prefix, 800, true));
243                 g_weak_map[prefix] = history_sp;
244                 return history_sp;
245             }
246 
IsValid() const247             bool IsValid() const
248             {
249                 return m_history != NULL;
250             }
251 
252             HistoryW *
GetHistoryPtr()253             GetHistoryPtr ()
254             {
255                 return m_history;
256             }
257 
258             void
Enter(const EditLineCharType * line_cstr)259             Enter (const EditLineCharType *line_cstr)
260             {
261                 if (m_history)
262                     history_w (m_history, &m_event, H_ENTER, line_cstr);
263             }
264 
265             bool
Load()266             Load ()
267             {
268                 if (m_history)
269                 {
270                     const char *path = GetHistoryFilePath();
271                     if (path)
272                     {
273                         history_w (m_history, &m_event, H_LOAD, path);
274                         return true;
275                     }
276                 }
277                 return false;
278             }
279 
280             bool
Save()281             Save ()
282             {
283                 if (m_history)
284                 {
285                     const char *path = GetHistoryFilePath();
286                     if (path)
287                     {
288                         history_w (m_history, &m_event, H_SAVE, path);
289                         return true;
290                     }
291                 }
292                 return false;
293             }
294 
295         protected:
296             HistoryW * m_history;   // The history object
297             HistEventW m_event;      // The history event needed to contain all history events
298             std::string m_prefix;     // The prefix name (usually the editline program name) to use when loading/saving history
299             std::string m_path;       // Path to the history file
300         };
301     }
302 }
303 
304 //------------------------------------------------------------------
305 // Editline private methods
306 //------------------------------------------------------------------
307 
308 void
SetBaseLineNumber(int line_number)309 Editline::SetBaseLineNumber (int line_number)
310 {
311     std::stringstream line_number_stream;
312     line_number_stream << line_number;
313     m_base_line_number = line_number;
314     m_line_number_digits = std::max (3, (int)line_number_stream.str().length() + 1);
315 }
316 
317 std::string
PromptForIndex(int line_index)318 Editline::PromptForIndex (int line_index)
319 {
320     bool use_line_numbers = m_multiline_enabled && m_base_line_number > 0;
321     std::string prompt = m_set_prompt;
322     if (use_line_numbers && prompt.length() == 0)
323     {
324         prompt = ": ";
325     }
326     std::string continuation_prompt = prompt;
327     if (m_set_continuation_prompt.length() > 0)
328     {
329         continuation_prompt = m_set_continuation_prompt;
330 
331         // Ensure that both prompts are the same length through space padding
332         while (continuation_prompt.length() < prompt.length())
333         {
334             continuation_prompt += ' ';
335         }
336         while (prompt.length() < continuation_prompt.length())
337         {
338             prompt += ' ';
339         }
340     }
341 
342     if (use_line_numbers)
343     {
344         StreamString prompt_stream;
345         prompt_stream.Printf("%*d%s", m_line_number_digits, m_base_line_number + line_index,
346                              (line_index == 0) ? prompt.c_str() : continuation_prompt.c_str());
347         return std::move (prompt_stream.GetString());
348     }
349     return (line_index == 0) ? prompt : continuation_prompt;
350 }
351 
352 void
SetCurrentLine(int line_index)353 Editline::SetCurrentLine (int line_index)
354 {
355     m_current_line_index = line_index;
356     m_current_prompt = PromptForIndex (line_index);
357 }
358 
359 int
GetPromptWidth()360 Editline::GetPromptWidth()
361 {
362     return (int)PromptForIndex (0).length();
363 }
364 
365 bool
IsEmacs()366 Editline::IsEmacs()
367 {
368     const char * editor;
369     el_get (m_editline, EL_EDITOR, &editor);
370     return editor[0] == 'e';
371 }
372 
373 bool
IsOnlySpaces()374 Editline::IsOnlySpaces()
375 {
376     const LineInfoW * info = el_wline (m_editline);
377     for (const EditLineCharType * character = info->buffer; character < info->lastchar; character++)
378     {
379         if (*character != ' ')
380             return false;
381     }
382     return true;
383 }
384 
385 int
GetLineIndexForLocation(CursorLocation location,int cursor_row)386 Editline::GetLineIndexForLocation (CursorLocation location, int cursor_row)
387 {
388     int line = 0;
389     if (location == CursorLocation::EditingPrompt || location == CursorLocation::BlockEnd ||
390         location == CursorLocation::EditingCursor)
391     {
392         for (unsigned index = 0; index < m_current_line_index; index++)
393         {
394             line += CountRowsForLine (m_input_lines[index]);
395         }
396         if (location == CursorLocation::EditingCursor)
397         {
398             line += cursor_row;
399         }
400         else if (location == CursorLocation::BlockEnd)
401         {
402             for (unsigned index = m_current_line_index; index < m_input_lines.size(); index++)
403             {
404                 line += CountRowsForLine (m_input_lines[index]);
405             }
406             --line;
407         }
408     }
409     return line;
410 }
411 
412 void
MoveCursor(CursorLocation from,CursorLocation to)413 Editline::MoveCursor (CursorLocation from, CursorLocation to)
414 {
415     const LineInfoW * info = el_wline (m_editline);
416     int editline_cursor_position = (int)((info->cursor - info->buffer) + GetPromptWidth());
417     int editline_cursor_row = editline_cursor_position / m_terminal_width;
418 
419     // Determine relative starting and ending lines
420     int fromLine = GetLineIndexForLocation (from, editline_cursor_row);
421     int toLine = GetLineIndexForLocation (to, editline_cursor_row);
422     if (toLine != fromLine)
423     {
424         fprintf (m_output_file, (toLine > fromLine) ? ANSI_DOWN_N_ROWS : ANSI_UP_N_ROWS, std::abs (toLine - fromLine));
425     }
426 
427     // Determine target column
428     int toColumn = 1;
429     if (to == CursorLocation::EditingCursor)
430     {
431         toColumn = editline_cursor_position - (editline_cursor_row * m_terminal_width) + 1;
432     }
433     else if (to == CursorLocation::BlockEnd)
434     {
435         toColumn = ((m_input_lines[m_input_lines.size() - 1].length() + GetPromptWidth()) % 80) + 1;
436     }
437     fprintf (m_output_file, ANSI_SET_COLUMN_N, toColumn);
438 }
439 
440 void
DisplayInput(int firstIndex)441 Editline::DisplayInput (int firstIndex)
442 {
443     fprintf (m_output_file, ANSI_SET_COLUMN_N ANSI_CLEAR_BELOW, 1);
444     int line_count = (int)m_input_lines.size();
445     const char *faint  = m_color_prompts ? ANSI_FAINT : "";
446     const char *unfaint  = m_color_prompts ? ANSI_UNFAINT : "";
447 
448     for (int index = firstIndex; index < line_count; index++)
449     {
450         fprintf (m_output_file, "%s" "%s" "%s" EditLineStringFormatSpec " ",
451                 faint,
452                 PromptForIndex (index).c_str(),
453                 unfaint,
454                 m_input_lines[index].c_str());
455         if (index < line_count - 1)
456             fprintf (m_output_file, "\n");
457     }
458 }
459 
460 
461 int
CountRowsForLine(const EditLineStringType & content)462 Editline::CountRowsForLine (const EditLineStringType & content)
463 {
464     auto prompt = PromptForIndex (0); // Prompt width is constant during an edit session
465     int line_length = (int)(content.length() + prompt.length());
466     return (line_length / m_terminal_width) + 1;
467 }
468 
469 void
SaveEditedLine()470 Editline::SaveEditedLine()
471 {
472     const LineInfoW * info = el_wline (m_editline);
473     m_input_lines[m_current_line_index] = EditLineStringType (info->buffer, info->lastchar - info->buffer);
474 }
475 
476 StringList
GetInputAsStringList(int line_count)477 Editline::GetInputAsStringList(int line_count)
478 {
479     StringList lines;
480     for (EditLineStringType line : m_input_lines)
481     {
482         if (line_count == 0)
483             break;
484 #if LLDB_EDITLINE_USE_WCHAR
485         lines.AppendString (m_utf8conv.to_bytes (line));
486 #else
487         lines.AppendString(line);
488 #endif
489         --line_count;
490     }
491     return lines;
492 }
493 
494 unsigned char
RecallHistory(bool earlier)495 Editline::RecallHistory (bool earlier)
496 {
497     if (!m_history_sp || !m_history_sp->IsValid())
498         return CC_ERROR;
499 
500     HistoryW * pHistory = m_history_sp->GetHistoryPtr();
501     HistEventW history_event;
502     std::vector<EditLineStringType> new_input_lines;
503 
504     // Treat moving from the "live" entry differently
505     if (!m_in_history)
506     {
507         if (earlier == false)
508             return CC_ERROR; // Can't go newer than the "live" entry
509         if (history_w (pHistory, &history_event, H_FIRST) == -1)
510             return CC_ERROR;
511 
512         // Save any edits to the "live" entry in case we return by moving forward in history
513         // (it would be more bash-like to save over any current entry, but libedit doesn't
514         // offer the ability to add entries anywhere except the end.)
515         SaveEditedLine();
516         m_live_history_lines = m_input_lines;
517         m_in_history = true;
518     }
519     else
520     {
521         if (history_w (pHistory, &history_event, earlier ? H_NEXT : H_PREV) == -1)
522         {
523             // Can't move earlier than the earliest entry
524             if (earlier)
525                 return CC_ERROR;
526 
527             // ... but moving to newer than the newest yields the "live" entry
528             new_input_lines = m_live_history_lines;
529             m_in_history = false;
530         }
531     }
532 
533     // If we're pulling the lines from history, split them apart
534     if (m_in_history)
535         new_input_lines = SplitLines (history_event.str);
536 
537     // Erase the current edit session and replace it with a new one
538     MoveCursor (CursorLocation::EditingCursor, CursorLocation::BlockStart);
539     m_input_lines = new_input_lines;
540     DisplayInput();
541 
542     // Prepare to edit the last line when moving to previous entry, or the first line
543     // when moving to next entry
544     SetCurrentLine (m_current_line_index = earlier ? (int)m_input_lines.size() - 1 : 0);
545     MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
546     return CC_NEWLINE;
547 }
548 
549 int
GetCharacter(EditLineCharType * c)550 Editline::GetCharacter (EditLineCharType * c)
551 {
552     const LineInfoW * info = el_wline (m_editline);
553 
554     // Paint a faint version of the desired prompt over the version libedit draws
555     // (will only be requested if colors are supported)
556     if (m_needs_prompt_repaint)
557     {
558         MoveCursor (CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
559         fprintf (m_output_file, "%s" "%s" "%s", ANSI_FAINT, Prompt(), ANSI_UNFAINT);
560         MoveCursor (CursorLocation::EditingPrompt, CursorLocation::EditingCursor);
561         m_needs_prompt_repaint = false;
562     }
563 
564     if (m_multiline_enabled)
565     {
566         // Detect when the number of rows used for this input line changes due to an edit
567         int lineLength = (int)((info->lastchar - info->buffer) + GetPromptWidth());
568         int new_line_rows = (lineLength / m_terminal_width) + 1;
569         if (m_current_line_rows != -1 && new_line_rows != m_current_line_rows)
570         {
571             // Respond by repainting the current state from this line on
572             MoveCursor (CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
573             SaveEditedLine();
574             DisplayInput (m_current_line_index);
575             MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingCursor);
576         }
577         m_current_line_rows = new_line_rows;
578     }
579 
580     // Read an actual character
581     while (true)
582     {
583         lldb::ConnectionStatus status = lldb::eConnectionStatusSuccess;
584         char ch = 0;
585 
586         // This mutex is locked by our caller (GetLine). Unlock it while we read a character
587         // (blocking operation), so we do not hold the mutex indefinitely. This gives a chance
588         // for someone to interrupt us. After Read returns, immediately lock the mutex again and
589         // check if we were interrupted.
590         m_output_mutex.Unlock();
591         int read_count = m_input_connection.Read(&ch, 1, UINT32_MAX, status, NULL);
592         m_output_mutex.Lock();
593         if (m_editor_status == EditorStatus::Interrupted)
594         {
595             while (read_count > 0 && status == lldb::eConnectionStatusSuccess)
596                 read_count = m_input_connection.Read(&ch, 1, UINT32_MAX, status, NULL);
597             lldbassert(status == lldb::eConnectionStatusInterrupted);
598             return 0;
599         }
600 
601         if (read_count)
602         {
603 #if LLDB_EDITLINE_USE_WCHAR
604             // After the initial interruptible read, this is guaranteed not to block
605             ungetc (ch, m_input_file);
606             *c = fgetwc (m_input_file);
607             if (*c != WEOF)
608                 return 1;
609 #else
610             *c = ch;
611             if(ch != (char)EOF)
612                 return 1;
613 #endif
614         }
615         else
616         {
617             switch (status)
618             {
619                 case lldb::eConnectionStatusSuccess:         // Success
620                     break;
621 
622                 case lldb::eConnectionStatusInterrupted:
623                     lldbassert(0 && "Interrupts should have been handled above.");
624 
625                 case lldb::eConnectionStatusError:           // Check GetError() for details
626                 case lldb::eConnectionStatusTimedOut:        // Request timed out
627                 case lldb::eConnectionStatusEndOfFile:       // End-of-file encountered
628                 case lldb::eConnectionStatusNoConnection:    // No connection
629                 case lldb::eConnectionStatusLostConnection:  // Lost connection while connected to a valid connection
630                     m_editor_status = EditorStatus::EndOfInput;
631                     return 0;
632             }
633         }
634     }
635 }
636 
637 const char *
Prompt()638 Editline::Prompt()
639 {
640     if (m_color_prompts)
641         m_needs_prompt_repaint = true;
642     return m_current_prompt.c_str();
643 }
644 
645 unsigned char
BreakLineCommand(int ch)646 Editline::BreakLineCommand (int ch)
647 {
648     // Preserve any content beyond the cursor, truncate and save the current line
649     const LineInfoW * info = el_wline (m_editline);
650     auto current_line = EditLineStringType (info->buffer, info->cursor - info->buffer);
651     auto new_line_fragment = EditLineStringType (info->cursor, info->lastchar - info->cursor);
652     m_input_lines[m_current_line_index] = current_line;
653 
654     // Ignore whitespace-only extra fragments when breaking a line
655     if (::IsOnlySpaces (new_line_fragment))
656         new_line_fragment = EditLineConstString("");
657 
658     // Establish the new cursor position at the start of a line when inserting a line break
659     m_revert_cursor_index = 0;
660 
661     // Don't perform end of input detection or automatic formatting when pasting
662     if (!IsInputPending (m_input_file))
663     {
664         // If this is the end of the last line, treat this as a potential exit
665         if (m_current_line_index == m_input_lines.size() - 1 && new_line_fragment.length() == 0)
666         {
667             bool end_of_input = true;
668             if (m_is_input_complete_callback)
669             {
670                 SaveEditedLine();
671                 auto lines = GetInputAsStringList();
672                 end_of_input = m_is_input_complete_callback (this, lines, m_is_input_complete_callback_baton);
673 
674                 // The completion test is allowed to change the input lines when complete
675                 if (end_of_input)
676                 {
677                     m_input_lines.clear();
678                     for (unsigned index = 0; index < lines.GetSize(); index++)
679                     {
680 #if LLDB_EDITLINE_USE_WCHAR
681                         m_input_lines.insert (m_input_lines.end(), m_utf8conv.from_bytes (lines[index]));
682 #else
683                         m_input_lines.insert (m_input_lines.end(), lines[index]);
684 #endif
685                     }
686                 }
687             }
688             if (end_of_input)
689             {
690                 fprintf (m_output_file, "\n");
691                 m_editor_status = EditorStatus::Complete;
692                 return CC_NEWLINE;
693             }
694         }
695 
696         // Apply smart indentation
697         if (m_fix_indentation_callback)
698         {
699             StringList lines = GetInputAsStringList (m_current_line_index + 1);
700 #if LLDB_EDITLINE_USE_WCHAR
701             lines.AppendString (m_utf8conv.to_bytes (new_line_fragment));
702 #else
703             lines.AppendString (new_line_fragment);
704 #endif
705 
706             int indent_correction = m_fix_indentation_callback (this, lines, 0, m_fix_indentation_callback_baton);
707             new_line_fragment = FixIndentation(new_line_fragment, indent_correction);
708             m_revert_cursor_index = GetIndentation(new_line_fragment);
709         }
710     }
711 
712     // Insert the new line and repaint everything from the split line on down
713     m_input_lines.insert (m_input_lines.begin() + m_current_line_index + 1, new_line_fragment);
714     MoveCursor (CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
715     DisplayInput (m_current_line_index);
716 
717     // Reposition the cursor to the right line and prepare to edit the new line
718     SetCurrentLine (m_current_line_index + 1);
719     MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
720     return CC_NEWLINE;
721 }
722 
723 unsigned char
DeleteNextCharCommand(int ch)724 Editline::DeleteNextCharCommand (int ch)
725 {
726     LineInfoW * info = const_cast<LineInfoW *>(el_wline (m_editline));
727 
728     // Just delete the next character normally if possible
729     if (info->cursor < info->lastchar)
730     {
731         info->cursor++;
732         el_deletestr (m_editline, 1);
733         return CC_REFRESH;
734     }
735 
736     // Fail when at the end of the last line, except when ^D is pressed on
737     // the line is empty, in which case it is treated as EOF
738     if (m_current_line_index == m_input_lines.size() - 1)
739     {
740         if (ch == 4 && info->buffer == info->lastchar)
741         {
742             fprintf (m_output_file, "^D\n");
743             m_editor_status = EditorStatus::EndOfInput;
744             return CC_EOF;
745         }
746         return CC_ERROR;
747     }
748 
749     // Prepare to combine this line with the one below
750     MoveCursor (CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
751 
752     // Insert the next line of text at the cursor and restore the cursor position
753     const EditLineCharType * cursor = info->cursor;
754     el_winsertstr (m_editline, m_input_lines[m_current_line_index + 1].c_str());
755     info->cursor = cursor;
756     SaveEditedLine();
757 
758     // Delete the extra line
759     m_input_lines.erase (m_input_lines.begin() + m_current_line_index + 1);
760 
761     // Clear and repaint from this line on down
762     DisplayInput (m_current_line_index);
763     MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingCursor);
764     return CC_REFRESH;
765 }
766 
767 unsigned char
DeletePreviousCharCommand(int ch)768 Editline::DeletePreviousCharCommand (int ch)
769 {
770     LineInfoW * info = const_cast<LineInfoW *>(el_wline (m_editline));
771 
772     // Just delete the previous character normally when not at the start of a line
773     if (info->cursor > info->buffer)
774     {
775         el_deletestr (m_editline, 1);
776         return CC_REFRESH;
777     }
778 
779     // No prior line and no prior character?  Let the user know
780     if (m_current_line_index == 0)
781         return CC_ERROR;
782 
783     // No prior character, but prior line?  Combine with the line above
784     SaveEditedLine();
785     SetCurrentLine (m_current_line_index - 1);
786     auto priorLine = m_input_lines[m_current_line_index];
787     m_input_lines.erase (m_input_lines.begin() + m_current_line_index);
788     m_input_lines[m_current_line_index] = priorLine + m_input_lines[m_current_line_index];
789 
790     // Repaint from the new line down
791     fprintf (m_output_file, ANSI_UP_N_ROWS ANSI_SET_COLUMN_N, CountRowsForLine (priorLine), 1);
792     DisplayInput (m_current_line_index);
793 
794     // Put the cursor back where libedit expects it to be before returning to editing
795     // by telling libedit about the newly inserted text
796     MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
797     el_winsertstr (m_editline, priorLine.c_str());
798     return CC_REDISPLAY;
799 }
800 
801 unsigned char
PreviousLineCommand(int ch)802 Editline::PreviousLineCommand (int ch)
803 {
804     SaveEditedLine();
805 
806     if (m_current_line_index == 0) {
807         return RecallHistory (true);
808     }
809 
810     // Start from a known location
811     MoveCursor (CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
812 
813     // Treat moving up from a blank last line as a deletion of that line
814     if (m_current_line_index == m_input_lines.size() - 1 && IsOnlySpaces())
815     {
816         m_input_lines.erase (m_input_lines.begin() + m_current_line_index);
817         fprintf (m_output_file, ANSI_CLEAR_BELOW);
818     }
819 
820     SetCurrentLine (m_current_line_index - 1);
821     fprintf (m_output_file, ANSI_UP_N_ROWS ANSI_SET_COLUMN_N,
822             CountRowsForLine (m_input_lines[m_current_line_index]), 1);
823     return CC_NEWLINE;
824 }
825 
826 unsigned char
NextLineCommand(int ch)827 Editline::NextLineCommand (int ch)
828 {
829     SaveEditedLine();
830 
831     // Handle attempts to move down from the last line
832     if (m_current_line_index == m_input_lines.size() - 1)
833     {
834         // Don't add an extra line if the existing last line is blank, move through history instead
835         if (IsOnlySpaces())
836         {
837             return RecallHistory (false);
838         }
839 
840         // Determine indentation for the new line
841         int indentation = 0;
842         if (m_fix_indentation_callback)
843         {
844             StringList lines = GetInputAsStringList();
845             lines.AppendString("");
846             indentation = m_fix_indentation_callback (this, lines, 0, m_fix_indentation_callback_baton);
847         }
848         m_input_lines.insert (m_input_lines.end(), EditLineStringType (indentation, EditLineCharType(' ')));
849     }
850 
851     // Move down past the current line using newlines to force scrolling if needed
852     SetCurrentLine (m_current_line_index + 1);
853     const LineInfoW * info = el_wline (m_editline);
854     int cursor_position = (int)((info->cursor - info->buffer) + GetPromptWidth());
855     int cursor_row = cursor_position / m_terminal_width;
856     for (int line_count = 0; line_count < m_current_line_rows - cursor_row; line_count++)
857     {
858         fprintf (m_output_file, "\n");
859     }
860     return CC_NEWLINE;
861 }
862 
863 unsigned char
FixIndentationCommand(int ch)864 Editline::FixIndentationCommand (int ch)
865 {
866     if (!m_fix_indentation_callback)
867         return CC_NORM;
868 
869     // Insert the character by hand prior to correction
870     EditLineCharType inserted[] = { (EditLineCharType)ch, 0 };
871     el_winsertstr (m_editline, inserted);
872     SaveEditedLine();
873     StringList lines = GetInputAsStringList (m_current_line_index + 1);
874 
875     // Determine the cursor position
876     LineInfoW * info = const_cast<LineInfoW *>(el_wline (m_editline));
877     int cursor_position = info->cursor - info->buffer;
878 
879     int indent_correction = m_fix_indentation_callback (this, lines, cursor_position, m_fix_indentation_callback_baton);
880 
881     // Adjust the input buffer to correct indentation
882     if (indent_correction > 0)
883     {
884         info->cursor = info->buffer;
885         el_winsertstr (m_editline, EditLineStringType (indent_correction, EditLineCharType(' ')).c_str());
886     }
887     else if (indent_correction < 0)
888     {
889         info->cursor = info->buffer - indent_correction;
890         el_wdeletestr (m_editline, -indent_correction);
891     }
892     info->cursor = info->buffer + cursor_position + indent_correction;
893     return CC_REFRESH;
894 }
895 
896 unsigned char
RevertLineCommand(int ch)897 Editline::RevertLineCommand (int ch)
898 {
899     el_winsertstr (m_editline, m_input_lines[m_current_line_index].c_str());
900     if (m_revert_cursor_index >= 0)
901     {
902         LineInfoW * info = const_cast<LineInfoW *>(el_wline (m_editline));
903         info->cursor = info->buffer + m_revert_cursor_index;
904         if (info->cursor > info->lastchar)
905         {
906             info->cursor = info->lastchar;
907         }
908         m_revert_cursor_index = -1;
909     }
910     return CC_REFRESH;
911 }
912 
913 unsigned char
BufferStartCommand(int ch)914 Editline::BufferStartCommand (int ch)
915 {
916     SaveEditedLine();
917     MoveCursor (CursorLocation::EditingCursor, CursorLocation::BlockStart);
918     SetCurrentLine (0);
919     m_revert_cursor_index = 0;
920     return CC_NEWLINE;
921 }
922 
923 unsigned char
BufferEndCommand(int ch)924 Editline::BufferEndCommand (int ch)
925 {
926     SaveEditedLine();
927     MoveCursor (CursorLocation::EditingCursor, CursorLocation::BlockEnd);
928     SetCurrentLine ((int)m_input_lines.size() - 1);
929     MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
930     return CC_NEWLINE;
931 }
932 
933 unsigned char
TabCommand(int ch)934 Editline::TabCommand (int ch)
935 {
936     if (m_completion_callback == nullptr)
937         return CC_ERROR;
938 
939     const LineInfo *line_info  = el_line (m_editline);
940     StringList completions;
941     int page_size = 40;
942 
943     const int num_completions = m_completion_callback (line_info->buffer,
944                                                        line_info->cursor,
945                                                        line_info->lastchar,
946                                                        0,     // Don't skip any matches (start at match zero)
947                                                        -1,    // Get all the matches
948                                                        completions,
949                                                        m_completion_callback_baton);
950 
951     if (num_completions == 0)
952         return CC_ERROR;
953     //    if (num_completions == -1)
954     //    {
955     //        el_insertstr (m_editline, m_completion_key);
956     //        return CC_REDISPLAY;
957     //    }
958     //    else
959     if (num_completions == -2)
960     {
961         // Replace the entire line with the first string...
962         el_deletestr (m_editline, line_info->cursor - line_info->buffer);
963         el_insertstr (m_editline, completions.GetStringAtIndex (0));
964         return CC_REDISPLAY;
965     }
966 
967     // If we get a longer match display that first.
968     const char *completion_str = completions.GetStringAtIndex (0);
969     if (completion_str != nullptr && *completion_str != '\0')
970     {
971         el_insertstr (m_editline, completion_str);
972         return CC_REDISPLAY;
973     }
974 
975     if (num_completions > 1)
976     {
977         int num_elements = num_completions + 1;
978         fprintf (m_output_file, "\n" ANSI_CLEAR_BELOW "Available completions:");
979         if (num_completions < page_size)
980         {
981             for (int i = 1; i < num_elements; i++)
982             {
983                 completion_str = completions.GetStringAtIndex (i);
984                 fprintf (m_output_file, "\n\t%s", completion_str);
985             }
986             fprintf (m_output_file, "\n");
987         }
988         else
989         {
990             int cur_pos = 1;
991             char reply;
992             int got_char;
993             while (cur_pos < num_elements)
994             {
995                 int endpoint = cur_pos + page_size;
996                 if (endpoint > num_elements)
997                     endpoint = num_elements;
998                 for (; cur_pos < endpoint; cur_pos++)
999                 {
1000                     completion_str = completions.GetStringAtIndex (cur_pos);
1001                     fprintf (m_output_file, "\n\t%s", completion_str);
1002                 }
1003 
1004                 if (cur_pos >= num_elements)
1005                 {
1006                     fprintf (m_output_file, "\n");
1007                     break;
1008                 }
1009 
1010                 fprintf (m_output_file, "\nMore (Y/n/a): ");
1011                 reply = 'n';
1012                 got_char = el_getc(m_editline, &reply);
1013                 if (got_char == -1 || reply == 'n')
1014                     break;
1015                 if (reply == 'a')
1016                     page_size = num_elements - cur_pos;
1017             }
1018         }
1019         DisplayInput();
1020         MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
1021     }
1022     return CC_REDISPLAY;
1023 }
1024 
1025 void
ConfigureEditor(bool multiline)1026 Editline::ConfigureEditor (bool multiline)
1027 {
1028     if (m_editline && m_multiline_enabled == multiline)
1029         return;
1030     m_multiline_enabled = multiline;
1031 
1032     if (m_editline)
1033     {
1034         // Disable edit mode to stop the terminal from flushing all input
1035         // during the call to el_end() since we expect to have multiple editline
1036         // instances in this program.
1037         el_set (m_editline, EL_EDITMODE, 0);
1038         el_end (m_editline);
1039     }
1040 
1041     m_editline = el_init (m_editor_name.c_str(), m_input_file, m_output_file, m_error_file);
1042     TerminalSizeChanged();
1043 
1044     if (m_history_sp && m_history_sp->IsValid())
1045     {
1046         m_history_sp->Load();
1047         el_wset (m_editline, EL_HIST, history, m_history_sp->GetHistoryPtr());
1048     }
1049     el_set (m_editline, EL_CLIENTDATA, this);
1050     el_set (m_editline, EL_SIGNAL, 0);
1051     el_set (m_editline, EL_EDITOR, "emacs");
1052     el_set (m_editline, EL_PROMPT, (EditlinePromptCallbackType)([] (EditLine *editline) {
1053         return Editline::InstanceFor (editline)->Prompt();
1054     }));
1055 
1056     el_wset (m_editline, EL_GETCFN,
1057             (EditlineGetCharCallbackType)([] (EditLine * editline, EditLineCharType * c) {
1058                 return Editline::InstanceFor (editline)->GetCharacter (c);
1059             }));
1060 
1061     // Commands used for multiline support, registered whether or not they're used
1062     el_set (m_editline, EL_ADDFN, "lldb-break-line", "Insert a line break",
1063            (EditlineCommandCallbackType)([] (EditLine * editline, int ch) {
1064                return Editline::InstanceFor (editline)->BreakLineCommand (ch);
1065            }));
1066     el_set (m_editline, EL_ADDFN, "lldb-delete-next-char", "Delete next character",
1067            (EditlineCommandCallbackType)([] (EditLine * editline, int ch) {
1068                return Editline::InstanceFor (editline)->DeleteNextCharCommand (ch);
1069            }));
1070     el_set (m_editline, EL_ADDFN, "lldb-delete-previous-char", "Delete previous character",
1071            (EditlineCommandCallbackType)([] (EditLine * editline, int ch) {
1072                return Editline::InstanceFor (editline)->DeletePreviousCharCommand (ch);
1073            }));
1074     el_set (m_editline, EL_ADDFN, "lldb-previous-line", "Move to previous line",
1075            (EditlineCommandCallbackType)([] (EditLine * editline, int ch) {
1076                return Editline::InstanceFor (editline)->PreviousLineCommand (ch);
1077            }));
1078     el_set (m_editline, EL_ADDFN, "lldb-next-line", "Move to next line",
1079            (EditlineCommandCallbackType)([] (EditLine * editline, int ch) {
1080                return Editline::InstanceFor (editline)->NextLineCommand (ch);
1081            }));
1082     el_set (m_editline, EL_ADDFN, "lldb-buffer-start", "Move to start of buffer",
1083            (EditlineCommandCallbackType)([] (EditLine * editline, int ch) {
1084                return Editline::InstanceFor (editline)->BufferStartCommand (ch);
1085            }));
1086     el_set (m_editline, EL_ADDFN, "lldb-buffer-end", "Move to end of buffer",
1087            (EditlineCommandCallbackType)([] (EditLine * editline, int ch) {
1088                 return Editline::InstanceFor (editline)->BufferEndCommand (ch);
1089             }));
1090     el_set (m_editline, EL_ADDFN, "lldb-fix-indentation", "Fix line indentation",
1091            (EditlineCommandCallbackType)([] (EditLine * editline, int ch) {
1092                 return Editline::InstanceFor (editline)->FixIndentationCommand (ch);
1093             }));
1094 
1095     // Register the complete callback under two names for compatibility with older clients using
1096     // custom .editrc files (largely becuase libedit has a bad bug where if you have a bind command
1097     // that tries to bind to a function name that doesn't exist, it can corrupt the heap and
1098     // crash your process later.)
1099     EditlineCommandCallbackType complete_callback = [] (EditLine * editline, int ch) {
1100         return Editline::InstanceFor (editline)->TabCommand (ch);
1101     };
1102     el_set (m_editline, EL_ADDFN, "lldb-complete", "Invoke completion", complete_callback);
1103     el_set (m_editline, EL_ADDFN, "lldb_complete", "Invoke completion", complete_callback);
1104 
1105     // General bindings we don't mind being overridden
1106     if (!multiline) {
1107         el_set (m_editline, EL_BIND, "^r", "em-inc-search-prev", NULL); // Cycle through backwards search, entering string
1108     }
1109     el_set (m_editline, EL_BIND, "^w", "ed-delete-prev-word", NULL); // Delete previous word, behave like bash in emacs mode
1110     el_set (m_editline, EL_BIND, "\t", "lldb-complete", NULL); // Bind TAB to auto complete
1111 
1112     // Allow user-specific customization prior to registering bindings we absolutely require
1113     el_source (m_editline, NULL);
1114 
1115     // Register an internal binding that external developers shouldn't use
1116     el_set (m_editline, EL_ADDFN, "lldb-revert-line", "Revert line to saved state",
1117            (EditlineCommandCallbackType)([] (EditLine * editline, int ch) {
1118                return Editline::InstanceFor (editline)->RevertLineCommand (ch);
1119            }));
1120 
1121     // Register keys that perform auto-indent correction
1122     if (m_fix_indentation_callback && m_fix_indentation_callback_chars)
1123     {
1124         char bind_key[2] = { 0, 0 };
1125         const char * indent_chars = m_fix_indentation_callback_chars;
1126         while (*indent_chars)
1127         {
1128             bind_key[0] = *indent_chars;
1129             el_set (m_editline, EL_BIND, bind_key, "lldb-fix-indentation", NULL);
1130             ++indent_chars;
1131         }
1132     }
1133 
1134     // Multi-line editor bindings
1135     if (multiline)
1136     {
1137         el_set (m_editline, EL_BIND, "\n", "lldb-break-line", NULL);
1138         el_set (m_editline, EL_BIND, "\r", "lldb-break-line", NULL);
1139         el_set (m_editline, EL_BIND, "^p", "lldb-previous-line", NULL);
1140         el_set (m_editline, EL_BIND, "^n", "lldb-next-line", NULL);
1141         el_set (m_editline, EL_BIND, "^?", "lldb-delete-previous-char", NULL);
1142         el_set (m_editline, EL_BIND, "^d", "lldb-delete-next-char", NULL);
1143         el_set (m_editline, EL_BIND, ESCAPE "[3~", "lldb-delete-next-char", NULL);
1144         el_set (m_editline, EL_BIND, ESCAPE "[\\^", "lldb-revert-line", NULL);
1145 
1146         // Editor-specific bindings
1147         if (IsEmacs())
1148         {
1149             el_set (m_editline, EL_BIND, ESCAPE "<", "lldb-buffer-start", NULL);
1150             el_set (m_editline, EL_BIND, ESCAPE ">", "lldb-buffer-end", NULL);
1151             el_set (m_editline, EL_BIND, ESCAPE "[A", "lldb-previous-line", NULL);
1152             el_set (m_editline, EL_BIND, ESCAPE "[B", "lldb-next-line", NULL);
1153         }
1154         else
1155         {
1156             el_set (m_editline, EL_BIND, "^H", "lldb-delete-previous-char", NULL);
1157 
1158             el_set (m_editline, EL_BIND, "-a", ESCAPE "[A", "lldb-previous-line", NULL);
1159             el_set (m_editline, EL_BIND, "-a", ESCAPE "[B", "lldb-next-line", NULL);
1160             el_set (m_editline, EL_BIND, "-a", "x", "lldb-delete-next-char", NULL);
1161             el_set (m_editline, EL_BIND, "-a", "^H", "lldb-delete-previous-char", NULL);
1162             el_set (m_editline, EL_BIND, "-a", "^?", "lldb-delete-previous-char", NULL);
1163 
1164             // Escape is absorbed exiting edit mode, so re-register important sequences
1165             // without the prefix
1166             el_set (m_editline, EL_BIND, "-a", "[A", "lldb-previous-line", NULL);
1167             el_set (m_editline, EL_BIND, "-a", "[B", "lldb-next-line", NULL);
1168             el_set (m_editline, EL_BIND, "-a", "[\\^", "lldb-revert-line", NULL);
1169         }
1170     }
1171 }
1172 
1173 //------------------------------------------------------------------
1174 // Editline public methods
1175 //------------------------------------------------------------------
1176 
1177 Editline *
InstanceFor(EditLine * editline)1178 Editline::InstanceFor (EditLine * editline)
1179 {
1180     Editline * editor;
1181     el_get (editline, EL_CLIENTDATA, &editor);
1182     return editor;
1183 }
1184 
Editline(const char * editline_name,FILE * input_file,FILE * output_file,FILE * error_file,bool color_prompts)1185 Editline::Editline (const char * editline_name, FILE * input_file, FILE * output_file, FILE * error_file, bool color_prompts) :
1186     m_editor_status (EditorStatus::Complete),
1187     m_color_prompts(color_prompts),
1188     m_input_file (input_file),
1189     m_output_file (output_file),
1190     m_error_file (error_file),
1191     m_input_connection (fileno(input_file), false)
1192 {
1193     // Get a shared history instance
1194     m_editor_name = (editline_name == nullptr) ? "lldb-tmp" : editline_name;
1195     m_history_sp = EditlineHistory::GetHistory (m_editor_name);
1196 }
1197 
~Editline()1198 Editline::~Editline()
1199 {
1200     if (m_editline)
1201     {
1202         // Disable edit mode to stop the terminal from flushing all input
1203         // during the call to el_end() since we expect to have multiple editline
1204         // instances in this program.
1205         el_set (m_editline, EL_EDITMODE, 0);
1206         el_end (m_editline);
1207         m_editline = nullptr;
1208     }
1209 
1210     // EditlineHistory objects are sometimes shared between multiple
1211     // Editline instances with the same program name. So just release
1212     // our shared pointer and if we are the last owner, it will save the
1213     // history to the history save file automatically.
1214     m_history_sp.reset();
1215 }
1216 
1217 void
SetPrompt(const char * prompt)1218 Editline::SetPrompt (const char * prompt)
1219 {
1220     m_set_prompt = prompt == nullptr ? "" : prompt;
1221 }
1222 
1223 void
SetContinuationPrompt(const char * continuation_prompt)1224 Editline::SetContinuationPrompt (const char * continuation_prompt)
1225 {
1226     m_set_continuation_prompt = continuation_prompt == nullptr ? "" : continuation_prompt;
1227 }
1228 
1229 void
TerminalSizeChanged()1230 Editline::TerminalSizeChanged()
1231 {
1232     if (m_editline != nullptr)
1233     {
1234         el_resize (m_editline);
1235         int columns;
1236         // Despite the man page claiming non-zero indicates success, it's actually zero
1237         if (el_get (m_editline, EL_GETTC, "co", &columns) == 0)
1238         {
1239             m_terminal_width = columns;
1240             if (m_current_line_rows != -1)
1241             {
1242                 const LineInfoW * info = el_wline (m_editline);
1243                 int lineLength = (int)((info->lastchar - info->buffer) + GetPromptWidth());
1244                 m_current_line_rows = (lineLength / columns) + 1;
1245             }
1246         }
1247         else
1248         {
1249             m_terminal_width = INT_MAX;
1250             m_current_line_rows = 1;
1251         }
1252     }
1253 }
1254 
1255 const char *
GetPrompt()1256 Editline::GetPrompt()
1257 {
1258     return m_set_prompt.c_str();
1259 }
1260 
1261 uint32_t
GetCurrentLine()1262 Editline::GetCurrentLine()
1263 {
1264     return m_current_line_index;
1265 }
1266 
1267 bool
Interrupt()1268 Editline::Interrupt()
1269 {
1270     bool result = true;
1271     Mutex::Locker locker(m_output_mutex);
1272     if (m_editor_status == EditorStatus::Editing) {
1273         fprintf(m_output_file, "^C\n");
1274         result = m_input_connection.InterruptRead();
1275     }
1276     m_editor_status = EditorStatus::Interrupted;
1277     return result;
1278 }
1279 
1280 bool
Cancel()1281 Editline::Cancel()
1282 {
1283     bool result = true;
1284     Mutex::Locker locker(m_output_mutex);
1285     if (m_editor_status == EditorStatus::Editing) {
1286         MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
1287         fprintf(m_output_file, ANSI_CLEAR_BELOW);
1288         result = m_input_connection.InterruptRead();
1289     }
1290     m_editor_status = EditorStatus::Interrupted;
1291     return result;
1292 }
1293 
1294 void
SetAutoCompleteCallback(CompleteCallbackType callback,void * baton)1295 Editline::SetAutoCompleteCallback (CompleteCallbackType callback, void * baton)
1296 {
1297     m_completion_callback = callback;
1298     m_completion_callback_baton = baton;
1299 }
1300 
1301 void
SetIsInputCompleteCallback(IsInputCompleteCallbackType callback,void * baton)1302 Editline::SetIsInputCompleteCallback (IsInputCompleteCallbackType callback, void * baton)
1303 {
1304     m_is_input_complete_callback = callback;
1305     m_is_input_complete_callback_baton = baton;
1306 }
1307 
1308 bool
SetFixIndentationCallback(FixIndentationCallbackType callback,void * baton,const char * indent_chars)1309 Editline::SetFixIndentationCallback (FixIndentationCallbackType callback,
1310                                      void * baton,
1311                                      const char * indent_chars)
1312 {
1313     m_fix_indentation_callback = callback;
1314     m_fix_indentation_callback_baton = baton;
1315     m_fix_indentation_callback_chars = indent_chars;
1316     return false;
1317 }
1318 
1319 bool
GetLine(std::string & line,bool & interrupted)1320 Editline::GetLine (std::string &line, bool &interrupted)
1321 {
1322     ConfigureEditor (false);
1323     m_input_lines = std::vector<EditLineStringType>();
1324     m_input_lines.insert (m_input_lines.begin(), EditLineConstString(""));
1325 
1326     Mutex::Locker locker(m_output_mutex);
1327 
1328     lldbassert(m_editor_status != EditorStatus::Editing);
1329     if (m_editor_status == EditorStatus::Interrupted)
1330     {
1331         m_editor_status = EditorStatus::Complete;
1332         interrupted = true;
1333         return true;
1334     }
1335 
1336     SetCurrentLine (0);
1337     m_in_history = false;
1338     m_editor_status = EditorStatus::Editing;
1339     m_revert_cursor_index = -1;
1340 
1341 #ifdef USE_SETUPTERM_WORKAROUND
1342         setupterm((char *)0, fileno(m_output_file), (int *)0);
1343 #endif
1344 
1345     int count;
1346     auto input = el_wgets (m_editline, &count);
1347 
1348     interrupted = m_editor_status == EditorStatus::Interrupted;
1349     if (!interrupted)
1350     {
1351         if (input == nullptr)
1352         {
1353             fprintf (m_output_file, "\n");
1354             m_editor_status = EditorStatus::EndOfInput;
1355         }
1356         else
1357         {
1358             m_history_sp->Enter (input);
1359 #if LLDB_EDITLINE_USE_WCHAR
1360             line = m_utf8conv.to_bytes (SplitLines (input)[0]);
1361 #else
1362             line = SplitLines (input)[0];
1363 #endif
1364             m_editor_status = EditorStatus::Complete;
1365         }
1366     }
1367     return m_editor_status != EditorStatus::EndOfInput;
1368 }
1369 
1370 bool
GetLines(int first_line_number,StringList & lines,bool & interrupted)1371 Editline::GetLines (int first_line_number, StringList &lines, bool &interrupted)
1372 {
1373     ConfigureEditor (true);
1374 
1375     // Print the initial input lines, then move the cursor back up to the start of input
1376     SetBaseLineNumber (first_line_number);
1377     m_input_lines = std::vector<EditLineStringType>();
1378     m_input_lines.insert (m_input_lines.begin(), EditLineConstString(""));
1379 
1380     Mutex::Locker locker(m_output_mutex);
1381     // Begin the line editing loop
1382     DisplayInput();
1383     SetCurrentLine (0);
1384     MoveCursor (CursorLocation::BlockEnd, CursorLocation::BlockStart);
1385     m_editor_status = EditorStatus::Editing;
1386     m_in_history = false;
1387 
1388     m_revert_cursor_index = -1;
1389     while (m_editor_status == EditorStatus::Editing)
1390     {
1391 #ifdef USE_SETUPTERM_WORKAROUND
1392         setupterm((char *)0, fileno(m_output_file), (int *)0);
1393 #endif
1394         int count;
1395         m_current_line_rows = -1;
1396         el_wpush (m_editline, EditLineConstString("\x1b[^")); // Revert to the existing line content
1397         el_wgets (m_editline, &count);
1398     }
1399 
1400     interrupted = m_editor_status == EditorStatus::Interrupted;
1401     if (!interrupted)
1402     {
1403         // Save the completed entry in history before returning
1404         m_history_sp->Enter (CombineLines (m_input_lines).c_str());
1405 
1406         lines = GetInputAsStringList();
1407     }
1408     return m_editor_status != EditorStatus::EndOfInput;
1409 }
1410 
1411 void
PrintAsync(Stream * stream,const char * s,size_t len)1412 Editline::PrintAsync (Stream *stream, const char *s, size_t len)
1413 {
1414     Mutex::Locker locker(m_output_mutex);
1415     if (m_editor_status == EditorStatus::Editing)
1416     {
1417         MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
1418         fprintf(m_output_file, ANSI_CLEAR_BELOW);
1419     }
1420     stream->Write (s, len);
1421     stream->Flush();
1422     if (m_editor_status == EditorStatus::Editing)
1423     {
1424         DisplayInput();
1425         MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
1426     }
1427 }
1428