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