diff options
author | dim <dim@FreeBSD.org> | 2017-04-02 17:24:58 +0000 |
---|---|---|
committer | dim <dim@FreeBSD.org> | 2017-04-02 17:24:58 +0000 |
commit | 60b571e49a90d38697b3aca23020d9da42fc7d7f (patch) | |
tree | 99351324c24d6cb146b6285b6caffa4d26fce188 /contrib/llvm/tools/lldb/source/Host/common/Editline.cpp | |
parent | bea1b22c7a9bce1dfdd73e6e5b65bc4752215180 (diff) | |
download | FreeBSD-src-60b571e49a90d38697b3aca23020d9da42fc7d7f.zip FreeBSD-src-60b571e49a90d38697b3aca23020d9da42fc7d7f.tar.gz |
Update clang, llvm, lld, lldb, compiler-rt and libc++ to 4.0.0 release:
MFC r309142 (by emaste):
Add WITH_LLD_AS_LD build knob
If set it installs LLD as /usr/bin/ld. LLD (as of version 3.9) is not
capable of linking the world and kernel, but can self-host and link many
substantial applications. GNU ld continues to be used for the world and
kernel build, regardless of how this knob is set.
It is on by default for arm64, and off for all other CPU architectures.
Sponsored by: The FreeBSD Foundation
MFC r310840:
Reapply 310775, now it also builds correctly if lldb is disabled:
Move llvm-objdump from CLANG_EXTRAS to installed by default
We currently install three tools from binutils 2.17.50: as, ld, and
objdump. Work is underway to migrate to a permissively-licensed
tool-chain, with one goal being the retirement of binutils 2.17.50.
LLVM's llvm-objdump is intended to be compatible with GNU objdump
although it is currently missing some options and may have formatting
differences. Enable it by default for testing and further investigation.
It may later be changed to install as /usr/bin/objdump, it becomes a
fully viable replacement.
Reviewed by: emaste
Differential Revision: https://reviews.freebsd.org/D8879
MFC r312855 (by emaste):
Rename LLD_AS_LD to LLD_IS_LD, for consistency with CLANG_IS_CC
Reported by: Dan McGregor <dan.mcgregor usask.ca>
MFC r313559 | glebius | 2017-02-10 18:34:48 +0100 (Fri, 10 Feb 2017) | 5 lines
Don't check struct rtentry on FreeBSD, it is an internal kernel structure.
On other systems it may be API structure for SIOCADDRT/SIOCDELRT.
Reviewed by: emaste, dim
MFC r314152 (by jkim):
Remove an assembler flag, which is redundant since r309124. The upstream
took care of it by introducing a macro NO_EXEC_STACK_DIRECTIVE.
http://llvm.org/viewvc/llvm-project?rev=273500&view=rev
Reviewed by: dim
MFC r314564:
Upgrade our copies of clang, llvm, lld, lldb, compiler-rt and libc++ to
4.0.0 (branches/release_40 296509). The release will follow soon.
Please note that from 3.5.0 onwards, clang, llvm and lldb require C++11
support to build; see UPDATING for more information.
Also note that as of 4.0.0, lld should be able to link the base system
on amd64 and aarch64. See the WITH_LLD_IS_LLD setting in src.conf(5).
Though please be aware that this is work in progress.
Release notes for llvm, clang and lld will be available here:
<http://releases.llvm.org/4.0.0/docs/ReleaseNotes.html>
<http://releases.llvm.org/4.0.0/tools/clang/docs/ReleaseNotes.html>
<http://releases.llvm.org/4.0.0/tools/lld/docs/ReleaseNotes.html>
Thanks to Ed Maste, Jan Beich, Antoine Brodin and Eric Fiselier for
their help.
Relnotes: yes
Exp-run: antoine
PR: 215969, 216008
MFC r314708:
For now, revert r287232 from upstream llvm trunk (by Daniil Fukalov):
[SCEV] limit recursion depth of CompareSCEVComplexity
Summary:
CompareSCEVComplexity goes too deep (50+ on a quite a big unrolled
loop) and runs almost infinite time.
Added cache of "equal" SCEV pairs to earlier cutoff of further
estimation. Recursion depth limit was also introduced as a parameter.
Reviewers: sanjoy
Subscribers: mzolotukhin, tstellarAMD, llvm-commits
Differential Revision: https://reviews.llvm.org/D26389
This commit is the cause of excessive compile times on skein_block.c
(and possibly other files) during kernel builds on amd64.
We never saw the problematic behavior described in this upstream commit,
so for now it is better to revert it. An upstream bug has been filed
here: https://bugs.llvm.org/show_bug.cgi?id=32142
Reported by: mjg
MFC r314795:
Reapply r287232 from upstream llvm trunk (by Daniil Fukalov):
[SCEV] limit recursion depth of CompareSCEVComplexity
Summary:
CompareSCEVComplexity goes too deep (50+ on a quite a big unrolled
loop) and runs almost infinite time.
Added cache of "equal" SCEV pairs to earlier cutoff of further
estimation. Recursion depth limit was also introduced as a parameter.
Reviewers: sanjoy
Subscribers: mzolotukhin, tstellarAMD, llvm-commits
Differential Revision: https://reviews.llvm.org/D26389
Pull in r296992 from upstream llvm trunk (by Sanjoy Das):
[SCEV] Decrease the recursion threshold for CompareValueComplexity
Fixes PR32142.
r287232 accidentally increased the recursion threshold for
CompareValueComplexity from 2 to 32. This change reverses that
change by introducing a separate flag for CompareValueComplexity's
threshold.
The latter revision fixes the excessive compile times for skein_block.c.
MFC r314907 | mmel | 2017-03-08 12:40:27 +0100 (Wed, 08 Mar 2017) | 7 lines
Unbreak ARMv6 world.
The new compiler_rt library imported with clang 4.0.0 have several fatal
issues (non-functional __udivsi3 for example) with ARM specific instrict
functions. As temporary workaround, until upstream solve these problems,
disable all thumb[1][2] related feature.
MFC r315016:
Update clang, llvm, lld, lldb, compiler-rt and libc++ to 4.0.0 release.
We were already very close to the last release candidate, so this is a
pretty minor update.
Relnotes: yes
MFC r316005:
Revert r314907, and pull in r298713 from upstream compiler-rt trunk (by
Weiming Zhao):
builtins: Select correct code fragments when compiling for Thumb1/Thum2/ARM ISA.
Summary:
Value of __ARM_ARCH_ISA_THUMB isn't based on the actual compilation
mode (-mthumb, -marm), it reflect's capability of given CPU.
Due to this:
- use __tbumb__ and __thumb2__ insteand of __ARM_ARCH_ISA_THUMB
- use '.thumb' directive consistently in all affected files
- decorate all thumb functions using
DEFINE_COMPILERRT_THUMB_FUNCTION()
---------
Note: This patch doesn't fix broken Thumb1 variant of __udivsi3 !
Reviewers: weimingz, rengolin, compnerd
Subscribers: aemerson, dim
Differential Revision: https://reviews.llvm.org/D30938
Discussed with: mmel
Diffstat (limited to 'contrib/llvm/tools/lldb/source/Host/common/Editline.cpp')
-rw-r--r-- | contrib/llvm/tools/lldb/source/Host/common/Editline.cpp | 2423 |
1 files changed, 1157 insertions, 1266 deletions
diff --git a/contrib/llvm/tools/lldb/source/Host/common/Editline.cpp b/contrib/llvm/tools/lldb/source/Host/common/Editline.cpp index d7209fe..1c5c0ff 100644 --- a/contrib/llvm/tools/lldb/source/Host/common/Editline.cpp +++ b/contrib/llvm/tools/lldb/source/Host/common/Editline.cpp @@ -11,15 +11,16 @@ #include <iostream> #include <limits.h> -#include "lldb/Host/Editline.h" -#include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Core/Error.h" -#include "lldb/Core/StringList.h" #include "lldb/Core/StreamString.h" +#include "lldb/Core/StringList.h" +#include "lldb/Host/ConnectionFileDescriptor.h" +#include "lldb/Host/Editline.h" #include "lldb/Host/FileSpec.h" #include "lldb/Host/FileSystem.h" #include "lldb/Host/Host.h" #include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/SelectHelper.h" using namespace lldb_private; using namespace lldb_private::line_editor; @@ -33,14 +34,17 @@ using namespace lldb_private::line_editor; // the function declaraction has been hoisted here. #if defined(__APPLE__) extern "C" { - int setupterm(char *term, int fildes, int *errret); +int setupterm(char *term, int fildes, int *errret); } #define USE_SETUPTERM_WORKAROUND #endif -// Editline uses careful cursor management to achieve the illusion of editing a multi-line block of text -// with a single line editor. Preserving this illusion requires fairly careful management of cursor -// state. Read and understand the relationship between DisplayInput(), MoveCursor(), SetCurrentLine(), +// Editline uses careful cursor management to achieve the illusion of editing a +// multi-line block of text +// with a single line editor. Preserving this illusion requires fairly careful +// management of cursor +// state. Read and understand the relationship between DisplayInput(), +// MoveCursor(), SetCurrentLine(), // and SaveEditedLine() before making changes. #define ESCAPE "\x1b" @@ -62,7 +66,8 @@ extern "C" { #define EditLineConstString(str) str #define EditLineStringFormatSpec "%s" -// use #defines so wide version functions and structs will resolve to old versions +// use #defines so wide version functions and structs will resolve to old +// versions // for case of libedit not built with wide char support #define history_w history #define history_winit history_init @@ -75,1431 +80,1317 @@ extern "C" { #define el_wgetc el_getc #define el_wpush el_push #define el_wparse el_parse -#define el_wset el_set -#define el_wget el_get +#define el_wset el_set +#define el_wget el_get #define el_wline el_line #define el_winsertstr el_insertstr -#define el_wdeletestr el_deletestr +#define el_wdeletestr el_deletestr #endif // #if LLDB_EDITLINE_USE_WCHAR -bool -IsOnlySpaces (const EditLineStringType & content) -{ - for (wchar_t ch : content) - { - if (ch != EditLineCharType(' ')) - return false; - } - return true; +bool IsOnlySpaces(const EditLineStringType &content) { + for (wchar_t ch : content) { + if (ch != EditLineCharType(' ')) + return false; + } + return true; } -EditLineStringType -CombineLines (const std::vector<EditLineStringType> & lines) -{ - EditLineStringStreamType combined_stream; - for (EditLineStringType line : lines) - { - combined_stream << line.c_str() << "\n"; - } - return combined_stream.str(); +EditLineStringType CombineLines(const std::vector<EditLineStringType> &lines) { + EditLineStringStreamType combined_stream; + for (EditLineStringType line : lines) { + combined_stream << line.c_str() << "\n"; + } + return combined_stream.str(); } -std::vector<EditLineStringType> -SplitLines (const EditLineStringType & input) -{ - std::vector<EditLineStringType> result; - size_t start = 0; - while (start < input.length()) - { - size_t end = input.find ('\n', start); - if (end == std::string::npos) - { - result.insert (result.end(), input.substr (start)); - break; - } - result.insert (result.end(), input.substr (start, end - start)); - start = end + 1; +std::vector<EditLineStringType> SplitLines(const EditLineStringType &input) { + std::vector<EditLineStringType> result; + size_t start = 0; + while (start < input.length()) { + size_t end = input.find('\n', start); + if (end == std::string::npos) { + result.insert(result.end(), input.substr(start)); + break; } - return result; + result.insert(result.end(), input.substr(start, end - start)); + start = end + 1; + } + return result; } -EditLineStringType -FixIndentation (const EditLineStringType & line, int indent_correction) -{ - if (indent_correction == 0) - return line; - if (indent_correction < 0) - return line.substr (-indent_correction); - return EditLineStringType (indent_correction, EditLineCharType(' ')) + line; +EditLineStringType FixIndentation(const EditLineStringType &line, + int indent_correction) { + if (indent_correction == 0) + return line; + if (indent_correction < 0) + return line.substr(-indent_correction); + return EditLineStringType(indent_correction, EditLineCharType(' ')) + line; } -int -GetIndentation (const EditLineStringType & line) -{ - int space_count = 0; - for (EditLineCharType ch : line) - { - if (ch != EditLineCharType(' ')) - break; - ++space_count; - } - return space_count; +int GetIndentation(const EditLineStringType &line) { + int space_count = 0; + for (EditLineCharType ch : line) { + if (ch != EditLineCharType(' ')) + break; + ++space_count; + } + return space_count; } -bool -IsInputPending (FILE * file) -{ - // FIXME: This will be broken on Windows if we ever re-enable Editline. You can't use select - // on something that isn't a socket. This will have to be re-written to not use a FILE*, but - // instead use some kind of yet-to-be-created abstraction that select-like functionality on - // non-socket objects. - const int fd = fileno (file); - fd_set fds; - FD_ZERO (&fds); - FD_SET (fd, &fds); - timeval timeout = { 0, 0 }; - return select (fd + 1, &fds, NULL, NULL, &timeout); +bool IsInputPending(FILE *file) { + // FIXME: This will be broken on Windows if we ever re-enable Editline. You + // can't use select + // on something that isn't a socket. This will have to be re-written to not + // use a FILE*, but + // instead use some kind of yet-to-be-created abstraction that select-like + // functionality on + // non-socket objects. + const int fd = fileno(file); + SelectHelper select_helper; + select_helper.SetTimeout(std::chrono::microseconds(0)); + select_helper.FDSetRead(fd); + return select_helper.Select().Success(); } -namespace lldb_private -{ - namespace line_editor - { - typedef std::weak_ptr<EditlineHistory> EditlineHistoryWP; - - // EditlineHistory objects are sometimes shared between multiple - // Editline instances with the same program name. - - class EditlineHistory - { - private: - // Use static GetHistory() function to get a EditlineHistorySP to one of these objects - EditlineHistory (const std::string &prefix, uint32_t size, bool unique_entries) : - m_history (NULL), - m_event (), - m_prefix (prefix), - m_path () - { - m_history = history_winit(); - history_w (m_history, &m_event, H_SETSIZE, size); - if (unique_entries) - history_w (m_history, &m_event, H_SETUNIQUE, 1); - } - - const char * - GetHistoryFilePath() - { - if (m_path.empty() && m_history && !m_prefix.empty()) - { - FileSpec parent_path{"~/.lldb", true}; - char history_path[PATH_MAX]; - if (FileSystem::MakeDirectory(parent_path, lldb::eFilePermissionsDirectoryDefault).Success()) - { - snprintf (history_path, sizeof (history_path), "~/.lldb/%s-history", m_prefix.c_str()); - } - else - { - snprintf (history_path, sizeof (history_path), "~/%s-widehistory", m_prefix.c_str()); - } - m_path = FileSpec (history_path, true).GetPath(); - } - if (m_path.empty()) - return NULL; - return m_path.c_str(); - } - - public: - - ~EditlineHistory() - { - Save(); - - if (m_history) - { - history_wend (m_history); - m_history = NULL; - } - } - - static EditlineHistorySP - GetHistory (const std::string &prefix) - { - typedef std::map<std::string, EditlineHistoryWP> WeakHistoryMap; - static std::recursive_mutex g_mutex; - static WeakHistoryMap g_weak_map; - std::lock_guard<std::recursive_mutex> guard(g_mutex); - WeakHistoryMap::const_iterator pos = g_weak_map.find (prefix); - EditlineHistorySP history_sp; - if (pos != g_weak_map.end()) - { - history_sp = pos->second.lock(); - if (history_sp) - return history_sp; - g_weak_map.erase (pos); - } - history_sp.reset (new EditlineHistory (prefix, 800, true)); - g_weak_map[prefix] = history_sp; - return history_sp; - } - - bool IsValid() const - { - return m_history != NULL; - } - - HistoryW * - GetHistoryPtr () - { - return m_history; - } - - void - Enter (const EditLineCharType *line_cstr) - { - if (m_history) - history_w (m_history, &m_event, H_ENTER, line_cstr); - } - - bool - Load () - { - if (m_history) - { - const char *path = GetHistoryFilePath(); - if (path) - { - history_w (m_history, &m_event, H_LOAD, path); - return true; - } - } - return false; - } - - bool - Save () - { - if (m_history) - { - const char *path = GetHistoryFilePath(); - if (path) - { - history_w (m_history, &m_event, H_SAVE, path); - return true; - } - } - return false; - } - - protected: - HistoryW * m_history; // The history object - HistEventW m_event; // The history event needed to contain all history events - std::string m_prefix; // The prefix name (usually the editline program name) to use when loading/saving history - std::string m_path; // Path to the history file - }; +namespace lldb_private { +namespace line_editor { +typedef std::weak_ptr<EditlineHistory> EditlineHistoryWP; + +// EditlineHistory objects are sometimes shared between multiple +// Editline instances with the same program name. + +class EditlineHistory { +private: + // Use static GetHistory() function to get a EditlineHistorySP to one of these + // objects + EditlineHistory(const std::string &prefix, uint32_t size, bool unique_entries) + : m_history(NULL), m_event(), m_prefix(prefix), m_path() { + m_history = history_winit(); + history_w(m_history, &m_event, H_SETSIZE, size); + if (unique_entries) + history_w(m_history, &m_event, H_SETUNIQUE, 1); + } + + const char *GetHistoryFilePath() { + if (m_path.empty() && m_history && !m_prefix.empty()) { + FileSpec parent_path{"~/.lldb", true}; + char history_path[PATH_MAX]; + if (FileSystem::MakeDirectory(parent_path, + lldb::eFilePermissionsDirectoryDefault) + .Success()) { + snprintf(history_path, sizeof(history_path), "~/.lldb/%s-history", + m_prefix.c_str()); + } else { + snprintf(history_path, sizeof(history_path), "~/%s-widehistory", + m_prefix.c_str()); + } + m_path = FileSpec(history_path, true).GetPath(); + } + if (m_path.empty()) + return NULL; + return m_path.c_str(); + } + +public: + ~EditlineHistory() { + Save(); + + if (m_history) { + history_wend(m_history); + m_history = NULL; + } + } + + static EditlineHistorySP GetHistory(const std::string &prefix) { + typedef std::map<std::string, EditlineHistoryWP> WeakHistoryMap; + static std::recursive_mutex g_mutex; + static WeakHistoryMap g_weak_map; + std::lock_guard<std::recursive_mutex> guard(g_mutex); + WeakHistoryMap::const_iterator pos = g_weak_map.find(prefix); + EditlineHistorySP history_sp; + if (pos != g_weak_map.end()) { + history_sp = pos->second.lock(); + if (history_sp) + return history_sp; + g_weak_map.erase(pos); + } + history_sp.reset(new EditlineHistory(prefix, 800, true)); + g_weak_map[prefix] = history_sp; + return history_sp; + } + + bool IsValid() const { return m_history != NULL; } + + HistoryW *GetHistoryPtr() { return m_history; } + + void Enter(const EditLineCharType *line_cstr) { + if (m_history) + history_w(m_history, &m_event, H_ENTER, line_cstr); + } + + bool Load() { + if (m_history) { + const char *path = GetHistoryFilePath(); + if (path) { + history_w(m_history, &m_event, H_LOAD, path); + return true; + } } + return false; + } + + bool Save() { + if (m_history) { + const char *path = GetHistoryFilePath(); + if (path) { + history_w(m_history, &m_event, H_SAVE, path); + return true; + } + } + return false; + } + +protected: + HistoryW *m_history; // The history object + HistEventW m_event; // The history event needed to contain all history events + std::string m_prefix; // The prefix name (usually the editline program name) + // to use when loading/saving history + std::string m_path; // Path to the history file +}; +} } //------------------------------------------------------------------ // Editline private methods //------------------------------------------------------------------ -void -Editline::SetBaseLineNumber (int line_number) -{ - std::stringstream line_number_stream; - line_number_stream << line_number; - m_base_line_number = line_number; - m_line_number_digits = std::max (3, (int)line_number_stream.str().length() + 1); +void Editline::SetBaseLineNumber(int line_number) { + std::stringstream line_number_stream; + line_number_stream << line_number; + m_base_line_number = line_number; + m_line_number_digits = + std::max(3, (int)line_number_stream.str().length() + 1); } -std::string -Editline::PromptForIndex (int line_index) -{ - bool use_line_numbers = m_multiline_enabled && m_base_line_number > 0; - std::string prompt = m_set_prompt; - if (use_line_numbers && prompt.length() == 0) - { - prompt = ": "; +std::string Editline::PromptForIndex(int line_index) { + bool use_line_numbers = m_multiline_enabled && m_base_line_number > 0; + std::string prompt = m_set_prompt; + if (use_line_numbers && prompt.length() == 0) { + prompt = ": "; + } + std::string continuation_prompt = prompt; + if (m_set_continuation_prompt.length() > 0) { + continuation_prompt = m_set_continuation_prompt; + + // Ensure that both prompts are the same length through space padding + while (continuation_prompt.length() < prompt.length()) { + continuation_prompt += ' '; } - std::string continuation_prompt = prompt; - if (m_set_continuation_prompt.length() > 0) - { - continuation_prompt = m_set_continuation_prompt; - - // Ensure that both prompts are the same length through space padding - while (continuation_prompt.length() < prompt.length()) - { - continuation_prompt += ' '; - } - while (prompt.length() < continuation_prompt.length()) - { - prompt += ' '; - } + while (prompt.length() < continuation_prompt.length()) { + prompt += ' '; } - - if (use_line_numbers) - { - StreamString prompt_stream; - prompt_stream.Printf("%*d%s", m_line_number_digits, m_base_line_number + line_index, - (line_index == 0) ? prompt.c_str() : continuation_prompt.c_str()); - return std::move (prompt_stream.GetString()); - } - return (line_index == 0) ? prompt : continuation_prompt; + } + + if (use_line_numbers) { + StreamString prompt_stream; + prompt_stream.Printf( + "%*d%s", m_line_number_digits, m_base_line_number + line_index, + (line_index == 0) ? prompt.c_str() : continuation_prompt.c_str()); + return std::move(prompt_stream.GetString()); + } + return (line_index == 0) ? prompt : continuation_prompt; } -void -Editline::SetCurrentLine (int line_index) -{ - m_current_line_index = line_index; - m_current_prompt = PromptForIndex (line_index); +void Editline::SetCurrentLine(int line_index) { + m_current_line_index = line_index; + m_current_prompt = PromptForIndex(line_index); } -int -Editline::GetPromptWidth() -{ - return (int)PromptForIndex (0).length(); -} +int Editline::GetPromptWidth() { return (int)PromptForIndex(0).length(); } -bool -Editline::IsEmacs() -{ - const char * editor; - el_get (m_editline, EL_EDITOR, &editor); - return editor[0] == 'e'; +bool Editline::IsEmacs() { + const char *editor; + el_get(m_editline, EL_EDITOR, &editor); + return editor[0] == 'e'; } -bool -Editline::IsOnlySpaces() -{ - const LineInfoW * info = el_wline (m_editline); - for (const EditLineCharType * character = info->buffer; character < info->lastchar; character++) - { - if (*character != ' ') - return false; - } - return true; -} - -int -Editline::GetLineIndexForLocation (CursorLocation location, int cursor_row) -{ - int line = 0; - if (location == CursorLocation::EditingPrompt || location == CursorLocation::BlockEnd || - location == CursorLocation::EditingCursor) - { - for (unsigned index = 0; index < m_current_line_index; index++) - { - line += CountRowsForLine (m_input_lines[index]); - } - if (location == CursorLocation::EditingCursor) - { - line += cursor_row; - } - else if (location == CursorLocation::BlockEnd) - { - for (unsigned index = m_current_line_index; index < m_input_lines.size(); index++) - { - line += CountRowsForLine (m_input_lines[index]); - } - --line; - } - } - return line; +bool Editline::IsOnlySpaces() { + const LineInfoW *info = el_wline(m_editline); + for (const EditLineCharType *character = info->buffer; + character < info->lastchar; character++) { + if (*character != ' ') + return false; + } + return true; } -void -Editline::MoveCursor (CursorLocation from, CursorLocation to) -{ - const LineInfoW * info = el_wline (m_editline); - int editline_cursor_position = (int)((info->cursor - info->buffer) + GetPromptWidth()); - int editline_cursor_row = editline_cursor_position / m_terminal_width; - - // Determine relative starting and ending lines - int fromLine = GetLineIndexForLocation (from, editline_cursor_row); - int toLine = GetLineIndexForLocation (to, editline_cursor_row); - if (toLine != fromLine) - { - fprintf (m_output_file, (toLine > fromLine) ? ANSI_DOWN_N_ROWS : ANSI_UP_N_ROWS, std::abs (toLine - fromLine)); +int Editline::GetLineIndexForLocation(CursorLocation location, int cursor_row) { + int line = 0; + if (location == CursorLocation::EditingPrompt || + location == CursorLocation::BlockEnd || + location == CursorLocation::EditingCursor) { + for (unsigned index = 0; index < m_current_line_index; index++) { + line += CountRowsForLine(m_input_lines[index]); } - - // Determine target column - int toColumn = 1; - if (to == CursorLocation::EditingCursor) - { - toColumn = editline_cursor_position - (editline_cursor_row * m_terminal_width) + 1; + if (location == CursorLocation::EditingCursor) { + line += cursor_row; + } else if (location == CursorLocation::BlockEnd) { + for (unsigned index = m_current_line_index; index < m_input_lines.size(); + index++) { + line += CountRowsForLine(m_input_lines[index]); + } + --line; } - else if (to == CursorLocation::BlockEnd) - { - toColumn = ((m_input_lines[m_input_lines.size() - 1].length() + GetPromptWidth()) % 80) + 1; - } - fprintf (m_output_file, ANSI_SET_COLUMN_N, toColumn); + } + return line; } -void -Editline::DisplayInput (int firstIndex) -{ - fprintf (m_output_file, ANSI_SET_COLUMN_N ANSI_CLEAR_BELOW, 1); - int line_count = (int)m_input_lines.size(); - const char *faint = m_color_prompts ? ANSI_FAINT : ""; - const char *unfaint = m_color_prompts ? ANSI_UNFAINT : ""; - - for (int index = firstIndex; index < line_count; index++) - { - fprintf (m_output_file, "%s" "%s" "%s" EditLineStringFormatSpec " ", - faint, - PromptForIndex (index).c_str(), - unfaint, - m_input_lines[index].c_str()); - if (index < line_count - 1) - fprintf (m_output_file, "\n"); - } +void Editline::MoveCursor(CursorLocation from, CursorLocation to) { + const LineInfoW *info = el_wline(m_editline); + int editline_cursor_position = + (int)((info->cursor - info->buffer) + GetPromptWidth()); + int editline_cursor_row = editline_cursor_position / m_terminal_width; + + // Determine relative starting and ending lines + int fromLine = GetLineIndexForLocation(from, editline_cursor_row); + int toLine = GetLineIndexForLocation(to, editline_cursor_row); + if (toLine != fromLine) { + fprintf(m_output_file, + (toLine > fromLine) ? ANSI_DOWN_N_ROWS : ANSI_UP_N_ROWS, + std::abs(toLine - fromLine)); + } + + // Determine target column + int toColumn = 1; + if (to == CursorLocation::EditingCursor) { + toColumn = + editline_cursor_position - (editline_cursor_row * m_terminal_width) + 1; + } else if (to == CursorLocation::BlockEnd) { + toColumn = + ((m_input_lines[m_input_lines.size() - 1].length() + GetPromptWidth()) % + 80) + + 1; + } + fprintf(m_output_file, ANSI_SET_COLUMN_N, toColumn); } +void Editline::DisplayInput(int firstIndex) { + fprintf(m_output_file, ANSI_SET_COLUMN_N ANSI_CLEAR_BELOW, 1); + int line_count = (int)m_input_lines.size(); + const char *faint = m_color_prompts ? ANSI_FAINT : ""; + const char *unfaint = m_color_prompts ? ANSI_UNFAINT : ""; + + for (int index = firstIndex; index < line_count; index++) { + fprintf(m_output_file, "%s" + "%s" + "%s" EditLineStringFormatSpec " ", + faint, PromptForIndex(index).c_str(), unfaint, + m_input_lines[index].c_str()); + if (index < line_count - 1) + fprintf(m_output_file, "\n"); + } +} -int -Editline::CountRowsForLine (const EditLineStringType & content) -{ - auto prompt = PromptForIndex (0); // Prompt width is constant during an edit session - int line_length = (int)(content.length() + prompt.length()); - return (line_length / m_terminal_width) + 1; +int Editline::CountRowsForLine(const EditLineStringType &content) { + auto prompt = + PromptForIndex(0); // Prompt width is constant during an edit session + int line_length = (int)(content.length() + prompt.length()); + return (line_length / m_terminal_width) + 1; } -void -Editline::SaveEditedLine() -{ - const LineInfoW * info = el_wline (m_editline); - m_input_lines[m_current_line_index] = EditLineStringType (info->buffer, info->lastchar - info->buffer); +void Editline::SaveEditedLine() { + const LineInfoW *info = el_wline(m_editline); + m_input_lines[m_current_line_index] = + EditLineStringType(info->buffer, info->lastchar - info->buffer); } -StringList -Editline::GetInputAsStringList(int line_count) -{ - StringList lines; - for (EditLineStringType line : m_input_lines) - { - if (line_count == 0) - break; +StringList Editline::GetInputAsStringList(int line_count) { + StringList lines; + for (EditLineStringType line : m_input_lines) { + if (line_count == 0) + break; #if LLDB_EDITLINE_USE_WCHAR - lines.AppendString (m_utf8conv.to_bytes (line)); + lines.AppendString(m_utf8conv.to_bytes(line)); #else - lines.AppendString(line); + lines.AppendString(line); #endif - --line_count; - } - return lines; + --line_count; + } + return lines; } -unsigned char -Editline::RecallHistory (bool earlier) -{ - if (!m_history_sp || !m_history_sp->IsValid()) +unsigned char Editline::RecallHistory(bool earlier) { + if (!m_history_sp || !m_history_sp->IsValid()) + return CC_ERROR; + + HistoryW *pHistory = m_history_sp->GetHistoryPtr(); + HistEventW history_event; + std::vector<EditLineStringType> new_input_lines; + + // Treat moving from the "live" entry differently + if (!m_in_history) { + if (earlier == false) + return CC_ERROR; // Can't go newer than the "live" entry + if (history_w(pHistory, &history_event, H_FIRST) == -1) + return CC_ERROR; + + // Save any edits to the "live" entry in case we return by moving forward in + // history + // (it would be more bash-like to save over any current entry, but libedit + // doesn't + // offer the ability to add entries anywhere except the end.) + SaveEditedLine(); + m_live_history_lines = m_input_lines; + m_in_history = true; + } else { + if (history_w(pHistory, &history_event, earlier ? H_NEXT : H_PREV) == -1) { + // Can't move earlier than the earliest entry + if (earlier) return CC_ERROR; - - HistoryW * pHistory = m_history_sp->GetHistoryPtr(); - HistEventW history_event; - std::vector<EditLineStringType> new_input_lines; - - // Treat moving from the "live" entry differently - if (!m_in_history) - { - if (earlier == false) - return CC_ERROR; // Can't go newer than the "live" entry - if (history_w (pHistory, &history_event, H_FIRST) == -1) - return CC_ERROR; - - // Save any edits to the "live" entry in case we return by moving forward in history - // (it would be more bash-like to save over any current entry, but libedit doesn't - // offer the ability to add entries anywhere except the end.) - SaveEditedLine(); - m_live_history_lines = m_input_lines; - m_in_history = true; - } - else - { - if (history_w (pHistory, &history_event, earlier ? H_NEXT : H_PREV) == -1) - { - // Can't move earlier than the earliest entry - if (earlier) - return CC_ERROR; - - // ... but moving to newer than the newest yields the "live" entry - new_input_lines = m_live_history_lines; - m_in_history = false; - } + + // ... but moving to newer than the newest yields the "live" entry + new_input_lines = m_live_history_lines; + m_in_history = false; } - - // If we're pulling the lines from history, split them apart - if (m_in_history) - new_input_lines = SplitLines (history_event.str); - - // Erase the current edit session and replace it with a new one - MoveCursor (CursorLocation::EditingCursor, CursorLocation::BlockStart); - m_input_lines = new_input_lines; - DisplayInput(); - - // Prepare to edit the last line when moving to previous entry, or the first line - // when moving to next entry - SetCurrentLine (m_current_line_index = earlier ? (int)m_input_lines.size() - 1 : 0); - MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingPrompt); - return CC_NEWLINE; + } + + // If we're pulling the lines from history, split them apart + if (m_in_history) + new_input_lines = SplitLines(history_event.str); + + // Erase the current edit session and replace it with a new one + MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart); + m_input_lines = new_input_lines; + DisplayInput(); + + // Prepare to edit the last line when moving to previous entry, or the first + // line + // when moving to next entry + SetCurrentLine(m_current_line_index = + earlier ? (int)m_input_lines.size() - 1 : 0); + MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt); + return CC_NEWLINE; } -int -Editline::GetCharacter (EditLineCharType * c) -{ - const LineInfoW * info = el_wline (m_editline); - - // Paint a faint version of the desired prompt over the version libedit draws - // (will only be requested if colors are supported) - if (m_needs_prompt_repaint) - { - MoveCursor (CursorLocation::EditingCursor, CursorLocation::EditingPrompt); - fprintf (m_output_file, "%s" "%s" "%s", ANSI_FAINT, Prompt(), ANSI_UNFAINT); - MoveCursor (CursorLocation::EditingPrompt, CursorLocation::EditingCursor); - m_needs_prompt_repaint = false; +int Editline::GetCharacter(EditLineCharType *c) { + const LineInfoW *info = el_wline(m_editline); + + // Paint a faint version of the desired prompt over the version libedit draws + // (will only be requested if colors are supported) + if (m_needs_prompt_repaint) { + MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt); + fprintf(m_output_file, "%s" + "%s" + "%s", + ANSI_FAINT, Prompt(), ANSI_UNFAINT); + MoveCursor(CursorLocation::EditingPrompt, CursorLocation::EditingCursor); + m_needs_prompt_repaint = false; + } + + if (m_multiline_enabled) { + // Detect when the number of rows used for this input line changes due to an + // edit + int lineLength = (int)((info->lastchar - info->buffer) + GetPromptWidth()); + int new_line_rows = (lineLength / m_terminal_width) + 1; + if (m_current_line_rows != -1 && new_line_rows != m_current_line_rows) { + // Respond by repainting the current state from this line on + MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt); + SaveEditedLine(); + DisplayInput(m_current_line_index); + MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor); } - - if (m_multiline_enabled) - { - // Detect when the number of rows used for this input line changes due to an edit - int lineLength = (int)((info->lastchar - info->buffer) + GetPromptWidth()); - int new_line_rows = (lineLength / m_terminal_width) + 1; - if (m_current_line_rows != -1 && new_line_rows != m_current_line_rows) - { - // Respond by repainting the current state from this line on - MoveCursor (CursorLocation::EditingCursor, CursorLocation::EditingPrompt); - SaveEditedLine(); - DisplayInput (m_current_line_index); - MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingCursor); - } - m_current_line_rows = new_line_rows; + m_current_line_rows = new_line_rows; + } + + // Read an actual character + while (true) { + lldb::ConnectionStatus status = lldb::eConnectionStatusSuccess; + char ch = 0; + + // This mutex is locked by our caller (GetLine). Unlock it while we read a + // character + // (blocking operation), so we do not hold the mutex indefinitely. This + // gives a chance + // for someone to interrupt us. After Read returns, immediately lock the + // mutex again and + // check if we were interrupted. + m_output_mutex.unlock(); + int read_count = m_input_connection.Read(&ch, 1, llvm::None, status, NULL); + m_output_mutex.lock(); + if (m_editor_status == EditorStatus::Interrupted) { + while (read_count > 0 && status == lldb::eConnectionStatusSuccess) + read_count = m_input_connection.Read(&ch, 1, llvm::None, status, NULL); + lldbassert(status == lldb::eConnectionStatusInterrupted); + return 0; } - - // Read an actual character - while (true) - { - lldb::ConnectionStatus status = lldb::eConnectionStatusSuccess; - char ch = 0; - - // This mutex is locked by our caller (GetLine). Unlock it while we read a character - // (blocking operation), so we do not hold the mutex indefinitely. This gives a chance - // for someone to interrupt us. After Read returns, immediately lock the mutex again and - // check if we were interrupted. - m_output_mutex.unlock(); - int read_count = m_input_connection.Read(&ch, 1, UINT32_MAX, status, NULL); - m_output_mutex.lock(); - if (m_editor_status == EditorStatus::Interrupted) - { - while (read_count > 0 && status == lldb::eConnectionStatusSuccess) - read_count = m_input_connection.Read(&ch, 1, UINT32_MAX, status, NULL); - lldbassert(status == lldb::eConnectionStatusInterrupted); - return 0; - } - if (read_count) - { -#if LLDB_EDITLINE_USE_WCHAR - // After the initial interruptible read, this is guaranteed not to block - ungetc (ch, m_input_file); - *c = fgetwc (m_input_file); - if (*c != WEOF) - return 1; -#else - *c = ch; - if(ch != (char)EOF) - return 1; -#endif - } - else - { - switch (status) - { - case lldb::eConnectionStatusSuccess: // Success - break; - - case lldb::eConnectionStatusInterrupted: - lldbassert(0 && "Interrupts should have been handled above."); - - case lldb::eConnectionStatusError: // Check GetError() for details - case lldb::eConnectionStatusTimedOut: // Request timed out - case lldb::eConnectionStatusEndOfFile: // End-of-file encountered - case lldb::eConnectionStatusNoConnection: // No connection - case lldb::eConnectionStatusLostConnection: // Lost connection while connected to a valid connection - m_editor_status = EditorStatus::EndOfInput; - return 0; - } - } + if (read_count) { + if (CompleteCharacter(ch, *c)) + return 1; + } else { + switch (status) { + case lldb::eConnectionStatusSuccess: // Success + break; + + case lldb::eConnectionStatusInterrupted: + lldbassert(0 && "Interrupts should have been handled above."); + + case lldb::eConnectionStatusError: // Check GetError() for details + case lldb::eConnectionStatusTimedOut: // Request timed out + case lldb::eConnectionStatusEndOfFile: // End-of-file encountered + case lldb::eConnectionStatusNoConnection: // No connection + case lldb::eConnectionStatusLostConnection: // Lost connection while + // connected to a valid + // connection + m_editor_status = EditorStatus::EndOfInput; + return 0; + } } + } } -const char * -Editline::Prompt() -{ - if (m_color_prompts) - m_needs_prompt_repaint = true; - return m_current_prompt.c_str(); +const char *Editline::Prompt() { + if (m_color_prompts) + m_needs_prompt_repaint = true; + return m_current_prompt.c_str(); } -unsigned char -Editline::BreakLineCommand (int ch) -{ - // Preserve any content beyond the cursor, truncate and save the current line - const LineInfoW * info = el_wline (m_editline); - auto current_line = EditLineStringType (info->buffer, info->cursor - info->buffer); - auto new_line_fragment = EditLineStringType (info->cursor, info->lastchar - info->cursor); - m_input_lines[m_current_line_index] = current_line; - - // Ignore whitespace-only extra fragments when breaking a line - if (::IsOnlySpaces (new_line_fragment)) - new_line_fragment = EditLineConstString(""); - - // Establish the new cursor position at the start of a line when inserting a line break - m_revert_cursor_index = 0; - - // Don't perform automatic formatting when pasting - if (!IsInputPending (m_input_file)) - { - // Apply smart indentation - if (m_fix_indentation_callback) - { - StringList lines = GetInputAsStringList (m_current_line_index + 1); +unsigned char Editline::BreakLineCommand(int ch) { + // Preserve any content beyond the cursor, truncate and save the current line + const LineInfoW *info = el_wline(m_editline); + auto current_line = + EditLineStringType(info->buffer, info->cursor - info->buffer); + auto new_line_fragment = + EditLineStringType(info->cursor, info->lastchar - info->cursor); + m_input_lines[m_current_line_index] = current_line; + + // Ignore whitespace-only extra fragments when breaking a line + if (::IsOnlySpaces(new_line_fragment)) + new_line_fragment = EditLineConstString(""); + + // Establish the new cursor position at the start of a line when inserting a + // line break + m_revert_cursor_index = 0; + + // Don't perform automatic formatting when pasting + if (!IsInputPending(m_input_file)) { + // Apply smart indentation + if (m_fix_indentation_callback) { + StringList lines = GetInputAsStringList(m_current_line_index + 1); #if LLDB_EDITLINE_USE_WCHAR - lines.AppendString (m_utf8conv.to_bytes (new_line_fragment)); + lines.AppendString(m_utf8conv.to_bytes(new_line_fragment)); #else - lines.AppendString (new_line_fragment); + lines.AppendString(new_line_fragment); #endif - - int indent_correction = m_fix_indentation_callback (this, lines, 0, m_fix_indentation_callback_baton); - new_line_fragment = FixIndentation(new_line_fragment, indent_correction); - m_revert_cursor_index = GetIndentation(new_line_fragment); - } + + int indent_correction = m_fix_indentation_callback( + this, lines, 0, m_fix_indentation_callback_baton); + new_line_fragment = FixIndentation(new_line_fragment, indent_correction); + m_revert_cursor_index = GetIndentation(new_line_fragment); } - - // Insert the new line and repaint everything from the split line on down - m_input_lines.insert (m_input_lines.begin() + m_current_line_index + 1, new_line_fragment); - MoveCursor (CursorLocation::EditingCursor, CursorLocation::EditingPrompt); - DisplayInput (m_current_line_index); - - // Reposition the cursor to the right line and prepare to edit the new line - SetCurrentLine (m_current_line_index + 1); - MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingPrompt); - return CC_NEWLINE; + } + + // Insert the new line and repaint everything from the split line on down + m_input_lines.insert(m_input_lines.begin() + m_current_line_index + 1, + new_line_fragment); + MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt); + DisplayInput(m_current_line_index); + + // Reposition the cursor to the right line and prepare to edit the new line + SetCurrentLine(m_current_line_index + 1); + MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt); + return CC_NEWLINE; } -unsigned char -Editline::EndOrAddLineCommand(int ch) -{ - // Don't perform end of input detection when pasting, always treat this as a line break - if (IsInputPending(m_input_file)) - { +unsigned char Editline::EndOrAddLineCommand(int ch) { + // Don't perform end of input detection when pasting, always treat this as a + // line break + if (IsInputPending(m_input_file)) { + return BreakLineCommand(ch); + } + + // Save any edits to this line + SaveEditedLine(); + + // If this is the end of the last line, consider whether to add a line instead + const LineInfoW *info = el_wline(m_editline); + if (m_current_line_index == m_input_lines.size() - 1 && + info->cursor == info->lastchar) { + if (m_is_input_complete_callback) { + auto lines = GetInputAsStringList(); + if (!m_is_input_complete_callback(this, lines, + m_is_input_complete_callback_baton)) { return BreakLineCommand(ch); - } + } - // Save any edits to this line - SaveEditedLine(); - - // If this is the end of the last line, consider whether to add a line instead - const LineInfoW *info = el_wline(m_editline); - if (m_current_line_index == m_input_lines.size() - 1 && info->cursor == info->lastchar) - { - if (m_is_input_complete_callback) - { - auto lines = GetInputAsStringList(); - if (!m_is_input_complete_callback(this, lines, m_is_input_complete_callback_baton)) - { - return BreakLineCommand(ch); - } - - // The completion test is allowed to change the input lines when complete - m_input_lines.clear(); - for (unsigned index = 0; index < lines.GetSize(); index++) - { + // The completion test is allowed to change the input lines when complete + m_input_lines.clear(); + for (unsigned index = 0; index < lines.GetSize(); index++) { #if LLDB_EDITLINE_USE_WCHAR - m_input_lines.insert(m_input_lines.end(), m_utf8conv.from_bytes(lines[index])); + m_input_lines.insert(m_input_lines.end(), + m_utf8conv.from_bytes(lines[index])); #else - m_input_lines.insert(m_input_lines.end(), lines[index]); + m_input_lines.insert(m_input_lines.end(), lines[index]); #endif - } - } + } } - MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockEnd); - fprintf(m_output_file, "\n"); - m_editor_status = EditorStatus::Complete; - return CC_NEWLINE; + } + MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockEnd); + fprintf(m_output_file, "\n"); + m_editor_status = EditorStatus::Complete; + return CC_NEWLINE; } -unsigned char -Editline::DeleteNextCharCommand(int ch) -{ - LineInfoW * info = const_cast<LineInfoW *>(el_wline (m_editline)); - - // Just delete the next character normally if possible - if (info->cursor < info->lastchar) - { - info->cursor++; - el_deletestr (m_editline, 1); - return CC_REFRESH; - } +unsigned char Editline::DeleteNextCharCommand(int ch) { + LineInfoW *info = const_cast<LineInfoW *>(el_wline(m_editline)); - // Fail when at the end of the last line, except when ^D is pressed on - // the line is empty, in which case it is treated as EOF - if (m_current_line_index == m_input_lines.size() - 1) - { - if (ch == 4 && info->buffer == info->lastchar) - { - fprintf (m_output_file, "^D\n"); - m_editor_status = EditorStatus::EndOfInput; - return CC_EOF; - } - return CC_ERROR; - } - - // Prepare to combine this line with the one below - MoveCursor (CursorLocation::EditingCursor, CursorLocation::EditingPrompt); - - // Insert the next line of text at the cursor and restore the cursor position - const EditLineCharType * cursor = info->cursor; - el_winsertstr (m_editline, m_input_lines[m_current_line_index + 1].c_str()); - info->cursor = cursor; - SaveEditedLine(); - - // Delete the extra line - m_input_lines.erase (m_input_lines.begin() + m_current_line_index + 1); - - // Clear and repaint from this line on down - DisplayInput (m_current_line_index); - MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingCursor); + // Just delete the next character normally if possible + if (info->cursor < info->lastchar) { + info->cursor++; + el_deletestr(m_editline, 1); return CC_REFRESH; + } + + // Fail when at the end of the last line, except when ^D is pressed on + // the line is empty, in which case it is treated as EOF + if (m_current_line_index == m_input_lines.size() - 1) { + if (ch == 4 && info->buffer == info->lastchar) { + fprintf(m_output_file, "^D\n"); + m_editor_status = EditorStatus::EndOfInput; + return CC_EOF; + } + return CC_ERROR; + } + + // Prepare to combine this line with the one below + MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt); + + // Insert the next line of text at the cursor and restore the cursor position + const EditLineCharType *cursor = info->cursor; + el_winsertstr(m_editline, m_input_lines[m_current_line_index + 1].c_str()); + info->cursor = cursor; + SaveEditedLine(); + + // Delete the extra line + m_input_lines.erase(m_input_lines.begin() + m_current_line_index + 1); + + // Clear and repaint from this line on down + DisplayInput(m_current_line_index); + MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor); + return CC_REFRESH; } -unsigned char -Editline::DeletePreviousCharCommand (int ch) -{ - LineInfoW * info = const_cast<LineInfoW *>(el_wline (m_editline)); - - // Just delete the previous character normally when not at the start of a line - if (info->cursor > info->buffer) - { - el_deletestr (m_editline, 1); - return CC_REFRESH; - } - - // No prior line and no prior character? Let the user know - if (m_current_line_index == 0) - return CC_ERROR; - - // No prior character, but prior line? Combine with the line above - SaveEditedLine(); - SetCurrentLine (m_current_line_index - 1); - auto priorLine = m_input_lines[m_current_line_index]; - m_input_lines.erase (m_input_lines.begin() + m_current_line_index); - m_input_lines[m_current_line_index] = priorLine + m_input_lines[m_current_line_index]; - - // Repaint from the new line down - fprintf (m_output_file, ANSI_UP_N_ROWS ANSI_SET_COLUMN_N, CountRowsForLine (priorLine), 1); - DisplayInput (m_current_line_index); - - // Put the cursor back where libedit expects it to be before returning to editing - // by telling libedit about the newly inserted text - MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingPrompt); - el_winsertstr (m_editline, priorLine.c_str()); - return CC_REDISPLAY; +unsigned char Editline::DeletePreviousCharCommand(int ch) { + LineInfoW *info = const_cast<LineInfoW *>(el_wline(m_editline)); + + // Just delete the previous character normally when not at the start of a line + if (info->cursor > info->buffer) { + el_deletestr(m_editline, 1); + return CC_REFRESH; + } + + // No prior line and no prior character? Let the user know + if (m_current_line_index == 0) + return CC_ERROR; + + // No prior character, but prior line? Combine with the line above + SaveEditedLine(); + SetCurrentLine(m_current_line_index - 1); + auto priorLine = m_input_lines[m_current_line_index]; + m_input_lines.erase(m_input_lines.begin() + m_current_line_index); + m_input_lines[m_current_line_index] = + priorLine + m_input_lines[m_current_line_index]; + + // Repaint from the new line down + fprintf(m_output_file, ANSI_UP_N_ROWS ANSI_SET_COLUMN_N, + CountRowsForLine(priorLine), 1); + DisplayInput(m_current_line_index); + + // Put the cursor back where libedit expects it to be before returning to + // editing + // by telling libedit about the newly inserted text + MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt); + el_winsertstr(m_editline, priorLine.c_str()); + return CC_REDISPLAY; } -unsigned char -Editline::PreviousLineCommand (int ch) -{ - SaveEditedLine(); +unsigned char Editline::PreviousLineCommand(int ch) { + SaveEditedLine(); - if (m_current_line_index == 0) { - return RecallHistory (true); - } - - // Start from a known location - MoveCursor (CursorLocation::EditingCursor, CursorLocation::EditingPrompt); - - // Treat moving up from a blank last line as a deletion of that line - if (m_current_line_index == m_input_lines.size() - 1 && IsOnlySpaces()) - { - m_input_lines.erase (m_input_lines.begin() + m_current_line_index); - fprintf (m_output_file, ANSI_CLEAR_BELOW); - } - - SetCurrentLine (m_current_line_index - 1); - fprintf (m_output_file, ANSI_UP_N_ROWS ANSI_SET_COLUMN_N, - CountRowsForLine (m_input_lines[m_current_line_index]), 1); - return CC_NEWLINE; + if (m_current_line_index == 0) { + return RecallHistory(true); + } + + // Start from a known location + MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt); + + // Treat moving up from a blank last line as a deletion of that line + if (m_current_line_index == m_input_lines.size() - 1 && IsOnlySpaces()) { + m_input_lines.erase(m_input_lines.begin() + m_current_line_index); + fprintf(m_output_file, ANSI_CLEAR_BELOW); + } + + SetCurrentLine(m_current_line_index - 1); + fprintf(m_output_file, ANSI_UP_N_ROWS ANSI_SET_COLUMN_N, + CountRowsForLine(m_input_lines[m_current_line_index]), 1); + return CC_NEWLINE; } -unsigned char -Editline::NextLineCommand (int ch) -{ - SaveEditedLine(); +unsigned char Editline::NextLineCommand(int ch) { + SaveEditedLine(); - // Handle attempts to move down from the last line - if (m_current_line_index == m_input_lines.size() - 1) - { - // Don't add an extra line if the existing last line is blank, move through history instead - if (IsOnlySpaces()) - { - return RecallHistory (false); - } - - // Determine indentation for the new line - int indentation = 0; - if (m_fix_indentation_callback) - { - StringList lines = GetInputAsStringList(); - lines.AppendString(""); - indentation = m_fix_indentation_callback (this, lines, 0, m_fix_indentation_callback_baton); - } - m_input_lines.insert (m_input_lines.end(), EditLineStringType (indentation, EditLineCharType(' '))); + // Handle attempts to move down from the last line + if (m_current_line_index == m_input_lines.size() - 1) { + // Don't add an extra line if the existing last line is blank, move through + // history instead + if (IsOnlySpaces()) { + return RecallHistory(false); } - - // Move down past the current line using newlines to force scrolling if needed - SetCurrentLine (m_current_line_index + 1); - const LineInfoW * info = el_wline (m_editline); - int cursor_position = (int)((info->cursor - info->buffer) + GetPromptWidth()); - int cursor_row = cursor_position / m_terminal_width; - for (int line_count = 0; line_count < m_current_line_rows - cursor_row; line_count++) - { - fprintf (m_output_file, "\n"); + + // Determine indentation for the new line + int indentation = 0; + if (m_fix_indentation_callback) { + StringList lines = GetInputAsStringList(); + lines.AppendString(""); + indentation = m_fix_indentation_callback( + this, lines, 0, m_fix_indentation_callback_baton); } - return CC_NEWLINE; + m_input_lines.insert( + m_input_lines.end(), + EditLineStringType(indentation, EditLineCharType(' '))); + } + + // Move down past the current line using newlines to force scrolling if needed + SetCurrentLine(m_current_line_index + 1); + const LineInfoW *info = el_wline(m_editline); + int cursor_position = (int)((info->cursor - info->buffer) + GetPromptWidth()); + int cursor_row = cursor_position / m_terminal_width; + for (int line_count = 0; line_count < m_current_line_rows - cursor_row; + line_count++) { + fprintf(m_output_file, "\n"); + } + return CC_NEWLINE; } -unsigned char -Editline::PreviousHistoryCommand(int ch) -{ - SaveEditedLine(); +unsigned char Editline::PreviousHistoryCommand(int ch) { + SaveEditedLine(); - return RecallHistory(true); + return RecallHistory(true); } -unsigned char -Editline::NextHistoryCommand(int ch) -{ - SaveEditedLine(); +unsigned char Editline::NextHistoryCommand(int ch) { + SaveEditedLine(); - return RecallHistory(false); + return RecallHistory(false); } -unsigned char -Editline::FixIndentationCommand(int ch) -{ - if (!m_fix_indentation_callback) - return CC_NORM; +unsigned char Editline::FixIndentationCommand(int ch) { + if (!m_fix_indentation_callback) + return CC_NORM; - // Insert the character typed before proceeding - EditLineCharType inserted[] = { (EditLineCharType)ch, 0 }; - el_winsertstr (m_editline, inserted); - LineInfoW * info = const_cast<LineInfoW *>(el_wline (m_editline)); - int cursor_position = info->cursor - info->buffer; + // Insert the character typed before proceeding + EditLineCharType inserted[] = {(EditLineCharType)ch, 0}; + el_winsertstr(m_editline, inserted); + LineInfoW *info = const_cast<LineInfoW *>(el_wline(m_editline)); + int cursor_position = info->cursor - info->buffer; - // Save the edits and determine the correct indentation level - SaveEditedLine(); - StringList lines = GetInputAsStringList (m_current_line_index + 1); - int indent_correction = m_fix_indentation_callback (this, lines, cursor_position, m_fix_indentation_callback_baton); - - // If it is already correct no special work is needed - if (indent_correction == 0) - return CC_REFRESH; - - // Change the indentation level of the line - std::string currentLine = lines.GetStringAtIndex (m_current_line_index); - if (indent_correction > 0) - { - currentLine = currentLine.insert (0, indent_correction, ' '); - } - else - { - currentLine = currentLine.erase (0, -indent_correction); - } + // Save the edits and determine the correct indentation level + SaveEditedLine(); + StringList lines = GetInputAsStringList(m_current_line_index + 1); + int indent_correction = m_fix_indentation_callback( + this, lines, cursor_position, m_fix_indentation_callback_baton); + + // If it is already correct no special work is needed + if (indent_correction == 0) + return CC_REFRESH; + + // Change the indentation level of the line + std::string currentLine = lines.GetStringAtIndex(m_current_line_index); + if (indent_correction > 0) { + currentLine = currentLine.insert(0, indent_correction, ' '); + } else { + currentLine = currentLine.erase(0, -indent_correction); + } #if LLDB_EDITLINE_USE_WCHAR - m_input_lines[m_current_line_index] = m_utf8conv.from_bytes (currentLine); + m_input_lines[m_current_line_index] = m_utf8conv.from_bytes(currentLine); #else - m_input_lines[m_current_line_index] = currentLine; + m_input_lines[m_current_line_index] = currentLine; #endif - // Update the display to reflect the change - MoveCursor (CursorLocation::EditingCursor, CursorLocation::EditingPrompt); - DisplayInput (m_current_line_index); - - // Reposition the cursor back on the original line and prepare to restart editing - // with a new cursor position - SetCurrentLine (m_current_line_index); - MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingPrompt); - m_revert_cursor_index = cursor_position + indent_correction; - return CC_NEWLINE; + // Update the display to reflect the change + MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt); + DisplayInput(m_current_line_index); + + // Reposition the cursor back on the original line and prepare to restart + // editing + // with a new cursor position + SetCurrentLine(m_current_line_index); + MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt); + m_revert_cursor_index = cursor_position + indent_correction; + return CC_NEWLINE; } -unsigned char -Editline::RevertLineCommand (int ch) -{ - el_winsertstr (m_editline, m_input_lines[m_current_line_index].c_str()); - if (m_revert_cursor_index >= 0) - { - LineInfoW * info = const_cast<LineInfoW *>(el_wline (m_editline)); - info->cursor = info->buffer + m_revert_cursor_index; - if (info->cursor > info->lastchar) - { - info->cursor = info->lastchar; - } - m_revert_cursor_index = -1; +unsigned char Editline::RevertLineCommand(int ch) { + el_winsertstr(m_editline, m_input_lines[m_current_line_index].c_str()); + if (m_revert_cursor_index >= 0) { + LineInfoW *info = const_cast<LineInfoW *>(el_wline(m_editline)); + info->cursor = info->buffer + m_revert_cursor_index; + if (info->cursor > info->lastchar) { + info->cursor = info->lastchar; } - return CC_REFRESH; + m_revert_cursor_index = -1; + } + return CC_REFRESH; } -unsigned char -Editline::BufferStartCommand (int ch) -{ - SaveEditedLine(); - MoveCursor (CursorLocation::EditingCursor, CursorLocation::BlockStart); - SetCurrentLine (0); - m_revert_cursor_index = 0; - return CC_NEWLINE; +unsigned char Editline::BufferStartCommand(int ch) { + SaveEditedLine(); + MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart); + SetCurrentLine(0); + m_revert_cursor_index = 0; + return CC_NEWLINE; } -unsigned char -Editline::BufferEndCommand (int ch) -{ - SaveEditedLine(); - MoveCursor (CursorLocation::EditingCursor, CursorLocation::BlockEnd); - SetCurrentLine ((int)m_input_lines.size() - 1); - MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingPrompt); - return CC_NEWLINE; +unsigned char Editline::BufferEndCommand(int ch) { + SaveEditedLine(); + MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockEnd); + SetCurrentLine((int)m_input_lines.size() - 1); + MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt); + return CC_NEWLINE; } -unsigned char -Editline::TabCommand (int ch) -{ - if (m_completion_callback == nullptr) - return CC_ERROR; - - const LineInfo *line_info = el_line (m_editline); - StringList completions; - int page_size = 40; - - const int num_completions = m_completion_callback (line_info->buffer, - line_info->cursor, - line_info->lastchar, - 0, // Don't skip any matches (start at match zero) - -1, // Get all the matches - completions, - m_completion_callback_baton); - - if (num_completions == 0) - return CC_ERROR; - // if (num_completions == -1) - // { - // el_insertstr (m_editline, m_completion_key); - // return CC_REDISPLAY; - // } - // else - if (num_completions == -2) - { - // Replace the entire line with the first string... - el_deletestr (m_editline, line_info->cursor - line_info->buffer); - el_insertstr (m_editline, completions.GetStringAtIndex (0)); - return CC_REDISPLAY; - } - - // If we get a longer match display that first. - const char *completion_str = completions.GetStringAtIndex (0); - if (completion_str != nullptr && *completion_str != '\0') - { - el_insertstr (m_editline, completion_str); - return CC_REDISPLAY; - } - - if (num_completions > 1) - { - int num_elements = num_completions + 1; - fprintf (m_output_file, "\n" ANSI_CLEAR_BELOW "Available completions:"); - if (num_completions < page_size) - { - for (int i = 1; i < num_elements; i++) - { - completion_str = completions.GetStringAtIndex (i); - fprintf (m_output_file, "\n\t%s", completion_str); - } - fprintf (m_output_file, "\n"); +unsigned char Editline::TabCommand(int ch) { + if (m_completion_callback == nullptr) + return CC_ERROR; + + const LineInfo *line_info = el_line(m_editline); + StringList completions; + int page_size = 40; + + const int num_completions = m_completion_callback( + line_info->buffer, line_info->cursor, line_info->lastchar, + 0, // Don't skip any matches (start at match zero) + -1, // Get all the matches + completions, m_completion_callback_baton); + + if (num_completions == 0) + return CC_ERROR; + // if (num_completions == -1) + // { + // el_insertstr (m_editline, m_completion_key); + // return CC_REDISPLAY; + // } + // else + if (num_completions == -2) { + // Replace the entire line with the first string... + el_deletestr(m_editline, line_info->cursor - line_info->buffer); + el_insertstr(m_editline, completions.GetStringAtIndex(0)); + return CC_REDISPLAY; + } + + // If we get a longer match display that first. + const char *completion_str = completions.GetStringAtIndex(0); + if (completion_str != nullptr && *completion_str != '\0') { + el_insertstr(m_editline, completion_str); + return CC_REDISPLAY; + } + + if (num_completions > 1) { + int num_elements = num_completions + 1; + fprintf(m_output_file, "\n" ANSI_CLEAR_BELOW "Available completions:"); + if (num_completions < page_size) { + for (int i = 1; i < num_elements; i++) { + completion_str = completions.GetStringAtIndex(i); + fprintf(m_output_file, "\n\t%s", completion_str); + } + fprintf(m_output_file, "\n"); + } else { + int cur_pos = 1; + char reply; + int got_char; + while (cur_pos < num_elements) { + int endpoint = cur_pos + page_size; + if (endpoint > num_elements) + endpoint = num_elements; + for (; cur_pos < endpoint; cur_pos++) { + completion_str = completions.GetStringAtIndex(cur_pos); + fprintf(m_output_file, "\n\t%s", completion_str); } - else - { - int cur_pos = 1; - char reply; - int got_char; - while (cur_pos < num_elements) - { - int endpoint = cur_pos + page_size; - if (endpoint > num_elements) - endpoint = num_elements; - for (; cur_pos < endpoint; cur_pos++) - { - completion_str = completions.GetStringAtIndex (cur_pos); - fprintf (m_output_file, "\n\t%s", completion_str); - } - - if (cur_pos >= num_elements) - { - fprintf (m_output_file, "\n"); - break; - } - - fprintf (m_output_file, "\nMore (Y/n/a): "); - reply = 'n'; - got_char = el_getc(m_editline, &reply); - if (got_char == -1 || reply == 'n') - break; - if (reply == 'a') - page_size = num_elements - cur_pos; - } + + if (cur_pos >= num_elements) { + fprintf(m_output_file, "\n"); + break; } - DisplayInput(); - MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor); + + fprintf(m_output_file, "\nMore (Y/n/a): "); + reply = 'n'; + got_char = el_getc(m_editline, &reply); + if (got_char == -1 || reply == 'n') + break; + if (reply == 'a') + page_size = num_elements - cur_pos; + } } - return CC_REDISPLAY; + DisplayInput(); + MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor); + } + return CC_REDISPLAY; } -void -Editline::ConfigureEditor (bool multiline) -{ - if (m_editline && m_multiline_enabled == multiline) - return; - m_multiline_enabled = multiline; - - if (m_editline) - { - // Disable edit mode to stop the terminal from flushing all input - // during the call to el_end() since we expect to have multiple editline - // instances in this program. - el_set (m_editline, EL_EDITMODE, 0); - el_end (m_editline); - } - - m_editline = el_init (m_editor_name.c_str(), m_input_file, m_output_file, m_error_file); - TerminalSizeChanged(); - - if (m_history_sp && m_history_sp->IsValid()) - { - m_history_sp->Load(); - el_wset (m_editline, EL_HIST, history, m_history_sp->GetHistoryPtr()); +void Editline::ConfigureEditor(bool multiline) { + if (m_editline && m_multiline_enabled == multiline) + return; + m_multiline_enabled = multiline; + + if (m_editline) { + // Disable edit mode to stop the terminal from flushing all input + // during the call to el_end() since we expect to have multiple editline + // instances in this program. + el_set(m_editline, EL_EDITMODE, 0); + el_end(m_editline); + } + + m_editline = + el_init(m_editor_name.c_str(), m_input_file, m_output_file, m_error_file); + TerminalSizeChanged(); + + if (m_history_sp && m_history_sp->IsValid()) { + m_history_sp->Load(); + el_wset(m_editline, EL_HIST, history, m_history_sp->GetHistoryPtr()); + } + el_set(m_editline, EL_CLIENTDATA, this); + el_set(m_editline, EL_SIGNAL, 0); + el_set(m_editline, EL_EDITOR, "emacs"); + el_set(m_editline, EL_PROMPT, + (EditlinePromptCallbackType)([](EditLine *editline) { + return Editline::InstanceFor(editline)->Prompt(); + })); + + el_wset(m_editline, EL_GETCFN, (EditlineGetCharCallbackType)([]( + EditLine *editline, EditLineCharType *c) { + return Editline::InstanceFor(editline)->GetCharacter(c); + })); + + // Commands used for multiline support, registered whether or not they're used + el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-break-line"), + EditLineConstString("Insert a line break"), + (EditlineCommandCallbackType)([](EditLine *editline, int ch) { + return Editline::InstanceFor(editline)->BreakLineCommand(ch); + })); + el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-end-or-add-line"), + EditLineConstString("End editing or continue when incomplete"), + (EditlineCommandCallbackType)([](EditLine *editline, int ch) { + return Editline::InstanceFor(editline)->EndOrAddLineCommand(ch); + })); + el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-delete-next-char"), + EditLineConstString("Delete next character"), + (EditlineCommandCallbackType)([](EditLine *editline, int ch) { + return Editline::InstanceFor(editline)->DeleteNextCharCommand(ch); + })); + el_wset( + m_editline, EL_ADDFN, EditLineConstString("lldb-delete-previous-char"), + EditLineConstString("Delete previous character"), + (EditlineCommandCallbackType)([](EditLine *editline, int ch) { + return Editline::InstanceFor(editline)->DeletePreviousCharCommand(ch); + })); + el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-previous-line"), + EditLineConstString("Move to previous line"), + (EditlineCommandCallbackType)([](EditLine *editline, int ch) { + return Editline::InstanceFor(editline)->PreviousLineCommand(ch); + })); + el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-next-line"), + EditLineConstString("Move to next line"), + (EditlineCommandCallbackType)([](EditLine *editline, int ch) { + return Editline::InstanceFor(editline)->NextLineCommand(ch); + })); + el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-previous-history"), + EditLineConstString("Move to previous history"), + (EditlineCommandCallbackType)([](EditLine *editline, int ch) { + return Editline::InstanceFor(editline)->PreviousHistoryCommand(ch); + })); + el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-next-history"), + EditLineConstString("Move to next history"), + (EditlineCommandCallbackType)([](EditLine *editline, int ch) { + return Editline::InstanceFor(editline)->NextHistoryCommand(ch); + })); + el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-buffer-start"), + EditLineConstString("Move to start of buffer"), + (EditlineCommandCallbackType)([](EditLine *editline, int ch) { + return Editline::InstanceFor(editline)->BufferStartCommand(ch); + })); + el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-buffer-end"), + EditLineConstString("Move to end of buffer"), + (EditlineCommandCallbackType)([](EditLine *editline, int ch) { + return Editline::InstanceFor(editline)->BufferEndCommand(ch); + })); + el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-fix-indentation"), + EditLineConstString("Fix line indentation"), + (EditlineCommandCallbackType)([](EditLine *editline, int ch) { + return Editline::InstanceFor(editline)->FixIndentationCommand(ch); + })); + + // Register the complete callback under two names for compatibility with older + // clients using + // custom .editrc files (largely because libedit has a bad bug where if you + // have a bind command + // that tries to bind to a function name that doesn't exist, it can corrupt + // the heap and + // crash your process later.) + EditlineCommandCallbackType complete_callback = [](EditLine *editline, + int ch) { + return Editline::InstanceFor(editline)->TabCommand(ch); + }; + el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-complete"), + EditLineConstString("Invoke completion"), complete_callback); + el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb_complete"), + EditLineConstString("Invoke completion"), complete_callback); + + // General bindings we don't mind being overridden + if (!multiline) { + el_set(m_editline, EL_BIND, "^r", "em-inc-search-prev", + NULL); // Cycle through backwards search, entering string + } + el_set(m_editline, EL_BIND, "^w", "ed-delete-prev-word", + NULL); // Delete previous word, behave like bash in emacs mode + el_set(m_editline, EL_BIND, "\t", "lldb-complete", + NULL); // Bind TAB to auto complete + + // Allow user-specific customization prior to registering bindings we + // absolutely require + el_source(m_editline, NULL); + + // Register an internal binding that external developers shouldn't use + el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-revert-line"), + EditLineConstString("Revert line to saved state"), + (EditlineCommandCallbackType)([](EditLine *editline, int ch) { + return Editline::InstanceFor(editline)->RevertLineCommand(ch); + })); + + // Register keys that perform auto-indent correction + if (m_fix_indentation_callback && m_fix_indentation_callback_chars) { + char bind_key[2] = {0, 0}; + const char *indent_chars = m_fix_indentation_callback_chars; + while (*indent_chars) { + bind_key[0] = *indent_chars; + el_set(m_editline, EL_BIND, bind_key, "lldb-fix-indentation", NULL); + ++indent_chars; } - el_set (m_editline, EL_CLIENTDATA, this); - el_set (m_editline, EL_SIGNAL, 0); - el_set (m_editline, EL_EDITOR, "emacs"); - el_set (m_editline, EL_PROMPT, (EditlinePromptCallbackType)([] (EditLine *editline) { - return Editline::InstanceFor (editline)->Prompt(); - })); - - el_wset (m_editline, EL_GETCFN, - (EditlineGetCharCallbackType)([] (EditLine * editline, EditLineCharType * c) { - return Editline::InstanceFor (editline)->GetCharacter (c); - })); - - // Commands used for multiline support, registered whether or not they're used - el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-break-line"), EditLineConstString("Insert a line break"), - (EditlineCommandCallbackType)( - [](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->BreakLineCommand(ch); })); - el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-end-or-add-line"), - EditLineConstString("End editing or continue when incomplete"), - (EditlineCommandCallbackType)( - [](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->EndOrAddLineCommand(ch); })); - el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-delete-next-char"), - EditLineConstString("Delete next character"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { - return Editline::InstanceFor(editline)->DeleteNextCharCommand(ch); - })); - el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-delete-previous-char"), - EditLineConstString("Delete previous character"), - (EditlineCommandCallbackType)([](EditLine *editline, int ch) { - return Editline::InstanceFor(editline)->DeletePreviousCharCommand(ch); - })); - el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-previous-line"), - EditLineConstString("Move to previous line"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { - return Editline::InstanceFor(editline)->PreviousLineCommand(ch); - })); - el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-next-line"), EditLineConstString("Move to next line"), - (EditlineCommandCallbackType)( - [](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->NextLineCommand(ch); })); - el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-previous-history"), - EditLineConstString("Move to previous history"), - (EditlineCommandCallbackType)([](EditLine *editline, int ch) { - return Editline::InstanceFor(editline)->PreviousHistoryCommand(ch); - })); - el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-next-history"), EditLineConstString("Move to next history"), - (EditlineCommandCallbackType)( - [](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->NextHistoryCommand(ch); })); - el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-buffer-start"), - EditLineConstString("Move to start of buffer"), - (EditlineCommandCallbackType)( - [](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->BufferStartCommand(ch); })); - el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-buffer-end"), EditLineConstString("Move to end of buffer"), - (EditlineCommandCallbackType)( - [](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->BufferEndCommand(ch); })); - el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-fix-indentation"), - EditLineConstString("Fix line indentation"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { - return Editline::InstanceFor (editline)->FixIndentationCommand (ch); - })); - - // Register the complete callback under two names for compatibility with older clients using - // custom .editrc files (largely becuase libedit has a bad bug where if you have a bind command - // that tries to bind to a function name that doesn't exist, it can corrupt the heap and - // crash your process later.) - EditlineCommandCallbackType complete_callback = [] (EditLine * editline, int ch) { - return Editline::InstanceFor (editline)->TabCommand (ch); - }; - el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-complete"), EditLineConstString("Invoke completion"), - complete_callback); - el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb_complete"), EditLineConstString("Invoke completion"), - complete_callback); - - // General bindings we don't mind being overridden - if (!multiline) { - el_set (m_editline, EL_BIND, "^r", "em-inc-search-prev", NULL); // Cycle through backwards search, entering string - } - el_set (m_editline, EL_BIND, "^w", "ed-delete-prev-word", NULL); // Delete previous word, behave like bash in emacs mode - el_set (m_editline, EL_BIND, "\t", "lldb-complete", NULL); // Bind TAB to auto complete - - // Allow user-specific customization prior to registering bindings we absolutely require - el_source (m_editline, NULL); - - // Register an internal binding that external developers shouldn't use - el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-revert-line"), - EditLineConstString("Revert line to saved state"), - (EditlineCommandCallbackType)( - [](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->RevertLineCommand(ch); })); - - // Register keys that perform auto-indent correction - if (m_fix_indentation_callback && m_fix_indentation_callback_chars) - { - char bind_key[2] = { 0, 0 }; - const char * indent_chars = m_fix_indentation_callback_chars; - while (*indent_chars) - { - bind_key[0] = *indent_chars; - el_set (m_editline, EL_BIND, bind_key, "lldb-fix-indentation", NULL); - ++indent_chars; - } - } - - // Multi-line editor bindings - if (multiline) - { - el_set(m_editline, EL_BIND, "\n", "lldb-end-or-add-line", NULL); - el_set(m_editline, EL_BIND, "\r", "lldb-end-or-add-line", NULL); - el_set(m_editline, EL_BIND, ESCAPE "\n", "lldb-break-line", NULL); - el_set(m_editline, EL_BIND, ESCAPE "\r", "lldb-break-line", NULL); - el_set (m_editline, EL_BIND, "^p", "lldb-previous-line", NULL); - el_set (m_editline, EL_BIND, "^n", "lldb-next-line", NULL); - el_set (m_editline, EL_BIND, "^?", "lldb-delete-previous-char", NULL); - el_set (m_editline, EL_BIND, "^d", "lldb-delete-next-char", NULL); - el_set (m_editline, EL_BIND, ESCAPE "[3~", "lldb-delete-next-char", NULL); - el_set (m_editline, EL_BIND, ESCAPE "[\\^", "lldb-revert-line", NULL); - - // Editor-specific bindings - if (IsEmacs()) - { - el_set (m_editline, EL_BIND, ESCAPE "<", "lldb-buffer-start", NULL); - el_set (m_editline, EL_BIND, ESCAPE ">", "lldb-buffer-end", NULL); - el_set (m_editline, EL_BIND, ESCAPE "[A", "lldb-previous-line", NULL); - el_set (m_editline, EL_BIND, ESCAPE "[B", "lldb-next-line", NULL); - el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[A", "lldb-previous-history", NULL); - el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[B", "lldb-next-history", NULL); - el_set(m_editline, EL_BIND, ESCAPE "[1;3A", "lldb-previous-history", NULL); - el_set(m_editline, EL_BIND, ESCAPE "[1;3B", "lldb-next-history", NULL); - } - else - { - el_set (m_editline, EL_BIND, "^H", "lldb-delete-previous-char", NULL); - - el_set (m_editline, EL_BIND, "-a", ESCAPE "[A", "lldb-previous-line", NULL); - el_set (m_editline, EL_BIND, "-a", ESCAPE "[B", "lldb-next-line", NULL); - el_set (m_editline, EL_BIND, "-a", "x", "lldb-delete-next-char", NULL); - el_set (m_editline, EL_BIND, "-a", "^H", "lldb-delete-previous-char", NULL); - el_set (m_editline, EL_BIND, "-a", "^?", "lldb-delete-previous-char", NULL); - - // Escape is absorbed exiting edit mode, so re-register important sequences - // without the prefix - el_set (m_editline, EL_BIND, "-a", "[A", "lldb-previous-line", NULL); - el_set (m_editline, EL_BIND, "-a", "[B", "lldb-next-line", NULL); - el_set (m_editline, EL_BIND, "-a", "[\\^", "lldb-revert-line", NULL); - } + } + + // Multi-line editor bindings + if (multiline) { + el_set(m_editline, EL_BIND, "\n", "lldb-end-or-add-line", NULL); + el_set(m_editline, EL_BIND, "\r", "lldb-end-or-add-line", NULL); + el_set(m_editline, EL_BIND, ESCAPE "\n", "lldb-break-line", NULL); + el_set(m_editline, EL_BIND, ESCAPE "\r", "lldb-break-line", NULL); + el_set(m_editline, EL_BIND, "^p", "lldb-previous-line", NULL); + el_set(m_editline, EL_BIND, "^n", "lldb-next-line", NULL); + el_set(m_editline, EL_BIND, "^?", "lldb-delete-previous-char", NULL); + el_set(m_editline, EL_BIND, "^d", "lldb-delete-next-char", NULL); + el_set(m_editline, EL_BIND, ESCAPE "[3~", "lldb-delete-next-char", NULL); + el_set(m_editline, EL_BIND, ESCAPE "[\\^", "lldb-revert-line", NULL); + + // Editor-specific bindings + if (IsEmacs()) { + el_set(m_editline, EL_BIND, ESCAPE "<", "lldb-buffer-start", NULL); + el_set(m_editline, EL_BIND, ESCAPE ">", "lldb-buffer-end", NULL); + el_set(m_editline, EL_BIND, ESCAPE "[A", "lldb-previous-line", NULL); + el_set(m_editline, EL_BIND, ESCAPE "[B", "lldb-next-line", NULL); + el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[A", "lldb-previous-history", + NULL); + el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[B", "lldb-next-history", + NULL); + el_set(m_editline, EL_BIND, ESCAPE "[1;3A", "lldb-previous-history", + NULL); + el_set(m_editline, EL_BIND, ESCAPE "[1;3B", "lldb-next-history", NULL); + } else { + el_set(m_editline, EL_BIND, "^H", "lldb-delete-previous-char", NULL); + + el_set(m_editline, EL_BIND, "-a", ESCAPE "[A", "lldb-previous-line", + NULL); + el_set(m_editline, EL_BIND, "-a", ESCAPE "[B", "lldb-next-line", NULL); + el_set(m_editline, EL_BIND, "-a", "x", "lldb-delete-next-char", NULL); + el_set(m_editline, EL_BIND, "-a", "^H", "lldb-delete-previous-char", + NULL); + el_set(m_editline, EL_BIND, "-a", "^?", "lldb-delete-previous-char", + NULL); + + // Escape is absorbed exiting edit mode, so re-register important + // sequences + // without the prefix + el_set(m_editline, EL_BIND, "-a", "[A", "lldb-previous-line", NULL); + el_set(m_editline, EL_BIND, "-a", "[B", "lldb-next-line", NULL); + el_set(m_editline, EL_BIND, "-a", "[\\^", "lldb-revert-line", NULL); } + } } //------------------------------------------------------------------ // Editline public methods //------------------------------------------------------------------ -Editline * -Editline::InstanceFor (EditLine * editline) -{ - Editline * editor; - el_get (editline, EL_CLIENTDATA, &editor); - return editor; +Editline *Editline::InstanceFor(EditLine *editline) { + Editline *editor; + el_get(editline, EL_CLIENTDATA, &editor); + return editor; } -Editline::Editline (const char * editline_name, FILE * input_file, FILE * output_file, FILE * error_file, bool color_prompts) : - m_editor_status (EditorStatus::Complete), - m_color_prompts(color_prompts), - m_input_file (input_file), - m_output_file (output_file), - m_error_file (error_file), - m_input_connection (fileno(input_file), false) -{ - // Get a shared history instance - m_editor_name = (editline_name == nullptr) ? "lldb-tmp" : editline_name; - m_history_sp = EditlineHistory::GetHistory (m_editor_name); +Editline::Editline(const char *editline_name, FILE *input_file, + FILE *output_file, FILE *error_file, bool color_prompts) + : m_editor_status(EditorStatus::Complete), m_color_prompts(color_prompts), + m_input_file(input_file), m_output_file(output_file), + m_error_file(error_file), m_input_connection(fileno(input_file), false) { + // Get a shared history instance + m_editor_name = (editline_name == nullptr) ? "lldb-tmp" : editline_name; + m_history_sp = EditlineHistory::GetHistory(m_editor_name); #ifdef USE_SETUPTERM_WORKAROUND - if (m_output_file) - { - const int term_fd = fileno(m_output_file); - if (term_fd != -1) - { - static std::mutex *g_init_terminal_fds_mutex_ptr = nullptr; - static std::set<int> *g_init_terminal_fds_ptr = nullptr; - static std::once_flag g_once_flag; - std::call_once(g_once_flag, [&]() { - g_init_terminal_fds_mutex_ptr = new std::mutex(); // NOTE: Leak to avoid C++ destructor chain issues - g_init_terminal_fds_ptr = new std::set<int>(); // NOTE: Leak to avoid C++ destructor chain issues - }); - - // We must make sure to initialize the terminal a given file descriptor - // only once. If we do this multiple times, we start leaking memory. - std::lock_guard<std::mutex> guard(*g_init_terminal_fds_mutex_ptr); - if (g_init_terminal_fds_ptr->find(term_fd) == g_init_terminal_fds_ptr->end()) - { - g_init_terminal_fds_ptr->insert(term_fd); - setupterm((char *)0, term_fd, (int *)0); - } - } + if (m_output_file) { + const int term_fd = fileno(m_output_file); + if (term_fd != -1) { + static std::mutex *g_init_terminal_fds_mutex_ptr = nullptr; + static std::set<int> *g_init_terminal_fds_ptr = nullptr; + static std::once_flag g_once_flag; + std::call_once(g_once_flag, [&]() { + g_init_terminal_fds_mutex_ptr = + new std::mutex(); // NOTE: Leak to avoid C++ destructor chain issues + g_init_terminal_fds_ptr = new std::set<int>(); // NOTE: Leak to avoid + // C++ destructor chain + // issues + }); + + // We must make sure to initialize the terminal a given file descriptor + // only once. If we do this multiple times, we start leaking memory. + std::lock_guard<std::mutex> guard(*g_init_terminal_fds_mutex_ptr); + if (g_init_terminal_fds_ptr->find(term_fd) == + g_init_terminal_fds_ptr->end()) { + g_init_terminal_fds_ptr->insert(term_fd); + setupterm((char *)0, term_fd, (int *)0); + } } + } #endif } -Editline::~Editline() -{ - if (m_editline) - { - // Disable edit mode to stop the terminal from flushing all input - // during the call to el_end() since we expect to have multiple editline - // instances in this program. - el_set (m_editline, EL_EDITMODE, 0); - el_end (m_editline); - m_editline = nullptr; - } - - // EditlineHistory objects are sometimes shared between multiple - // Editline instances with the same program name. So just release - // our shared pointer and if we are the last owner, it will save the - // history to the history save file automatically. - m_history_sp.reset(); +Editline::~Editline() { + if (m_editline) { + // Disable edit mode to stop the terminal from flushing all input + // during the call to el_end() since we expect to have multiple editline + // instances in this program. + el_set(m_editline, EL_EDITMODE, 0); + el_end(m_editline); + m_editline = nullptr; + } + + // EditlineHistory objects are sometimes shared between multiple + // Editline instances with the same program name. So just release + // our shared pointer and if we are the last owner, it will save the + // history to the history save file automatically. + m_history_sp.reset(); } -void -Editline::SetPrompt (const char * prompt) -{ - m_set_prompt = prompt == nullptr ? "" : prompt; +void Editline::SetPrompt(const char *prompt) { + m_set_prompt = prompt == nullptr ? "" : prompt; } -void -Editline::SetContinuationPrompt (const char * continuation_prompt) -{ - m_set_continuation_prompt = continuation_prompt == nullptr ? "" : continuation_prompt; +void Editline::SetContinuationPrompt(const char *continuation_prompt) { + m_set_continuation_prompt = + continuation_prompt == nullptr ? "" : continuation_prompt; } -void -Editline::TerminalSizeChanged() -{ - if (m_editline != nullptr) - { - el_resize (m_editline); - int columns; - // Despite the man page claiming non-zero indicates success, it's actually zero - if (el_get (m_editline, EL_GETTC, "co", &columns) == 0) - { - m_terminal_width = columns; - if (m_current_line_rows != -1) - { - const LineInfoW * info = el_wline (m_editline); - int lineLength = (int)((info->lastchar - info->buffer) + GetPromptWidth()); - m_current_line_rows = (lineLength / columns) + 1; - } - } - else - { - m_terminal_width = INT_MAX; - m_current_line_rows = 1; - } +void Editline::TerminalSizeChanged() { + if (m_editline != nullptr) { + el_resize(m_editline); + int columns; + // Despite the man page claiming non-zero indicates success, it's actually + // zero + if (el_get(m_editline, EL_GETTC, "co", &columns) == 0) { + m_terminal_width = columns; + if (m_current_line_rows != -1) { + const LineInfoW *info = el_wline(m_editline); + int lineLength = + (int)((info->lastchar - info->buffer) + GetPromptWidth()); + m_current_line_rows = (lineLength / columns) + 1; + } + } else { + m_terminal_width = INT_MAX; + m_current_line_rows = 1; } + } } -const char * -Editline::GetPrompt() -{ - return m_set_prompt.c_str(); -} +const char *Editline::GetPrompt() { return m_set_prompt.c_str(); } -uint32_t -Editline::GetCurrentLine() -{ - return m_current_line_index; -} +uint32_t Editline::GetCurrentLine() { return m_current_line_index; } -bool -Editline::Interrupt() -{ - bool result = true; - std::lock_guard<std::mutex> guard(m_output_mutex); - if (m_editor_status == EditorStatus::Editing) { - fprintf(m_output_file, "^C\n"); - result = m_input_connection.InterruptRead(); - } - m_editor_status = EditorStatus::Interrupted; - return result; +bool Editline::Interrupt() { + bool result = true; + std::lock_guard<std::mutex> guard(m_output_mutex); + if (m_editor_status == EditorStatus::Editing) { + fprintf(m_output_file, "^C\n"); + result = m_input_connection.InterruptRead(); + } + m_editor_status = EditorStatus::Interrupted; + return result; } -bool -Editline::Cancel() -{ - bool result = true; - std::lock_guard<std::mutex> guard(m_output_mutex); - if (m_editor_status == EditorStatus::Editing) { - MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart); - fprintf(m_output_file, ANSI_CLEAR_BELOW); - result = m_input_connection.InterruptRead(); - } - m_editor_status = EditorStatus::Interrupted; - return result; +bool Editline::Cancel() { + bool result = true; + std::lock_guard<std::mutex> guard(m_output_mutex); + if (m_editor_status == EditorStatus::Editing) { + MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart); + fprintf(m_output_file, ANSI_CLEAR_BELOW); + result = m_input_connection.InterruptRead(); + } + m_editor_status = EditorStatus::Interrupted; + return result; } -void -Editline::SetAutoCompleteCallback (CompleteCallbackType callback, void * baton) -{ - m_completion_callback = callback; - m_completion_callback_baton = baton; +void Editline::SetAutoCompleteCallback(CompleteCallbackType callback, + void *baton) { + m_completion_callback = callback; + m_completion_callback_baton = baton; } -void -Editline::SetIsInputCompleteCallback (IsInputCompleteCallbackType callback, void * baton) -{ - m_is_input_complete_callback = callback; - m_is_input_complete_callback_baton = baton; +void Editline::SetIsInputCompleteCallback(IsInputCompleteCallbackType callback, + void *baton) { + m_is_input_complete_callback = callback; + m_is_input_complete_callback_baton = baton; } -bool -Editline::SetFixIndentationCallback (FixIndentationCallbackType callback, - void * baton, - const char * indent_chars) -{ - m_fix_indentation_callback = callback; - m_fix_indentation_callback_baton = baton; - m_fix_indentation_callback_chars = indent_chars; - return false; +bool Editline::SetFixIndentationCallback(FixIndentationCallbackType callback, + void *baton, + const char *indent_chars) { + m_fix_indentation_callback = callback; + m_fix_indentation_callback_baton = baton; + m_fix_indentation_callback_chars = indent_chars; + return false; } -bool -Editline::GetLine (std::string &line, bool &interrupted) -{ - ConfigureEditor (false); - m_input_lines = std::vector<EditLineStringType>(); - m_input_lines.insert (m_input_lines.begin(), EditLineConstString("")); - - std::lock_guard<std::mutex> guard(m_output_mutex); - - lldbassert(m_editor_status != EditorStatus::Editing); - if (m_editor_status == EditorStatus::Interrupted) - { - m_editor_status = EditorStatus::Complete; - interrupted = true; - return true; - } +bool Editline::GetLine(std::string &line, bool &interrupted) { + ConfigureEditor(false); + m_input_lines = std::vector<EditLineStringType>(); + m_input_lines.insert(m_input_lines.begin(), EditLineConstString("")); - SetCurrentLine (0); - m_in_history = false; - m_editor_status = EditorStatus::Editing; - m_revert_cursor_index = -1; + std::lock_guard<std::mutex> guard(m_output_mutex); - int count; - auto input = el_wgets (m_editline, &count); - - interrupted = m_editor_status == EditorStatus::Interrupted; - if (!interrupted) - { - if (input == nullptr) - { - fprintf (m_output_file, "\n"); - m_editor_status = EditorStatus::EndOfInput; - } - else - { - m_history_sp->Enter (input); + lldbassert(m_editor_status != EditorStatus::Editing); + if (m_editor_status == EditorStatus::Interrupted) { + m_editor_status = EditorStatus::Complete; + interrupted = true; + return true; + } + + SetCurrentLine(0); + m_in_history = false; + m_editor_status = EditorStatus::Editing; + m_revert_cursor_index = -1; + + int count; + auto input = el_wgets(m_editline, &count); + + interrupted = m_editor_status == EditorStatus::Interrupted; + if (!interrupted) { + if (input == nullptr) { + fprintf(m_output_file, "\n"); + m_editor_status = EditorStatus::EndOfInput; + } else { + m_history_sp->Enter(input); #if LLDB_EDITLINE_USE_WCHAR - line = m_utf8conv.to_bytes (SplitLines (input)[0]); + line = m_utf8conv.to_bytes(SplitLines(input)[0]); #else - line = SplitLines (input)[0]; + line = SplitLines(input)[0]; #endif - m_editor_status = EditorStatus::Complete; - } + m_editor_status = EditorStatus::Complete; } - return m_editor_status != EditorStatus::EndOfInput; + } + return m_editor_status != EditorStatus::EndOfInput; } -bool -Editline::GetLines (int first_line_number, StringList &lines, bool &interrupted) -{ - ConfigureEditor (true); - - // Print the initial input lines, then move the cursor back up to the start of input - SetBaseLineNumber (first_line_number); - m_input_lines = std::vector<EditLineStringType>(); - m_input_lines.insert (m_input_lines.begin(), EditLineConstString("")); - - std::lock_guard<std::mutex> guard(m_output_mutex); - // Begin the line editing loop - DisplayInput(); - SetCurrentLine (0); - MoveCursor (CursorLocation::BlockEnd, CursorLocation::BlockStart); - m_editor_status = EditorStatus::Editing; - m_in_history = false; +bool Editline::GetLines(int first_line_number, StringList &lines, + bool &interrupted) { + ConfigureEditor(true); + + // Print the initial input lines, then move the cursor back up to the start of + // input + SetBaseLineNumber(first_line_number); + m_input_lines = std::vector<EditLineStringType>(); + m_input_lines.insert(m_input_lines.begin(), EditLineConstString("")); + + std::lock_guard<std::mutex> guard(m_output_mutex); + // Begin the line editing loop + DisplayInput(); + SetCurrentLine(0); + MoveCursor(CursorLocation::BlockEnd, CursorLocation::BlockStart); + m_editor_status = EditorStatus::Editing; + m_in_history = false; + + m_revert_cursor_index = -1; + while (m_editor_status == EditorStatus::Editing) { + int count; + m_current_line_rows = -1; + el_wpush(m_editline, EditLineConstString( + "\x1b[^")); // Revert to the existing line content + el_wgets(m_editline, &count); + } + + interrupted = m_editor_status == EditorStatus::Interrupted; + if (!interrupted) { + // Save the completed entry in history before returning + m_history_sp->Enter(CombineLines(m_input_lines).c_str()); + + lines = GetInputAsStringList(); + } + return m_editor_status != EditorStatus::EndOfInput; +} - m_revert_cursor_index = -1; - while (m_editor_status == EditorStatus::Editing) - { - int count; - m_current_line_rows = -1; - el_wpush (m_editline, EditLineConstString("\x1b[^")); // Revert to the existing line content - el_wgets (m_editline, &count); - } - - interrupted = m_editor_status == EditorStatus::Interrupted; - if (!interrupted) - { - // Save the completed entry in history before returning - m_history_sp->Enter (CombineLines (m_input_lines).c_str()); - - lines = GetInputAsStringList(); - } - return m_editor_status != EditorStatus::EndOfInput; +void Editline::PrintAsync(Stream *stream, const char *s, size_t len) { + std::lock_guard<std::mutex> guard(m_output_mutex); + if (m_editor_status == EditorStatus::Editing) { + MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart); + fprintf(m_output_file, ANSI_CLEAR_BELOW); + } + stream->Write(s, len); + stream->Flush(); + if (m_editor_status == EditorStatus::Editing) { + DisplayInput(); + MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor); + } } -void -Editline::PrintAsync (Stream *stream, const char *s, size_t len) -{ - std::lock_guard<std::mutex> guard(m_output_mutex); - if (m_editor_status == EditorStatus::Editing) - { - MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart); - fprintf(m_output_file, ANSI_CLEAR_BELOW); - } - stream->Write (s, len); - stream->Flush(); - if (m_editor_status == EditorStatus::Editing) - { - DisplayInput(); - MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor); +bool Editline::CompleteCharacter(char ch, EditLineCharType &out) { +#if !LLDB_EDITLINE_USE_WCHAR + if (ch == (char)EOF) + return false; + + out = ch; + return true; +#else + std::codecvt_utf8<wchar_t> cvt; + llvm::SmallString<4> input; + for (;;) { + const char *from_next; + wchar_t *to_next; + std::mbstate_t state = std::mbstate_t(); + input.push_back(ch); + switch (cvt.in(state, input.begin(), input.end(), from_next, &out, &out + 1, + to_next)) { + case std::codecvt_base::ok: + return out != WEOF; + + case std::codecvt_base::error: + case std::codecvt_base::noconv: + return false; + + case std::codecvt_base::partial: + lldb::ConnectionStatus status; + size_t read_count = m_input_connection.Read( + &ch, 1, std::chrono::seconds(0), status, nullptr); + if (read_count == 0) + return false; + break; } + } +#endif } |