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