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