1""" 2Data structures for the Buffer. 3It holds the text, cursor position, history, etc... 4""" 5from __future__ import unicode_literals 6 7from .auto_suggest import AutoSuggest 8from .clipboard import ClipboardData 9from .completion import Completer, Completion, CompleteEvent 10from .document import Document 11from .enums import IncrementalSearchDirection 12from .filters import to_simple_filter 13from .history import History, InMemoryHistory 14from .search_state import SearchState 15from .selection import SelectionType, SelectionState, PasteMode 16from .utils import Event 17from .cache import FastDictCache 18from .validation import ValidationError 19 20from six.moves import range 21 22import os 23import re 24import six 25import subprocess 26import tempfile 27 28__all__ = ( 29 'EditReadOnlyBuffer', 30 'AcceptAction', 31 'Buffer', 32 'indent', 33 'unindent', 34 'reshape_text', 35) 36 37 38class EditReadOnlyBuffer(Exception): 39 " Attempt editing of read-only :class:`.Buffer`. " 40 41 42class AcceptAction(object): 43 """ 44 What to do when the input is accepted by the user. 45 (When Enter was pressed in the command line.) 46 47 :param handler: (optional) A callable which takes a 48 :class:`~prompt_toolkit.interface.CommandLineInterface` and 49 :class:`~prompt_toolkit.document.Document`. It is called when the user 50 accepts input. 51 """ 52 def __init__(self, handler=None): 53 assert handler is None or callable(handler) 54 self.handler = handler 55 56 @classmethod 57 def run_in_terminal(cls, handler, render_cli_done=False): 58 """ 59 Create an :class:`.AcceptAction` that runs the given handler in the 60 terminal. 61 62 :param render_cli_done: When True, render the interface in the 'Done' 63 state first, then execute the function. If False, erase the 64 interface instead. 65 """ 66 def _handler(cli, buffer): 67 cli.run_in_terminal(lambda: handler(cli, buffer), render_cli_done=render_cli_done) 68 return AcceptAction(handler=_handler) 69 70 @property 71 def is_returnable(self): 72 """ 73 True when there is something handling accept. 74 """ 75 return bool(self.handler) 76 77 def validate_and_handle(self, cli, buffer): 78 """ 79 Validate buffer and handle the accept action. 80 """ 81 if buffer.validate(): 82 if self.handler: 83 self.handler(cli, buffer) 84 85 buffer.append_to_history() 86 87 88def _return_document_handler(cli, buffer): 89 # Set return value. 90 cli.set_return_value(buffer.document) 91 92 # Make sure that if we run this UI again, that we reset this buffer, next 93 # time. 94 def reset_this_buffer(): 95 buffer.reset() 96 cli.pre_run_callables.append(reset_this_buffer) 97 98 99AcceptAction.RETURN_DOCUMENT = AcceptAction(_return_document_handler) 100AcceptAction.IGNORE = AcceptAction(handler=None) 101 102 103class ValidationState(object): 104 " The validation state of a buffer. This is set after the validation. " 105 VALID = 'VALID' 106 INVALID = 'INVALID' 107 UNKNOWN = 'UNKNOWN' 108 109 110class CompletionState(object): 111 """ 112 Immutable class that contains a completion state. 113 """ 114 def __init__(self, original_document, current_completions=None, complete_index=None): 115 #: Document as it was when the completion started. 116 self.original_document = original_document 117 118 #: List of all the current Completion instances which are possible at 119 #: this point. 120 self.current_completions = current_completions or [] 121 122 #: Position in the `current_completions` array. 123 #: This can be `None` to indicate "no completion", the original text. 124 self.complete_index = complete_index # Position in the `_completions` array. 125 126 def __repr__(self): 127 return '%s(%r, <%r> completions, index=%r)' % ( 128 self.__class__.__name__, 129 self.original_document, len(self.current_completions), self.complete_index) 130 131 def go_to_index(self, index): 132 """ 133 Create a new :class:`.CompletionState` object with the new index. 134 """ 135 return CompletionState(self.original_document, self.current_completions, complete_index=index) 136 137 def new_text_and_position(self): 138 """ 139 Return (new_text, new_cursor_position) for this completion. 140 """ 141 if self.complete_index is None: 142 return self.original_document.text, self.original_document.cursor_position 143 else: 144 original_text_before_cursor = self.original_document.text_before_cursor 145 original_text_after_cursor = self.original_document.text_after_cursor 146 147 c = self.current_completions[self.complete_index] 148 if c.start_position == 0: 149 before = original_text_before_cursor 150 else: 151 before = original_text_before_cursor[:c.start_position] 152 153 new_text = before + c.text + original_text_after_cursor 154 new_cursor_position = len(before) + len(c.text) 155 return new_text, new_cursor_position 156 157 @property 158 def current_completion(self): 159 """ 160 Return the current completion, or return `None` when no completion is 161 selected. 162 """ 163 if self.complete_index is not None: 164 return self.current_completions[self.complete_index] 165 166 167_QUOTED_WORDS_RE = re.compile(r"""(\s+|".*?"|'.*?')""") 168 169 170class YankNthArgState(object): 171 """ 172 For yank-last-arg/yank-nth-arg: Keep track of where we are in the history. 173 """ 174 def __init__(self, history_position=0, n=-1, previous_inserted_word=''): 175 self.history_position = history_position 176 self.previous_inserted_word = previous_inserted_word 177 self.n = n 178 179 def __repr__(self): 180 return '%s(history_position=%r, n=%r, previous_inserted_word=%r)' % ( 181 self.__class__.__name__, self.history_position, self.n, 182 self.previous_inserted_word) 183 184 185class Buffer(object): 186 """ 187 The core data structure that holds the text and cursor position of the 188 current input line and implements all text manupulations on top of it. It 189 also implements the history, undo stack and the completion state. 190 191 :param completer: :class:`~prompt_toolkit.completion.Completer` instance. 192 :param history: :class:`~prompt_toolkit.history.History` instance. 193 :param tempfile_suffix: Suffix to be appended to the tempfile for the 'open 194 in editor' function. 195 196 Events: 197 198 :param on_text_changed: When the buffer text changes. (Callable on None.) 199 :param on_text_insert: When new text is inserted. (Callable on None.) 200 :param on_cursor_position_changed: When the cursor moves. (Callable on None.) 201 202 Filters: 203 204 :param is_multiline: :class:`~prompt_toolkit.filters.SimpleFilter` to 205 indicate whether we should consider this buffer a multiline input. If 206 so, key bindings can decide to insert newlines when pressing [Enter]. 207 (Instead of accepting the input.) 208 :param complete_while_typing: :class:`~prompt_toolkit.filters.SimpleFilter` 209 instance. Decide whether or not to do asynchronous autocompleting while 210 typing. 211 :param enable_history_search: :class:`~prompt_toolkit.filters.SimpleFilter` 212 to indicate when up-arrow partial string matching is enabled. It is 213 adviced to not enable this at the same time as `complete_while_typing`, 214 because when there is an autocompletion found, the up arrows usually 215 browse through the completions, rather than through the history. 216 :param read_only: :class:`~prompt_toolkit.filters.SimpleFilter`. When True, 217 changes will not be allowed. 218 """ 219 def __init__(self, completer=None, auto_suggest=None, history=None, 220 validator=None, tempfile_suffix='', 221 is_multiline=False, complete_while_typing=False, 222 enable_history_search=False, initial_document=None, 223 accept_action=AcceptAction.IGNORE, read_only=False, 224 on_text_changed=None, on_text_insert=None, on_cursor_position_changed=None): 225 226 # Accept both filters and booleans as input. 227 enable_history_search = to_simple_filter(enable_history_search) 228 is_multiline = to_simple_filter(is_multiline) 229 complete_while_typing = to_simple_filter(complete_while_typing) 230 read_only = to_simple_filter(read_only) 231 232 # Validate input. 233 assert completer is None or isinstance(completer, Completer) 234 assert auto_suggest is None or isinstance(auto_suggest, AutoSuggest) 235 assert history is None or isinstance(history, History) 236 assert on_text_changed is None or callable(on_text_changed) 237 assert on_text_insert is None or callable(on_text_insert) 238 assert on_cursor_position_changed is None or callable(on_cursor_position_changed) 239 240 self.completer = completer 241 self.auto_suggest = auto_suggest 242 self.validator = validator 243 self.tempfile_suffix = tempfile_suffix 244 self.accept_action = accept_action 245 246 # Filters. (Usually, used by the key bindings to drive the buffer.) 247 self.is_multiline = is_multiline 248 self.complete_while_typing = complete_while_typing 249 self.enable_history_search = enable_history_search 250 self.read_only = read_only 251 252 # Text width. (For wrapping, used by the Vi 'gq' operator.) 253 self.text_width = 0 254 255 #: The command buffer history. 256 # Note that we shouldn't use a lazy 'or' here. bool(history) could be 257 # False when empty. 258 self.history = InMemoryHistory() if history is None else history 259 260 self.__cursor_position = 0 261 262 # Events 263 self.on_text_changed = Event(self, on_text_changed) 264 self.on_text_insert = Event(self, on_text_insert) 265 self.on_cursor_position_changed = Event(self, on_cursor_position_changed) 266 267 # Document cache. (Avoid creating new Document instances.) 268 self._document_cache = FastDictCache(Document, size=10) 269 270 self.reset(initial_document=initial_document) 271 272 def reset(self, initial_document=None, append_to_history=False): 273 """ 274 :param append_to_history: Append current input to history first. 275 """ 276 assert initial_document is None or isinstance(initial_document, Document) 277 278 if append_to_history: 279 self.append_to_history() 280 281 initial_document = initial_document or Document() 282 283 self.__cursor_position = initial_document.cursor_position 284 285 # `ValidationError` instance. (Will be set when the input is wrong.) 286 self.validation_error = None 287 self.validation_state = ValidationState.UNKNOWN 288 289 # State of the selection. 290 self.selection_state = None 291 292 # Multiple cursor mode. (When we press 'I' or 'A' in visual-block mode, 293 # we can insert text on multiple lines at once. This is implemented by 294 # using multiple cursors.) 295 self.multiple_cursor_positions = [] 296 297 # When doing consecutive up/down movements, prefer to stay at this column. 298 self.preferred_column = None 299 300 # State of complete browser 301 self.complete_state = None # For interactive completion through Ctrl-N/Ctrl-P. 302 303 # State of Emacs yank-nth-arg completion. 304 self.yank_nth_arg_state = None # for yank-nth-arg. 305 306 # Remember the document that we had *right before* the last paste 307 # operation. This is used for rotating through the kill ring. 308 self.document_before_paste = None 309 310 # Current suggestion. 311 self.suggestion = None 312 313 # The history search text. (Used for filtering the history when we 314 # browse through it.) 315 self.history_search_text = None 316 317 # Undo/redo stacks 318 self._undo_stack = [] # Stack of (text, cursor_position) 319 self._redo_stack = [] 320 321 #: The working lines. Similar to history, except that this can be 322 #: modified. The user can press arrow_up and edit previous entries. 323 #: Ctrl-C should reset this, and copy the whole history back in here. 324 #: Enter should process the current command and append to the real 325 #: history. 326 self._working_lines = self.history.strings[:] 327 self._working_lines.append(initial_document.text) 328 self.__working_index = len(self._working_lines) - 1 329 330 # <getters/setters> 331 332 def _set_text(self, value): 333 """ set text at current working_index. Return whether it changed. """ 334 working_index = self.working_index 335 working_lines = self._working_lines 336 337 original_value = working_lines[working_index] 338 working_lines[working_index] = value 339 340 # Return True when this text has been changed. 341 if len(value) != len(original_value): 342 # For Python 2, it seems that when two strings have a different 343 # length and one is a prefix of the other, Python still scans 344 # character by character to see whether the strings are different. 345 # (Some benchmarking showed significant differences for big 346 # documents. >100,000 of lines.) 347 return True 348 elif value != original_value: 349 return True 350 return False 351 352 def _set_cursor_position(self, value): 353 """ Set cursor position. Return whether it changed. """ 354 original_position = self.__cursor_position 355 self.__cursor_position = max(0, value) 356 357 return value != original_position 358 359 @property 360 def text(self): 361 return self._working_lines[self.working_index] 362 363 @text.setter 364 def text(self, value): 365 """ 366 Setting text. (When doing this, make sure that the cursor_position is 367 valid for this text. text/cursor_position should be consistent at any time, 368 otherwise set a Document instead.) 369 """ 370 assert isinstance(value, six.text_type), 'Got %r' % value 371 assert self.cursor_position <= len(value) 372 373 # Don't allow editing of read-only buffers. 374 if self.read_only(): 375 raise EditReadOnlyBuffer() 376 377 changed = self._set_text(value) 378 379 if changed: 380 self._text_changed() 381 382 # Reset history search text. 383 self.history_search_text = None 384 385 @property 386 def cursor_position(self): 387 return self.__cursor_position 388 389 @cursor_position.setter 390 def cursor_position(self, value): 391 """ 392 Setting cursor position. 393 """ 394 assert isinstance(value, int) 395 assert value <= len(self.text) 396 397 changed = self._set_cursor_position(value) 398 399 if changed: 400 self._cursor_position_changed() 401 402 @property 403 def working_index(self): 404 return self.__working_index 405 406 @working_index.setter 407 def working_index(self, value): 408 if self.__working_index != value: 409 self.__working_index = value 410 self._text_changed() 411 412 def _text_changed(self): 413 # Remove any validation errors and complete state. 414 self.validation_error = None 415 self.validation_state = ValidationState.UNKNOWN 416 self.complete_state = None 417 self.yank_nth_arg_state = None 418 self.document_before_paste = None 419 self.selection_state = None 420 self.suggestion = None 421 self.preferred_column = None 422 423 # fire 'on_text_changed' event. 424 self.on_text_changed.fire() 425 426 def _cursor_position_changed(self): 427 # Remove any validation errors and complete state. 428 self.validation_error = None 429 self.validation_state = ValidationState.UNKNOWN 430 self.complete_state = None 431 self.yank_nth_arg_state = None 432 self.document_before_paste = None 433 434 # Unset preferred_column. (Will be set after the cursor movement, if 435 # required.) 436 self.preferred_column = None 437 438 # Note that the cursor position can change if we have a selection the 439 # new position of the cursor determines the end of the selection. 440 441 # fire 'on_cursor_position_changed' event. 442 self.on_cursor_position_changed.fire() 443 444 @property 445 def document(self): 446 """ 447 Return :class:`~prompt_toolkit.document.Document` instance from the 448 current text, cursor position and selection state. 449 """ 450 return self._document_cache[ 451 self.text, self.cursor_position, self.selection_state] 452 453 @document.setter 454 def document(self, value): 455 """ 456 Set :class:`~prompt_toolkit.document.Document` instance. 457 458 This will set both the text and cursor position at the same time, but 459 atomically. (Change events will be triggered only after both have been set.) 460 """ 461 self.set_document(value) 462 463 def set_document(self, value, bypass_readonly=False): 464 """ 465 Set :class:`~prompt_toolkit.document.Document` instance. Like the 466 ``document`` property, but accept an ``bypass_readonly`` argument. 467 468 :param bypass_readonly: When True, don't raise an 469 :class:`.EditReadOnlyBuffer` exception, even 470 when the buffer is read-only. 471 """ 472 assert isinstance(value, Document) 473 474 # Don't allow editing of read-only buffers. 475 if not bypass_readonly and self.read_only(): 476 raise EditReadOnlyBuffer() 477 478 # Set text and cursor position first. 479 text_changed = self._set_text(value.text) 480 cursor_position_changed = self._set_cursor_position(value.cursor_position) 481 482 # Now handle change events. (We do this when text/cursor position is 483 # both set and consistent.) 484 if text_changed: 485 self._text_changed() 486 487 if cursor_position_changed: 488 self._cursor_position_changed() 489 490 # End of <getters/setters> 491 492 def save_to_undo_stack(self, clear_redo_stack=True): 493 """ 494 Safe current state (input text and cursor position), so that we can 495 restore it by calling undo. 496 """ 497 # Safe if the text is different from the text at the top of the stack 498 # is different. If the text is the same, just update the cursor position. 499 if self._undo_stack and self._undo_stack[-1][0] == self.text: 500 self._undo_stack[-1] = (self._undo_stack[-1][0], self.cursor_position) 501 else: 502 self._undo_stack.append((self.text, self.cursor_position)) 503 504 # Saving anything to the undo stack, clears the redo stack. 505 if clear_redo_stack: 506 self._redo_stack = [] 507 508 def transform_lines(self, line_index_iterator, transform_callback): 509 """ 510 Transforms the text on a range of lines. 511 When the iterator yield an index not in the range of lines that the 512 document contains, it skips them silently. 513 514 To uppercase some lines:: 515 516 new_text = transform_lines(range(5,10), lambda text: text.upper()) 517 518 :param line_index_iterator: Iterator of line numbers (int) 519 :param transform_callback: callable that takes the original text of a 520 line, and return the new text for this line. 521 522 :returns: The new text. 523 """ 524 # Split lines 525 lines = self.text.split('\n') 526 527 # Apply transformation 528 for index in line_index_iterator: 529 try: 530 lines[index] = transform_callback(lines[index]) 531 except IndexError: 532 pass 533 534 return '\n'.join(lines) 535 536 def transform_current_line(self, transform_callback): 537 """ 538 Apply the given transformation function to the current line. 539 540 :param transform_callback: callable that takes a string and return a new string. 541 """ 542 document = self.document 543 a = document.cursor_position + document.get_start_of_line_position() 544 b = document.cursor_position + document.get_end_of_line_position() 545 self.text = ( 546 document.text[:a] + 547 transform_callback(document.text[a:b]) + 548 document.text[b:]) 549 550 def transform_region(self, from_, to, transform_callback): 551 """ 552 Transform a part of the input string. 553 554 :param from_: (int) start position. 555 :param to: (int) end position. 556 :param transform_callback: Callable which accepts a string and returns 557 the transformed string. 558 """ 559 assert from_ < to 560 561 self.text = ''.join([ 562 self.text[:from_] + 563 transform_callback(self.text[from_:to]) + 564 self.text[to:] 565 ]) 566 567 def cursor_left(self, count=1): 568 self.cursor_position += self.document.get_cursor_left_position(count=count) 569 570 def cursor_right(self, count=1): 571 self.cursor_position += self.document.get_cursor_right_position(count=count) 572 573 def cursor_up(self, count=1): 574 """ (for multiline edit). Move cursor to the previous line. """ 575 original_column = self.preferred_column or self.document.cursor_position_col 576 self.cursor_position += self.document.get_cursor_up_position( 577 count=count, preferred_column=original_column) 578 579 # Remember the original column for the next up/down movement. 580 self.preferred_column = original_column 581 582 def cursor_down(self, count=1): 583 """ (for multiline edit). Move cursor to the next line. """ 584 original_column = self.preferred_column or self.document.cursor_position_col 585 self.cursor_position += self.document.get_cursor_down_position( 586 count=count, preferred_column=original_column) 587 588 # Remember the original column for the next up/down movement. 589 self.preferred_column = original_column 590 591 def auto_up(self, count=1, go_to_start_of_line_if_history_changes=False): 592 """ 593 If we're not on the first line (of a multiline input) go a line up, 594 otherwise go back in history. (If nothing is selected.) 595 """ 596 if self.complete_state: 597 self.complete_previous(count=count) 598 elif self.document.cursor_position_row > 0: 599 self.cursor_up(count=count) 600 elif not self.selection_state: 601 self.history_backward(count=count) 602 603 # Go to the start of the line? 604 if go_to_start_of_line_if_history_changes: 605 self.cursor_position += self.document.get_start_of_line_position() 606 607 def auto_down(self, count=1, go_to_start_of_line_if_history_changes=False): 608 """ 609 If we're not on the last line (of a multiline input) go a line down, 610 otherwise go forward in history. (If nothing is selected.) 611 """ 612 if self.complete_state: 613 self.complete_next(count=count) 614 elif self.document.cursor_position_row < self.document.line_count - 1: 615 self.cursor_down(count=count) 616 elif not self.selection_state: 617 self.history_forward(count=count) 618 619 # Go to the start of the line? 620 if go_to_start_of_line_if_history_changes: 621 self.cursor_position += self.document.get_start_of_line_position() 622 623 def delete_before_cursor(self, count=1): 624 """ 625 Delete specified number of characters before cursor and return the 626 deleted text. 627 """ 628 assert count >= 0 629 deleted = '' 630 631 if self.cursor_position > 0: 632 deleted = self.text[self.cursor_position - count:self.cursor_position] 633 634 new_text = self.text[:self.cursor_position - count] + self.text[self.cursor_position:] 635 new_cursor_position = self.cursor_position - len(deleted) 636 637 # Set new Document atomically. 638 self.document = Document(new_text, new_cursor_position) 639 640 return deleted 641 642 def delete(self, count=1): 643 """ 644 Delete specified number of characters and Return the deleted text. 645 """ 646 if self.cursor_position < len(self.text): 647 deleted = self.document.text_after_cursor[:count] 648 self.text = self.text[:self.cursor_position] + \ 649 self.text[self.cursor_position + len(deleted):] 650 return deleted 651 else: 652 return '' 653 654 def join_next_line(self, separator=' '): 655 """ 656 Join the next line to the current one by deleting the line ending after 657 the current line. 658 """ 659 if not self.document.on_last_line: 660 self.cursor_position += self.document.get_end_of_line_position() 661 self.delete() 662 663 # Remove spaces. 664 self.text = (self.document.text_before_cursor + separator + 665 self.document.text_after_cursor.lstrip(' ')) 666 667 def join_selected_lines(self, separator=' '): 668 """ 669 Join the selected lines. 670 """ 671 assert self.selection_state 672 673 # Get lines. 674 from_, to = sorted([self.cursor_position, self.selection_state.original_cursor_position]) 675 676 before = self.text[:from_] 677 lines = self.text[from_:to].splitlines() 678 after = self.text[to:] 679 680 # Replace leading spaces with just one space. 681 lines = [l.lstrip(' ') + separator for l in lines] 682 683 # Set new document. 684 self.document = Document(text=before + ''.join(lines) + after, 685 cursor_position=len(before + ''.join(lines[:-1])) - 1) 686 687 def swap_characters_before_cursor(self): 688 """ 689 Swap the last two characters before the cursor. 690 """ 691 pos = self.cursor_position 692 693 if pos >= 2: 694 a = self.text[pos - 2] 695 b = self.text[pos - 1] 696 697 self.text = self.text[:pos-2] + b + a + self.text[pos:] 698 699 def go_to_history(self, index): 700 """ 701 Go to this item in the history. 702 """ 703 if index < len(self._working_lines): 704 self.working_index = index 705 self.cursor_position = len(self.text) 706 707 def complete_next(self, count=1, disable_wrap_around=False): 708 """ 709 Browse to the next completions. 710 (Does nothing if there are no completion.) 711 """ 712 if self.complete_state: 713 completions_count = len(self.complete_state.current_completions) 714 715 if self.complete_state.complete_index is None: 716 index = 0 717 elif self.complete_state.complete_index == completions_count - 1: 718 index = None 719 720 if disable_wrap_around: 721 return 722 else: 723 index = min(completions_count-1, self.complete_state.complete_index + count) 724 self.go_to_completion(index) 725 726 def complete_previous(self, count=1, disable_wrap_around=False): 727 """ 728 Browse to the previous completions. 729 (Does nothing if there are no completion.) 730 """ 731 if self.complete_state: 732 if self.complete_state.complete_index == 0: 733 index = None 734 735 if disable_wrap_around: 736 return 737 elif self.complete_state.complete_index is None: 738 index = len(self.complete_state.current_completions) - 1 739 else: 740 index = max(0, self.complete_state.complete_index - count) 741 742 self.go_to_completion(index) 743 744 def cancel_completion(self): 745 """ 746 Cancel completion, go back to the original text. 747 """ 748 if self.complete_state: 749 self.go_to_completion(None) 750 self.complete_state = None 751 752 def set_completions(self, completions, go_to_first=True, go_to_last=False): 753 """ 754 Start completions. (Generate list of completions and initialize.) 755 """ 756 assert not (go_to_first and go_to_last) 757 758 # Generate list of all completions. 759 if completions is None: 760 if self.completer: 761 completions = list(self.completer.get_completions( 762 self.document, 763 CompleteEvent(completion_requested=True) 764 )) 765 else: 766 completions = [] 767 768 # Set `complete_state`. 769 if completions: 770 self.complete_state = CompletionState( 771 original_document=self.document, 772 current_completions=completions) 773 if go_to_first: 774 self.go_to_completion(0) 775 elif go_to_last: 776 self.go_to_completion(len(completions) - 1) 777 else: 778 self.go_to_completion(None) 779 780 else: 781 self.complete_state = None 782 783 def start_history_lines_completion(self): 784 """ 785 Start a completion based on all the other lines in the document and the 786 history. 787 """ 788 found_completions = set() 789 completions = [] 790 791 # For every line of the whole history, find matches with the current line. 792 current_line = self.document.current_line_before_cursor.lstrip() 793 794 for i, string in enumerate(self._working_lines): 795 for j, l in enumerate(string.split('\n')): 796 l = l.strip() 797 if l and l.startswith(current_line): 798 # When a new line has been found. 799 if l not in found_completions: 800 found_completions.add(l) 801 802 # Create completion. 803 if i == self.working_index: 804 display_meta = "Current, line %s" % (j+1) 805 else: 806 display_meta = "History %s, line %s" % (i+1, j+1) 807 808 completions.append(Completion( 809 l, 810 start_position=-len(current_line), 811 display_meta=display_meta)) 812 813 self.set_completions(completions=completions[::-1]) 814 815 def go_to_completion(self, index): 816 """ 817 Select a completion from the list of current completions. 818 """ 819 assert index is None or isinstance(index, int) 820 assert self.complete_state 821 822 # Set new completion 823 state = self.complete_state.go_to_index(index) 824 825 # Set text/cursor position 826 new_text, new_cursor_position = state.new_text_and_position() 827 self.document = Document(new_text, new_cursor_position) 828 829 # (changing text/cursor position will unset complete_state.) 830 self.complete_state = state 831 832 def apply_completion(self, completion): 833 """ 834 Insert a given completion. 835 """ 836 assert isinstance(completion, Completion) 837 838 # If there was already a completion active, cancel that one. 839 if self.complete_state: 840 self.go_to_completion(None) 841 self.complete_state = None 842 843 # Insert text from the given completion. 844 self.delete_before_cursor(-completion.start_position) 845 self.insert_text(completion.text) 846 847 def _set_history_search(self): 848 """ Set `history_search_text`. """ 849 if self.enable_history_search(): 850 if self.history_search_text is None: 851 self.history_search_text = self.text 852 else: 853 self.history_search_text = None 854 855 def _history_matches(self, i): 856 """ 857 True when the current entry matches the history search. 858 (when we don't have history search, it's also True.) 859 """ 860 return (self.history_search_text is None or 861 self._working_lines[i].startswith(self.history_search_text)) 862 863 def history_forward(self, count=1): 864 """ 865 Move forwards through the history. 866 867 :param count: Amount of items to move forward. 868 """ 869 self._set_history_search() 870 871 # Go forward in history. 872 found_something = False 873 874 for i in range(self.working_index + 1, len(self._working_lines)): 875 if self._history_matches(i): 876 self.working_index = i 877 count -= 1 878 found_something = True 879 if count == 0: 880 break 881 882 # If we found an entry, move cursor to the end of the first line. 883 if found_something: 884 self.cursor_position = 0 885 self.cursor_position += self.document.get_end_of_line_position() 886 887 def history_backward(self, count=1): 888 """ 889 Move backwards through history. 890 """ 891 self._set_history_search() 892 893 # Go back in history. 894 found_something = False 895 896 for i in range(self.working_index - 1, -1, -1): 897 if self._history_matches(i): 898 self.working_index = i 899 count -= 1 900 found_something = True 901 if count == 0: 902 break 903 904 # If we move to another entry, move cursor to the end of the line. 905 if found_something: 906 self.cursor_position = len(self.text) 907 908 def yank_nth_arg(self, n=None, _yank_last_arg=False): 909 """ 910 Pick nth word from previous history entry (depending on current 911 `yank_nth_arg_state`) and insert it at current position. Rotate through 912 history if called repeatedly. If no `n` has been given, take the first 913 argument. (The second word.) 914 915 :param n: (None or int), The index of the word from the previous line 916 to take. 917 """ 918 assert n is None or isinstance(n, int) 919 920 if not len(self.history): 921 return 922 923 # Make sure we have a `YankNthArgState`. 924 if self.yank_nth_arg_state is None: 925 state = YankNthArgState(n=-1 if _yank_last_arg else 1) 926 else: 927 state = self.yank_nth_arg_state 928 929 if n is not None: 930 state.n = n 931 932 # Get new history position. 933 new_pos = state.history_position - 1 934 if -new_pos > len(self.history): 935 new_pos = -1 936 937 # Take argument from line. 938 line = self.history[new_pos] 939 940 words = [w.strip() for w in _QUOTED_WORDS_RE.split(line)] 941 words = [w for w in words if w] 942 try: 943 word = words[state.n] 944 except IndexError: 945 word = '' 946 947 # Insert new argument. 948 if state.previous_inserted_word: 949 self.delete_before_cursor(len(state.previous_inserted_word)) 950 self.insert_text(word) 951 952 # Save state again for next completion. (Note that the 'insert' 953 # operation from above clears `self.yank_nth_arg_state`.) 954 state.previous_inserted_word = word 955 state.history_position = new_pos 956 self.yank_nth_arg_state = state 957 958 def yank_last_arg(self, n=None): 959 """ 960 Like `yank_nth_arg`, but if no argument has been given, yank the last 961 word by default. 962 """ 963 self.yank_nth_arg(n=n, _yank_last_arg=True) 964 965 def start_selection(self, selection_type=SelectionType.CHARACTERS): 966 """ 967 Take the current cursor position as the start of this selection. 968 """ 969 self.selection_state = SelectionState(self.cursor_position, selection_type) 970 971 def copy_selection(self, _cut=False): 972 """ 973 Copy selected text and return :class:`.ClipboardData` instance. 974 """ 975 new_document, clipboard_data = self.document.cut_selection() 976 if _cut: 977 self.document = new_document 978 979 self.selection_state = None 980 return clipboard_data 981 982 def cut_selection(self): 983 """ 984 Delete selected text and return :class:`.ClipboardData` instance. 985 """ 986 return self.copy_selection(_cut=True) 987 988 def paste_clipboard_data(self, data, paste_mode=PasteMode.EMACS, count=1): 989 """ 990 Insert the data from the clipboard. 991 """ 992 assert isinstance(data, ClipboardData) 993 assert paste_mode in (PasteMode.VI_BEFORE, PasteMode.VI_AFTER, PasteMode.EMACS) 994 995 original_document = self.document 996 self.document = self.document.paste_clipboard_data(data, paste_mode=paste_mode, count=count) 997 998 # Remember original document. This assignment should come at the end, 999 # because assigning to 'document' will erase it. 1000 self.document_before_paste = original_document 1001 1002 def newline(self, copy_margin=True): 1003 """ 1004 Insert a line ending at the current position. 1005 """ 1006 if copy_margin: 1007 self.insert_text('\n' + self.document.leading_whitespace_in_current_line) 1008 else: 1009 self.insert_text('\n') 1010 1011 def insert_line_above(self, copy_margin=True): 1012 """ 1013 Insert a new line above the current one. 1014 """ 1015 if copy_margin: 1016 insert = self.document.leading_whitespace_in_current_line + '\n' 1017 else: 1018 insert = '\n' 1019 1020 self.cursor_position += self.document.get_start_of_line_position() 1021 self.insert_text(insert) 1022 self.cursor_position -= 1 1023 1024 def insert_line_below(self, copy_margin=True): 1025 """ 1026 Insert a new line below the current one. 1027 """ 1028 if copy_margin: 1029 insert = '\n' + self.document.leading_whitespace_in_current_line 1030 else: 1031 insert = '\n' 1032 1033 self.cursor_position += self.document.get_end_of_line_position() 1034 self.insert_text(insert) 1035 1036 def insert_text(self, data, overwrite=False, move_cursor=True, fire_event=True): 1037 """ 1038 Insert characters at cursor position. 1039 1040 :param fire_event: Fire `on_text_insert` event. This is mainly used to 1041 trigger autocompletion while typing. 1042 """ 1043 # Original text & cursor position. 1044 otext = self.text 1045 ocpos = self.cursor_position 1046 1047 # In insert/text mode. 1048 if overwrite: 1049 # Don't overwrite the newline itself. Just before the line ending, 1050 # it should act like insert mode. 1051 overwritten_text = otext[ocpos:ocpos + len(data)] 1052 if '\n' in overwritten_text: 1053 overwritten_text = overwritten_text[:overwritten_text.find('\n')] 1054 1055 self.text = otext[:ocpos] + data + otext[ocpos + len(overwritten_text):] 1056 else: 1057 self.text = otext[:ocpos] + data + otext[ocpos:] 1058 1059 if move_cursor: 1060 self.cursor_position += len(data) 1061 1062 # Fire 'on_text_insert' event. 1063 if fire_event: 1064 self.on_text_insert.fire() 1065 1066 def undo(self): 1067 # Pop from the undo-stack until we find a text that if different from 1068 # the current text. (The current logic of `save_to_undo_stack` will 1069 # cause that the top of the undo stack is usually the same as the 1070 # current text, so in that case we have to pop twice.) 1071 while self._undo_stack: 1072 text, pos = self._undo_stack.pop() 1073 1074 if text != self.text: 1075 # Push current text to redo stack. 1076 self._redo_stack.append((self.text, self.cursor_position)) 1077 1078 # Set new text/cursor_position. 1079 self.document = Document(text, cursor_position=pos) 1080 break 1081 1082 def redo(self): 1083 if self._redo_stack: 1084 # Copy current state on undo stack. 1085 self.save_to_undo_stack(clear_redo_stack=False) 1086 1087 # Pop state from redo stack. 1088 text, pos = self._redo_stack.pop() 1089 self.document = Document(text, cursor_position=pos) 1090 1091 def validate(self): 1092 """ 1093 Returns `True` if valid. 1094 """ 1095 # Don't call the validator again, if it was already called for the 1096 # current input. 1097 if self.validation_state != ValidationState.UNKNOWN: 1098 return self.validation_state == ValidationState.VALID 1099 1100 # Validate first. If not valid, set validation exception. 1101 if self.validator: 1102 try: 1103 self.validator.validate(self.document) 1104 except ValidationError as e: 1105 # Set cursor position (don't allow invalid values.) 1106 cursor_position = e.cursor_position 1107 self.cursor_position = min(max(0, cursor_position), len(self.text)) 1108 1109 self.validation_state = ValidationState.INVALID 1110 self.validation_error = e 1111 return False 1112 1113 self.validation_state = ValidationState.VALID 1114 self.validation_error = None 1115 return True 1116 1117 def append_to_history(self): 1118 """ 1119 Append the current input to the history. 1120 (Only if valid input.) 1121 """ 1122 # Validate first. If not valid, set validation exception. 1123 if not self.validate(): 1124 return 1125 1126 # Save at the tail of the history. (But don't if the last entry the 1127 # history is already the same.) 1128 if self.text and (not len(self.history) or self.history[-1] != self.text): 1129 self.history.append(self.text) 1130 1131 def _search(self, search_state, include_current_position=False, count=1): 1132 """ 1133 Execute search. Return (working_index, cursor_position) tuple when this 1134 search is applied. Returns `None` when this text cannot be found. 1135 """ 1136 assert isinstance(search_state, SearchState) 1137 assert isinstance(count, int) and count > 0 1138 1139 text = search_state.text 1140 direction = search_state.direction 1141 ignore_case = search_state.ignore_case() 1142 1143 def search_once(working_index, document): 1144 """ 1145 Do search one time. 1146 Return (working_index, document) or `None` 1147 """ 1148 if direction == IncrementalSearchDirection.FORWARD: 1149 # Try find at the current input. 1150 new_index = document.find( 1151 text, include_current_position=include_current_position, 1152 ignore_case=ignore_case) 1153 1154 if new_index is not None: 1155 return (working_index, 1156 Document(document.text, document.cursor_position + new_index)) 1157 else: 1158 # No match, go forward in the history. (Include len+1 to wrap around.) 1159 # (Here we should always include all cursor positions, because 1160 # it's a different line.) 1161 for i in range(working_index + 1, len(self._working_lines) + 1): 1162 i %= len(self._working_lines) 1163 1164 document = Document(self._working_lines[i], 0) 1165 new_index = document.find(text, include_current_position=True, 1166 ignore_case=ignore_case) 1167 if new_index is not None: 1168 return (i, Document(document.text, new_index)) 1169 else: 1170 # Try find at the current input. 1171 new_index = document.find_backwards( 1172 text, ignore_case=ignore_case) 1173 1174 if new_index is not None: 1175 return (working_index, 1176 Document(document.text, document.cursor_position + new_index)) 1177 else: 1178 # No match, go back in the history. (Include -1 to wrap around.) 1179 for i in range(working_index - 1, -2, -1): 1180 i %= len(self._working_lines) 1181 1182 document = Document(self._working_lines[i], len(self._working_lines[i])) 1183 new_index = document.find_backwards( 1184 text, ignore_case=ignore_case) 1185 if new_index is not None: 1186 return (i, Document(document.text, len(document.text) + new_index)) 1187 1188 # Do 'count' search iterations. 1189 working_index = self.working_index 1190 document = self.document 1191 for _ in range(count): 1192 result = search_once(working_index, document) 1193 if result is None: 1194 return # Nothing found. 1195 else: 1196 working_index, document = result 1197 1198 return (working_index, document.cursor_position) 1199 1200 def document_for_search(self, search_state): 1201 """ 1202 Return a :class:`~prompt_toolkit.document.Document` instance that has 1203 the text/cursor position for this search, if we would apply it. This 1204 will be used in the 1205 :class:`~prompt_toolkit.layout.controls.BufferControl` to display 1206 feedback while searching. 1207 """ 1208 search_result = self._search(search_state, include_current_position=True) 1209 1210 if search_result is None: 1211 return self.document 1212 else: 1213 working_index, cursor_position = search_result 1214 1215 # Keep selection, when `working_index` was not changed. 1216 if working_index == self.working_index: 1217 selection = self.selection_state 1218 else: 1219 selection = None 1220 1221 return Document(self._working_lines[working_index], 1222 cursor_position, selection=selection) 1223 1224 def get_search_position(self, search_state, include_current_position=True, count=1): 1225 """ 1226 Get the cursor position for this search. 1227 (This operation won't change the `working_index`. It's won't go through 1228 the history. Vi text objects can't span multiple items.) 1229 """ 1230 search_result = self._search( 1231 search_state, include_current_position=include_current_position, count=count) 1232 1233 if search_result is None: 1234 return self.cursor_position 1235 else: 1236 working_index, cursor_position = search_result 1237 return cursor_position 1238 1239 def apply_search(self, search_state, include_current_position=True, count=1): 1240 """ 1241 Apply search. If something is found, set `working_index` and 1242 `cursor_position`. 1243 """ 1244 search_result = self._search( 1245 search_state, include_current_position=include_current_position, count=count) 1246 1247 if search_result is not None: 1248 working_index, cursor_position = search_result 1249 self.working_index = working_index 1250 self.cursor_position = cursor_position 1251 1252 def exit_selection(self): 1253 self.selection_state = None 1254 1255 def open_in_editor(self, cli): 1256 """ 1257 Open code in editor. 1258 1259 :param cli: :class:`~prompt_toolkit.interface.CommandLineInterface` 1260 instance. 1261 """ 1262 if self.read_only(): 1263 raise EditReadOnlyBuffer() 1264 1265 # Write to temporary file 1266 descriptor, filename = tempfile.mkstemp(self.tempfile_suffix) 1267 os.write(descriptor, self.text.encode('utf-8')) 1268 os.close(descriptor) 1269 1270 # Open in editor 1271 # (We need to use `cli.run_in_terminal`, because not all editors go to 1272 # the alternate screen buffer, and some could influence the cursor 1273 # position.) 1274 succes = cli.run_in_terminal(lambda: self._open_file_in_editor(filename)) 1275 1276 # Read content again. 1277 if succes: 1278 with open(filename, 'rb') as f: 1279 text = f.read().decode('utf-8') 1280 1281 # Drop trailing newline. (Editors are supposed to add it at the 1282 # end, but we don't need it.) 1283 if text.endswith('\n'): 1284 text = text[:-1] 1285 1286 self.document = Document( 1287 text=text, 1288 cursor_position=len(text)) 1289 1290 # Clean up temp file. 1291 os.remove(filename) 1292 1293 def _open_file_in_editor(self, filename): 1294 """ 1295 Call editor executable. 1296 1297 Return True when we received a zero return code. 1298 """ 1299 # If the 'VISUAL' or 'EDITOR' environment variable has been set, use that. 1300 # Otherwise, fall back to the first available editor that we can find. 1301 visual = os.environ.get('VISUAL') 1302 editor = os.environ.get('EDITOR') 1303 1304 editors = [ 1305 visual, 1306 editor, 1307 1308 # Order of preference. 1309 '/usr/bin/editor', 1310 '/usr/bin/nano', 1311 '/usr/bin/pico', 1312 '/usr/bin/vi', 1313 '/usr/bin/emacs', 1314 ] 1315 1316 for e in editors: 1317 if e: 1318 try: 1319 returncode = subprocess.call([e, filename]) 1320 return returncode == 0 1321 1322 except OSError: 1323 # Executable does not exist, try the next one. 1324 pass 1325 1326 return False 1327 1328 1329def indent(buffer, from_row, to_row, count=1): 1330 """ 1331 Indent text of a :class:`.Buffer` object. 1332 """ 1333 current_row = buffer.document.cursor_position_row 1334 line_range = range(from_row, to_row) 1335 1336 # Apply transformation. 1337 new_text = buffer.transform_lines(line_range, lambda l: ' ' * count + l) 1338 buffer.document = Document( 1339 new_text, 1340 Document(new_text).translate_row_col_to_index(current_row, 0)) 1341 1342 # Go to the start of the line. 1343 buffer.cursor_position += buffer.document.get_start_of_line_position(after_whitespace=True) 1344 1345 1346def unindent(buffer, from_row, to_row, count=1): 1347 """ 1348 Unindent text of a :class:`.Buffer` object. 1349 """ 1350 current_row = buffer.document.cursor_position_row 1351 line_range = range(from_row, to_row) 1352 1353 def transform(text): 1354 remove = ' ' * count 1355 if text.startswith(remove): 1356 return text[len(remove):] 1357 else: 1358 return text.lstrip() 1359 1360 # Apply transformation. 1361 new_text = buffer.transform_lines(line_range, transform) 1362 buffer.document = Document( 1363 new_text, 1364 Document(new_text).translate_row_col_to_index(current_row, 0)) 1365 1366 # Go to the start of the line. 1367 buffer.cursor_position += buffer.document.get_start_of_line_position(after_whitespace=True) 1368 1369 1370def reshape_text(buffer, from_row, to_row): 1371 """ 1372 Reformat text, taking the width into account. 1373 `to_row` is included. 1374 (Vi 'gq' operator.) 1375 """ 1376 lines = buffer.text.splitlines(True) 1377 lines_before = lines[:from_row] 1378 lines_after = lines[to_row + 1:] 1379 lines_to_reformat = lines[from_row:to_row + 1] 1380 1381 if lines_to_reformat: 1382 # Take indentation from the first line. 1383 length = re.search(r'^\s*', lines_to_reformat[0]).end() 1384 indent = lines_to_reformat[0][:length].replace('\n', '') 1385 1386 # Now, take all the 'words' from the lines to be reshaped. 1387 words = ''.join(lines_to_reformat).split() 1388 1389 # And reshape. 1390 width = (buffer.text_width or 80) - len(indent) 1391 reshaped_text = [indent] 1392 current_width = 0 1393 for w in words: 1394 if current_width: 1395 if len(w) + current_width + 1 > width: 1396 reshaped_text.append('\n') 1397 reshaped_text.append(indent) 1398 current_width = 0 1399 else: 1400 reshaped_text.append(' ') 1401 current_width += 1 1402 1403 reshaped_text.append(w) 1404 current_width += len(w) 1405 1406 if reshaped_text[-1] != '\n': 1407 reshaped_text.append('\n') 1408 1409 # Apply result. 1410 buffer.document = Document( 1411 text=''.join(lines_before + reshaped_text + lines_after), 1412 cursor_position=len(''.join(lines_before + reshaped_text))) 1413