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 end of input detection or automatic formatting when pasting 661 if (!IsInputPending (m_input_file)) 662 { 663 // If this is the end of the last line, treat this as a potential exit 664 if (m_current_line_index == m_input_lines.size() - 1 && new_line_fragment.length() == 0) 665 { 666 bool end_of_input = true; 667 if (m_is_input_complete_callback) 668 { 669 SaveEditedLine(); 670 auto lines = GetInputAsStringList(); 671 end_of_input = m_is_input_complete_callback (this, lines, m_is_input_complete_callback_baton); 672 673 // The completion test is allowed to change the input lines when complete 674 if (end_of_input) 675 { 676 m_input_lines.clear(); 677 for (unsigned index = 0; index < lines.GetSize(); index++) 678 { 679 #if LLDB_EDITLINE_USE_WCHAR 680 m_input_lines.insert (m_input_lines.end(), m_utf8conv.from_bytes (lines[index])); 681 #else 682 m_input_lines.insert (m_input_lines.end(), lines[index]); 683 #endif 684 } 685 } 686 } 687 if (end_of_input) 688 { 689 fprintf (m_output_file, "\n"); 690 m_editor_status = EditorStatus::Complete; 691 return CC_NEWLINE; 692 } 693 } 694 695 // Apply smart indentation 696 if (m_fix_indentation_callback) 697 { 698 StringList lines = GetInputAsStringList (m_current_line_index + 1); 699 #if LLDB_EDITLINE_USE_WCHAR 700 lines.AppendString (m_utf8conv.to_bytes (new_line_fragment)); 701 #else 702 lines.AppendString (new_line_fragment); 703 #endif 704 705 int indent_correction = m_fix_indentation_callback (this, lines, 0, m_fix_indentation_callback_baton); 706 new_line_fragment = FixIndentation(new_line_fragment, indent_correction); 707 m_revert_cursor_index = GetIndentation(new_line_fragment); 708 } 709 } 710 711 // Insert the new line and repaint everything from the split line on down 712 m_input_lines.insert (m_input_lines.begin() + m_current_line_index + 1, new_line_fragment); 713 MoveCursor (CursorLocation::EditingCursor, CursorLocation::EditingPrompt); 714 DisplayInput (m_current_line_index); 715 716 // Reposition the cursor to the right line and prepare to edit the new line 717 SetCurrentLine (m_current_line_index + 1); 718 MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingPrompt); 719 return CC_NEWLINE; 720 } 721 722 unsigned char 723 Editline::DeleteNextCharCommand (int ch) 724 { 725 LineInfoW * info = const_cast<LineInfoW *>(el_wline (m_editline)); 726 727 // Just delete the next character normally if possible 728 if (info->cursor < info->lastchar) 729 { 730 info->cursor++; 731 el_deletestr (m_editline, 1); 732 return CC_REFRESH; 733 } 734 735 // Fail when at the end of the last line, except when ^D is pressed on 736 // the line is empty, in which case it is treated as EOF 737 if (m_current_line_index == m_input_lines.size() - 1) 738 { 739 if (ch == 4 && info->buffer == info->lastchar) 740 { 741 fprintf (m_output_file, "^D\n"); 742 m_editor_status = EditorStatus::EndOfInput; 743 return CC_EOF; 744 } 745 return CC_ERROR; 746 } 747 748 // Prepare to combine this line with the one below 749 MoveCursor (CursorLocation::EditingCursor, CursorLocation::EditingPrompt); 750 751 // Insert the next line of text at the cursor and restore the cursor position 752 const EditLineCharType * cursor = info->cursor; 753 el_winsertstr (m_editline, m_input_lines[m_current_line_index + 1].c_str()); 754 info->cursor = cursor; 755 SaveEditedLine(); 756 757 // Delete the extra line 758 m_input_lines.erase (m_input_lines.begin() + m_current_line_index + 1); 759 760 // Clear and repaint from this line on down 761 DisplayInput (m_current_line_index); 762 MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingCursor); 763 return CC_REFRESH; 764 } 765 766 unsigned char 767 Editline::DeletePreviousCharCommand (int ch) 768 { 769 LineInfoW * info = const_cast<LineInfoW *>(el_wline (m_editline)); 770 771 // Just delete the previous character normally when not at the start of a line 772 if (info->cursor > info->buffer) 773 { 774 el_deletestr (m_editline, 1); 775 return CC_REFRESH; 776 } 777 778 // No prior line and no prior character? Let the user know 779 if (m_current_line_index == 0) 780 return CC_ERROR; 781 782 // No prior character, but prior line? Combine with the line above 783 SaveEditedLine(); 784 SetCurrentLine (m_current_line_index - 1); 785 auto priorLine = m_input_lines[m_current_line_index]; 786 m_input_lines.erase (m_input_lines.begin() + m_current_line_index); 787 m_input_lines[m_current_line_index] = priorLine + m_input_lines[m_current_line_index]; 788 789 // Repaint from the new line down 790 fprintf (m_output_file, ANSI_UP_N_ROWS ANSI_SET_COLUMN_N, CountRowsForLine (priorLine), 1); 791 DisplayInput (m_current_line_index); 792 793 // Put the cursor back where libedit expects it to be before returning to editing 794 // by telling libedit about the newly inserted text 795 MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingPrompt); 796 el_winsertstr (m_editline, priorLine.c_str()); 797 return CC_REDISPLAY; 798 } 799 800 unsigned char 801 Editline::PreviousLineCommand (int ch) 802 { 803 SaveEditedLine(); 804 805 if (m_current_line_index == 0) { 806 return RecallHistory (true); 807 } 808 809 // Start from a known location 810 MoveCursor (CursorLocation::EditingCursor, CursorLocation::EditingPrompt); 811 812 // Treat moving up from a blank last line as a deletion of that line 813 if (m_current_line_index == m_input_lines.size() - 1 && IsOnlySpaces()) 814 { 815 m_input_lines.erase (m_input_lines.begin() + m_current_line_index); 816 fprintf (m_output_file, ANSI_CLEAR_BELOW); 817 } 818 819 SetCurrentLine (m_current_line_index - 1); 820 fprintf (m_output_file, ANSI_UP_N_ROWS ANSI_SET_COLUMN_N, 821 CountRowsForLine (m_input_lines[m_current_line_index]), 1); 822 return CC_NEWLINE; 823 } 824 825 unsigned char 826 Editline::NextLineCommand (int ch) 827 { 828 SaveEditedLine(); 829 830 // Handle attempts to move down from the last line 831 if (m_current_line_index == m_input_lines.size() - 1) 832 { 833 // Don't add an extra line if the existing last line is blank, move through history instead 834 if (IsOnlySpaces()) 835 { 836 return RecallHistory (false); 837 } 838 839 // Determine indentation for the new line 840 int indentation = 0; 841 if (m_fix_indentation_callback) 842 { 843 StringList lines = GetInputAsStringList(); 844 lines.AppendString(""); 845 indentation = m_fix_indentation_callback (this, lines, 0, m_fix_indentation_callback_baton); 846 } 847 m_input_lines.insert (m_input_lines.end(), EditLineStringType (indentation, EditLineCharType(' '))); 848 } 849 850 // Move down past the current line using newlines to force scrolling if needed 851 SetCurrentLine (m_current_line_index + 1); 852 const LineInfoW * info = el_wline (m_editline); 853 int cursor_position = (int)((info->cursor - info->buffer) + GetPromptWidth()); 854 int cursor_row = cursor_position / m_terminal_width; 855 for (int line_count = 0; line_count < m_current_line_rows - cursor_row; line_count++) 856 { 857 fprintf (m_output_file, "\n"); 858 } 859 return CC_NEWLINE; 860 } 861 862 unsigned char 863 Editline::FixIndentationCommand (int ch) 864 { 865 if (!m_fix_indentation_callback) 866 return CC_NORM; 867 868 // Insert the character typed before proceeding 869 EditLineCharType inserted[] = { (EditLineCharType)ch, 0 }; 870 el_winsertstr (m_editline, inserted); 871 LineInfoW * info = const_cast<LineInfoW *>(el_wline (m_editline)); 872 int cursor_position = info->cursor - info->buffer; 873 874 // Save the edits and determine the correct indentation level 875 SaveEditedLine(); 876 StringList lines = GetInputAsStringList (m_current_line_index + 1); 877 int indent_correction = m_fix_indentation_callback (this, lines, cursor_position, m_fix_indentation_callback_baton); 878 879 // If it is already correct no special work is needed 880 if (indent_correction == 0) 881 return CC_REFRESH; 882 883 // Change the indentation level of the line 884 std::string currentLine = lines.GetStringAtIndex (m_current_line_index); 885 if (indent_correction > 0) 886 { 887 currentLine = currentLine.insert (0, indent_correction, ' '); 888 } 889 else 890 { 891 currentLine = currentLine.erase (0, -indent_correction); 892 } 893 #if LLDB_EDITLINE_USE_WCHAR 894 m_input_lines[m_current_line_index] = m_utf8conv.from_bytes (currentLine); 895 #else 896 m_input_lines[m_current_line_index] = currentLine; 897 #endif 898 899 // Update the display to reflect the change 900 MoveCursor (CursorLocation::EditingCursor, CursorLocation::EditingPrompt); 901 DisplayInput (m_current_line_index); 902 903 // Reposition the cursor back on the original line and prepare to restart editing 904 // with a new cursor position 905 SetCurrentLine (m_current_line_index); 906 MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingPrompt); 907 m_revert_cursor_index = cursor_position + indent_correction; 908 return CC_NEWLINE; 909 } 910 911 unsigned char 912 Editline::RevertLineCommand (int ch) 913 { 914 el_winsertstr (m_editline, m_input_lines[m_current_line_index].c_str()); 915 if (m_revert_cursor_index >= 0) 916 { 917 LineInfoW * info = const_cast<LineInfoW *>(el_wline (m_editline)); 918 info->cursor = info->buffer + m_revert_cursor_index; 919 if (info->cursor > info->lastchar) 920 { 921 info->cursor = info->lastchar; 922 } 923 m_revert_cursor_index = -1; 924 } 925 return CC_REFRESH; 926 } 927 928 unsigned char 929 Editline::BufferStartCommand (int ch) 930 { 931 SaveEditedLine(); 932 MoveCursor (CursorLocation::EditingCursor, CursorLocation::BlockStart); 933 SetCurrentLine (0); 934 m_revert_cursor_index = 0; 935 return CC_NEWLINE; 936 } 937 938 unsigned char 939 Editline::BufferEndCommand (int ch) 940 { 941 SaveEditedLine(); 942 MoveCursor (CursorLocation::EditingCursor, CursorLocation::BlockEnd); 943 SetCurrentLine ((int)m_input_lines.size() - 1); 944 MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingPrompt); 945 return CC_NEWLINE; 946 } 947 948 unsigned char 949 Editline::TabCommand (int ch) 950 { 951 if (m_completion_callback == nullptr) 952 return CC_ERROR; 953 954 const LineInfo *line_info = el_line (m_editline); 955 StringList completions; 956 int page_size = 40; 957 958 const int num_completions = m_completion_callback (line_info->buffer, 959 line_info->cursor, 960 line_info->lastchar, 961 0, // Don't skip any matches (start at match zero) 962 -1, // Get all the matches 963 completions, 964 m_completion_callback_baton); 965 966 if (num_completions == 0) 967 return CC_ERROR; 968 // if (num_completions == -1) 969 // { 970 // el_insertstr (m_editline, m_completion_key); 971 // return CC_REDISPLAY; 972 // } 973 // else 974 if (num_completions == -2) 975 { 976 // Replace the entire line with the first string... 977 el_deletestr (m_editline, line_info->cursor - line_info->buffer); 978 el_insertstr (m_editline, completions.GetStringAtIndex (0)); 979 return CC_REDISPLAY; 980 } 981 982 // If we get a longer match display that first. 983 const char *completion_str = completions.GetStringAtIndex (0); 984 if (completion_str != nullptr && *completion_str != '\0') 985 { 986 el_insertstr (m_editline, completion_str); 987 return CC_REDISPLAY; 988 } 989 990 if (num_completions > 1) 991 { 992 int num_elements = num_completions + 1; 993 fprintf (m_output_file, "\n" ANSI_CLEAR_BELOW "Available completions:"); 994 if (num_completions < page_size) 995 { 996 for (int i = 1; i < num_elements; i++) 997 { 998 completion_str = completions.GetStringAtIndex (i); 999 fprintf (m_output_file, "\n\t%s", completion_str); 1000 } 1001 fprintf (m_output_file, "\n"); 1002 } 1003 else 1004 { 1005 int cur_pos = 1; 1006 char reply; 1007 int got_char; 1008 while (cur_pos < num_elements) 1009 { 1010 int endpoint = cur_pos + page_size; 1011 if (endpoint > num_elements) 1012 endpoint = num_elements; 1013 for (; cur_pos < endpoint; cur_pos++) 1014 { 1015 completion_str = completions.GetStringAtIndex (cur_pos); 1016 fprintf (m_output_file, "\n\t%s", completion_str); 1017 } 1018 1019 if (cur_pos >= num_elements) 1020 { 1021 fprintf (m_output_file, "\n"); 1022 break; 1023 } 1024 1025 fprintf (m_output_file, "\nMore (Y/n/a): "); 1026 reply = 'n'; 1027 got_char = el_getc(m_editline, &reply); 1028 if (got_char == -1 || reply == 'n') 1029 break; 1030 if (reply == 'a') 1031 page_size = num_elements - cur_pos; 1032 } 1033 } 1034 DisplayInput(); 1035 MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor); 1036 } 1037 return CC_REDISPLAY; 1038 } 1039 1040 void 1041 Editline::ConfigureEditor (bool multiline) 1042 { 1043 if (m_editline && m_multiline_enabled == multiline) 1044 return; 1045 m_multiline_enabled = multiline; 1046 1047 if (m_editline) 1048 { 1049 // Disable edit mode to stop the terminal from flushing all input 1050 // during the call to el_end() since we expect to have multiple editline 1051 // instances in this program. 1052 el_set (m_editline, EL_EDITMODE, 0); 1053 el_end (m_editline); 1054 } 1055 1056 m_editline = el_init (m_editor_name.c_str(), m_input_file, m_output_file, m_error_file); 1057 TerminalSizeChanged(); 1058 1059 if (m_history_sp && m_history_sp->IsValid()) 1060 { 1061 m_history_sp->Load(); 1062 el_wset (m_editline, EL_HIST, history, m_history_sp->GetHistoryPtr()); 1063 } 1064 el_set (m_editline, EL_CLIENTDATA, this); 1065 el_set (m_editline, EL_SIGNAL, 0); 1066 el_set (m_editline, EL_EDITOR, "emacs"); 1067 el_set (m_editline, EL_PROMPT, (EditlinePromptCallbackType)([] (EditLine *editline) { 1068 return Editline::InstanceFor (editline)->Prompt(); 1069 })); 1070 1071 el_wset (m_editline, EL_GETCFN, 1072 (EditlineGetCharCallbackType)([] (EditLine * editline, EditLineCharType * c) { 1073 return Editline::InstanceFor (editline)->GetCharacter (c); 1074 })); 1075 1076 // Commands used for multiline support, registered whether or not they're used 1077 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-break-line"), EditLineConstString("Insert a line break"), 1078 (EditlineCommandCallbackType)( 1079 [](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->BreakLineCommand(ch); })); 1080 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-delete-next-char"), 1081 EditLineConstString("Delete next character"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { 1082 return Editline::InstanceFor(editline)->DeleteNextCharCommand(ch); 1083 })); 1084 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-delete-previous-char"), 1085 EditLineConstString("Delete previous character"), 1086 (EditlineCommandCallbackType)([](EditLine *editline, int ch) { 1087 return Editline::InstanceFor(editline)->DeletePreviousCharCommand(ch); 1088 })); 1089 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-previous-line"), 1090 EditLineConstString("Move to previous line"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { 1091 return Editline::InstanceFor(editline)->PreviousLineCommand(ch); 1092 })); 1093 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-next-line"), EditLineConstString("Move to next line"), 1094 (EditlineCommandCallbackType)( 1095 [](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->NextLineCommand(ch); })); 1096 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-buffer-start"), 1097 EditLineConstString("Move to start of buffer"), 1098 (EditlineCommandCallbackType)( 1099 [](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->BufferStartCommand(ch); })); 1100 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-buffer-end"), EditLineConstString("Move to end of buffer"), 1101 (EditlineCommandCallbackType)( 1102 [](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->BufferEndCommand(ch); })); 1103 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-fix-indentation"), 1104 EditLineConstString("Fix line indentation"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { 1105 return Editline::InstanceFor (editline)->FixIndentationCommand (ch); 1106 })); 1107 1108 // Register the complete callback under two names for compatibility with older clients using 1109 // custom .editrc files (largely becuase libedit has a bad bug where if you have a bind command 1110 // that tries to bind to a function name that doesn't exist, it can corrupt the heap and 1111 // crash your process later.) 1112 EditlineCommandCallbackType complete_callback = [] (EditLine * editline, int ch) { 1113 return Editline::InstanceFor (editline)->TabCommand (ch); 1114 }; 1115 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-complete"), EditLineConstString("Invoke completion"), 1116 complete_callback); 1117 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb_complete"), EditLineConstString("Invoke completion"), 1118 complete_callback); 1119 1120 // General bindings we don't mind being overridden 1121 if (!multiline) { 1122 el_set (m_editline, EL_BIND, "^r", "em-inc-search-prev", NULL); // Cycle through backwards search, entering string 1123 } 1124 el_set (m_editline, EL_BIND, "^w", "ed-delete-prev-word", NULL); // Delete previous word, behave like bash in emacs mode 1125 el_set (m_editline, EL_BIND, "\t", "lldb-complete", NULL); // Bind TAB to auto complete 1126 1127 // Allow user-specific customization prior to registering bindings we absolutely require 1128 el_source (m_editline, NULL); 1129 1130 // Register an internal binding that external developers shouldn't use 1131 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-revert-line"), 1132 EditLineConstString("Revert line to saved state"), 1133 (EditlineCommandCallbackType)( 1134 [](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->RevertLineCommand(ch); })); 1135 1136 // Register keys that perform auto-indent correction 1137 if (m_fix_indentation_callback && m_fix_indentation_callback_chars) 1138 { 1139 char bind_key[2] = { 0, 0 }; 1140 const char * indent_chars = m_fix_indentation_callback_chars; 1141 while (*indent_chars) 1142 { 1143 bind_key[0] = *indent_chars; 1144 el_set (m_editline, EL_BIND, bind_key, "lldb-fix-indentation", NULL); 1145 ++indent_chars; 1146 } 1147 } 1148 1149 // Multi-line editor bindings 1150 if (multiline) 1151 { 1152 el_set (m_editline, EL_BIND, "\n", "lldb-break-line", NULL); 1153 el_set (m_editline, EL_BIND, "\r", "lldb-break-line", NULL); 1154 el_set (m_editline, EL_BIND, "^p", "lldb-previous-line", NULL); 1155 el_set (m_editline, EL_BIND, "^n", "lldb-next-line", NULL); 1156 el_set (m_editline, EL_BIND, "^?", "lldb-delete-previous-char", NULL); 1157 el_set (m_editline, EL_BIND, "^d", "lldb-delete-next-char", NULL); 1158 el_set (m_editline, EL_BIND, ESCAPE "[3~", "lldb-delete-next-char", NULL); 1159 el_set (m_editline, EL_BIND, ESCAPE "[\\^", "lldb-revert-line", NULL); 1160 1161 // Editor-specific bindings 1162 if (IsEmacs()) 1163 { 1164 el_set (m_editline, EL_BIND, ESCAPE "<", "lldb-buffer-start", NULL); 1165 el_set (m_editline, EL_BIND, ESCAPE ">", "lldb-buffer-end", NULL); 1166 el_set (m_editline, EL_BIND, ESCAPE "[A", "lldb-previous-line", NULL); 1167 el_set (m_editline, EL_BIND, ESCAPE "[B", "lldb-next-line", NULL); 1168 } 1169 else 1170 { 1171 el_set (m_editline, EL_BIND, "^H", "lldb-delete-previous-char", NULL); 1172 1173 el_set (m_editline, EL_BIND, "-a", ESCAPE "[A", "lldb-previous-line", NULL); 1174 el_set (m_editline, EL_BIND, "-a", ESCAPE "[B", "lldb-next-line", NULL); 1175 el_set (m_editline, EL_BIND, "-a", "x", "lldb-delete-next-char", NULL); 1176 el_set (m_editline, EL_BIND, "-a", "^H", "lldb-delete-previous-char", NULL); 1177 el_set (m_editline, EL_BIND, "-a", "^?", "lldb-delete-previous-char", NULL); 1178 1179 // Escape is absorbed exiting edit mode, so re-register important sequences 1180 // without the prefix 1181 el_set (m_editline, EL_BIND, "-a", "[A", "lldb-previous-line", NULL); 1182 el_set (m_editline, EL_BIND, "-a", "[B", "lldb-next-line", NULL); 1183 el_set (m_editline, EL_BIND, "-a", "[\\^", "lldb-revert-line", NULL); 1184 } 1185 } 1186 } 1187 1188 //------------------------------------------------------------------ 1189 // Editline public methods 1190 //------------------------------------------------------------------ 1191 1192 Editline * 1193 Editline::InstanceFor (EditLine * editline) 1194 { 1195 Editline * editor; 1196 el_get (editline, EL_CLIENTDATA, &editor); 1197 return editor; 1198 } 1199 1200 Editline::Editline (const char * editline_name, FILE * input_file, FILE * output_file, FILE * error_file, bool color_prompts) : 1201 m_editor_status (EditorStatus::Complete), 1202 m_color_prompts(color_prompts), 1203 m_input_file (input_file), 1204 m_output_file (output_file), 1205 m_error_file (error_file), 1206 m_input_connection (fileno(input_file), false) 1207 { 1208 // Get a shared history instance 1209 m_editor_name = (editline_name == nullptr) ? "lldb-tmp" : editline_name; 1210 m_history_sp = EditlineHistory::GetHistory (m_editor_name); 1211 1212 #ifdef USE_SETUPTERM_WORKAROUND 1213 if (m_output_file) 1214 { 1215 const int term_fd = fileno(m_output_file); 1216 if (term_fd != -1) 1217 { 1218 static std::mutex *g_init_terminal_fds_mutex_ptr = nullptr; 1219 static std::set<int> *g_init_terminal_fds_ptr = nullptr; 1220 static std::once_flag g_once_flag; 1221 std::call_once(g_once_flag, [&]() { 1222 g_init_terminal_fds_mutex_ptr = new std::mutex(); // NOTE: Leak to avoid C++ destructor chain issues 1223 g_init_terminal_fds_ptr = new std::set<int>(); // NOTE: Leak to avoid C++ destructor chain issues 1224 }); 1225 1226 // We must make sure to initialize the terminal a given file descriptor 1227 // only once. If we do this multiple times, we start leaking memory. 1228 std::lock_guard<std::mutex> guard(*g_init_terminal_fds_mutex_ptr); 1229 if (g_init_terminal_fds_ptr->find(term_fd) == g_init_terminal_fds_ptr->end()) 1230 { 1231 g_init_terminal_fds_ptr->insert(term_fd); 1232 setupterm((char *)0, term_fd, (int *)0); 1233 } 1234 } 1235 } 1236 #endif 1237 } 1238 1239 Editline::~Editline() 1240 { 1241 if (m_editline) 1242 { 1243 // Disable edit mode to stop the terminal from flushing all input 1244 // during the call to el_end() since we expect to have multiple editline 1245 // instances in this program. 1246 el_set (m_editline, EL_EDITMODE, 0); 1247 el_end (m_editline); 1248 m_editline = nullptr; 1249 } 1250 1251 // EditlineHistory objects are sometimes shared between multiple 1252 // Editline instances with the same program name. So just release 1253 // our shared pointer and if we are the last owner, it will save the 1254 // history to the history save file automatically. 1255 m_history_sp.reset(); 1256 } 1257 1258 void 1259 Editline::SetPrompt (const char * prompt) 1260 { 1261 m_set_prompt = prompt == nullptr ? "" : prompt; 1262 } 1263 1264 void 1265 Editline::SetContinuationPrompt (const char * continuation_prompt) 1266 { 1267 m_set_continuation_prompt = continuation_prompt == nullptr ? "" : continuation_prompt; 1268 } 1269 1270 void 1271 Editline::TerminalSizeChanged() 1272 { 1273 if (m_editline != nullptr) 1274 { 1275 el_resize (m_editline); 1276 int columns; 1277 // Despite the man page claiming non-zero indicates success, it's actually zero 1278 if (el_get (m_editline, EL_GETTC, "co", &columns) == 0) 1279 { 1280 m_terminal_width = columns; 1281 if (m_current_line_rows != -1) 1282 { 1283 const LineInfoW * info = el_wline (m_editline); 1284 int lineLength = (int)((info->lastchar - info->buffer) + GetPromptWidth()); 1285 m_current_line_rows = (lineLength / columns) + 1; 1286 } 1287 } 1288 else 1289 { 1290 m_terminal_width = INT_MAX; 1291 m_current_line_rows = 1; 1292 } 1293 } 1294 } 1295 1296 const char * 1297 Editline::GetPrompt() 1298 { 1299 return m_set_prompt.c_str(); 1300 } 1301 1302 uint32_t 1303 Editline::GetCurrentLine() 1304 { 1305 return m_current_line_index; 1306 } 1307 1308 bool 1309 Editline::Interrupt() 1310 { 1311 bool result = true; 1312 std::lock_guard<std::mutex> guard(m_output_mutex); 1313 if (m_editor_status == EditorStatus::Editing) { 1314 fprintf(m_output_file, "^C\n"); 1315 result = m_input_connection.InterruptRead(); 1316 } 1317 m_editor_status = EditorStatus::Interrupted; 1318 return result; 1319 } 1320 1321 bool 1322 Editline::Cancel() 1323 { 1324 bool result = true; 1325 std::lock_guard<std::mutex> guard(m_output_mutex); 1326 if (m_editor_status == EditorStatus::Editing) { 1327 MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart); 1328 fprintf(m_output_file, ANSI_CLEAR_BELOW); 1329 result = m_input_connection.InterruptRead(); 1330 } 1331 m_editor_status = EditorStatus::Interrupted; 1332 return result; 1333 } 1334 1335 void 1336 Editline::SetAutoCompleteCallback (CompleteCallbackType callback, void * baton) 1337 { 1338 m_completion_callback = callback; 1339 m_completion_callback_baton = baton; 1340 } 1341 1342 void 1343 Editline::SetIsInputCompleteCallback (IsInputCompleteCallbackType callback, void * baton) 1344 { 1345 m_is_input_complete_callback = callback; 1346 m_is_input_complete_callback_baton = baton; 1347 } 1348 1349 bool 1350 Editline::SetFixIndentationCallback (FixIndentationCallbackType callback, 1351 void * baton, 1352 const char * indent_chars) 1353 { 1354 m_fix_indentation_callback = callback; 1355 m_fix_indentation_callback_baton = baton; 1356 m_fix_indentation_callback_chars = indent_chars; 1357 return false; 1358 } 1359 1360 bool 1361 Editline::GetLine (std::string &line, bool &interrupted) 1362 { 1363 ConfigureEditor (false); 1364 m_input_lines = std::vector<EditLineStringType>(); 1365 m_input_lines.insert (m_input_lines.begin(), EditLineConstString("")); 1366 1367 std::lock_guard<std::mutex> guard(m_output_mutex); 1368 1369 lldbassert(m_editor_status != EditorStatus::Editing); 1370 if (m_editor_status == EditorStatus::Interrupted) 1371 { 1372 m_editor_status = EditorStatus::Complete; 1373 interrupted = true; 1374 return true; 1375 } 1376 1377 SetCurrentLine (0); 1378 m_in_history = false; 1379 m_editor_status = EditorStatus::Editing; 1380 m_revert_cursor_index = -1; 1381 1382 int count; 1383 auto input = el_wgets (m_editline, &count); 1384 1385 interrupted = m_editor_status == EditorStatus::Interrupted; 1386 if (!interrupted) 1387 { 1388 if (input == nullptr) 1389 { 1390 fprintf (m_output_file, "\n"); 1391 m_editor_status = EditorStatus::EndOfInput; 1392 } 1393 else 1394 { 1395 m_history_sp->Enter (input); 1396 #if LLDB_EDITLINE_USE_WCHAR 1397 line = m_utf8conv.to_bytes (SplitLines (input)[0]); 1398 #else 1399 line = SplitLines (input)[0]; 1400 #endif 1401 m_editor_status = EditorStatus::Complete; 1402 } 1403 } 1404 return m_editor_status != EditorStatus::EndOfInput; 1405 } 1406 1407 bool 1408 Editline::GetLines (int first_line_number, StringList &lines, bool &interrupted) 1409 { 1410 ConfigureEditor (true); 1411 1412 // Print the initial input lines, then move the cursor back up to the start of input 1413 SetBaseLineNumber (first_line_number); 1414 m_input_lines = std::vector<EditLineStringType>(); 1415 m_input_lines.insert (m_input_lines.begin(), EditLineConstString("")); 1416 1417 std::lock_guard<std::mutex> guard(m_output_mutex); 1418 // Begin the line editing loop 1419 DisplayInput(); 1420 SetCurrentLine (0); 1421 MoveCursor (CursorLocation::BlockEnd, CursorLocation::BlockStart); 1422 m_editor_status = EditorStatus::Editing; 1423 m_in_history = false; 1424 1425 m_revert_cursor_index = -1; 1426 while (m_editor_status == EditorStatus::Editing) 1427 { 1428 int count; 1429 m_current_line_rows = -1; 1430 el_wpush (m_editline, EditLineConstString("\x1b[^")); // Revert to the existing line content 1431 el_wgets (m_editline, &count); 1432 } 1433 1434 interrupted = m_editor_status == EditorStatus::Interrupted; 1435 if (!interrupted) 1436 { 1437 // Save the completed entry in history before returning 1438 m_history_sp->Enter (CombineLines (m_input_lines).c_str()); 1439 1440 lines = GetInputAsStringList(); 1441 } 1442 return m_editor_status != EditorStatus::EndOfInput; 1443 } 1444 1445 void 1446 Editline::PrintAsync (Stream *stream, const char *s, size_t len) 1447 { 1448 std::lock_guard<std::mutex> guard(m_output_mutex); 1449 if (m_editor_status == EditorStatus::Editing) 1450 { 1451 MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart); 1452 fprintf(m_output_file, ANSI_CLEAR_BELOW); 1453 } 1454 stream->Write (s, len); 1455 stream->Flush(); 1456 if (m_editor_status == EditorStatus::Editing) 1457 { 1458 DisplayInput(); 1459 MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor); 1460 } 1461 } 1462