1262182Semaste//===-- Editline.cpp --------------------------------------------*- C++ -*-===//
2262182Semaste//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6262182Semaste//
7262182Semaste//===----------------------------------------------------------------------===//
8262182Semaste
9280031Sdim#include <iomanip>
10280031Sdim#include <iostream>
11280031Sdim#include <limits.h>
12262182Semaste
13314564Sdim#include "lldb/Host/ConnectionFileDescriptor.h"
14314564Sdim#include "lldb/Host/Editline.h"
15344779Sdim#include "lldb/Host/FileSystem.h"
16262182Semaste#include "lldb/Host/Host.h"
17360784Sdim#include "lldb/Utility/CompletionRequest.h"
18321369Sdim#include "lldb/Utility/FileSpec.h"
19288943Sdim#include "lldb/Utility/LLDBAssert.h"
20314564Sdim#include "lldb/Utility/SelectHelper.h"
21321369Sdim#include "lldb/Utility/Status.h"
22321369Sdim#include "lldb/Utility/StreamString.h"
23321369Sdim#include "lldb/Utility/StringList.h"
24321369Sdim#include "lldb/Utility/Timeout.h"
25262182Semaste
26321369Sdim#include "llvm/Support/FileSystem.h"
27321369Sdim#include "llvm/Support/Threading.h"
28321369Sdim
29262182Semasteusing namespace lldb_private;
30280031Sdimusing namespace lldb_private::line_editor;
31262182Semaste
32288943Sdim// Workaround for what looks like an OS X-specific issue, but other platforms
33288943Sdim// may benefit from something similar if issues arise.  The libedit library
34288943Sdim// doesn't explicitly initialize the curses termcap library, which it gets away
35288943Sdim// with until TERM is set to VT100 where it stumbles over an implementation
36288943Sdim// assumption that may not exist on other platforms.  The setupterm() function
37341825Sdim// would normally require headers that don't work gracefully in this context,
38341825Sdim// so the function declaraction has been hoisted here.
39288943Sdim#if defined(__APPLE__)
40288943Sdimextern "C" {
41314564Sdimint setupterm(char *term, int fildes, int *errret);
42288943Sdim}
43288943Sdim#define USE_SETUPTERM_WORKAROUND
44288943Sdim#endif
45288943Sdim
46314564Sdim// Editline uses careful cursor management to achieve the illusion of editing a
47341825Sdim// multi-line block of text with a single line editor.  Preserving this
48341825Sdim// illusion requires fairly careful management of cursor state.  Read and
49341825Sdim// understand the relationship between DisplayInput(), MoveCursor(),
50341825Sdim// SetCurrentLine(), and SaveEditedLine() before making changes.
51280031Sdim
52280031Sdim#define ESCAPE "\x1b"
53280031Sdim#define ANSI_FAINT ESCAPE "[2m"
54280031Sdim#define ANSI_UNFAINT ESCAPE "[22m"
55280031Sdim#define ANSI_CLEAR_BELOW ESCAPE "[J"
56280031Sdim#define ANSI_CLEAR_RIGHT ESCAPE "[K"
57280031Sdim#define ANSI_SET_COLUMN_N ESCAPE "[%dG"
58280031Sdim#define ANSI_UP_N_ROWS ESCAPE "[%dA"
59280031Sdim#define ANSI_DOWN_N_ROWS ESCAPE "[%dB"
60280031Sdim
61280031Sdim#if LLDB_EDITLINE_USE_WCHAR
62280031Sdim
63280031Sdim#define EditLineConstString(str) L##str
64280031Sdim#define EditLineStringFormatSpec "%ls"
65280031Sdim
66280031Sdim#else
67280031Sdim
68280031Sdim#define EditLineConstString(str) str
69280031Sdim#define EditLineStringFormatSpec "%s"
70280031Sdim
71314564Sdim// use #defines so wide version functions and structs will resolve to old
72341825Sdim// versions for case of libedit not built with wide char support
73280031Sdim#define history_w history
74280031Sdim#define history_winit history_init
75280031Sdim#define history_wend history_end
76280031Sdim#define HistoryW History
77280031Sdim#define HistEventW HistEvent
78280031Sdim#define LineInfoW LineInfo
79280031Sdim
80280031Sdim#define el_wgets el_gets
81280031Sdim#define el_wgetc el_getc
82280031Sdim#define el_wpush el_push
83280031Sdim#define el_wparse el_parse
84314564Sdim#define el_wset el_set
85314564Sdim#define el_wget el_get
86280031Sdim#define el_wline el_line
87280031Sdim#define el_winsertstr el_insertstr
88314564Sdim#define el_wdeletestr el_deletestr
89280031Sdim
90280031Sdim#endif // #if LLDB_EDITLINE_USE_WCHAR
91280031Sdim
92314564Sdimbool IsOnlySpaces(const EditLineStringType &content) {
93314564Sdim  for (wchar_t ch : content) {
94314564Sdim    if (ch != EditLineCharType(' '))
95314564Sdim      return false;
96314564Sdim  }
97314564Sdim  return true;
98280031Sdim}
99280031Sdim
100360784Sdimstatic int GetOperation(HistoryOperation op) {
101360784Sdim  // The naming used by editline for the history operations is counter
102360784Sdim  // intuitive to how it's used here.
103360784Sdim  //
104360784Sdim  //  - The H_PREV operation returns the previous element in the history, which
105360784Sdim  //    is newer than the current one.
106360784Sdim  //
107360784Sdim  //  - The H_NEXT operation returns the next element in the history, which is
108360784Sdim  //    older than the current one.
109360784Sdim  //
110360784Sdim  // The naming of the enum entries match the semantic meaning.
111360784Sdim  switch(op) {
112360784Sdim    case HistoryOperation::Oldest:
113360784Sdim      return H_FIRST;
114360784Sdim    case HistoryOperation::Older:
115360784Sdim      return H_NEXT;
116360784Sdim    case HistoryOperation::Current:
117360784Sdim      return H_CURR;
118360784Sdim    case HistoryOperation::Newer:
119360784Sdim      return H_PREV;
120360784Sdim    case HistoryOperation::Newest:
121360784Sdim      return H_LAST;
122360784Sdim  }
123360784Sdim  llvm_unreachable("Fully covered switch!");
124360784Sdim}
125360784Sdim
126360784Sdim
127314564SdimEditLineStringType CombineLines(const std::vector<EditLineStringType> &lines) {
128314564Sdim  EditLineStringStreamType combined_stream;
129314564Sdim  for (EditLineStringType line : lines) {
130314564Sdim    combined_stream << line.c_str() << "\n";
131314564Sdim  }
132314564Sdim  return combined_stream.str();
133280031Sdim}
134280031Sdim
135314564Sdimstd::vector<EditLineStringType> SplitLines(const EditLineStringType &input) {
136314564Sdim  std::vector<EditLineStringType> result;
137314564Sdim  size_t start = 0;
138314564Sdim  while (start < input.length()) {
139314564Sdim    size_t end = input.find('\n', start);
140314564Sdim    if (end == std::string::npos) {
141314564Sdim      result.insert(result.end(), input.substr(start));
142314564Sdim      break;
143280031Sdim    }
144314564Sdim    result.insert(result.end(), input.substr(start, end - start));
145314564Sdim    start = end + 1;
146314564Sdim  }
147314564Sdim  return result;
148280031Sdim}
149280031Sdim
150314564SdimEditLineStringType FixIndentation(const EditLineStringType &line,
151314564Sdim                                  int indent_correction) {
152314564Sdim  if (indent_correction == 0)
153314564Sdim    return line;
154314564Sdim  if (indent_correction < 0)
155314564Sdim    return line.substr(-indent_correction);
156314564Sdim  return EditLineStringType(indent_correction, EditLineCharType(' ')) + line;
157280031Sdim}
158280031Sdim
159314564Sdimint GetIndentation(const EditLineStringType &line) {
160314564Sdim  int space_count = 0;
161314564Sdim  for (EditLineCharType ch : line) {
162314564Sdim    if (ch != EditLineCharType(' '))
163314564Sdim      break;
164314564Sdim    ++space_count;
165314564Sdim  }
166314564Sdim  return space_count;
167280031Sdim}
168280031Sdim
169314564Sdimbool IsInputPending(FILE *file) {
170314564Sdim  // FIXME: This will be broken on Windows if we ever re-enable Editline.  You
171314564Sdim  // can't use select
172314564Sdim  // on something that isn't a socket.  This will have to be re-written to not
173341825Sdim  // use a FILE*, but instead use some kind of yet-to-be-created abstraction
174341825Sdim  // that select-like functionality on non-socket objects.
175314564Sdim  const int fd = fileno(file);
176314564Sdim  SelectHelper select_helper;
177314564Sdim  select_helper.SetTimeout(std::chrono::microseconds(0));
178314564Sdim  select_helper.FDSetRead(fd);
179314564Sdim  return select_helper.Select().Success();
180280031Sdim}
181280031Sdim
182314564Sdimnamespace lldb_private {
183314564Sdimnamespace line_editor {
184314564Sdimtypedef std::weak_ptr<EditlineHistory> EditlineHistoryWP;
185280031Sdim
186341825Sdim// EditlineHistory objects are sometimes shared between multiple Editline
187341825Sdim// instances with the same program name.
188280031Sdim
189314564Sdimclass EditlineHistory {
190314564Sdimprivate:
191341825Sdim  // Use static GetHistory() function to get a EditlineHistorySP to one of
192341825Sdim  // these objects
193314564Sdim  EditlineHistory(const std::string &prefix, uint32_t size, bool unique_entries)
194353358Sdim      : m_history(nullptr), m_event(), m_prefix(prefix), m_path() {
195314564Sdim    m_history = history_winit();
196314564Sdim    history_w(m_history, &m_event, H_SETSIZE, size);
197314564Sdim    if (unique_entries)
198314564Sdim      history_w(m_history, &m_event, H_SETUNIQUE, 1);
199314564Sdim  }
200314564Sdim
201314564Sdim  const char *GetHistoryFilePath() {
202353358Sdim    // Compute the history path lazily.
203314564Sdim    if (m_path.empty() && m_history && !m_prefix.empty()) {
204353358Sdim      llvm::SmallString<128> lldb_history_file;
205353358Sdim      llvm::sys::path::home_directory(lldb_history_file);
206353358Sdim      llvm::sys::path::append(lldb_history_file, ".lldb");
207353358Sdim
208353358Sdim      // LLDB stores its history in ~/.lldb/. If for some reason this directory
209353358Sdim      // isn't writable or cannot be created, history won't be available.
210353358Sdim      if (!llvm::sys::fs::create_directory(lldb_history_file)) {
211353358Sdim#if LLDB_EDITLINE_USE_WCHAR
212353358Sdim        std::string filename = m_prefix + "-widehistory";
213353358Sdim#else
214353358Sdim        std::string filename = m_prefix + "-history";
215353358Sdim#endif
216353358Sdim        llvm::sys::path::append(lldb_history_file, filename);
217353358Sdim        m_path = lldb_history_file.str();
218314564Sdim      }
219280031Sdim    }
220353358Sdim
221314564Sdim    if (m_path.empty())
222353358Sdim      return nullptr;
223353358Sdim
224314564Sdim    return m_path.c_str();
225314564Sdim  }
226314564Sdim
227314564Sdimpublic:
228314564Sdim  ~EditlineHistory() {
229314564Sdim    Save();
230314564Sdim
231314564Sdim    if (m_history) {
232314564Sdim      history_wend(m_history);
233353358Sdim      m_history = nullptr;
234314564Sdim    }
235314564Sdim  }
236314564Sdim
237314564Sdim  static EditlineHistorySP GetHistory(const std::string &prefix) {
238314564Sdim    typedef std::map<std::string, EditlineHistoryWP> WeakHistoryMap;
239314564Sdim    static std::recursive_mutex g_mutex;
240314564Sdim    static WeakHistoryMap g_weak_map;
241314564Sdim    std::lock_guard<std::recursive_mutex> guard(g_mutex);
242314564Sdim    WeakHistoryMap::const_iterator pos = g_weak_map.find(prefix);
243314564Sdim    EditlineHistorySP history_sp;
244314564Sdim    if (pos != g_weak_map.end()) {
245314564Sdim      history_sp = pos->second.lock();
246314564Sdim      if (history_sp)
247314564Sdim        return history_sp;
248314564Sdim      g_weak_map.erase(pos);
249314564Sdim    }
250314564Sdim    history_sp.reset(new EditlineHistory(prefix, 800, true));
251314564Sdim    g_weak_map[prefix] = history_sp;
252314564Sdim    return history_sp;
253314564Sdim  }
254314564Sdim
255353358Sdim  bool IsValid() const { return m_history != nullptr; }
256314564Sdim
257314564Sdim  HistoryW *GetHistoryPtr() { return m_history; }
258314564Sdim
259314564Sdim  void Enter(const EditLineCharType *line_cstr) {
260314564Sdim    if (m_history)
261314564Sdim      history_w(m_history, &m_event, H_ENTER, line_cstr);
262314564Sdim  }
263314564Sdim
264314564Sdim  bool Load() {
265314564Sdim    if (m_history) {
266314564Sdim      const char *path = GetHistoryFilePath();
267314564Sdim      if (path) {
268314564Sdim        history_w(m_history, &m_event, H_LOAD, path);
269314564Sdim        return true;
270314564Sdim      }
271314564Sdim    }
272314564Sdim    return false;
273314564Sdim  }
274314564Sdim
275314564Sdim  bool Save() {
276314564Sdim    if (m_history) {
277314564Sdim      const char *path = GetHistoryFilePath();
278314564Sdim      if (path) {
279314564Sdim        history_w(m_history, &m_event, H_SAVE, path);
280314564Sdim        return true;
281314564Sdim      }
282314564Sdim    }
283314564Sdim    return false;
284314564Sdim  }
285314564Sdim
286314564Sdimprotected:
287314564Sdim  HistoryW *m_history; // The history object
288314564Sdim  HistEventW m_event;  // The history event needed to contain all history events
289314564Sdim  std::string m_prefix; // The prefix name (usually the editline program name)
290314564Sdim                        // to use when loading/saving history
291314564Sdim  std::string m_path;   // Path to the history file
292314564Sdim};
293276479Sdim}
294314564Sdim}
295276479Sdim
296280031Sdim// Editline private methods
297276479Sdim
298314564Sdimvoid Editline::SetBaseLineNumber(int line_number) {
299314564Sdim  std::stringstream line_number_stream;
300314564Sdim  line_number_stream << line_number;
301314564Sdim  m_base_line_number = line_number;
302314564Sdim  m_line_number_digits =
303314564Sdim      std::max(3, (int)line_number_stream.str().length() + 1);
304280031Sdim}
305262182Semaste
306314564Sdimstd::string Editline::PromptForIndex(int line_index) {
307314564Sdim  bool use_line_numbers = m_multiline_enabled && m_base_line_number > 0;
308314564Sdim  std::string prompt = m_set_prompt;
309314564Sdim  if (use_line_numbers && prompt.length() == 0) {
310314564Sdim    prompt = ": ";
311314564Sdim  }
312314564Sdim  std::string continuation_prompt = prompt;
313314564Sdim  if (m_set_continuation_prompt.length() > 0) {
314314564Sdim    continuation_prompt = m_set_continuation_prompt;
315314564Sdim
316314564Sdim    // Ensure that both prompts are the same length through space padding
317314564Sdim    while (continuation_prompt.length() < prompt.length()) {
318314564Sdim      continuation_prompt += ' ';
319280031Sdim    }
320314564Sdim    while (prompt.length() < continuation_prompt.length()) {
321314564Sdim      prompt += ' ';
322262182Semaste    }
323314564Sdim  }
324262182Semaste
325314564Sdim  if (use_line_numbers) {
326314564Sdim    StreamString prompt_stream;
327314564Sdim    prompt_stream.Printf(
328314564Sdim        "%*d%s", m_line_number_digits, m_base_line_number + line_index,
329314564Sdim        (line_index == 0) ? prompt.c_str() : continuation_prompt.c_str());
330314564Sdim    return std::move(prompt_stream.GetString());
331314564Sdim  }
332314564Sdim  return (line_index == 0) ? prompt : continuation_prompt;
333280031Sdim}
334262182Semaste
335314564Sdimvoid Editline::SetCurrentLine(int line_index) {
336314564Sdim  m_current_line_index = line_index;
337314564Sdim  m_current_prompt = PromptForIndex(line_index);
338280031Sdim}
339262182Semaste
340314564Sdimint Editline::GetPromptWidth() { return (int)PromptForIndex(0).length(); }
341280031Sdim
342314564Sdimbool Editline::IsEmacs() {
343314564Sdim  const char *editor;
344314564Sdim  el_get(m_editline, EL_EDITOR, &editor);
345314564Sdim  return editor[0] == 'e';
346280031Sdim}
347262182Semaste
348314564Sdimbool Editline::IsOnlySpaces() {
349314564Sdim  const LineInfoW *info = el_wline(m_editline);
350314564Sdim  for (const EditLineCharType *character = info->buffer;
351314564Sdim       character < info->lastchar; character++) {
352314564Sdim    if (*character != ' ')
353314564Sdim      return false;
354314564Sdim  }
355314564Sdim  return true;
356280031Sdim}
357280031Sdim
358314564Sdimint Editline::GetLineIndexForLocation(CursorLocation location, int cursor_row) {
359314564Sdim  int line = 0;
360314564Sdim  if (location == CursorLocation::EditingPrompt ||
361314564Sdim      location == CursorLocation::BlockEnd ||
362314564Sdim      location == CursorLocation::EditingCursor) {
363314564Sdim    for (unsigned index = 0; index < m_current_line_index; index++) {
364314564Sdim      line += CountRowsForLine(m_input_lines[index]);
365276479Sdim    }
366314564Sdim    if (location == CursorLocation::EditingCursor) {
367314564Sdim      line += cursor_row;
368314564Sdim    } else if (location == CursorLocation::BlockEnd) {
369314564Sdim      for (unsigned index = m_current_line_index; index < m_input_lines.size();
370314564Sdim           index++) {
371314564Sdim        line += CountRowsForLine(m_input_lines[index]);
372314564Sdim      }
373314564Sdim      --line;
374276479Sdim    }
375314564Sdim  }
376314564Sdim  return line;
377262182Semaste}
378262182Semaste
379314564Sdimvoid Editline::MoveCursor(CursorLocation from, CursorLocation to) {
380314564Sdim  const LineInfoW *info = el_wline(m_editline);
381314564Sdim  int editline_cursor_position =
382314564Sdim      (int)((info->cursor - info->buffer) + GetPromptWidth());
383314564Sdim  int editline_cursor_row = editline_cursor_position / m_terminal_width;
384314564Sdim
385314564Sdim  // Determine relative starting and ending lines
386314564Sdim  int fromLine = GetLineIndexForLocation(from, editline_cursor_row);
387314564Sdim  int toLine = GetLineIndexForLocation(to, editline_cursor_row);
388314564Sdim  if (toLine != fromLine) {
389314564Sdim    fprintf(m_output_file,
390314564Sdim            (toLine > fromLine) ? ANSI_DOWN_N_ROWS : ANSI_UP_N_ROWS,
391314564Sdim            std::abs(toLine - fromLine));
392314564Sdim  }
393314564Sdim
394314564Sdim  // Determine target column
395314564Sdim  int toColumn = 1;
396314564Sdim  if (to == CursorLocation::EditingCursor) {
397314564Sdim    toColumn =
398314564Sdim        editline_cursor_position - (editline_cursor_row * m_terminal_width) + 1;
399321369Sdim  } else if (to == CursorLocation::BlockEnd && !m_input_lines.empty()) {
400314564Sdim    toColumn =
401314564Sdim        ((m_input_lines[m_input_lines.size() - 1].length() + GetPromptWidth()) %
402314564Sdim         80) +
403314564Sdim        1;
404314564Sdim  }
405314564Sdim  fprintf(m_output_file, ANSI_SET_COLUMN_N, toColumn);
406280031Sdim}
407262182Semaste
408314564Sdimvoid Editline::DisplayInput(int firstIndex) {
409314564Sdim  fprintf(m_output_file, ANSI_SET_COLUMN_N ANSI_CLEAR_BELOW, 1);
410314564Sdim  int line_count = (int)m_input_lines.size();
411314564Sdim  const char *faint = m_color_prompts ? ANSI_FAINT : "";
412314564Sdim  const char *unfaint = m_color_prompts ? ANSI_UNFAINT : "";
413280031Sdim
414314564Sdim  for (int index = firstIndex; index < line_count; index++) {
415314564Sdim    fprintf(m_output_file, "%s"
416314564Sdim                           "%s"
417314564Sdim                           "%s" EditLineStringFormatSpec " ",
418314564Sdim            faint, PromptForIndex(index).c_str(), unfaint,
419314564Sdim            m_input_lines[index].c_str());
420314564Sdim    if (index < line_count - 1)
421314564Sdim      fprintf(m_output_file, "\n");
422314564Sdim  }
423262182Semaste}
424262182Semaste
425314564Sdimint Editline::CountRowsForLine(const EditLineStringType &content) {
426314564Sdim  auto prompt =
427314564Sdim      PromptForIndex(0); // Prompt width is constant during an edit session
428314564Sdim  int line_length = (int)(content.length() + prompt.length());
429314564Sdim  return (line_length / m_terminal_width) + 1;
430262182Semaste}
431262182Semaste
432314564Sdimvoid Editline::SaveEditedLine() {
433314564Sdim  const LineInfoW *info = el_wline(m_editline);
434314564Sdim  m_input_lines[m_current_line_index] =
435314564Sdim      EditLineStringType(info->buffer, info->lastchar - info->buffer);
436314564Sdim}
437314564Sdim
438314564SdimStringList Editline::GetInputAsStringList(int line_count) {
439314564Sdim  StringList lines;
440314564Sdim  for (EditLineStringType line : m_input_lines) {
441314564Sdim    if (line_count == 0)
442314564Sdim      break;
443280031Sdim#if LLDB_EDITLINE_USE_WCHAR
444314564Sdim    lines.AppendString(m_utf8conv.to_bytes(line));
445280031Sdim#else
446314564Sdim    lines.AppendString(line);
447280031Sdim#endif
448314564Sdim    --line_count;
449314564Sdim  }
450314564Sdim  return lines;
451262182Semaste}
452262182Semaste
453360784Sdimunsigned char Editline::RecallHistory(HistoryOperation op) {
454360784Sdim  assert(op == HistoryOperation::Older || op == HistoryOperation::Newer);
455314564Sdim  if (!m_history_sp || !m_history_sp->IsValid())
456314564Sdim    return CC_ERROR;
457314564Sdim
458314564Sdim  HistoryW *pHistory = m_history_sp->GetHistoryPtr();
459314564Sdim  HistEventW history_event;
460314564Sdim  std::vector<EditLineStringType> new_input_lines;
461314564Sdim
462314564Sdim  // Treat moving from the "live" entry differently
463314564Sdim  if (!m_in_history) {
464360784Sdim    switch (op) {
465360784Sdim    case HistoryOperation::Newer:
466314564Sdim      return CC_ERROR; // Can't go newer than the "live" entry
467360784Sdim    case HistoryOperation::Older: {
468360784Sdim      if (history_w(pHistory, &history_event,
469360784Sdim                    GetOperation(HistoryOperation::Newest)) == -1)
470360784Sdim        return CC_ERROR;
471360784Sdim      // Save any edits to the "live" entry in case we return by moving forward
472360784Sdim      // in history (it would be more bash-like to save over any current entry,
473360784Sdim      // but libedit doesn't offer the ability to add entries anywhere except
474360784Sdim      // the end.)
475360784Sdim      SaveEditedLine();
476360784Sdim      m_live_history_lines = m_input_lines;
477360784Sdim      m_in_history = true;
478360784Sdim    } break;
479360784Sdim    default:
480360784Sdim      llvm_unreachable("unsupported history direction");
481360784Sdim    }
482314564Sdim  } else {
483360784Sdim    if (history_w(pHistory, &history_event, GetOperation(op)) == -1) {
484360784Sdim      switch (op) {
485360784Sdim      case HistoryOperation::Older:
486360784Sdim        // Can't move earlier than the earliest entry.
487280031Sdim        return CC_ERROR;
488360784Sdim      case HistoryOperation::Newer:
489360784Sdim        // Moving to newer-than-the-newest entry yields the "live" entry.
490360784Sdim        new_input_lines = m_live_history_lines;
491360784Sdim        m_in_history = false;
492360784Sdim        break;
493360784Sdim      default:
494360784Sdim        llvm_unreachable("unsupported history direction");
495360784Sdim      }
496280031Sdim    }
497314564Sdim  }
498280031Sdim
499314564Sdim  // If we're pulling the lines from history, split them apart
500314564Sdim  if (m_in_history)
501314564Sdim    new_input_lines = SplitLines(history_event.str);
502280031Sdim
503314564Sdim  // Erase the current edit session and replace it with a new one
504314564Sdim  MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
505314564Sdim  m_input_lines = new_input_lines;
506314564Sdim  DisplayInput();
507314564Sdim
508314564Sdim  // Prepare to edit the last line when moving to previous entry, or the first
509341825Sdim  // line when moving to next entry
510360784Sdim  switch (op) {
511360784Sdim  case HistoryOperation::Older:
512360784Sdim    m_current_line_index = (int)m_input_lines.size() - 1;
513360784Sdim    break;
514360784Sdim  case HistoryOperation::Newer:
515360784Sdim    m_current_line_index = 0;
516360784Sdim    break;
517360784Sdim  default:
518360784Sdim    llvm_unreachable("unsupported history direction");
519360784Sdim  }
520360784Sdim  SetCurrentLine(m_current_line_index);
521314564Sdim  MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
522314564Sdim  return CC_NEWLINE;
523262182Semaste}
524262182Semaste
525321369Sdimint Editline::GetCharacter(EditLineGetCharType *c) {
526314564Sdim  const LineInfoW *info = el_wline(m_editline);
527314564Sdim
528314564Sdim  // Paint a faint version of the desired prompt over the version libedit draws
529314564Sdim  // (will only be requested if colors are supported)
530314564Sdim  if (m_needs_prompt_repaint) {
531314564Sdim    MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
532314564Sdim    fprintf(m_output_file, "%s"
533314564Sdim                           "%s"
534314564Sdim                           "%s",
535314564Sdim            ANSI_FAINT, Prompt(), ANSI_UNFAINT);
536314564Sdim    MoveCursor(CursorLocation::EditingPrompt, CursorLocation::EditingCursor);
537314564Sdim    m_needs_prompt_repaint = false;
538314564Sdim  }
539314564Sdim
540314564Sdim  if (m_multiline_enabled) {
541341825Sdim    // Detect when the number of rows used for this input line changes due to
542341825Sdim    // an edit
543314564Sdim    int lineLength = (int)((info->lastchar - info->buffer) + GetPromptWidth());
544314564Sdim    int new_line_rows = (lineLength / m_terminal_width) + 1;
545314564Sdim    if (m_current_line_rows != -1 && new_line_rows != m_current_line_rows) {
546314564Sdim      // Respond by repainting the current state from this line on
547314564Sdim      MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
548314564Sdim      SaveEditedLine();
549314564Sdim      DisplayInput(m_current_line_index);
550314564Sdim      MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
551262182Semaste    }
552314564Sdim    m_current_line_rows = new_line_rows;
553314564Sdim  }
554314564Sdim
555314564Sdim  // Read an actual character
556314564Sdim  while (true) {
557314564Sdim    lldb::ConnectionStatus status = lldb::eConnectionStatusSuccess;
558314564Sdim    char ch = 0;
559314564Sdim
560314564Sdim    // This mutex is locked by our caller (GetLine). Unlock it while we read a
561341825Sdim    // character (blocking operation), so we do not hold the mutex
562341825Sdim    // indefinitely. This gives a chance for someone to interrupt us. After
563341825Sdim    // Read returns, immediately lock the mutex again and check if we were
564341825Sdim    // interrupted.
565314564Sdim    m_output_mutex.unlock();
566353358Sdim    int read_count =
567353358Sdim        m_input_connection.Read(&ch, 1, llvm::None, status, nullptr);
568314564Sdim    m_output_mutex.lock();
569314564Sdim    if (m_editor_status == EditorStatus::Interrupted) {
570314564Sdim      while (read_count > 0 && status == lldb::eConnectionStatusSuccess)
571353358Sdim        read_count =
572353358Sdim            m_input_connection.Read(&ch, 1, llvm::None, status, nullptr);
573314564Sdim      lldbassert(status == lldb::eConnectionStatusInterrupted);
574314564Sdim      return 0;
575280031Sdim    }
576288943Sdim
577314564Sdim    if (read_count) {
578314564Sdim      if (CompleteCharacter(ch, *c))
579314564Sdim        return 1;
580314564Sdim    } else {
581314564Sdim      switch (status) {
582314564Sdim      case lldb::eConnectionStatusSuccess: // Success
583314564Sdim        break;
584288943Sdim
585314564Sdim      case lldb::eConnectionStatusInterrupted:
586344779Sdim        llvm_unreachable("Interrupts should have been handled above.");
587288943Sdim
588314564Sdim      case lldb::eConnectionStatusError:        // Check GetError() for details
589314564Sdim      case lldb::eConnectionStatusTimedOut:     // Request timed out
590314564Sdim      case lldb::eConnectionStatusEndOfFile:    // End-of-file encountered
591314564Sdim      case lldb::eConnectionStatusNoConnection: // No connection
592314564Sdim      case lldb::eConnectionStatusLostConnection: // Lost connection while
593314564Sdim                                                  // connected to a valid
594314564Sdim                                                  // connection
595314564Sdim        m_editor_status = EditorStatus::EndOfInput;
596314564Sdim        return 0;
597314564Sdim      }
598262182Semaste    }
599314564Sdim  }
600262182Semaste}
601262182Semaste
602314564Sdimconst char *Editline::Prompt() {
603314564Sdim  if (m_color_prompts)
604314564Sdim    m_needs_prompt_repaint = true;
605314564Sdim  return m_current_prompt.c_str();
606280031Sdim}
607262182Semaste
608314564Sdimunsigned char Editline::BreakLineCommand(int ch) {
609314564Sdim  // Preserve any content beyond the cursor, truncate and save the current line
610314564Sdim  const LineInfoW *info = el_wline(m_editline);
611314564Sdim  auto current_line =
612314564Sdim      EditLineStringType(info->buffer, info->cursor - info->buffer);
613314564Sdim  auto new_line_fragment =
614314564Sdim      EditLineStringType(info->cursor, info->lastchar - info->cursor);
615314564Sdim  m_input_lines[m_current_line_index] = current_line;
616262182Semaste
617314564Sdim  // Ignore whitespace-only extra fragments when breaking a line
618314564Sdim  if (::IsOnlySpaces(new_line_fragment))
619314564Sdim    new_line_fragment = EditLineConstString("");
620262182Semaste
621314564Sdim  // Establish the new cursor position at the start of a line when inserting a
622314564Sdim  // line break
623314564Sdim  m_revert_cursor_index = 0;
624314564Sdim
625314564Sdim  // Don't perform automatic formatting when pasting
626314564Sdim  if (!IsInputPending(m_input_file)) {
627314564Sdim    // Apply smart indentation
628314564Sdim    if (m_fix_indentation_callback) {
629314564Sdim      StringList lines = GetInputAsStringList(m_current_line_index + 1);
630280031Sdim#if LLDB_EDITLINE_USE_WCHAR
631314564Sdim      lines.AppendString(m_utf8conv.to_bytes(new_line_fragment));
632280031Sdim#else
633314564Sdim      lines.AppendString(new_line_fragment);
634280031Sdim#endif
635314564Sdim
636314564Sdim      int indent_correction = m_fix_indentation_callback(
637314564Sdim          this, lines, 0, m_fix_indentation_callback_baton);
638314564Sdim      new_line_fragment = FixIndentation(new_line_fragment, indent_correction);
639314564Sdim      m_revert_cursor_index = GetIndentation(new_line_fragment);
640262182Semaste    }
641314564Sdim  }
642314564Sdim
643314564Sdim  // Insert the new line and repaint everything from the split line on down
644314564Sdim  m_input_lines.insert(m_input_lines.begin() + m_current_line_index + 1,
645314564Sdim                       new_line_fragment);
646314564Sdim  MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
647314564Sdim  DisplayInput(m_current_line_index);
648314564Sdim
649314564Sdim  // Reposition the cursor to the right line and prepare to edit the new line
650314564Sdim  SetCurrentLine(m_current_line_index + 1);
651314564Sdim  MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
652314564Sdim  return CC_NEWLINE;
653280031Sdim}
654262182Semaste
655314564Sdimunsigned char Editline::EndOrAddLineCommand(int ch) {
656314564Sdim  // Don't perform end of input detection when pasting, always treat this as a
657314564Sdim  // line break
658314564Sdim  if (IsInputPending(m_input_file)) {
659314564Sdim    return BreakLineCommand(ch);
660314564Sdim  }
661309124Sdim
662314564Sdim  // Save any edits to this line
663314564Sdim  SaveEditedLine();
664309124Sdim
665341825Sdim  // If this is the end of the last line, consider whether to add a line
666341825Sdim  // instead
667314564Sdim  const LineInfoW *info = el_wline(m_editline);
668314564Sdim  if (m_current_line_index == m_input_lines.size() - 1 &&
669314564Sdim      info->cursor == info->lastchar) {
670314564Sdim    if (m_is_input_complete_callback) {
671314564Sdim      auto lines = GetInputAsStringList();
672314564Sdim      if (!m_is_input_complete_callback(this, lines,
673314564Sdim                                        m_is_input_complete_callback_baton)) {
674314564Sdim        return BreakLineCommand(ch);
675314564Sdim      }
676309124Sdim
677314564Sdim      // The completion test is allowed to change the input lines when complete
678314564Sdim      m_input_lines.clear();
679314564Sdim      for (unsigned index = 0; index < lines.GetSize(); index++) {
680309124Sdim#if LLDB_EDITLINE_USE_WCHAR
681314564Sdim        m_input_lines.insert(m_input_lines.end(),
682314564Sdim                             m_utf8conv.from_bytes(lines[index]));
683309124Sdim#else
684314564Sdim        m_input_lines.insert(m_input_lines.end(), lines[index]);
685309124Sdim#endif
686314564Sdim      }
687309124Sdim    }
688314564Sdim  }
689314564Sdim  MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockEnd);
690314564Sdim  fprintf(m_output_file, "\n");
691314564Sdim  m_editor_status = EditorStatus::Complete;
692314564Sdim  return CC_NEWLINE;
693309124Sdim}
694309124Sdim
695314564Sdimunsigned char Editline::DeleteNextCharCommand(int ch) {
696314564Sdim  LineInfoW *info = const_cast<LineInfoW *>(el_wline(m_editline));
697276479Sdim
698314564Sdim  // Just delete the next character normally if possible
699314564Sdim  if (info->cursor < info->lastchar) {
700314564Sdim    info->cursor++;
701314564Sdim    el_deletestr(m_editline, 1);
702280031Sdim    return CC_REFRESH;
703314564Sdim  }
704262182Semaste
705341825Sdim  // Fail when at the end of the last line, except when ^D is pressed on the
706341825Sdim  // line is empty, in which case it is treated as EOF
707314564Sdim  if (m_current_line_index == m_input_lines.size() - 1) {
708314564Sdim    if (ch == 4 && info->buffer == info->lastchar) {
709314564Sdim      fprintf(m_output_file, "^D\n");
710314564Sdim      m_editor_status = EditorStatus::EndOfInput;
711314564Sdim      return CC_EOF;
712262182Semaste    }
713314564Sdim    return CC_ERROR;
714314564Sdim  }
715314564Sdim
716314564Sdim  // Prepare to combine this line with the one below
717314564Sdim  MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
718314564Sdim
719314564Sdim  // Insert the next line of text at the cursor and restore the cursor position
720314564Sdim  const EditLineCharType *cursor = info->cursor;
721314564Sdim  el_winsertstr(m_editline, m_input_lines[m_current_line_index + 1].c_str());
722314564Sdim  info->cursor = cursor;
723314564Sdim  SaveEditedLine();
724314564Sdim
725314564Sdim  // Delete the extra line
726314564Sdim  m_input_lines.erase(m_input_lines.begin() + m_current_line_index + 1);
727314564Sdim
728314564Sdim  // Clear and repaint from this line on down
729314564Sdim  DisplayInput(m_current_line_index);
730314564Sdim  MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
731314564Sdim  return CC_REFRESH;
732262182Semaste}
733262182Semaste
734314564Sdimunsigned char Editline::DeletePreviousCharCommand(int ch) {
735314564Sdim  LineInfoW *info = const_cast<LineInfoW *>(el_wline(m_editline));
736262182Semaste
737341825Sdim  // Just delete the previous character normally when not at the start of a
738341825Sdim  // line
739314564Sdim  if (info->cursor > info->buffer) {
740314564Sdim    el_deletestr(m_editline, 1);
741314564Sdim    return CC_REFRESH;
742314564Sdim  }
743314564Sdim
744314564Sdim  // No prior line and no prior character?  Let the user know
745314564Sdim  if (m_current_line_index == 0)
746314564Sdim    return CC_ERROR;
747314564Sdim
748314564Sdim  // No prior character, but prior line?  Combine with the line above
749314564Sdim  SaveEditedLine();
750314564Sdim  SetCurrentLine(m_current_line_index - 1);
751314564Sdim  auto priorLine = m_input_lines[m_current_line_index];
752314564Sdim  m_input_lines.erase(m_input_lines.begin() + m_current_line_index);
753314564Sdim  m_input_lines[m_current_line_index] =
754314564Sdim      priorLine + m_input_lines[m_current_line_index];
755314564Sdim
756314564Sdim  // Repaint from the new line down
757314564Sdim  fprintf(m_output_file, ANSI_UP_N_ROWS ANSI_SET_COLUMN_N,
758314564Sdim          CountRowsForLine(priorLine), 1);
759314564Sdim  DisplayInput(m_current_line_index);
760314564Sdim
761314564Sdim  // Put the cursor back where libedit expects it to be before returning to
762341825Sdim  // editing by telling libedit about the newly inserted text
763314564Sdim  MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
764314564Sdim  el_winsertstr(m_editline, priorLine.c_str());
765314564Sdim  return CC_REDISPLAY;
766280031Sdim}
767262182Semaste
768314564Sdimunsigned char Editline::PreviousLineCommand(int ch) {
769314564Sdim  SaveEditedLine();
770262182Semaste
771314564Sdim  if (m_current_line_index == 0) {
772360784Sdim    return RecallHistory(HistoryOperation::Older);
773314564Sdim  }
774314564Sdim
775314564Sdim  // Start from a known location
776314564Sdim  MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
777314564Sdim
778314564Sdim  // Treat moving up from a blank last line as a deletion of that line
779314564Sdim  if (m_current_line_index == m_input_lines.size() - 1 && IsOnlySpaces()) {
780314564Sdim    m_input_lines.erase(m_input_lines.begin() + m_current_line_index);
781314564Sdim    fprintf(m_output_file, ANSI_CLEAR_BELOW);
782314564Sdim  }
783314564Sdim
784314564Sdim  SetCurrentLine(m_current_line_index - 1);
785314564Sdim  fprintf(m_output_file, ANSI_UP_N_ROWS ANSI_SET_COLUMN_N,
786314564Sdim          CountRowsForLine(m_input_lines[m_current_line_index]), 1);
787314564Sdim  return CC_NEWLINE;
788314564Sdim}
789314564Sdim
790314564Sdimunsigned char Editline::NextLineCommand(int ch) {
791314564Sdim  SaveEditedLine();
792314564Sdim
793314564Sdim  // Handle attempts to move down from the last line
794314564Sdim  if (m_current_line_index == m_input_lines.size() - 1) {
795314564Sdim    // Don't add an extra line if the existing last line is blank, move through
796314564Sdim    // history instead
797314564Sdim    if (IsOnlySpaces()) {
798360784Sdim      return RecallHistory(HistoryOperation::Newer);
799280031Sdim    }
800314564Sdim
801314564Sdim    // Determine indentation for the new line
802314564Sdim    int indentation = 0;
803314564Sdim    if (m_fix_indentation_callback) {
804314564Sdim      StringList lines = GetInputAsStringList();
805314564Sdim      lines.AppendString("");
806314564Sdim      indentation = m_fix_indentation_callback(
807314564Sdim          this, lines, 0, m_fix_indentation_callback_baton);
808280031Sdim    }
809314564Sdim    m_input_lines.insert(
810314564Sdim        m_input_lines.end(),
811314564Sdim        EditLineStringType(indentation, EditLineCharType(' ')));
812314564Sdim  }
813314564Sdim
814341825Sdim  // Move down past the current line using newlines to force scrolling if
815341825Sdim  // needed
816314564Sdim  SetCurrentLine(m_current_line_index + 1);
817314564Sdim  const LineInfoW *info = el_wline(m_editline);
818314564Sdim  int cursor_position = (int)((info->cursor - info->buffer) + GetPromptWidth());
819314564Sdim  int cursor_row = cursor_position / m_terminal_width;
820314564Sdim  for (int line_count = 0; line_count < m_current_line_rows - cursor_row;
821314564Sdim       line_count++) {
822314564Sdim    fprintf(m_output_file, "\n");
823314564Sdim  }
824314564Sdim  return CC_NEWLINE;
825280031Sdim}
826280031Sdim
827314564Sdimunsigned char Editline::PreviousHistoryCommand(int ch) {
828314564Sdim  SaveEditedLine();
829309124Sdim
830360784Sdim  return RecallHistory(HistoryOperation::Older);
831309124Sdim}
832309124Sdim
833314564Sdimunsigned char Editline::NextHistoryCommand(int ch) {
834314564Sdim  SaveEditedLine();
835309124Sdim
836360784Sdim  return RecallHistory(HistoryOperation::Newer);
837309124Sdim}
838309124Sdim
839314564Sdimunsigned char Editline::FixIndentationCommand(int ch) {
840314564Sdim  if (!m_fix_indentation_callback)
841314564Sdim    return CC_NORM;
842296417Sdim
843314564Sdim  // Insert the character typed before proceeding
844314564Sdim  EditLineCharType inserted[] = {(EditLineCharType)ch, 0};
845314564Sdim  el_winsertstr(m_editline, inserted);
846314564Sdim  LineInfoW *info = const_cast<LineInfoW *>(el_wline(m_editline));
847314564Sdim  int cursor_position = info->cursor - info->buffer;
848296417Sdim
849314564Sdim  // Save the edits and determine the correct indentation level
850314564Sdim  SaveEditedLine();
851314564Sdim  StringList lines = GetInputAsStringList(m_current_line_index + 1);
852314564Sdim  int indent_correction = m_fix_indentation_callback(
853314564Sdim      this, lines, cursor_position, m_fix_indentation_callback_baton);
854280031Sdim
855314564Sdim  // If it is already correct no special work is needed
856314564Sdim  if (indent_correction == 0)
857314564Sdim    return CC_REFRESH;
858296417Sdim
859314564Sdim  // Change the indentation level of the line
860314564Sdim  std::string currentLine = lines.GetStringAtIndex(m_current_line_index);
861314564Sdim  if (indent_correction > 0) {
862314564Sdim    currentLine = currentLine.insert(0, indent_correction, ' ');
863314564Sdim  } else {
864314564Sdim    currentLine = currentLine.erase(0, -indent_correction);
865314564Sdim  }
866296417Sdim#if LLDB_EDITLINE_USE_WCHAR
867314564Sdim  m_input_lines[m_current_line_index] = m_utf8conv.from_bytes(currentLine);
868296417Sdim#else
869314564Sdim  m_input_lines[m_current_line_index] = currentLine;
870296417Sdim#endif
871296417Sdim
872314564Sdim  // Update the display to reflect the change
873314564Sdim  MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
874314564Sdim  DisplayInput(m_current_line_index);
875314564Sdim
876314564Sdim  // Reposition the cursor back on the original line and prepare to restart
877341825Sdim  // editing with a new cursor position
878314564Sdim  SetCurrentLine(m_current_line_index);
879314564Sdim  MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
880314564Sdim  m_revert_cursor_index = cursor_position + indent_correction;
881314564Sdim  return CC_NEWLINE;
882280031Sdim}
883280031Sdim
884314564Sdimunsigned char Editline::RevertLineCommand(int ch) {
885314564Sdim  el_winsertstr(m_editline, m_input_lines[m_current_line_index].c_str());
886314564Sdim  if (m_revert_cursor_index >= 0) {
887314564Sdim    LineInfoW *info = const_cast<LineInfoW *>(el_wline(m_editline));
888314564Sdim    info->cursor = info->buffer + m_revert_cursor_index;
889314564Sdim    if (info->cursor > info->lastchar) {
890314564Sdim      info->cursor = info->lastchar;
891262182Semaste    }
892314564Sdim    m_revert_cursor_index = -1;
893314564Sdim  }
894314564Sdim  return CC_REFRESH;
895280031Sdim}
896262182Semaste
897314564Sdimunsigned char Editline::BufferStartCommand(int ch) {
898314564Sdim  SaveEditedLine();
899314564Sdim  MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
900314564Sdim  SetCurrentLine(0);
901314564Sdim  m_revert_cursor_index = 0;
902314564Sdim  return CC_NEWLINE;
903280031Sdim}
904262182Semaste
905314564Sdimunsigned char Editline::BufferEndCommand(int ch) {
906314564Sdim  SaveEditedLine();
907314564Sdim  MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockEnd);
908314564Sdim  SetCurrentLine((int)m_input_lines.size() - 1);
909314564Sdim  MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
910314564Sdim  return CC_NEWLINE;
911262182Semaste}
912262182Semaste
913344779Sdim/// Prints completions and their descriptions to the given file. Only the
914344779Sdim/// completions in the interval [start, end) are printed.
915360784Sdimstatic void
916360784SdimPrintCompletion(FILE *output_file,
917360784Sdim                llvm::ArrayRef<CompletionResult::Completion> results,
918360784Sdim                size_t max_len) {
919360784Sdim  for (const CompletionResult::Completion &c : results) {
920360784Sdim    fprintf(output_file, "\t%-*s", (int)max_len, c.GetCompletion().c_str());
921360784Sdim    if (!c.GetDescription().empty())
922360784Sdim      fprintf(output_file, " -- %s", c.GetDescription().c_str());
923360784Sdim    fprintf(output_file, "\n");
924360784Sdim  }
925360784Sdim}
926344779Sdim
927360784Sdimstatic void
928360784SdimDisplayCompletions(::EditLine *editline, FILE *output_file,
929360784Sdim                   llvm::ArrayRef<CompletionResult::Completion> results) {
930360784Sdim  assert(!results.empty());
931360784Sdim
932360784Sdim  fprintf(output_file, "\n" ANSI_CLEAR_BELOW "Available completions:\n");
933360784Sdim  const size_t page_size = 40;
934360784Sdim  bool all = false;
935360784Sdim
936360784Sdim  auto longest =
937360784Sdim      std::max_element(results.begin(), results.end(), [](auto &c1, auto &c2) {
938360784Sdim        return c1.GetCompletion().size() < c2.GetCompletion().size();
939360784Sdim      });
940360784Sdim
941360784Sdim  const size_t max_len = longest->GetCompletion().size();
942360784Sdim
943360784Sdim  if (results.size() < page_size) {
944360784Sdim    PrintCompletion(output_file, results, max_len);
945360784Sdim    return;
946344779Sdim  }
947344779Sdim
948360784Sdim  size_t cur_pos = 0;
949360784Sdim  while (cur_pos < results.size()) {
950360784Sdim    size_t remaining = results.size() - cur_pos;
951360784Sdim    size_t next_size = all ? remaining : std::min(page_size, remaining);
952344779Sdim
953360784Sdim    PrintCompletion(output_file, results.slice(cur_pos, next_size), max_len);
954344779Sdim
955360784Sdim    cur_pos += next_size;
956360784Sdim
957360784Sdim    if (cur_pos >= results.size())
958360784Sdim      break;
959360784Sdim
960360784Sdim    fprintf(output_file, "More (Y/n/a): ");
961360784Sdim    char reply = 'n';
962360784Sdim    int got_char = el_getc(editline, &reply);
963360784Sdim    fprintf(output_file, "\n");
964360784Sdim    if (got_char == -1 || reply == 'n')
965360784Sdim      break;
966360784Sdim    if (reply == 'a')
967360784Sdim      all = true;
968344779Sdim  }
969344779Sdim}
970344779Sdim
971314564Sdimunsigned char Editline::TabCommand(int ch) {
972314564Sdim  if (m_completion_callback == nullptr)
973314564Sdim    return CC_ERROR;
974314564Sdim
975314564Sdim  const LineInfo *line_info = el_line(m_editline);
976314564Sdim
977360784Sdim  llvm::StringRef line(line_info->buffer,
978360784Sdim                       line_info->lastchar - line_info->buffer);
979360784Sdim  unsigned cursor_index = line_info->cursor - line_info->buffer;
980360784Sdim  CompletionResult result;
981360784Sdim  CompletionRequest request(line, cursor_index, result);
982314564Sdim
983360784Sdim  m_completion_callback(request, m_completion_callback_baton);
984360784Sdim
985360784Sdim  llvm::ArrayRef<CompletionResult::Completion> results = result.GetResults();
986360784Sdim
987360784Sdim  StringList completions;
988360784Sdim  result.GetMatches(completions);
989360784Sdim
990360784Sdim  if (results.size() == 0)
991314564Sdim    return CC_ERROR;
992360784Sdim
993360784Sdim  if (results.size() == 1) {
994360784Sdim    CompletionResult::Completion completion = results.front();
995360784Sdim    switch (completion.GetMode()) {
996360784Sdim    case CompletionMode::Normal: {
997360784Sdim      std::string to_add = completion.GetCompletion();
998360784Sdim      to_add = to_add.substr(request.GetCursorArgumentPrefix().size());
999360784Sdim      if (request.GetParsedArg().IsQuoted())
1000360784Sdim        to_add.push_back(request.GetParsedArg().GetQuoteChar());
1001360784Sdim      to_add.push_back(' ');
1002360784Sdim      el_insertstr(m_editline, to_add.c_str());
1003360784Sdim      break;
1004360784Sdim    }
1005360784Sdim    case CompletionMode::Partial: {
1006360784Sdim      std::string to_add = completion.GetCompletion();
1007360784Sdim      to_add = to_add.substr(request.GetCursorArgumentPrefix().size());
1008360784Sdim      el_insertstr(m_editline, to_add.c_str());
1009360784Sdim      break;
1010360784Sdim    }
1011360784Sdim    case CompletionMode::RewriteLine: {
1012360784Sdim      el_deletestr(m_editline, line_info->cursor - line_info->buffer);
1013360784Sdim      el_insertstr(m_editline, completion.GetCompletion().c_str());
1014360784Sdim      break;
1015360784Sdim    }
1016360784Sdim    }
1017314564Sdim    return CC_REDISPLAY;
1018314564Sdim  }
1019314564Sdim
1020314564Sdim  // If we get a longer match display that first.
1021360784Sdim  std::string longest_prefix = completions.LongestCommonPrefix();
1022360784Sdim  if (!longest_prefix.empty())
1023360784Sdim    longest_prefix =
1024360784Sdim        longest_prefix.substr(request.GetCursorArgumentPrefix().size());
1025360784Sdim  if (!longest_prefix.empty()) {
1026360784Sdim    el_insertstr(m_editline, longest_prefix.c_str());
1027314564Sdim    return CC_REDISPLAY;
1028314564Sdim  }
1029314564Sdim
1030360784Sdim  DisplayCompletions(m_editline, m_output_file, results);
1031314564Sdim
1032360784Sdim  DisplayInput();
1033360784Sdim  MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
1034314564Sdim  return CC_REDISPLAY;
1035280031Sdim}
1036280031Sdim
1037314564Sdimvoid Editline::ConfigureEditor(bool multiline) {
1038314564Sdim  if (m_editline && m_multiline_enabled == multiline)
1039314564Sdim    return;
1040314564Sdim  m_multiline_enabled = multiline;
1041280031Sdim
1042314564Sdim  if (m_editline) {
1043341825Sdim    // Disable edit mode to stop the terminal from flushing all input during
1044341825Sdim    // the call to el_end() since we expect to have multiple editline instances
1045341825Sdim    // in this program.
1046314564Sdim    el_set(m_editline, EL_EDITMODE, 0);
1047314564Sdim    el_end(m_editline);
1048314564Sdim  }
1049280031Sdim
1050314564Sdim  m_editline =
1051314564Sdim      el_init(m_editor_name.c_str(), m_input_file, m_output_file, m_error_file);
1052314564Sdim  TerminalSizeChanged();
1053309124Sdim
1054314564Sdim  if (m_history_sp && m_history_sp->IsValid()) {
1055353358Sdim    if (!m_history_sp->Load()) {
1056353358Sdim        fputs("Could not load history file\n.", m_output_file);
1057353358Sdim    }
1058314564Sdim    el_wset(m_editline, EL_HIST, history, m_history_sp->GetHistoryPtr());
1059314564Sdim  }
1060314564Sdim  el_set(m_editline, EL_CLIENTDATA, this);
1061314564Sdim  el_set(m_editline, EL_SIGNAL, 0);
1062314564Sdim  el_set(m_editline, EL_EDITOR, "emacs");
1063314564Sdim  el_set(m_editline, EL_PROMPT,
1064314564Sdim         (EditlinePromptCallbackType)([](EditLine *editline) {
1065314564Sdim           return Editline::InstanceFor(editline)->Prompt();
1066314564Sdim         }));
1067280031Sdim
1068314564Sdim  el_wset(m_editline, EL_GETCFN, (EditlineGetCharCallbackType)([](
1069321369Sdim                                     EditLine *editline, EditLineGetCharType *c) {
1070314564Sdim            return Editline::InstanceFor(editline)->GetCharacter(c);
1071314564Sdim          }));
1072280031Sdim
1073341825Sdim  // Commands used for multiline support, registered whether or not they're
1074341825Sdim  // used
1075314564Sdim  el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-break-line"),
1076314564Sdim          EditLineConstString("Insert a line break"),
1077314564Sdim          (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
1078314564Sdim            return Editline::InstanceFor(editline)->BreakLineCommand(ch);
1079314564Sdim          }));
1080314564Sdim  el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-end-or-add-line"),
1081314564Sdim          EditLineConstString("End editing or continue when incomplete"),
1082314564Sdim          (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
1083314564Sdim            return Editline::InstanceFor(editline)->EndOrAddLineCommand(ch);
1084314564Sdim          }));
1085314564Sdim  el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-delete-next-char"),
1086314564Sdim          EditLineConstString("Delete next character"),
1087314564Sdim          (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
1088314564Sdim            return Editline::InstanceFor(editline)->DeleteNextCharCommand(ch);
1089314564Sdim          }));
1090314564Sdim  el_wset(
1091314564Sdim      m_editline, EL_ADDFN, EditLineConstString("lldb-delete-previous-char"),
1092314564Sdim      EditLineConstString("Delete previous character"),
1093314564Sdim      (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
1094314564Sdim        return Editline::InstanceFor(editline)->DeletePreviousCharCommand(ch);
1095314564Sdim      }));
1096314564Sdim  el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-previous-line"),
1097314564Sdim          EditLineConstString("Move to previous line"),
1098314564Sdim          (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
1099314564Sdim            return Editline::InstanceFor(editline)->PreviousLineCommand(ch);
1100314564Sdim          }));
1101314564Sdim  el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-next-line"),
1102314564Sdim          EditLineConstString("Move to next line"),
1103314564Sdim          (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
1104314564Sdim            return Editline::InstanceFor(editline)->NextLineCommand(ch);
1105314564Sdim          }));
1106314564Sdim  el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-previous-history"),
1107314564Sdim          EditLineConstString("Move to previous history"),
1108314564Sdim          (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
1109314564Sdim            return Editline::InstanceFor(editline)->PreviousHistoryCommand(ch);
1110314564Sdim          }));
1111314564Sdim  el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-next-history"),
1112314564Sdim          EditLineConstString("Move to next history"),
1113314564Sdim          (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
1114314564Sdim            return Editline::InstanceFor(editline)->NextHistoryCommand(ch);
1115314564Sdim          }));
1116314564Sdim  el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-buffer-start"),
1117314564Sdim          EditLineConstString("Move to start of buffer"),
1118314564Sdim          (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
1119314564Sdim            return Editline::InstanceFor(editline)->BufferStartCommand(ch);
1120314564Sdim          }));
1121314564Sdim  el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-buffer-end"),
1122314564Sdim          EditLineConstString("Move to end of buffer"),
1123314564Sdim          (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
1124314564Sdim            return Editline::InstanceFor(editline)->BufferEndCommand(ch);
1125314564Sdim          }));
1126314564Sdim  el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-fix-indentation"),
1127314564Sdim          EditLineConstString("Fix line indentation"),
1128314564Sdim          (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
1129314564Sdim            return Editline::InstanceFor(editline)->FixIndentationCommand(ch);
1130314564Sdim          }));
1131314564Sdim
1132341825Sdim  // Register the complete callback under two names for compatibility with
1133341825Sdim  // older clients using custom .editrc files (largely because libedit has a
1134341825Sdim  // bad bug where if you have a bind command that tries to bind to a function
1135341825Sdim  // name that doesn't exist, it can corrupt the heap and crash your process
1136341825Sdim  // later.)
1137314564Sdim  EditlineCommandCallbackType complete_callback = [](EditLine *editline,
1138314564Sdim                                                     int ch) {
1139314564Sdim    return Editline::InstanceFor(editline)->TabCommand(ch);
1140314564Sdim  };
1141314564Sdim  el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-complete"),
1142314564Sdim          EditLineConstString("Invoke completion"), complete_callback);
1143314564Sdim  el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb_complete"),
1144314564Sdim          EditLineConstString("Invoke completion"), complete_callback);
1145314564Sdim
1146314564Sdim  // General bindings we don't mind being overridden
1147314564Sdim  if (!multiline) {
1148314564Sdim    el_set(m_editline, EL_BIND, "^r", "em-inc-search-prev",
1149314564Sdim           NULL); // Cycle through backwards search, entering string
1150314564Sdim  }
1151314564Sdim  el_set(m_editline, EL_BIND, "^w", "ed-delete-prev-word",
1152314564Sdim         NULL); // Delete previous word, behave like bash in emacs mode
1153314564Sdim  el_set(m_editline, EL_BIND, "\t", "lldb-complete",
1154314564Sdim         NULL); // Bind TAB to auto complete
1155314564Sdim
1156360784Sdim  // Allow ctrl-left-arrow and ctrl-right-arrow for navigation, behave like
1157360784Sdim  // bash in emacs mode.
1158360784Sdim  el_set(m_editline, EL_BIND, ESCAPE "[1;5C", "em-next-word", NULL);
1159360784Sdim  el_set(m_editline, EL_BIND, ESCAPE "[1;5D", "ed-prev-word", NULL);
1160360784Sdim  el_set(m_editline, EL_BIND, ESCAPE "[5C", "em-next-word", NULL);
1161360784Sdim  el_set(m_editline, EL_BIND, ESCAPE "[5D", "ed-prev-word", NULL);
1162360784Sdim  el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[C", "em-next-word", NULL);
1163360784Sdim  el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[D", "ed-prev-word", NULL);
1164360784Sdim
1165314564Sdim  // Allow user-specific customization prior to registering bindings we
1166314564Sdim  // absolutely require
1167353358Sdim  el_source(m_editline, nullptr);
1168314564Sdim
1169314564Sdim  // Register an internal binding that external developers shouldn't use
1170314564Sdim  el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-revert-line"),
1171314564Sdim          EditLineConstString("Revert line to saved state"),
1172314564Sdim          (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
1173314564Sdim            return Editline::InstanceFor(editline)->RevertLineCommand(ch);
1174314564Sdim          }));
1175314564Sdim
1176314564Sdim  // Register keys that perform auto-indent correction
1177314564Sdim  if (m_fix_indentation_callback && m_fix_indentation_callback_chars) {
1178314564Sdim    char bind_key[2] = {0, 0};
1179314564Sdim    const char *indent_chars = m_fix_indentation_callback_chars;
1180314564Sdim    while (*indent_chars) {
1181314564Sdim      bind_key[0] = *indent_chars;
1182314564Sdim      el_set(m_editline, EL_BIND, bind_key, "lldb-fix-indentation", NULL);
1183314564Sdim      ++indent_chars;
1184280031Sdim    }
1185314564Sdim  }
1186280031Sdim
1187314564Sdim  // Multi-line editor bindings
1188314564Sdim  if (multiline) {
1189314564Sdim    el_set(m_editline, EL_BIND, "\n", "lldb-end-or-add-line", NULL);
1190314564Sdim    el_set(m_editline, EL_BIND, "\r", "lldb-end-or-add-line", NULL);
1191314564Sdim    el_set(m_editline, EL_BIND, ESCAPE "\n", "lldb-break-line", NULL);
1192314564Sdim    el_set(m_editline, EL_BIND, ESCAPE "\r", "lldb-break-line", NULL);
1193314564Sdim    el_set(m_editline, EL_BIND, "^p", "lldb-previous-line", NULL);
1194314564Sdim    el_set(m_editline, EL_BIND, "^n", "lldb-next-line", NULL);
1195314564Sdim    el_set(m_editline, EL_BIND, "^?", "lldb-delete-previous-char", NULL);
1196314564Sdim    el_set(m_editline, EL_BIND, "^d", "lldb-delete-next-char", NULL);
1197314564Sdim    el_set(m_editline, EL_BIND, ESCAPE "[3~", "lldb-delete-next-char", NULL);
1198314564Sdim    el_set(m_editline, EL_BIND, ESCAPE "[\\^", "lldb-revert-line", NULL);
1199280031Sdim
1200314564Sdim    // Editor-specific bindings
1201314564Sdim    if (IsEmacs()) {
1202314564Sdim      el_set(m_editline, EL_BIND, ESCAPE "<", "lldb-buffer-start", NULL);
1203314564Sdim      el_set(m_editline, EL_BIND, ESCAPE ">", "lldb-buffer-end", NULL);
1204314564Sdim      el_set(m_editline, EL_BIND, ESCAPE "[A", "lldb-previous-line", NULL);
1205314564Sdim      el_set(m_editline, EL_BIND, ESCAPE "[B", "lldb-next-line", NULL);
1206314564Sdim      el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[A", "lldb-previous-history",
1207314564Sdim             NULL);
1208314564Sdim      el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[B", "lldb-next-history",
1209314564Sdim             NULL);
1210314564Sdim      el_set(m_editline, EL_BIND, ESCAPE "[1;3A", "lldb-previous-history",
1211314564Sdim             NULL);
1212314564Sdim      el_set(m_editline, EL_BIND, ESCAPE "[1;3B", "lldb-next-history", NULL);
1213314564Sdim    } else {
1214314564Sdim      el_set(m_editline, EL_BIND, "^H", "lldb-delete-previous-char", NULL);
1215314564Sdim
1216314564Sdim      el_set(m_editline, EL_BIND, "-a", ESCAPE "[A", "lldb-previous-line",
1217314564Sdim             NULL);
1218314564Sdim      el_set(m_editline, EL_BIND, "-a", ESCAPE "[B", "lldb-next-line", NULL);
1219314564Sdim      el_set(m_editline, EL_BIND, "-a", "x", "lldb-delete-next-char", NULL);
1220314564Sdim      el_set(m_editline, EL_BIND, "-a", "^H", "lldb-delete-previous-char",
1221314564Sdim             NULL);
1222314564Sdim      el_set(m_editline, EL_BIND, "-a", "^?", "lldb-delete-previous-char",
1223314564Sdim             NULL);
1224314564Sdim
1225314564Sdim      // Escape is absorbed exiting edit mode, so re-register important
1226341825Sdim      // sequences without the prefix
1227314564Sdim      el_set(m_editline, EL_BIND, "-a", "[A", "lldb-previous-line", NULL);
1228314564Sdim      el_set(m_editline, EL_BIND, "-a", "[B", "lldb-next-line", NULL);
1229314564Sdim      el_set(m_editline, EL_BIND, "-a", "[\\^", "lldb-revert-line", NULL);
1230280031Sdim    }
1231314564Sdim  }
1232262182Semaste}
1233262182Semaste
1234280031Sdim// Editline public methods
1235280031Sdim
1236314564SdimEditline *Editline::InstanceFor(EditLine *editline) {
1237314564Sdim  Editline *editor;
1238314564Sdim  el_get(editline, EL_CLIENTDATA, &editor);
1239314564Sdim  return editor;
1240262182Semaste}
1241262182Semaste
1242314564SdimEditline::Editline(const char *editline_name, FILE *input_file,
1243314564Sdim                   FILE *output_file, FILE *error_file, bool color_prompts)
1244314564Sdim    : m_editor_status(EditorStatus::Complete), m_color_prompts(color_prompts),
1245314564Sdim      m_input_file(input_file), m_output_file(output_file),
1246314564Sdim      m_error_file(error_file), m_input_connection(fileno(input_file), false) {
1247314564Sdim  // Get a shared history instance
1248314564Sdim  m_editor_name = (editline_name == nullptr) ? "lldb-tmp" : editline_name;
1249314564Sdim  m_history_sp = EditlineHistory::GetHistory(m_editor_name);
1250309124Sdim
1251309124Sdim#ifdef USE_SETUPTERM_WORKAROUND
1252314564Sdim  if (m_output_file) {
1253314564Sdim    const int term_fd = fileno(m_output_file);
1254314564Sdim    if (term_fd != -1) {
1255314564Sdim      static std::mutex *g_init_terminal_fds_mutex_ptr = nullptr;
1256314564Sdim      static std::set<int> *g_init_terminal_fds_ptr = nullptr;
1257321369Sdim      static llvm::once_flag g_once_flag;
1258321369Sdim      llvm::call_once(g_once_flag, [&]() {
1259314564Sdim        g_init_terminal_fds_mutex_ptr =
1260314564Sdim            new std::mutex(); // NOTE: Leak to avoid C++ destructor chain issues
1261314564Sdim        g_init_terminal_fds_ptr = new std::set<int>(); // NOTE: Leak to avoid
1262314564Sdim                                                       // C++ destructor chain
1263314564Sdim                                                       // issues
1264314564Sdim      });
1265309124Sdim
1266314564Sdim      // We must make sure to initialize the terminal a given file descriptor
1267314564Sdim      // only once. If we do this multiple times, we start leaking memory.
1268314564Sdim      std::lock_guard<std::mutex> guard(*g_init_terminal_fds_mutex_ptr);
1269314564Sdim      if (g_init_terminal_fds_ptr->find(term_fd) ==
1270314564Sdim          g_init_terminal_fds_ptr->end()) {
1271314564Sdim        g_init_terminal_fds_ptr->insert(term_fd);
1272314564Sdim        setupterm((char *)0, term_fd, (int *)0);
1273314564Sdim      }
1274309124Sdim    }
1275314564Sdim  }
1276309124Sdim#endif
1277262182Semaste}
1278262182Semaste
1279314564SdimEditline::~Editline() {
1280314564Sdim  if (m_editline) {
1281341825Sdim    // Disable edit mode to stop the terminal from flushing all input during
1282341825Sdim    // the call to el_end() since we expect to have multiple editline instances
1283341825Sdim    // in this program.
1284314564Sdim    el_set(m_editline, EL_EDITMODE, 0);
1285314564Sdim    el_end(m_editline);
1286314564Sdim    m_editline = nullptr;
1287314564Sdim  }
1288280031Sdim
1289341825Sdim  // EditlineHistory objects are sometimes shared between multiple Editline
1290341825Sdim  // instances with the same program name. So just release our shared pointer
1291341825Sdim  // and if we are the last owner, it will save the history to the history save
1292341825Sdim  // file automatically.
1293314564Sdim  m_history_sp.reset();
1294262182Semaste}
1295262182Semaste
1296314564Sdimvoid Editline::SetPrompt(const char *prompt) {
1297314564Sdim  m_set_prompt = prompt == nullptr ? "" : prompt;
1298262182Semaste}
1299262182Semaste
1300314564Sdimvoid Editline::SetContinuationPrompt(const char *continuation_prompt) {
1301314564Sdim  m_set_continuation_prompt =
1302314564Sdim      continuation_prompt == nullptr ? "" : continuation_prompt;
1303280031Sdim}
1304280031Sdim
1305314564Sdimvoid Editline::TerminalSizeChanged() {
1306314564Sdim  if (m_editline != nullptr) {
1307314564Sdim    el_resize(m_editline);
1308314564Sdim    int columns;
1309353358Sdim    // This function is documenting as taking (const char *, void *) for the
1310353358Sdim    // vararg part, but in reality in was consuming arguments until the first
1311353358Sdim    // null pointer. This was fixed in libedit in April 2019
1312353358Sdim    // <http://mail-index.netbsd.org/source-changes/2019/04/26/msg105454.html>,
1313353358Sdim    // but we're keeping the workaround until a version with that fix is more
1314353358Sdim    // widely available.
1315353358Sdim    if (el_get(m_editline, EL_GETTC, "co", &columns, nullptr) == 0) {
1316314564Sdim      m_terminal_width = columns;
1317314564Sdim      if (m_current_line_rows != -1) {
1318314564Sdim        const LineInfoW *info = el_wline(m_editline);
1319314564Sdim        int lineLength =
1320314564Sdim            (int)((info->lastchar - info->buffer) + GetPromptWidth());
1321314564Sdim        m_current_line_rows = (lineLength / columns) + 1;
1322314564Sdim      }
1323314564Sdim    } else {
1324314564Sdim      m_terminal_width = INT_MAX;
1325314564Sdim      m_current_line_rows = 1;
1326280031Sdim    }
1327314564Sdim  }
1328280031Sdim}
1329280031Sdim
1330314564Sdimconst char *Editline::GetPrompt() { return m_set_prompt.c_str(); }
1331280031Sdim
1332314564Sdimuint32_t Editline::GetCurrentLine() { return m_current_line_index; }
1333280031Sdim
1334314564Sdimbool Editline::Interrupt() {
1335314564Sdim  bool result = true;
1336314564Sdim  std::lock_guard<std::mutex> guard(m_output_mutex);
1337314564Sdim  if (m_editor_status == EditorStatus::Editing) {
1338314564Sdim    fprintf(m_output_file, "^C\n");
1339314564Sdim    result = m_input_connection.InterruptRead();
1340314564Sdim  }
1341314564Sdim  m_editor_status = EditorStatus::Interrupted;
1342314564Sdim  return result;
1343262182Semaste}
1344262182Semaste
1345314564Sdimbool Editline::Cancel() {
1346314564Sdim  bool result = true;
1347314564Sdim  std::lock_guard<std::mutex> guard(m_output_mutex);
1348314564Sdim  if (m_editor_status == EditorStatus::Editing) {
1349314564Sdim    MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
1350314564Sdim    fprintf(m_output_file, ANSI_CLEAR_BELOW);
1351314564Sdim    result = m_input_connection.InterruptRead();
1352314564Sdim  }
1353314564Sdim  m_editor_status = EditorStatus::Interrupted;
1354314564Sdim  return result;
1355262182Semaste}
1356280031Sdim
1357314564Sdimvoid Editline::SetAutoCompleteCallback(CompleteCallbackType callback,
1358314564Sdim                                       void *baton) {
1359314564Sdim  m_completion_callback = callback;
1360314564Sdim  m_completion_callback_baton = baton;
1361262182Semaste}
1362262182Semaste
1363314564Sdimvoid Editline::SetIsInputCompleteCallback(IsInputCompleteCallbackType callback,
1364314564Sdim                                          void *baton) {
1365314564Sdim  m_is_input_complete_callback = callback;
1366314564Sdim  m_is_input_complete_callback_baton = baton;
1367262182Semaste}
1368262182Semaste
1369314564Sdimbool Editline::SetFixIndentationCallback(FixIndentationCallbackType callback,
1370314564Sdim                                         void *baton,
1371314564Sdim                                         const char *indent_chars) {
1372314564Sdim  m_fix_indentation_callback = callback;
1373314564Sdim  m_fix_indentation_callback_baton = baton;
1374314564Sdim  m_fix_indentation_callback_chars = indent_chars;
1375314564Sdim  return false;
1376262182Semaste}
1377262182Semaste
1378314564Sdimbool Editline::GetLine(std::string &line, bool &interrupted) {
1379314564Sdim  ConfigureEditor(false);
1380314564Sdim  m_input_lines = std::vector<EditLineStringType>();
1381314564Sdim  m_input_lines.insert(m_input_lines.begin(), EditLineConstString(""));
1382288943Sdim
1383314564Sdim  std::lock_guard<std::mutex> guard(m_output_mutex);
1384309124Sdim
1385314564Sdim  lldbassert(m_editor_status != EditorStatus::Editing);
1386314564Sdim  if (m_editor_status == EditorStatus::Interrupted) {
1387314564Sdim    m_editor_status = EditorStatus::Complete;
1388314564Sdim    interrupted = true;
1389314564Sdim    return true;
1390314564Sdim  }
1391288943Sdim
1392314564Sdim  SetCurrentLine(0);
1393314564Sdim  m_in_history = false;
1394314564Sdim  m_editor_status = EditorStatus::Editing;
1395314564Sdim  m_revert_cursor_index = -1;
1396280031Sdim
1397314564Sdim  int count;
1398314564Sdim  auto input = el_wgets(m_editline, &count);
1399280031Sdim
1400314564Sdim  interrupted = m_editor_status == EditorStatus::Interrupted;
1401314564Sdim  if (!interrupted) {
1402314564Sdim    if (input == nullptr) {
1403314564Sdim      fprintf(m_output_file, "\n");
1404314564Sdim      m_editor_status = EditorStatus::EndOfInput;
1405314564Sdim    } else {
1406314564Sdim      m_history_sp->Enter(input);
1407280031Sdim#if LLDB_EDITLINE_USE_WCHAR
1408314564Sdim      line = m_utf8conv.to_bytes(SplitLines(input)[0]);
1409280031Sdim#else
1410314564Sdim      line = SplitLines(input)[0];
1411280031Sdim#endif
1412314564Sdim      m_editor_status = EditorStatus::Complete;
1413262182Semaste    }
1414314564Sdim  }
1415314564Sdim  return m_editor_status != EditorStatus::EndOfInput;
1416262182Semaste}
1417262182Semaste
1418314564Sdimbool Editline::GetLines(int first_line_number, StringList &lines,
1419314564Sdim                        bool &interrupted) {
1420314564Sdim  ConfigureEditor(true);
1421309124Sdim
1422341825Sdim  // Print the initial input lines, then move the cursor back up to the start
1423341825Sdim  // of input
1424314564Sdim  SetBaseLineNumber(first_line_number);
1425314564Sdim  m_input_lines = std::vector<EditLineStringType>();
1426314564Sdim  m_input_lines.insert(m_input_lines.begin(), EditLineConstString(""));
1427280031Sdim
1428314564Sdim  std::lock_guard<std::mutex> guard(m_output_mutex);
1429314564Sdim  // Begin the line editing loop
1430314564Sdim  DisplayInput();
1431314564Sdim  SetCurrentLine(0);
1432314564Sdim  MoveCursor(CursorLocation::BlockEnd, CursorLocation::BlockStart);
1433314564Sdim  m_editor_status = EditorStatus::Editing;
1434314564Sdim  m_in_history = false;
1435262182Semaste
1436314564Sdim  m_revert_cursor_index = -1;
1437314564Sdim  while (m_editor_status == EditorStatus::Editing) {
1438314564Sdim    int count;
1439314564Sdim    m_current_line_rows = -1;
1440314564Sdim    el_wpush(m_editline, EditLineConstString(
1441314564Sdim                             "\x1b[^")); // Revert to the existing line content
1442314564Sdim    el_wgets(m_editline, &count);
1443314564Sdim  }
1444314564Sdim
1445314564Sdim  interrupted = m_editor_status == EditorStatus::Interrupted;
1446314564Sdim  if (!interrupted) {
1447314564Sdim    // Save the completed entry in history before returning
1448314564Sdim    m_history_sp->Enter(CombineLines(m_input_lines).c_str());
1449314564Sdim
1450314564Sdim    lines = GetInputAsStringList();
1451314564Sdim  }
1452314564Sdim  return m_editor_status != EditorStatus::EndOfInput;
1453262182Semaste}
1454288943Sdim
1455314564Sdimvoid Editline::PrintAsync(Stream *stream, const char *s, size_t len) {
1456314564Sdim  std::lock_guard<std::mutex> guard(m_output_mutex);
1457314564Sdim  if (m_editor_status == EditorStatus::Editing) {
1458314564Sdim    MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
1459314564Sdim    fprintf(m_output_file, ANSI_CLEAR_BELOW);
1460314564Sdim  }
1461314564Sdim  stream->Write(s, len);
1462314564Sdim  stream->Flush();
1463314564Sdim  if (m_editor_status == EditorStatus::Editing) {
1464314564Sdim    DisplayInput();
1465314564Sdim    MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
1466314564Sdim  }
1467314564Sdim}
1468314564Sdim
1469321369Sdimbool Editline::CompleteCharacter(char ch, EditLineGetCharType &out) {
1470314564Sdim#if !LLDB_EDITLINE_USE_WCHAR
1471314564Sdim  if (ch == (char)EOF)
1472314564Sdim    return false;
1473314564Sdim
1474321369Sdim  out = (unsigned char)ch;
1475314564Sdim  return true;
1476314564Sdim#else
1477314564Sdim  std::codecvt_utf8<wchar_t> cvt;
1478314564Sdim  llvm::SmallString<4> input;
1479314564Sdim  for (;;) {
1480314564Sdim    const char *from_next;
1481314564Sdim    wchar_t *to_next;
1482314564Sdim    std::mbstate_t state = std::mbstate_t();
1483314564Sdim    input.push_back(ch);
1484314564Sdim    switch (cvt.in(state, input.begin(), input.end(), from_next, &out, &out + 1,
1485314564Sdim                   to_next)) {
1486314564Sdim    case std::codecvt_base::ok:
1487360784Sdim      return out != (int)WEOF;
1488314564Sdim
1489314564Sdim    case std::codecvt_base::error:
1490314564Sdim    case std::codecvt_base::noconv:
1491314564Sdim      return false;
1492314564Sdim
1493314564Sdim    case std::codecvt_base::partial:
1494314564Sdim      lldb::ConnectionStatus status;
1495314564Sdim      size_t read_count = m_input_connection.Read(
1496314564Sdim          &ch, 1, std::chrono::seconds(0), status, nullptr);
1497314564Sdim      if (read_count == 0)
1498314564Sdim        return false;
1499314564Sdim      break;
1500288943Sdim    }
1501314564Sdim  }
1502314564Sdim#endif
1503288943Sdim}
1504