aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/prompt-toolkit/py2/prompt_toolkit/buffer.py
diff options
context:
space:
mode:
authorIvan Blinkov <ivan@blinkov.ru>2022-02-10 16:47:10 +0300
committerDaniil Cherednik <dcherednik@yandex-team.ru>2022-02-10 16:47:10 +0300
commit1aeb9a455974457866f78722ad98114bafc84e8a (patch)
treee4340eaf1668684d83a0a58c36947c5def5350ad /contrib/python/prompt-toolkit/py2/prompt_toolkit/buffer.py
parentbd5ef432f5cfb1e18851381329d94665a4c22470 (diff)
downloadydb-1aeb9a455974457866f78722ad98114bafc84e8a.tar.gz
Restoring authorship annotation for Ivan Blinkov <ivan@blinkov.ru>. Commit 1 of 2.
Diffstat (limited to 'contrib/python/prompt-toolkit/py2/prompt_toolkit/buffer.py')
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/buffer.py2208
1 files changed, 1104 insertions, 1104 deletions
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/buffer.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/buffer.py
index f5df289827..6a8b1dc4f5 100644
--- a/contrib/python/prompt-toolkit/py2/prompt_toolkit/buffer.py
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/buffer.py
@@ -1,106 +1,106 @@
-"""
-Data structures for the Buffer.
-It holds the text, cursor position, history, etc...
-"""
-from __future__ import unicode_literals
-
-from .auto_suggest import AutoSuggest
-from .clipboard import ClipboardData
-from .completion import Completer, Completion, CompleteEvent
-from .document import Document
-from .enums import IncrementalSearchDirection
-from .filters import to_simple_filter
-from .history import History, InMemoryHistory
-from .search_state import SearchState
+"""
+Data structures for the Buffer.
+It holds the text, cursor position, history, etc...
+"""
+from __future__ import unicode_literals
+
+from .auto_suggest import AutoSuggest
+from .clipboard import ClipboardData
+from .completion import Completer, Completion, CompleteEvent
+from .document import Document
+from .enums import IncrementalSearchDirection
+from .filters import to_simple_filter
+from .history import History, InMemoryHistory
+from .search_state import SearchState
from .selection import SelectionType, SelectionState, PasteMode
from .utils import Event
from .cache import FastDictCache
-from .validation import ValidationError
-
+from .validation import ValidationError
+
from six.moves import range
-import os
+import os
import re
import shlex
-import six
-import subprocess
-import tempfile
-
-__all__ = (
- 'EditReadOnlyBuffer',
- 'AcceptAction',
- 'Buffer',
- 'indent',
- 'unindent',
+import six
+import subprocess
+import tempfile
+
+__all__ = (
+ 'EditReadOnlyBuffer',
+ 'AcceptAction',
+ 'Buffer',
+ 'indent',
+ 'unindent',
'reshape_text',
-)
-
-
-class EditReadOnlyBuffer(Exception):
- " Attempt editing of read-only :class:`.Buffer`. "
-
-
-class AcceptAction(object):
- """
- What to do when the input is accepted by the user.
- (When Enter was pressed in the command line.)
-
- :param handler: (optional) A callable which takes a
- :class:`~prompt_toolkit.interface.CommandLineInterface` and
- :class:`~prompt_toolkit.document.Document`. It is called when the user
- accepts input.
- """
- def __init__(self, handler=None):
- assert handler is None or callable(handler)
- self.handler = handler
-
- @classmethod
- def run_in_terminal(cls, handler, render_cli_done=False):
- """
- Create an :class:`.AcceptAction` that runs the given handler in the
- terminal.
-
- :param render_cli_done: When True, render the interface in the 'Done'
- state first, then execute the function. If False, erase the
- interface instead.
- """
- def _handler(cli, buffer):
- cli.run_in_terminal(lambda: handler(cli, buffer), render_cli_done=render_cli_done)
- return AcceptAction(handler=_handler)
-
- @property
- def is_returnable(self):
- """
- True when there is something handling accept.
- """
- return bool(self.handler)
-
- def validate_and_handle(self, cli, buffer):
- """
- Validate buffer and handle the accept action.
- """
- if buffer.validate():
- if self.handler:
- self.handler(cli, buffer)
-
- buffer.append_to_history()
-
-
-def _return_document_handler(cli, buffer):
+)
+
+
+class EditReadOnlyBuffer(Exception):
+ " Attempt editing of read-only :class:`.Buffer`. "
+
+
+class AcceptAction(object):
+ """
+ What to do when the input is accepted by the user.
+ (When Enter was pressed in the command line.)
+
+ :param handler: (optional) A callable which takes a
+ :class:`~prompt_toolkit.interface.CommandLineInterface` and
+ :class:`~prompt_toolkit.document.Document`. It is called when the user
+ accepts input.
+ """
+ def __init__(self, handler=None):
+ assert handler is None or callable(handler)
+ self.handler = handler
+
+ @classmethod
+ def run_in_terminal(cls, handler, render_cli_done=False):
+ """
+ Create an :class:`.AcceptAction` that runs the given handler in the
+ terminal.
+
+ :param render_cli_done: When True, render the interface in the 'Done'
+ state first, then execute the function. If False, erase the
+ interface instead.
+ """
+ def _handler(cli, buffer):
+ cli.run_in_terminal(lambda: handler(cli, buffer), render_cli_done=render_cli_done)
+ return AcceptAction(handler=_handler)
+
+ @property
+ def is_returnable(self):
+ """
+ True when there is something handling accept.
+ """
+ return bool(self.handler)
+
+ def validate_and_handle(self, cli, buffer):
+ """
+ Validate buffer and handle the accept action.
+ """
+ if buffer.validate():
+ if self.handler:
+ self.handler(cli, buffer)
+
+ buffer.append_to_history()
+
+
+def _return_document_handler(cli, buffer):
# Set return value.
- cli.set_return_value(buffer.document)
-
+ cli.set_return_value(buffer.document)
+
# Make sure that if we run this UI again, that we reset this buffer, next
# time.
def reset_this_buffer():
buffer.reset()
cli.pre_run_callables.append(reset_this_buffer)
+
-
-AcceptAction.RETURN_DOCUMENT = AcceptAction(_return_document_handler)
-AcceptAction.IGNORE = AcceptAction(handler=None)
-
-
+AcceptAction.RETURN_DOCUMENT = AcceptAction(_return_document_handler)
+AcceptAction.IGNORE = AcceptAction(handler=None)
+
+
class ValidationState(object):
" The validation state of a buffer. This is set after the validation. "
VALID = 'VALID'
@@ -108,63 +108,63 @@ class ValidationState(object):
UNKNOWN = 'UNKNOWN'
-class CompletionState(object):
- """
- Immutable class that contains a completion state.
- """
- def __init__(self, original_document, current_completions=None, complete_index=None):
- #: Document as it was when the completion started.
- self.original_document = original_document
-
- #: List of all the current Completion instances which are possible at
- #: this point.
- self.current_completions = current_completions or []
-
- #: Position in the `current_completions` array.
- #: This can be `None` to indicate "no completion", the original text.
- self.complete_index = complete_index # Position in the `_completions` array.
-
- def __repr__(self):
- return '%s(%r, <%r> completions, index=%r)' % (
- self.__class__.__name__,
- self.original_document, len(self.current_completions), self.complete_index)
-
- def go_to_index(self, index):
- """
- Create a new :class:`.CompletionState` object with the new index.
- """
- return CompletionState(self.original_document, self.current_completions, complete_index=index)
-
- def new_text_and_position(self):
- """
- Return (new_text, new_cursor_position) for this completion.
- """
- if self.complete_index is None:
- return self.original_document.text, self.original_document.cursor_position
- else:
- original_text_before_cursor = self.original_document.text_before_cursor
- original_text_after_cursor = self.original_document.text_after_cursor
-
- c = self.current_completions[self.complete_index]
- if c.start_position == 0:
- before = original_text_before_cursor
- else:
- before = original_text_before_cursor[:c.start_position]
-
- new_text = before + c.text + original_text_after_cursor
- new_cursor_position = len(before) + len(c.text)
- return new_text, new_cursor_position
-
- @property
- def current_completion(self):
- """
- Return the current completion, or return `None` when no completion is
- selected.
- """
- if self.complete_index is not None:
- return self.current_completions[self.complete_index]
-
-
+class CompletionState(object):
+ """
+ Immutable class that contains a completion state.
+ """
+ def __init__(self, original_document, current_completions=None, complete_index=None):
+ #: Document as it was when the completion started.
+ self.original_document = original_document
+
+ #: List of all the current Completion instances which are possible at
+ #: this point.
+ self.current_completions = current_completions or []
+
+ #: Position in the `current_completions` array.
+ #: This can be `None` to indicate "no completion", the original text.
+ self.complete_index = complete_index # Position in the `_completions` array.
+
+ def __repr__(self):
+ return '%s(%r, <%r> completions, index=%r)' % (
+ self.__class__.__name__,
+ self.original_document, len(self.current_completions), self.complete_index)
+
+ def go_to_index(self, index):
+ """
+ Create a new :class:`.CompletionState` object with the new index.
+ """
+ return CompletionState(self.original_document, self.current_completions, complete_index=index)
+
+ def new_text_and_position(self):
+ """
+ Return (new_text, new_cursor_position) for this completion.
+ """
+ if self.complete_index is None:
+ return self.original_document.text, self.original_document.cursor_position
+ else:
+ original_text_before_cursor = self.original_document.text_before_cursor
+ original_text_after_cursor = self.original_document.text_after_cursor
+
+ c = self.current_completions[self.complete_index]
+ if c.start_position == 0:
+ before = original_text_before_cursor
+ else:
+ before = original_text_before_cursor[:c.start_position]
+
+ new_text = before + c.text + original_text_after_cursor
+ new_cursor_position = len(before) + len(c.text)
+ return new_text, new_cursor_position
+
+ @property
+ def current_completion(self):
+ """
+ Return the current completion, or return `None` when no completion is
+ selected.
+ """
+ if self.complete_index is not None:
+ return self.current_completions[self.complete_index]
+
+
_QUOTED_WORDS_RE = re.compile(r"""(\s+|".*?"|'.*?')""")
@@ -183,113 +183,113 @@ class YankNthArgState(object):
self.previous_inserted_word)
-class Buffer(object):
- """
- The core data structure that holds the text and cursor position of the
- current input line and implements all text manupulations on top of it. It
- also implements the history, undo stack and the completion state.
-
- :param completer: :class:`~prompt_toolkit.completion.Completer` instance.
- :param history: :class:`~prompt_toolkit.history.History` instance.
- :param tempfile_suffix: Suffix to be appended to the tempfile for the 'open
- in editor' function.
-
- Events:
-
+class Buffer(object):
+ """
+ The core data structure that holds the text and cursor position of the
+ current input line and implements all text manupulations on top of it. It
+ also implements the history, undo stack and the completion state.
+
+ :param completer: :class:`~prompt_toolkit.completion.Completer` instance.
+ :param history: :class:`~prompt_toolkit.history.History` instance.
+ :param tempfile_suffix: Suffix to be appended to the tempfile for the 'open
+ in editor' function.
+
+ Events:
+
:param on_text_changed: When the buffer text changes. (Callable on None.)
:param on_text_insert: When new text is inserted. (Callable on None.)
:param on_cursor_position_changed: When the cursor moves. (Callable on None.)
-
- Filters:
-
- :param is_multiline: :class:`~prompt_toolkit.filters.SimpleFilter` to
- indicate whether we should consider this buffer a multiline input. If
- so, key bindings can decide to insert newlines when pressing [Enter].
- (Instead of accepting the input.)
- :param complete_while_typing: :class:`~prompt_toolkit.filters.SimpleFilter`
- instance. Decide whether or not to do asynchronous autocompleting while
- typing.
- :param enable_history_search: :class:`~prompt_toolkit.filters.SimpleFilter`
- to indicate when up-arrow partial string matching is enabled. It is
- adviced to not enable this at the same time as `complete_while_typing`,
- because when there is an autocompletion found, the up arrows usually
- browse through the completions, rather than through the history.
- :param read_only: :class:`~prompt_toolkit.filters.SimpleFilter`. When True,
- changes will not be allowed.
- """
- def __init__(self, completer=None, auto_suggest=None, history=None,
- validator=None, tempfile_suffix='',
- is_multiline=False, complete_while_typing=False,
- enable_history_search=False, initial_document=None,
- accept_action=AcceptAction.IGNORE, read_only=False,
- on_text_changed=None, on_text_insert=None, on_cursor_position_changed=None):
-
- # Accept both filters and booleans as input.
- enable_history_search = to_simple_filter(enable_history_search)
- is_multiline = to_simple_filter(is_multiline)
- complete_while_typing = to_simple_filter(complete_while_typing)
- read_only = to_simple_filter(read_only)
-
- # Validate input.
- assert completer is None or isinstance(completer, Completer)
- assert auto_suggest is None or isinstance(auto_suggest, AutoSuggest)
- assert history is None or isinstance(history, History)
+
+ Filters:
+
+ :param is_multiline: :class:`~prompt_toolkit.filters.SimpleFilter` to
+ indicate whether we should consider this buffer a multiline input. If
+ so, key bindings can decide to insert newlines when pressing [Enter].
+ (Instead of accepting the input.)
+ :param complete_while_typing: :class:`~prompt_toolkit.filters.SimpleFilter`
+ instance. Decide whether or not to do asynchronous autocompleting while
+ typing.
+ :param enable_history_search: :class:`~prompt_toolkit.filters.SimpleFilter`
+ to indicate when up-arrow partial string matching is enabled. It is
+ adviced to not enable this at the same time as `complete_while_typing`,
+ because when there is an autocompletion found, the up arrows usually
+ browse through the completions, rather than through the history.
+ :param read_only: :class:`~prompt_toolkit.filters.SimpleFilter`. When True,
+ changes will not be allowed.
+ """
+ def __init__(self, completer=None, auto_suggest=None, history=None,
+ validator=None, tempfile_suffix='',
+ is_multiline=False, complete_while_typing=False,
+ enable_history_search=False, initial_document=None,
+ accept_action=AcceptAction.IGNORE, read_only=False,
+ on_text_changed=None, on_text_insert=None, on_cursor_position_changed=None):
+
+ # Accept both filters and booleans as input.
+ enable_history_search = to_simple_filter(enable_history_search)
+ is_multiline = to_simple_filter(is_multiline)
+ complete_while_typing = to_simple_filter(complete_while_typing)
+ read_only = to_simple_filter(read_only)
+
+ # Validate input.
+ assert completer is None or isinstance(completer, Completer)
+ assert auto_suggest is None or isinstance(auto_suggest, AutoSuggest)
+ assert history is None or isinstance(history, History)
assert on_text_changed is None or callable(on_text_changed)
assert on_text_insert is None or callable(on_text_insert)
assert on_cursor_position_changed is None or callable(on_cursor_position_changed)
-
- self.completer = completer
- self.auto_suggest = auto_suggest
- self.validator = validator
- self.tempfile_suffix = tempfile_suffix
- self.accept_action = accept_action
-
- # Filters. (Usually, used by the key bindings to drive the buffer.)
- self.is_multiline = is_multiline
- self.complete_while_typing = complete_while_typing
- self.enable_history_search = enable_history_search
- self.read_only = read_only
-
+
+ self.completer = completer
+ self.auto_suggest = auto_suggest
+ self.validator = validator
+ self.tempfile_suffix = tempfile_suffix
+ self.accept_action = accept_action
+
+ # Filters. (Usually, used by the key bindings to drive the buffer.)
+ self.is_multiline = is_multiline
+ self.complete_while_typing = complete_while_typing
+ self.enable_history_search = enable_history_search
+ self.read_only = read_only
+
# Text width. (For wrapping, used by the Vi 'gq' operator.)
self.text_width = 0
- #: The command buffer history.
- # Note that we shouldn't use a lazy 'or' here. bool(history) could be
- # False when empty.
- self.history = InMemoryHistory() if history is None else history
-
- self.__cursor_position = 0
-
- # Events
+ #: The command buffer history.
+ # Note that we shouldn't use a lazy 'or' here. bool(history) could be
+ # False when empty.
+ self.history = InMemoryHistory() if history is None else history
+
+ self.__cursor_position = 0
+
+ # Events
self.on_text_changed = Event(self, on_text_changed)
self.on_text_insert = Event(self, on_text_insert)
self.on_cursor_position_changed = Event(self, on_cursor_position_changed)
-
+
# Document cache. (Avoid creating new Document instances.)
self._document_cache = FastDictCache(Document, size=10)
- self.reset(initial_document=initial_document)
-
- def reset(self, initial_document=None, append_to_history=False):
- """
- :param append_to_history: Append current input to history first.
- """
- assert initial_document is None or isinstance(initial_document, Document)
-
- if append_to_history:
- self.append_to_history()
-
- initial_document = initial_document or Document()
-
- self.__cursor_position = initial_document.cursor_position
-
- # `ValidationError` instance. (Will be set when the input is wrong.)
- self.validation_error = None
+ self.reset(initial_document=initial_document)
+
+ def reset(self, initial_document=None, append_to_history=False):
+ """
+ :param append_to_history: Append current input to history first.
+ """
+ assert initial_document is None or isinstance(initial_document, Document)
+
+ if append_to_history:
+ self.append_to_history()
+
+ initial_document = initial_document or Document()
+
+ self.__cursor_position = initial_document.cursor_position
+
+ # `ValidationError` instance. (Will be set when the input is wrong.)
+ self.validation_error = None
self.validation_state = ValidationState.UNKNOWN
-
- # State of the selection.
- self.selection_state = None
-
+
+ # State of the selection.
+ self.selection_state = None
+
# Multiple cursor mode. (When we press 'I' or 'A' in visual-block mode,
# we can insert text on multiple lines at once. This is implemented by
# using multiple cursors.)
@@ -298,9 +298,9 @@ class Buffer(object):
# When doing consecutive up/down movements, prefer to stay at this column.
self.preferred_column = None
- # State of complete browser
- self.complete_state = None # For interactive completion through Ctrl-N/Ctrl-P.
-
+ # State of complete browser
+ self.complete_state = None # For interactive completion through Ctrl-N/Ctrl-P.
+
# State of Emacs yank-nth-arg completion.
self.yank_nth_arg_state = None # for yank-nth-arg.
@@ -308,36 +308,36 @@ class Buffer(object):
# operation. This is used for rotating through the kill ring.
self.document_before_paste = None
- # Current suggestion.
- self.suggestion = None
-
- # The history search text. (Used for filtering the history when we
- # browse through it.)
- self.history_search_text = None
-
- # Undo/redo stacks
- self._undo_stack = [] # Stack of (text, cursor_position)
- self._redo_stack = []
-
- #: The working lines. Similar to history, except that this can be
- #: modified. The user can press arrow_up and edit previous entries.
- #: Ctrl-C should reset this, and copy the whole history back in here.
- #: Enter should process the current command and append to the real
- #: history.
- self._working_lines = self.history.strings[:]
- self._working_lines.append(initial_document.text)
- self.__working_index = len(self._working_lines) - 1
-
- # <getters/setters>
-
- def _set_text(self, value):
- """ set text at current working_index. Return whether it changed. """
+ # Current suggestion.
+ self.suggestion = None
+
+ # The history search text. (Used for filtering the history when we
+ # browse through it.)
+ self.history_search_text = None
+
+ # Undo/redo stacks
+ self._undo_stack = [] # Stack of (text, cursor_position)
+ self._redo_stack = []
+
+ #: The working lines. Similar to history, except that this can be
+ #: modified. The user can press arrow_up and edit previous entries.
+ #: Ctrl-C should reset this, and copy the whole history back in here.
+ #: Enter should process the current command and append to the real
+ #: history.
+ self._working_lines = self.history.strings[:]
+ self._working_lines.append(initial_document.text)
+ self.__working_index = len(self._working_lines) - 1
+
+ # <getters/setters>
+
+ def _set_text(self, value):
+ """ set text at current working_index. Return whether it changed. """
working_index = self.working_index
working_lines = self._working_lines
-
+
original_value = working_lines[working_index]
working_lines[working_index] = value
-
+
# Return True when this text has been changed.
if len(value) != len(original_value):
# For Python 2, it seems that when two strings have a different
@@ -350,190 +350,190 @@ class Buffer(object):
return True
return False
- def _set_cursor_position(self, value):
- """ Set cursor position. Return whether it changed. """
- original_position = self.__cursor_position
- self.__cursor_position = max(0, value)
-
- return value != original_position
-
- @property
- def text(self):
- return self._working_lines[self.working_index]
-
- @text.setter
- def text(self, value):
- """
- Setting text. (When doing this, make sure that the cursor_position is
- valid for this text. text/cursor_position should be consistent at any time,
- otherwise set a Document instead.)
- """
- assert isinstance(value, six.text_type), 'Got %r' % value
- assert self.cursor_position <= len(value)
-
- # Don't allow editing of read-only buffers.
- if self.read_only():
- raise EditReadOnlyBuffer()
-
- changed = self._set_text(value)
-
- if changed:
- self._text_changed()
-
- # Reset history search text.
- self.history_search_text = None
-
- @property
- def cursor_position(self):
- return self.__cursor_position
-
- @cursor_position.setter
- def cursor_position(self, value):
- """
- Setting cursor position.
- """
- assert isinstance(value, int)
- assert value <= len(self.text)
-
- changed = self._set_cursor_position(value)
-
- if changed:
- self._cursor_position_changed()
-
- @property
- def working_index(self):
- return self.__working_index
-
- @working_index.setter
- def working_index(self, value):
- if self.__working_index != value:
- self.__working_index = value
- self._text_changed()
-
- def _text_changed(self):
- # Remove any validation errors and complete state.
- self.validation_error = None
+ def _set_cursor_position(self, value):
+ """ Set cursor position. Return whether it changed. """
+ original_position = self.__cursor_position
+ self.__cursor_position = max(0, value)
+
+ return value != original_position
+
+ @property
+ def text(self):
+ return self._working_lines[self.working_index]
+
+ @text.setter
+ def text(self, value):
+ """
+ Setting text. (When doing this, make sure that the cursor_position is
+ valid for this text. text/cursor_position should be consistent at any time,
+ otherwise set a Document instead.)
+ """
+ assert isinstance(value, six.text_type), 'Got %r' % value
+ assert self.cursor_position <= len(value)
+
+ # Don't allow editing of read-only buffers.
+ if self.read_only():
+ raise EditReadOnlyBuffer()
+
+ changed = self._set_text(value)
+
+ if changed:
+ self._text_changed()
+
+ # Reset history search text.
+ self.history_search_text = None
+
+ @property
+ def cursor_position(self):
+ return self.__cursor_position
+
+ @cursor_position.setter
+ def cursor_position(self, value):
+ """
+ Setting cursor position.
+ """
+ assert isinstance(value, int)
+ assert value <= len(self.text)
+
+ changed = self._set_cursor_position(value)
+
+ if changed:
+ self._cursor_position_changed()
+
+ @property
+ def working_index(self):
+ return self.__working_index
+
+ @working_index.setter
+ def working_index(self, value):
+ if self.__working_index != value:
+ self.__working_index = value
+ self._text_changed()
+
+ def _text_changed(self):
+ # Remove any validation errors and complete state.
+ self.validation_error = None
self.validation_state = ValidationState.UNKNOWN
- self.complete_state = None
+ self.complete_state = None
self.yank_nth_arg_state = None
self.document_before_paste = None
- self.selection_state = None
- self.suggestion = None
+ self.selection_state = None
+ self.suggestion = None
self.preferred_column = None
-
- # fire 'on_text_changed' event.
- self.on_text_changed.fire()
-
- def _cursor_position_changed(self):
- # Remove any validation errors and complete state.
- self.validation_error = None
+
+ # fire 'on_text_changed' event.
+ self.on_text_changed.fire()
+
+ def _cursor_position_changed(self):
+ # Remove any validation errors and complete state.
+ self.validation_error = None
self.validation_state = ValidationState.UNKNOWN
- self.complete_state = None
+ self.complete_state = None
self.yank_nth_arg_state = None
self.document_before_paste = None
-
+
# Unset preferred_column. (Will be set after the cursor movement, if
# required.)
self.preferred_column = None
- # Note that the cursor position can change if we have a selection the
- # new position of the cursor determines the end of the selection.
-
- # fire 'on_cursor_position_changed' event.
- self.on_cursor_position_changed.fire()
-
- @property
- def document(self):
- """
- Return :class:`~prompt_toolkit.document.Document` instance from the
+ # Note that the cursor position can change if we have a selection the
+ # new position of the cursor determines the end of the selection.
+
+ # fire 'on_cursor_position_changed' event.
+ self.on_cursor_position_changed.fire()
+
+ @property
+ def document(self):
+ """
+ Return :class:`~prompt_toolkit.document.Document` instance from the
current text, cursor position and selection state.
- """
+ """
return self._document_cache[
self.text, self.cursor_position, self.selection_state]
-
- @document.setter
- def document(self, value):
- """
- Set :class:`~prompt_toolkit.document.Document` instance.
-
- This will set both the text and cursor position at the same time, but
- atomically. (Change events will be triggered only after both have been set.)
- """
- self.set_document(value)
-
- def set_document(self, value, bypass_readonly=False):
- """
- Set :class:`~prompt_toolkit.document.Document` instance. Like the
- ``document`` property, but accept an ``bypass_readonly`` argument.
-
- :param bypass_readonly: When True, don't raise an
- :class:`.EditReadOnlyBuffer` exception, even
- when the buffer is read-only.
- """
- assert isinstance(value, Document)
-
- # Don't allow editing of read-only buffers.
- if not bypass_readonly and self.read_only():
- raise EditReadOnlyBuffer()
-
- # Set text and cursor position first.
- text_changed = self._set_text(value.text)
- cursor_position_changed = self._set_cursor_position(value.cursor_position)
-
- # Now handle change events. (We do this when text/cursor position is
- # both set and consistent.)
- if text_changed:
- self._text_changed()
-
- if cursor_position_changed:
- self._cursor_position_changed()
-
- # End of <getters/setters>
-
- def save_to_undo_stack(self, clear_redo_stack=True):
- """
- Safe current state (input text and cursor position), so that we can
- restore it by calling undo.
- """
- # Safe if the text is different from the text at the top of the stack
- # is different. If the text is the same, just update the cursor position.
- if self._undo_stack and self._undo_stack[-1][0] == self.text:
- self._undo_stack[-1] = (self._undo_stack[-1][0], self.cursor_position)
- else:
- self._undo_stack.append((self.text, self.cursor_position))
-
- # Saving anything to the undo stack, clears the redo stack.
- if clear_redo_stack:
- self._redo_stack = []
-
- def transform_lines(self, line_index_iterator, transform_callback):
- """
- Transforms the text on a range of lines.
- When the iterator yield an index not in the range of lines that the
- document contains, it skips them silently.
-
- To uppercase some lines::
-
- new_text = transform_lines(range(5,10), lambda text: text.upper())
-
- :param line_index_iterator: Iterator of line numbers (int)
- :param transform_callback: callable that takes the original text of a
- line, and return the new text for this line.
-
- :returns: The new text.
- """
- # Split lines
- lines = self.text.split('\n')
-
- # Apply transformation
- for index in line_index_iterator:
- try:
- lines[index] = transform_callback(lines[index])
- except IndexError:
- pass
-
- return '\n'.join(lines)
-
+
+ @document.setter
+ def document(self, value):
+ """
+ Set :class:`~prompt_toolkit.document.Document` instance.
+
+ This will set both the text and cursor position at the same time, but
+ atomically. (Change events will be triggered only after both have been set.)
+ """
+ self.set_document(value)
+
+ def set_document(self, value, bypass_readonly=False):
+ """
+ Set :class:`~prompt_toolkit.document.Document` instance. Like the
+ ``document`` property, but accept an ``bypass_readonly`` argument.
+
+ :param bypass_readonly: When True, don't raise an
+ :class:`.EditReadOnlyBuffer` exception, even
+ when the buffer is read-only.
+ """
+ assert isinstance(value, Document)
+
+ # Don't allow editing of read-only buffers.
+ if not bypass_readonly and self.read_only():
+ raise EditReadOnlyBuffer()
+
+ # Set text and cursor position first.
+ text_changed = self._set_text(value.text)
+ cursor_position_changed = self._set_cursor_position(value.cursor_position)
+
+ # Now handle change events. (We do this when text/cursor position is
+ # both set and consistent.)
+ if text_changed:
+ self._text_changed()
+
+ if cursor_position_changed:
+ self._cursor_position_changed()
+
+ # End of <getters/setters>
+
+ def save_to_undo_stack(self, clear_redo_stack=True):
+ """
+ Safe current state (input text and cursor position), so that we can
+ restore it by calling undo.
+ """
+ # Safe if the text is different from the text at the top of the stack
+ # is different. If the text is the same, just update the cursor position.
+ if self._undo_stack and self._undo_stack[-1][0] == self.text:
+ self._undo_stack[-1] = (self._undo_stack[-1][0], self.cursor_position)
+ else:
+ self._undo_stack.append((self.text, self.cursor_position))
+
+ # Saving anything to the undo stack, clears the redo stack.
+ if clear_redo_stack:
+ self._redo_stack = []
+
+ def transform_lines(self, line_index_iterator, transform_callback):
+ """
+ Transforms the text on a range of lines.
+ When the iterator yield an index not in the range of lines that the
+ document contains, it skips them silently.
+
+ To uppercase some lines::
+
+ new_text = transform_lines(range(5,10), lambda text: text.upper())
+
+ :param line_index_iterator: Iterator of line numbers (int)
+ :param transform_callback: callable that takes the original text of a
+ line, and return the new text for this line.
+
+ :returns: The new text.
+ """
+ # Split lines
+ lines = self.text.split('\n')
+
+ # Apply transformation
+ for index in line_index_iterator:
+ try:
+ lines[index] = transform_callback(lines[index])
+ except IndexError:
+ pass
+
+ return '\n'.join(lines)
+
def transform_current_line(self, transform_callback):
"""
Apply the given transformation function to the current line.
@@ -548,364 +548,364 @@ class Buffer(object):
transform_callback(document.text[a:b]) +
document.text[b:])
- def transform_region(self, from_, to, transform_callback):
- """
- Transform a part of the input string.
-
- :param from_: (int) start position.
- :param to: (int) end position.
- :param transform_callback: Callable which accepts a string and returns
- the transformed string.
- """
- assert from_ < to
-
- self.text = ''.join([
- self.text[:from_] +
- transform_callback(self.text[from_:to]) +
- self.text[to:]
- ])
-
- def cursor_left(self, count=1):
- self.cursor_position += self.document.get_cursor_left_position(count=count)
-
- def cursor_right(self, count=1):
- self.cursor_position += self.document.get_cursor_right_position(count=count)
-
- def cursor_up(self, count=1):
- """ (for multiline edit). Move cursor to the previous line. """
+ def transform_region(self, from_, to, transform_callback):
+ """
+ Transform a part of the input string.
+
+ :param from_: (int) start position.
+ :param to: (int) end position.
+ :param transform_callback: Callable which accepts a string and returns
+ the transformed string.
+ """
+ assert from_ < to
+
+ self.text = ''.join([
+ self.text[:from_] +
+ transform_callback(self.text[from_:to]) +
+ self.text[to:]
+ ])
+
+ def cursor_left(self, count=1):
+ self.cursor_position += self.document.get_cursor_left_position(count=count)
+
+ def cursor_right(self, count=1):
+ self.cursor_position += self.document.get_cursor_right_position(count=count)
+
+ def cursor_up(self, count=1):
+ """ (for multiline edit). Move cursor to the previous line. """
original_column = self.preferred_column or self.document.cursor_position_col
self.cursor_position += self.document.get_cursor_up_position(
count=count, preferred_column=original_column)
-
+
# Remember the original column for the next up/down movement.
self.preferred_column = original_column
- def cursor_down(self, count=1):
- """ (for multiline edit). Move cursor to the next line. """
+ def cursor_down(self, count=1):
+ """ (for multiline edit). Move cursor to the next line. """
original_column = self.preferred_column or self.document.cursor_position_col
self.cursor_position += self.document.get_cursor_down_position(
count=count, preferred_column=original_column)
-
+
# Remember the original column for the next up/down movement.
self.preferred_column = original_column
def auto_up(self, count=1, go_to_start_of_line_if_history_changes=False):
- """
- If we're not on the first line (of a multiline input) go a line up,
- otherwise go back in history. (If nothing is selected.)
- """
- if self.complete_state:
- self.complete_previous(count=count)
- elif self.document.cursor_position_row > 0:
+ """
+ If we're not on the first line (of a multiline input) go a line up,
+ otherwise go back in history. (If nothing is selected.)
+ """
+ if self.complete_state:
+ self.complete_previous(count=count)
+ elif self.document.cursor_position_row > 0:
self.cursor_up(count=count)
- elif not self.selection_state:
- self.history_backward(count=count)
-
+ elif not self.selection_state:
+ self.history_backward(count=count)
+
# Go to the start of the line?
if go_to_start_of_line_if_history_changes:
self.cursor_position += self.document.get_start_of_line_position()
def auto_down(self, count=1, go_to_start_of_line_if_history_changes=False):
- """
- If we're not on the last line (of a multiline input) go a line down,
- otherwise go forward in history. (If nothing is selected.)
- """
- if self.complete_state:
- self.complete_next(count=count)
- elif self.document.cursor_position_row < self.document.line_count - 1:
+ """
+ If we're not on the last line (of a multiline input) go a line down,
+ otherwise go forward in history. (If nothing is selected.)
+ """
+ if self.complete_state:
+ self.complete_next(count=count)
+ elif self.document.cursor_position_row < self.document.line_count - 1:
self.cursor_down(count=count)
- elif not self.selection_state:
- self.history_forward(count=count)
-
+ elif not self.selection_state:
+ self.history_forward(count=count)
+
# Go to the start of the line?
if go_to_start_of_line_if_history_changes:
self.cursor_position += self.document.get_start_of_line_position()
- def delete_before_cursor(self, count=1):
- """
+ def delete_before_cursor(self, count=1):
+ """
Delete specified number of characters before cursor and return the
deleted text.
- """
- assert count >= 0
- deleted = ''
-
- if self.cursor_position > 0:
- deleted = self.text[self.cursor_position - count:self.cursor_position]
-
- new_text = self.text[:self.cursor_position - count] + self.text[self.cursor_position:]
- new_cursor_position = self.cursor_position - len(deleted)
-
- # Set new Document atomically.
- self.document = Document(new_text, new_cursor_position)
-
- return deleted
-
- def delete(self, count=1):
- """
+ """
+ assert count >= 0
+ deleted = ''
+
+ if self.cursor_position > 0:
+ deleted = self.text[self.cursor_position - count:self.cursor_position]
+
+ new_text = self.text[:self.cursor_position - count] + self.text[self.cursor_position:]
+ new_cursor_position = self.cursor_position - len(deleted)
+
+ # Set new Document atomically.
+ self.document = Document(new_text, new_cursor_position)
+
+ return deleted
+
+ def delete(self, count=1):
+ """
Delete specified number of characters and Return the deleted text.
- """
- if self.cursor_position < len(self.text):
- deleted = self.document.text_after_cursor[:count]
- self.text = self.text[:self.cursor_position] + \
- self.text[self.cursor_position + len(deleted):]
- return deleted
- else:
- return ''
-
+ """
+ if self.cursor_position < len(self.text):
+ deleted = self.document.text_after_cursor[:count]
+ self.text = self.text[:self.cursor_position] + \
+ self.text[self.cursor_position + len(deleted):]
+ return deleted
+ else:
+ return ''
+
def join_next_line(self, separator=' '):
- """
- Join the next line to the current one by deleting the line ending after
- the current line.
- """
- if not self.document.on_last_line:
- self.cursor_position += self.document.get_end_of_line_position()
- self.delete()
-
- # Remove spaces.
+ """
+ Join the next line to the current one by deleting the line ending after
+ the current line.
+ """
+ if not self.document.on_last_line:
+ self.cursor_position += self.document.get_end_of_line_position()
+ self.delete()
+
+ # Remove spaces.
self.text = (self.document.text_before_cursor + separator +
- self.document.text_after_cursor.lstrip(' '))
-
+ self.document.text_after_cursor.lstrip(' '))
+
def join_selected_lines(self, separator=' '):
- """
- Join the selected lines.
- """
- assert self.selection_state
-
- # Get lines.
- from_, to = sorted([self.cursor_position, self.selection_state.original_cursor_position])
-
- before = self.text[:from_]
- lines = self.text[from_:to].splitlines()
- after = self.text[to:]
-
- # Replace leading spaces with just one space.
+ """
+ Join the selected lines.
+ """
+ assert self.selection_state
+
+ # Get lines.
+ from_, to = sorted([self.cursor_position, self.selection_state.original_cursor_position])
+
+ before = self.text[:from_]
+ lines = self.text[from_:to].splitlines()
+ after = self.text[to:]
+
+ # Replace leading spaces with just one space.
lines = [l.lstrip(' ') + separator for l in lines]
-
- # Set new document.
- self.document = Document(text=before + ''.join(lines) + after,
- cursor_position=len(before + ''.join(lines[:-1])) - 1)
-
- def swap_characters_before_cursor(self):
- """
- Swap the last two characters before the cursor.
- """
- pos = self.cursor_position
-
- if pos >= 2:
- a = self.text[pos - 2]
- b = self.text[pos - 1]
-
- self.text = self.text[:pos-2] + b + a + self.text[pos:]
-
- def go_to_history(self, index):
- """
- Go to this item in the history.
- """
- if index < len(self._working_lines):
- self.working_index = index
- self.cursor_position = len(self.text)
-
- def complete_next(self, count=1, disable_wrap_around=False):
- """
- Browse to the next completions.
- (Does nothing if there are no completion.)
- """
- if self.complete_state:
- completions_count = len(self.complete_state.current_completions)
-
- if self.complete_state.complete_index is None:
- index = 0
- elif self.complete_state.complete_index == completions_count - 1:
- index = None
-
- if disable_wrap_around:
- return
- else:
- index = min(completions_count-1, self.complete_state.complete_index + count)
- self.go_to_completion(index)
-
- def complete_previous(self, count=1, disable_wrap_around=False):
- """
- Browse to the previous completions.
- (Does nothing if there are no completion.)
- """
- if self.complete_state:
- if self.complete_state.complete_index == 0:
- index = None
-
- if disable_wrap_around:
- return
- elif self.complete_state.complete_index is None:
- index = len(self.complete_state.current_completions) - 1
- else:
- index = max(0, self.complete_state.complete_index - count)
-
- self.go_to_completion(index)
-
- def cancel_completion(self):
- """
- Cancel completion, go back to the original text.
- """
- if self.complete_state:
- self.go_to_completion(None)
- self.complete_state = None
-
- def set_completions(self, completions, go_to_first=True, go_to_last=False):
- """
- Start completions. (Generate list of completions and initialize.)
- """
- assert not (go_to_first and go_to_last)
-
- # Generate list of all completions.
- if completions is None:
- if self.completer:
- completions = list(self.completer.get_completions(
- self.document,
- CompleteEvent(completion_requested=True)
- ))
- else:
- completions = []
-
- # Set `complete_state`.
- if completions:
- self.complete_state = CompletionState(
- original_document=self.document,
- current_completions=completions)
- if go_to_first:
- self.go_to_completion(0)
- elif go_to_last:
- self.go_to_completion(len(completions) - 1)
- else:
- self.go_to_completion(None)
-
- else:
- self.complete_state = None
-
- def start_history_lines_completion(self):
- """
- Start a completion based on all the other lines in the document and the
- history.
- """
- found_completions = set()
- completions = []
-
- # For every line of the whole history, find matches with the current line.
- current_line = self.document.current_line_before_cursor.lstrip()
-
- for i, string in enumerate(self._working_lines):
- for j, l in enumerate(string.split('\n')):
- l = l.strip()
- if l and l.startswith(current_line):
- # When a new line has been found.
- if l not in found_completions:
- found_completions.add(l)
-
- # Create completion.
- if i == self.working_index:
- display_meta = "Current, line %s" % (j+1)
- else:
- display_meta = "History %s, line %s" % (i+1, j+1)
-
- completions.append(Completion(
- l,
- start_position=-len(current_line),
- display_meta=display_meta))
-
- self.set_completions(completions=completions[::-1])
-
- def go_to_completion(self, index):
- """
- Select a completion from the list of current completions.
- """
- assert index is None or isinstance(index, int)
- assert self.complete_state
-
- # Set new completion
- state = self.complete_state.go_to_index(index)
-
- # Set text/cursor position
- new_text, new_cursor_position = state.new_text_and_position()
- self.document = Document(new_text, new_cursor_position)
-
- # (changing text/cursor position will unset complete_state.)
- self.complete_state = state
-
- def apply_completion(self, completion):
- """
- Insert a given completion.
- """
- assert isinstance(completion, Completion)
-
- # If there was already a completion active, cancel that one.
- if self.complete_state:
- self.go_to_completion(None)
- self.complete_state = None
-
- # Insert text from the given completion.
- self.delete_before_cursor(-completion.start_position)
- self.insert_text(completion.text)
-
- def _set_history_search(self):
- """ Set `history_search_text`. """
- if self.enable_history_search():
- if self.history_search_text is None:
+
+ # Set new document.
+ self.document = Document(text=before + ''.join(lines) + after,
+ cursor_position=len(before + ''.join(lines[:-1])) - 1)
+
+ def swap_characters_before_cursor(self):
+ """
+ Swap the last two characters before the cursor.
+ """
+ pos = self.cursor_position
+
+ if pos >= 2:
+ a = self.text[pos - 2]
+ b = self.text[pos - 1]
+
+ self.text = self.text[:pos-2] + b + a + self.text[pos:]
+
+ def go_to_history(self, index):
+ """
+ Go to this item in the history.
+ """
+ if index < len(self._working_lines):
+ self.working_index = index
+ self.cursor_position = len(self.text)
+
+ def complete_next(self, count=1, disable_wrap_around=False):
+ """
+ Browse to the next completions.
+ (Does nothing if there are no completion.)
+ """
+ if self.complete_state:
+ completions_count = len(self.complete_state.current_completions)
+
+ if self.complete_state.complete_index is None:
+ index = 0
+ elif self.complete_state.complete_index == completions_count - 1:
+ index = None
+
+ if disable_wrap_around:
+ return
+ else:
+ index = min(completions_count-1, self.complete_state.complete_index + count)
+ self.go_to_completion(index)
+
+ def complete_previous(self, count=1, disable_wrap_around=False):
+ """
+ Browse to the previous completions.
+ (Does nothing if there are no completion.)
+ """
+ if self.complete_state:
+ if self.complete_state.complete_index == 0:
+ index = None
+
+ if disable_wrap_around:
+ return
+ elif self.complete_state.complete_index is None:
+ index = len(self.complete_state.current_completions) - 1
+ else:
+ index = max(0, self.complete_state.complete_index - count)
+
+ self.go_to_completion(index)
+
+ def cancel_completion(self):
+ """
+ Cancel completion, go back to the original text.
+ """
+ if self.complete_state:
+ self.go_to_completion(None)
+ self.complete_state = None
+
+ def set_completions(self, completions, go_to_first=True, go_to_last=False):
+ """
+ Start completions. (Generate list of completions and initialize.)
+ """
+ assert not (go_to_first and go_to_last)
+
+ # Generate list of all completions.
+ if completions is None:
+ if self.completer:
+ completions = list(self.completer.get_completions(
+ self.document,
+ CompleteEvent(completion_requested=True)
+ ))
+ else:
+ completions = []
+
+ # Set `complete_state`.
+ if completions:
+ self.complete_state = CompletionState(
+ original_document=self.document,
+ current_completions=completions)
+ if go_to_first:
+ self.go_to_completion(0)
+ elif go_to_last:
+ self.go_to_completion(len(completions) - 1)
+ else:
+ self.go_to_completion(None)
+
+ else:
+ self.complete_state = None
+
+ def start_history_lines_completion(self):
+ """
+ Start a completion based on all the other lines in the document and the
+ history.
+ """
+ found_completions = set()
+ completions = []
+
+ # For every line of the whole history, find matches with the current line.
+ current_line = self.document.current_line_before_cursor.lstrip()
+
+ for i, string in enumerate(self._working_lines):
+ for j, l in enumerate(string.split('\n')):
+ l = l.strip()
+ if l and l.startswith(current_line):
+ # When a new line has been found.
+ if l not in found_completions:
+ found_completions.add(l)
+
+ # Create completion.
+ if i == self.working_index:
+ display_meta = "Current, line %s" % (j+1)
+ else:
+ display_meta = "History %s, line %s" % (i+1, j+1)
+
+ completions.append(Completion(
+ l,
+ start_position=-len(current_line),
+ display_meta=display_meta))
+
+ self.set_completions(completions=completions[::-1])
+
+ def go_to_completion(self, index):
+ """
+ Select a completion from the list of current completions.
+ """
+ assert index is None or isinstance(index, int)
+ assert self.complete_state
+
+ # Set new completion
+ state = self.complete_state.go_to_index(index)
+
+ # Set text/cursor position
+ new_text, new_cursor_position = state.new_text_and_position()
+ self.document = Document(new_text, new_cursor_position)
+
+ # (changing text/cursor position will unset complete_state.)
+ self.complete_state = state
+
+ def apply_completion(self, completion):
+ """
+ Insert a given completion.
+ """
+ assert isinstance(completion, Completion)
+
+ # If there was already a completion active, cancel that one.
+ if self.complete_state:
+ self.go_to_completion(None)
+ self.complete_state = None
+
+ # Insert text from the given completion.
+ self.delete_before_cursor(-completion.start_position)
+ self.insert_text(completion.text)
+
+ def _set_history_search(self):
+ """ Set `history_search_text`. """
+ if self.enable_history_search():
+ if self.history_search_text is None:
self.history_search_text = self.document.text_before_cursor
- else:
- self.history_search_text = None
-
- def _history_matches(self, i):
- """
- True when the current entry matches the history search.
- (when we don't have history search, it's also True.)
- """
- return (self.history_search_text is None or
- self._working_lines[i].startswith(self.history_search_text))
-
- def history_forward(self, count=1):
- """
- Move forwards through the history.
-
- :param count: Amount of items to move forward.
- """
- self._set_history_search()
-
- # Go forward in history.
- found_something = False
-
- for i in range(self.working_index + 1, len(self._working_lines)):
- if self._history_matches(i):
- self.working_index = i
- count -= 1
- found_something = True
- if count == 0:
- break
-
- # If we found an entry, move cursor to the end of the first line.
- if found_something:
- self.cursor_position = 0
- self.cursor_position += self.document.get_end_of_line_position()
-
- def history_backward(self, count=1):
- """
- Move backwards through history.
- """
- self._set_history_search()
-
- # Go back in history.
- found_something = False
-
- for i in range(self.working_index - 1, -1, -1):
- if self._history_matches(i):
- self.working_index = i
- count -= 1
- found_something = True
- if count == 0:
- break
-
- # If we move to another entry, move cursor to the end of the line.
- if found_something:
- self.cursor_position = len(self.text)
-
+ else:
+ self.history_search_text = None
+
+ def _history_matches(self, i):
+ """
+ True when the current entry matches the history search.
+ (when we don't have history search, it's also True.)
+ """
+ return (self.history_search_text is None or
+ self._working_lines[i].startswith(self.history_search_text))
+
+ def history_forward(self, count=1):
+ """
+ Move forwards through the history.
+
+ :param count: Amount of items to move forward.
+ """
+ self._set_history_search()
+
+ # Go forward in history.
+ found_something = False
+
+ for i in range(self.working_index + 1, len(self._working_lines)):
+ if self._history_matches(i):
+ self.working_index = i
+ count -= 1
+ found_something = True
+ if count == 0:
+ break
+
+ # If we found an entry, move cursor to the end of the first line.
+ if found_something:
+ self.cursor_position = 0
+ self.cursor_position += self.document.get_end_of_line_position()
+
+ def history_backward(self, count=1):
+ """
+ Move backwards through history.
+ """
+ self._set_history_search()
+
+ # Go back in history.
+ found_something = False
+
+ for i in range(self.working_index - 1, -1, -1):
+ if self._history_matches(i):
+ self.working_index = i
+ count -= 1
+ found_something = True
+ if count == 0:
+ break
+
+ # If we move to another entry, move cursor to the end of the line.
+ if found_something:
+ self.cursor_position = len(self.text)
+
def yank_nth_arg(self, n=None, _yank_last_arg=False):
"""
Pick nth word from previous history entry (depending on current
@@ -963,36 +963,36 @@ class Buffer(object):
"""
self.yank_nth_arg(n=n, _yank_last_arg=True)
- def start_selection(self, selection_type=SelectionType.CHARACTERS):
- """
- Take the current cursor position as the start of this selection.
- """
- self.selection_state = SelectionState(self.cursor_position, selection_type)
-
- def copy_selection(self, _cut=False):
- """
- Copy selected text and return :class:`.ClipboardData` instance.
- """
- new_document, clipboard_data = self.document.cut_selection()
- if _cut:
- self.document = new_document
-
- self.selection_state = None
- return clipboard_data
-
- def cut_selection(self):
- """
- Delete selected text and return :class:`.ClipboardData` instance.
- """
- return self.copy_selection(_cut=True)
-
+ def start_selection(self, selection_type=SelectionType.CHARACTERS):
+ """
+ Take the current cursor position as the start of this selection.
+ """
+ self.selection_state = SelectionState(self.cursor_position, selection_type)
+
+ def copy_selection(self, _cut=False):
+ """
+ Copy selected text and return :class:`.ClipboardData` instance.
+ """
+ new_document, clipboard_data = self.document.cut_selection()
+ if _cut:
+ self.document = new_document
+
+ self.selection_state = None
+ return clipboard_data
+
+ def cut_selection(self):
+ """
+ Delete selected text and return :class:`.ClipboardData` instance.
+ """
+ return self.copy_selection(_cut=True)
+
def paste_clipboard_data(self, data, paste_mode=PasteMode.EMACS, count=1):
- """
- Insert the data from the clipboard.
- """
- assert isinstance(data, ClipboardData)
+ """
+ Insert the data from the clipboard.
+ """
+ assert isinstance(data, ClipboardData)
assert paste_mode in (PasteMode.VI_BEFORE, PasteMode.VI_AFTER, PasteMode.EMACS)
-
+
original_document = self.document
self.document = self.document.paste_clipboard_data(data, paste_mode=paste_mode, count=count)
@@ -1000,219 +1000,219 @@ class Buffer(object):
# because assigning to 'document' will erase it.
self.document_before_paste = original_document
- def newline(self, copy_margin=True):
- """
- Insert a line ending at the current position.
- """
- if copy_margin:
- self.insert_text('\n' + self.document.leading_whitespace_in_current_line)
- else:
- self.insert_text('\n')
-
- def insert_line_above(self, copy_margin=True):
- """
- Insert a new line above the current one.
- """
- if copy_margin:
- insert = self.document.leading_whitespace_in_current_line + '\n'
- else:
- insert = '\n'
-
- self.cursor_position += self.document.get_start_of_line_position()
- self.insert_text(insert)
- self.cursor_position -= 1
-
- def insert_line_below(self, copy_margin=True):
- """
- Insert a new line below the current one.
- """
- if copy_margin:
- insert = '\n' + self.document.leading_whitespace_in_current_line
- else:
- insert = '\n'
-
- self.cursor_position += self.document.get_end_of_line_position()
- self.insert_text(insert)
-
- def insert_text(self, data, overwrite=False, move_cursor=True, fire_event=True):
- """
- Insert characters at cursor position.
-
- :param fire_event: Fire `on_text_insert` event. This is mainly used to
- trigger autocompletion while typing.
- """
+ def newline(self, copy_margin=True):
+ """
+ Insert a line ending at the current position.
+ """
+ if copy_margin:
+ self.insert_text('\n' + self.document.leading_whitespace_in_current_line)
+ else:
+ self.insert_text('\n')
+
+ def insert_line_above(self, copy_margin=True):
+ """
+ Insert a new line above the current one.
+ """
+ if copy_margin:
+ insert = self.document.leading_whitespace_in_current_line + '\n'
+ else:
+ insert = '\n'
+
+ self.cursor_position += self.document.get_start_of_line_position()
+ self.insert_text(insert)
+ self.cursor_position -= 1
+
+ def insert_line_below(self, copy_margin=True):
+ """
+ Insert a new line below the current one.
+ """
+ if copy_margin:
+ insert = '\n' + self.document.leading_whitespace_in_current_line
+ else:
+ insert = '\n'
+
+ self.cursor_position += self.document.get_end_of_line_position()
+ self.insert_text(insert)
+
+ def insert_text(self, data, overwrite=False, move_cursor=True, fire_event=True):
+ """
+ Insert characters at cursor position.
+
+ :param fire_event: Fire `on_text_insert` event. This is mainly used to
+ trigger autocompletion while typing.
+ """
# Original text & cursor position.
otext = self.text
ocpos = self.cursor_position
- # In insert/text mode.
- if overwrite:
+ # In insert/text mode.
+ if overwrite:
# Don't overwrite the newline itself. Just before the line ending,
# it should act like insert mode.
overwritten_text = otext[ocpos:ocpos + len(data)]
- if '\n' in overwritten_text:
- overwritten_text = overwritten_text[:overwritten_text.find('\n')]
-
+ if '\n' in overwritten_text:
+ overwritten_text = overwritten_text[:overwritten_text.find('\n')]
+
self.text = otext[:ocpos] + data + otext[ocpos + len(overwritten_text):]
- else:
+ else:
self.text = otext[:ocpos] + data + otext[ocpos:]
-
- if move_cursor:
- self.cursor_position += len(data)
-
- # Fire 'on_text_insert' event.
- if fire_event:
- self.on_text_insert.fire()
-
- def undo(self):
- # Pop from the undo-stack until we find a text that if different from
- # the current text. (The current logic of `save_to_undo_stack` will
- # cause that the top of the undo stack is usually the same as the
- # current text, so in that case we have to pop twice.)
- while self._undo_stack:
- text, pos = self._undo_stack.pop()
-
- if text != self.text:
- # Push current text to redo stack.
- self._redo_stack.append((self.text, self.cursor_position))
-
- # Set new text/cursor_position.
- self.document = Document(text, cursor_position=pos)
- break
-
- def redo(self):
- if self._redo_stack:
- # Copy current state on undo stack.
- self.save_to_undo_stack(clear_redo_stack=False)
-
- # Pop state from redo stack.
- text, pos = self._redo_stack.pop()
- self.document = Document(text, cursor_position=pos)
-
- def validate(self):
- """
- Returns `True` if valid.
- """
+
+ if move_cursor:
+ self.cursor_position += len(data)
+
+ # Fire 'on_text_insert' event.
+ if fire_event:
+ self.on_text_insert.fire()
+
+ def undo(self):
+ # Pop from the undo-stack until we find a text that if different from
+ # the current text. (The current logic of `save_to_undo_stack` will
+ # cause that the top of the undo stack is usually the same as the
+ # current text, so in that case we have to pop twice.)
+ while self._undo_stack:
+ text, pos = self._undo_stack.pop()
+
+ if text != self.text:
+ # Push current text to redo stack.
+ self._redo_stack.append((self.text, self.cursor_position))
+
+ # Set new text/cursor_position.
+ self.document = Document(text, cursor_position=pos)
+ break
+
+ def redo(self):
+ if self._redo_stack:
+ # Copy current state on undo stack.
+ self.save_to_undo_stack(clear_redo_stack=False)
+
+ # Pop state from redo stack.
+ text, pos = self._redo_stack.pop()
+ self.document = Document(text, cursor_position=pos)
+
+ def validate(self):
+ """
+ Returns `True` if valid.
+ """
# Don't call the validator again, if it was already called for the
# current input.
if self.validation_state != ValidationState.UNKNOWN:
return self.validation_state == ValidationState.VALID
-
- # Validate first. If not valid, set validation exception.
- if self.validator:
- try:
- self.validator.validate(self.document)
- except ValidationError as e:
- # Set cursor position (don't allow invalid values.)
- cursor_position = e.cursor_position
- self.cursor_position = min(max(0, cursor_position), len(self.text))
-
+
+ # Validate first. If not valid, set validation exception.
+ if self.validator:
+ try:
+ self.validator.validate(self.document)
+ except ValidationError as e:
+ # Set cursor position (don't allow invalid values.)
+ cursor_position = e.cursor_position
+ self.cursor_position = min(max(0, cursor_position), len(self.text))
+
self.validation_state = ValidationState.INVALID
- self.validation_error = e
- return False
-
+ self.validation_error = e
+ return False
+
self.validation_state = ValidationState.VALID
self.validation_error = None
- return True
-
- def append_to_history(self):
- """
- Append the current input to the history.
- (Only if valid input.)
- """
- # Validate first. If not valid, set validation exception.
- if not self.validate():
- return
-
- # Save at the tail of the history. (But don't if the last entry the
- # history is already the same.)
- if self.text and (not len(self.history) or self.history[-1] != self.text):
- self.history.append(self.text)
-
- def _search(self, search_state, include_current_position=False, count=1):
- """
- Execute search. Return (working_index, cursor_position) tuple when this
- search is applied. Returns `None` when this text cannot be found.
- """
- assert isinstance(search_state, SearchState)
- assert isinstance(count, int) and count > 0
-
- text = search_state.text
- direction = search_state.direction
- ignore_case = search_state.ignore_case()
-
- def search_once(working_index, document):
- """
- Do search one time.
- Return (working_index, document) or `None`
- """
- if direction == IncrementalSearchDirection.FORWARD:
- # Try find at the current input.
- new_index = document.find(
- text, include_current_position=include_current_position,
- ignore_case=ignore_case)
-
- if new_index is not None:
- return (working_index,
- Document(document.text, document.cursor_position + new_index))
- else:
- # No match, go forward in the history. (Include len+1 to wrap around.)
- # (Here we should always include all cursor positions, because
- # it's a different line.)
- for i in range(working_index + 1, len(self._working_lines) + 1):
- i %= len(self._working_lines)
-
- document = Document(self._working_lines[i], 0)
- new_index = document.find(text, include_current_position=True,
- ignore_case=ignore_case)
- if new_index is not None:
- return (i, Document(document.text, new_index))
- else:
- # Try find at the current input.
- new_index = document.find_backwards(
- text, ignore_case=ignore_case)
-
- if new_index is not None:
- return (working_index,
- Document(document.text, document.cursor_position + new_index))
- else:
- # No match, go back in the history. (Include -1 to wrap around.)
- for i in range(working_index - 1, -2, -1):
- i %= len(self._working_lines)
-
- document = Document(self._working_lines[i], len(self._working_lines[i]))
- new_index = document.find_backwards(
- text, ignore_case=ignore_case)
- if new_index is not None:
- return (i, Document(document.text, len(document.text) + new_index))
-
- # Do 'count' search iterations.
- working_index = self.working_index
- document = self.document
- for _ in range(count):
- result = search_once(working_index, document)
- if result is None:
+ return True
+
+ def append_to_history(self):
+ """
+ Append the current input to the history.
+ (Only if valid input.)
+ """
+ # Validate first. If not valid, set validation exception.
+ if not self.validate():
+ return
+
+ # Save at the tail of the history. (But don't if the last entry the
+ # history is already the same.)
+ if self.text and (not len(self.history) or self.history[-1] != self.text):
+ self.history.append(self.text)
+
+ def _search(self, search_state, include_current_position=False, count=1):
+ """
+ Execute search. Return (working_index, cursor_position) tuple when this
+ search is applied. Returns `None` when this text cannot be found.
+ """
+ assert isinstance(search_state, SearchState)
+ assert isinstance(count, int) and count > 0
+
+ text = search_state.text
+ direction = search_state.direction
+ ignore_case = search_state.ignore_case()
+
+ def search_once(working_index, document):
+ """
+ Do search one time.
+ Return (working_index, document) or `None`
+ """
+ if direction == IncrementalSearchDirection.FORWARD:
+ # Try find at the current input.
+ new_index = document.find(
+ text, include_current_position=include_current_position,
+ ignore_case=ignore_case)
+
+ if new_index is not None:
+ return (working_index,
+ Document(document.text, document.cursor_position + new_index))
+ else:
+ # No match, go forward in the history. (Include len+1 to wrap around.)
+ # (Here we should always include all cursor positions, because
+ # it's a different line.)
+ for i in range(working_index + 1, len(self._working_lines) + 1):
+ i %= len(self._working_lines)
+
+ document = Document(self._working_lines[i], 0)
+ new_index = document.find(text, include_current_position=True,
+ ignore_case=ignore_case)
+ if new_index is not None:
+ return (i, Document(document.text, new_index))
+ else:
+ # Try find at the current input.
+ new_index = document.find_backwards(
+ text, ignore_case=ignore_case)
+
+ if new_index is not None:
+ return (working_index,
+ Document(document.text, document.cursor_position + new_index))
+ else:
+ # No match, go back in the history. (Include -1 to wrap around.)
+ for i in range(working_index - 1, -2, -1):
+ i %= len(self._working_lines)
+
+ document = Document(self._working_lines[i], len(self._working_lines[i]))
+ new_index = document.find_backwards(
+ text, ignore_case=ignore_case)
+ if new_index is not None:
+ return (i, Document(document.text, len(document.text) + new_index))
+
+ # Do 'count' search iterations.
+ working_index = self.working_index
+ document = self.document
+ for _ in range(count):
+ result = search_once(working_index, document)
+ if result is None:
return # Nothing found.
- else:
- working_index, document = result
-
- return (working_index, document.cursor_position)
-
- def document_for_search(self, search_state):
- """
- Return a :class:`~prompt_toolkit.document.Document` instance that has
+ else:
+ working_index, document = result
+
+ return (working_index, document.cursor_position)
+
+ def document_for_search(self, search_state):
+ """
+ Return a :class:`~prompt_toolkit.document.Document` instance that has
the text/cursor position for this search, if we would apply it. This
will be used in the
:class:`~prompt_toolkit.layout.controls.BufferControl` to display
feedback while searching.
- """
- search_result = self._search(search_state, include_current_position=True)
-
- if search_result is None:
- return self.document
- else:
- working_index, cursor_position = search_result
-
+ """
+ search_result = self._search(search_state, include_current_position=True)
+
+ if search_result is None:
+ return self.document
+ else:
+ working_index, cursor_position = search_result
+
# Keep selection, when `working_index` was not changed.
if working_index == self.working_index:
selection = self.selection_state
@@ -1237,137 +1237,137 @@ class Buffer(object):
working_index, cursor_position = search_result
return cursor_position
- def apply_search(self, search_state, include_current_position=True, count=1):
- """
- Apply search. If something is found, set `working_index` and
- `cursor_position`.
- """
+ def apply_search(self, search_state, include_current_position=True, count=1):
+ """
+ Apply search. If something is found, set `working_index` and
+ `cursor_position`.
+ """
search_result = self._search(
search_state, include_current_position=include_current_position, count=count)
-
- if search_result is not None:
- working_index, cursor_position = search_result
- self.working_index = working_index
- self.cursor_position = cursor_position
-
- def exit_selection(self):
- self.selection_state = None
-
- def open_in_editor(self, cli):
- """
- Open code in editor.
-
- :param cli: :class:`~prompt_toolkit.interface.CommandLineInterface`
- instance.
- """
- if self.read_only():
- raise EditReadOnlyBuffer()
-
- # Write to temporary file
- descriptor, filename = tempfile.mkstemp(self.tempfile_suffix)
- os.write(descriptor, self.text.encode('utf-8'))
- os.close(descriptor)
-
- # Open in editor
- # (We need to use `cli.run_in_terminal`, because not all editors go to
- # the alternate screen buffer, and some could influence the cursor
- # position.)
- succes = cli.run_in_terminal(lambda: self._open_file_in_editor(filename))
-
- # Read content again.
- if succes:
- with open(filename, 'rb') as f:
- text = f.read().decode('utf-8')
-
- # Drop trailing newline. (Editors are supposed to add it at the
- # end, but we don't need it.)
- if text.endswith('\n'):
- text = text[:-1]
-
- self.document = Document(
- text=text,
- cursor_position=len(text))
-
- # Clean up temp file.
- os.remove(filename)
-
- def _open_file_in_editor(self, filename):
- """
- Call editor executable.
-
- Return True when we received a zero return code.
- """
+
+ if search_result is not None:
+ working_index, cursor_position = search_result
+ self.working_index = working_index
+ self.cursor_position = cursor_position
+
+ def exit_selection(self):
+ self.selection_state = None
+
+ def open_in_editor(self, cli):
+ """
+ Open code in editor.
+
+ :param cli: :class:`~prompt_toolkit.interface.CommandLineInterface`
+ instance.
+ """
+ if self.read_only():
+ raise EditReadOnlyBuffer()
+
+ # Write to temporary file
+ descriptor, filename = tempfile.mkstemp(self.tempfile_suffix)
+ os.write(descriptor, self.text.encode('utf-8'))
+ os.close(descriptor)
+
+ # Open in editor
+ # (We need to use `cli.run_in_terminal`, because not all editors go to
+ # the alternate screen buffer, and some could influence the cursor
+ # position.)
+ succes = cli.run_in_terminal(lambda: self._open_file_in_editor(filename))
+
+ # Read content again.
+ if succes:
+ with open(filename, 'rb') as f:
+ text = f.read().decode('utf-8')
+
+ # Drop trailing newline. (Editors are supposed to add it at the
+ # end, but we don't need it.)
+ if text.endswith('\n'):
+ text = text[:-1]
+
+ self.document = Document(
+ text=text,
+ cursor_position=len(text))
+
+ # Clean up temp file.
+ os.remove(filename)
+
+ def _open_file_in_editor(self, filename):
+ """
+ Call editor executable.
+
+ Return True when we received a zero return code.
+ """
# If the 'VISUAL' or 'EDITOR' environment variable has been set, use that.
- # Otherwise, fall back to the first available editor that we can find.
+ # Otherwise, fall back to the first available editor that we can find.
visual = os.environ.get('VISUAL')
- editor = os.environ.get('EDITOR')
-
- editors = [
+ editor = os.environ.get('EDITOR')
+
+ editors = [
visual,
- editor,
-
- # Order of preference.
- '/usr/bin/editor',
- '/usr/bin/nano',
- '/usr/bin/pico',
- '/usr/bin/vi',
- '/usr/bin/emacs',
- ]
-
- for e in editors:
- if e:
- try:
+ editor,
+
+ # Order of preference.
+ '/usr/bin/editor',
+ '/usr/bin/nano',
+ '/usr/bin/pico',
+ '/usr/bin/vi',
+ '/usr/bin/emacs',
+ ]
+
+ for e in editors:
+ if e:
+ try:
# Use 'shlex.split()', because $VISUAL can contain spaces
# and quotes.
returncode = subprocess.call(shlex.split(e) + [filename])
- return returncode == 0
-
- except OSError:
- # Executable does not exist, try the next one.
- pass
-
- return False
-
-
-def indent(buffer, from_row, to_row, count=1):
- """
- Indent text of a :class:`.Buffer` object.
- """
- current_row = buffer.document.cursor_position_row
- line_range = range(from_row, to_row)
-
- # Apply transformation.
- new_text = buffer.transform_lines(line_range, lambda l: ' ' * count + l)
- buffer.document = Document(
- new_text,
- Document(new_text).translate_row_col_to_index(current_row, 0))
-
- # Go to the start of the line.
- buffer.cursor_position += buffer.document.get_start_of_line_position(after_whitespace=True)
-
-
-def unindent(buffer, from_row, to_row, count=1):
- """
- Unindent text of a :class:`.Buffer` object.
- """
- current_row = buffer.document.cursor_position_row
- line_range = range(from_row, to_row)
-
- def transform(text):
- remove = ' ' * count
- if text.startswith(remove):
- return text[len(remove):]
- else:
- return text.lstrip()
-
- # Apply transformation.
- new_text = buffer.transform_lines(line_range, transform)
- buffer.document = Document(
- new_text,
- Document(new_text).translate_row_col_to_index(current_row, 0))
-
- # Go to the start of the line.
- buffer.cursor_position += buffer.document.get_start_of_line_position(after_whitespace=True)
+ return returncode == 0
+
+ except OSError:
+ # Executable does not exist, try the next one.
+ pass
+
+ return False
+
+
+def indent(buffer, from_row, to_row, count=1):
+ """
+ Indent text of a :class:`.Buffer` object.
+ """
+ current_row = buffer.document.cursor_position_row
+ line_range = range(from_row, to_row)
+
+ # Apply transformation.
+ new_text = buffer.transform_lines(line_range, lambda l: ' ' * count + l)
+ buffer.document = Document(
+ new_text,
+ Document(new_text).translate_row_col_to_index(current_row, 0))
+
+ # Go to the start of the line.
+ buffer.cursor_position += buffer.document.get_start_of_line_position(after_whitespace=True)
+
+
+def unindent(buffer, from_row, to_row, count=1):
+ """
+ Unindent text of a :class:`.Buffer` object.
+ """
+ current_row = buffer.document.cursor_position_row
+ line_range = range(from_row, to_row)
+
+ def transform(text):
+ remove = ' ' * count
+ if text.startswith(remove):
+ return text[len(remove):]
+ else:
+ return text.lstrip()
+
+ # Apply transformation.
+ new_text = buffer.transform_lines(line_range, transform)
+ buffer.document = Document(
+ new_text,
+ Document(new_text).translate_row_col_to_index(current_row, 0))
+
+ # Go to the start of the line.
+ buffer.cursor_position += buffer.document.get_start_of_line_position(after_whitespace=True)
def reshape_text(buffer, from_row, to_row):