xref: /freebsd-src/contrib/llvm-project/lldb/source/Host/common/Editline.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
15ffd83dbSDimitry Andric //===-- Editline.cpp ------------------------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
9fe6060f1SDimitry Andric #include <climits>
100b57cec5SDimitry Andric #include <iomanip>
11bdd1243dSDimitry Andric #include <optional>
12fe6060f1SDimitry Andric 
13fe6060f1SDimitry Andric #include "lldb/Host/Editline.h"
140b57cec5SDimitry Andric 
150b57cec5SDimitry Andric #include "lldb/Host/ConnectionFileDescriptor.h"
160b57cec5SDimitry Andric #include "lldb/Host/FileSystem.h"
170b57cec5SDimitry Andric #include "lldb/Host/Host.h"
189dba64beSDimitry Andric #include "lldb/Utility/CompletionRequest.h"
190b57cec5SDimitry Andric #include "lldb/Utility/FileSpec.h"
200b57cec5SDimitry Andric #include "lldb/Utility/LLDBAssert.h"
210b57cec5SDimitry Andric #include "lldb/Utility/SelectHelper.h"
220b57cec5SDimitry Andric #include "lldb/Utility/Status.h"
230b57cec5SDimitry Andric #include "lldb/Utility/StreamString.h"
240b57cec5SDimitry Andric #include "lldb/Utility/StringList.h"
250b57cec5SDimitry Andric #include "lldb/Utility/Timeout.h"
260b57cec5SDimitry Andric 
270b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h"
285f757f3fSDimitry Andric #include "llvm/Support/Locale.h"
290b57cec5SDimitry Andric #include "llvm/Support/Threading.h"
300b57cec5SDimitry Andric 
310b57cec5SDimitry Andric using namespace lldb_private;
320b57cec5SDimitry Andric using namespace lldb_private::line_editor;
330b57cec5SDimitry Andric 
340b57cec5SDimitry Andric // Editline uses careful cursor management to achieve the illusion of editing a
350b57cec5SDimitry Andric // multi-line block of text with a single line editor.  Preserving this
360b57cec5SDimitry Andric // illusion requires fairly careful management of cursor state.  Read and
370b57cec5SDimitry Andric // understand the relationship between DisplayInput(), MoveCursor(),
380b57cec5SDimitry Andric // SetCurrentLine(), and SaveEditedLine() before making changes.
390b57cec5SDimitry Andric 
40e8d8bef9SDimitry Andric /// https://www.ecma-international.org/publications/files/ECMA-ST/Ecma-048.pdf
410b57cec5SDimitry Andric #define ESCAPE "\x1b"
420b57cec5SDimitry Andric #define ANSI_CLEAR_BELOW ESCAPE "[J"
430b57cec5SDimitry Andric #define ANSI_CLEAR_RIGHT ESCAPE "[K"
440b57cec5SDimitry Andric #define ANSI_SET_COLUMN_N ESCAPE "[%dG"
450b57cec5SDimitry Andric #define ANSI_UP_N_ROWS ESCAPE "[%dA"
460b57cec5SDimitry Andric #define ANSI_DOWN_N_ROWS ESCAPE "[%dB"
470b57cec5SDimitry Andric 
480b57cec5SDimitry Andric #if LLDB_EDITLINE_USE_WCHAR
490b57cec5SDimitry Andric 
500b57cec5SDimitry Andric #define EditLineConstString(str) L##str
510b57cec5SDimitry Andric #define EditLineStringFormatSpec "%ls"
520b57cec5SDimitry Andric 
530b57cec5SDimitry Andric #else
540b57cec5SDimitry Andric 
550b57cec5SDimitry Andric #define EditLineConstString(str) str
560b57cec5SDimitry Andric #define EditLineStringFormatSpec "%s"
570b57cec5SDimitry Andric 
580b57cec5SDimitry Andric // use #defines so wide version functions and structs will resolve to old
590b57cec5SDimitry Andric // versions for case of libedit not built with wide char support
600b57cec5SDimitry Andric #define history_w history
610b57cec5SDimitry Andric #define history_winit history_init
620b57cec5SDimitry Andric #define history_wend history_end
630b57cec5SDimitry Andric #define HistoryW History
640b57cec5SDimitry Andric #define HistEventW HistEvent
650b57cec5SDimitry Andric #define LineInfoW LineInfo
660b57cec5SDimitry Andric 
670b57cec5SDimitry Andric #define el_wgets el_gets
680b57cec5SDimitry Andric #define el_wgetc el_getc
690b57cec5SDimitry Andric #define el_wpush el_push
700b57cec5SDimitry Andric #define el_wparse el_parse
710b57cec5SDimitry Andric #define el_wset el_set
720b57cec5SDimitry Andric #define el_wget el_get
730b57cec5SDimitry Andric #define el_wline el_line
740b57cec5SDimitry Andric #define el_winsertstr el_insertstr
750b57cec5SDimitry Andric #define el_wdeletestr el_deletestr
760b57cec5SDimitry Andric 
770b57cec5SDimitry Andric #endif // #if LLDB_EDITLINE_USE_WCHAR
780b57cec5SDimitry Andric 
790b57cec5SDimitry Andric bool IsOnlySpaces(const EditLineStringType &content) {
800b57cec5SDimitry Andric   for (wchar_t ch : content) {
810b57cec5SDimitry Andric     if (ch != EditLineCharType(' '))
820b57cec5SDimitry Andric       return false;
830b57cec5SDimitry Andric   }
840b57cec5SDimitry Andric   return true;
850b57cec5SDimitry Andric }
860b57cec5SDimitry Andric 
875f757f3fSDimitry Andric static size_t ColumnWidth(llvm::StringRef str) {
885f757f3fSDimitry Andric   return llvm::sys::locale::columnWidth(str);
895f757f3fSDimitry Andric }
905f757f3fSDimitry Andric 
91480093f4SDimitry Andric static int GetOperation(HistoryOperation op) {
92480093f4SDimitry Andric   // The naming used by editline for the history operations is counter
935ffd83dbSDimitry Andric   // intuitive to how it's used in LLDB's editline implementation.
945ffd83dbSDimitry Andric   //
955ffd83dbSDimitry Andric   //  - The H_LAST returns the oldest entry in the history.
96480093f4SDimitry Andric   //
97480093f4SDimitry Andric   //  - The H_PREV operation returns the previous element in the history, which
98480093f4SDimitry Andric   //    is newer than the current one.
99480093f4SDimitry Andric   //
1005ffd83dbSDimitry Andric   //  - The H_CURR returns the current entry in the history.
1015ffd83dbSDimitry Andric   //
102480093f4SDimitry Andric   //  - The H_NEXT operation returns the next element in the history, which is
103480093f4SDimitry Andric   //    older than the current one.
104480093f4SDimitry Andric   //
1055ffd83dbSDimitry Andric   //  - The H_FIRST returns the most recent entry in the history.
1065ffd83dbSDimitry Andric   //
107480093f4SDimitry Andric   // The naming of the enum entries match the semantic meaning.
108480093f4SDimitry Andric   switch(op) {
109480093f4SDimitry Andric     case HistoryOperation::Oldest:
1105ffd83dbSDimitry Andric       return H_LAST;
111480093f4SDimitry Andric     case HistoryOperation::Older:
112480093f4SDimitry Andric       return H_NEXT;
113480093f4SDimitry Andric     case HistoryOperation::Current:
114480093f4SDimitry Andric       return H_CURR;
115480093f4SDimitry Andric     case HistoryOperation::Newer:
116480093f4SDimitry Andric       return H_PREV;
117480093f4SDimitry Andric     case HistoryOperation::Newest:
1185ffd83dbSDimitry Andric       return H_FIRST;
119480093f4SDimitry Andric   }
120480093f4SDimitry Andric   llvm_unreachable("Fully covered switch!");
121480093f4SDimitry Andric }
122480093f4SDimitry Andric 
123480093f4SDimitry Andric 
1240b57cec5SDimitry Andric EditLineStringType CombineLines(const std::vector<EditLineStringType> &lines) {
1250b57cec5SDimitry Andric   EditLineStringStreamType combined_stream;
1260b57cec5SDimitry Andric   for (EditLineStringType line : lines) {
1270b57cec5SDimitry Andric     combined_stream << line.c_str() << "\n";
1280b57cec5SDimitry Andric   }
1290b57cec5SDimitry Andric   return combined_stream.str();
1300b57cec5SDimitry Andric }
1310b57cec5SDimitry Andric 
1320b57cec5SDimitry Andric std::vector<EditLineStringType> SplitLines(const EditLineStringType &input) {
1330b57cec5SDimitry Andric   std::vector<EditLineStringType> result;
1340b57cec5SDimitry Andric   size_t start = 0;
1350b57cec5SDimitry Andric   while (start < input.length()) {
1360b57cec5SDimitry Andric     size_t end = input.find('\n', start);
1370b57cec5SDimitry Andric     if (end == std::string::npos) {
1385ffd83dbSDimitry Andric       result.push_back(input.substr(start));
1390b57cec5SDimitry Andric       break;
1400b57cec5SDimitry Andric     }
1415ffd83dbSDimitry Andric     result.push_back(input.substr(start, end - start));
1420b57cec5SDimitry Andric     start = end + 1;
1430b57cec5SDimitry Andric   }
144fe6060f1SDimitry Andric   // Treat an empty history session as a single command of zero-length instead
145fe6060f1SDimitry Andric   // of returning an empty vector.
146fe6060f1SDimitry Andric   if (result.empty()) {
147fe6060f1SDimitry Andric     result.emplace_back();
148fe6060f1SDimitry Andric   }
1490b57cec5SDimitry Andric   return result;
1500b57cec5SDimitry Andric }
1510b57cec5SDimitry Andric 
1520b57cec5SDimitry Andric EditLineStringType FixIndentation(const EditLineStringType &line,
1530b57cec5SDimitry Andric                                   int indent_correction) {
1540b57cec5SDimitry Andric   if (indent_correction == 0)
1550b57cec5SDimitry Andric     return line;
1560b57cec5SDimitry Andric   if (indent_correction < 0)
1570b57cec5SDimitry Andric     return line.substr(-indent_correction);
1580b57cec5SDimitry Andric   return EditLineStringType(indent_correction, EditLineCharType(' ')) + line;
1590b57cec5SDimitry Andric }
1600b57cec5SDimitry Andric 
1610b57cec5SDimitry Andric int GetIndentation(const EditLineStringType &line) {
1620b57cec5SDimitry Andric   int space_count = 0;
1630b57cec5SDimitry Andric   for (EditLineCharType ch : line) {
1640b57cec5SDimitry Andric     if (ch != EditLineCharType(' '))
1650b57cec5SDimitry Andric       break;
1660b57cec5SDimitry Andric     ++space_count;
1670b57cec5SDimitry Andric   }
1680b57cec5SDimitry Andric   return space_count;
1690b57cec5SDimitry Andric }
1700b57cec5SDimitry Andric 
1710b57cec5SDimitry Andric bool IsInputPending(FILE *file) {
1720b57cec5SDimitry Andric   // FIXME: This will be broken on Windows if we ever re-enable Editline.  You
1730b57cec5SDimitry Andric   // can't use select
1740b57cec5SDimitry Andric   // on something that isn't a socket.  This will have to be re-written to not
1750b57cec5SDimitry Andric   // use a FILE*, but instead use some kind of yet-to-be-created abstraction
1760b57cec5SDimitry Andric   // that select-like functionality on non-socket objects.
1770b57cec5SDimitry Andric   const int fd = fileno(file);
1780b57cec5SDimitry Andric   SelectHelper select_helper;
1790b57cec5SDimitry Andric   select_helper.SetTimeout(std::chrono::microseconds(0));
1800b57cec5SDimitry Andric   select_helper.FDSetRead(fd);
1810b57cec5SDimitry Andric   return select_helper.Select().Success();
1820b57cec5SDimitry Andric }
1830b57cec5SDimitry Andric 
1840b57cec5SDimitry Andric namespace lldb_private {
1850b57cec5SDimitry Andric namespace line_editor {
1860b57cec5SDimitry Andric typedef std::weak_ptr<EditlineHistory> EditlineHistoryWP;
1870b57cec5SDimitry Andric 
1880b57cec5SDimitry Andric // EditlineHistory objects are sometimes shared between multiple Editline
1890b57cec5SDimitry Andric // instances with the same program name.
1900b57cec5SDimitry Andric 
1910b57cec5SDimitry Andric class EditlineHistory {
1920b57cec5SDimitry Andric private:
1930b57cec5SDimitry Andric   // Use static GetHistory() function to get a EditlineHistorySP to one of
1940b57cec5SDimitry Andric   // these objects
1950b57cec5SDimitry Andric   EditlineHistory(const std::string &prefix, uint32_t size, bool unique_entries)
196349cc55cSDimitry Andric       : m_prefix(prefix) {
1970b57cec5SDimitry Andric     m_history = history_winit();
1980b57cec5SDimitry Andric     history_w(m_history, &m_event, H_SETSIZE, size);
1990b57cec5SDimitry Andric     if (unique_entries)
2000b57cec5SDimitry Andric       history_w(m_history, &m_event, H_SETUNIQUE, 1);
2010b57cec5SDimitry Andric   }
2020b57cec5SDimitry Andric 
2030b57cec5SDimitry Andric   const char *GetHistoryFilePath() {
2040b57cec5SDimitry Andric     // Compute the history path lazily.
2050b57cec5SDimitry Andric     if (m_path.empty() && m_history && !m_prefix.empty()) {
2060b57cec5SDimitry Andric       llvm::SmallString<128> lldb_history_file;
207e8d8bef9SDimitry Andric       FileSystem::Instance().GetHomeDirectory(lldb_history_file);
2080b57cec5SDimitry Andric       llvm::sys::path::append(lldb_history_file, ".lldb");
2090b57cec5SDimitry Andric 
2100b57cec5SDimitry Andric       // LLDB stores its history in ~/.lldb/. If for some reason this directory
2110b57cec5SDimitry Andric       // isn't writable or cannot be created, history won't be available.
2120b57cec5SDimitry Andric       if (!llvm::sys::fs::create_directory(lldb_history_file)) {
2130b57cec5SDimitry Andric #if LLDB_EDITLINE_USE_WCHAR
2140b57cec5SDimitry Andric         std::string filename = m_prefix + "-widehistory";
2150b57cec5SDimitry Andric #else
2160b57cec5SDimitry Andric         std::string filename = m_prefix + "-history";
2170b57cec5SDimitry Andric #endif
2180b57cec5SDimitry Andric         llvm::sys::path::append(lldb_history_file, filename);
2195ffd83dbSDimitry Andric         m_path = std::string(lldb_history_file.str());
2200b57cec5SDimitry Andric       }
2210b57cec5SDimitry Andric     }
2220b57cec5SDimitry Andric 
2230b57cec5SDimitry Andric     if (m_path.empty())
2240b57cec5SDimitry Andric       return nullptr;
2250b57cec5SDimitry Andric 
2260b57cec5SDimitry Andric     return m_path.c_str();
2270b57cec5SDimitry Andric   }
2280b57cec5SDimitry Andric 
2290b57cec5SDimitry Andric public:
2300b57cec5SDimitry Andric   ~EditlineHistory() {
2310b57cec5SDimitry Andric     Save();
2320b57cec5SDimitry Andric 
2330b57cec5SDimitry Andric     if (m_history) {
2340b57cec5SDimitry Andric       history_wend(m_history);
2350b57cec5SDimitry Andric       m_history = nullptr;
2360b57cec5SDimitry Andric     }
2370b57cec5SDimitry Andric   }
2380b57cec5SDimitry Andric 
2390b57cec5SDimitry Andric   static EditlineHistorySP GetHistory(const std::string &prefix) {
2400b57cec5SDimitry Andric     typedef std::map<std::string, EditlineHistoryWP> WeakHistoryMap;
2410b57cec5SDimitry Andric     static std::recursive_mutex g_mutex;
2420b57cec5SDimitry Andric     static WeakHistoryMap g_weak_map;
2430b57cec5SDimitry Andric     std::lock_guard<std::recursive_mutex> guard(g_mutex);
2440b57cec5SDimitry Andric     WeakHistoryMap::const_iterator pos = g_weak_map.find(prefix);
2450b57cec5SDimitry Andric     EditlineHistorySP history_sp;
2460b57cec5SDimitry Andric     if (pos != g_weak_map.end()) {
2470b57cec5SDimitry Andric       history_sp = pos->second.lock();
2480b57cec5SDimitry Andric       if (history_sp)
2490b57cec5SDimitry Andric         return history_sp;
2500b57cec5SDimitry Andric       g_weak_map.erase(pos);
2510b57cec5SDimitry Andric     }
2520b57cec5SDimitry Andric     history_sp.reset(new EditlineHistory(prefix, 800, true));
2530b57cec5SDimitry Andric     g_weak_map[prefix] = history_sp;
2540b57cec5SDimitry Andric     return history_sp;
2550b57cec5SDimitry Andric   }
2560b57cec5SDimitry Andric 
2570b57cec5SDimitry Andric   bool IsValid() const { return m_history != nullptr; }
2580b57cec5SDimitry Andric 
2590b57cec5SDimitry Andric   HistoryW *GetHistoryPtr() { return m_history; }
2600b57cec5SDimitry Andric 
2610b57cec5SDimitry Andric   void Enter(const EditLineCharType *line_cstr) {
2620b57cec5SDimitry Andric     if (m_history)
2630b57cec5SDimitry Andric       history_w(m_history, &m_event, H_ENTER, line_cstr);
2640b57cec5SDimitry Andric   }
2650b57cec5SDimitry Andric 
2660b57cec5SDimitry Andric   bool Load() {
2670b57cec5SDimitry Andric     if (m_history) {
2680b57cec5SDimitry Andric       const char *path = GetHistoryFilePath();
2690b57cec5SDimitry Andric       if (path) {
2700b57cec5SDimitry Andric         history_w(m_history, &m_event, H_LOAD, path);
2710b57cec5SDimitry Andric         return true;
2720b57cec5SDimitry Andric       }
2730b57cec5SDimitry Andric     }
2740b57cec5SDimitry Andric     return false;
2750b57cec5SDimitry Andric   }
2760b57cec5SDimitry Andric 
2770b57cec5SDimitry Andric   bool Save() {
2780b57cec5SDimitry Andric     if (m_history) {
2790b57cec5SDimitry Andric       const char *path = GetHistoryFilePath();
2800b57cec5SDimitry Andric       if (path) {
2810b57cec5SDimitry Andric         history_w(m_history, &m_event, H_SAVE, path);
2820b57cec5SDimitry Andric         return true;
2830b57cec5SDimitry Andric       }
2840b57cec5SDimitry Andric     }
2850b57cec5SDimitry Andric     return false;
2860b57cec5SDimitry Andric   }
2870b57cec5SDimitry Andric 
2880b57cec5SDimitry Andric protected:
289349cc55cSDimitry Andric   /// The history object.
290349cc55cSDimitry Andric   HistoryW *m_history = nullptr;
291349cc55cSDimitry Andric   /// The history event needed to contain all history events.
292349cc55cSDimitry Andric   HistEventW m_event;
293349cc55cSDimitry Andric   /// The prefix name (usually the editline program name) to use when
294349cc55cSDimitry Andric   /// loading/saving history.
295349cc55cSDimitry Andric   std::string m_prefix;
296349cc55cSDimitry Andric   /// Path to the history file.
297349cc55cSDimitry Andric   std::string m_path;
2980b57cec5SDimitry Andric };
2990b57cec5SDimitry Andric }
3000b57cec5SDimitry Andric }
3010b57cec5SDimitry Andric 
3020b57cec5SDimitry Andric // Editline private methods
3030b57cec5SDimitry Andric 
3040b57cec5SDimitry Andric void Editline::SetBaseLineNumber(int line_number) {
3050b57cec5SDimitry Andric   m_base_line_number = line_number;
3060b57cec5SDimitry Andric   m_line_number_digits =
3075ffd83dbSDimitry Andric       std::max<int>(3, std::to_string(line_number).length() + 1);
3080b57cec5SDimitry Andric }
3090b57cec5SDimitry Andric 
3100b57cec5SDimitry Andric std::string Editline::PromptForIndex(int line_index) {
3110b57cec5SDimitry Andric   bool use_line_numbers = m_multiline_enabled && m_base_line_number > 0;
3120b57cec5SDimitry Andric   std::string prompt = m_set_prompt;
3135ffd83dbSDimitry Andric   if (use_line_numbers && prompt.length() == 0)
3140b57cec5SDimitry Andric     prompt = ": ";
3150b57cec5SDimitry Andric   std::string continuation_prompt = prompt;
3160b57cec5SDimitry Andric   if (m_set_continuation_prompt.length() > 0) {
3170b57cec5SDimitry Andric     continuation_prompt = m_set_continuation_prompt;
3180b57cec5SDimitry Andric     // Ensure that both prompts are the same length through space padding
3195f757f3fSDimitry Andric     const size_t prompt_width = ColumnWidth(prompt);
3205f757f3fSDimitry Andric     const size_t cont_prompt_width = ColumnWidth(continuation_prompt);
3215f757f3fSDimitry Andric     const size_t padded_prompt_width =
3225f757f3fSDimitry Andric         std::max(prompt_width, cont_prompt_width);
3235f757f3fSDimitry Andric     if (prompt_width < padded_prompt_width)
3245f757f3fSDimitry Andric       prompt += std::string(padded_prompt_width - prompt_width, ' ');
3255f757f3fSDimitry Andric     else if (cont_prompt_width < padded_prompt_width)
3265f757f3fSDimitry Andric       continuation_prompt +=
3275f757f3fSDimitry Andric           std::string(padded_prompt_width - cont_prompt_width, ' ');
3280b57cec5SDimitry Andric   }
3290b57cec5SDimitry Andric 
3300b57cec5SDimitry Andric   if (use_line_numbers) {
3310b57cec5SDimitry Andric     StreamString prompt_stream;
3320b57cec5SDimitry Andric     prompt_stream.Printf(
3330b57cec5SDimitry Andric         "%*d%s", m_line_number_digits, m_base_line_number + line_index,
3340b57cec5SDimitry Andric         (line_index == 0) ? prompt.c_str() : continuation_prompt.c_str());
3355ffd83dbSDimitry Andric     return std::string(std::move(prompt_stream.GetString()));
3360b57cec5SDimitry Andric   }
3370b57cec5SDimitry Andric   return (line_index == 0) ? prompt : continuation_prompt;
3380b57cec5SDimitry Andric }
3390b57cec5SDimitry Andric 
3400b57cec5SDimitry Andric void Editline::SetCurrentLine(int line_index) {
3410b57cec5SDimitry Andric   m_current_line_index = line_index;
3420b57cec5SDimitry Andric   m_current_prompt = PromptForIndex(line_index);
3430b57cec5SDimitry Andric }
3440b57cec5SDimitry Andric 
3455f757f3fSDimitry Andric size_t Editline::GetPromptWidth() { return ColumnWidth(PromptForIndex(0)); }
3460b57cec5SDimitry Andric 
3470b57cec5SDimitry Andric bool Editline::IsEmacs() {
3480b57cec5SDimitry Andric   const char *editor;
3490b57cec5SDimitry Andric   el_get(m_editline, EL_EDITOR, &editor);
3500b57cec5SDimitry Andric   return editor[0] == 'e';
3510b57cec5SDimitry Andric }
3520b57cec5SDimitry Andric 
3530b57cec5SDimitry Andric bool Editline::IsOnlySpaces() {
3540b57cec5SDimitry Andric   const LineInfoW *info = el_wline(m_editline);
3550b57cec5SDimitry Andric   for (const EditLineCharType *character = info->buffer;
3560b57cec5SDimitry Andric        character < info->lastchar; character++) {
3570b57cec5SDimitry Andric     if (*character != ' ')
3580b57cec5SDimitry Andric       return false;
3590b57cec5SDimitry Andric   }
3600b57cec5SDimitry Andric   return true;
3610b57cec5SDimitry Andric }
3620b57cec5SDimitry Andric 
3630b57cec5SDimitry Andric int Editline::GetLineIndexForLocation(CursorLocation location, int cursor_row) {
3640b57cec5SDimitry Andric   int line = 0;
3650b57cec5SDimitry Andric   if (location == CursorLocation::EditingPrompt ||
3660b57cec5SDimitry Andric       location == CursorLocation::BlockEnd ||
3670b57cec5SDimitry Andric       location == CursorLocation::EditingCursor) {
3680b57cec5SDimitry Andric     for (unsigned index = 0; index < m_current_line_index; index++) {
3690b57cec5SDimitry Andric       line += CountRowsForLine(m_input_lines[index]);
3700b57cec5SDimitry Andric     }
3710b57cec5SDimitry Andric     if (location == CursorLocation::EditingCursor) {
3720b57cec5SDimitry Andric       line += cursor_row;
3730b57cec5SDimitry Andric     } else if (location == CursorLocation::BlockEnd) {
3740b57cec5SDimitry Andric       for (unsigned index = m_current_line_index; index < m_input_lines.size();
3750b57cec5SDimitry Andric            index++) {
3760b57cec5SDimitry Andric         line += CountRowsForLine(m_input_lines[index]);
3770b57cec5SDimitry Andric       }
3780b57cec5SDimitry Andric       --line;
3790b57cec5SDimitry Andric     }
3800b57cec5SDimitry Andric   }
3810b57cec5SDimitry Andric   return line;
3820b57cec5SDimitry Andric }
3830b57cec5SDimitry Andric 
3840b57cec5SDimitry Andric void Editline::MoveCursor(CursorLocation from, CursorLocation to) {
3850b57cec5SDimitry Andric   const LineInfoW *info = el_wline(m_editline);
3860b57cec5SDimitry Andric   int editline_cursor_position =
3870b57cec5SDimitry Andric       (int)((info->cursor - info->buffer) + GetPromptWidth());
3880b57cec5SDimitry Andric   int editline_cursor_row = editline_cursor_position / m_terminal_width;
3890b57cec5SDimitry Andric 
3900b57cec5SDimitry Andric   // Determine relative starting and ending lines
3910b57cec5SDimitry Andric   int fromLine = GetLineIndexForLocation(from, editline_cursor_row);
3920b57cec5SDimitry Andric   int toLine = GetLineIndexForLocation(to, editline_cursor_row);
3930b57cec5SDimitry Andric   if (toLine != fromLine) {
3940b57cec5SDimitry Andric     fprintf(m_output_file,
3950b57cec5SDimitry Andric             (toLine > fromLine) ? ANSI_DOWN_N_ROWS : ANSI_UP_N_ROWS,
3960b57cec5SDimitry Andric             std::abs(toLine - fromLine));
3970b57cec5SDimitry Andric   }
3980b57cec5SDimitry Andric 
3990b57cec5SDimitry Andric   // Determine target column
4000b57cec5SDimitry Andric   int toColumn = 1;
4010b57cec5SDimitry Andric   if (to == CursorLocation::EditingCursor) {
4020b57cec5SDimitry Andric     toColumn =
4030b57cec5SDimitry Andric         editline_cursor_position - (editline_cursor_row * m_terminal_width) + 1;
4040b57cec5SDimitry Andric   } else if (to == CursorLocation::BlockEnd && !m_input_lines.empty()) {
4050b57cec5SDimitry Andric     toColumn =
4060b57cec5SDimitry Andric         ((m_input_lines[m_input_lines.size() - 1].length() + GetPromptWidth()) %
4070b57cec5SDimitry Andric          80) +
4080b57cec5SDimitry Andric         1;
4090b57cec5SDimitry Andric   }
4100b57cec5SDimitry Andric   fprintf(m_output_file, ANSI_SET_COLUMN_N, toColumn);
4110b57cec5SDimitry Andric }
4120b57cec5SDimitry Andric 
4130b57cec5SDimitry Andric void Editline::DisplayInput(int firstIndex) {
4140b57cec5SDimitry Andric   fprintf(m_output_file, ANSI_SET_COLUMN_N ANSI_CLEAR_BELOW, 1);
4150b57cec5SDimitry Andric   int line_count = (int)m_input_lines.size();
4160b57cec5SDimitry Andric   for (int index = firstIndex; index < line_count; index++) {
4175f757f3fSDimitry Andric     fprintf(m_output_file,
4185f757f3fSDimitry Andric             "%s"
4190b57cec5SDimitry Andric             "%s"
4200b57cec5SDimitry Andric             "%s" EditLineStringFormatSpec " ",
4215f757f3fSDimitry Andric             m_prompt_ansi_prefix.c_str(), PromptForIndex(index).c_str(),
4225f757f3fSDimitry Andric             m_prompt_ansi_suffix.c_str(), m_input_lines[index].c_str());
4230b57cec5SDimitry Andric     if (index < line_count - 1)
4240b57cec5SDimitry Andric       fprintf(m_output_file, "\n");
4250b57cec5SDimitry Andric   }
4260b57cec5SDimitry Andric }
4270b57cec5SDimitry Andric 
4280b57cec5SDimitry Andric int Editline::CountRowsForLine(const EditLineStringType &content) {
4295ffd83dbSDimitry Andric   std::string prompt =
4300b57cec5SDimitry Andric       PromptForIndex(0); // Prompt width is constant during an edit session
4315f757f3fSDimitry Andric   int line_length = (int)(content.length() + ColumnWidth(prompt));
4320b57cec5SDimitry Andric   return (line_length / m_terminal_width) + 1;
4330b57cec5SDimitry Andric }
4340b57cec5SDimitry Andric 
4350b57cec5SDimitry Andric void Editline::SaveEditedLine() {
4360b57cec5SDimitry Andric   const LineInfoW *info = el_wline(m_editline);
4370b57cec5SDimitry Andric   m_input_lines[m_current_line_index] =
4380b57cec5SDimitry Andric       EditLineStringType(info->buffer, info->lastchar - info->buffer);
4390b57cec5SDimitry Andric }
4400b57cec5SDimitry Andric 
4410b57cec5SDimitry Andric StringList Editline::GetInputAsStringList(int line_count) {
4420b57cec5SDimitry Andric   StringList lines;
4430b57cec5SDimitry Andric   for (EditLineStringType line : m_input_lines) {
4440b57cec5SDimitry Andric     if (line_count == 0)
4450b57cec5SDimitry Andric       break;
4460b57cec5SDimitry Andric #if LLDB_EDITLINE_USE_WCHAR
4470b57cec5SDimitry Andric     lines.AppendString(m_utf8conv.to_bytes(line));
4480b57cec5SDimitry Andric #else
4490b57cec5SDimitry Andric     lines.AppendString(line);
4500b57cec5SDimitry Andric #endif
4510b57cec5SDimitry Andric     --line_count;
4520b57cec5SDimitry Andric   }
4530b57cec5SDimitry Andric   return lines;
4540b57cec5SDimitry Andric }
4550b57cec5SDimitry Andric 
456480093f4SDimitry Andric unsigned char Editline::RecallHistory(HistoryOperation op) {
457480093f4SDimitry Andric   assert(op == HistoryOperation::Older || op == HistoryOperation::Newer);
4580b57cec5SDimitry Andric   if (!m_history_sp || !m_history_sp->IsValid())
4590b57cec5SDimitry Andric     return CC_ERROR;
4600b57cec5SDimitry Andric 
4610b57cec5SDimitry Andric   HistoryW *pHistory = m_history_sp->GetHistoryPtr();
4620b57cec5SDimitry Andric   HistEventW history_event;
4630b57cec5SDimitry Andric   std::vector<EditLineStringType> new_input_lines;
4640b57cec5SDimitry Andric 
4650b57cec5SDimitry Andric   // Treat moving from the "live" entry differently
4660b57cec5SDimitry Andric   if (!m_in_history) {
467480093f4SDimitry Andric     switch (op) {
468480093f4SDimitry Andric     case HistoryOperation::Newer:
4690b57cec5SDimitry Andric       return CC_ERROR; // Can't go newer than the "live" entry
470480093f4SDimitry Andric     case HistoryOperation::Older: {
471480093f4SDimitry Andric       if (history_w(pHistory, &history_event,
472480093f4SDimitry Andric                     GetOperation(HistoryOperation::Newest)) == -1)
4730b57cec5SDimitry Andric         return CC_ERROR;
4740b57cec5SDimitry Andric       // Save any edits to the "live" entry in case we return by moving forward
4750b57cec5SDimitry Andric       // in history (it would be more bash-like to save over any current entry,
476480093f4SDimitry Andric       // but libedit doesn't offer the ability to add entries anywhere except
477480093f4SDimitry Andric       // the end.)
4780b57cec5SDimitry Andric       SaveEditedLine();
4790b57cec5SDimitry Andric       m_live_history_lines = m_input_lines;
4800b57cec5SDimitry Andric       m_in_history = true;
481480093f4SDimitry Andric     } break;
482480093f4SDimitry Andric     default:
483480093f4SDimitry Andric       llvm_unreachable("unsupported history direction");
484480093f4SDimitry Andric     }
4850b57cec5SDimitry Andric   } else {
486480093f4SDimitry Andric     if (history_w(pHistory, &history_event, GetOperation(op)) == -1) {
487480093f4SDimitry Andric       switch (op) {
488480093f4SDimitry Andric       case HistoryOperation::Older:
489480093f4SDimitry Andric         // Can't move earlier than the earliest entry.
4900b57cec5SDimitry Andric         return CC_ERROR;
491480093f4SDimitry Andric       case HistoryOperation::Newer:
492480093f4SDimitry Andric         // Moving to newer-than-the-newest entry yields the "live" entry.
4930b57cec5SDimitry Andric         new_input_lines = m_live_history_lines;
4940b57cec5SDimitry Andric         m_in_history = false;
495480093f4SDimitry Andric         break;
496480093f4SDimitry Andric       default:
497480093f4SDimitry Andric         llvm_unreachable("unsupported history direction");
498480093f4SDimitry Andric       }
4990b57cec5SDimitry Andric     }
5000b57cec5SDimitry Andric   }
5010b57cec5SDimitry Andric 
5020b57cec5SDimitry Andric   // If we're pulling the lines from history, split them apart
5030b57cec5SDimitry Andric   if (m_in_history)
5040b57cec5SDimitry Andric     new_input_lines = SplitLines(history_event.str);
5050b57cec5SDimitry Andric 
5060b57cec5SDimitry Andric   // Erase the current edit session and replace it with a new one
5070b57cec5SDimitry Andric   MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
5080b57cec5SDimitry Andric   m_input_lines = new_input_lines;
5090b57cec5SDimitry Andric   DisplayInput();
5100b57cec5SDimitry Andric 
5110b57cec5SDimitry Andric   // Prepare to edit the last line when moving to previous entry, or the first
5120b57cec5SDimitry Andric   // line when moving to next entry
513480093f4SDimitry Andric   switch (op) {
514480093f4SDimitry Andric   case HistoryOperation::Older:
515480093f4SDimitry Andric     m_current_line_index = (int)m_input_lines.size() - 1;
516480093f4SDimitry Andric     break;
517480093f4SDimitry Andric   case HistoryOperation::Newer:
518480093f4SDimitry Andric     m_current_line_index = 0;
519480093f4SDimitry Andric     break;
520480093f4SDimitry Andric   default:
521480093f4SDimitry Andric     llvm_unreachable("unsupported history direction");
522480093f4SDimitry Andric   }
523480093f4SDimitry Andric   SetCurrentLine(m_current_line_index);
5240b57cec5SDimitry Andric   MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
5250b57cec5SDimitry Andric   return CC_NEWLINE;
5260b57cec5SDimitry Andric }
5270b57cec5SDimitry Andric 
5280b57cec5SDimitry Andric int Editline::GetCharacter(EditLineGetCharType *c) {
5290b57cec5SDimitry Andric   const LineInfoW *info = el_wline(m_editline);
5300b57cec5SDimitry Andric 
5315f757f3fSDimitry Andric   // Paint a ANSI formatted version of the desired prompt over the version
5325f757f3fSDimitry Andric   // libedit draws. (will only be requested if colors are supported)
5330b57cec5SDimitry Andric   if (m_needs_prompt_repaint) {
5340b57cec5SDimitry Andric     MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
5355f757f3fSDimitry Andric     fprintf(m_output_file,
5365f757f3fSDimitry Andric             "%s"
5370b57cec5SDimitry Andric             "%s"
5380b57cec5SDimitry Andric             "%s",
5395f757f3fSDimitry Andric             m_prompt_ansi_prefix.c_str(), Prompt(),
5405f757f3fSDimitry Andric             m_prompt_ansi_suffix.c_str());
5410b57cec5SDimitry Andric     MoveCursor(CursorLocation::EditingPrompt, CursorLocation::EditingCursor);
5420b57cec5SDimitry Andric     m_needs_prompt_repaint = false;
5430b57cec5SDimitry Andric   }
5440b57cec5SDimitry Andric 
5450b57cec5SDimitry Andric   if (m_multiline_enabled) {
5460b57cec5SDimitry Andric     // Detect when the number of rows used for this input line changes due to
5470b57cec5SDimitry Andric     // an edit
5480b57cec5SDimitry Andric     int lineLength = (int)((info->lastchar - info->buffer) + GetPromptWidth());
5490b57cec5SDimitry Andric     int new_line_rows = (lineLength / m_terminal_width) + 1;
5500b57cec5SDimitry Andric     if (m_current_line_rows != -1 && new_line_rows != m_current_line_rows) {
5510b57cec5SDimitry Andric       // Respond by repainting the current state from this line on
5520b57cec5SDimitry Andric       MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
5530b57cec5SDimitry Andric       SaveEditedLine();
5540b57cec5SDimitry Andric       DisplayInput(m_current_line_index);
5550b57cec5SDimitry Andric       MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
5560b57cec5SDimitry Andric     }
5570b57cec5SDimitry Andric     m_current_line_rows = new_line_rows;
5580b57cec5SDimitry Andric   }
5590b57cec5SDimitry Andric 
5600b57cec5SDimitry Andric   // Read an actual character
5610b57cec5SDimitry Andric   while (true) {
5620b57cec5SDimitry Andric     lldb::ConnectionStatus status = lldb::eConnectionStatusSuccess;
5630b57cec5SDimitry Andric     char ch = 0;
5640b57cec5SDimitry Andric 
5655ffd83dbSDimitry Andric     if (m_terminal_size_has_changed)
5665ffd83dbSDimitry Andric       ApplyTerminalSizeChange();
5675ffd83dbSDimitry Andric 
5680b57cec5SDimitry Andric     // This mutex is locked by our caller (GetLine). Unlock it while we read a
5690b57cec5SDimitry Andric     // character (blocking operation), so we do not hold the mutex
5700b57cec5SDimitry Andric     // indefinitely. This gives a chance for someone to interrupt us. After
5710b57cec5SDimitry Andric     // Read returns, immediately lock the mutex again and check if we were
5720b57cec5SDimitry Andric     // interrupted.
5730b57cec5SDimitry Andric     m_output_mutex.unlock();
5740b57cec5SDimitry Andric     int read_count =
575bdd1243dSDimitry Andric         m_input_connection.Read(&ch, 1, std::nullopt, status, nullptr);
5760b57cec5SDimitry Andric     m_output_mutex.lock();
5770b57cec5SDimitry Andric     if (m_editor_status == EditorStatus::Interrupted) {
5780b57cec5SDimitry Andric       while (read_count > 0 && status == lldb::eConnectionStatusSuccess)
5790b57cec5SDimitry Andric         read_count =
580bdd1243dSDimitry Andric             m_input_connection.Read(&ch, 1, std::nullopt, status, nullptr);
5810b57cec5SDimitry Andric       lldbassert(status == lldb::eConnectionStatusInterrupted);
5820b57cec5SDimitry Andric       return 0;
5830b57cec5SDimitry Andric     }
5840b57cec5SDimitry Andric 
5850b57cec5SDimitry Andric     if (read_count) {
5860b57cec5SDimitry Andric       if (CompleteCharacter(ch, *c))
5870b57cec5SDimitry Andric         return 1;
5880b57cec5SDimitry Andric     } else {
5890b57cec5SDimitry Andric       switch (status) {
5900b57cec5SDimitry Andric       case lldb::eConnectionStatusSuccess: // Success
5910b57cec5SDimitry Andric         break;
5920b57cec5SDimitry Andric 
5930b57cec5SDimitry Andric       case lldb::eConnectionStatusInterrupted:
5940b57cec5SDimitry Andric         llvm_unreachable("Interrupts should have been handled above.");
5950b57cec5SDimitry Andric 
5960b57cec5SDimitry Andric       case lldb::eConnectionStatusError:        // Check GetError() for details
5970b57cec5SDimitry Andric       case lldb::eConnectionStatusTimedOut:     // Request timed out
5980b57cec5SDimitry Andric       case lldb::eConnectionStatusEndOfFile:    // End-of-file encountered
5990b57cec5SDimitry Andric       case lldb::eConnectionStatusNoConnection: // No connection
6000b57cec5SDimitry Andric       case lldb::eConnectionStatusLostConnection: // Lost connection while
6010b57cec5SDimitry Andric                                                   // connected to a valid
6020b57cec5SDimitry Andric                                                   // connection
6030b57cec5SDimitry Andric         m_editor_status = EditorStatus::EndOfInput;
6040b57cec5SDimitry Andric         return 0;
6050b57cec5SDimitry Andric       }
6060b57cec5SDimitry Andric     }
6070b57cec5SDimitry Andric   }
6080b57cec5SDimitry Andric }
6090b57cec5SDimitry Andric 
6100b57cec5SDimitry Andric const char *Editline::Prompt() {
6115f757f3fSDimitry Andric   if (!m_prompt_ansi_prefix.empty() || !m_prompt_ansi_suffix.empty())
6120b57cec5SDimitry Andric     m_needs_prompt_repaint = true;
6130b57cec5SDimitry Andric   return m_current_prompt.c_str();
6140b57cec5SDimitry Andric }
6150b57cec5SDimitry Andric 
6160b57cec5SDimitry Andric unsigned char Editline::BreakLineCommand(int ch) {
6170b57cec5SDimitry Andric   // Preserve any content beyond the cursor, truncate and save the current line
6180b57cec5SDimitry Andric   const LineInfoW *info = el_wline(m_editline);
6190b57cec5SDimitry Andric   auto current_line =
6200b57cec5SDimitry Andric       EditLineStringType(info->buffer, info->cursor - info->buffer);
6210b57cec5SDimitry Andric   auto new_line_fragment =
6220b57cec5SDimitry Andric       EditLineStringType(info->cursor, info->lastchar - info->cursor);
6230b57cec5SDimitry Andric   m_input_lines[m_current_line_index] = current_line;
6240b57cec5SDimitry Andric 
6250b57cec5SDimitry Andric   // Ignore whitespace-only extra fragments when breaking a line
6260b57cec5SDimitry Andric   if (::IsOnlySpaces(new_line_fragment))
6270b57cec5SDimitry Andric     new_line_fragment = EditLineConstString("");
6280b57cec5SDimitry Andric 
6290b57cec5SDimitry Andric   // Establish the new cursor position at the start of a line when inserting a
6300b57cec5SDimitry Andric   // line break
6310b57cec5SDimitry Andric   m_revert_cursor_index = 0;
6320b57cec5SDimitry Andric 
6330b57cec5SDimitry Andric   // Don't perform automatic formatting when pasting
6340b57cec5SDimitry Andric   if (!IsInputPending(m_input_file)) {
6350b57cec5SDimitry Andric     // Apply smart indentation
6360b57cec5SDimitry Andric     if (m_fix_indentation_callback) {
6370b57cec5SDimitry Andric       StringList lines = GetInputAsStringList(m_current_line_index + 1);
6380b57cec5SDimitry Andric #if LLDB_EDITLINE_USE_WCHAR
6390b57cec5SDimitry Andric       lines.AppendString(m_utf8conv.to_bytes(new_line_fragment));
6400b57cec5SDimitry Andric #else
6410b57cec5SDimitry Andric       lines.AppendString(new_line_fragment);
6420b57cec5SDimitry Andric #endif
6430b57cec5SDimitry Andric 
644fe6060f1SDimitry Andric       int indent_correction = m_fix_indentation_callback(this, lines, 0);
6450b57cec5SDimitry Andric       new_line_fragment = FixIndentation(new_line_fragment, indent_correction);
6460b57cec5SDimitry Andric       m_revert_cursor_index = GetIndentation(new_line_fragment);
6470b57cec5SDimitry Andric     }
6480b57cec5SDimitry Andric   }
6490b57cec5SDimitry Andric 
6500b57cec5SDimitry Andric   // Insert the new line and repaint everything from the split line on down
6510b57cec5SDimitry Andric   m_input_lines.insert(m_input_lines.begin() + m_current_line_index + 1,
6520b57cec5SDimitry Andric                        new_line_fragment);
6530b57cec5SDimitry Andric   MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
6540b57cec5SDimitry Andric   DisplayInput(m_current_line_index);
6550b57cec5SDimitry Andric 
6560b57cec5SDimitry Andric   // Reposition the cursor to the right line and prepare to edit the new line
6570b57cec5SDimitry Andric   SetCurrentLine(m_current_line_index + 1);
6580b57cec5SDimitry Andric   MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
6590b57cec5SDimitry Andric   return CC_NEWLINE;
6600b57cec5SDimitry Andric }
6610b57cec5SDimitry Andric 
6620b57cec5SDimitry Andric unsigned char Editline::EndOrAddLineCommand(int ch) {
6630b57cec5SDimitry Andric   // Don't perform end of input detection when pasting, always treat this as a
6640b57cec5SDimitry Andric   // line break
6650b57cec5SDimitry Andric   if (IsInputPending(m_input_file)) {
6660b57cec5SDimitry Andric     return BreakLineCommand(ch);
6670b57cec5SDimitry Andric   }
6680b57cec5SDimitry Andric 
6690b57cec5SDimitry Andric   // Save any edits to this line
6700b57cec5SDimitry Andric   SaveEditedLine();
6710b57cec5SDimitry Andric 
6720b57cec5SDimitry Andric   // If this is the end of the last line, consider whether to add a line
6730b57cec5SDimitry Andric   // instead
6740b57cec5SDimitry Andric   const LineInfoW *info = el_wline(m_editline);
6750b57cec5SDimitry Andric   if (m_current_line_index == m_input_lines.size() - 1 &&
6760b57cec5SDimitry Andric       info->cursor == info->lastchar) {
6770b57cec5SDimitry Andric     if (m_is_input_complete_callback) {
6780b57cec5SDimitry Andric       auto lines = GetInputAsStringList();
679fe6060f1SDimitry Andric       if (!m_is_input_complete_callback(this, lines)) {
6800b57cec5SDimitry Andric         return BreakLineCommand(ch);
6810b57cec5SDimitry Andric       }
6820b57cec5SDimitry Andric 
6830b57cec5SDimitry Andric       // The completion test is allowed to change the input lines when complete
6840b57cec5SDimitry Andric       m_input_lines.clear();
6850b57cec5SDimitry Andric       for (unsigned index = 0; index < lines.GetSize(); index++) {
6860b57cec5SDimitry Andric #if LLDB_EDITLINE_USE_WCHAR
6870b57cec5SDimitry Andric         m_input_lines.insert(m_input_lines.end(),
6880b57cec5SDimitry Andric                              m_utf8conv.from_bytes(lines[index]));
6890b57cec5SDimitry Andric #else
6900b57cec5SDimitry Andric         m_input_lines.insert(m_input_lines.end(), lines[index]);
6910b57cec5SDimitry Andric #endif
6920b57cec5SDimitry Andric       }
6930b57cec5SDimitry Andric     }
6940b57cec5SDimitry Andric   }
6950b57cec5SDimitry Andric   MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockEnd);
6960b57cec5SDimitry Andric   fprintf(m_output_file, "\n");
6970b57cec5SDimitry Andric   m_editor_status = EditorStatus::Complete;
6980b57cec5SDimitry Andric   return CC_NEWLINE;
6990b57cec5SDimitry Andric }
7000b57cec5SDimitry Andric 
7010b57cec5SDimitry Andric unsigned char Editline::DeleteNextCharCommand(int ch) {
7020b57cec5SDimitry Andric   LineInfoW *info = const_cast<LineInfoW *>(el_wline(m_editline));
7030b57cec5SDimitry Andric 
7040b57cec5SDimitry Andric   // Just delete the next character normally if possible
7050b57cec5SDimitry Andric   if (info->cursor < info->lastchar) {
7060b57cec5SDimitry Andric     info->cursor++;
7070b57cec5SDimitry Andric     el_deletestr(m_editline, 1);
7080b57cec5SDimitry Andric     return CC_REFRESH;
7090b57cec5SDimitry Andric   }
7100b57cec5SDimitry Andric 
7110b57cec5SDimitry Andric   // Fail when at the end of the last line, except when ^D is pressed on the
7120b57cec5SDimitry Andric   // line is empty, in which case it is treated as EOF
7130b57cec5SDimitry Andric   if (m_current_line_index == m_input_lines.size() - 1) {
7140b57cec5SDimitry Andric     if (ch == 4 && info->buffer == info->lastchar) {
7150b57cec5SDimitry Andric       fprintf(m_output_file, "^D\n");
7160b57cec5SDimitry Andric       m_editor_status = EditorStatus::EndOfInput;
7170b57cec5SDimitry Andric       return CC_EOF;
7180b57cec5SDimitry Andric     }
7190b57cec5SDimitry Andric     return CC_ERROR;
7200b57cec5SDimitry Andric   }
7210b57cec5SDimitry Andric 
7220b57cec5SDimitry Andric   // Prepare to combine this line with the one below
7230b57cec5SDimitry Andric   MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
7240b57cec5SDimitry Andric 
7250b57cec5SDimitry Andric   // Insert the next line of text at the cursor and restore the cursor position
7260b57cec5SDimitry Andric   const EditLineCharType *cursor = info->cursor;
7270b57cec5SDimitry Andric   el_winsertstr(m_editline, m_input_lines[m_current_line_index + 1].c_str());
7280b57cec5SDimitry Andric   info->cursor = cursor;
7290b57cec5SDimitry Andric   SaveEditedLine();
7300b57cec5SDimitry Andric 
7310b57cec5SDimitry Andric   // Delete the extra line
7320b57cec5SDimitry Andric   m_input_lines.erase(m_input_lines.begin() + m_current_line_index + 1);
7330b57cec5SDimitry Andric 
7340b57cec5SDimitry Andric   // Clear and repaint from this line on down
7350b57cec5SDimitry Andric   DisplayInput(m_current_line_index);
7360b57cec5SDimitry Andric   MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
7370b57cec5SDimitry Andric   return CC_REFRESH;
7380b57cec5SDimitry Andric }
7390b57cec5SDimitry Andric 
7400b57cec5SDimitry Andric unsigned char Editline::DeletePreviousCharCommand(int ch) {
7410b57cec5SDimitry Andric   LineInfoW *info = const_cast<LineInfoW *>(el_wline(m_editline));
7420b57cec5SDimitry Andric 
7430b57cec5SDimitry Andric   // Just delete the previous character normally when not at the start of a
7440b57cec5SDimitry Andric   // line
7450b57cec5SDimitry Andric   if (info->cursor > info->buffer) {
7460b57cec5SDimitry Andric     el_deletestr(m_editline, 1);
7470b57cec5SDimitry Andric     return CC_REFRESH;
7480b57cec5SDimitry Andric   }
7490b57cec5SDimitry Andric 
7500b57cec5SDimitry Andric   // No prior line and no prior character?  Let the user know
7510b57cec5SDimitry Andric   if (m_current_line_index == 0)
7520b57cec5SDimitry Andric     return CC_ERROR;
7530b57cec5SDimitry Andric 
7540b57cec5SDimitry Andric   // No prior character, but prior line?  Combine with the line above
7550b57cec5SDimitry Andric   SaveEditedLine();
7560b57cec5SDimitry Andric   SetCurrentLine(m_current_line_index - 1);
7570b57cec5SDimitry Andric   auto priorLine = m_input_lines[m_current_line_index];
7580b57cec5SDimitry Andric   m_input_lines.erase(m_input_lines.begin() + m_current_line_index);
7590b57cec5SDimitry Andric   m_input_lines[m_current_line_index] =
7600b57cec5SDimitry Andric       priorLine + m_input_lines[m_current_line_index];
7610b57cec5SDimitry Andric 
7620b57cec5SDimitry Andric   // Repaint from the new line down
7630b57cec5SDimitry Andric   fprintf(m_output_file, ANSI_UP_N_ROWS ANSI_SET_COLUMN_N,
7640b57cec5SDimitry Andric           CountRowsForLine(priorLine), 1);
7650b57cec5SDimitry Andric   DisplayInput(m_current_line_index);
7660b57cec5SDimitry Andric 
7670b57cec5SDimitry Andric   // Put the cursor back where libedit expects it to be before returning to
7680b57cec5SDimitry Andric   // editing by telling libedit about the newly inserted text
7690b57cec5SDimitry Andric   MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
7700b57cec5SDimitry Andric   el_winsertstr(m_editline, priorLine.c_str());
7710b57cec5SDimitry Andric   return CC_REDISPLAY;
7720b57cec5SDimitry Andric }
7730b57cec5SDimitry Andric 
7740b57cec5SDimitry Andric unsigned char Editline::PreviousLineCommand(int ch) {
7750b57cec5SDimitry Andric   SaveEditedLine();
7760b57cec5SDimitry Andric 
7770b57cec5SDimitry Andric   if (m_current_line_index == 0) {
778480093f4SDimitry Andric     return RecallHistory(HistoryOperation::Older);
7790b57cec5SDimitry Andric   }
7800b57cec5SDimitry Andric 
7810b57cec5SDimitry Andric   // Start from a known location
7820b57cec5SDimitry Andric   MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
7830b57cec5SDimitry Andric 
7840b57cec5SDimitry Andric   // Treat moving up from a blank last line as a deletion of that line
7850b57cec5SDimitry Andric   if (m_current_line_index == m_input_lines.size() - 1 && IsOnlySpaces()) {
7860b57cec5SDimitry Andric     m_input_lines.erase(m_input_lines.begin() + m_current_line_index);
7870b57cec5SDimitry Andric     fprintf(m_output_file, ANSI_CLEAR_BELOW);
7880b57cec5SDimitry Andric   }
7890b57cec5SDimitry Andric 
7900b57cec5SDimitry Andric   SetCurrentLine(m_current_line_index - 1);
7910b57cec5SDimitry Andric   fprintf(m_output_file, ANSI_UP_N_ROWS ANSI_SET_COLUMN_N,
7920b57cec5SDimitry Andric           CountRowsForLine(m_input_lines[m_current_line_index]), 1);
7930b57cec5SDimitry Andric   return CC_NEWLINE;
7940b57cec5SDimitry Andric }
7950b57cec5SDimitry Andric 
7960b57cec5SDimitry Andric unsigned char Editline::NextLineCommand(int ch) {
7970b57cec5SDimitry Andric   SaveEditedLine();
7980b57cec5SDimitry Andric 
7990b57cec5SDimitry Andric   // Handle attempts to move down from the last line
8000b57cec5SDimitry Andric   if (m_current_line_index == m_input_lines.size() - 1) {
8010b57cec5SDimitry Andric     // Don't add an extra line if the existing last line is blank, move through
8020b57cec5SDimitry Andric     // history instead
8030b57cec5SDimitry Andric     if (IsOnlySpaces()) {
804480093f4SDimitry Andric       return RecallHistory(HistoryOperation::Newer);
8050b57cec5SDimitry Andric     }
8060b57cec5SDimitry Andric 
8070b57cec5SDimitry Andric     // Determine indentation for the new line
8080b57cec5SDimitry Andric     int indentation = 0;
8090b57cec5SDimitry Andric     if (m_fix_indentation_callback) {
8100b57cec5SDimitry Andric       StringList lines = GetInputAsStringList();
8110b57cec5SDimitry Andric       lines.AppendString("");
812fe6060f1SDimitry Andric       indentation = m_fix_indentation_callback(this, lines, 0);
8130b57cec5SDimitry Andric     }
8140b57cec5SDimitry Andric     m_input_lines.insert(
8150b57cec5SDimitry Andric         m_input_lines.end(),
8160b57cec5SDimitry Andric         EditLineStringType(indentation, EditLineCharType(' ')));
8170b57cec5SDimitry Andric   }
8180b57cec5SDimitry Andric 
8190b57cec5SDimitry Andric   // Move down past the current line using newlines to force scrolling if
8200b57cec5SDimitry Andric   // needed
8210b57cec5SDimitry Andric   SetCurrentLine(m_current_line_index + 1);
8220b57cec5SDimitry Andric   const LineInfoW *info = el_wline(m_editline);
8230b57cec5SDimitry Andric   int cursor_position = (int)((info->cursor - info->buffer) + GetPromptWidth());
8240b57cec5SDimitry Andric   int cursor_row = cursor_position / m_terminal_width;
8250b57cec5SDimitry Andric   for (int line_count = 0; line_count < m_current_line_rows - cursor_row;
8260b57cec5SDimitry Andric        line_count++) {
8270b57cec5SDimitry Andric     fprintf(m_output_file, "\n");
8280b57cec5SDimitry Andric   }
8290b57cec5SDimitry Andric   return CC_NEWLINE;
8300b57cec5SDimitry Andric }
8310b57cec5SDimitry Andric 
8320b57cec5SDimitry Andric unsigned char Editline::PreviousHistoryCommand(int ch) {
8330b57cec5SDimitry Andric   SaveEditedLine();
8340b57cec5SDimitry Andric 
835480093f4SDimitry Andric   return RecallHistory(HistoryOperation::Older);
8360b57cec5SDimitry Andric }
8370b57cec5SDimitry Andric 
8380b57cec5SDimitry Andric unsigned char Editline::NextHistoryCommand(int ch) {
8390b57cec5SDimitry Andric   SaveEditedLine();
8400b57cec5SDimitry Andric 
841480093f4SDimitry Andric   return RecallHistory(HistoryOperation::Newer);
8420b57cec5SDimitry Andric }
8430b57cec5SDimitry Andric 
8440b57cec5SDimitry Andric unsigned char Editline::FixIndentationCommand(int ch) {
8450b57cec5SDimitry Andric   if (!m_fix_indentation_callback)
8460b57cec5SDimitry Andric     return CC_NORM;
8470b57cec5SDimitry Andric 
8480b57cec5SDimitry Andric   // Insert the character typed before proceeding
8490b57cec5SDimitry Andric   EditLineCharType inserted[] = {(EditLineCharType)ch, 0};
8500b57cec5SDimitry Andric   el_winsertstr(m_editline, inserted);
8510b57cec5SDimitry Andric   LineInfoW *info = const_cast<LineInfoW *>(el_wline(m_editline));
8520b57cec5SDimitry Andric   int cursor_position = info->cursor - info->buffer;
8530b57cec5SDimitry Andric 
8540b57cec5SDimitry Andric   // Save the edits and determine the correct indentation level
8550b57cec5SDimitry Andric   SaveEditedLine();
8560b57cec5SDimitry Andric   StringList lines = GetInputAsStringList(m_current_line_index + 1);
857fe6060f1SDimitry Andric   int indent_correction =
858fe6060f1SDimitry Andric       m_fix_indentation_callback(this, lines, cursor_position);
8590b57cec5SDimitry Andric 
8600b57cec5SDimitry Andric   // If it is already correct no special work is needed
8610b57cec5SDimitry Andric   if (indent_correction == 0)
8620b57cec5SDimitry Andric     return CC_REFRESH;
8630b57cec5SDimitry Andric 
8640b57cec5SDimitry Andric   // Change the indentation level of the line
8650b57cec5SDimitry Andric   std::string currentLine = lines.GetStringAtIndex(m_current_line_index);
8660b57cec5SDimitry Andric   if (indent_correction > 0) {
8670b57cec5SDimitry Andric     currentLine = currentLine.insert(0, indent_correction, ' ');
8680b57cec5SDimitry Andric   } else {
8690b57cec5SDimitry Andric     currentLine = currentLine.erase(0, -indent_correction);
8700b57cec5SDimitry Andric   }
8710b57cec5SDimitry Andric #if LLDB_EDITLINE_USE_WCHAR
8720b57cec5SDimitry Andric   m_input_lines[m_current_line_index] = m_utf8conv.from_bytes(currentLine);
8730b57cec5SDimitry Andric #else
8740b57cec5SDimitry Andric   m_input_lines[m_current_line_index] = currentLine;
8750b57cec5SDimitry Andric #endif
8760b57cec5SDimitry Andric 
8770b57cec5SDimitry Andric   // Update the display to reflect the change
8780b57cec5SDimitry Andric   MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
8790b57cec5SDimitry Andric   DisplayInput(m_current_line_index);
8800b57cec5SDimitry Andric 
8810b57cec5SDimitry Andric   // Reposition the cursor back on the original line and prepare to restart
8820b57cec5SDimitry Andric   // editing with a new cursor position
8830b57cec5SDimitry Andric   SetCurrentLine(m_current_line_index);
8840b57cec5SDimitry Andric   MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
8850b57cec5SDimitry Andric   m_revert_cursor_index = cursor_position + indent_correction;
8860b57cec5SDimitry Andric   return CC_NEWLINE;
8870b57cec5SDimitry Andric }
8880b57cec5SDimitry Andric 
8890b57cec5SDimitry Andric unsigned char Editline::RevertLineCommand(int ch) {
8900b57cec5SDimitry Andric   el_winsertstr(m_editline, m_input_lines[m_current_line_index].c_str());
8910b57cec5SDimitry Andric   if (m_revert_cursor_index >= 0) {
8920b57cec5SDimitry Andric     LineInfoW *info = const_cast<LineInfoW *>(el_wline(m_editline));
8930b57cec5SDimitry Andric     info->cursor = info->buffer + m_revert_cursor_index;
8940b57cec5SDimitry Andric     if (info->cursor > info->lastchar) {
8950b57cec5SDimitry Andric       info->cursor = info->lastchar;
8960b57cec5SDimitry Andric     }
8970b57cec5SDimitry Andric     m_revert_cursor_index = -1;
8980b57cec5SDimitry Andric   }
8990b57cec5SDimitry Andric   return CC_REFRESH;
9000b57cec5SDimitry Andric }
9010b57cec5SDimitry Andric 
9020b57cec5SDimitry Andric unsigned char Editline::BufferStartCommand(int ch) {
9030b57cec5SDimitry Andric   SaveEditedLine();
9040b57cec5SDimitry Andric   MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
9050b57cec5SDimitry Andric   SetCurrentLine(0);
9060b57cec5SDimitry Andric   m_revert_cursor_index = 0;
9070b57cec5SDimitry Andric   return CC_NEWLINE;
9080b57cec5SDimitry Andric }
9090b57cec5SDimitry Andric 
9100b57cec5SDimitry Andric unsigned char Editline::BufferEndCommand(int ch) {
9110b57cec5SDimitry Andric   SaveEditedLine();
9120b57cec5SDimitry Andric   MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockEnd);
9130b57cec5SDimitry Andric   SetCurrentLine((int)m_input_lines.size() - 1);
9140b57cec5SDimitry Andric   MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
9150b57cec5SDimitry Andric   return CC_NEWLINE;
9160b57cec5SDimitry Andric }
9170b57cec5SDimitry Andric 
9180b57cec5SDimitry Andric /// Prints completions and their descriptions to the given file. Only the
9190b57cec5SDimitry Andric /// completions in the interval [start, end) are printed.
9209dba64beSDimitry Andric static void
9219dba64beSDimitry Andric PrintCompletion(FILE *output_file,
9229dba64beSDimitry Andric                 llvm::ArrayRef<CompletionResult::Completion> results,
9239dba64beSDimitry Andric                 size_t max_len) {
9249dba64beSDimitry Andric   for (const CompletionResult::Completion &c : results) {
9259dba64beSDimitry Andric     fprintf(output_file, "\t%-*s", (int)max_len, c.GetCompletion().c_str());
9269dba64beSDimitry Andric     if (!c.GetDescription().empty())
9279dba64beSDimitry Andric       fprintf(output_file, " -- %s", c.GetDescription().c_str());
9289dba64beSDimitry Andric     fprintf(output_file, "\n");
9299dba64beSDimitry Andric   }
9300b57cec5SDimitry Andric }
9310b57cec5SDimitry Andric 
9325f757f3fSDimitry Andric void Editline::DisplayCompletions(
9335f757f3fSDimitry Andric     Editline &editline, llvm::ArrayRef<CompletionResult::Completion> results) {
9349dba64beSDimitry Andric   assert(!results.empty());
9350b57cec5SDimitry Andric 
9365f757f3fSDimitry Andric   fprintf(editline.m_output_file,
9375f757f3fSDimitry Andric           "\n" ANSI_CLEAR_BELOW "Available completions:\n");
9389dba64beSDimitry Andric   const size_t page_size = 40;
9399dba64beSDimitry Andric   bool all = false;
9400b57cec5SDimitry Andric 
9419dba64beSDimitry Andric   auto longest =
9429dba64beSDimitry Andric       std::max_element(results.begin(), results.end(), [](auto &c1, auto &c2) {
9439dba64beSDimitry Andric         return c1.GetCompletion().size() < c2.GetCompletion().size();
9449dba64beSDimitry Andric       });
9459dba64beSDimitry Andric 
9469dba64beSDimitry Andric   const size_t max_len = longest->GetCompletion().size();
9479dba64beSDimitry Andric 
9489dba64beSDimitry Andric   if (results.size() < page_size) {
9495f757f3fSDimitry Andric     PrintCompletion(editline.m_output_file, results, max_len);
9509dba64beSDimitry Andric     return;
9519dba64beSDimitry Andric   }
9529dba64beSDimitry Andric 
9539dba64beSDimitry Andric   size_t cur_pos = 0;
9549dba64beSDimitry Andric   while (cur_pos < results.size()) {
9559dba64beSDimitry Andric     size_t remaining = results.size() - cur_pos;
9569dba64beSDimitry Andric     size_t next_size = all ? remaining : std::min(page_size, remaining);
9579dba64beSDimitry Andric 
9585f757f3fSDimitry Andric     PrintCompletion(editline.m_output_file, results.slice(cur_pos, next_size),
9595f757f3fSDimitry Andric                     max_len);
9609dba64beSDimitry Andric 
9619dba64beSDimitry Andric     cur_pos += next_size;
9629dba64beSDimitry Andric 
9639dba64beSDimitry Andric     if (cur_pos >= results.size())
9649dba64beSDimitry Andric       break;
9659dba64beSDimitry Andric 
9665f757f3fSDimitry Andric     fprintf(editline.m_output_file, "More (Y/n/a): ");
9675f757f3fSDimitry Andric     // The type for the output and the type for the parameter are different,
9685f757f3fSDimitry Andric     // to allow interoperability with older versions of libedit. The container
9695f757f3fSDimitry Andric     // for the reply must be as wide as what our implementation is using,
9705f757f3fSDimitry Andric     // but libedit may use a narrower type depending on the build
9715f757f3fSDimitry Andric     // configuration.
9725f757f3fSDimitry Andric     EditLineGetCharType reply = L'n';
9735f757f3fSDimitry Andric     int got_char = el_wgetc(editline.m_editline,
9745f757f3fSDimitry Andric                             reinterpret_cast<EditLineCharType *>(&reply));
9755f757f3fSDimitry Andric     // Check for a ^C or other interruption.
9765f757f3fSDimitry Andric     if (editline.m_editor_status == EditorStatus::Interrupted) {
9775f757f3fSDimitry Andric       editline.m_editor_status = EditorStatus::Editing;
9785f757f3fSDimitry Andric       fprintf(editline.m_output_file, "^C\n");
9795f757f3fSDimitry Andric       break;
9805f757f3fSDimitry Andric     }
9815f757f3fSDimitry Andric 
9825f757f3fSDimitry Andric     fprintf(editline.m_output_file, "\n");
9839dba64beSDimitry Andric     if (got_char == -1 || reply == 'n')
9849dba64beSDimitry Andric       break;
9859dba64beSDimitry Andric     if (reply == 'a')
9869dba64beSDimitry Andric       all = true;
9870b57cec5SDimitry Andric   }
9880b57cec5SDimitry Andric }
9890b57cec5SDimitry Andric 
9900b57cec5SDimitry Andric unsigned char Editline::TabCommand(int ch) {
991fe6060f1SDimitry Andric   if (!m_completion_callback)
9920b57cec5SDimitry Andric     return CC_ERROR;
9930b57cec5SDimitry Andric 
9940b57cec5SDimitry Andric   const LineInfo *line_info = el_line(m_editline);
9950b57cec5SDimitry Andric 
9969dba64beSDimitry Andric   llvm::StringRef line(line_info->buffer,
9979dba64beSDimitry Andric                        line_info->lastchar - line_info->buffer);
9989dba64beSDimitry Andric   unsigned cursor_index = line_info->cursor - line_info->buffer;
9999dba64beSDimitry Andric   CompletionResult result;
10009dba64beSDimitry Andric   CompletionRequest request(line, cursor_index, result);
10010b57cec5SDimitry Andric 
1002fe6060f1SDimitry Andric   m_completion_callback(request);
10039dba64beSDimitry Andric 
10049dba64beSDimitry Andric   llvm::ArrayRef<CompletionResult::Completion> results = result.GetResults();
10059dba64beSDimitry Andric 
10069dba64beSDimitry Andric   StringList completions;
10079dba64beSDimitry Andric   result.GetMatches(completions);
10089dba64beSDimitry Andric 
10099dba64beSDimitry Andric   if (results.size() == 0)
10100b57cec5SDimitry Andric     return CC_ERROR;
10119dba64beSDimitry Andric 
10129dba64beSDimitry Andric   if (results.size() == 1) {
10139dba64beSDimitry Andric     CompletionResult::Completion completion = results.front();
10149dba64beSDimitry Andric     switch (completion.GetMode()) {
10159dba64beSDimitry Andric     case CompletionMode::Normal: {
10169dba64beSDimitry Andric       std::string to_add = completion.GetCompletion();
1017e8d8bef9SDimitry Andric       // Terminate the current argument with a quote if it started with a quote.
1018*0fca6ea1SDimitry Andric       Args &parsedLine = request.GetParsedLine();
1019*0fca6ea1SDimitry Andric       if (!parsedLine.empty() && request.GetCursorIndex() < parsedLine.size() &&
1020*0fca6ea1SDimitry Andric           request.GetParsedArg().IsQuoted()) {
10219dba64beSDimitry Andric         to_add.push_back(request.GetParsedArg().GetQuoteChar());
1022*0fca6ea1SDimitry Andric       }
10239dba64beSDimitry Andric       to_add.push_back(' ');
1024349cc55cSDimitry Andric       el_deletestr(m_editline, request.GetCursorArgumentPrefix().size());
10259dba64beSDimitry Andric       el_insertstr(m_editline, to_add.c_str());
1026e8d8bef9SDimitry Andric       // Clear all the autosuggestion parts if the only single space can be completed.
1027e8d8bef9SDimitry Andric       if (to_add == " ")
1028e8d8bef9SDimitry Andric         return CC_REDISPLAY;
1029e8d8bef9SDimitry Andric       return CC_REFRESH;
10309dba64beSDimitry Andric     }
10319dba64beSDimitry Andric     case CompletionMode::Partial: {
10329dba64beSDimitry Andric       std::string to_add = completion.GetCompletion();
10339dba64beSDimitry Andric       to_add = to_add.substr(request.GetCursorArgumentPrefix().size());
10349dba64beSDimitry Andric       el_insertstr(m_editline, to_add.c_str());
10359dba64beSDimitry Andric       break;
10369dba64beSDimitry Andric     }
10379dba64beSDimitry Andric     case CompletionMode::RewriteLine: {
10380b57cec5SDimitry Andric       el_deletestr(m_editline, line_info->cursor - line_info->buffer);
10399dba64beSDimitry Andric       el_insertstr(m_editline, completion.GetCompletion().c_str());
10409dba64beSDimitry Andric       break;
10419dba64beSDimitry Andric     }
10429dba64beSDimitry Andric     }
10430b57cec5SDimitry Andric     return CC_REDISPLAY;
10440b57cec5SDimitry Andric   }
10450b57cec5SDimitry Andric 
10460b57cec5SDimitry Andric   // If we get a longer match display that first.
10479dba64beSDimitry Andric   std::string longest_prefix = completions.LongestCommonPrefix();
10489dba64beSDimitry Andric   if (!longest_prefix.empty())
10499dba64beSDimitry Andric     longest_prefix =
10509dba64beSDimitry Andric         longest_prefix.substr(request.GetCursorArgumentPrefix().size());
10519dba64beSDimitry Andric   if (!longest_prefix.empty()) {
10529dba64beSDimitry Andric     el_insertstr(m_editline, longest_prefix.c_str());
10530b57cec5SDimitry Andric     return CC_REDISPLAY;
10540b57cec5SDimitry Andric   }
10550b57cec5SDimitry Andric 
10565f757f3fSDimitry Andric   DisplayCompletions(*this, results);
10570b57cec5SDimitry Andric 
10580b57cec5SDimitry Andric   DisplayInput();
10590b57cec5SDimitry Andric   MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
10600b57cec5SDimitry Andric   return CC_REDISPLAY;
10610b57cec5SDimitry Andric }
10620b57cec5SDimitry Andric 
1063e8d8bef9SDimitry Andric unsigned char Editline::ApplyAutosuggestCommand(int ch) {
1064fe6060f1SDimitry Andric   if (!m_suggestion_callback) {
1065fe6060f1SDimitry Andric     return CC_REDISPLAY;
1066fe6060f1SDimitry Andric   }
1067fe6060f1SDimitry Andric 
1068e8d8bef9SDimitry Andric   const LineInfo *line_info = el_line(m_editline);
1069e8d8bef9SDimitry Andric   llvm::StringRef line(line_info->buffer,
1070e8d8bef9SDimitry Andric                        line_info->lastchar - line_info->buffer);
1071e8d8bef9SDimitry Andric 
1072bdd1243dSDimitry Andric   if (std::optional<std::string> to_add = m_suggestion_callback(line))
1073e8d8bef9SDimitry Andric     el_insertstr(m_editline, to_add->c_str());
1074e8d8bef9SDimitry Andric 
1075e8d8bef9SDimitry Andric   return CC_REDISPLAY;
1076e8d8bef9SDimitry Andric }
1077e8d8bef9SDimitry Andric 
1078e8d8bef9SDimitry Andric unsigned char Editline::TypedCharacter(int ch) {
1079e8d8bef9SDimitry Andric   std::string typed = std::string(1, ch);
1080e8d8bef9SDimitry Andric   el_insertstr(m_editline, typed.c_str());
1081fe6060f1SDimitry Andric 
1082fe6060f1SDimitry Andric   if (!m_suggestion_callback) {
1083fe6060f1SDimitry Andric     return CC_REDISPLAY;
1084fe6060f1SDimitry Andric   }
1085fe6060f1SDimitry Andric 
1086e8d8bef9SDimitry Andric   const LineInfo *line_info = el_line(m_editline);
1087e8d8bef9SDimitry Andric   llvm::StringRef line(line_info->buffer,
1088e8d8bef9SDimitry Andric                        line_info->lastchar - line_info->buffer);
1089e8d8bef9SDimitry Andric 
1090bdd1243dSDimitry Andric   if (std::optional<std::string> to_add = m_suggestion_callback(line)) {
10915f757f3fSDimitry Andric     std::string to_add_color =
10925f757f3fSDimitry Andric         m_suggestion_ansi_prefix + to_add.value() + m_suggestion_ansi_suffix;
1093e8d8bef9SDimitry Andric     fputs(typed.c_str(), m_output_file);
1094e8d8bef9SDimitry Andric     fputs(to_add_color.c_str(), m_output_file);
1095e8d8bef9SDimitry Andric     size_t new_autosuggestion_size = line.size() + to_add->length();
1096e8d8bef9SDimitry Andric     // Print spaces to hide any remains of a previous longer autosuggestion.
1097e8d8bef9SDimitry Andric     if (new_autosuggestion_size < m_previous_autosuggestion_size) {
1098e8d8bef9SDimitry Andric       size_t spaces_to_print =
1099e8d8bef9SDimitry Andric           m_previous_autosuggestion_size - new_autosuggestion_size;
1100e8d8bef9SDimitry Andric       std::string spaces = std::string(spaces_to_print, ' ');
1101e8d8bef9SDimitry Andric       fputs(spaces.c_str(), m_output_file);
1102e8d8bef9SDimitry Andric     }
1103e8d8bef9SDimitry Andric     m_previous_autosuggestion_size = new_autosuggestion_size;
1104e8d8bef9SDimitry Andric 
1105e8d8bef9SDimitry Andric     int editline_cursor_position =
1106e8d8bef9SDimitry Andric         (int)((line_info->cursor - line_info->buffer) + GetPromptWidth());
1107e8d8bef9SDimitry Andric     int editline_cursor_row = editline_cursor_position / m_terminal_width;
1108e8d8bef9SDimitry Andric     int toColumn =
1109e8d8bef9SDimitry Andric         editline_cursor_position - (editline_cursor_row * m_terminal_width);
1110e8d8bef9SDimitry Andric     fprintf(m_output_file, ANSI_SET_COLUMN_N, toColumn);
1111e8d8bef9SDimitry Andric     return CC_REFRESH;
1112e8d8bef9SDimitry Andric   }
1113e8d8bef9SDimitry Andric 
1114e8d8bef9SDimitry Andric   return CC_REDISPLAY;
1115e8d8bef9SDimitry Andric }
1116e8d8bef9SDimitry Andric 
1117fe6060f1SDimitry Andric void Editline::AddFunctionToEditLine(const EditLineCharType *command,
1118fe6060f1SDimitry Andric                                      const EditLineCharType *helptext,
1119fe6060f1SDimitry Andric                                      EditlineCommandCallbackType callbackFn) {
1120fe6060f1SDimitry Andric   el_wset(m_editline, EL_ADDFN, command, helptext, callbackFn);
1121fe6060f1SDimitry Andric }
1122fe6060f1SDimitry Andric 
1123fe6060f1SDimitry Andric void Editline::SetEditLinePromptCallback(
1124fe6060f1SDimitry Andric     EditlinePromptCallbackType callbackFn) {
1125fe6060f1SDimitry Andric   el_set(m_editline, EL_PROMPT, callbackFn);
1126fe6060f1SDimitry Andric }
1127fe6060f1SDimitry Andric 
1128fe6060f1SDimitry Andric void Editline::SetGetCharacterFunction(EditlineGetCharCallbackType callbackFn) {
1129fe6060f1SDimitry Andric   el_wset(m_editline, EL_GETCFN, callbackFn);
1130fe6060f1SDimitry Andric }
1131fe6060f1SDimitry Andric 
11320b57cec5SDimitry Andric void Editline::ConfigureEditor(bool multiline) {
11330b57cec5SDimitry Andric   if (m_editline && m_multiline_enabled == multiline)
11340b57cec5SDimitry Andric     return;
11350b57cec5SDimitry Andric   m_multiline_enabled = multiline;
11360b57cec5SDimitry Andric 
11370b57cec5SDimitry Andric   if (m_editline) {
11380b57cec5SDimitry Andric     // Disable edit mode to stop the terminal from flushing all input during
11390b57cec5SDimitry Andric     // the call to el_end() since we expect to have multiple editline instances
11400b57cec5SDimitry Andric     // in this program.
11410b57cec5SDimitry Andric     el_set(m_editline, EL_EDITMODE, 0);
11420b57cec5SDimitry Andric     el_end(m_editline);
11430b57cec5SDimitry Andric   }
11440b57cec5SDimitry Andric 
11450b57cec5SDimitry Andric   m_editline =
11460b57cec5SDimitry Andric       el_init(m_editor_name.c_str(), m_input_file, m_output_file, m_error_file);
11475ffd83dbSDimitry Andric   ApplyTerminalSizeChange();
11480b57cec5SDimitry Andric 
11490b57cec5SDimitry Andric   if (m_history_sp && m_history_sp->IsValid()) {
11500b57cec5SDimitry Andric     if (!m_history_sp->Load()) {
11510b57cec5SDimitry Andric         fputs("Could not load history file\n.", m_output_file);
11520b57cec5SDimitry Andric     }
11530b57cec5SDimitry Andric     el_wset(m_editline, EL_HIST, history, m_history_sp->GetHistoryPtr());
11540b57cec5SDimitry Andric   }
11550b57cec5SDimitry Andric   el_set(m_editline, EL_CLIENTDATA, this);
11560b57cec5SDimitry Andric   el_set(m_editline, EL_SIGNAL, 0);
11570b57cec5SDimitry Andric   el_set(m_editline, EL_EDITOR, "emacs");
11580b57cec5SDimitry Andric 
1159fe6060f1SDimitry Andric   SetGetCharacterFunction([](EditLine *editline, EditLineGetCharType *c) {
11600b57cec5SDimitry Andric     return Editline::InstanceFor(editline)->GetCharacter(c);
1161fe6060f1SDimitry Andric   });
1162fe6060f1SDimitry Andric 
1163fe6060f1SDimitry Andric   SetEditLinePromptCallback([](EditLine *editline) {
1164fe6060f1SDimitry Andric     return Editline::InstanceFor(editline)->Prompt();
1165fe6060f1SDimitry Andric   });
11660b57cec5SDimitry Andric 
11670b57cec5SDimitry Andric   // Commands used for multiline support, registered whether or not they're
11680b57cec5SDimitry Andric   // used
1169fe6060f1SDimitry Andric   AddFunctionToEditLine(
1170fe6060f1SDimitry Andric       EditLineConstString("lldb-break-line"),
11710b57cec5SDimitry Andric       EditLineConstString("Insert a line break"),
1172fe6060f1SDimitry Andric       [](EditLine *editline, int ch) {
11730b57cec5SDimitry Andric         return Editline::InstanceFor(editline)->BreakLineCommand(ch);
1174fe6060f1SDimitry Andric       });
1175fe6060f1SDimitry Andric 
1176fe6060f1SDimitry Andric   AddFunctionToEditLine(
1177fe6060f1SDimitry Andric       EditLineConstString("lldb-end-or-add-line"),
11780b57cec5SDimitry Andric       EditLineConstString("End editing or continue when incomplete"),
1179fe6060f1SDimitry Andric       [](EditLine *editline, int ch) {
11800b57cec5SDimitry Andric         return Editline::InstanceFor(editline)->EndOrAddLineCommand(ch);
1181fe6060f1SDimitry Andric       });
1182fe6060f1SDimitry Andric   AddFunctionToEditLine(
1183fe6060f1SDimitry Andric       EditLineConstString("lldb-delete-next-char"),
11840b57cec5SDimitry Andric       EditLineConstString("Delete next character"),
1185fe6060f1SDimitry Andric       [](EditLine *editline, int ch) {
11860b57cec5SDimitry Andric         return Editline::InstanceFor(editline)->DeleteNextCharCommand(ch);
1187fe6060f1SDimitry Andric       });
1188fe6060f1SDimitry Andric   AddFunctionToEditLine(
1189fe6060f1SDimitry Andric       EditLineConstString("lldb-delete-previous-char"),
11900b57cec5SDimitry Andric       EditLineConstString("Delete previous character"),
1191fe6060f1SDimitry Andric       [](EditLine *editline, int ch) {
11920b57cec5SDimitry Andric         return Editline::InstanceFor(editline)->DeletePreviousCharCommand(ch);
1193fe6060f1SDimitry Andric       });
1194fe6060f1SDimitry Andric   AddFunctionToEditLine(
1195fe6060f1SDimitry Andric       EditLineConstString("lldb-previous-line"),
11960b57cec5SDimitry Andric       EditLineConstString("Move to previous line"),
1197fe6060f1SDimitry Andric       [](EditLine *editline, int ch) {
11980b57cec5SDimitry Andric         return Editline::InstanceFor(editline)->PreviousLineCommand(ch);
1199fe6060f1SDimitry Andric       });
1200fe6060f1SDimitry Andric   AddFunctionToEditLine(
1201fe6060f1SDimitry Andric       EditLineConstString("lldb-next-line"),
1202fe6060f1SDimitry Andric       EditLineConstString("Move to next line"), [](EditLine *editline, int ch) {
12030b57cec5SDimitry Andric         return Editline::InstanceFor(editline)->NextLineCommand(ch);
1204fe6060f1SDimitry Andric       });
1205fe6060f1SDimitry Andric   AddFunctionToEditLine(
1206fe6060f1SDimitry Andric       EditLineConstString("lldb-previous-history"),
12070b57cec5SDimitry Andric       EditLineConstString("Move to previous history"),
1208fe6060f1SDimitry Andric       [](EditLine *editline, int ch) {
12090b57cec5SDimitry Andric         return Editline::InstanceFor(editline)->PreviousHistoryCommand(ch);
1210fe6060f1SDimitry Andric       });
1211fe6060f1SDimitry Andric   AddFunctionToEditLine(
1212fe6060f1SDimitry Andric       EditLineConstString("lldb-next-history"),
12130b57cec5SDimitry Andric       EditLineConstString("Move to next history"),
1214fe6060f1SDimitry Andric       [](EditLine *editline, int ch) {
12150b57cec5SDimitry Andric         return Editline::InstanceFor(editline)->NextHistoryCommand(ch);
1216fe6060f1SDimitry Andric       });
1217fe6060f1SDimitry Andric   AddFunctionToEditLine(
1218fe6060f1SDimitry Andric       EditLineConstString("lldb-buffer-start"),
12190b57cec5SDimitry Andric       EditLineConstString("Move to start of buffer"),
1220fe6060f1SDimitry Andric       [](EditLine *editline, int ch) {
12210b57cec5SDimitry Andric         return Editline::InstanceFor(editline)->BufferStartCommand(ch);
1222fe6060f1SDimitry Andric       });
1223fe6060f1SDimitry Andric   AddFunctionToEditLine(
1224fe6060f1SDimitry Andric       EditLineConstString("lldb-buffer-end"),
12250b57cec5SDimitry Andric       EditLineConstString("Move to end of buffer"),
1226fe6060f1SDimitry Andric       [](EditLine *editline, int ch) {
12270b57cec5SDimitry Andric         return Editline::InstanceFor(editline)->BufferEndCommand(ch);
1228fe6060f1SDimitry Andric       });
1229fe6060f1SDimitry Andric   AddFunctionToEditLine(
1230fe6060f1SDimitry Andric       EditLineConstString("lldb-fix-indentation"),
12310b57cec5SDimitry Andric       EditLineConstString("Fix line indentation"),
1232fe6060f1SDimitry Andric       [](EditLine *editline, int ch) {
12330b57cec5SDimitry Andric         return Editline::InstanceFor(editline)->FixIndentationCommand(ch);
1234fe6060f1SDimitry Andric       });
12350b57cec5SDimitry Andric 
12360b57cec5SDimitry Andric   // Register the complete callback under two names for compatibility with
12370b57cec5SDimitry Andric   // older clients using custom .editrc files (largely because libedit has a
12380b57cec5SDimitry Andric   // bad bug where if you have a bind command that tries to bind to a function
12390b57cec5SDimitry Andric   // name that doesn't exist, it can corrupt the heap and crash your process
12400b57cec5SDimitry Andric   // later.)
12410b57cec5SDimitry Andric   EditlineCommandCallbackType complete_callback = [](EditLine *editline,
12420b57cec5SDimitry Andric                                                      int ch) {
12430b57cec5SDimitry Andric     return Editline::InstanceFor(editline)->TabCommand(ch);
12440b57cec5SDimitry Andric   };
1245fe6060f1SDimitry Andric   AddFunctionToEditLine(EditLineConstString("lldb-complete"),
1246fe6060f1SDimitry Andric                         EditLineConstString("Invoke completion"),
1247fe6060f1SDimitry Andric                         complete_callback);
1248fe6060f1SDimitry Andric   AddFunctionToEditLine(EditLineConstString("lldb_complete"),
1249fe6060f1SDimitry Andric                         EditLineConstString("Invoke completion"),
1250fe6060f1SDimitry Andric                         complete_callback);
12510b57cec5SDimitry Andric 
12520b57cec5SDimitry Andric   // General bindings we don't mind being overridden
12530b57cec5SDimitry Andric   if (!multiline) {
12540b57cec5SDimitry Andric     el_set(m_editline, EL_BIND, "^r", "em-inc-search-prev",
12550b57cec5SDimitry Andric            NULL); // Cycle through backwards search, entering string
1256e8d8bef9SDimitry Andric 
1257e8d8bef9SDimitry Andric     if (m_suggestion_callback) {
1258fe6060f1SDimitry Andric       AddFunctionToEditLine(
1259fe6060f1SDimitry Andric           EditLineConstString("lldb-apply-complete"),
1260e8d8bef9SDimitry Andric           EditLineConstString("Adopt autocompletion"),
1261fe6060f1SDimitry Andric           [](EditLine *editline, int ch) {
1262fe6060f1SDimitry Andric             return Editline::InstanceFor(editline)->ApplyAutosuggestCommand(ch);
1263fe6060f1SDimitry Andric           });
1264e8d8bef9SDimitry Andric 
1265e8d8bef9SDimitry Andric       el_set(m_editline, EL_BIND, "^f", "lldb-apply-complete",
1266e8d8bef9SDimitry Andric              NULL); // Apply a part that is suggested automatically
1267e8d8bef9SDimitry Andric 
1268fe6060f1SDimitry Andric       AddFunctionToEditLine(
1269fe6060f1SDimitry Andric           EditLineConstString("lldb-typed-character"),
1270e8d8bef9SDimitry Andric           EditLineConstString("Typed character"),
1271fe6060f1SDimitry Andric           [](EditLine *editline, int ch) {
1272e8d8bef9SDimitry Andric             return Editline::InstanceFor(editline)->TypedCharacter(ch);
1273fe6060f1SDimitry Andric           });
1274e8d8bef9SDimitry Andric 
1275e8d8bef9SDimitry Andric       char bind_key[2] = {0, 0};
1276e8d8bef9SDimitry Andric       llvm::StringRef ascii_chars =
1277e8d8bef9SDimitry Andric           "abcdefghijklmnopqrstuvwxzyABCDEFGHIJKLMNOPQRSTUVWXZY1234567890!\"#$%"
1278e8d8bef9SDimitry Andric           "&'()*+,./:;<=>?@[]_`{|}~ ";
1279e8d8bef9SDimitry Andric       for (char c : ascii_chars) {
1280e8d8bef9SDimitry Andric         bind_key[0] = c;
1281e8d8bef9SDimitry Andric         el_set(m_editline, EL_BIND, bind_key, "lldb-typed-character", NULL);
12820b57cec5SDimitry Andric       }
1283e8d8bef9SDimitry Andric       el_set(m_editline, EL_BIND, "\\-", "lldb-typed-character", NULL);
1284e8d8bef9SDimitry Andric       el_set(m_editline, EL_BIND, "\\^", "lldb-typed-character", NULL);
1285e8d8bef9SDimitry Andric       el_set(m_editline, EL_BIND, "\\\\", "lldb-typed-character", NULL);
1286e8d8bef9SDimitry Andric     }
1287e8d8bef9SDimitry Andric   }
1288e8d8bef9SDimitry Andric 
12890b57cec5SDimitry Andric   el_set(m_editline, EL_BIND, "^w", "ed-delete-prev-word",
12900b57cec5SDimitry Andric          NULL); // Delete previous word, behave like bash in emacs mode
12910b57cec5SDimitry Andric   el_set(m_editline, EL_BIND, "\t", "lldb-complete",
12920b57cec5SDimitry Andric          NULL); // Bind TAB to auto complete
12930b57cec5SDimitry Andric 
1294480093f4SDimitry Andric   // Allow ctrl-left-arrow and ctrl-right-arrow for navigation, behave like
1295480093f4SDimitry Andric   // bash in emacs mode.
1296480093f4SDimitry Andric   el_set(m_editline, EL_BIND, ESCAPE "[1;5C", "em-next-word", NULL);
1297480093f4SDimitry Andric   el_set(m_editline, EL_BIND, ESCAPE "[1;5D", "ed-prev-word", NULL);
1298480093f4SDimitry Andric   el_set(m_editline, EL_BIND, ESCAPE "[5C", "em-next-word", NULL);
1299480093f4SDimitry Andric   el_set(m_editline, EL_BIND, ESCAPE "[5D", "ed-prev-word", NULL);
1300480093f4SDimitry Andric   el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[C", "em-next-word", NULL);
1301480093f4SDimitry Andric   el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[D", "ed-prev-word", NULL);
1302480093f4SDimitry Andric 
13030b57cec5SDimitry Andric   // Allow user-specific customization prior to registering bindings we
13040b57cec5SDimitry Andric   // absolutely require
13050b57cec5SDimitry Andric   el_source(m_editline, nullptr);
13060b57cec5SDimitry Andric 
13070b57cec5SDimitry Andric   // Register an internal binding that external developers shouldn't use
1308fe6060f1SDimitry Andric   AddFunctionToEditLine(
1309fe6060f1SDimitry Andric       EditLineConstString("lldb-revert-line"),
13100b57cec5SDimitry Andric       EditLineConstString("Revert line to saved state"),
1311fe6060f1SDimitry Andric       [](EditLine *editline, int ch) {
13120b57cec5SDimitry Andric         return Editline::InstanceFor(editline)->RevertLineCommand(ch);
1313fe6060f1SDimitry Andric       });
13140b57cec5SDimitry Andric 
13150b57cec5SDimitry Andric   // Register keys that perform auto-indent correction
13160b57cec5SDimitry Andric   if (m_fix_indentation_callback && m_fix_indentation_callback_chars) {
13170b57cec5SDimitry Andric     char bind_key[2] = {0, 0};
13180b57cec5SDimitry Andric     const char *indent_chars = m_fix_indentation_callback_chars;
13190b57cec5SDimitry Andric     while (*indent_chars) {
13200b57cec5SDimitry Andric       bind_key[0] = *indent_chars;
13210b57cec5SDimitry Andric       el_set(m_editline, EL_BIND, bind_key, "lldb-fix-indentation", NULL);
13220b57cec5SDimitry Andric       ++indent_chars;
13230b57cec5SDimitry Andric     }
13240b57cec5SDimitry Andric   }
13250b57cec5SDimitry Andric 
13260b57cec5SDimitry Andric   // Multi-line editor bindings
13270b57cec5SDimitry Andric   if (multiline) {
13280b57cec5SDimitry Andric     el_set(m_editline, EL_BIND, "\n", "lldb-end-or-add-line", NULL);
13290b57cec5SDimitry Andric     el_set(m_editline, EL_BIND, "\r", "lldb-end-or-add-line", NULL);
13300b57cec5SDimitry Andric     el_set(m_editline, EL_BIND, ESCAPE "\n", "lldb-break-line", NULL);
13310b57cec5SDimitry Andric     el_set(m_editline, EL_BIND, ESCAPE "\r", "lldb-break-line", NULL);
13320b57cec5SDimitry Andric     el_set(m_editline, EL_BIND, "^p", "lldb-previous-line", NULL);
13330b57cec5SDimitry Andric     el_set(m_editline, EL_BIND, "^n", "lldb-next-line", NULL);
13340b57cec5SDimitry Andric     el_set(m_editline, EL_BIND, "^?", "lldb-delete-previous-char", NULL);
13350b57cec5SDimitry Andric     el_set(m_editline, EL_BIND, "^d", "lldb-delete-next-char", NULL);
13360b57cec5SDimitry Andric     el_set(m_editline, EL_BIND, ESCAPE "[3~", "lldb-delete-next-char", NULL);
13370b57cec5SDimitry Andric     el_set(m_editline, EL_BIND, ESCAPE "[\\^", "lldb-revert-line", NULL);
13380b57cec5SDimitry Andric 
13390b57cec5SDimitry Andric     // Editor-specific bindings
13400b57cec5SDimitry Andric     if (IsEmacs()) {
13410b57cec5SDimitry Andric       el_set(m_editline, EL_BIND, ESCAPE "<", "lldb-buffer-start", NULL);
13420b57cec5SDimitry Andric       el_set(m_editline, EL_BIND, ESCAPE ">", "lldb-buffer-end", NULL);
13430b57cec5SDimitry Andric       el_set(m_editline, EL_BIND, ESCAPE "[A", "lldb-previous-line", NULL);
13440b57cec5SDimitry Andric       el_set(m_editline, EL_BIND, ESCAPE "[B", "lldb-next-line", NULL);
13450b57cec5SDimitry Andric       el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[A", "lldb-previous-history",
13460b57cec5SDimitry Andric              NULL);
13470b57cec5SDimitry Andric       el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[B", "lldb-next-history",
13480b57cec5SDimitry Andric              NULL);
13490b57cec5SDimitry Andric       el_set(m_editline, EL_BIND, ESCAPE "[1;3A", "lldb-previous-history",
13500b57cec5SDimitry Andric              NULL);
13510b57cec5SDimitry Andric       el_set(m_editline, EL_BIND, ESCAPE "[1;3B", "lldb-next-history", NULL);
13520b57cec5SDimitry Andric     } else {
13530b57cec5SDimitry Andric       el_set(m_editline, EL_BIND, "^H", "lldb-delete-previous-char", NULL);
13540b57cec5SDimitry Andric 
13550b57cec5SDimitry Andric       el_set(m_editline, EL_BIND, "-a", ESCAPE "[A", "lldb-previous-line",
13560b57cec5SDimitry Andric              NULL);
13570b57cec5SDimitry Andric       el_set(m_editline, EL_BIND, "-a", ESCAPE "[B", "lldb-next-line", NULL);
13580b57cec5SDimitry Andric       el_set(m_editline, EL_BIND, "-a", "x", "lldb-delete-next-char", NULL);
13590b57cec5SDimitry Andric       el_set(m_editline, EL_BIND, "-a", "^H", "lldb-delete-previous-char",
13600b57cec5SDimitry Andric              NULL);
13610b57cec5SDimitry Andric       el_set(m_editline, EL_BIND, "-a", "^?", "lldb-delete-previous-char",
13620b57cec5SDimitry Andric              NULL);
13630b57cec5SDimitry Andric 
13640b57cec5SDimitry Andric       // Escape is absorbed exiting edit mode, so re-register important
13650b57cec5SDimitry Andric       // sequences without the prefix
13660b57cec5SDimitry Andric       el_set(m_editline, EL_BIND, "-a", "[A", "lldb-previous-line", NULL);
13670b57cec5SDimitry Andric       el_set(m_editline, EL_BIND, "-a", "[B", "lldb-next-line", NULL);
13680b57cec5SDimitry Andric       el_set(m_editline, EL_BIND, "-a", "[\\^", "lldb-revert-line", NULL);
13690b57cec5SDimitry Andric     }
13700b57cec5SDimitry Andric   }
13710b57cec5SDimitry Andric }
13720b57cec5SDimitry Andric 
13730b57cec5SDimitry Andric // Editline public methods
13740b57cec5SDimitry Andric 
13750b57cec5SDimitry Andric Editline *Editline::InstanceFor(EditLine *editline) {
13760b57cec5SDimitry Andric   Editline *editor;
13770b57cec5SDimitry Andric   el_get(editline, EL_CLIENTDATA, &editor);
13780b57cec5SDimitry Andric   return editor;
13790b57cec5SDimitry Andric }
13800b57cec5SDimitry Andric 
13810b57cec5SDimitry Andric Editline::Editline(const char *editline_name, FILE *input_file,
138281ad6265SDimitry Andric                    FILE *output_file, FILE *error_file,
13835f757f3fSDimitry Andric                    std::recursive_mutex &output_mutex)
13845f757f3fSDimitry Andric     : m_editor_status(EditorStatus::Complete), m_input_file(input_file),
13855f757f3fSDimitry Andric       m_output_file(output_file), m_error_file(error_file),
13865f757f3fSDimitry Andric       m_input_connection(fileno(input_file), false),
138781ad6265SDimitry Andric       m_output_mutex(output_mutex) {
13880b57cec5SDimitry Andric   // Get a shared history instance
13890b57cec5SDimitry Andric   m_editor_name = (editline_name == nullptr) ? "lldb-tmp" : editline_name;
13900b57cec5SDimitry Andric   m_history_sp = EditlineHistory::GetHistory(m_editor_name);
13910b57cec5SDimitry Andric }
13920b57cec5SDimitry Andric 
13930b57cec5SDimitry Andric Editline::~Editline() {
13940b57cec5SDimitry Andric   if (m_editline) {
13950b57cec5SDimitry Andric     // Disable edit mode to stop the terminal from flushing all input during
13960b57cec5SDimitry Andric     // the call to el_end() since we expect to have multiple editline instances
13970b57cec5SDimitry Andric     // in this program.
13980b57cec5SDimitry Andric     el_set(m_editline, EL_EDITMODE, 0);
13990b57cec5SDimitry Andric     el_end(m_editline);
14000b57cec5SDimitry Andric     m_editline = nullptr;
14010b57cec5SDimitry Andric   }
14020b57cec5SDimitry Andric 
14030b57cec5SDimitry Andric   // EditlineHistory objects are sometimes shared between multiple Editline
14040b57cec5SDimitry Andric   // instances with the same program name. So just release our shared pointer
14050b57cec5SDimitry Andric   // and if we are the last owner, it will save the history to the history save
14060b57cec5SDimitry Andric   // file automatically.
14070b57cec5SDimitry Andric   m_history_sp.reset();
14080b57cec5SDimitry Andric }
14090b57cec5SDimitry Andric 
14100b57cec5SDimitry Andric void Editline::SetPrompt(const char *prompt) {
14110b57cec5SDimitry Andric   m_set_prompt = prompt == nullptr ? "" : prompt;
14120b57cec5SDimitry Andric }
14130b57cec5SDimitry Andric 
14140b57cec5SDimitry Andric void Editline::SetContinuationPrompt(const char *continuation_prompt) {
14150b57cec5SDimitry Andric   m_set_continuation_prompt =
14160b57cec5SDimitry Andric       continuation_prompt == nullptr ? "" : continuation_prompt;
14170b57cec5SDimitry Andric }
14180b57cec5SDimitry Andric 
14195ffd83dbSDimitry Andric void Editline::TerminalSizeChanged() { m_terminal_size_has_changed = 1; }
14205ffd83dbSDimitry Andric 
14215ffd83dbSDimitry Andric void Editline::ApplyTerminalSizeChange() {
14225ffd83dbSDimitry Andric   if (!m_editline)
14235ffd83dbSDimitry Andric     return;
14245ffd83dbSDimitry Andric 
14255ffd83dbSDimitry Andric   m_terminal_size_has_changed = 0;
14260b57cec5SDimitry Andric   el_resize(m_editline);
14270b57cec5SDimitry Andric   int columns;
14280b57cec5SDimitry Andric   // This function is documenting as taking (const char *, void *) for the
14290b57cec5SDimitry Andric   // vararg part, but in reality in was consuming arguments until the first
14300b57cec5SDimitry Andric   // null pointer. This was fixed in libedit in April 2019
14310b57cec5SDimitry Andric   // <http://mail-index.netbsd.org/source-changes/2019/04/26/msg105454.html>,
14320b57cec5SDimitry Andric   // but we're keeping the workaround until a version with that fix is more
14330b57cec5SDimitry Andric   // widely available.
14340b57cec5SDimitry Andric   if (el_get(m_editline, EL_GETTC, "co", &columns, nullptr) == 0) {
14350b57cec5SDimitry Andric     m_terminal_width = columns;
14360b57cec5SDimitry Andric     if (m_current_line_rows != -1) {
14370b57cec5SDimitry Andric       const LineInfoW *info = el_wline(m_editline);
14380b57cec5SDimitry Andric       int lineLength =
14390b57cec5SDimitry Andric           (int)((info->lastchar - info->buffer) + GetPromptWidth());
14400b57cec5SDimitry Andric       m_current_line_rows = (lineLength / columns) + 1;
14410b57cec5SDimitry Andric     }
14420b57cec5SDimitry Andric   } else {
14430b57cec5SDimitry Andric     m_terminal_width = INT_MAX;
14440b57cec5SDimitry Andric     m_current_line_rows = 1;
14450b57cec5SDimitry Andric   }
14460b57cec5SDimitry Andric }
14470b57cec5SDimitry Andric 
14480b57cec5SDimitry Andric const char *Editline::GetPrompt() { return m_set_prompt.c_str(); }
14490b57cec5SDimitry Andric 
14500b57cec5SDimitry Andric uint32_t Editline::GetCurrentLine() { return m_current_line_index; }
14510b57cec5SDimitry Andric 
14520b57cec5SDimitry Andric bool Editline::Interrupt() {
14530b57cec5SDimitry Andric   bool result = true;
145481ad6265SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
14550b57cec5SDimitry Andric   if (m_editor_status == EditorStatus::Editing) {
14560b57cec5SDimitry Andric     fprintf(m_output_file, "^C\n");
14570b57cec5SDimitry Andric     result = m_input_connection.InterruptRead();
14580b57cec5SDimitry Andric   }
14590b57cec5SDimitry Andric   m_editor_status = EditorStatus::Interrupted;
14600b57cec5SDimitry Andric   return result;
14610b57cec5SDimitry Andric }
14620b57cec5SDimitry Andric 
14630b57cec5SDimitry Andric bool Editline::Cancel() {
14640b57cec5SDimitry Andric   bool result = true;
146581ad6265SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
14660b57cec5SDimitry Andric   if (m_editor_status == EditorStatus::Editing) {
14670b57cec5SDimitry Andric     MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
14680b57cec5SDimitry Andric     fprintf(m_output_file, ANSI_CLEAR_BELOW);
14690b57cec5SDimitry Andric     result = m_input_connection.InterruptRead();
14700b57cec5SDimitry Andric   }
14710b57cec5SDimitry Andric   m_editor_status = EditorStatus::Interrupted;
14720b57cec5SDimitry Andric   return result;
14730b57cec5SDimitry Andric }
14740b57cec5SDimitry Andric 
14750b57cec5SDimitry Andric bool Editline::GetLine(std::string &line, bool &interrupted) {
14760b57cec5SDimitry Andric   ConfigureEditor(false);
14770b57cec5SDimitry Andric   m_input_lines = std::vector<EditLineStringType>();
14780b57cec5SDimitry Andric   m_input_lines.insert(m_input_lines.begin(), EditLineConstString(""));
14790b57cec5SDimitry Andric 
148081ad6265SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
14810b57cec5SDimitry Andric 
14820b57cec5SDimitry Andric   lldbassert(m_editor_status != EditorStatus::Editing);
14830b57cec5SDimitry Andric   if (m_editor_status == EditorStatus::Interrupted) {
14840b57cec5SDimitry Andric     m_editor_status = EditorStatus::Complete;
14850b57cec5SDimitry Andric     interrupted = true;
14860b57cec5SDimitry Andric     return true;
14870b57cec5SDimitry Andric   }
14880b57cec5SDimitry Andric 
14890b57cec5SDimitry Andric   SetCurrentLine(0);
14900b57cec5SDimitry Andric   m_in_history = false;
14910b57cec5SDimitry Andric   m_editor_status = EditorStatus::Editing;
14920b57cec5SDimitry Andric   m_revert_cursor_index = -1;
14930b57cec5SDimitry Andric 
14940b57cec5SDimitry Andric   int count;
14950b57cec5SDimitry Andric   auto input = el_wgets(m_editline, &count);
14960b57cec5SDimitry Andric 
14970b57cec5SDimitry Andric   interrupted = m_editor_status == EditorStatus::Interrupted;
14980b57cec5SDimitry Andric   if (!interrupted) {
14990b57cec5SDimitry Andric     if (input == nullptr) {
15000b57cec5SDimitry Andric       fprintf(m_output_file, "\n");
15010b57cec5SDimitry Andric       m_editor_status = EditorStatus::EndOfInput;
15020b57cec5SDimitry Andric     } else {
15030b57cec5SDimitry Andric       m_history_sp->Enter(input);
15040b57cec5SDimitry Andric #if LLDB_EDITLINE_USE_WCHAR
15050b57cec5SDimitry Andric       line = m_utf8conv.to_bytes(SplitLines(input)[0]);
15060b57cec5SDimitry Andric #else
15070b57cec5SDimitry Andric       line = SplitLines(input)[0];
15080b57cec5SDimitry Andric #endif
15090b57cec5SDimitry Andric       m_editor_status = EditorStatus::Complete;
15100b57cec5SDimitry Andric     }
15110b57cec5SDimitry Andric   }
15120b57cec5SDimitry Andric   return m_editor_status != EditorStatus::EndOfInput;
15130b57cec5SDimitry Andric }
15140b57cec5SDimitry Andric 
15150b57cec5SDimitry Andric bool Editline::GetLines(int first_line_number, StringList &lines,
15160b57cec5SDimitry Andric                         bool &interrupted) {
15170b57cec5SDimitry Andric   ConfigureEditor(true);
15180b57cec5SDimitry Andric 
15190b57cec5SDimitry Andric   // Print the initial input lines, then move the cursor back up to the start
15200b57cec5SDimitry Andric   // of input
15210b57cec5SDimitry Andric   SetBaseLineNumber(first_line_number);
15220b57cec5SDimitry Andric   m_input_lines = std::vector<EditLineStringType>();
15230b57cec5SDimitry Andric   m_input_lines.insert(m_input_lines.begin(), EditLineConstString(""));
15240b57cec5SDimitry Andric 
152581ad6265SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
15260b57cec5SDimitry Andric   // Begin the line editing loop
15270b57cec5SDimitry Andric   DisplayInput();
15280b57cec5SDimitry Andric   SetCurrentLine(0);
15290b57cec5SDimitry Andric   MoveCursor(CursorLocation::BlockEnd, CursorLocation::BlockStart);
15300b57cec5SDimitry Andric   m_editor_status = EditorStatus::Editing;
15310b57cec5SDimitry Andric   m_in_history = false;
15320b57cec5SDimitry Andric 
15330b57cec5SDimitry Andric   m_revert_cursor_index = -1;
15340b57cec5SDimitry Andric   while (m_editor_status == EditorStatus::Editing) {
15350b57cec5SDimitry Andric     int count;
15360b57cec5SDimitry Andric     m_current_line_rows = -1;
15370b57cec5SDimitry Andric     el_wpush(m_editline, EditLineConstString(
15380b57cec5SDimitry Andric                              "\x1b[^")); // Revert to the existing line content
15390b57cec5SDimitry Andric     el_wgets(m_editline, &count);
15400b57cec5SDimitry Andric   }
15410b57cec5SDimitry Andric 
15420b57cec5SDimitry Andric   interrupted = m_editor_status == EditorStatus::Interrupted;
15430b57cec5SDimitry Andric   if (!interrupted) {
1544349cc55cSDimitry Andric     // Save the completed entry in history before returning. Don't save empty
1545349cc55cSDimitry Andric     // input as that just clutters the command history.
1546349cc55cSDimitry Andric     if (!m_input_lines.empty())
15470b57cec5SDimitry Andric       m_history_sp->Enter(CombineLines(m_input_lines).c_str());
15480b57cec5SDimitry Andric 
15490b57cec5SDimitry Andric     lines = GetInputAsStringList();
15500b57cec5SDimitry Andric   }
15510b57cec5SDimitry Andric   return m_editor_status != EditorStatus::EndOfInput;
15520b57cec5SDimitry Andric }
15530b57cec5SDimitry Andric 
15540b57cec5SDimitry Andric void Editline::PrintAsync(Stream *stream, const char *s, size_t len) {
155581ad6265SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
15560b57cec5SDimitry Andric   if (m_editor_status == EditorStatus::Editing) {
1557*0fca6ea1SDimitry Andric     SaveEditedLine();
15580b57cec5SDimitry Andric     MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
15590b57cec5SDimitry Andric     fprintf(m_output_file, ANSI_CLEAR_BELOW);
15600b57cec5SDimitry Andric   }
15610b57cec5SDimitry Andric   stream->Write(s, len);
15620b57cec5SDimitry Andric   stream->Flush();
15630b57cec5SDimitry Andric   if (m_editor_status == EditorStatus::Editing) {
15640b57cec5SDimitry Andric     DisplayInput();
15650b57cec5SDimitry Andric     MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
15660b57cec5SDimitry Andric   }
15670b57cec5SDimitry Andric }
15680b57cec5SDimitry Andric 
15690b57cec5SDimitry Andric bool Editline::CompleteCharacter(char ch, EditLineGetCharType &out) {
15700b57cec5SDimitry Andric #if !LLDB_EDITLINE_USE_WCHAR
15710b57cec5SDimitry Andric   if (ch == (char)EOF)
15720b57cec5SDimitry Andric     return false;
15730b57cec5SDimitry Andric 
15740b57cec5SDimitry Andric   out = (unsigned char)ch;
15750b57cec5SDimitry Andric   return true;
15760b57cec5SDimitry Andric #else
15770b57cec5SDimitry Andric   std::codecvt_utf8<wchar_t> cvt;
15780b57cec5SDimitry Andric   llvm::SmallString<4> input;
15790b57cec5SDimitry Andric   for (;;) {
15800b57cec5SDimitry Andric     const char *from_next;
15810b57cec5SDimitry Andric     wchar_t *to_next;
15820b57cec5SDimitry Andric     std::mbstate_t state = std::mbstate_t();
15830b57cec5SDimitry Andric     input.push_back(ch);
15840b57cec5SDimitry Andric     switch (cvt.in(state, input.begin(), input.end(), from_next, &out, &out + 1,
15850b57cec5SDimitry Andric                    to_next)) {
15860b57cec5SDimitry Andric     case std::codecvt_base::ok:
1587bdd1243dSDimitry Andric       return out != (EditLineGetCharType)WEOF;
15880b57cec5SDimitry Andric 
15890b57cec5SDimitry Andric     case std::codecvt_base::error:
15900b57cec5SDimitry Andric     case std::codecvt_base::noconv:
15910b57cec5SDimitry Andric       return false;
15920b57cec5SDimitry Andric 
15930b57cec5SDimitry Andric     case std::codecvt_base::partial:
15940b57cec5SDimitry Andric       lldb::ConnectionStatus status;
15950b57cec5SDimitry Andric       size_t read_count = m_input_connection.Read(
15960b57cec5SDimitry Andric           &ch, 1, std::chrono::seconds(0), status, nullptr);
15970b57cec5SDimitry Andric       if (read_count == 0)
15980b57cec5SDimitry Andric         return false;
15990b57cec5SDimitry Andric       break;
16000b57cec5SDimitry Andric     }
16010b57cec5SDimitry Andric   }
16020b57cec5SDimitry Andric #endif
16030b57cec5SDimitry Andric }
1604