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