aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/prompt-toolkit/py2/prompt_toolkit
diff options
context:
space:
mode:
authorDevtools Arcadia <arcadia-devtools@yandex-team.ru>2022-02-07 18:08:42 +0300
committerDevtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net>2022-02-07 18:08:42 +0300
commit1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch)
treee26c9fed0de5d9873cce7e00bc214573dc2195b7 /contrib/python/prompt-toolkit/py2/prompt_toolkit
downloadydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'contrib/python/prompt-toolkit/py2/prompt_toolkit')
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/__init__.py22
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/application.py192
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/auto_suggest.py88
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/buffer.py1415
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/buffer_mapping.py92
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/cache.py111
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/clipboard/__init__.py8
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/clipboard/base.py62
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/clipboard/in_memory.py42
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/clipboard/pyperclip.py39
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/completion.py170
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/__init__.py0
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/completers/__init__.py5
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/completers/base.py61
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/completers/filesystem.py105
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/completers/system.py56
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/regular_languages/__init__.py76
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/regular_languages/compiler.py408
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/regular_languages/completion.py84
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/regular_languages/lexer.py90
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/regular_languages/regex_parser.py262
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/regular_languages/validation.py57
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/telnet/__init__.py2
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/telnet/application.py32
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/telnet/log.py11
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/telnet/protocol.py181
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/telnet/server.py407
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/validators/__init__.py0
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/validators/base.py34
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/document.py1001
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/enums.py29
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/__init__.py0
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/asyncio_base.py46
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/asyncio_posix.py113
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/asyncio_win32.py83
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/base.py85
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/callbacks.py29
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/inputhook.py107
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/posix.py306
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/posix_utils.py82
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/select.py216
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/utils.py23
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/win32.py187
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/filters/__init__.py36
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/filters/base.py234
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/filters/cli.py395
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/filters/types.py55
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/filters/utils.py39
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/history.py120
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/input.py135
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/interface.py1190
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/__init__.py1
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/bindings/__init__.py0
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/bindings/basic.py407
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/bindings/completion.py161
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/bindings/emacs.py452
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/bindings/named_commands.py578
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/bindings/scroll.py185
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/bindings/utils.py25
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/bindings/vi.py1903
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/defaults.py119
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/digraphs.py1378
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/input_processor.py372
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/manager.py96
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/registry.py350
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/vi_state.py61
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/keys.py129
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/__init__.py51
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/containers.py1665
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/controls.py730
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/dimension.py92
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/lexers.py320
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/margins.py253
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/menus.py496
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/mouse_handlers.py29
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/processors.py605
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/prompt.py111
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/screen.py151
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/toolbars.py209
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/utils.py181
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/mouse_events.py48
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/output.py192
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/reactive.py56
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/renderer.py535
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/search_state.py36
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/selection.py47
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/shortcuts.py717
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/styles/__init__.py21
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/styles/base.py86
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/styles/defaults.py95
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/styles/from_dict.py151
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/styles/from_pygments.py77
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/styles/utils.py45
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/terminal/__init__.py0
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/terminal/conemu_output.py42
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/terminal/vt100_input.py520
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/terminal/vt100_output.py632
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/terminal/win32_input.py364
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/terminal/win32_output.py556
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/token.py47
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/utils.py240
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/validation.py64
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/win32_types.py155
103 files changed, 24459 insertions, 0 deletions
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/__init__.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/__init__.py
new file mode 100644
index 0000000000..6478ba4f9a
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/__init__.py
@@ -0,0 +1,22 @@
+"""
+prompt_toolkit
+==============
+
+Author: Jonathan Slenders
+
+Description: prompt_toolkit is a Library for building powerful interactive
+ command lines in Python. It can be a replacement for GNU
+ readline, but it can be much more than that.
+
+See the examples directory to learn about the usage.
+
+Probably, to get started, you meight also want to have a look at
+`prompt_toolkit.shortcuts.prompt`.
+"""
+from .interface import CommandLineInterface
+from .application import AbortAction, Application
+from .shortcuts import prompt, prompt_async
+
+
+# Don't forget to update in `docs/conf.py`!
+__version__ = '1.0.18'
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/application.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/application.py
new file mode 100644
index 0000000000..272d8bbcbb
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/application.py
@@ -0,0 +1,192 @@
+from __future__ import unicode_literals
+
+from .buffer import Buffer, AcceptAction
+from .buffer_mapping import BufferMapping
+from .clipboard import Clipboard, InMemoryClipboard
+from .enums import DEFAULT_BUFFER, EditingMode
+from .filters import CLIFilter, to_cli_filter
+from .key_binding.bindings.basic import load_basic_bindings
+from .key_binding.bindings.emacs import load_emacs_bindings
+from .key_binding.bindings.vi import load_vi_bindings
+from .key_binding.registry import BaseRegistry
+from .key_binding.defaults import load_key_bindings
+from .layout import Window
+from .layout.containers import Container
+from .layout.controls import BufferControl
+from .styles import DEFAULT_STYLE, Style
+import six
+
+__all__ = (
+ 'AbortAction',
+ 'Application',
+)
+
+
+class AbortAction(object):
+ """
+ Actions to take on an Exit or Abort exception.
+ """
+ RETRY = 'retry'
+ RAISE_EXCEPTION = 'raise-exception'
+ RETURN_NONE = 'return-none'
+
+ _all = (RETRY, RAISE_EXCEPTION, RETURN_NONE)
+
+
+class Application(object):
+ """
+ Application class to be passed to a
+ :class:`~prompt_toolkit.interface.CommandLineInterface`.
+
+ This contains all customizable logic that is not I/O dependent.
+ (So, what is independent of event loops, input and output.)
+
+ This way, such an :class:`.Application` can run easily on several
+ :class:`~prompt_toolkit.interface.CommandLineInterface` instances, each
+ with a different I/O backends. that runs for instance over telnet, SSH or
+ any other I/O backend.
+
+ :param layout: A :class:`~prompt_toolkit.layout.containers.Container` instance.
+ :param buffer: A :class:`~prompt_toolkit.buffer.Buffer` instance for the default buffer.
+ :param initial_focussed_buffer: Name of the buffer that is focussed during start-up.
+ :param key_bindings_registry:
+ :class:`~prompt_toolkit.key_binding.registry.BaseRegistry` instance for
+ the key bindings.
+ :param clipboard: :class:`~prompt_toolkit.clipboard.base.Clipboard` to use.
+ :param on_abort: What to do when Control-C is pressed.
+ :param on_exit: What to do when Control-D is pressed.
+ :param use_alternate_screen: When True, run the application on the alternate screen buffer.
+ :param get_title: Callable that returns the current title to be displayed in the terminal.
+ :param erase_when_done: (bool) Clear the application output when it finishes.
+ :param reverse_vi_search_direction: Normally, in Vi mode, a '/' searches
+ forward and a '?' searches backward. In readline mode, this is usually
+ reversed.
+
+ Filters:
+
+ :param mouse_support: (:class:`~prompt_toolkit.filters.CLIFilter` or
+ boolean). When True, enable mouse support.
+ :param paste_mode: :class:`~prompt_toolkit.filters.CLIFilter` or boolean.
+ :param ignore_case: :class:`~prompt_toolkit.filters.CLIFilter` or boolean.
+ :param editing_mode: :class:`~prompt_toolkit.enums.EditingMode`.
+
+ Callbacks (all of these should accept a
+ :class:`~prompt_toolkit.interface.CommandLineInterface` object as input.)
+
+ :param on_input_timeout: Called when there is no input for x seconds.
+ (Fired when any eventloop.onInputTimeout is fired.)
+ :param on_start: Called when reading input starts.
+ :param on_stop: Called when reading input ends.
+ :param on_reset: Called during reset.
+ :param on_buffer_changed: Called when the content of a buffer has been changed.
+ :param on_initialize: Called after the
+ :class:`~prompt_toolkit.interface.CommandLineInterface` initializes.
+ :param on_render: Called right after rendering.
+ :param on_invalidate: Called when the UI has been invalidated.
+ """
+ def __init__(self, layout=None, buffer=None, buffers=None,
+ initial_focussed_buffer=DEFAULT_BUFFER,
+ style=None,
+ key_bindings_registry=None, clipboard=None,
+ on_abort=AbortAction.RAISE_EXCEPTION, on_exit=AbortAction.RAISE_EXCEPTION,
+ use_alternate_screen=False, mouse_support=False,
+ get_title=None,
+
+ paste_mode=False, ignore_case=False, editing_mode=EditingMode.EMACS,
+ erase_when_done=False,
+ reverse_vi_search_direction=False,
+
+ on_input_timeout=None, on_start=None, on_stop=None,
+ on_reset=None, on_initialize=None, on_buffer_changed=None,
+ on_render=None, on_invalidate=None):
+
+ paste_mode = to_cli_filter(paste_mode)
+ ignore_case = to_cli_filter(ignore_case)
+ mouse_support = to_cli_filter(mouse_support)
+ reverse_vi_search_direction = to_cli_filter(reverse_vi_search_direction)
+
+ assert layout is None or isinstance(layout, Container)
+ assert buffer is None or isinstance(buffer, Buffer)
+ assert buffers is None or isinstance(buffers, (dict, BufferMapping))
+ assert key_bindings_registry is None or isinstance(key_bindings_registry, BaseRegistry)
+ assert clipboard is None or isinstance(clipboard, Clipboard)
+ assert on_abort in AbortAction._all
+ assert on_exit in AbortAction._all
+ assert isinstance(use_alternate_screen, bool)
+ assert get_title is None or callable(get_title)
+ assert isinstance(paste_mode, CLIFilter)
+ assert isinstance(ignore_case, CLIFilter)
+ assert isinstance(editing_mode, six.string_types)
+ assert on_input_timeout is None or callable(on_input_timeout)
+ assert style is None or isinstance(style, Style)
+ assert isinstance(erase_when_done, bool)
+
+ assert on_start is None or callable(on_start)
+ assert on_stop is None or callable(on_stop)
+ assert on_reset is None or callable(on_reset)
+ assert on_buffer_changed is None or callable(on_buffer_changed)
+ assert on_initialize is None or callable(on_initialize)
+ assert on_render is None or callable(on_render)
+ assert on_invalidate is None or callable(on_invalidate)
+
+ self.layout = layout or Window(BufferControl())
+
+ # Make sure that the 'buffers' dictionary is a BufferMapping.
+ # NOTE: If no buffer is given, we create a default Buffer, with IGNORE as
+ # default accept_action. This is what makes sense for most users
+ # creating full screen applications. Doing nothing is the obvious
+ # default. Those creating a REPL would use the shortcuts module that
+ # passes in RETURN_DOCUMENT.
+ self.buffer = buffer or Buffer(accept_action=AcceptAction.IGNORE)
+ if not buffers or not isinstance(buffers, BufferMapping):
+ self.buffers = BufferMapping(buffers, initial=initial_focussed_buffer)
+ else:
+ self.buffers = buffers
+
+ if buffer:
+ self.buffers[DEFAULT_BUFFER] = buffer
+
+ self.initial_focussed_buffer = initial_focussed_buffer
+
+ self.style = style or DEFAULT_STYLE
+
+ if key_bindings_registry is None:
+ key_bindings_registry = load_key_bindings()
+
+ if get_title is None:
+ get_title = lambda: None
+
+ self.key_bindings_registry = key_bindings_registry
+ self.clipboard = clipboard or InMemoryClipboard()
+ self.on_abort = on_abort
+ self.on_exit = on_exit
+ self.use_alternate_screen = use_alternate_screen
+ self.mouse_support = mouse_support
+ self.get_title = get_title
+
+ self.paste_mode = paste_mode
+ self.ignore_case = ignore_case
+ self.editing_mode = editing_mode
+ self.erase_when_done = erase_when_done
+ self.reverse_vi_search_direction = reverse_vi_search_direction
+
+ def dummy_handler(cli):
+ " Dummy event handler. "
+
+ self.on_input_timeout = on_input_timeout or dummy_handler
+ self.on_start = on_start or dummy_handler
+ self.on_stop = on_stop or dummy_handler
+ self.on_reset = on_reset or dummy_handler
+ self.on_initialize = on_initialize or dummy_handler
+ self.on_buffer_changed = on_buffer_changed or dummy_handler
+ self.on_render = on_render or dummy_handler
+ self.on_invalidate = on_invalidate or dummy_handler
+
+ # List of 'extra' functions to execute before a CommandLineInterface.run.
+ # Note: It's important to keep this here, and not in the
+ # CommandLineInterface itself. shortcuts.run_application creates
+ # a new Application instance everytime. (Which is correct, it
+ # could be that we want to detach from one IO backend and attach
+ # the UI on a different backend.) But important is to keep as
+ # much state as possible between runs.
+ self.pre_run_callables = []
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/auto_suggest.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/auto_suggest.py
new file mode 100644
index 0000000000..1d5130521f
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/auto_suggest.py
@@ -0,0 +1,88 @@
+"""
+`Fish-style <http://fishshell.com/>`_ like auto-suggestion.
+
+While a user types input in a certain buffer, suggestions are generated
+(asynchronously.) Usually, they are displayed after the input. When the cursor
+presses the right arrow and the cursor is at the end of the input, the
+suggestion will be inserted.
+"""
+from __future__ import unicode_literals
+from abc import ABCMeta, abstractmethod
+from six import with_metaclass
+
+from .filters import to_cli_filter
+
+__all__ = (
+ 'Suggestion',
+ 'AutoSuggest',
+ 'AutoSuggestFromHistory',
+ 'ConditionalAutoSuggest',
+)
+
+
+class Suggestion(object):
+ """
+ Suggestion returned by an auto-suggest algorithm.
+
+ :param text: The suggestion text.
+ """
+ def __init__(self, text):
+ self.text = text
+
+ def __repr__(self):
+ return 'Suggestion(%s)' % self.text
+
+
+class AutoSuggest(with_metaclass(ABCMeta, object)):
+ """
+ Base class for auto suggestion implementations.
+ """
+ @abstractmethod
+ def get_suggestion(self, cli, buffer, document):
+ """
+ Return `None` or a :class:`.Suggestion` instance.
+
+ We receive both ``buffer`` and ``document``. The reason is that auto
+ suggestions are retrieved asynchronously. (Like completions.) The
+ buffer text could be changed in the meantime, but ``document`` contains
+ the buffer document like it was at the start of the auto suggestion
+ call. So, from here, don't access ``buffer.text``, but use
+ ``document.text`` instead.
+
+ :param buffer: The :class:`~prompt_toolkit.buffer.Buffer` instance.
+ :param document: The :class:`~prompt_toolkit.document.Document` instance.
+ """
+
+
+class AutoSuggestFromHistory(AutoSuggest):
+ """
+ Give suggestions based on the lines in the history.
+ """
+ def get_suggestion(self, cli, buffer, document):
+ history = buffer.history
+
+ # Consider only the last line for the suggestion.
+ text = document.text.rsplit('\n', 1)[-1]
+
+ # Only create a suggestion when this is not an empty line.
+ if text.strip():
+ # Find first matching line in history.
+ for string in reversed(list(history)):
+ for line in reversed(string.splitlines()):
+ if line.startswith(text):
+ return Suggestion(line[len(text):])
+
+
+class ConditionalAutoSuggest(AutoSuggest):
+ """
+ Auto suggest that can be turned on and of according to a certain condition.
+ """
+ def __init__(self, auto_suggest, filter):
+ assert isinstance(auto_suggest, AutoSuggest)
+
+ self.auto_suggest = auto_suggest
+ self.filter = to_cli_filter(filter)
+
+ def get_suggestion(self, cli, buffer, document):
+ if self.filter(cli):
+ return self.auto_suggest.get_suggestion(cli, buffer, document)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/buffer.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/buffer.py
new file mode 100644
index 0000000000..f5df289827
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/buffer.py
@@ -0,0 +1,1415 @@
+"""
+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 six.moves import range
+
+import os
+import re
+import shlex
+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):
+ # Set return value.
+ 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)
+
+
+class ValidationState(object):
+ " The validation state of a buffer. This is set after the validation. "
+ VALID = 'VALID'
+ INVALID = 'INVALID'
+ 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]
+
+
+_QUOTED_WORDS_RE = re.compile(r"""(\s+|".*?"|'.*?')""")
+
+
+class YankNthArgState(object):
+ """
+ For yank-last-arg/yank-nth-arg: Keep track of where we are in the history.
+ """
+ def __init__(self, history_position=0, n=-1, previous_inserted_word=''):
+ self.history_position = history_position
+ self.previous_inserted_word = previous_inserted_word
+ self.n = n
+
+ def __repr__(self):
+ return '%s(history_position=%r, n=%r, previous_inserted_word=%r)' % (
+ self.__class__.__name__, self.history_position, self.n,
+ 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:
+
+ :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)
+ 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
+
+ # 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
+ 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.validation_state = ValidationState.UNKNOWN
+
+ # 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.)
+ self.multiple_cursor_positions = []
+
+ # 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 Emacs yank-nth-arg completion.
+ self.yank_nth_arg_state = None # for yank-nth-arg.
+
+ # Remember the document that we had *right before* the last paste
+ # 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. """
+ 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
+ # length and one is a prefix of the other, Python still scans
+ # character by character to see whether the strings are different.
+ # (Some benchmarking showed significant differences for big
+ # documents. >100,000 of lines.)
+ return True
+ elif value != original_value:
+ 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
+ self.validation_state = ValidationState.UNKNOWN
+ self.complete_state = None
+ self.yank_nth_arg_state = None
+ self.document_before_paste = 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
+ self.validation_state = ValidationState.UNKNOWN
+ 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
+ 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)
+
+ def transform_current_line(self, transform_callback):
+ """
+ Apply the given transformation function to the current line.
+
+ :param transform_callback: callable that takes a string and return a new string.
+ """
+ document = self.document
+ a = document.cursor_position + document.get_start_of_line_position()
+ b = document.cursor_position + document.get_end_of_line_position()
+ self.text = (
+ document.text[:a] +
+ 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. """
+ 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. """
+ 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:
+ self.cursor_up(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:
+ self.cursor_down(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):
+ """
+ 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):
+ """
+ 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 ''
+
+ 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.
+ self.text = (self.document.text_before_cursor + separator +
+ 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.
+ 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:
+ 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)
+
+ def yank_nth_arg(self, n=None, _yank_last_arg=False):
+ """
+ Pick nth word from previous history entry (depending on current
+ `yank_nth_arg_state`) and insert it at current position. Rotate through
+ history if called repeatedly. If no `n` has been given, take the first
+ argument. (The second word.)
+
+ :param n: (None or int), The index of the word from the previous line
+ to take.
+ """
+ assert n is None or isinstance(n, int)
+
+ if not len(self.history):
+ return
+
+ # Make sure we have a `YankNthArgState`.
+ if self.yank_nth_arg_state is None:
+ state = YankNthArgState(n=-1 if _yank_last_arg else 1)
+ else:
+ state = self.yank_nth_arg_state
+
+ if n is not None:
+ state.n = n
+
+ # Get new history position.
+ new_pos = state.history_position - 1
+ if -new_pos > len(self.history):
+ new_pos = -1
+
+ # Take argument from line.
+ line = self.history[new_pos]
+
+ words = [w.strip() for w in _QUOTED_WORDS_RE.split(line)]
+ words = [w for w in words if w]
+ try:
+ word = words[state.n]
+ except IndexError:
+ word = ''
+
+ # Insert new argument.
+ if state.previous_inserted_word:
+ self.delete_before_cursor(len(state.previous_inserted_word))
+ self.insert_text(word)
+
+ # Save state again for next completion. (Note that the 'insert'
+ # operation from above clears `self.yank_nth_arg_state`.)
+ state.previous_inserted_word = word
+ state.history_position = new_pos
+ self.yank_nth_arg_state = state
+
+ def yank_last_arg(self, n=None):
+ """
+ Like `yank_nth_arg`, but if no argument has been given, yank the last
+ word by default.
+ """
+ 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 paste_clipboard_data(self, data, paste_mode=PasteMode.EMACS, count=1):
+ """
+ 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)
+
+ # Remember original document. This assignment should come at the end,
+ # 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.
+ """
+ # Original text & cursor position.
+ otext = self.text
+ ocpos = self.cursor_position
+
+ # 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')]
+
+ self.text = otext[:ocpos] + data + otext[ocpos + len(overwritten_text):]
+ 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.
+ """
+ # 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))
+
+ self.validation_state = ValidationState.INVALID
+ 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 # 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
+ 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
+
+ # Keep selection, when `working_index` was not changed.
+ if working_index == self.working_index:
+ selection = self.selection_state
+ else:
+ selection = None
+
+ return Document(self._working_lines[working_index],
+ cursor_position, selection=selection)
+
+ def get_search_position(self, search_state, include_current_position=True, count=1):
+ """
+ Get the cursor position for this search.
+ (This operation won't change the `working_index`. It's won't go through
+ the history. Vi text objects can't span multiple items.)
+ """
+ search_result = self._search(
+ search_state, include_current_position=include_current_position, count=count)
+
+ if search_result is None:
+ return self.cursor_position
+ else:
+ 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`.
+ """
+ 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 the 'VISUAL' or 'EDITOR' environment variable has been set, use that.
+ # Otherwise, fall back to the first available editor that we can find.
+ visual = os.environ.get('VISUAL')
+ 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:
+ # 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)
+
+
+def reshape_text(buffer, from_row, to_row):
+ """
+ Reformat text, taking the width into account.
+ `to_row` is included.
+ (Vi 'gq' operator.)
+ """
+ lines = buffer.text.splitlines(True)
+ lines_before = lines[:from_row]
+ lines_after = lines[to_row + 1:]
+ lines_to_reformat = lines[from_row:to_row + 1]
+
+ if lines_to_reformat:
+ # Take indentation from the first line.
+ length = re.search(r'^\s*', lines_to_reformat[0]).end()
+ indent = lines_to_reformat[0][:length].replace('\n', '')
+
+ # Now, take all the 'words' from the lines to be reshaped.
+ words = ''.join(lines_to_reformat).split()
+
+ # And reshape.
+ width = (buffer.text_width or 80) - len(indent)
+ reshaped_text = [indent]
+ current_width = 0
+ for w in words:
+ if current_width:
+ if len(w) + current_width + 1 > width:
+ reshaped_text.append('\n')
+ reshaped_text.append(indent)
+ current_width = 0
+ else:
+ reshaped_text.append(' ')
+ current_width += 1
+
+ reshaped_text.append(w)
+ current_width += len(w)
+
+ if reshaped_text[-1] != '\n':
+ reshaped_text.append('\n')
+
+ # Apply result.
+ buffer.document = Document(
+ text=''.join(lines_before + reshaped_text + lines_after),
+ cursor_position=len(''.join(lines_before + reshaped_text)))
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/buffer_mapping.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/buffer_mapping.py
new file mode 100644
index 0000000000..34f443bd47
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/buffer_mapping.py
@@ -0,0 +1,92 @@
+"""
+The BufferMapping contains all the buffers for a command line interface, and it
+keeps track of which buffer gets the focus.
+"""
+from __future__ import unicode_literals
+from .enums import DEFAULT_BUFFER, SEARCH_BUFFER, SYSTEM_BUFFER, DUMMY_BUFFER
+from .buffer import Buffer, AcceptAction
+from .history import InMemoryHistory
+
+import six
+
+__all__ = (
+ 'BufferMapping',
+)
+
+
+class BufferMapping(dict):
+ """
+ Dictionary that maps the name of the buffers to the
+ :class:`~prompt_toolkit.buffer.Buffer` instances.
+
+ This mapping also keeps track of which buffer currently has the focus.
+ (Some methods receive a 'cli' parameter. This is useful for applications
+ where this `BufferMapping` is shared between several applications.)
+ """
+ def __init__(self, buffers=None, initial=DEFAULT_BUFFER):
+ assert buffers is None or isinstance(buffers, dict)
+
+ # Start with an empty dict.
+ super(BufferMapping, self).__init__()
+
+ # Add default buffers.
+ self.update({
+ # For the 'search' and 'system' buffers, 'returnable' is False, in
+ # order to block normal Enter/ControlC behaviour.
+ DEFAULT_BUFFER: Buffer(accept_action=AcceptAction.RETURN_DOCUMENT),
+ SEARCH_BUFFER: Buffer(history=InMemoryHistory(), accept_action=AcceptAction.IGNORE),
+ SYSTEM_BUFFER: Buffer(history=InMemoryHistory(), accept_action=AcceptAction.IGNORE),
+ DUMMY_BUFFER: Buffer(read_only=True),
+ })
+
+ # Add received buffers.
+ if buffers is not None:
+ self.update(buffers)
+
+ # Focus stack.
+ self.focus_stack = [initial or DEFAULT_BUFFER]
+
+ def current(self, cli):
+ """
+ The active :class:`.Buffer`.
+ """
+ return self[self.focus_stack[-1]]
+
+ def current_name(self, cli):
+ """
+ The name of the active :class:`.Buffer`.
+ """
+ return self.focus_stack[-1]
+
+ def previous(self, cli):
+ """
+ Return the previously focussed :class:`.Buffer` or `None`.
+ """
+ if len(self.focus_stack) > 1:
+ try:
+ return self[self.focus_stack[-2]]
+ except KeyError:
+ pass
+
+ def focus(self, cli, buffer_name):
+ """
+ Focus the buffer with the given name.
+ """
+ assert isinstance(buffer_name, six.text_type)
+ self.focus_stack = [buffer_name]
+
+ def push_focus(self, cli, buffer_name):
+ """
+ Push buffer on the focus stack.
+ """
+ assert isinstance(buffer_name, six.text_type)
+ self.focus_stack.append(buffer_name)
+
+ def pop_focus(self, cli):
+ """
+ Pop buffer from the focus stack.
+ """
+ if len(self.focus_stack) > 1:
+ self.focus_stack.pop()
+ else:
+ raise IndexError('Cannot pop last item from the focus stack.')
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/cache.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/cache.py
new file mode 100644
index 0000000000..55c7369c9c
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/cache.py
@@ -0,0 +1,111 @@
+from __future__ import unicode_literals
+from collections import deque
+from functools import wraps
+
+__all__ = (
+ 'SimpleCache',
+ 'FastDictCache',
+ 'memoized',
+)
+
+
+class SimpleCache(object):
+ """
+ Very simple cache that discards the oldest item when the cache size is
+ exceeded.
+
+ :param maxsize: Maximum size of the cache. (Don't make it too big.)
+ """
+ def __init__(self, maxsize=8):
+ assert isinstance(maxsize, int) and maxsize > 0
+
+ self._data = {}
+ self._keys = deque()
+ self.maxsize = maxsize
+
+ def get(self, key, getter_func):
+ """
+ Get object from the cache.
+ If not found, call `getter_func` to resolve it, and put that on the top
+ of the cache instead.
+ """
+ # Look in cache first.
+ try:
+ return self._data[key]
+ except KeyError:
+ # Not found? Get it.
+ value = getter_func()
+ self._data[key] = value
+ self._keys.append(key)
+
+ # Remove the oldest key when the size is exceeded.
+ if len(self._data) > self.maxsize:
+ key_to_remove = self._keys.popleft()
+ if key_to_remove in self._data:
+ del self._data[key_to_remove]
+
+ return value
+
+ def clear(self):
+ " Clear cache. "
+ self._data = {}
+ self._keys = deque()
+
+
+class FastDictCache(dict):
+ """
+ Fast, lightweight cache which keeps at most `size` items.
+ It will discard the oldest items in the cache first.
+
+ The cache is a dictionary, which doesn't keep track of access counts.
+ It is perfect to cache little immutable objects which are not expensive to
+ create, but where a dictionary lookup is still much faster than an object
+ instantiation.
+
+ :param get_value: Callable that's called in case of a missing key.
+ """
+ # NOTE: This cache is used to cache `prompt_toolkit.layout.screen.Char` and
+ # `prompt_toolkit.Document`. Make sure to keep this really lightweight.
+ # Accessing the cache should stay faster than instantiating new
+ # objects.
+ # (Dictionary lookups are really fast.)
+ # SimpleCache is still required for cases where the cache key is not
+ # the same as the arguments given to the function that creates the
+ # value.)
+ def __init__(self, get_value=None, size=1000000):
+ assert callable(get_value)
+ assert isinstance(size, int) and size > 0
+
+ self._keys = deque()
+ self.get_value = get_value
+ self.size = size
+
+ def __missing__(self, key):
+ # Remove the oldest key when the size is exceeded.
+ if len(self) > self.size:
+ key_to_remove = self._keys.popleft()
+ if key_to_remove in self:
+ del self[key_to_remove]
+
+ result = self.get_value(*key)
+ self[key] = result
+ self._keys.append(key)
+ return result
+
+
+def memoized(maxsize=1024):
+ """
+ Momoization decorator for immutable classes and pure functions.
+ """
+ cache = SimpleCache(maxsize=maxsize)
+
+ def decorator(obj):
+ @wraps(obj)
+ def new_callable(*a, **kw):
+ def create_new():
+ return obj(*a, **kw)
+
+ key = (a, tuple(kw.items()))
+ return cache.get(key, create_new)
+ return new_callable
+ return decorator
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/clipboard/__init__.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/clipboard/__init__.py
new file mode 100644
index 0000000000..56202ddd3b
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/clipboard/__init__.py
@@ -0,0 +1,8 @@
+from .base import Clipboard, ClipboardData
+from .in_memory import InMemoryClipboard
+
+
+# We are not importing `PyperclipClipboard` here, because it would require the
+# `pyperclip` module to be present.
+
+#from .pyperclip import PyperclipClipboard
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/clipboard/base.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/clipboard/base.py
new file mode 100644
index 0000000000..803c0b0e7d
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/clipboard/base.py
@@ -0,0 +1,62 @@
+"""
+Clipboard for command line interface.
+"""
+from __future__ import unicode_literals
+from abc import ABCMeta, abstractmethod
+from six import with_metaclass
+import six
+
+from prompt_toolkit.selection import SelectionType
+
+__all__ = (
+ 'Clipboard',
+ 'ClipboardData',
+)
+
+
+class ClipboardData(object):
+ """
+ Text on the clipboard.
+
+ :param text: string
+ :param type: :class:`~prompt_toolkit.selection.SelectionType`
+ """
+ def __init__(self, text='', type=SelectionType.CHARACTERS):
+ assert isinstance(text, six.string_types)
+ assert type in (SelectionType.CHARACTERS, SelectionType.LINES, SelectionType.BLOCK)
+
+ self.text = text
+ self.type = type
+
+
+class Clipboard(with_metaclass(ABCMeta, object)):
+ """
+ Abstract baseclass for clipboards.
+ (An implementation can be in memory, it can share the X11 or Windows
+ keyboard, or can be persistent.)
+ """
+ @abstractmethod
+ def set_data(self, data):
+ """
+ Set data to the clipboard.
+
+ :param data: :class:`~.ClipboardData` instance.
+ """
+
+ def set_text(self, text): # Not abstract.
+ """
+ Shortcut for setting plain text on clipboard.
+ """
+ assert isinstance(text, six.string_types)
+ self.set_data(ClipboardData(text))
+
+ def rotate(self):
+ """
+ For Emacs mode, rotate the kill ring.
+ """
+
+ @abstractmethod
+ def get_data(self):
+ """
+ Return clipboard data.
+ """
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/clipboard/in_memory.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/clipboard/in_memory.py
new file mode 100644
index 0000000000..081666ab80
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/clipboard/in_memory.py
@@ -0,0 +1,42 @@
+from .base import Clipboard, ClipboardData
+
+from collections import deque
+
+__all__ = (
+ 'InMemoryClipboard',
+)
+
+
+class InMemoryClipboard(Clipboard):
+ """
+ Default clipboard implementation.
+ Just keep the data in memory.
+
+ This implements a kill-ring, for Emacs mode.
+ """
+ def __init__(self, data=None, max_size=60):
+ assert data is None or isinstance(data, ClipboardData)
+ assert max_size >= 1
+
+ self.max_size = max_size
+ self._ring = deque()
+ if data is not None:
+ self.set_data(data)
+
+ def set_data(self, data):
+ assert isinstance(data, ClipboardData)
+ self._ring.appendleft(data)
+
+ while len(self._ring) > self.max_size:
+ self._ring.pop()
+
+ def get_data(self):
+ if self._ring:
+ return self._ring[0]
+ else:
+ return ClipboardData()
+
+ def rotate(self):
+ if self._ring:
+ # Add the very first item at the end.
+ self._ring.append(self._ring.popleft())
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/clipboard/pyperclip.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/clipboard/pyperclip.py
new file mode 100644
index 0000000000..61ab3aac0a
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/clipboard/pyperclip.py
@@ -0,0 +1,39 @@
+from __future__ import absolute_import, unicode_literals
+import pyperclip
+
+from prompt_toolkit.selection import SelectionType
+from .base import Clipboard, ClipboardData
+
+__all__ = (
+ 'PyperclipClipboard',
+)
+
+
+class PyperclipClipboard(Clipboard):
+ """
+ Clipboard that synchronizes with the Windows/Mac/Linux system clipboard,
+ using the pyperclip module.
+ """
+ def __init__(self):
+ self._data = None
+
+ def set_data(self, data):
+ assert isinstance(data, ClipboardData)
+ self._data = data
+ pyperclip.copy(data.text)
+
+ def get_data(self):
+ text = pyperclip.paste()
+
+ # When the clipboard data is equal to what we copied last time, reuse
+ # the `ClipboardData` instance. That way we're sure to keep the same
+ # `SelectionType`.
+ if self._data and self._data.text == text:
+ return self._data
+
+ # Pyperclip returned something else. Create a new `ClipboardData`
+ # instance.
+ else:
+ return ClipboardData(
+ text=text,
+ type=SelectionType.LINES if '\n' in text else SelectionType.LINES)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/completion.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/completion.py
new file mode 100644
index 0000000000..339738ab97
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/completion.py
@@ -0,0 +1,170 @@
+"""
+"""
+from __future__ import unicode_literals
+from abc import ABCMeta, abstractmethod
+from six import with_metaclass
+
+__all__ = (
+ 'Completion',
+ 'Completer',
+ 'CompleteEvent',
+ 'get_common_complete_suffix',
+)
+
+
+class Completion(object):
+ """
+ :param text: The new string that will be inserted into the document.
+ :param start_position: Position relative to the cursor_position where the
+ new text will start. The text will be inserted between the
+ start_position and the original cursor position.
+ :param display: (optional string) If the completion has to be displayed
+ differently in the completion menu.
+ :param display_meta: (Optional string) Meta information about the
+ completion, e.g. the path or source where it's coming from.
+ :param get_display_meta: Lazy `display_meta`. Retrieve meta information
+ only when meta is displayed.
+ """
+ def __init__(self, text, start_position=0, display=None, display_meta=None,
+ get_display_meta=None):
+ self.text = text
+ self.start_position = start_position
+ self._display_meta = display_meta
+ self._get_display_meta = get_display_meta
+
+ if display is None:
+ self.display = text
+ else:
+ self.display = display
+
+ assert self.start_position <= 0
+
+ def __repr__(self):
+ if self.display == self.text:
+ return '%s(text=%r, start_position=%r)' % (
+ self.__class__.__name__, self.text, self.start_position)
+ else:
+ return '%s(text=%r, start_position=%r, display=%r)' % (
+ self.__class__.__name__, self.text, self.start_position,
+ self.display)
+
+ def __eq__(self, other):
+ return (
+ self.text == other.text and
+ self.start_position == other.start_position and
+ self.display == other.display and
+ self.display_meta == other.display_meta)
+
+ def __hash__(self):
+ return hash((self.text, self.start_position, self.display, self.display_meta))
+
+ @property
+ def display_meta(self):
+ # Return meta-text. (This is lazy when using "get_display_meta".)
+ if self._display_meta is not None:
+ return self._display_meta
+
+ elif self._get_display_meta:
+ self._display_meta = self._get_display_meta()
+ return self._display_meta
+
+ else:
+ return ''
+
+ def new_completion_from_position(self, position):
+ """
+ (Only for internal use!)
+ Get a new completion by splitting this one. Used by
+ `CommandLineInterface` when it needs to have a list of new completions
+ after inserting the common prefix.
+ """
+ assert isinstance(position, int) and position - self.start_position >= 0
+
+ return Completion(
+ text=self.text[position - self.start_position:],
+ display=self.display,
+ display_meta=self._display_meta,
+ get_display_meta=self._get_display_meta)
+
+
+class CompleteEvent(object):
+ """
+ Event that called the completer.
+
+ :param text_inserted: When True, it means that completions are requested
+ because of a text insert. (`Buffer.complete_while_typing`.)
+ :param completion_requested: When True, it means that the user explicitely
+ pressed the `Tab` key in order to view the completions.
+
+ These two flags can be used for instance to implemented a completer that
+ shows some completions when ``Tab`` has been pressed, but not
+ automatically when the user presses a space. (Because of
+ `complete_while_typing`.)
+ """
+ def __init__(self, text_inserted=False, completion_requested=False):
+ assert not (text_inserted and completion_requested)
+
+ #: Automatic completion while typing.
+ self.text_inserted = text_inserted
+
+ #: Used explicitely requested completion by pressing 'tab'.
+ self.completion_requested = completion_requested
+
+ def __repr__(self):
+ return '%s(text_inserted=%r, completion_requested=%r)' % (
+ self.__class__.__name__, self.text_inserted, self.completion_requested)
+
+
+class Completer(with_metaclass(ABCMeta, object)):
+ """
+ Base class for completer implementations.
+ """
+ @abstractmethod
+ def get_completions(self, document, complete_event):
+ """
+ Yield :class:`.Completion` instances.
+
+ :param document: :class:`~prompt_toolkit.document.Document` instance.
+ :param complete_event: :class:`.CompleteEvent` instance.
+ """
+ while False:
+ yield
+
+
+def get_common_complete_suffix(document, completions):
+ """
+ Return the common prefix for all completions.
+ """
+ # Take only completions that don't change the text before the cursor.
+ def doesnt_change_before_cursor(completion):
+ end = completion.text[:-completion.start_position]
+ return document.text_before_cursor.endswith(end)
+
+ completions2 = [c for c in completions if doesnt_change_before_cursor(c)]
+
+ # When there is at least one completion that changes the text before the
+ # cursor, don't return any common part.
+ if len(completions2) != len(completions):
+ return ''
+
+ # Return the common prefix.
+ def get_suffix(completion):
+ return completion.text[-completion.start_position:]
+
+ return _commonprefix([get_suffix(c) for c in completions2])
+
+
+def _commonprefix(strings):
+ # Similar to os.path.commonprefix
+ if not strings:
+ return ''
+
+ else:
+ s1 = min(strings)
+ s2 = max(strings)
+
+ for i, c in enumerate(s1):
+ if c != s2[i]:
+ return s1[:i]
+
+ return s1
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/__init__.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/__init__.py
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/completers/__init__.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/completers/__init__.py
new file mode 100644
index 0000000000..43893b8c8c
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/completers/__init__.py
@@ -0,0 +1,5 @@
+from __future__ import unicode_literals
+
+from .filesystem import PathCompleter
+from .base import WordCompleter
+from .system import SystemCompleter
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/completers/base.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/completers/base.py
new file mode 100644
index 0000000000..65a69fede1
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/completers/base.py
@@ -0,0 +1,61 @@
+from __future__ import unicode_literals
+
+from six import string_types
+from prompt_toolkit.completion import Completer, Completion
+
+__all__ = (
+ 'WordCompleter',
+)
+
+
+class WordCompleter(Completer):
+ """
+ Simple autocompletion on a list of words.
+
+ :param words: List of words.
+ :param ignore_case: If True, case-insensitive completion.
+ :param meta_dict: Optional dict mapping words to their meta-information.
+ :param WORD: When True, use WORD characters.
+ :param sentence: When True, don't complete by comparing the word before the
+ cursor, but by comparing all the text before the cursor. In this case,
+ the list of words is just a list of strings, where each string can
+ contain spaces. (Can not be used together with the WORD option.)
+ :param match_middle: When True, match not only the start, but also in the
+ middle of the word.
+ """
+ def __init__(self, words, ignore_case=False, meta_dict=None, WORD=False,
+ sentence=False, match_middle=False):
+ assert not (WORD and sentence)
+
+ self.words = list(words)
+ self.ignore_case = ignore_case
+ self.meta_dict = meta_dict or {}
+ self.WORD = WORD
+ self.sentence = sentence
+ self.match_middle = match_middle
+ assert all(isinstance(w, string_types) for w in self.words)
+
+ def get_completions(self, document, complete_event):
+ # Get word/text before cursor.
+ if self.sentence:
+ word_before_cursor = document.text_before_cursor
+ else:
+ word_before_cursor = document.get_word_before_cursor(WORD=self.WORD)
+
+ if self.ignore_case:
+ word_before_cursor = word_before_cursor.lower()
+
+ def word_matches(word):
+ """ True when the word before the cursor matches. """
+ if self.ignore_case:
+ word = word.lower()
+
+ if self.match_middle:
+ return word_before_cursor in word
+ else:
+ return word.startswith(word_before_cursor)
+
+ for a in self.words:
+ if word_matches(a):
+ display_meta = self.meta_dict.get(a, '')
+ yield Completion(a, -len(word_before_cursor), display_meta=display_meta)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/completers/filesystem.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/completers/filesystem.py
new file mode 100644
index 0000000000..cbd74d8fea
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/completers/filesystem.py
@@ -0,0 +1,105 @@
+from __future__ import unicode_literals
+
+from prompt_toolkit.completion import Completer, Completion
+import os
+
+__all__ = (
+ 'PathCompleter',
+ 'ExecutableCompleter',
+)
+
+
+class PathCompleter(Completer):
+ """
+ Complete for Path variables.
+
+ :param get_paths: Callable which returns a list of directories to look into
+ when the user enters a relative path.
+ :param file_filter: Callable which takes a filename and returns whether
+ this file should show up in the completion. ``None``
+ when no filtering has to be done.
+ :param min_input_len: Don't do autocompletion when the input string is shorter.
+ """
+ def __init__(self, only_directories=False, get_paths=None, file_filter=None,
+ min_input_len=0, expanduser=False):
+ assert get_paths is None or callable(get_paths)
+ assert file_filter is None or callable(file_filter)
+ assert isinstance(min_input_len, int)
+ assert isinstance(expanduser, bool)
+
+ self.only_directories = only_directories
+ self.get_paths = get_paths or (lambda: ['.'])
+ self.file_filter = file_filter or (lambda _: True)
+ self.min_input_len = min_input_len
+ self.expanduser = expanduser
+
+ def get_completions(self, document, complete_event):
+ text = document.text_before_cursor
+
+ # Complete only when we have at least the minimal input length,
+ # otherwise, we can too many results and autocompletion will become too
+ # heavy.
+ if len(text) < self.min_input_len:
+ return
+
+ try:
+ # Do tilde expansion.
+ if self.expanduser:
+ text = os.path.expanduser(text)
+
+ # Directories where to look.
+ dirname = os.path.dirname(text)
+ if dirname:
+ directories = [os.path.dirname(os.path.join(p, text))
+ for p in self.get_paths()]
+ else:
+ directories = self.get_paths()
+
+ # Start of current file.
+ prefix = os.path.basename(text)
+
+ # Get all filenames.
+ filenames = []
+ for directory in directories:
+ # Look for matches in this directory.
+ if os.path.isdir(directory):
+ for filename in os.listdir(directory):
+ if filename.startswith(prefix):
+ filenames.append((directory, filename))
+
+ # Sort
+ filenames = sorted(filenames, key=lambda k: k[1])
+
+ # Yield them.
+ for directory, filename in filenames:
+ completion = filename[len(prefix):]
+ full_name = os.path.join(directory, filename)
+
+ if os.path.isdir(full_name):
+ # For directories, add a slash to the filename.
+ # (We don't add them to the `completion`. Users can type it
+ # to trigger the autocompletion themself.)
+ filename += '/'
+ elif self.only_directories:
+ continue
+
+ if not self.file_filter(full_name):
+ continue
+
+ yield Completion(completion, 0, display=filename)
+ except OSError:
+ pass
+
+
+class ExecutableCompleter(PathCompleter):
+ """
+ Complete only excutable files in the current path.
+ """
+ def __init__(self):
+ PathCompleter.__init__(
+ self,
+ only_directories=False,
+ min_input_len=1,
+ get_paths=lambda: os.environ.get('PATH', '').split(os.pathsep),
+ file_filter=lambda name: os.access(name, os.X_OK),
+ expanduser=True),
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/completers/system.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/completers/system.py
new file mode 100644
index 0000000000..76d6c1f9b4
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/completers/system.py
@@ -0,0 +1,56 @@
+from __future__ import unicode_literals
+
+from prompt_toolkit.contrib.regular_languages.completion import GrammarCompleter
+from prompt_toolkit.contrib.regular_languages.compiler import compile
+
+from .filesystem import PathCompleter, ExecutableCompleter
+
+__all__ = (
+ 'SystemCompleter',
+)
+
+
+class SystemCompleter(GrammarCompleter):
+ """
+ Completer for system commands.
+ """
+ def __init__(self):
+ # Compile grammar.
+ g = compile(
+ r"""
+ # First we have an executable.
+ (?P<executable>[^\s]+)
+
+ # Ignore literals in between.
+ (
+ \s+
+ ("[^"]*" | '[^']*' | [^'"]+ )
+ )*
+
+ \s+
+
+ # Filename as parameters.
+ (
+ (?P<filename>[^\s]+) |
+ "(?P<double_quoted_filename>[^\s]+)" |
+ '(?P<single_quoted_filename>[^\s]+)'
+ )
+ """,
+ escape_funcs={
+ 'double_quoted_filename': (lambda string: string.replace('"', '\\"')),
+ 'single_quoted_filename': (lambda string: string.replace("'", "\\'")),
+ },
+ unescape_funcs={
+ 'double_quoted_filename': (lambda string: string.replace('\\"', '"')), # XXX: not enterily correct.
+ 'single_quoted_filename': (lambda string: string.replace("\\'", "'")),
+ })
+
+ # Create GrammarCompleter
+ super(SystemCompleter, self).__init__(
+ g,
+ {
+ 'executable': ExecutableCompleter(),
+ 'filename': PathCompleter(only_directories=False, expanduser=True),
+ 'double_quoted_filename': PathCompleter(only_directories=False, expanduser=True),
+ 'single_quoted_filename': PathCompleter(only_directories=False, expanduser=True),
+ })
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/regular_languages/__init__.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/regular_languages/__init__.py
new file mode 100644
index 0000000000..314cb1f81d
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/regular_languages/__init__.py
@@ -0,0 +1,76 @@
+r"""
+Tool for expressing the grammar of an input as a regular language.
+==================================================================
+
+The grammar for the input of many simple command line interfaces can be
+expressed by a regular language. Examples are PDB (the Python debugger); a
+simple (bash-like) shell with "pwd", "cd", "cat" and "ls" commands; arguments
+that you can pass to an executable; etc. It is possible to use regular
+expressions for validation and parsing of such a grammar. (More about regular
+languages: http://en.wikipedia.org/wiki/Regular_language)
+
+Example
+-------
+
+Let's take the pwd/cd/cat/ls example. We want to have a shell that accepts
+these three commands. "cd" is followed by a quoted directory name and "cat" is
+followed by a quoted file name. (We allow quotes inside the filename when
+they're escaped with a backslash.) We could define the grammar using the
+following regular expression::
+
+ grammar = \s* (
+ pwd |
+ ls |
+ (cd \s+ " ([^"]|\.)+ ") |
+ (cat \s+ " ([^"]|\.)+ ")
+ ) \s*
+
+
+What can we do with this grammar?
+---------------------------------
+
+- Syntax highlighting: We could use this for instance to give file names
+ different colour.
+- Parse the result: .. We can extract the file names and commands by using a
+ regular expression with named groups.
+- Input validation: .. Don't accept anything that does not match this grammar.
+ When combined with a parser, we can also recursively do
+ filename validation (and accept only existing files.)
+- Autocompletion: .... Each part of the grammar can have its own autocompleter.
+ "cat" has to be completed using file names, while "cd"
+ has to be completed using directory names.
+
+How does it work?
+-----------------
+
+As a user of this library, you have to define the grammar of the input as a
+regular expression. The parts of this grammar where autocompletion, validation
+or any other processing is required need to be marked using a regex named
+group. Like ``(?P<varname>...)`` for instance.
+
+When the input is processed for validation (for instance), the regex will
+execute, the named group is captured, and the validator associated with this
+named group will test the captured string.
+
+There is one tricky bit:
+
+ Ofter we operate on incomplete input (this is by definition the case for
+ autocompletion) and we have to decide for the cursor position in which
+ possible state the grammar it could be and in which way variables could be
+ matched up to that point.
+
+To solve this problem, the compiler takes the original regular expression and
+translates it into a set of other regular expressions which each match prefixes
+of strings that would match the first expression. (We translate it into
+multiple expression, because we want to have each possible state the regex
+could be in -- in case there are several or-clauses with each different
+completers.)
+
+
+TODO: some examples of:
+ - How to create a highlighter from this grammar.
+ - How to create a validator from this grammar.
+ - How to create an autocompleter from this grammar.
+ - How to create a parser from this grammar.
+"""
+from .compiler import compile
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/regular_languages/compiler.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/regular_languages/compiler.py
new file mode 100644
index 0000000000..01476bf626
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/regular_languages/compiler.py
@@ -0,0 +1,408 @@
+r"""
+Compiler for a regular grammar.
+
+Example usage::
+
+ # Create and compile grammar.
+ p = compile('add \s+ (?P<var1>[^\s]+) \s+ (?P<var2>[^\s]+)')
+
+ # Match input string.
+ m = p.match('add 23 432')
+
+ # Get variables.
+ m.variables().get('var1') # Returns "23"
+ m.variables().get('var2') # Returns "432"
+
+
+Partial matches are possible::
+
+ # Create and compile grammar.
+ p = compile('''
+ # Operators with two arguments.
+ ((?P<operator1>[^\s]+) \s+ (?P<var1>[^\s]+) \s+ (?P<var2>[^\s]+)) |
+
+ # Operators with only one arguments.
+ ((?P<operator2>[^\s]+) \s+ (?P<var1>[^\s]+))
+ ''')
+
+ # Match partial input string.
+ m = p.match_prefix('add 23')
+
+ # Get variables. (Notice that both operator1 and operator2 contain the
+ # value "add".) This is because our input is incomplete, and we don't know
+ # yet in which rule of the regex we we'll end up. It could also be that
+ # `operator1` and `operator2` have a different autocompleter and we want to
+ # call all possible autocompleters that would result in valid input.)
+ m.variables().get('var1') # Returns "23"
+ m.variables().get('operator1') # Returns "add"
+ m.variables().get('operator2') # Returns "add"
+
+"""
+from __future__ import unicode_literals
+import re
+
+from six.moves import range
+from .regex_parser import Any, Sequence, Regex, Variable, Repeat, Lookahead
+from .regex_parser import parse_regex, tokenize_regex
+
+__all__ = (
+ 'compile',
+)
+
+
+# Name of the named group in the regex, matching trailing input.
+# (Trailing input is when the input contains characters after the end of the
+# expression has been matched.)
+_INVALID_TRAILING_INPUT = 'invalid_trailing'
+
+
+class _CompiledGrammar(object):
+ """
+ Compiles a grammar. This will take the parse tree of a regular expression
+ and compile the grammar.
+
+ :param root_node: :class~`.regex_parser.Node` instance.
+ :param escape_funcs: `dict` mapping variable names to escape callables.
+ :param unescape_funcs: `dict` mapping variable names to unescape callables.
+ """
+ def __init__(self, root_node, escape_funcs=None, unescape_funcs=None):
+ self.root_node = root_node
+ self.escape_funcs = escape_funcs or {}
+ self.unescape_funcs = unescape_funcs or {}
+
+ #: Dictionary that will map the redex names to Node instances.
+ self._group_names_to_nodes = {} # Maps regex group names to varnames.
+ counter = [0]
+
+ def create_group_func(node):
+ name = 'n%s' % counter[0]
+ self._group_names_to_nodes[name] = node.varname
+ counter[0] += 1
+ return name
+
+ # Compile regex strings.
+ self._re_pattern = '^%s$' % self._transform(root_node, create_group_func)
+ self._re_prefix_patterns = list(self._transform_prefix(root_node, create_group_func))
+
+ # Compile the regex itself.
+ flags = re.DOTALL # Note that we don't need re.MULTILINE! (^ and $
+ # still represent the start and end of input text.)
+ self._re = re.compile(self._re_pattern, flags)
+ self._re_prefix = [re.compile(t, flags) for t in self._re_prefix_patterns]
+
+ # We compile one more set of regexes, similar to `_re_prefix`, but accept any trailing
+ # input. This will ensure that we can still highlight the input correctly, even when the
+ # input contains some additional characters at the end that don't match the grammar.)
+ self._re_prefix_with_trailing_input = [
+ re.compile(r'(?:%s)(?P<%s>.*?)$' % (t.rstrip('$'), _INVALID_TRAILING_INPUT), flags)
+ for t in self._re_prefix_patterns]
+
+ def escape(self, varname, value):
+ """
+ Escape `value` to fit in the place of this variable into the grammar.
+ """
+ f = self.escape_funcs.get(varname)
+ return f(value) if f else value
+
+ def unescape(self, varname, value):
+ """
+ Unescape `value`.
+ """
+ f = self.unescape_funcs.get(varname)
+ return f(value) if f else value
+
+ @classmethod
+ def _transform(cls, root_node, create_group_func):
+ """
+ Turn a :class:`Node` object into a regular expression.
+
+ :param root_node: The :class:`Node` instance for which we generate the grammar.
+ :param create_group_func: A callable which takes a `Node` and returns the next
+ free name for this node.
+ """
+ def transform(node):
+ # Turn `Any` into an OR.
+ if isinstance(node, Any):
+ return '(?:%s)' % '|'.join(transform(c) for c in node.children)
+
+ # Concatenate a `Sequence`
+ elif isinstance(node, Sequence):
+ return ''.join(transform(c) for c in node.children)
+
+ # For Regex and Lookahead nodes, just insert them literally.
+ elif isinstance(node, Regex):
+ return node.regex
+
+ elif isinstance(node, Lookahead):
+ before = ('(?!' if node.negative else '(=')
+ return before + transform(node.childnode) + ')'
+
+ # A `Variable` wraps the children into a named group.
+ elif isinstance(node, Variable):
+ return '(?P<%s>%s)' % (create_group_func(node), transform(node.childnode))
+
+ # `Repeat`.
+ elif isinstance(node, Repeat):
+ return '(?:%s){%i,%s}%s' % (
+ transform(node.childnode), node.min_repeat,
+ ('' if node.max_repeat is None else str(node.max_repeat)),
+ ('' if node.greedy else '?')
+ )
+ else:
+ raise TypeError('Got %r' % (node, ))
+
+ return transform(root_node)
+
+ @classmethod
+ def _transform_prefix(cls, root_node, create_group_func):
+ """
+ Yield all the regular expressions matching a prefix of the grammar
+ defined by the `Node` instance.
+
+ This can yield multiple expressions, because in the case of on OR
+ operation in the grammar, we can have another outcome depending on
+ which clause would appear first. E.g. "(A|B)C" is not the same as
+ "(B|A)C" because the regex engine is lazy and takes the first match.
+ However, because we the current input is actually a prefix of the
+ grammar which meight not yet contain the data for "C", we need to know
+ both intermediate states, in order to call the appropriate
+ autocompletion for both cases.
+
+ :param root_node: The :class:`Node` instance for which we generate the grammar.
+ :param create_group_func: A callable which takes a `Node` and returns the next
+ free name for this node.
+ """
+ def transform(node):
+ # Generate regexes for all permutations of this OR. Each node
+ # should be in front once.
+ if isinstance(node, Any):
+ for c in node.children:
+ for r in transform(c):
+ yield '(?:%s)?' % r
+
+ # For a sequence. We can either have a match for the sequence
+ # of all the children, or for an exact match of the first X
+ # children, followed by a partial match of the next children.
+ elif isinstance(node, Sequence):
+ for i in range(len(node.children)):
+ a = [cls._transform(c, create_group_func) for c in node.children[:i]]
+ for c in transform(node.children[i]):
+ yield '(?:%s)' % (''.join(a) + c)
+
+ elif isinstance(node, Regex):
+ yield '(?:%s)?' % node.regex
+
+ elif isinstance(node, Lookahead):
+ if node.negative:
+ yield '(?!%s)' % cls._transform(node.childnode, create_group_func)
+ else:
+ # Not sure what the correct semantics are in this case.
+ # (Probably it's not worth implementing this.)
+ raise Exception('Positive lookahead not yet supported.')
+
+ elif isinstance(node, Variable):
+ # (Note that we should not append a '?' here. the 'transform'
+ # method will already recursively do that.)
+ for c in transform(node.childnode):
+ yield '(?P<%s>%s)' % (create_group_func(node), c)
+
+ elif isinstance(node, Repeat):
+ # If we have a repetition of 8 times. That would mean that the
+ # current input could have for instance 7 times a complete
+ # match, followed by a partial match.
+ prefix = cls._transform(node.childnode, create_group_func)
+
+ for c in transform(node.childnode):
+ if node.max_repeat:
+ repeat_sign = '{,%i}' % (node.max_repeat - 1)
+ else:
+ repeat_sign = '*'
+ yield '(?:%s)%s%s(?:%s)?' % (
+ prefix,
+ repeat_sign,
+ ('' if node.greedy else '?'),
+ c)
+
+ else:
+ raise TypeError('Got %r' % node)
+
+ for r in transform(root_node):
+ yield '^%s$' % r
+
+ def match(self, string):
+ """
+ Match the string with the grammar.
+ Returns a :class:`Match` instance or `None` when the input doesn't match the grammar.
+
+ :param string: The input string.
+ """
+ m = self._re.match(string)
+
+ if m:
+ return Match(string, [(self._re, m)], self._group_names_to_nodes, self.unescape_funcs)
+
+ def match_prefix(self, string):
+ """
+ Do a partial match of the string with the grammar. The returned
+ :class:`Match` instance can contain multiple representations of the
+ match. This will never return `None`. If it doesn't match at all, the "trailing input"
+ part will capture all of the input.
+
+ :param string: The input string.
+ """
+ # First try to match using `_re_prefix`. If nothing is found, use the patterns that
+ # also accept trailing characters.
+ for patterns in [self._re_prefix, self._re_prefix_with_trailing_input]:
+ matches = [(r, r.match(string)) for r in patterns]
+ matches = [(r, m) for r, m in matches if m]
+
+ if matches != []:
+ return Match(string, matches, self._group_names_to_nodes, self.unescape_funcs)
+
+
+class Match(object):
+ """
+ :param string: The input string.
+ :param re_matches: List of (compiled_re_pattern, re_match) tuples.
+ :param group_names_to_nodes: Dictionary mapping all the re group names to the matching Node instances.
+ """
+ def __init__(self, string, re_matches, group_names_to_nodes, unescape_funcs):
+ self.string = string
+ self._re_matches = re_matches
+ self._group_names_to_nodes = group_names_to_nodes
+ self._unescape_funcs = unescape_funcs
+
+ def _nodes_to_regs(self):
+ """
+ Return a list of (varname, reg) tuples.
+ """
+ def get_tuples():
+ for r, re_match in self._re_matches:
+ for group_name, group_index in r.groupindex.items():
+ if group_name != _INVALID_TRAILING_INPUT:
+ reg = re_match.regs[group_index]
+ node = self._group_names_to_nodes[group_name]
+ yield (node, reg)
+
+ return list(get_tuples())
+
+ def _nodes_to_values(self):
+ """
+ Returns list of list of (Node, string_value) tuples.
+ """
+ def is_none(slice):
+ return slice[0] == -1 and slice[1] == -1
+
+ def get(slice):
+ return self.string[slice[0]:slice[1]]
+
+ return [(varname, get(slice), slice) for varname, slice in self._nodes_to_regs() if not is_none(slice)]
+
+ def _unescape(self, varname, value):
+ unwrapper = self._unescape_funcs.get(varname)
+ return unwrapper(value) if unwrapper else value
+
+ def variables(self):
+ """
+ Returns :class:`Variables` instance.
+ """
+ return Variables([(k, self._unescape(k, v), sl) for k, v, sl in self._nodes_to_values()])
+
+ def trailing_input(self):
+ """
+ Get the `MatchVariable` instance, representing trailing input, if there is any.
+ "Trailing input" is input at the end that does not match the grammar anymore, but
+ when this is removed from the end of the input, the input would be a valid string.
+ """
+ slices = []
+
+ # Find all regex group for the name _INVALID_TRAILING_INPUT.
+ for r, re_match in self._re_matches:
+ for group_name, group_index in r.groupindex.items():
+ if group_name == _INVALID_TRAILING_INPUT:
+ slices.append(re_match.regs[group_index])
+
+ # Take the smallest part. (Smaller trailing text means that a larger input has
+ # been matched, so that is better.)
+ if slices:
+ slice = [max(i[0] for i in slices), max(i[1] for i in slices)]
+ value = self.string[slice[0]:slice[1]]
+ return MatchVariable('<trailing_input>', value, slice)
+
+ def end_nodes(self):
+ """
+ Yields `MatchVariable` instances for all the nodes having their end
+ position at the end of the input string.
+ """
+ for varname, reg in self._nodes_to_regs():
+ # If this part goes until the end of the input string.
+ if reg[1] == len(self.string):
+ value = self._unescape(varname, self.string[reg[0]: reg[1]])
+ yield MatchVariable(varname, value, (reg[0], reg[1]))
+
+
+class Variables(object):
+ def __init__(self, tuples):
+ #: List of (varname, value, slice) tuples.
+ self._tuples = tuples
+
+ def __repr__(self):
+ return '%s(%s)' % (
+ self.__class__.__name__, ', '.join('%s=%r' % (k, v) for k, v, _ in self._tuples))
+
+ def get(self, key, default=None):
+ items = self.getall(key)
+ return items[0] if items else default
+
+ def getall(self, key):
+ return [v for k, v, _ in self._tuples if k == key]
+
+ def __getitem__(self, key):
+ return self.get(key)
+
+ def __iter__(self):
+ """
+ Yield `MatchVariable` instances.
+ """
+ for varname, value, slice in self._tuples:
+ yield MatchVariable(varname, value, slice)
+
+
+class MatchVariable(object):
+ """
+ Represents a match of a variable in the grammar.
+
+ :param varname: (string) Name of the variable.
+ :param value: (string) Value of this variable.
+ :param slice: (start, stop) tuple, indicating the position of this variable
+ in the input string.
+ """
+ def __init__(self, varname, value, slice):
+ self.varname = varname
+ self.value = value
+ self.slice = slice
+
+ self.start = self.slice[0]
+ self.stop = self.slice[1]
+
+ def __repr__(self):
+ return '%s(%r, %r)' % (self.__class__.__name__, self.varname, self.value)
+
+
+def compile(expression, escape_funcs=None, unescape_funcs=None):
+ """
+ Compile grammar (given as regex string), returning a `CompiledGrammar`
+ instance.
+ """
+ return _compile_from_parse_tree(
+ parse_regex(tokenize_regex(expression)),
+ escape_funcs=escape_funcs,
+ unescape_funcs=unescape_funcs)
+
+
+def _compile_from_parse_tree(root_node, *a, **kw):
+ """
+ Compile grammar (given as parse tree), returning a `CompiledGrammar`
+ instance.
+ """
+ return _CompiledGrammar(root_node, *a, **kw)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/regular_languages/completion.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/regular_languages/completion.py
new file mode 100644
index 0000000000..bb49986a03
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/regular_languages/completion.py
@@ -0,0 +1,84 @@
+"""
+Completer for a regular grammar.
+"""
+from __future__ import unicode_literals
+
+from prompt_toolkit.completion import Completer, Completion
+from prompt_toolkit.document import Document
+
+from .compiler import _CompiledGrammar
+
+__all__ = (
+ 'GrammarCompleter',
+)
+
+
+class GrammarCompleter(Completer):
+ """
+ Completer which can be used for autocompletion according to variables in
+ the grammar. Each variable can have a different autocompleter.
+
+ :param compiled_grammar: `GrammarCompleter` instance.
+ :param completers: `dict` mapping variable names of the grammar to the
+ `Completer` instances to be used for each variable.
+ """
+ def __init__(self, compiled_grammar, completers):
+ assert isinstance(compiled_grammar, _CompiledGrammar)
+ assert isinstance(completers, dict)
+
+ self.compiled_grammar = compiled_grammar
+ self.completers = completers
+
+ def get_completions(self, document, complete_event):
+ m = self.compiled_grammar.match_prefix(document.text_before_cursor)
+
+ if m:
+ completions = self._remove_duplicates(
+ self._get_completions_for_match(m, complete_event))
+
+ for c in completions:
+ yield c
+
+ def _get_completions_for_match(self, match, complete_event):
+ """
+ Yield all the possible completions for this input string.
+ (The completer assumes that the cursor position was at the end of the
+ input string.)
+ """
+ for match_variable in match.end_nodes():
+ varname = match_variable.varname
+ start = match_variable.start
+
+ completer = self.completers.get(varname)
+
+ if completer:
+ text = match_variable.value
+
+ # Unwrap text.
+ unwrapped_text = self.compiled_grammar.unescape(varname, text)
+
+ # Create a document, for the completions API (text/cursor_position)
+ document = Document(unwrapped_text, len(unwrapped_text))
+
+ # Call completer
+ for completion in completer.get_completions(document, complete_event):
+ new_text = unwrapped_text[:len(text) + completion.start_position] + completion.text
+
+ # Wrap again.
+ yield Completion(
+ text=self.compiled_grammar.escape(varname, new_text),
+ start_position=start - len(match.string),
+ display=completion.display,
+ display_meta=completion.display_meta)
+
+ def _remove_duplicates(self, items):
+ """
+ Remove duplicates, while keeping the order.
+ (Sometimes we have duplicates, because the there several matches of the
+ same grammar, each yielding similar completions.)
+ """
+ result = []
+ for i in items:
+ if i not in result:
+ result.append(i)
+ return result
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/regular_languages/lexer.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/regular_languages/lexer.py
new file mode 100644
index 0000000000..c166d84fd1
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/regular_languages/lexer.py
@@ -0,0 +1,90 @@
+"""
+`GrammarLexer` is compatible with Pygments lexers and can be used to highlight
+the input using a regular grammar with token annotations.
+"""
+from __future__ import unicode_literals
+from prompt_toolkit.document import Document
+from prompt_toolkit.layout.lexers import Lexer
+from prompt_toolkit.layout.utils import split_lines
+from prompt_toolkit.token import Token
+
+from .compiler import _CompiledGrammar
+from six.moves import range
+
+__all__ = (
+ 'GrammarLexer',
+)
+
+
+class GrammarLexer(Lexer):
+ """
+ Lexer which can be used for highlighting of tokens according to variables in the grammar.
+
+ (It does not actual lexing of the string, but it exposes an API, compatible
+ with the Pygments lexer class.)
+
+ :param compiled_grammar: Grammar as returned by the `compile()` function.
+ :param lexers: Dictionary mapping variable names of the regular grammar to
+ the lexers that should be used for this part. (This can
+ call other lexers recursively.) If you wish a part of the
+ grammar to just get one token, use a
+ `prompt_toolkit.layout.lexers.SimpleLexer`.
+ """
+ def __init__(self, compiled_grammar, default_token=None, lexers=None):
+ assert isinstance(compiled_grammar, _CompiledGrammar)
+ assert default_token is None or isinstance(default_token, tuple)
+ assert lexers is None or all(isinstance(v, Lexer) for k, v in lexers.items())
+ assert lexers is None or isinstance(lexers, dict)
+
+ self.compiled_grammar = compiled_grammar
+ self.default_token = default_token or Token
+ self.lexers = lexers or {}
+
+ def _get_tokens(self, cli, text):
+ m = self.compiled_grammar.match_prefix(text)
+
+ if m:
+ characters = [[self.default_token, c] for c in text]
+
+ for v in m.variables():
+ # If we have a `Lexer` instance for this part of the input.
+ # Tokenize recursively and apply tokens.
+ lexer = self.lexers.get(v.varname)
+
+ if lexer:
+ document = Document(text[v.start:v.stop])
+ lexer_tokens_for_line = lexer.lex_document(cli, document)
+ lexer_tokens = []
+ for i in range(len(document.lines)):
+ lexer_tokens.extend(lexer_tokens_for_line(i))
+ lexer_tokens.append((Token, '\n'))
+ if lexer_tokens:
+ lexer_tokens.pop()
+
+ i = v.start
+ for t, s in lexer_tokens:
+ for c in s:
+ if characters[i][0] == self.default_token:
+ characters[i][0] = t
+ i += 1
+
+ # Highlight trailing input.
+ trailing_input = m.trailing_input()
+ if trailing_input:
+ for i in range(trailing_input.start, trailing_input.stop):
+ characters[i][0] = Token.TrailingInput
+
+ return characters
+ else:
+ return [(Token, text)]
+
+ def lex_document(self, cli, document):
+ lines = list(split_lines(self._get_tokens(cli, document.text)))
+
+ def get_line(lineno):
+ try:
+ return lines[lineno]
+ except IndexError:
+ return []
+
+ return get_line
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/regular_languages/regex_parser.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/regular_languages/regex_parser.py
new file mode 100644
index 0000000000..e5909b241e
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/regular_languages/regex_parser.py
@@ -0,0 +1,262 @@
+"""
+Parser for parsing a regular expression.
+Take a string representing a regular expression and return the root node of its
+parse tree.
+
+usage::
+
+ root_node = parse_regex('(hello|world)')
+
+Remarks:
+- The regex parser processes multiline, it ignores all whitespace and supports
+ multiple named groups with the same name and #-style comments.
+
+Limitations:
+- Lookahead is not supported.
+"""
+from __future__ import unicode_literals
+import re
+
+__all__ = (
+ 'Repeat',
+ 'Variable',
+ 'Regex',
+ 'Lookahead',
+
+ 'tokenize_regex',
+ 'parse_regex',
+)
+
+
+class Node(object):
+ """
+ Base class for all the grammar nodes.
+ (You don't initialize this one.)
+ """
+ def __add__(self, other_node):
+ return Sequence([self, other_node])
+
+ def __or__(self, other_node):
+ return Any([self, other_node])
+
+
+class Any(Node):
+ """
+ Union operation (OR operation) between several grammars. You don't
+ initialize this yourself, but it's a result of a "Grammar1 | Grammar2"
+ operation.
+ """
+ def __init__(self, children):
+ self.children = children
+
+ def __or__(self, other_node):
+ return Any(self.children + [other_node])
+
+ def __repr__(self):
+ return '%s(%r)' % (self.__class__.__name__, self.children)
+
+
+class Sequence(Node):
+ """
+ Concatenation operation of several grammars. You don't initialize this
+ yourself, but it's a result of a "Grammar1 + Grammar2" operation.
+ """
+ def __init__(self, children):
+ self.children = children
+
+ def __add__(self, other_node):
+ return Sequence(self.children + [other_node])
+
+ def __repr__(self):
+ return '%s(%r)' % (self.__class__.__name__, self.children)
+
+
+class Regex(Node):
+ """
+ Regular expression.
+ """
+ def __init__(self, regex):
+ re.compile(regex) # Validate
+
+ self.regex = regex
+
+ def __repr__(self):
+ return '%s(/%s/)' % (self.__class__.__name__, self.regex)
+
+
+class Lookahead(Node):
+ """
+ Lookahead expression.
+ """
+ def __init__(self, childnode, negative=False):
+ self.childnode = childnode
+ self.negative = negative
+
+ def __repr__(self):
+ return '%s(%r)' % (self.__class__.__name__, self.childnode)
+
+
+class Variable(Node):
+ """
+ Mark a variable in the regular grammar. This will be translated into a
+ named group. Each variable can have his own completer, validator, etc..
+
+ :param childnode: The grammar which is wrapped inside this variable.
+ :param varname: String.
+ """
+ def __init__(self, childnode, varname=None):
+ self.childnode = childnode
+ self.varname = varname
+
+ def __repr__(self):
+ return '%s(childnode=%r, varname=%r)' % (
+ self.__class__.__name__, self.childnode, self.varname)
+
+
+class Repeat(Node):
+ def __init__(self, childnode, min_repeat=0, max_repeat=None, greedy=True):
+ self.childnode = childnode
+ self.min_repeat = min_repeat
+ self.max_repeat = max_repeat
+ self.greedy = greedy
+
+ def __repr__(self):
+ return '%s(childnode=%r)' % (self.__class__.__name__, self.childnode)
+
+
+def tokenize_regex(input):
+ """
+ Takes a string, representing a regular expression as input, and tokenizes
+ it.
+
+ :param input: string, representing a regular expression.
+ :returns: List of tokens.
+ """
+ # Regular expression for tokenizing other regular expressions.
+ p = re.compile(r'''^(
+ \(\?P\<[a-zA-Z0-9_-]+\> | # Start of named group.
+ \(\?#[^)]*\) | # Comment
+ \(\?= | # Start of lookahead assertion
+ \(\?! | # Start of negative lookahead assertion
+ \(\?<= | # If preceded by.
+ \(\?< | # If not preceded by.
+ \(?: | # Start of group. (non capturing.)
+ \( | # Start of group.
+ \(?[iLmsux] | # Flags.
+ \(?P=[a-zA-Z]+\) | # Back reference to named group
+ \) | # End of group.
+ \{[^{}]*\} | # Repetition
+ \*\? | \+\? | \?\?\ | # Non greedy repetition.
+ \* | \+ | \? | # Repetition
+ \#.*\n | # Comment
+ \\. |
+
+ # Character group.
+ \[
+ ( [^\]\\] | \\.)*
+ \] |
+
+ [^(){}] |
+ .
+ )''', re.VERBOSE)
+
+ tokens = []
+
+ while input:
+ m = p.match(input)
+ if m:
+ token, input = input[:m.end()], input[m.end():]
+ if not token.isspace():
+ tokens.append(token)
+ else:
+ raise Exception('Could not tokenize input regex.')
+
+ return tokens
+
+
+def parse_regex(regex_tokens):
+ """
+ Takes a list of tokens from the tokenizer, and returns a parse tree.
+ """
+ # We add a closing brace because that represents the final pop of the stack.
+ tokens = [')'] + regex_tokens[::-1]
+
+ def wrap(lst):
+ """ Turn list into sequence when it contains several items. """
+ if len(lst) == 1:
+ return lst[0]
+ else:
+ return Sequence(lst)
+
+ def _parse():
+ or_list = []
+ result = []
+
+ def wrapped_result():
+ if or_list == []:
+ return wrap(result)
+ else:
+ or_list.append(result)
+ return Any([wrap(i) for i in or_list])
+
+ while tokens:
+ t = tokens.pop()
+
+ if t.startswith('(?P<'):
+ variable = Variable(_parse(), varname=t[4:-1])
+ result.append(variable)
+
+ elif t in ('*', '*?'):
+ greedy = (t == '*')
+ result[-1] = Repeat(result[-1], greedy=greedy)
+
+ elif t in ('+', '+?'):
+ greedy = (t == '+')
+ result[-1] = Repeat(result[-1], min_repeat=1, greedy=greedy)
+
+ elif t in ('?', '??'):
+ if result == []:
+ raise Exception('Nothing to repeat.' + repr(tokens))
+ else:
+ greedy = (t == '?')
+ result[-1] = Repeat(result[-1], min_repeat=0, max_repeat=1, greedy=greedy)
+
+ elif t == '|':
+ or_list.append(result)
+ result = []
+
+ elif t in ('(', '(?:'):
+ result.append(_parse())
+
+ elif t == '(?!':
+ result.append(Lookahead(_parse(), negative=True))
+
+ elif t == '(?=':
+ result.append(Lookahead(_parse(), negative=False))
+
+ elif t == ')':
+ return wrapped_result()
+
+ elif t.startswith('#'):
+ pass
+
+ elif t.startswith('{'):
+ # TODO: implement!
+ raise Exception('{}-style repitition not yet supported' % t)
+
+ elif t.startswith('(?'):
+ raise Exception('%r not supported' % t)
+
+ elif t.isspace():
+ pass
+ else:
+ result.append(Regex(t))
+
+ raise Exception("Expecting ')' token")
+
+ result = _parse()
+
+ if len(tokens) != 0:
+ raise Exception("Unmatched parantheses.")
+ else:
+ return result
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/regular_languages/validation.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/regular_languages/validation.py
new file mode 100644
index 0000000000..d5f8cfccc6
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/regular_languages/validation.py
@@ -0,0 +1,57 @@
+"""
+Validator for a regular langage.
+"""
+from __future__ import unicode_literals
+
+from prompt_toolkit.validation import Validator, ValidationError
+from prompt_toolkit.document import Document
+
+from .compiler import _CompiledGrammar
+
+__all__ = (
+ 'GrammarValidator',
+)
+
+
+class GrammarValidator(Validator):
+ """
+ Validator which can be used for validation according to variables in
+ the grammar. Each variable can have its own validator.
+
+ :param compiled_grammar: `GrammarCompleter` instance.
+ :param validators: `dict` mapping variable names of the grammar to the
+ `Validator` instances to be used for each variable.
+ """
+ def __init__(self, compiled_grammar, validators):
+ assert isinstance(compiled_grammar, _CompiledGrammar)
+ assert isinstance(validators, dict)
+
+ self.compiled_grammar = compiled_grammar
+ self.validators = validators
+
+ def validate(self, document):
+ # Parse input document.
+ # We use `match`, not `match_prefix`, because for validation, we want
+ # the actual, unambiguous interpretation of the input.
+ m = self.compiled_grammar.match(document.text)
+
+ if m:
+ for v in m.variables():
+ validator = self.validators.get(v.varname)
+
+ if validator:
+ # Unescape text.
+ unwrapped_text = self.compiled_grammar.unescape(v.varname, v.value)
+
+ # Create a document, for the completions API (text/cursor_position)
+ inner_document = Document(unwrapped_text, len(unwrapped_text))
+
+ try:
+ validator.validate(inner_document)
+ except ValidationError as e:
+ raise ValidationError(
+ cursor_position=v.start + e.cursor_position,
+ message=e.message)
+ else:
+ raise ValidationError(cursor_position=len(document.text),
+ message='Invalid command')
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/telnet/__init__.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/telnet/__init__.py
new file mode 100644
index 0000000000..7b7aeec820
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/telnet/__init__.py
@@ -0,0 +1,2 @@
+from .server import *
+from .application import *
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/telnet/application.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/telnet/application.py
new file mode 100644
index 0000000000..7fe6cc97b7
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/telnet/application.py
@@ -0,0 +1,32 @@
+"""
+Interface for Telnet applications.
+"""
+from __future__ import unicode_literals
+from abc import ABCMeta, abstractmethod
+from six import with_metaclass
+
+__all__ = (
+ 'TelnetApplication',
+)
+
+
+class TelnetApplication(with_metaclass(ABCMeta, object)):
+ """
+ The interface which has to be implemented for any telnet application.
+ An instance of this class has to be passed to `TelnetServer`.
+ """
+ @abstractmethod
+ def client_connected(self, telnet_connection):
+ """
+ Called when a new client was connected.
+
+ Probably you want to call `telnet_connection.set_cli` here to set a
+ the CommandLineInterface instance to be used.
+ Hint: Use the following shortcut: `prompt_toolkit.shortcuts.create_cli`
+ """
+
+ @abstractmethod
+ def client_leaving(self, telnet_connection):
+ """
+ Called when a client quits.
+ """
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/telnet/log.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/telnet/log.py
new file mode 100644
index 0000000000..10792ceed6
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/telnet/log.py
@@ -0,0 +1,11 @@
+"""
+Python logger for the telnet server.
+"""
+from __future__ import unicode_literals
+import logging
+
+logger = logging.getLogger(__package__)
+
+__all__ = (
+ 'logger',
+)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/telnet/protocol.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/telnet/protocol.py
new file mode 100644
index 0000000000..b1bb0ccbe8
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/telnet/protocol.py
@@ -0,0 +1,181 @@
+"""
+Parser for the Telnet protocol. (Not a complete implementation of the telnet
+specification, but sufficient for a command line interface.)
+
+Inspired by `Twisted.conch.telnet`.
+"""
+from __future__ import unicode_literals
+
+import struct
+from six import int2byte, binary_type, iterbytes
+
+from .log import logger
+
+__all__ = (
+ 'TelnetProtocolParser',
+)
+
+# Telnet constants.
+NOP = int2byte(0)
+SGA = int2byte(3)
+
+IAC = int2byte(255)
+DO = int2byte(253)
+DONT = int2byte(254)
+LINEMODE = int2byte(34)
+SB = int2byte(250)
+WILL = int2byte(251)
+WONT = int2byte(252)
+MODE = int2byte(1)
+SE = int2byte(240)
+ECHO = int2byte(1)
+NAWS = int2byte(31)
+LINEMODE = int2byte(34)
+SUPPRESS_GO_AHEAD = int2byte(3)
+
+DM = int2byte(242)
+BRK = int2byte(243)
+IP = int2byte(244)
+AO = int2byte(245)
+AYT = int2byte(246)
+EC = int2byte(247)
+EL = int2byte(248)
+GA = int2byte(249)
+
+
+class TelnetProtocolParser(object):
+ """
+ Parser for the Telnet protocol.
+ Usage::
+
+ def data_received(data):
+ print(data)
+
+ def size_received(rows, columns):
+ print(rows, columns)
+
+ p = TelnetProtocolParser(data_received, size_received)
+ p.feed(binary_data)
+ """
+ def __init__(self, data_received_callback, size_received_callback):
+ self.data_received_callback = data_received_callback
+ self.size_received_callback = size_received_callback
+
+ self._parser = self._parse_coroutine()
+ self._parser.send(None)
+
+ def received_data(self, data):
+ self.data_received_callback(data)
+
+ def do_received(self, data):
+ """ Received telnet DO command. """
+ logger.info('DO %r', data)
+
+ def dont_received(self, data):
+ """ Received telnet DONT command. """
+ logger.info('DONT %r', data)
+
+ def will_received(self, data):
+ """ Received telnet WILL command. """
+ logger.info('WILL %r', data)
+
+ def wont_received(self, data):
+ """ Received telnet WONT command. """
+ logger.info('WONT %r', data)
+
+ def command_received(self, command, data):
+ if command == DO:
+ self.do_received(data)
+
+ elif command == DONT:
+ self.dont_received(data)
+
+ elif command == WILL:
+ self.will_received(data)
+
+ elif command == WONT:
+ self.wont_received(data)
+
+ else:
+ logger.info('command received %r %r', command, data)
+
+ def naws(self, data):
+ """
+ Received NAWS. (Window dimensions.)
+ """
+ if len(data) == 4:
+ # NOTE: the first parameter of struct.unpack should be
+ # a 'str' object. Both on Py2/py3. This crashes on OSX
+ # otherwise.
+ columns, rows = struct.unpack(str('!HH'), data)
+ self.size_received_callback(rows, columns)
+ else:
+ logger.warning('Wrong number of NAWS bytes')
+
+ def negotiate(self, data):
+ """
+ Got negotiate data.
+ """
+ command, payload = data[0:1], data[1:]
+ assert isinstance(command, bytes)
+
+ if command == NAWS:
+ self.naws(payload)
+ else:
+ logger.info('Negotiate (%r got bytes)', len(data))
+
+ def _parse_coroutine(self):
+ """
+ Parser state machine.
+ Every 'yield' expression returns the next byte.
+ """
+ while True:
+ d = yield
+
+ if d == int2byte(0):
+ pass # NOP
+
+ # Go to state escaped.
+ elif d == IAC:
+ d2 = yield
+
+ if d2 == IAC:
+ self.received_data(d2)
+
+ # Handle simple commands.
+ elif d2 in (NOP, DM, BRK, IP, AO, AYT, EC, EL, GA):
+ self.command_received(d2, None)
+
+ # Handle IAC-[DO/DONT/WILL/WONT] commands.
+ elif d2 in (DO, DONT, WILL, WONT):
+ d3 = yield
+ self.command_received(d2, d3)
+
+ # Subnegotiation
+ elif d2 == SB:
+ # Consume everything until next IAC-SE
+ data = []
+
+ while True:
+ d3 = yield
+
+ if d3 == IAC:
+ d4 = yield
+ if d4 == SE:
+ break
+ else:
+ data.append(d4)
+ else:
+ data.append(d3)
+
+ self.negotiate(b''.join(data))
+ else:
+ self.received_data(d)
+
+ def feed(self, data):
+ """
+ Feed data to the parser.
+ """
+ assert isinstance(data, binary_type)
+ for b in iterbytes(data):
+ self._parser.send(int2byte(b))
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/telnet/server.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/telnet/server.py
new file mode 100644
index 0000000000..d75a9572eb
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/telnet/server.py
@@ -0,0 +1,407 @@
+"""
+Telnet server.
+
+Example usage::
+
+ class MyTelnetApplication(TelnetApplication):
+ def client_connected(self, telnet_connection):
+ # Set CLI with simple prompt.
+ telnet_connection.set_application(
+ telnet_connection.create_prompt_application(...))
+
+ def handle_command(self, telnet_connection, document):
+ # When the client enters a command, just reply.
+ telnet_connection.send('You said: %r\n\n' % document.text)
+
+ ...
+
+ a = MyTelnetApplication()
+ TelnetServer(application=a, host='127.0.0.1', port=23).run()
+"""
+from __future__ import unicode_literals
+
+import socket
+import select
+
+import threading
+import os
+import fcntl
+
+from six import int2byte, text_type, binary_type
+from codecs import getincrementaldecoder
+
+from prompt_toolkit.enums import DEFAULT_BUFFER
+from prompt_toolkit.eventloop.base import EventLoop
+from prompt_toolkit.interface import CommandLineInterface, Application
+from prompt_toolkit.layout.screen import Size
+from prompt_toolkit.shortcuts import create_prompt_application
+from prompt_toolkit.terminal.vt100_input import InputStream
+from prompt_toolkit.terminal.vt100_output import Vt100_Output
+
+from .log import logger
+from .protocol import IAC, DO, LINEMODE, SB, MODE, SE, WILL, ECHO, NAWS, SUPPRESS_GO_AHEAD
+from .protocol import TelnetProtocolParser
+from .application import TelnetApplication
+
+__all__ = (
+ 'TelnetServer',
+)
+
+
+def _initialize_telnet(connection):
+ logger.info('Initializing telnet connection')
+
+ # Iac Do Linemode
+ connection.send(IAC + DO + LINEMODE)
+
+ # Suppress Go Ahead. (This seems important for Putty to do correct echoing.)
+ # This will allow bi-directional operation.
+ connection.send(IAC + WILL + SUPPRESS_GO_AHEAD)
+
+ # Iac sb
+ connection.send(IAC + SB + LINEMODE + MODE + int2byte(0) + IAC + SE)
+
+ # IAC Will Echo
+ connection.send(IAC + WILL + ECHO)
+
+ # Negotiate window size
+ connection.send(IAC + DO + NAWS)
+
+
+class _ConnectionStdout(object):
+ """
+ Wrapper around socket which provides `write` and `flush` methods for the
+ Vt100_Output output.
+ """
+ def __init__(self, connection, encoding):
+ self._encoding = encoding
+ self._connection = connection
+ self._buffer = []
+
+ def write(self, data):
+ assert isinstance(data, text_type)
+ self._buffer.append(data.encode(self._encoding))
+ self.flush()
+
+ def flush(self):
+ try:
+ self._connection.send(b''.join(self._buffer))
+ except socket.error as e:
+ logger.error("Couldn't send data over socket: %s" % e)
+
+ self._buffer = []
+
+
+class TelnetConnection(object):
+ """
+ Class that represents one Telnet connection.
+ """
+ def __init__(self, conn, addr, application, server, encoding):
+ assert isinstance(addr, tuple) # (addr, port) tuple
+ assert isinstance(application, TelnetApplication)
+ assert isinstance(server, TelnetServer)
+ assert isinstance(encoding, text_type) # e.g. 'utf-8'
+
+ self.conn = conn
+ self.addr = addr
+ self.application = application
+ self.closed = False
+ self.handling_command = True
+ self.server = server
+ self.encoding = encoding
+ self.callback = None # Function that handles the CLI result.
+
+ # Create "Output" object.
+ self.size = Size(rows=40, columns=79)
+
+ # Initialize.
+ _initialize_telnet(conn)
+
+ # Create output.
+ def get_size():
+ return self.size
+ self.stdout = _ConnectionStdout(conn, encoding=encoding)
+ self.vt100_output = Vt100_Output(self.stdout, get_size, write_binary=False)
+
+ # Create an eventloop (adaptor) for the CommandLineInterface.
+ self.eventloop = _TelnetEventLoopInterface(server)
+
+ # Set default CommandLineInterface.
+ self.set_application(create_prompt_application())
+
+ # Call client_connected
+ application.client_connected(self)
+
+ # Draw for the first time.
+ self.handling_command = False
+ self.cli._redraw()
+
+ def set_application(self, app, callback=None):
+ """
+ Set ``CommandLineInterface`` instance for this connection.
+ (This can be replaced any time.)
+
+ :param cli: CommandLineInterface instance.
+ :param callback: Callable that takes the result of the CLI.
+ """
+ assert isinstance(app, Application)
+ assert callback is None or callable(callback)
+
+ self.cli = CommandLineInterface(
+ application=app,
+ eventloop=self.eventloop,
+ output=self.vt100_output)
+ self.callback = callback
+
+ # Create a parser, and parser callbacks.
+ cb = self.cli.create_eventloop_callbacks()
+ inputstream = InputStream(cb.feed_key)
+
+ # Input decoder for stdin. (Required when working with multibyte
+ # characters, like chinese input.)
+ stdin_decoder_cls = getincrementaldecoder(self.encoding)
+ stdin_decoder = [stdin_decoder_cls()] # nonlocal
+
+ # Tell the CLI that it's running. We don't start it through the run()
+ # call, but will still want _redraw() to work.
+ self.cli._is_running = True
+
+ def data_received(data):
+ """ TelnetProtocolParser 'data_received' callback """
+ assert isinstance(data, binary_type)
+
+ try:
+ result = stdin_decoder[0].decode(data)
+ inputstream.feed(result)
+ except UnicodeDecodeError:
+ stdin_decoder[0] = stdin_decoder_cls()
+ return ''
+
+ def size_received(rows, columns):
+ """ TelnetProtocolParser 'size_received' callback """
+ self.size = Size(rows=rows, columns=columns)
+ cb.terminal_size_changed()
+
+ self.parser = TelnetProtocolParser(data_received, size_received)
+
+ def feed(self, data):
+ """
+ Handler for incoming data. (Called by TelnetServer.)
+ """
+ assert isinstance(data, binary_type)
+
+ self.parser.feed(data)
+
+ # Render again.
+ self.cli._redraw()
+
+ # When a return value has been set (enter was pressed), handle command.
+ if self.cli.is_returning:
+ try:
+ return_value = self.cli.return_value()
+ except (EOFError, KeyboardInterrupt) as e:
+ # Control-D or Control-C was pressed.
+ logger.info('%s, closing connection.', type(e).__name__)
+ self.close()
+ return
+
+ # Handle CLI command
+ self._handle_command(return_value)
+
+ def _handle_command(self, command):
+ """
+ Handle command. This will run in a separate thread, in order not
+ to block the event loop.
+ """
+ logger.info('Handle command %r', command)
+
+ def in_executor():
+ self.handling_command = True
+ try:
+ if self.callback is not None:
+ self.callback(self, command)
+ finally:
+ self.server.call_from_executor(done)
+
+ def done():
+ self.handling_command = False
+
+ # Reset state and draw again. (If the connection is still open --
+ # the application could have called TelnetConnection.close()
+ if not self.closed:
+ self.cli.reset()
+ self.cli.buffers[DEFAULT_BUFFER].reset()
+ self.cli.renderer.request_absolute_cursor_position()
+ self.vt100_output.flush()
+ self.cli._redraw()
+
+ self.server.run_in_executor(in_executor)
+
+ def erase_screen(self):
+ """
+ Erase output screen.
+ """
+ self.vt100_output.erase_screen()
+ self.vt100_output.cursor_goto(0, 0)
+ self.vt100_output.flush()
+
+ def send(self, data):
+ """
+ Send text to the client.
+ """
+ assert isinstance(data, text_type)
+
+ # When data is send back to the client, we should replace the line
+ # endings. (We didn't allocate a real pseudo terminal, and the telnet
+ # connection is raw, so we are responsible for inserting \r.)
+ self.stdout.write(data.replace('\n', '\r\n'))
+ self.stdout.flush()
+
+ def close(self):
+ """
+ Close the connection.
+ """
+ self.application.client_leaving(self)
+
+ self.conn.close()
+ self.closed = True
+
+
+class _TelnetEventLoopInterface(EventLoop):
+ """
+ Eventloop object to be assigned to `CommandLineInterface`.
+ """
+ def __init__(self, server):
+ self._server = server
+
+ def close(self):
+ " Ignore. "
+
+ def stop(self):
+ " Ignore. "
+
+ def run_in_executor(self, callback):
+ self._server.run_in_executor(callback)
+
+ def call_from_executor(self, callback, _max_postpone_until=None):
+ self._server.call_from_executor(callback)
+
+ def add_reader(self, fd, callback):
+ raise NotImplementedError
+
+ def remove_reader(self, fd):
+ raise NotImplementedError
+
+
+class TelnetServer(object):
+ """
+ Telnet server implementation.
+ """
+ def __init__(self, host='127.0.0.1', port=23, application=None, encoding='utf-8'):
+ assert isinstance(host, text_type)
+ assert isinstance(port, int)
+ assert isinstance(application, TelnetApplication)
+ assert isinstance(encoding, text_type)
+
+ self.host = host
+ self.port = port
+ self.application = application
+ self.encoding = encoding
+
+ self.connections = set()
+
+ self._calls_from_executor = []
+
+ # Create a pipe for inter thread communication.
+ self._schedule_pipe = os.pipe()
+ fcntl.fcntl(self._schedule_pipe[0], fcntl.F_SETFL, os.O_NONBLOCK)
+
+ @classmethod
+ def create_socket(cls, host, port):
+ # Create and bind socket
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ s.bind((host, port))
+
+ s.listen(4)
+ return s
+
+ def run_in_executor(self, callback):
+ threading.Thread(target=callback).start()
+
+ def call_from_executor(self, callback):
+ self._calls_from_executor.append(callback)
+
+ if self._schedule_pipe:
+ os.write(self._schedule_pipe[1], b'x')
+
+ def _process_callbacks(self):
+ """
+ Process callbacks from `call_from_executor` in eventloop.
+ """
+ # Flush all the pipe content.
+ os.read(self._schedule_pipe[0], 1024)
+
+ # Process calls from executor.
+ calls_from_executor, self._calls_from_executor = self._calls_from_executor, []
+ for c in calls_from_executor:
+ c()
+
+ def run(self):
+ """
+ Run the eventloop for the telnet server.
+ """
+ listen_socket = self.create_socket(self.host, self.port)
+ logger.info('Listening for telnet connections on %s port %r', self.host, self.port)
+
+ try:
+ while True:
+ # Removed closed connections.
+ self.connections = set([c for c in self.connections if not c.closed])
+
+ # Ignore connections handling commands.
+ connections = set([c for c in self.connections if not c.handling_command])
+
+ # Wait for next event.
+ read_list = (
+ [listen_socket, self._schedule_pipe[0]] +
+ [c.conn for c in connections])
+
+ read, _, _ = select.select(read_list, [], [])
+
+ for s in read:
+ # When the socket itself is ready, accept a new connection.
+ if s == listen_socket:
+ self._accept(listen_socket)
+
+ # If we receive something on our "call_from_executor" pipe, process
+ # these callbacks in a thread safe way.
+ elif s == self._schedule_pipe[0]:
+ self._process_callbacks()
+
+ # Handle incoming data on socket.
+ else:
+ self._handle_incoming_data(s)
+ finally:
+ listen_socket.close()
+
+ def _accept(self, listen_socket):
+ """
+ Accept new incoming connection.
+ """
+ conn, addr = listen_socket.accept()
+ connection = TelnetConnection(conn, addr, self.application, self, encoding=self.encoding)
+ self.connections.add(connection)
+
+ logger.info('New connection %r %r', *addr)
+
+ def _handle_incoming_data(self, conn):
+ """
+ Handle incoming data on socket.
+ """
+ connection = [c for c in self.connections if c.conn == conn][0]
+ data = conn.recv(1024)
+ if data:
+ connection.feed(data)
+ else:
+ self.connections.remove(connection)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/validators/__init__.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/validators/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/validators/__init__.py
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/validators/base.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/validators/base.py
new file mode 100644
index 0000000000..16c1539c52
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/contrib/validators/base.py
@@ -0,0 +1,34 @@
+from __future__ import unicode_literals
+from prompt_toolkit.validation import Validator, ValidationError
+from six import string_types
+
+
+class SentenceValidator(Validator):
+ """
+ Validate input only when it appears in this list of sentences.
+
+ :param sentences: List of sentences.
+ :param ignore_case: If True, case-insensitive comparisons.
+ """
+ def __init__(self, sentences, ignore_case=False, error_message='Invalid input', move_cursor_to_end=False):
+ assert all(isinstance(s, string_types) for s in sentences)
+ assert isinstance(ignore_case, bool)
+ assert isinstance(error_message, string_types)
+
+ self.sentences = list(sentences)
+ self.ignore_case = ignore_case
+ self.error_message = error_message
+ self.move_cursor_to_end = move_cursor_to_end
+
+ if ignore_case:
+ self.sentences = set([s.lower() for s in self.sentences])
+
+ def validate(self, document):
+ if document.text not in self.sentences:
+ if self.move_cursor_to_end:
+ index = len(document.text)
+ else:
+ index = 0
+
+ raise ValidationError(cursor_position=index,
+ message=self.error_message)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/document.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/document.py
new file mode 100644
index 0000000000..25d817ddd0
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/document.py
@@ -0,0 +1,1001 @@
+"""
+The `Document` that implements all the text operations/querying.
+"""
+from __future__ import unicode_literals
+
+import bisect
+import re
+import six
+import string
+import weakref
+from six.moves import range, map
+
+from .selection import SelectionType, SelectionState, PasteMode
+from .clipboard import ClipboardData
+
+__all__ = ('Document',)
+
+
+# Regex for finding "words" in documents. (We consider a group of alnum
+# characters a word, but also a group of special characters a word, as long as
+# it doesn't contain a space.)
+# (This is a 'word' in Vi.)
+_FIND_WORD_RE = re.compile(r'([a-zA-Z0-9_]+|[^a-zA-Z0-9_\s]+)')
+_FIND_CURRENT_WORD_RE = re.compile(r'^([a-zA-Z0-9_]+|[^a-zA-Z0-9_\s]+)')
+_FIND_CURRENT_WORD_INCLUDE_TRAILING_WHITESPACE_RE = re.compile(r'^(([a-zA-Z0-9_]+|[^a-zA-Z0-9_\s]+)\s*)')
+
+# Regex for finding "WORDS" in documents.
+# (This is a 'WORD in Vi.)
+_FIND_BIG_WORD_RE = re.compile(r'([^\s]+)')
+_FIND_CURRENT_BIG_WORD_RE = re.compile(r'^([^\s]+)')
+_FIND_CURRENT_BIG_WORD_INCLUDE_TRAILING_WHITESPACE_RE = re.compile(r'^([^\s]+\s*)')
+
+# Share the Document._cache between all Document instances.
+# (Document instances are considered immutable. That means that if another
+# `Document` is constructed with the same text, it should have the same
+# `_DocumentCache`.)
+_text_to_document_cache = weakref.WeakValueDictionary() # Maps document.text to DocumentCache instance.
+
+
+class _ImmutableLineList(list):
+ """
+ Some protection for our 'lines' list, which is assumed to be immutable in the cache.
+ (Useful for detecting obvious bugs.)
+ """
+ def _error(self, *a, **kw):
+ raise NotImplementedError('Attempt to modifiy an immutable list.')
+
+ __setitem__ = _error
+ append = _error
+ clear = _error
+ extend = _error
+ insert = _error
+ pop = _error
+ remove = _error
+ reverse = _error
+ sort = _error
+
+
+class _DocumentCache(object):
+ def __init__(self):
+ #: List of lines for the Document text.
+ self.lines = None
+
+ #: List of index positions, pointing to the start of all the lines.
+ self.line_indexes = None
+
+
+class Document(object):
+ """
+ This is a immutable class around the text and cursor position, and contains
+ methods for querying this data, e.g. to give the text before the cursor.
+
+ This class is usually instantiated by a :class:`~prompt_toolkit.buffer.Buffer`
+ object, and accessed as the `document` property of that class.
+
+ :param text: string
+ :param cursor_position: int
+ :param selection: :class:`.SelectionState`
+ """
+ __slots__ = ('_text', '_cursor_position', '_selection', '_cache')
+
+ def __init__(self, text='', cursor_position=None, selection=None):
+ assert isinstance(text, six.text_type), 'Got %r' % text
+ assert selection is None or isinstance(selection, SelectionState)
+
+ # Check cursor position. It can also be right after the end. (Where we
+ # insert text.)
+ assert cursor_position is None or cursor_position <= len(text), AssertionError(
+ 'cursor_position=%r, len_text=%r' % (cursor_position, len(text)))
+
+ # By default, if no cursor position was given, make sure to put the
+ # cursor position is at the end of the document. This is what makes
+ # sense in most places.
+ if cursor_position is None:
+ cursor_position = len(text)
+
+ # Keep these attributes private. A `Document` really has to be
+ # considered to be immutable, because otherwise the caching will break
+ # things. Because of that, we wrap these into read-only properties.
+ self._text = text
+ self._cursor_position = cursor_position
+ self._selection = selection
+
+ # Cache for lines/indexes. (Shared with other Document instances that
+ # contain the same text.
+ try:
+ self._cache = _text_to_document_cache[self.text]
+ except KeyError:
+ self._cache = _DocumentCache()
+ _text_to_document_cache[self.text] = self._cache
+
+ # XX: For some reason, above, we can't use 'WeakValueDictionary.setdefault'.
+ # This fails in Pypy3. `self._cache` becomes None, because that's what
+ # 'setdefault' returns.
+ # self._cache = _text_to_document_cache.setdefault(self.text, _DocumentCache())
+ # assert self._cache
+
+ def __repr__(self):
+ return '%s(%r, %r)' % (self.__class__.__name__, self.text, self.cursor_position)
+
+ @property
+ def text(self):
+ " The document text. "
+ return self._text
+
+ @property
+ def cursor_position(self):
+ " The document cursor position. "
+ return self._cursor_position
+
+ @property
+ def selection(self):
+ " :class:`.SelectionState` object. "
+ return self._selection
+
+ @property
+ def current_char(self):
+ """ Return character under cursor or an empty string. """
+ return self._get_char_relative_to_cursor(0) or ''
+
+ @property
+ def char_before_cursor(self):
+ """ Return character before the cursor or an empty string. """
+ return self._get_char_relative_to_cursor(-1) or ''
+
+ @property
+ def text_before_cursor(self):
+ return self.text[:self.cursor_position:]
+
+ @property
+ def text_after_cursor(self):
+ return self.text[self.cursor_position:]
+
+ @property
+ def current_line_before_cursor(self):
+ """ Text from the start of the line until the cursor. """
+ _, _, text = self.text_before_cursor.rpartition('\n')
+ return text
+
+ @property
+ def current_line_after_cursor(self):
+ """ Text from the cursor until the end of the line. """
+ text, _, _ = self.text_after_cursor.partition('\n')
+ return text
+
+ @property
+ def lines(self):
+ """
+ Array of all the lines.
+ """
+ # Cache, because this one is reused very often.
+ if self._cache.lines is None:
+ self._cache.lines = _ImmutableLineList(self.text.split('\n'))
+
+ return self._cache.lines
+
+ @property
+ def _line_start_indexes(self):
+ """
+ Array pointing to the start indexes of all the lines.
+ """
+ # Cache, because this is often reused. (If it is used, it's often used
+ # many times. And this has to be fast for editing big documents!)
+ if self._cache.line_indexes is None:
+ # Create list of line lengths.
+ line_lengths = map(len, self.lines)
+
+ # Calculate cumulative sums.
+ indexes = [0]
+ append = indexes.append
+ pos = 0
+
+ for line_length in line_lengths:
+ pos += line_length + 1
+ append(pos)
+
+ # Remove the last item. (This is not a new line.)
+ if len(indexes) > 1:
+ indexes.pop()
+
+ self._cache.line_indexes = indexes
+
+ return self._cache.line_indexes
+
+ @property
+ def lines_from_current(self):
+ """
+ Array of the lines starting from the current line, until the last line.
+ """
+ return self.lines[self.cursor_position_row:]
+
+ @property
+ def line_count(self):
+ r""" Return the number of lines in this document. If the document ends
+ with a trailing \n, that counts as the beginning of a new line. """
+ return len(self.lines)
+
+ @property
+ def current_line(self):
+ """ Return the text on the line where the cursor is. (when the input
+ consists of just one line, it equals `text`. """
+ return self.current_line_before_cursor + self.current_line_after_cursor
+
+ @property
+ def leading_whitespace_in_current_line(self):
+ """ The leading whitespace in the left margin of the current line. """
+ current_line = self.current_line
+ length = len(current_line) - len(current_line.lstrip())
+ return current_line[:length]
+
+ def _get_char_relative_to_cursor(self, offset=0):
+ """
+ Return character relative to cursor position, or empty string
+ """
+ try:
+ return self.text[self.cursor_position + offset]
+ except IndexError:
+ return ''
+
+ @property
+ def on_first_line(self):
+ """
+ True when we are at the first line.
+ """
+ return self.cursor_position_row == 0
+
+ @property
+ def on_last_line(self):
+ """
+ True when we are at the last line.
+ """
+ return self.cursor_position_row == self.line_count - 1
+
+ @property
+ def cursor_position_row(self):
+ """
+ Current row. (0-based.)
+ """
+ row, _ = self._find_line_start_index(self.cursor_position)
+ return row
+
+ @property
+ def cursor_position_col(self):
+ """
+ Current column. (0-based.)
+ """
+ # (Don't use self.text_before_cursor to calculate this. Creating
+ # substrings and doing rsplit is too expensive for getting the cursor
+ # position.)
+ _, line_start_index = self._find_line_start_index(self.cursor_position)
+ return self.cursor_position - line_start_index
+
+ def _find_line_start_index(self, index):
+ """
+ For the index of a character at a certain line, calculate the index of
+ the first character on that line.
+
+ Return (row, index) tuple.
+ """
+ indexes = self._line_start_indexes
+
+ pos = bisect.bisect_right(indexes, index) - 1
+ return pos, indexes[pos]
+
+ def translate_index_to_position(self, index):
+ """
+ Given an index for the text, return the corresponding (row, col) tuple.
+ (0-based. Returns (0, 0) for index=0.)
+ """
+ # Find start of this line.
+ row, row_index = self._find_line_start_index(index)
+ col = index - row_index
+
+ return row, col
+
+
+ def translate_row_col_to_index(self, row, col):
+ """
+ Given a (row, col) tuple, return the corresponding index.
+ (Row and col params are 0-based.)
+
+ Negative row/col values are turned into zero.
+ """
+ try:
+ result = self._line_start_indexes[row]
+ line = self.lines[row]
+ except IndexError:
+ if row < 0:
+ result = self._line_start_indexes[0]
+ line = self.lines[0]
+ else:
+ result = self._line_start_indexes[-1]
+ line = self.lines[-1]
+
+ result += max(0, min(col, len(line)))
+
+ # Keep in range. (len(self.text) is included, because the cursor can be
+ # right after the end of the text as well.)
+ result = max(0, min(result, len(self.text)))
+ return result
+
+ @property
+ def is_cursor_at_the_end(self):
+ """ True when the cursor is at the end of the text. """
+ return self.cursor_position == len(self.text)
+
+ @property
+ def is_cursor_at_the_end_of_line(self):
+ """ True when the cursor is at the end of this line. """
+ return self.current_char in ('\n', '')
+
+ def has_match_at_current_position(self, sub):
+ """
+ `True` when this substring is found at the cursor position.
+ """
+ return self.text.find(sub, self.cursor_position) == self.cursor_position
+
+ def find(self, sub, in_current_line=False, include_current_position=False,
+ ignore_case=False, count=1):
+ """
+ Find `text` after the cursor, return position relative to the cursor
+ position. Return `None` if nothing was found.
+
+ :param count: Find the n-th occurance.
+ """
+ assert isinstance(ignore_case, bool)
+
+ if in_current_line:
+ text = self.current_line_after_cursor
+ else:
+ text = self.text_after_cursor
+
+ if not include_current_position:
+ if len(text) == 0:
+ return # (Otherwise, we always get a match for the empty string.)
+ else:
+ text = text[1:]
+
+ flags = re.IGNORECASE if ignore_case else 0
+ iterator = re.finditer(re.escape(sub), text, flags)
+
+ try:
+ for i, match in enumerate(iterator):
+ if i + 1 == count:
+ if include_current_position:
+ return match.start(0)
+ else:
+ return match.start(0) + 1
+ except StopIteration:
+ pass
+
+ def find_all(self, sub, ignore_case=False):
+ """
+ Find all occurances of the substring. Return a list of absolute
+ positions in the document.
+ """
+ flags = re.IGNORECASE if ignore_case else 0
+ return [a.start() for a in re.finditer(re.escape(sub), self.text, flags)]
+
+ def find_backwards(self, sub, in_current_line=False, ignore_case=False, count=1):
+ """
+ Find `text` before the cursor, return position relative to the cursor
+ position. Return `None` if nothing was found.
+
+ :param count: Find the n-th occurance.
+ """
+ if in_current_line:
+ before_cursor = self.current_line_before_cursor[::-1]
+ else:
+ before_cursor = self.text_before_cursor[::-1]
+
+ flags = re.IGNORECASE if ignore_case else 0
+ iterator = re.finditer(re.escape(sub[::-1]), before_cursor, flags)
+
+ try:
+ for i, match in enumerate(iterator):
+ if i + 1 == count:
+ return - match.start(0) - len(sub)
+ except StopIteration:
+ pass
+
+ def get_word_before_cursor(self, WORD=False):
+ """
+ Give the word before the cursor.
+ If we have whitespace before the cursor this returns an empty string.
+ """
+ if self.text_before_cursor[-1:].isspace():
+ return ''
+ else:
+ return self.text_before_cursor[self.find_start_of_previous_word(WORD=WORD):]
+
+ def find_start_of_previous_word(self, count=1, WORD=False):
+ """
+ Return an index relative to the cursor position pointing to the start
+ of the previous word. Return `None` if nothing was found.
+ """
+ # Reverse the text before the cursor, in order to do an efficient
+ # backwards search.
+ text_before_cursor = self.text_before_cursor[::-1]
+
+ regex = _FIND_BIG_WORD_RE if WORD else _FIND_WORD_RE
+ iterator = regex.finditer(text_before_cursor)
+
+ try:
+ for i, match in enumerate(iterator):
+ if i + 1 == count:
+ return - match.end(1)
+ except StopIteration:
+ pass
+
+ def find_boundaries_of_current_word(self, WORD=False, include_leading_whitespace=False,
+ include_trailing_whitespace=False):
+ """
+ Return the relative boundaries (startpos, endpos) of the current word under the
+ cursor. (This is at the current line, because line boundaries obviously
+ don't belong to any word.)
+ If not on a word, this returns (0,0)
+ """
+ text_before_cursor = self.current_line_before_cursor[::-1]
+ text_after_cursor = self.current_line_after_cursor
+
+ def get_regex(include_whitespace):
+ return {
+ (False, False): _FIND_CURRENT_WORD_RE,
+ (False, True): _FIND_CURRENT_WORD_INCLUDE_TRAILING_WHITESPACE_RE,
+ (True, False): _FIND_CURRENT_BIG_WORD_RE,
+ (True, True): _FIND_CURRENT_BIG_WORD_INCLUDE_TRAILING_WHITESPACE_RE,
+ }[(WORD, include_whitespace)]
+
+ match_before = get_regex(include_leading_whitespace).search(text_before_cursor)
+ match_after = get_regex(include_trailing_whitespace).search(text_after_cursor)
+
+ # When there is a match before and after, and we're not looking for
+ # WORDs, make sure that both the part before and after the cursor are
+ # either in the [a-zA-Z_] alphabet or not. Otherwise, drop the part
+ # before the cursor.
+ if not WORD and match_before and match_after:
+ c1 = self.text[self.cursor_position - 1]
+ c2 = self.text[self.cursor_position]
+ alphabet = string.ascii_letters + '0123456789_'
+
+ if (c1 in alphabet) != (c2 in alphabet):
+ match_before = None
+
+ return (
+ - match_before.end(1) if match_before else 0,
+ match_after.end(1) if match_after else 0
+ )
+
+ def get_word_under_cursor(self, WORD=False):
+ """
+ Return the word, currently below the cursor.
+ This returns an empty string when the cursor is on a whitespace region.
+ """
+ start, end = self.find_boundaries_of_current_word(WORD=WORD)
+ return self.text[self.cursor_position + start: self.cursor_position + end]
+
+ def find_next_word_beginning(self, count=1, WORD=False):
+ """
+ Return an index relative to the cursor position pointing to the start
+ of the next word. Return `None` if nothing was found.
+ """
+ if count < 0:
+ return self.find_previous_word_beginning(count=-count, WORD=WORD)
+
+ regex = _FIND_BIG_WORD_RE if WORD else _FIND_WORD_RE
+ iterator = regex.finditer(self.text_after_cursor)
+
+ try:
+ for i, match in enumerate(iterator):
+ # Take first match, unless it's the word on which we're right now.
+ if i == 0 and match.start(1) == 0:
+ count += 1
+
+ if i + 1 == count:
+ return match.start(1)
+ except StopIteration:
+ pass
+
+ def find_next_word_ending(self, include_current_position=False, count=1, WORD=False):
+ """
+ Return an index relative to the cursor position pointing to the end
+ of the next word. Return `None` if nothing was found.
+ """
+ if count < 0:
+ return self.find_previous_word_ending(count=-count, WORD=WORD)
+
+ if include_current_position:
+ text = self.text_after_cursor
+ else:
+ text = self.text_after_cursor[1:]
+
+ regex = _FIND_BIG_WORD_RE if WORD else _FIND_WORD_RE
+ iterable = regex.finditer(text)
+
+ try:
+ for i, match in enumerate(iterable):
+ if i + 1 == count:
+ value = match.end(1)
+
+ if include_current_position:
+ return value
+ else:
+ return value + 1
+
+ except StopIteration:
+ pass
+
+ def find_previous_word_beginning(self, count=1, WORD=False):
+ """
+ Return an index relative to the cursor position pointing to the start
+ of the previous word. Return `None` if nothing was found.
+ """
+ if count < 0:
+ return self.find_next_word_beginning(count=-count, WORD=WORD)
+
+ regex = _FIND_BIG_WORD_RE if WORD else _FIND_WORD_RE
+ iterator = regex.finditer(self.text_before_cursor[::-1])
+
+ try:
+ for i, match in enumerate(iterator):
+ if i + 1 == count:
+ return - match.end(1)
+ except StopIteration:
+ pass
+
+ def find_previous_word_ending(self, count=1, WORD=False):
+ """
+ Return an index relative to the cursor position pointing to the end
+ of the previous word. Return `None` if nothing was found.
+ """
+ if count < 0:
+ return self.find_next_word_ending(count=-count, WORD=WORD)
+
+ text_before_cursor = self.text_after_cursor[:1] + self.text_before_cursor[::-1]
+
+ regex = _FIND_BIG_WORD_RE if WORD else _FIND_WORD_RE
+ iterator = regex.finditer(text_before_cursor)
+
+ try:
+ for i, match in enumerate(iterator):
+ # Take first match, unless it's the word on which we're right now.
+ if i == 0 and match.start(1) == 0:
+ count += 1
+
+ if i + 1 == count:
+ return -match.start(1) + 1
+ except StopIteration:
+ pass
+
+ def find_next_matching_line(self, match_func, count=1):
+ """
+ Look downwards for empty lines.
+ Return the line index, relative to the current line.
+ """
+ result = None
+
+ for index, line in enumerate(self.lines[self.cursor_position_row + 1:]):
+ if match_func(line):
+ result = 1 + index
+ count -= 1
+
+ if count == 0:
+ break
+
+ return result
+
+ def find_previous_matching_line(self, match_func, count=1):
+ """
+ Look upwards for empty lines.
+ Return the line index, relative to the current line.
+ """
+ result = None
+
+ for index, line in enumerate(self.lines[:self.cursor_position_row][::-1]):
+ if match_func(line):
+ result = -1 - index
+ count -= 1
+
+ if count == 0:
+ break
+
+ return result
+
+ def get_cursor_left_position(self, count=1):
+ """
+ Relative position for cursor left.
+ """
+ if count < 0:
+ return self.get_cursor_right_position(-count)
+
+ return - min(self.cursor_position_col, count)
+
+ def get_cursor_right_position(self, count=1):
+ """
+ Relative position for cursor_right.
+ """
+ if count < 0:
+ return self.get_cursor_left_position(-count)
+
+ return min(count, len(self.current_line_after_cursor))
+
+ def get_cursor_up_position(self, count=1, preferred_column=None):
+ """
+ Return the relative cursor position (character index) where we would be if the
+ user pressed the arrow-up button.
+
+ :param preferred_column: When given, go to this column instead of
+ staying at the current column.
+ """
+ assert count >= 1
+ column = self.cursor_position_col if preferred_column is None else preferred_column
+
+ return self.translate_row_col_to_index(
+ max(0, self.cursor_position_row - count), column) - self.cursor_position
+
+ def get_cursor_down_position(self, count=1, preferred_column=None):
+ """
+ Return the relative cursor position (character index) where we would be if the
+ user pressed the arrow-down button.
+
+ :param preferred_column: When given, go to this column instead of
+ staying at the current column.
+ """
+ assert count >= 1
+ column = self.cursor_position_col if preferred_column is None else preferred_column
+
+ return self.translate_row_col_to_index(
+ self.cursor_position_row + count, column) - self.cursor_position
+
+ def find_enclosing_bracket_right(self, left_ch, right_ch, end_pos=None):
+ """
+ Find the right bracket enclosing current position. Return the relative
+ position to the cursor position.
+
+ When `end_pos` is given, don't look past the position.
+ """
+ if self.current_char == right_ch:
+ return 0
+
+ if end_pos is None:
+ end_pos = len(self.text)
+ else:
+ end_pos = min(len(self.text), end_pos)
+
+ stack = 1
+
+ # Look forward.
+ for i in range(self.cursor_position + 1, end_pos):
+ c = self.text[i]
+
+ if c == left_ch:
+ stack += 1
+ elif c == right_ch:
+ stack -= 1
+
+ if stack == 0:
+ return i - self.cursor_position
+
+ def find_enclosing_bracket_left(self, left_ch, right_ch, start_pos=None):
+ """
+ Find the left bracket enclosing current position. Return the relative
+ position to the cursor position.
+
+ When `start_pos` is given, don't look past the position.
+ """
+ if self.current_char == left_ch:
+ return 0
+
+ if start_pos is None:
+ start_pos = 0
+ else:
+ start_pos = max(0, start_pos)
+
+ stack = 1
+
+ # Look backward.
+ for i in range(self.cursor_position - 1, start_pos - 1, -1):
+ c = self.text[i]
+
+ if c == right_ch:
+ stack += 1
+ elif c == left_ch:
+ stack -= 1
+
+ if stack == 0:
+ return i - self.cursor_position
+
+ def find_matching_bracket_position(self, start_pos=None, end_pos=None):
+ """
+ Return relative cursor position of matching [, (, { or < bracket.
+
+ When `start_pos` or `end_pos` are given. Don't look past the positions.
+ """
+
+ # Look for a match.
+ for A, B in '()', '[]', '{}', '<>':
+ if self.current_char == A:
+ return self.find_enclosing_bracket_right(A, B, end_pos=end_pos) or 0
+ elif self.current_char == B:
+ return self.find_enclosing_bracket_left(A, B, start_pos=start_pos) or 0
+
+ return 0
+
+ def get_start_of_document_position(self):
+ """ Relative position for the start of the document. """
+ return - self.cursor_position
+
+ def get_end_of_document_position(self):
+ """ Relative position for the end of the document. """
+ return len(self.text) - self.cursor_position
+
+ def get_start_of_line_position(self, after_whitespace=False):
+ """ Relative position for the start of this line. """
+ if after_whitespace:
+ current_line = self.current_line
+ return len(current_line) - len(current_line.lstrip()) - self.cursor_position_col
+ else:
+ return - len(self.current_line_before_cursor)
+
+ def get_end_of_line_position(self):
+ """ Relative position for the end of this line. """
+ return len(self.current_line_after_cursor)
+
+ def last_non_blank_of_current_line_position(self):
+ """
+ Relative position for the last non blank character of this line.
+ """
+ return len(self.current_line.rstrip()) - self.cursor_position_col - 1
+
+ def get_column_cursor_position(self, column):
+ """
+ Return the relative cursor position for this column at the current
+ line. (It will stay between the boundaries of the line in case of a
+ larger number.)
+ """
+ line_length = len(self.current_line)
+ current_column = self.cursor_position_col
+ column = max(0, min(line_length, column))
+
+ return column - current_column
+
+ def selection_range(self): # XXX: shouldn't this return `None` if there is no selection???
+ """
+ Return (from, to) tuple of the selection.
+ start and end position are included.
+
+ This doesn't take the selection type into account. Use
+ `selection_ranges` instead.
+ """
+ if self.selection:
+ from_, to = sorted([self.cursor_position, self.selection.original_cursor_position])
+ else:
+ from_, to = self.cursor_position, self.cursor_position
+
+ return from_, to
+
+ def selection_ranges(self):
+ """
+ Return a list of (from, to) tuples for the selection or none if nothing
+ was selected. start and end position are always included in the
+ selection.
+
+ This will yield several (from, to) tuples in case of a BLOCK selection.
+ """
+ if self.selection:
+ from_, to = sorted([self.cursor_position, self.selection.original_cursor_position])
+
+ if self.selection.type == SelectionType.BLOCK:
+ from_line, from_column = self.translate_index_to_position(from_)
+ to_line, to_column = self.translate_index_to_position(to)
+ from_column, to_column = sorted([from_column, to_column])
+ lines = self.lines
+
+ for l in range(from_line, to_line + 1):
+ line_length = len(lines[l])
+ if from_column < line_length:
+ yield (self.translate_row_col_to_index(l, from_column),
+ self.translate_row_col_to_index(l, min(line_length - 1, to_column)))
+ else:
+ # In case of a LINES selection, go to the start/end of the lines.
+ if self.selection.type == SelectionType.LINES:
+ from_ = max(0, self.text.rfind('\n', 0, from_) + 1)
+
+ if self.text.find('\n', to) >= 0:
+ to = self.text.find('\n', to)
+ else:
+ to = len(self.text) - 1
+
+ yield from_, to
+
+ def selection_range_at_line(self, row):
+ """
+ If the selection spans a portion of the given line, return a (from, to) tuple.
+ Otherwise, return None.
+ """
+ if self.selection:
+ row_start = self.translate_row_col_to_index(row, 0)
+ row_end = self.translate_row_col_to_index(row, max(0, len(self.lines[row]) - 1))
+
+ from_, to = sorted([self.cursor_position, self.selection.original_cursor_position])
+
+ # Take the intersection of the current line and the selection.
+ intersection_start = max(row_start, from_)
+ intersection_end = min(row_end, to)
+
+ if intersection_start <= intersection_end:
+ if self.selection.type == SelectionType.LINES:
+ intersection_start = row_start
+ intersection_end = row_end
+ elif self.selection.type == SelectionType.BLOCK:
+ _, col1 = self.translate_index_to_position(from_)
+ _, col2 = self.translate_index_to_position(to)
+ col1, col2 = sorted([col1, col2])
+ intersection_start = self.translate_row_col_to_index(row, col1)
+ intersection_end = self.translate_row_col_to_index(row, col2)
+
+ _, from_column = self.translate_index_to_position(intersection_start)
+ _, to_column = self.translate_index_to_position(intersection_end)
+
+ return from_column, to_column
+
+ def cut_selection(self):
+ """
+ Return a (:class:`.Document`, :class:`.ClipboardData`) tuple, where the
+ document represents the new document when the selection is cut, and the
+ clipboard data, represents whatever has to be put on the clipboard.
+ """
+ if self.selection:
+ cut_parts = []
+ remaining_parts = []
+ new_cursor_position = self.cursor_position
+
+ last_to = 0
+ for from_, to in self.selection_ranges():
+ if last_to == 0:
+ new_cursor_position = from_
+
+ remaining_parts.append(self.text[last_to:from_])
+ cut_parts.append(self.text[from_:to + 1])
+ last_to = to + 1
+
+ remaining_parts.append(self.text[last_to:])
+
+ cut_text = '\n'.join(cut_parts)
+ remaining_text = ''.join(remaining_parts)
+
+ # In case of a LINES selection, don't include the trailing newline.
+ if self.selection.type == SelectionType.LINES and cut_text.endswith('\n'):
+ cut_text = cut_text[:-1]
+
+ return (Document(text=remaining_text, cursor_position=new_cursor_position),
+ ClipboardData(cut_text, self.selection.type))
+ else:
+ return self, ClipboardData('')
+
+ def paste_clipboard_data(self, data, paste_mode=PasteMode.EMACS, count=1):
+ """
+ Return a new :class:`.Document` instance which contains the result if
+ we would paste this data at the current cursor position.
+
+ :param paste_mode: Where to paste. (Before/after/emacs.)
+ :param count: When >1, Paste multiple times.
+ """
+ assert isinstance(data, ClipboardData)
+ assert paste_mode in (PasteMode.VI_BEFORE, PasteMode.VI_AFTER, PasteMode.EMACS)
+
+ before = (paste_mode == PasteMode.VI_BEFORE)
+ after = (paste_mode == PasteMode.VI_AFTER)
+
+ if data.type == SelectionType.CHARACTERS:
+ if after:
+ new_text = (self.text[:self.cursor_position + 1] + data.text * count +
+ self.text[self.cursor_position + 1:])
+ else:
+ new_text = self.text_before_cursor + data.text * count + self.text_after_cursor
+
+ new_cursor_position = self.cursor_position + len(data.text) * count
+ if before:
+ new_cursor_position -= 1
+
+ elif data.type == SelectionType.LINES:
+ l = self.cursor_position_row
+ if before:
+ lines = self.lines[:l] + [data.text] * count + self.lines[l:]
+ new_text = '\n'.join(lines)
+ new_cursor_position = len(''.join(self.lines[:l])) + l
+ else:
+ lines = self.lines[:l + 1] + [data.text] * count + self.lines[l + 1:]
+ new_cursor_position = len(''.join(self.lines[:l + 1])) + l + 1
+ new_text = '\n'.join(lines)
+
+ elif data.type == SelectionType.BLOCK:
+ lines = self.lines[:]
+ start_line = self.cursor_position_row
+ start_column = self.cursor_position_col + (0 if before else 1)
+
+ for i, line in enumerate(data.text.split('\n')):
+ index = i + start_line
+ if index >= len(lines):
+ lines.append('')
+
+ lines[index] = lines[index].ljust(start_column)
+ lines[index] = lines[index][:start_column] + line * count + lines[index][start_column:]
+
+ new_text = '\n'.join(lines)
+ new_cursor_position = self.cursor_position + (0 if before else 1)
+
+ return Document(text=new_text, cursor_position=new_cursor_position)
+
+ def empty_line_count_at_the_end(self):
+ """
+ Return number of empty lines at the end of the document.
+ """
+ count = 0
+ for line in self.lines[::-1]:
+ if not line or line.isspace():
+ count += 1
+ else:
+ break
+
+ return count
+
+ def start_of_paragraph(self, count=1, before=False):
+ """
+ Return the start of the current paragraph. (Relative cursor position.)
+ """
+ def match_func(text):
+ return not text or text.isspace()
+
+ line_index = self.find_previous_matching_line(match_func=match_func, count=count)
+
+ if line_index:
+ add = 0 if before else 1
+ return min(0, self.get_cursor_up_position(count=-line_index) + add)
+ else:
+ return -self.cursor_position
+
+ def end_of_paragraph(self, count=1, after=False):
+ """
+ Return the end of the current paragraph. (Relative cursor position.)
+ """
+ def match_func(text):
+ return not text or text.isspace()
+
+ line_index = self.find_next_matching_line(match_func=match_func, count=count)
+
+ if line_index:
+ add = 0 if after else 1
+ return max(0, self.get_cursor_down_position(count=line_index) - add)
+ else:
+ return len(self.text_after_cursor)
+
+ # Modifiers.
+
+ def insert_after(self, text):
+ """
+ Create a new document, with this text inserted after the buffer.
+ It keeps selection ranges and cursor position in sync.
+ """
+ return Document(
+ text=self.text + text,
+ cursor_position=self.cursor_position,
+ selection=self.selection)
+
+ def insert_before(self, text):
+ """
+ Create a new document, with this text inserted before the buffer.
+ It keeps selection ranges and cursor position in sync.
+ """
+ selection_state = self.selection
+
+ if selection_state:
+ selection_state = SelectionState(
+ original_cursor_position=selection_state.original_cursor_position + len(text),
+ type=selection_state.type)
+
+ return Document(
+ text=text + self.text,
+ cursor_position=self.cursor_position + len(text),
+ selection=selection_state)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/enums.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/enums.py
new file mode 100644
index 0000000000..6945f44c96
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/enums.py
@@ -0,0 +1,29 @@
+from __future__ import unicode_literals
+
+
+class IncrementalSearchDirection(object):
+ FORWARD = 'FORWARD'
+ BACKWARD = 'BACKWARD'
+
+
+class EditingMode(object):
+ # The set of key bindings that is active.
+ VI = 'VI'
+ EMACS = 'EMACS'
+
+
+#: Name of the search buffer.
+SEARCH_BUFFER = 'SEARCH_BUFFER'
+
+#: Name of the default buffer.
+DEFAULT_BUFFER = 'DEFAULT_BUFFER'
+
+#: Name of the system buffer.
+SYSTEM_BUFFER = 'SYSTEM_BUFFER'
+
+# Dummy buffer. This is the buffer returned by
+# `CommandLineInterface.current_buffer` when the top of the `FocusStack` is
+# `None`. This could be the case when there is some widget has the focus and no
+# actual text editing is possible. This buffer should also never be displayed.
+# (It will never contain any actual text.)
+DUMMY_BUFFER = 'DUMMY_BUFFER'
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/__init__.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/__init__.py
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/asyncio_base.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/asyncio_base.py
new file mode 100644
index 0000000000..ace2b8db49
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/asyncio_base.py
@@ -0,0 +1,46 @@
+"""
+Eventloop for integration with Python3 asyncio.
+
+Note that we can't use "yield from", because the package should be installable
+under Python 2.6 as well, and it should contain syntactically valid Python 2.6
+code.
+"""
+from __future__ import unicode_literals
+
+__all__ = (
+ 'AsyncioTimeout',
+)
+
+
+class AsyncioTimeout(object):
+ """
+ Call the `timeout` function when the timeout expires.
+ Every call of the `reset` method, resets the timeout and starts a new
+ timer.
+ """
+ def __init__(self, timeout, callback, loop):
+ self.timeout = timeout
+ self.callback = callback
+ self.loop = loop
+
+ self.counter = 0
+ self.running = True
+
+ def reset(self):
+ """
+ Reset the timeout. Starts a new timer.
+ """
+ self.counter += 1
+ local_counter = self.counter
+
+ def timer_timeout():
+ if self.counter == local_counter and self.running:
+ self.callback()
+
+ self.loop.call_later(self.timeout, timer_timeout)
+
+ def stop(self):
+ """
+ Ignore timeout. Don't call the callback anymore.
+ """
+ self.running = False
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/asyncio_posix.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/asyncio_posix.py
new file mode 100644
index 0000000000..426ed96f67
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/asyncio_posix.py
@@ -0,0 +1,113 @@
+"""
+Posix asyncio event loop.
+"""
+from __future__ import unicode_literals
+
+from ..terminal.vt100_input import InputStream
+from .asyncio_base import AsyncioTimeout
+from .base import EventLoop, INPUT_TIMEOUT
+from .callbacks import EventLoopCallbacks
+from .posix_utils import PosixStdinReader
+
+import asyncio
+import signal
+
+__all__ = (
+ 'PosixAsyncioEventLoop',
+)
+
+
+class PosixAsyncioEventLoop(EventLoop):
+ def __init__(self, loop=None):
+ self.loop = loop or asyncio.get_event_loop()
+ self.closed = False
+
+ self._stopped_f = asyncio.Future(loop=self.loop)
+
+ @asyncio.coroutine
+ def run_as_coroutine(self, stdin, callbacks):
+ """
+ The input 'event loop'.
+ """
+ assert isinstance(callbacks, EventLoopCallbacks)
+
+ # Create reader class.
+ stdin_reader = PosixStdinReader(stdin.fileno())
+
+ if self.closed:
+ raise Exception('Event loop already closed.')
+
+ inputstream = InputStream(callbacks.feed_key)
+
+ try:
+ # Create a new Future every time.
+ self._stopped_f = asyncio.Future(loop=self.loop)
+
+ # Handle input timouts
+ def timeout_handler():
+ """
+ When no input has been received for INPUT_TIMEOUT seconds,
+ flush the input stream and fire the timeout event.
+ """
+ inputstream.flush()
+
+ callbacks.input_timeout()
+
+ timeout = AsyncioTimeout(INPUT_TIMEOUT, timeout_handler, self.loop)
+
+ # Catch sigwinch
+ def received_winch():
+ self.call_from_executor(callbacks.terminal_size_changed)
+
+ self.loop.add_signal_handler(signal.SIGWINCH, received_winch)
+
+ # Read input data.
+ def stdin_ready():
+ data = stdin_reader.read()
+ inputstream.feed(data)
+ timeout.reset()
+
+ # Quit when the input stream was closed.
+ if stdin_reader.closed:
+ self.stop()
+
+ self.loop.add_reader(stdin.fileno(), stdin_ready)
+
+ # Block this coroutine until stop() has been called.
+ for f in self._stopped_f:
+ yield f
+
+ finally:
+ # Clean up.
+ self.loop.remove_reader(stdin.fileno())
+ self.loop.remove_signal_handler(signal.SIGWINCH)
+
+ # Don't trigger any timeout events anymore.
+ timeout.stop()
+
+ def stop(self):
+ # Trigger the 'Stop' future.
+ self._stopped_f.set_result(True)
+
+ def close(self):
+ # Note: we should not close the asyncio loop itself, because that one
+ # was not created here.
+ self.closed = True
+
+ def run_in_executor(self, callback):
+ self.loop.run_in_executor(None, callback)
+
+ def call_from_executor(self, callback, _max_postpone_until=None):
+ """
+ Call this function in the main event loop.
+ Similar to Twisted's ``callFromThread``.
+ """
+ self.loop.call_soon_threadsafe(callback)
+
+ def add_reader(self, fd, callback):
+ " Start watching the file descriptor for read availability. "
+ self.loop.add_reader(fd, callback)
+
+ def remove_reader(self, fd):
+ " Stop watching the file descriptor for read availability. "
+ self.loop.remove_reader(fd)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/asyncio_win32.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/asyncio_win32.py
new file mode 100644
index 0000000000..45f5f52679
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/asyncio_win32.py
@@ -0,0 +1,83 @@
+"""
+Win32 asyncio event loop.
+
+Windows notes:
+- Somehow it doesn't seem to work with the 'ProactorEventLoop'.
+"""
+from __future__ import unicode_literals
+
+from .base import EventLoop, INPUT_TIMEOUT
+from ..terminal.win32_input import ConsoleInputReader
+from .callbacks import EventLoopCallbacks
+from .asyncio_base import AsyncioTimeout
+
+import asyncio
+
+__all__ = (
+ 'Win32AsyncioEventLoop',
+)
+
+
+class Win32AsyncioEventLoop(EventLoop):
+ def __init__(self, loop=None):
+ self._console_input_reader = ConsoleInputReader()
+ self.running = False
+ self.closed = False
+ self.loop = loop or asyncio.get_event_loop()
+
+ @asyncio.coroutine
+ def run_as_coroutine(self, stdin, callbacks):
+ """
+ The input 'event loop'.
+ """
+ # Note: We cannot use "yield from", because this package also
+ # installs on Python 2.
+ assert isinstance(callbacks, EventLoopCallbacks)
+
+ if self.closed:
+ raise Exception('Event loop already closed.')
+
+ timeout = AsyncioTimeout(INPUT_TIMEOUT, callbacks.input_timeout, self.loop)
+ self.running = True
+
+ try:
+ while self.running:
+ timeout.reset()
+
+ # Get keys
+ try:
+ g = iter(self.loop.run_in_executor(None, self._console_input_reader.read))
+ while True:
+ yield next(g)
+ except StopIteration as e:
+ keys = e.args[0]
+
+ # Feed keys to input processor.
+ for k in keys:
+ callbacks.feed_key(k)
+ finally:
+ timeout.stop()
+
+ def stop(self):
+ self.running = False
+
+ def close(self):
+ # Note: we should not close the asyncio loop itself, because that one
+ # was not created here.
+ self.closed = True
+
+ self._console_input_reader.close()
+
+ def run_in_executor(self, callback):
+ self.loop.run_in_executor(None, callback)
+
+ def call_from_executor(self, callback, _max_postpone_until=None):
+ self.loop.call_soon_threadsafe(callback)
+
+ def add_reader(self, fd, callback):
+ " Start watching the file descriptor for read availability. "
+ self.loop.add_reader(fd, callback)
+
+ def remove_reader(self, fd):
+ " Stop watching the file descriptor for read availability. "
+ self.loop.remove_reader(fd)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/base.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/base.py
new file mode 100644
index 0000000000..db86face66
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/base.py
@@ -0,0 +1,85 @@
+from __future__ import unicode_literals
+from abc import ABCMeta, abstractmethod
+from six import with_metaclass
+
+__all__ = (
+ 'EventLoop',
+ 'INPUT_TIMEOUT',
+)
+
+
+#: When to trigger the `onInputTimeout` event.
+INPUT_TIMEOUT = .5
+
+
+class EventLoop(with_metaclass(ABCMeta, object)):
+ """
+ Eventloop interface.
+ """
+ def run(self, stdin, callbacks):
+ """
+ Run the eventloop until stop() is called. Report all
+ input/timeout/terminal-resize events to the callbacks.
+
+ :param stdin: :class:`~prompt_toolkit.input.Input` instance.
+ :param callbacks: :class:`~prompt_toolkit.eventloop.callbacks.EventLoopCallbacks` instance.
+ """
+ raise NotImplementedError("This eventloop doesn't implement synchronous 'run()'.")
+
+ def run_as_coroutine(self, stdin, callbacks):
+ """
+ Similar to `run`, but this is a coroutine. (For asyncio integration.)
+ """
+ raise NotImplementedError("This eventloop doesn't implement 'run_as_coroutine()'.")
+
+ @abstractmethod
+ def stop(self):
+ """
+ Stop the `run` call. (Normally called by
+ :class:`~prompt_toolkit.interface.CommandLineInterface`, when a result
+ is available, or Abort/Quit has been called.)
+ """
+
+ @abstractmethod
+ def close(self):
+ """
+ Clean up of resources. Eventloop cannot be reused a second time after
+ this call.
+ """
+
+ @abstractmethod
+ def add_reader(self, fd, callback):
+ """
+ Start watching the file descriptor for read availability and then call
+ the callback.
+ """
+
+ @abstractmethod
+ def remove_reader(self, fd):
+ """
+ Stop watching the file descriptor for read availability.
+ """
+
+ @abstractmethod
+ def run_in_executor(self, callback):
+ """
+ Run a long running function in a background thread. (This is
+ recommended for code that could block the event loop.)
+ Similar to Twisted's ``deferToThread``.
+ """
+
+ @abstractmethod
+ def call_from_executor(self, callback, _max_postpone_until=None):
+ """
+ Call this function in the main event loop. Similar to Twisted's
+ ``callFromThread``.
+
+ :param _max_postpone_until: `None` or `time.time` value. For interal
+ use. If the eventloop is saturated, consider this task to be low
+ priority and postpone maximum until this timestamp. (For instance,
+ repaint is done using low priority.)
+
+ Note: In the past, this used to be a datetime.datetime instance,
+ but apparently, executing `time.time` is more efficient: it
+ does fewer system calls. (It doesn't read /etc/localtime.)
+ """
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/callbacks.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/callbacks.py
new file mode 100644
index 0000000000..04adab6fd4
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/callbacks.py
@@ -0,0 +1,29 @@
+from __future__ import unicode_literals
+from abc import ABCMeta, abstractmethod
+from six import with_metaclass
+
+__all__ = (
+ 'EventLoopCallbacks',
+)
+
+
+class EventLoopCallbacks(with_metaclass(ABCMeta, object)):
+ """
+ This is the glue between the :class:`~prompt_toolkit.eventloop.base.EventLoop`
+ and :class:`~prompt_toolkit.interface.CommandLineInterface`.
+
+ :meth:`~prompt_toolkit.eventloop.base.EventLoop.run` takes an
+ :class:`.EventLoopCallbacks` instance and operates on that one, driving the
+ interface.
+ """
+ @abstractmethod
+ def terminal_size_changed(self):
+ pass
+
+ @abstractmethod
+ def input_timeout(self):
+ pass
+
+ @abstractmethod
+ def feed_key(self, key):
+ pass
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/inputhook.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/inputhook.py
new file mode 100644
index 0000000000..bab1f4c003
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/inputhook.py
@@ -0,0 +1,107 @@
+"""
+Similar to `PyOS_InputHook` of the Python API. Some eventloops can have an
+inputhook to allow easy integration with other event loops.
+
+When the eventloop of prompt-toolkit is idle, it can call such a hook. This
+hook can call another eventloop that runs for a short while, for instance to
+keep a graphical user interface responsive.
+
+It's the responsibility of this hook to exit when there is input ready.
+There are two ways to detect when input is ready:
+
+- Call the `input_is_ready` method periodically. Quit when this returns `True`.
+
+- Add the `fileno` as a watch to the external eventloop. Quit when file descriptor
+ becomes readable. (But don't read from it.)
+
+ Note that this is not the same as checking for `sys.stdin.fileno()`. The
+ eventloop of prompt-toolkit allows thread-based executors, for example for
+ asynchronous autocompletion. When the completion for instance is ready, we
+ also want prompt-toolkit to gain control again in order to display that.
+
+An alternative to using input hooks, is to create a custom `EventLoop` class that
+controls everything.
+"""
+from __future__ import unicode_literals
+import os
+import threading
+from prompt_toolkit.utils import is_windows
+from .select import select_fds
+
+__all__ = (
+ 'InputHookContext',
+)
+
+
+class InputHookContext(object):
+ """
+ Given as a parameter to the inputhook.
+ """
+ def __init__(self, inputhook):
+ assert callable(inputhook)
+
+ self.inputhook = inputhook
+ self._input_is_ready = None
+
+ self._r, self._w = os.pipe()
+
+ def input_is_ready(self):
+ """
+ Return True when the input is ready.
+ """
+ return self._input_is_ready(wait=False)
+
+ def fileno(self):
+ """
+ File descriptor that will become ready when the event loop needs to go on.
+ """
+ return self._r
+
+ def call_inputhook(self, input_is_ready_func):
+ """
+ Call the inputhook. (Called by a prompt-toolkit eventloop.)
+ """
+ self._input_is_ready = input_is_ready_func
+
+ # Start thread that activates this pipe when there is input to process.
+ def thread():
+ input_is_ready_func(wait=True)
+ os.write(self._w, b'x')
+
+ threading.Thread(target=thread).start()
+
+ # Call inputhook.
+ self.inputhook(self)
+
+ # Flush the read end of the pipe.
+ try:
+ # Before calling 'os.read', call select.select. This is required
+ # when the gevent monkey patch has been applied. 'os.read' is never
+ # monkey patched and won't be cooperative, so that would block all
+ # other select() calls otherwise.
+ # See: http://www.gevent.org/gevent.os.html
+
+ # Note: On Windows, this is apparently not an issue.
+ # However, if we would ever want to add a select call, it
+ # should use `windll.kernel32.WaitForMultipleObjects`,
+ # because `select.select` can't wait for a pipe on Windows.
+ if not is_windows():
+ select_fds([self._r], timeout=None)
+
+ os.read(self._r, 1024)
+ except OSError:
+ # This happens when the window resizes and a SIGWINCH was received.
+ # We get 'Error: [Errno 4] Interrupted system call'
+ # Just ignore.
+ pass
+ self._input_is_ready = None
+
+ def close(self):
+ """
+ Clean up resources.
+ """
+ if self._r:
+ os.close(self._r)
+ os.close(self._w)
+
+ self._r = self._w = None
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/posix.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/posix.py
new file mode 100644
index 0000000000..f631dbd891
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/posix.py
@@ -0,0 +1,306 @@
+from __future__ import unicode_literals
+import fcntl
+import os
+import signal
+import threading
+import time
+
+from prompt_toolkit.terminal.vt100_input import InputStream
+from prompt_toolkit.utils import DummyContext, in_main_thread
+from prompt_toolkit.input import Input
+from .base import EventLoop, INPUT_TIMEOUT
+from .callbacks import EventLoopCallbacks
+from .inputhook import InputHookContext
+from .posix_utils import PosixStdinReader
+from .utils import TimeIt
+from .select import AutoSelector, Selector, fd_to_int
+
+__all__ = (
+ 'PosixEventLoop',
+)
+
+_now = time.time
+
+
+class PosixEventLoop(EventLoop):
+ """
+ Event loop for posix systems (Linux, Mac os X).
+ """
+ def __init__(self, inputhook=None, selector=AutoSelector):
+ assert inputhook is None or callable(inputhook)
+ assert issubclass(selector, Selector)
+
+ self.running = False
+ self.closed = False
+ self._running = False
+ self._callbacks = None
+
+ self._calls_from_executor = []
+ self._read_fds = {} # Maps fd to handler.
+ self.selector = selector()
+
+ # Create a pipe for inter thread communication.
+ self._schedule_pipe = os.pipe()
+ fcntl.fcntl(self._schedule_pipe[0], fcntl.F_SETFL, os.O_NONBLOCK)
+
+ # Create inputhook context.
+ self._inputhook_context = InputHookContext(inputhook) if inputhook else None
+
+ def run(self, stdin, callbacks):
+ """
+ The input 'event loop'.
+ """
+ assert isinstance(stdin, Input)
+ assert isinstance(callbacks, EventLoopCallbacks)
+ assert not self._running
+
+ if self.closed:
+ raise Exception('Event loop already closed.')
+
+ self._running = True
+ self._callbacks = callbacks
+
+ inputstream = InputStream(callbacks.feed_key)
+ current_timeout = [INPUT_TIMEOUT] # Nonlocal
+
+ # Create reader class.
+ stdin_reader = PosixStdinReader(stdin.fileno())
+
+ # Only attach SIGWINCH signal handler in main thread.
+ # (It's not possible to attach signal handlers in other threads. In
+ # that case we should rely on a the main thread to call this manually
+ # instead.)
+ if in_main_thread():
+ ctx = call_on_sigwinch(self.received_winch)
+ else:
+ ctx = DummyContext()
+
+ def read_from_stdin():
+ " Read user input. "
+ # Feed input text.
+ data = stdin_reader.read()
+ inputstream.feed(data)
+
+ # Set timeout again.
+ current_timeout[0] = INPUT_TIMEOUT
+
+ # Quit when the input stream was closed.
+ if stdin_reader.closed:
+ self.stop()
+
+ self.add_reader(stdin, read_from_stdin)
+ self.add_reader(self._schedule_pipe[0], None)
+
+ with ctx:
+ while self._running:
+ # Call inputhook.
+ if self._inputhook_context:
+ with TimeIt() as inputhook_timer:
+ def ready(wait):
+ " True when there is input ready. The inputhook should return control. "
+ return self._ready_for_reading(current_timeout[0] if wait else 0) != []
+ self._inputhook_context.call_inputhook(ready)
+ inputhook_duration = inputhook_timer.duration
+ else:
+ inputhook_duration = 0
+
+ # Calculate remaining timeout. (The inputhook consumed some of the time.)
+ if current_timeout[0] is None:
+ remaining_timeout = None
+ else:
+ remaining_timeout = max(0, current_timeout[0] - inputhook_duration)
+
+ # Wait until input is ready.
+ fds = self._ready_for_reading(remaining_timeout)
+
+ # When any of the FDs are ready. Call the appropriate callback.
+ if fds:
+ # Create lists of high/low priority tasks. The main reason
+ # for this is to allow painting the UI to happen as soon as
+ # possible, but when there are many events happening, we
+ # don't want to call the UI renderer 1000x per second. If
+ # the eventloop is completely saturated with many CPU
+ # intensive tasks (like processing input/output), we say
+ # that drawing the UI can be postponed a little, to make
+ # CPU available. This will be a low priority task in that
+ # case.
+ tasks = []
+ low_priority_tasks = []
+ now = None # Lazy load time. (Fewer system calls.)
+
+ for fd in fds:
+ # For the 'call_from_executor' fd, put each pending
+ # item on either the high or low priority queue.
+ if fd == self._schedule_pipe[0]:
+ for c, max_postpone_until in self._calls_from_executor:
+ if max_postpone_until is None:
+ # Execute now.
+ tasks.append(c)
+ else:
+ # Execute soon, if `max_postpone_until` is in the future.
+ now = now or _now()
+ if max_postpone_until < now:
+ tasks.append(c)
+ else:
+ low_priority_tasks.append((c, max_postpone_until))
+ self._calls_from_executor = []
+
+ # Flush all the pipe content.
+ os.read(self._schedule_pipe[0], 1024)
+ else:
+ handler = self._read_fds.get(fd)
+ if handler:
+ tasks.append(handler)
+
+ # When there are high priority tasks, run all these.
+ # Schedule low priority tasks for the next iteration.
+ if tasks:
+ for t in tasks:
+ t()
+
+ # Postpone low priority tasks.
+ for t, max_postpone_until in low_priority_tasks:
+ self.call_from_executor(t, _max_postpone_until=max_postpone_until)
+ else:
+ # Currently there are only low priority tasks -> run them right now.
+ for t, _ in low_priority_tasks:
+ t()
+
+ else:
+ # Flush all pending keys on a timeout. (This is most
+ # important to flush the vt100 'Escape' key early when
+ # nothing else follows.)
+ inputstream.flush()
+
+ # Fire input timeout event.
+ callbacks.input_timeout()
+ current_timeout[0] = None
+
+ self.remove_reader(stdin)
+ self.remove_reader(self._schedule_pipe[0])
+
+ self._callbacks = None
+
+ def _ready_for_reading(self, timeout=None):
+ """
+ Return the file descriptors that are ready for reading.
+ """
+ fds = self.selector.select(timeout)
+ return fds
+
+ def received_winch(self):
+ """
+ Notify the event loop that SIGWINCH has been received
+ """
+ # Process signal asynchronously, because this handler can write to the
+ # output, and doing this inside the signal handler causes easily
+ # reentrant calls, giving runtime errors..
+
+ # Furthur, this has to be thread safe. When the CommandLineInterface
+ # runs not in the main thread, this function still has to be called
+ # from the main thread. (The only place where we can install signal
+ # handlers.)
+ def process_winch():
+ if self._callbacks:
+ self._callbacks.terminal_size_changed()
+
+ self.call_from_executor(process_winch)
+
+ def run_in_executor(self, callback):
+ """
+ Run a long running function in a background thread.
+ (This is recommended for code that could block the event loop.)
+ Similar to Twisted's ``deferToThread``.
+ """
+ # Wait until the main thread is idle.
+ # We start the thread by using `call_from_executor`. The event loop
+ # favours processing input over `calls_from_executor`, so the thread
+ # will not start until there is no more input to process and the main
+ # thread becomes idle for an instant. This is good, because Python
+ # threading favours CPU over I/O -- an autocompletion thread in the
+ # background would cause a significantly slow down of the main thread.
+ # It is mostly noticable when pasting large portions of text while
+ # having real time autocompletion while typing on.
+ def start_executor():
+ threading.Thread(target=callback).start()
+ self.call_from_executor(start_executor)
+
+ def call_from_executor(self, callback, _max_postpone_until=None):
+ """
+ Call this function in the main event loop.
+ Similar to Twisted's ``callFromThread``.
+
+ :param _max_postpone_until: `None` or `time.time` value. For interal
+ use. If the eventloop is saturated, consider this task to be low
+ priority and postpone maximum until this timestamp. (For instance,
+ repaint is done using low priority.)
+ """
+ assert _max_postpone_until is None or isinstance(_max_postpone_until, float)
+ self._calls_from_executor.append((callback, _max_postpone_until))
+
+ if self._schedule_pipe:
+ try:
+ os.write(self._schedule_pipe[1], b'x')
+ except (AttributeError, IndexError, OSError):
+ # Handle race condition. We're in a different thread.
+ # - `_schedule_pipe` could have become None in the meantime.
+ # - We catch `OSError` (actually BrokenPipeError), because the
+ # main thread could have closed the pipe already.
+ pass
+
+ def stop(self):
+ """
+ Stop the event loop.
+ """
+ self._running = False
+
+ def close(self):
+ self.closed = True
+
+ # Close pipes.
+ schedule_pipe = self._schedule_pipe
+ self._schedule_pipe = None
+
+ if schedule_pipe:
+ os.close(schedule_pipe[0])
+ os.close(schedule_pipe[1])
+
+ if self._inputhook_context:
+ self._inputhook_context.close()
+
+ def add_reader(self, fd, callback):
+ " Add read file descriptor to the event loop. "
+ fd = fd_to_int(fd)
+ self._read_fds[fd] = callback
+ self.selector.register(fd)
+
+ def remove_reader(self, fd):
+ " Remove read file descriptor from the event loop. "
+ fd = fd_to_int(fd)
+
+ if fd in self._read_fds:
+ del self._read_fds[fd]
+
+ self.selector.unregister(fd)
+
+
+class call_on_sigwinch(object):
+ """
+ Context manager which Installs a SIGWINCH callback.
+ (This signal occurs when the terminal size changes.)
+ """
+ def __init__(self, callback):
+ self.callback = callback
+ self.previous_callback = None
+
+ def __enter__(self):
+ self.previous_callback = signal.signal(signal.SIGWINCH, lambda *a: self.callback())
+
+ def __exit__(self, *a, **kw):
+ if self.previous_callback is None:
+ # Normally, `signal.signal` should never return `None`.
+ # For some reason it happens here:
+ # https://github.com/jonathanslenders/python-prompt-toolkit/pull/174
+ signal.signal(signal.SIGWINCH, 0)
+ else:
+ signal.signal(signal.SIGWINCH, self.previous_callback)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/posix_utils.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/posix_utils.py
new file mode 100644
index 0000000000..320df438ca
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/posix_utils.py
@@ -0,0 +1,82 @@
+from __future__ import unicode_literals
+
+from codecs import getincrementaldecoder
+import os
+import six
+
+__all__ = (
+ 'PosixStdinReader',
+)
+
+
+class PosixStdinReader(object):
+ """
+ Wrapper around stdin which reads (nonblocking) the next available 1024
+ bytes and decodes it.
+
+ Note that you can't be sure that the input file is closed if the ``read``
+ function returns an empty string. When ``errors=ignore`` is passed,
+ ``read`` can return an empty string if all malformed input was replaced by
+ an empty string. (We can't block here and wait for more input.) So, because
+ of that, check the ``closed`` attribute, to be sure that the file has been
+ closed.
+
+ :param stdin_fd: File descriptor from which we read.
+ :param errors: Can be 'ignore', 'strict' or 'replace'.
+ On Python3, this can be 'surrogateescape', which is the default.
+
+ 'surrogateescape' is preferred, because this allows us to transfer
+ unrecognised bytes to the key bindings. Some terminals, like lxterminal
+ and Guake, use the 'Mxx' notation to send mouse events, where each 'x'
+ can be any possible byte.
+ """
+ # By default, we want to 'ignore' errors here. The input stream can be full
+ # of junk. One occurrence of this that I had was when using iTerm2 on OS X,
+ # with "Option as Meta" checked (You should choose "Option as +Esc".)
+
+ def __init__(self, stdin_fd,
+ errors=('ignore' if six.PY2 else 'surrogateescape')):
+ assert isinstance(stdin_fd, int)
+ self.stdin_fd = stdin_fd
+ self.errors = errors
+
+ # Create incremental decoder for decoding stdin.
+ # We can not just do `os.read(stdin.fileno(), 1024).decode('utf-8')`, because
+ # it could be that we are in the middle of a utf-8 byte sequence.
+ self._stdin_decoder_cls = getincrementaldecoder('utf-8')
+ self._stdin_decoder = self._stdin_decoder_cls(errors=errors)
+
+ #: True when there is nothing anymore to read.
+ self.closed = False
+
+ def read(self, count=1024):
+ # By default we choose a rather small chunk size, because reading
+ # big amounts of input at once, causes the event loop to process
+ # all these key bindings also at once without going back to the
+ # loop. This will make the application feel unresponsive.
+ """
+ Read the input and return it as a string.
+
+ Return the text. Note that this can return an empty string, even when
+ the input stream was not yet closed. This means that something went
+ wrong during the decoding.
+ """
+ if self.closed:
+ return b''
+
+ # Note: the following works better than wrapping `self.stdin` like
+ # `codecs.getreader('utf-8')(stdin)` and doing `read(1)`.
+ # Somehow that causes some latency when the escape
+ # character is pressed. (Especially on combination with the `select`.)
+ try:
+ data = os.read(self.stdin_fd, count)
+
+ # Nothing more to read, stream is closed.
+ if data == b'':
+ self.closed = True
+ return ''
+ except OSError:
+ # In case of SIGWINCH
+ data = b''
+
+ return self._stdin_decoder.decode(data)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/select.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/select.py
new file mode 100644
index 0000000000..f678f84c55
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/select.py
@@ -0,0 +1,216 @@
+"""
+Selectors for the Posix event loop.
+"""
+from __future__ import unicode_literals, absolute_import
+import sys
+import abc
+import errno
+import select
+import six
+
+__all__ = (
+ 'AutoSelector',
+ 'PollSelector',
+ 'SelectSelector',
+ 'Selector',
+ 'fd_to_int',
+)
+
+def fd_to_int(fd):
+ assert isinstance(fd, int) or hasattr(fd, 'fileno')
+
+ if isinstance(fd, int):
+ return fd
+ else:
+ return fd.fileno()
+
+
+class Selector(six.with_metaclass(abc.ABCMeta, object)):
+ @abc.abstractmethod
+ def register(self, fd):
+ assert isinstance(fd, int)
+
+ @abc.abstractmethod
+ def unregister(self, fd):
+ assert isinstance(fd, int)
+
+ @abc.abstractmethod
+ def select(self, timeout):
+ pass
+
+ @abc.abstractmethod
+ def close(self):
+ pass
+
+
+class AutoSelector(Selector):
+ def __init__(self):
+ self._fds = []
+
+ self._select_selector = SelectSelector()
+ self._selectors = [self._select_selector]
+
+ # When 'select.poll' exists, create a PollSelector.
+ if hasattr(select, 'poll'):
+ self._poll_selector = PollSelector()
+ self._selectors.append(self._poll_selector)
+ else:
+ self._poll_selector = None
+
+ # Use of the 'select' module, that was introduced in Python3.4. We don't
+ # use it before 3.5 however, because this is the point where this module
+ # retries interrupted system calls.
+ if sys.version_info >= (3, 5):
+ self._py3_selector = Python3Selector()
+ self._selectors.append(self._py3_selector)
+ else:
+ self._py3_selector = None
+
+ def register(self, fd):
+ assert isinstance(fd, int)
+
+ self._fds.append(fd)
+
+ for sel in self._selectors:
+ sel.register(fd)
+
+ def unregister(self, fd):
+ assert isinstance(fd, int)
+
+ self._fds.remove(fd)
+
+ for sel in self._selectors:
+ sel.unregister(fd)
+
+ def select(self, timeout):
+ # Try Python 3 selector first.
+ if self._py3_selector:
+ try:
+ return self._py3_selector.select(timeout)
+ except PermissionError:
+ # We had a situation (in pypager) where epoll raised a
+ # PermissionError when a local file descriptor was registered,
+ # however poll and select worked fine. So, in that case, just
+ # try using select below.
+ pass
+
+ try:
+ # Prefer 'select.select', if we don't have much file descriptors.
+ # This is more universal.
+ return self._select_selector.select(timeout)
+ except ValueError:
+ # When we have more than 1024 open file descriptors, we'll always
+ # get a "ValueError: filedescriptor out of range in select()" for
+ # 'select'. In this case, try, using 'poll' instead.
+ if self._poll_selector is not None:
+ return self._poll_selector.select(timeout)
+ else:
+ raise
+
+ def close(self):
+ for sel in self._selectors:
+ sel.close()
+
+
+class Python3Selector(Selector):
+ """
+ Use of the Python3 'selectors' module.
+
+ NOTE: Only use on Python 3.5 or newer!
+ """
+ def __init__(self):
+ assert sys.version_info >= (3, 5)
+
+ import selectors # Inline import: Python3 only!
+ self._sel = selectors.DefaultSelector()
+
+ def register(self, fd):
+ assert isinstance(fd, int)
+ import selectors # Inline import: Python3 only!
+ self._sel.register(fd, selectors.EVENT_READ, None)
+
+ def unregister(self, fd):
+ assert isinstance(fd, int)
+ self._sel.unregister(fd)
+
+ def select(self, timeout):
+ events = self._sel.select(timeout=timeout)
+ return [key.fileobj for key, mask in events]
+
+ def close(self):
+ self._sel.close()
+
+
+class PollSelector(Selector):
+ def __init__(self):
+ self._poll = select.poll()
+
+ def register(self, fd):
+ assert isinstance(fd, int)
+ self._poll.register(fd, select.POLLIN)
+
+ def unregister(self, fd):
+ assert isinstance(fd, int)
+
+ def select(self, timeout):
+ tuples = self._poll.poll(timeout) # Returns (fd, event) tuples.
+ return [t[0] for t in tuples]
+
+ def close(self):
+ pass # XXX
+
+
+class SelectSelector(Selector):
+ """
+ Wrapper around select.select.
+
+ When the SIGWINCH signal is handled, other system calls, like select
+ are aborted in Python. This wrapper will retry the system call.
+ """
+ def __init__(self):
+ self._fds = []
+
+ def register(self, fd):
+ self._fds.append(fd)
+
+ def unregister(self, fd):
+ self._fds.remove(fd)
+
+ def select(self, timeout):
+ while True:
+ try:
+ return select.select(self._fds, [], [], timeout)[0]
+ except select.error as e:
+ # Retry select call when EINTR
+ if e.args and e.args[0] == errno.EINTR:
+ continue
+ else:
+ raise
+
+ def close(self):
+ pass
+
+
+def select_fds(read_fds, timeout, selector=AutoSelector):
+ """
+ Wait for a list of file descriptors (`read_fds`) to become ready for
+ reading. This chooses the most appropriate select-tool for use in
+ prompt-toolkit.
+ """
+ # Map to ensure that we return the objects that were passed in originally.
+ # Whether they are a fd integer or an object that has a fileno().
+ # (The 'poll' implementation for instance, returns always integers.)
+ fd_map = dict((fd_to_int(fd), fd) for fd in read_fds)
+
+ # Wait, using selector.
+ sel = selector()
+ try:
+ for fd in read_fds:
+ sel.register(fd)
+
+ result = sel.select(timeout)
+
+ if result is not None:
+ return [fd_map[fd_to_int(fd)] for fd in result]
+ finally:
+ sel.close()
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/utils.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/utils.py
new file mode 100644
index 0000000000..ff3a4cfd69
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/utils.py
@@ -0,0 +1,23 @@
+from __future__ import unicode_literals
+import time
+
+__all__ = (
+ 'TimeIt',
+)
+
+
+class TimeIt(object):
+ """
+ Context manager that times the duration of the code body.
+ The `duration` attribute will contain the execution time in seconds.
+ """
+ def __init__(self):
+ self.duration = None
+
+ def __enter__(self):
+ self.start = time.time()
+ return self
+
+ def __exit__(self, *args):
+ self.end = time.time()
+ self.duration = self.end - self.start
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/win32.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/win32.py
new file mode 100644
index 0000000000..18e356f088
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/win32.py
@@ -0,0 +1,187 @@
+"""
+Win32 event loop.
+
+Windows notes:
+ - Somehow it doesn't seem to work with the 'ProactorEventLoop'.
+"""
+from __future__ import unicode_literals
+
+from ..terminal.win32_input import ConsoleInputReader
+from ..win32_types import SECURITY_ATTRIBUTES
+from .base import EventLoop, INPUT_TIMEOUT
+from .inputhook import InputHookContext
+from .utils import TimeIt
+
+from ctypes import windll, pointer
+from ctypes.wintypes import DWORD, BOOL, HANDLE
+
+import msvcrt
+import threading
+
+__all__ = (
+ 'Win32EventLoop',
+)
+
+WAIT_TIMEOUT = 0x00000102
+INPUT_TIMEOUT_MS = int(1000 * INPUT_TIMEOUT)
+
+
+class Win32EventLoop(EventLoop):
+ """
+ Event loop for Windows systems.
+
+ :param recognize_paste: When True, try to discover paste actions and turn
+ the event into a BracketedPaste.
+ """
+ def __init__(self, inputhook=None, recognize_paste=True):
+ assert inputhook is None or callable(inputhook)
+
+ self._event = HANDLE(_create_event())
+ self._console_input_reader = ConsoleInputReader(recognize_paste=recognize_paste)
+ self._calls_from_executor = []
+
+ self.closed = False
+ self._running = False
+
+ # Additional readers.
+ self._read_fds = {} # Maps fd to handler.
+
+ # Create inputhook context.
+ self._inputhook_context = InputHookContext(inputhook) if inputhook else None
+
+ def run(self, stdin, callbacks):
+ if self.closed:
+ raise Exception('Event loop already closed.')
+
+ current_timeout = INPUT_TIMEOUT_MS
+ self._running = True
+
+ while self._running:
+ # Call inputhook.
+ with TimeIt() as inputhook_timer:
+ if self._inputhook_context:
+ def ready(wait):
+ " True when there is input ready. The inputhook should return control. "
+ return bool(self._ready_for_reading(current_timeout if wait else 0))
+ self._inputhook_context.call_inputhook(ready)
+
+ # Calculate remaining timeout. (The inputhook consumed some of the time.)
+ if current_timeout == -1:
+ remaining_timeout = -1
+ else:
+ remaining_timeout = max(0, current_timeout - int(1000 * inputhook_timer.duration))
+
+ # Wait for the next event.
+ handle = self._ready_for_reading(remaining_timeout)
+
+ if handle == self._console_input_reader.handle.value:
+ # When stdin is ready, read input and reset timeout timer.
+ keys = self._console_input_reader.read()
+ for k in keys:
+ callbacks.feed_key(k)
+ current_timeout = INPUT_TIMEOUT_MS
+
+ elif handle == self._event.value:
+ # When the Windows Event has been trigger, process the messages in the queue.
+ windll.kernel32.ResetEvent(self._event)
+ self._process_queued_calls_from_executor()
+
+ elif handle in self._read_fds:
+ callback = self._read_fds[handle]
+ callback()
+ else:
+ # Fire input timeout event.
+ callbacks.input_timeout()
+ current_timeout = -1
+
+ def _ready_for_reading(self, timeout=None):
+ """
+ Return the handle that is ready for reading or `None` on timeout.
+ """
+ handles = [self._event, self._console_input_reader.handle]
+ handles.extend(self._read_fds.keys())
+ return _wait_for_handles(handles, timeout)
+
+ def stop(self):
+ self._running = False
+
+ def close(self):
+ self.closed = True
+
+ # Clean up Event object.
+ windll.kernel32.CloseHandle(self._event)
+
+ if self._inputhook_context:
+ self._inputhook_context.close()
+
+ self._console_input_reader.close()
+
+ def run_in_executor(self, callback):
+ """
+ Run a long running function in a background thread.
+ (This is recommended for code that could block the event loop.)
+ Similar to Twisted's ``deferToThread``.
+ """
+ # Wait until the main thread is idle for an instant before starting the
+ # executor. (Like in eventloop/posix.py, we start the executor using
+ # `call_from_executor`.)
+ def start_executor():
+ threading.Thread(target=callback).start()
+ self.call_from_executor(start_executor)
+
+ def call_from_executor(self, callback, _max_postpone_until=None):
+ """
+ Call this function in the main event loop.
+ Similar to Twisted's ``callFromThread``.
+ """
+ # Append to list of pending callbacks.
+ self._calls_from_executor.append(callback)
+
+ # Set Windows event.
+ windll.kernel32.SetEvent(self._event)
+
+ def _process_queued_calls_from_executor(self):
+ # Process calls from executor.
+ calls_from_executor, self._calls_from_executor = self._calls_from_executor, []
+ for c in calls_from_executor:
+ c()
+
+ def add_reader(self, fd, callback):
+ " Start watching the file descriptor for read availability. "
+ h = msvcrt.get_osfhandle(fd)
+ self._read_fds[h] = callback
+
+ def remove_reader(self, fd):
+ " Stop watching the file descriptor for read availability. "
+ h = msvcrt.get_osfhandle(fd)
+ if h in self._read_fds:
+ del self._read_fds[h]
+
+
+def _wait_for_handles(handles, timeout=-1):
+ """
+ Waits for multiple handles. (Similar to 'select') Returns the handle which is ready.
+ Returns `None` on timeout.
+
+ http://msdn.microsoft.com/en-us/library/windows/desktop/ms687025(v=vs.85).aspx
+ """
+ arrtype = HANDLE * len(handles)
+ handle_array = arrtype(*handles)
+
+ ret = windll.kernel32.WaitForMultipleObjects(
+ len(handle_array), handle_array, BOOL(False), DWORD(timeout))
+
+ if ret == WAIT_TIMEOUT:
+ return None
+ else:
+ h = handle_array[ret]
+ return h
+
+
+def _create_event():
+ """
+ Creates a Win32 unnamed Event .
+
+ http://msdn.microsoft.com/en-us/library/windows/desktop/ms682396(v=vs.85).aspx
+ """
+ return windll.kernel32.CreateEventA(pointer(SECURITY_ATTRIBUTES()), BOOL(True), BOOL(False), None)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/filters/__init__.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/filters/__init__.py
new file mode 100644
index 0000000000..d3f14efc1c
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/filters/__init__.py
@@ -0,0 +1,36 @@
+"""
+Filters decide whether something is active or not (they decide about a boolean
+state). This is used to enable/disable features, like key bindings, parts of
+the layout and other stuff. For instance, we could have a `HasSearch` filter
+attached to some part of the layout, in order to show that part of the user
+interface only while the user is searching.
+
+Filters are made to avoid having to attach callbacks to all event in order to
+propagate state. However, they are lazy, they don't automatically propagate the
+state of what they are observing. Only when a filter is called (it's actually a
+callable), it will calculate its value. So, its not really reactive
+programming, but it's made to fit for this framework.
+
+One class of filters observe a `CommandLineInterface` instance. However, they
+are not attached to such an instance. (We have to pass this instance to the
+filter when calling it.) The reason for this is to allow declarative
+programming: for key bindings, we can attach a filter to a key binding without
+knowing yet which `CommandLineInterface` instance it will observe in the end.
+Examples are `HasSearch` or `IsExiting`.
+
+Another class of filters doesn't take anything as input. And a third class of
+filters are universal, for instance `Always` and `Never`.
+It is impossible to mix the first and the second class, because that would mean
+mixing filters with a different signature.
+
+Filters can be chained using ``&`` and ``|`` operations, and inverted using the
+``~`` operator, for instance::
+
+ filter = HasFocus('default') & ~ HasSelection()
+"""
+from __future__ import unicode_literals
+
+from .base import *
+from .cli import *
+from .types import *
+from .utils import *
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/filters/base.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/filters/base.py
new file mode 100644
index 0000000000..6a1a1d0b10
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/filters/base.py
@@ -0,0 +1,234 @@
+from __future__ import unicode_literals
+from abc import ABCMeta, abstractmethod
+from six import with_metaclass
+
+from prompt_toolkit.utils import test_callable_args
+
+
+__all__ = (
+ 'Filter',
+ 'Never',
+ 'Always',
+ 'Condition',
+)
+
+
+class Filter(with_metaclass(ABCMeta, object)):
+ """
+ Filter to activate/deactivate a feature, depending on a condition.
+ The return value of ``__call__`` will tell if the feature should be active.
+ """
+ @abstractmethod
+ def __call__(self, *a, **kw):
+ """
+ The actual call to evaluate the filter.
+ """
+ return True
+
+ def __and__(self, other):
+ """
+ Chaining of filters using the & operator.
+ """
+ return _and_cache[self, other]
+
+ def __or__(self, other):
+ """
+ Chaining of filters using the | operator.
+ """
+ return _or_cache[self, other]
+
+ def __invert__(self):
+ """
+ Inverting of filters using the ~ operator.
+ """
+ return _invert_cache[self]
+
+ def __bool__(self):
+ """
+ By purpose, we don't allow bool(...) operations directly on a filter,
+ because because the meaning is ambigue.
+
+ Executing a filter has to be done always by calling it. Providing
+ defaults for `None` values should be done through an `is None` check
+ instead of for instance ``filter1 or Always()``.
+ """
+ raise TypeError
+
+ __nonzero__ = __bool__ # For Python 2.
+
+ def test_args(self, *args):
+ """
+ Test whether this filter can be called with the following argument list.
+ """
+ return test_callable_args(self.__call__, args)
+
+
+class _AndCache(dict):
+ """
+ Cache for And operation between filters.
+ (Filter classes are stateless, so we can reuse them.)
+
+ Note: This could be a memory leak if we keep creating filters at runtime.
+ If that is True, the filters should be weakreffed (not the tuple of
+ filters), and tuples should be removed when one of these filters is
+ removed. In practise however, there is a finite amount of filters.
+ """
+ def __missing__(self, filters):
+ a, b = filters
+ assert isinstance(b, Filter), 'Expecting filter, got %r' % b
+
+ if isinstance(b, Always) or isinstance(a, Never):
+ return a
+ elif isinstance(b, Never) or isinstance(a, Always):
+ return b
+
+ result = _AndList(filters)
+ self[filters] = result
+ return result
+
+
+class _OrCache(dict):
+ """ Cache for Or operation between filters. """
+ def __missing__(self, filters):
+ a, b = filters
+ assert isinstance(b, Filter), 'Expecting filter, got %r' % b
+
+ if isinstance(b, Always) or isinstance(a, Never):
+ return b
+ elif isinstance(b, Never) or isinstance(a, Always):
+ return a
+
+ result = _OrList(filters)
+ self[filters] = result
+ return result
+
+
+class _InvertCache(dict):
+ """ Cache for inversion operator. """
+ def __missing__(self, filter):
+ result = _Invert(filter)
+ self[filter] = result
+ return result
+
+
+_and_cache = _AndCache()
+_or_cache = _OrCache()
+_invert_cache = _InvertCache()
+
+
+class _AndList(Filter):
+ """
+ Result of &-operation between several filters.
+ """
+ def __init__(self, filters):
+ all_filters = []
+
+ for f in filters:
+ if isinstance(f, _AndList): # Turn nested _AndLists into one.
+ all_filters.extend(f.filters)
+ else:
+ all_filters.append(f)
+
+ self.filters = all_filters
+
+ def test_args(self, *args):
+ return all(f.test_args(*args) for f in self.filters)
+
+ def __call__(self, *a, **kw):
+ return all(f(*a, **kw) for f in self.filters)
+
+ def __repr__(self):
+ return '&'.join(repr(f) for f in self.filters)
+
+
+class _OrList(Filter):
+ """
+ Result of |-operation between several filters.
+ """
+ def __init__(self, filters):
+ all_filters = []
+
+ for f in filters:
+ if isinstance(f, _OrList): # Turn nested _OrLists into one.
+ all_filters.extend(f.filters)
+ else:
+ all_filters.append(f)
+
+ self.filters = all_filters
+
+ def test_args(self, *args):
+ return all(f.test_args(*args) for f in self.filters)
+
+ def __call__(self, *a, **kw):
+ return any(f(*a, **kw) for f in self.filters)
+
+ def __repr__(self):
+ return '|'.join(repr(f) for f in self.filters)
+
+
+class _Invert(Filter):
+ """
+ Negation of another filter.
+ """
+ def __init__(self, filter):
+ self.filter = filter
+
+ def __call__(self, *a, **kw):
+ return not self.filter(*a, **kw)
+
+ def __repr__(self):
+ return '~%r' % self.filter
+
+ def test_args(self, *args):
+ return self.filter.test_args(*args)
+
+
+class Always(Filter):
+ """
+ Always enable feature.
+ """
+ def __call__(self, *a, **kw):
+ return True
+
+ def __invert__(self):
+ return Never()
+
+
+class Never(Filter):
+ """
+ Never enable feature.
+ """
+ def __call__(self, *a, **kw):
+ return False
+
+ def __invert__(self):
+ return Always()
+
+
+class Condition(Filter):
+ """
+ Turn any callable (which takes a cli and returns a boolean) into a Filter.
+
+ This can be used as a decorator::
+
+ @Condition
+ def feature_is_active(cli): # `feature_is_active` becomes a Filter.
+ return True
+
+ :param func: Callable which takes either a
+ :class:`~prompt_toolkit.interface.CommandLineInterface` or nothing and
+ returns a boolean. (Depending on what it takes, this will become a
+ :class:`.Filter` or :class:`~prompt_toolkit.filters.CLIFilter`.)
+ """
+ def __init__(self, func):
+ assert callable(func)
+ self.func = func
+
+ def __call__(self, *a, **kw):
+ return self.func(*a, **kw)
+
+ def __repr__(self):
+ return 'Condition(%r)' % self.func
+
+ def test_args(self, *a):
+ return test_callable_args(self.func, a)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/filters/cli.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/filters/cli.py
new file mode 100644
index 0000000000..c0b07317be
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/filters/cli.py
@@ -0,0 +1,395 @@
+"""
+Filters that accept a `CommandLineInterface` as argument.
+"""
+from __future__ import unicode_literals
+from .base import Filter
+from prompt_toolkit.enums import EditingMode
+from prompt_toolkit.key_binding.vi_state import InputMode as ViInputMode
+from prompt_toolkit.cache import memoized
+
+__all__ = (
+ 'HasArg',
+ 'HasCompletions',
+ 'HasFocus',
+ 'InFocusStack',
+ 'HasSearch',
+ 'HasSelection',
+ 'HasValidationError',
+ 'IsAborting',
+ 'IsDone',
+ 'IsMultiline',
+ 'IsReadOnly',
+ 'IsReturning',
+ 'RendererHeightIsKnown',
+ 'InEditingMode',
+
+ # Vi modes.
+ 'ViMode',
+ 'ViNavigationMode',
+ 'ViInsertMode',
+ 'ViInsertMultipleMode',
+ 'ViReplaceMode',
+ 'ViSelectionMode',
+ 'ViWaitingForTextObjectMode',
+ 'ViDigraphMode',
+
+ # Emacs modes.
+ 'EmacsMode',
+ 'EmacsInsertMode',
+ 'EmacsSelectionMode',
+)
+
+
+@memoized()
+class HasFocus(Filter):
+ """
+ Enable when this buffer has the focus.
+ """
+ def __init__(self, buffer_name):
+ self._buffer_name = buffer_name
+
+ @property
+ def buffer_name(self):
+ " The given buffer name. (Read-only) "
+ return self._buffer_name
+
+ def __call__(self, cli):
+ return cli.current_buffer_name == self.buffer_name
+
+ def __repr__(self):
+ return 'HasFocus(%r)' % self.buffer_name
+
+
+@memoized()
+class InFocusStack(Filter):
+ """
+ Enable when this buffer appears on the focus stack.
+ """
+ def __init__(self, buffer_name):
+ self._buffer_name = buffer_name
+
+ @property
+ def buffer_name(self):
+ " The given buffer name. (Read-only) "
+ return self._buffer_name
+
+ def __call__(self, cli):
+ return self.buffer_name in cli.buffers.focus_stack
+
+ def __repr__(self):
+ return 'InFocusStack(%r)' % self.buffer_name
+
+
+@memoized()
+class HasSelection(Filter):
+ """
+ Enable when the current buffer has a selection.
+ """
+ def __call__(self, cli):
+ return bool(cli.current_buffer.selection_state)
+
+ def __repr__(self):
+ return 'HasSelection()'
+
+
+@memoized()
+class HasCompletions(Filter):
+ """
+ Enable when the current buffer has completions.
+ """
+ def __call__(self, cli):
+ return cli.current_buffer.complete_state is not None
+
+ def __repr__(self):
+ return 'HasCompletions()'
+
+
+@memoized()
+class IsMultiline(Filter):
+ """
+ Enable in multiline mode.
+ """
+ def __call__(self, cli):
+ return cli.current_buffer.is_multiline()
+
+ def __repr__(self):
+ return 'IsMultiline()'
+
+
+@memoized()
+class IsReadOnly(Filter):
+ """
+ True when the current buffer is read only.
+ """
+ def __call__(self, cli):
+ return cli.current_buffer.read_only()
+
+ def __repr__(self):
+ return 'IsReadOnly()'
+
+
+@memoized()
+class HasValidationError(Filter):
+ """
+ Current buffer has validation error.
+ """
+ def __call__(self, cli):
+ return cli.current_buffer.validation_error is not None
+
+ def __repr__(self):
+ return 'HasValidationError()'
+
+
+@memoized()
+class HasArg(Filter):
+ """
+ Enable when the input processor has an 'arg'.
+ """
+ def __call__(self, cli):
+ return cli.input_processor.arg is not None
+
+ def __repr__(self):
+ return 'HasArg()'
+
+
+@memoized()
+class HasSearch(Filter):
+ """
+ Incremental search is active.
+ """
+ def __call__(self, cli):
+ return cli.is_searching
+
+ def __repr__(self):
+ return 'HasSearch()'
+
+
+@memoized()
+class IsReturning(Filter):
+ """
+ When a return value has been set.
+ """
+ def __call__(self, cli):
+ return cli.is_returning
+
+ def __repr__(self):
+ return 'IsReturning()'
+
+
+@memoized()
+class IsAborting(Filter):
+ """
+ True when aborting. (E.g. Control-C pressed.)
+ """
+ def __call__(self, cli):
+ return cli.is_aborting
+
+ def __repr__(self):
+ return 'IsAborting()'
+
+
+@memoized()
+class IsExiting(Filter):
+ """
+ True when exiting. (E.g. Control-D pressed.)
+ """
+ def __call__(self, cli):
+ return cli.is_exiting
+
+ def __repr__(self):
+ return 'IsExiting()'
+
+
+@memoized()
+class IsDone(Filter):
+ """
+ True when the CLI is returning, aborting or exiting.
+ """
+ def __call__(self, cli):
+ return cli.is_done
+
+ def __repr__(self):
+ return 'IsDone()'
+
+
+@memoized()
+class RendererHeightIsKnown(Filter):
+ """
+ Only True when the renderer knows it's real height.
+
+ (On VT100 terminals, we have to wait for a CPR response, before we can be
+ sure of the available height between the cursor position and the bottom of
+ the terminal. And usually it's nicer to wait with drawing bottom toolbars
+ until we receive the height, in order to avoid flickering -- first drawing
+ somewhere in the middle, and then again at the bottom.)
+ """
+ def __call__(self, cli):
+ return cli.renderer.height_is_known
+
+ def __repr__(self):
+ return 'RendererHeightIsKnown()'
+
+
+@memoized()
+class InEditingMode(Filter):
+ """
+ Check whether a given editing mode is active. (Vi or Emacs.)
+ """
+ def __init__(self, editing_mode):
+ self._editing_mode = editing_mode
+
+ @property
+ def editing_mode(self):
+ " The given editing mode. (Read-only) "
+ return self._editing_mode
+
+ def __call__(self, cli):
+ return cli.editing_mode == self.editing_mode
+
+ def __repr__(self):
+ return 'InEditingMode(%r)' % (self.editing_mode, )
+
+
+@memoized()
+class ViMode(Filter):
+ def __call__(self, cli):
+ return cli.editing_mode == EditingMode.VI
+
+ def __repr__(self):
+ return 'ViMode()'
+
+
+@memoized()
+class ViNavigationMode(Filter):
+ """
+ Active when the set for Vi navigation key bindings are active.
+ """
+ def __call__(self, cli):
+ if (cli.editing_mode != EditingMode.VI
+ or cli.vi_state.operator_func
+ or cli.vi_state.waiting_for_digraph
+ or cli.current_buffer.selection_state):
+ return False
+
+ return (cli.vi_state.input_mode == ViInputMode.NAVIGATION or
+ cli.current_buffer.read_only())
+
+ def __repr__(self):
+ return 'ViNavigationMode()'
+
+
+@memoized()
+class ViInsertMode(Filter):
+ def __call__(self, cli):
+ if (cli.editing_mode != EditingMode.VI
+ or cli.vi_state.operator_func
+ or cli.vi_state.waiting_for_digraph
+ or cli.current_buffer.selection_state
+ or cli.current_buffer.read_only()):
+ return False
+
+ return cli.vi_state.input_mode == ViInputMode.INSERT
+
+ def __repr__(self):
+ return 'ViInputMode()'
+
+
+@memoized()
+class ViInsertMultipleMode(Filter):
+ def __call__(self, cli):
+ if (cli.editing_mode != EditingMode.VI
+ or cli.vi_state.operator_func
+ or cli.vi_state.waiting_for_digraph
+ or cli.current_buffer.selection_state
+ or cli.current_buffer.read_only()):
+ return False
+
+ return cli.vi_state.input_mode == ViInputMode.INSERT_MULTIPLE
+
+ def __repr__(self):
+ return 'ViInsertMultipleMode()'
+
+
+@memoized()
+class ViReplaceMode(Filter):
+ def __call__(self, cli):
+ if (cli.editing_mode != EditingMode.VI
+ or cli.vi_state.operator_func
+ or cli.vi_state.waiting_for_digraph
+ or cli.current_buffer.selection_state
+ or cli.current_buffer.read_only()):
+ return False
+
+ return cli.vi_state.input_mode == ViInputMode.REPLACE
+
+ def __repr__(self):
+ return 'ViReplaceMode()'
+
+
+@memoized()
+class ViSelectionMode(Filter):
+ def __call__(self, cli):
+ if cli.editing_mode != EditingMode.VI:
+ return False
+
+ return bool(cli.current_buffer.selection_state)
+
+ def __repr__(self):
+ return 'ViSelectionMode()'
+
+
+@memoized()
+class ViWaitingForTextObjectMode(Filter):
+ def __call__(self, cli):
+ if cli.editing_mode != EditingMode.VI:
+ return False
+
+ return cli.vi_state.operator_func is not None
+
+ def __repr__(self):
+ return 'ViWaitingForTextObjectMode()'
+
+
+@memoized()
+class ViDigraphMode(Filter):
+ def __call__(self, cli):
+ if cli.editing_mode != EditingMode.VI:
+ return False
+
+ return cli.vi_state.waiting_for_digraph
+
+ def __repr__(self):
+ return 'ViDigraphMode()'
+
+
+@memoized()
+class EmacsMode(Filter):
+ " When the Emacs bindings are active. "
+ def __call__(self, cli):
+ return cli.editing_mode == EditingMode.EMACS
+
+ def __repr__(self):
+ return 'EmacsMode()'
+
+
+@memoized()
+class EmacsInsertMode(Filter):
+ def __call__(self, cli):
+ if (cli.editing_mode != EditingMode.EMACS
+ or cli.current_buffer.selection_state
+ or cli.current_buffer.read_only()):
+ return False
+ return True
+
+ def __repr__(self):
+ return 'EmacsInsertMode()'
+
+
+@memoized()
+class EmacsSelectionMode(Filter):
+ def __call__(self, cli):
+ return (cli.editing_mode == EditingMode.EMACS
+ and cli.current_buffer.selection_state)
+
+ def __repr__(self):
+ return 'EmacsSelectionMode()'
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/filters/types.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/filters/types.py
new file mode 100644
index 0000000000..3e89c39c01
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/filters/types.py
@@ -0,0 +1,55 @@
+from __future__ import unicode_literals
+from six import with_metaclass
+from collections import defaultdict
+import weakref
+
+__all__ = (
+ 'CLIFilter',
+ 'SimpleFilter',
+)
+
+# Cache for _FilterTypeMeta. (Don't test the same __instancecheck__ twice as
+# long as the object lives. -- We do this a lot and calling 'test_args' is
+# expensive.)
+_instance_check_cache = defaultdict(weakref.WeakKeyDictionary)
+
+
+class _FilterTypeMeta(type):
+ def __instancecheck__(cls, instance):
+ cache = _instance_check_cache[tuple(cls.arguments_list)]
+
+ def get():
+ " The actual test. "
+ if not hasattr(instance, 'test_args'):
+ return False
+ return instance.test_args(*cls.arguments_list)
+
+ try:
+ return cache[instance]
+ except KeyError:
+ result = get()
+ cache[instance] = result
+ return result
+
+
+class _FilterType(with_metaclass(_FilterTypeMeta)):
+ def __new__(cls):
+ raise NotImplementedError('This class should not be initiated.')
+
+
+class CLIFilter(_FilterType):
+ """
+ Abstract base class for filters that accept a
+ :class:`~prompt_toolkit.interface.CommandLineInterface` argument. It cannot
+ be instantiated, it's only to be used for instance assertions, e.g.::
+
+ isinstance(my_filter, CliFilter)
+ """
+ arguments_list = ['cli']
+
+
+class SimpleFilter(_FilterType):
+ """
+ Abstract base class for filters that don't accept any arguments.
+ """
+ arguments_list = []
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/filters/utils.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/filters/utils.py
new file mode 100644
index 0000000000..836d2956e7
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/filters/utils.py
@@ -0,0 +1,39 @@
+from __future__ import unicode_literals
+from .base import Always, Never
+from .types import SimpleFilter, CLIFilter
+
+__all__ = (
+ 'to_cli_filter',
+ 'to_simple_filter',
+)
+
+_always = Always()
+_never = Never()
+
+
+def to_simple_filter(bool_or_filter):
+ """
+ Accept both booleans and CLIFilters as input and
+ turn it into a SimpleFilter.
+ """
+ if not isinstance(bool_or_filter, (bool, SimpleFilter)):
+ raise TypeError('Expecting a bool or a SimpleFilter instance. Got %r' % bool_or_filter)
+
+ return {
+ True: _always,
+ False: _never,
+ }.get(bool_or_filter, bool_or_filter)
+
+
+def to_cli_filter(bool_or_filter):
+ """
+ Accept both booleans and CLIFilters as input and
+ turn it into a CLIFilter.
+ """
+ if not isinstance(bool_or_filter, (bool, CLIFilter)):
+ raise TypeError('Expecting a bool or a CLIFilter instance. Got %r' % bool_or_filter)
+
+ return {
+ True: _always,
+ False: _never,
+ }.get(bool_or_filter, bool_or_filter)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/history.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/history.py
new file mode 100644
index 0000000000..d1eb5f2730
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/history.py
@@ -0,0 +1,120 @@
+from __future__ import unicode_literals
+from abc import ABCMeta, abstractmethod
+from six import with_metaclass
+
+import datetime
+import os
+
+__all__ = (
+ 'FileHistory',
+ 'History',
+ 'InMemoryHistory',
+)
+
+
+class History(with_metaclass(ABCMeta, object)):
+ """
+ Base ``History`` interface.
+ """
+ @abstractmethod
+ def append(self, string):
+ " Append string to history. "
+
+ @abstractmethod
+ def __getitem__(self, key):
+ " Return one item of the history. It should be accessible like a `list`. "
+
+ @abstractmethod
+ def __iter__(self):
+ " Iterate through all the items of the history. Cronologically. "
+
+ @abstractmethod
+ def __len__(self):
+ " Return the length of the history. "
+
+ def __bool__(self):
+ """
+ Never evaluate to False, even when the history is empty.
+ (Python calls __len__ if __bool__ is not implemented.)
+ This is mainly to allow lazy evaluation::
+
+ x = history or InMemoryHistory()
+ """
+ return True
+
+ __nonzero__ = __bool__ # For Python 2.
+
+
+class InMemoryHistory(History):
+ """
+ :class:`.History` class that keeps a list of all strings in memory.
+ """
+ def __init__(self):
+ self.strings = []
+
+ def append(self, string):
+ self.strings.append(string)
+
+ def __getitem__(self, key):
+ return self.strings[key]
+
+ def __iter__(self):
+ return iter(self.strings)
+
+ def __len__(self):
+ return len(self.strings)
+
+
+class FileHistory(History):
+ """
+ :class:`.History` class that stores all strings in a file.
+ """
+ def __init__(self, filename):
+ self.strings = []
+ self.filename = filename
+
+ self._load()
+
+ def _load(self):
+ lines = []
+
+ def add():
+ if lines:
+ # Join and drop trailing newline.
+ string = ''.join(lines)[:-1]
+
+ self.strings.append(string)
+
+ if os.path.exists(self.filename):
+ with open(self.filename, 'rb') as f:
+ for line in f:
+ line = line.decode('utf-8')
+
+ if line.startswith('+'):
+ lines.append(line[1:])
+ else:
+ add()
+ lines = []
+
+ add()
+
+ def append(self, string):
+ self.strings.append(string)
+
+ # Save to file.
+ with open(self.filename, 'ab') as f:
+ def write(t):
+ f.write(t.encode('utf-8'))
+
+ write('\n# %s\n' % datetime.datetime.now())
+ for line in string.split('\n'):
+ write('+%s\n' % line)
+
+ def __getitem__(self, key):
+ return self.strings[key]
+
+ def __iter__(self):
+ return iter(self.strings)
+
+ def __len__(self):
+ return len(self.strings)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/input.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/input.py
new file mode 100644
index 0000000000..f123732560
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/input.py
@@ -0,0 +1,135 @@
+"""
+Abstraction of CLI Input.
+"""
+from __future__ import unicode_literals
+
+from .utils import DummyContext, is_windows
+from abc import ABCMeta, abstractmethod
+from six import with_metaclass
+
+import io
+import os
+import sys
+
+if is_windows():
+ from .terminal.win32_input import raw_mode, cooked_mode
+else:
+ from .terminal.vt100_input import raw_mode, cooked_mode
+
+__all__ = (
+ 'Input',
+ 'StdinInput',
+ 'PipeInput',
+)
+
+
+class Input(with_metaclass(ABCMeta, object)):
+ """
+ Abstraction for any input.
+
+ An instance of this class can be given to the constructor of a
+ :class:`~prompt_toolkit.interface.CommandLineInterface` and will also be
+ passed to the :class:`~prompt_toolkit.eventloop.base.EventLoop`.
+ """
+ @abstractmethod
+ def fileno(self):
+ """
+ Fileno for putting this in an event loop.
+ """
+
+ @abstractmethod
+ def read(self):
+ """
+ Return text from the input.
+ """
+
+ @abstractmethod
+ def raw_mode(self):
+ """
+ Context manager that turns the input into raw mode.
+ """
+
+ @abstractmethod
+ def cooked_mode(self):
+ """
+ Context manager that turns the input into cooked mode.
+ """
+
+
+class StdinInput(Input):
+ """
+ Simple wrapper around stdin.
+ """
+ def __init__(self, stdin=None):
+ self.stdin = stdin or sys.stdin
+
+ # The input object should be a TTY.
+ assert self.stdin.isatty()
+
+ # Test whether the given input object has a file descriptor.
+ # (Idle reports stdin to be a TTY, but fileno() is not implemented.)
+ try:
+ # This should not raise, but can return 0.
+ self.stdin.fileno()
+ except io.UnsupportedOperation:
+ if 'idlelib.run' in sys.modules:
+ raise io.UnsupportedOperation(
+ 'Stdin is not a terminal. Running from Idle is not supported.')
+ else:
+ raise io.UnsupportedOperation('Stdin is not a terminal.')
+
+ def __repr__(self):
+ return 'StdinInput(stdin=%r)' % (self.stdin,)
+
+ def raw_mode(self):
+ return raw_mode(self.stdin.fileno())
+
+ def cooked_mode(self):
+ return cooked_mode(self.stdin.fileno())
+
+ def fileno(self):
+ return self.stdin.fileno()
+
+ def read(self):
+ return self.stdin.read()
+
+
+class PipeInput(Input):
+ """
+ Input that is send through a pipe.
+ This is useful if we want to send the input programatically into the
+ interface, but still use the eventloop.
+
+ Usage::
+
+ input = PipeInput()
+ input.send('inputdata')
+ """
+ def __init__(self):
+ self._r, self._w = os.pipe()
+
+ def fileno(self):
+ return self._r
+
+ def read(self):
+ return os.read(self._r)
+
+ def send_text(self, data):
+ " Send text to the input. "
+ os.write(self._w, data.encode('utf-8'))
+
+ # Deprecated alias for `send_text`.
+ send = send_text
+
+ def raw_mode(self):
+ return DummyContext()
+
+ def cooked_mode(self):
+ return DummyContext()
+
+ def close(self):
+ " Close pipe fds. "
+ os.close(self._r)
+ os.close(self._w)
+ self._r = None
+ self._w = None
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/interface.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/interface.py
new file mode 100644
index 0000000000..e1e0e56393
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/interface.py
@@ -0,0 +1,1190 @@
+"""
+The main `CommandLineInterface` class and logic.
+"""
+from __future__ import unicode_literals
+
+import functools
+import os
+import signal
+import six
+import sys
+import textwrap
+import threading
+import time
+import types
+import weakref
+
+from subprocess import Popen
+
+from .application import Application, AbortAction
+from .buffer import Buffer
+from .buffer_mapping import BufferMapping
+from .completion import CompleteEvent, get_common_complete_suffix
+from .enums import SEARCH_BUFFER
+from .eventloop.base import EventLoop
+from .eventloop.callbacks import EventLoopCallbacks
+from .filters import Condition
+from .input import StdinInput, Input
+from .key_binding.input_processor import InputProcessor
+from .key_binding.input_processor import KeyPress
+from .key_binding.registry import Registry
+from .key_binding.vi_state import ViState
+from .keys import Keys
+from .output import Output
+from .renderer import Renderer, print_tokens
+from .search_state import SearchState
+from .utils import Event
+
+# Following import is required for backwards compatibility.
+from .buffer import AcceptAction
+
+__all__ = (
+ 'AbortAction',
+ 'CommandLineInterface',
+)
+
+
+class CommandLineInterface(object):
+ """
+ Wrapper around all the other classes, tying everything together.
+
+ Typical usage::
+
+ application = Application(...)
+ cli = CommandLineInterface(application, eventloop)
+ result = cli.run()
+ print(result)
+
+ :param application: :class:`~prompt_toolkit.application.Application` instance.
+ :param eventloop: The :class:`~prompt_toolkit.eventloop.base.EventLoop` to
+ be used when `run` is called. The easiest way to create
+ an eventloop is by calling
+ :meth:`~prompt_toolkit.shortcuts.create_eventloop`.
+ :param input: :class:`~prompt_toolkit.input.Input` instance.
+ :param output: :class:`~prompt_toolkit.output.Output` instance. (Probably
+ Vt100_Output or Win32Output.)
+ """
+ def __init__(self, application, eventloop=None, input=None, output=None):
+ assert isinstance(application, Application)
+ assert isinstance(eventloop, EventLoop), 'Passing an eventloop is required.'
+ assert output is None or isinstance(output, Output)
+ assert input is None or isinstance(input, Input)
+
+ from .shortcuts import create_output
+
+ self.application = application
+ self.eventloop = eventloop
+ self._is_running = False
+
+ # Inputs and outputs.
+ self.output = output or create_output()
+ self.input = input or StdinInput(sys.stdin)
+
+ #: The input buffers.
+ assert isinstance(application.buffers, BufferMapping)
+ self.buffers = application.buffers
+
+ #: EditingMode.VI or EditingMode.EMACS
+ self.editing_mode = application.editing_mode
+
+ #: Quoted insert. This flag is set if we go into quoted insert mode.
+ self.quoted_insert = False
+
+ #: Vi state. (For Vi key bindings.)
+ self.vi_state = ViState()
+
+ #: The `Renderer` instance.
+ # Make sure that the same stdout is used, when a custom renderer has been passed.
+ self.renderer = Renderer(
+ self.application.style,
+ self.output,
+ use_alternate_screen=application.use_alternate_screen,
+ mouse_support=application.mouse_support)
+
+ #: Render counter. This one is increased every time the UI is rendered.
+ #: It can be used as a key for caching certain information during one
+ #: rendering.
+ self.render_counter = 0
+
+ #: When there is high CPU, postpone the renderering max x seconds.
+ #: '0' means: don't postpone. '.5' means: try to draw at least twice a second.
+ self.max_render_postpone_time = 0 # E.g. .5
+
+ # Invalidate flag. When 'True', a repaint has been scheduled.
+ self._invalidated = False
+
+ #: The `InputProcessor` instance.
+ self.input_processor = InputProcessor(application.key_bindings_registry, weakref.ref(self))
+
+ self._async_completers = {} # Map buffer name to completer function.
+
+ # Pointer to sub CLI. (In chain of CLI instances.)
+ self._sub_cli = None # None or other CommandLineInterface instance.
+
+ # Call `add_buffer` for each buffer.
+ for name, b in self.buffers.items():
+ self.add_buffer(name, b)
+
+ # Events.
+ self.on_buffer_changed = Event(self, application.on_buffer_changed)
+ self.on_initialize = Event(self, application.on_initialize)
+ self.on_input_timeout = Event(self, application.on_input_timeout)
+ self.on_invalidate = Event(self, application.on_invalidate)
+ self.on_render = Event(self, application.on_render)
+ self.on_reset = Event(self, application.on_reset)
+ self.on_start = Event(self, application.on_start)
+ self.on_stop = Event(self, application.on_stop)
+
+ # Trigger initialize callback.
+ self.reset()
+ self.on_initialize += self.application.on_initialize
+ self.on_initialize.fire()
+
+ @property
+ def layout(self):
+ return self.application.layout
+
+ @property
+ def clipboard(self):
+ return self.application.clipboard
+
+ @property
+ def pre_run_callables(self):
+ return self.application.pre_run_callables
+
+ def add_buffer(self, name, buffer, focus=False):
+ """
+ Insert a new buffer.
+ """
+ assert isinstance(buffer, Buffer)
+ self.buffers[name] = buffer
+
+ if focus:
+ self.buffers.focus(name)
+
+ # Create asynchronous completer / auto suggestion.
+ auto_suggest_function = self._create_auto_suggest_function(buffer)
+ completer_function = self._create_async_completer(buffer)
+ self._async_completers[name] = completer_function
+
+ # Complete/suggest on text insert.
+ def create_on_insert_handler():
+ """
+ Wrapper around the asynchronous completer and auto suggestion, that
+ ensures that it's only called while typing if the
+ `complete_while_typing` filter is enabled.
+ """
+ def on_text_insert(_):
+ # Only complete when "complete_while_typing" is enabled.
+ if buffer.completer and buffer.complete_while_typing():
+ completer_function()
+
+ # Call auto_suggest.
+ if buffer.auto_suggest:
+ auto_suggest_function()
+
+ return on_text_insert
+
+ buffer.on_text_insert += create_on_insert_handler()
+
+ def buffer_changed(_):
+ """
+ When the text in a buffer changes.
+ (A paste event is also a change, but not an insert. So we don't
+ want to do autocompletions in this case, but we want to propagate
+ the on_buffer_changed event.)
+ """
+ # Trigger on_buffer_changed.
+ self.on_buffer_changed.fire()
+
+ buffer.on_text_changed += buffer_changed
+
+ def start_completion(self, buffer_name=None, select_first=False,
+ select_last=False, insert_common_part=False,
+ complete_event=None):
+ """
+ Start asynchronous autocompletion of this buffer.
+ (This will do nothing if a previous completion was still in progress.)
+ """
+ buffer_name = buffer_name or self.current_buffer_name
+ completer = self._async_completers.get(buffer_name)
+
+ if completer:
+ completer(select_first=select_first,
+ select_last=select_last,
+ insert_common_part=insert_common_part,
+ complete_event=CompleteEvent(completion_requested=True))
+
+ @property
+ def current_buffer_name(self):
+ """
+ The name of the current :class:`.Buffer`. (Or `None`.)
+ """
+ return self.buffers.current_name(self)
+
+ @property
+ def current_buffer(self):
+ """
+ The currently focussed :class:`~.Buffer`.
+
+ (This returns a dummy :class:`.Buffer` when none of the actual buffers
+ has the focus. In this case, it's really not practical to check for
+ `None` values or catch exceptions every time.)
+ """
+ return self.buffers.current(self)
+
+ def focus(self, buffer_name):
+ """
+ Focus the buffer with the given name on the focus stack.
+ """
+ self.buffers.focus(self, buffer_name)
+
+ def push_focus(self, buffer_name):
+ """
+ Push to the focus stack.
+ """
+ self.buffers.push_focus(self, buffer_name)
+
+ def pop_focus(self):
+ """
+ Pop from the focus stack.
+ """
+ self.buffers.pop_focus(self)
+
+ @property
+ def terminal_title(self):
+ """
+ Return the current title to be displayed in the terminal.
+ When this in `None`, the terminal title remains the original.
+ """
+ result = self.application.get_title()
+
+ # Make sure that this function returns a unicode object,
+ # and not a byte string.
+ assert result is None or isinstance(result, six.text_type)
+ return result
+
+ @property
+ def is_searching(self):
+ """
+ True when we are searching.
+ """
+ return self.current_buffer_name == SEARCH_BUFFER
+
+ def reset(self, reset_current_buffer=False):
+ """
+ Reset everything, for reading the next input.
+
+ :param reset_current_buffer: XXX: not used anymore. The reason for
+ having this option in the past was when this CommandLineInterface
+ is run multiple times, that we could reset the buffer content from
+ the previous run. This is now handled in the AcceptAction.
+ """
+ # Notice that we don't reset the buffers. (This happens just before
+ # returning, and when we have multiple buffers, we clearly want the
+ # content in the other buffers to remain unchanged between several
+ # calls of `run`. (And the same is true for the focus stack.)
+
+ self._exit_flag = False
+ self._abort_flag = False
+
+ self._return_value = None
+
+ self.renderer.reset()
+ self.input_processor.reset()
+ self.layout.reset()
+ self.vi_state.reset()
+
+ # Search new search state. (Does also remember what has to be
+ # highlighted.)
+ self.search_state = SearchState(ignore_case=Condition(lambda: self.is_ignoring_case))
+
+ # Trigger reset event.
+ self.on_reset.fire()
+
+ @property
+ def in_paste_mode(self):
+ """ True when we are in paste mode. """
+ return self.application.paste_mode(self)
+
+ @property
+ def is_ignoring_case(self):
+ """ True when we currently ignore casing. """
+ return self.application.ignore_case(self)
+
+ def invalidate(self):
+ """
+ Thread safe way of sending a repaint trigger to the input event loop.
+ """
+ # Never schedule a second redraw, when a previous one has not yet been
+ # executed. (This should protect against other threads calling
+ # 'invalidate' many times, resulting in 100% CPU.)
+ if self._invalidated:
+ return
+ else:
+ self._invalidated = True
+
+ # Trigger event.
+ self.on_invalidate.fire()
+
+ if self.eventloop is not None:
+ def redraw():
+ self._invalidated = False
+ self._redraw()
+
+ # Call redraw in the eventloop (thread safe).
+ # Usually with the high priority, in order to make the application
+ # feel responsive, but this can be tuned by changing the value of
+ # `max_render_postpone_time`.
+ if self.max_render_postpone_time:
+ _max_postpone_until = time.time() + self.max_render_postpone_time
+ else:
+ _max_postpone_until = None
+
+ self.eventloop.call_from_executor(
+ redraw, _max_postpone_until=_max_postpone_until)
+
+ # Depracated alias for 'invalidate'.
+ request_redraw = invalidate
+
+ def _redraw(self):
+ """
+ Render the command line again. (Not thread safe!) (From other threads,
+ or if unsure, use :meth:`.CommandLineInterface.invalidate`.)
+ """
+ # Only draw when no sub application was started.
+ if self._is_running and self._sub_cli is None:
+ self.render_counter += 1
+ self.renderer.render(self, self.layout, is_done=self.is_done)
+
+ # Fire render event.
+ self.on_render.fire()
+
+ def _on_resize(self):
+ """
+ When the window size changes, we erase the current output and request
+ again the cursor position. When the CPR answer arrives, the output is
+ drawn again.
+ """
+ # Erase, request position (when cursor is at the start position)
+ # and redraw again. -- The order is important.
+ self.renderer.erase(leave_alternate_screen=False, erase_title=False)
+ self.renderer.request_absolute_cursor_position()
+ self._redraw()
+
+ def _load_next_buffer_indexes(self):
+ for buff, index in self._next_buffer_indexes.items():
+ if buff in self.buffers:
+ self.buffers[buff].working_index = index
+
+ def _pre_run(self, pre_run=None):
+ " Called during `run`. "
+ if pre_run:
+ pre_run()
+
+ # Process registered "pre_run_callables" and clear list.
+ for c in self.pre_run_callables:
+ c()
+ del self.pre_run_callables[:]
+
+ def run(self, reset_current_buffer=False, pre_run=None):
+ """
+ Read input from the command line.
+ This runs the eventloop until a return value has been set.
+
+ :param reset_current_buffer: XXX: Not used anymore.
+ :param pre_run: Callable that is called right after the reset has taken
+ place. This allows custom initialisation.
+ """
+ assert pre_run is None or callable(pre_run)
+
+ try:
+ self._is_running = True
+
+ self.on_start.fire()
+ self.reset()
+
+ # Call pre_run.
+ self._pre_run(pre_run)
+
+ # Run eventloop in raw mode.
+ with self.input.raw_mode():
+ self.renderer.request_absolute_cursor_position()
+ self._redraw()
+
+ self.eventloop.run(self.input, self.create_eventloop_callbacks())
+ finally:
+ # Clean up renderer. (This will leave the alternate screen, if we use
+ # that.)
+
+ # If exit/abort haven't been called set, but another exception was
+ # thrown instead for some reason, make sure that we redraw in exit
+ # mode.
+ if not self.is_done:
+ self._exit_flag = True
+ self._redraw()
+
+ self.renderer.reset()
+ self.on_stop.fire()
+ self._is_running = False
+
+ # Return result.
+ return self.return_value()
+
+ try:
+ # The following `run_async` function is compiled at runtime
+ # because it contains syntax which is not supported on older Python
+ # versions. (A 'return' inside a generator.)
+ six.exec_(textwrap.dedent('''
+ def run_async(self, reset_current_buffer=True, pre_run=None):
+ """
+ Same as `run`, but this returns a coroutine.
+
+ This is only available on Python >3.3, with asyncio.
+ """
+ # Inline import, because it slows down startup when asyncio is not
+ # needed.
+ import asyncio
+
+ @asyncio.coroutine
+ def run():
+ assert pre_run is None or callable(pre_run)
+
+ try:
+ self._is_running = True
+
+ self.on_start.fire()
+ self.reset()
+
+ # Call pre_run.
+ self._pre_run(pre_run)
+
+ with self.input.raw_mode():
+ self.renderer.request_absolute_cursor_position()
+ self._redraw()
+
+ yield from self.eventloop.run_as_coroutine(
+ self.input, self.create_eventloop_callbacks())
+
+ return self.return_value()
+ finally:
+ if not self.is_done:
+ self._exit_flag = True
+ self._redraw()
+
+ self.renderer.reset()
+ self.on_stop.fire()
+ self._is_running = False
+
+ return run()
+ '''))
+ except SyntaxError:
+ # Python2, or early versions of Python 3.
+ def run_async(self, reset_current_buffer=True, pre_run=None):
+ """
+ Same as `run`, but this returns a coroutine.
+
+ This is only available on Python >3.3, with asyncio.
+ """
+ raise NotImplementedError
+
+ def run_sub_application(self, application, done_callback=None, erase_when_done=False,
+ _from_application_generator=False):
+ # `erase_when_done` is deprecated, set Application.erase_when_done instead.
+ """
+ Run a sub :class:`~prompt_toolkit.application.Application`.
+
+ This will suspend the main application and display the sub application
+ until that one returns a value. The value is returned by calling
+ `done_callback` with the result.
+
+ The sub application will share the same I/O of the main application.
+ That means, it uses the same input and output channels and it shares
+ the same event loop.
+
+ .. note:: Technically, it gets another Eventloop instance, but that is
+ only a proxy to our main event loop. The reason is that calling
+ 'stop' --which returns the result of an application when it's
+ done-- is handled differently.
+ """
+ assert isinstance(application, Application)
+ assert done_callback is None or callable(done_callback)
+
+ if self._sub_cli is not None:
+ raise RuntimeError('Another sub application started already.')
+
+ # Erase current application.
+ if not _from_application_generator:
+ self.renderer.erase()
+
+ # Callback when the sub app is done.
+ def done():
+ # Redraw sub app in done state.
+ # and reset the renderer. (This reset will also quit the alternate
+ # screen, if the sub application used that.)
+ sub_cli._redraw()
+ if erase_when_done or application.erase_when_done:
+ sub_cli.renderer.erase()
+ sub_cli.renderer.reset()
+ sub_cli._is_running = False # Don't render anymore.
+
+ self._sub_cli = None
+
+ # Restore main application.
+ if not _from_application_generator:
+ self.renderer.request_absolute_cursor_position()
+ self._redraw()
+
+ # Deliver result.
+ if done_callback:
+ done_callback(sub_cli.return_value())
+
+ # Create sub CommandLineInterface.
+ sub_cli = CommandLineInterface(
+ application=application,
+ eventloop=_SubApplicationEventLoop(self, done),
+ input=self.input,
+ output=self.output)
+ sub_cli._is_running = True # Allow rendering of sub app.
+
+ sub_cli._redraw()
+ self._sub_cli = sub_cli
+
+ def exit(self):
+ """
+ Set exit. When Control-D has been pressed.
+ """
+ on_exit = self.application.on_exit
+ self._exit_flag = True
+ self._redraw()
+
+ if on_exit == AbortAction.RAISE_EXCEPTION:
+ def eof_error():
+ raise EOFError()
+ self._set_return_callable(eof_error)
+
+ elif on_exit == AbortAction.RETRY:
+ self.reset()
+ self.renderer.request_absolute_cursor_position()
+ self.current_buffer.reset()
+
+ elif on_exit == AbortAction.RETURN_NONE:
+ self.set_return_value(None)
+
+ def abort(self):
+ """
+ Set abort. When Control-C has been pressed.
+ """
+ on_abort = self.application.on_abort
+ self._abort_flag = True
+ self._redraw()
+
+ if on_abort == AbortAction.RAISE_EXCEPTION:
+ def keyboard_interrupt():
+ raise KeyboardInterrupt()
+ self._set_return_callable(keyboard_interrupt)
+
+ elif on_abort == AbortAction.RETRY:
+ self.reset()
+ self.renderer.request_absolute_cursor_position()
+ self.current_buffer.reset()
+
+ elif on_abort == AbortAction.RETURN_NONE:
+ self.set_return_value(None)
+
+ # Deprecated aliase for exit/abort.
+ set_exit = exit
+ set_abort = abort
+
+ def set_return_value(self, document):
+ """
+ Set a return value. The eventloop can retrieve the result it by calling
+ `return_value`.
+ """
+ self._set_return_callable(lambda: document)
+ self._redraw() # Redraw in "done" state, after the return value has been set.
+
+ def _set_return_callable(self, value):
+ assert callable(value)
+ self._return_value = value
+
+ if self.eventloop:
+ self.eventloop.stop()
+
+ def run_in_terminal(self, func, render_cli_done=False, cooked_mode=True):
+ """
+ Run function on the terminal above the prompt.
+
+ What this does is first hiding the prompt, then running this callable
+ (which can safely output to the terminal), and then again rendering the
+ prompt which causes the output of this function to scroll above the
+ prompt.
+
+ :param func: The callable to execute.
+ :param render_cli_done: When True, render the interface in the
+ 'Done' state first, then execute the function. If False,
+ erase the interface first.
+ :param cooked_mode: When True (the default), switch the input to
+ cooked mode while executing the function.
+
+ :returns: the result of `func`.
+ """
+ # Draw interface in 'done' state, or erase.
+ if render_cli_done:
+ self._return_value = True
+ self._redraw()
+ self.renderer.reset() # Make sure to disable mouse mode, etc...
+ else:
+ self.renderer.erase()
+ self._return_value = None
+
+ # Run system command.
+ if cooked_mode:
+ with self.input.cooked_mode():
+ result = func()
+ else:
+ result = func()
+
+ # Redraw interface again.
+ self.renderer.reset()
+ self.renderer.request_absolute_cursor_position()
+ self._redraw()
+
+ return result
+
+ def run_application_generator(self, coroutine, render_cli_done=False):
+ """
+ EXPERIMENTAL
+ Like `run_in_terminal`, but takes a generator that can yield Application instances.
+
+ Example:
+
+ def f():
+ yield Application1(...)
+ print('...')
+ yield Application2(...)
+ cli.run_in_terminal_async(f)
+
+ The values which are yielded by the given coroutine are supposed to be
+ `Application` instances that run in the current CLI, all other code is
+ supposed to be CPU bound, so except for yielding the applications,
+ there should not be any user interaction or I/O in the given function.
+ """
+ # Draw interface in 'done' state, or erase.
+ if render_cli_done:
+ self._return_value = True
+ self._redraw()
+ self.renderer.reset() # Make sure to disable mouse mode, etc...
+ else:
+ self.renderer.erase()
+ self._return_value = None
+
+ # Loop through the generator.
+ g = coroutine()
+ assert isinstance(g, types.GeneratorType)
+
+ def step_next(send_value=None):
+ " Execute next step of the coroutine."
+ try:
+ # Run until next yield, in cooked mode.
+ with self.input.cooked_mode():
+ result = g.send(send_value)
+ except StopIteration:
+ done()
+ except:
+ done()
+ raise
+ else:
+ # Process yielded value from coroutine.
+ assert isinstance(result, Application)
+ self.run_sub_application(result, done_callback=step_next,
+ _from_application_generator=True)
+
+ def done():
+ # Redraw interface again.
+ self.renderer.reset()
+ self.renderer.request_absolute_cursor_position()
+ self._redraw()
+
+ # Start processing coroutine.
+ step_next()
+
+ def run_system_command(self, command):
+ """
+ Run system command (While hiding the prompt. When finished, all the
+ output will scroll above the prompt.)
+
+ :param command: Shell command to be executed.
+ """
+ def wait_for_enter():
+ """
+ Create a sub application to wait for the enter key press.
+ This has two advantages over using 'input'/'raw_input':
+ - This will share the same input/output I/O.
+ - This doesn't block the event loop.
+ """
+ from .shortcuts import create_prompt_application
+
+ registry = Registry()
+
+ @registry.add_binding(Keys.ControlJ)
+ @registry.add_binding(Keys.ControlM)
+ def _(event):
+ event.cli.set_return_value(None)
+
+ application = create_prompt_application(
+ message='Press ENTER to continue...',
+ key_bindings_registry=registry)
+ self.run_sub_application(application)
+
+ def run():
+ # Try to use the same input/output file descriptors as the one,
+ # used to run this application.
+ try:
+ input_fd = self.input.fileno()
+ except AttributeError:
+ input_fd = sys.stdin.fileno()
+ try:
+ output_fd = self.output.fileno()
+ except AttributeError:
+ output_fd = sys.stdout.fileno()
+
+ # Run sub process.
+ # XXX: This will still block the event loop.
+ p = Popen(command, shell=True,
+ stdin=input_fd, stdout=output_fd)
+ p.wait()
+
+ # Wait for the user to press enter.
+ wait_for_enter()
+
+ self.run_in_terminal(run)
+
+ def suspend_to_background(self, suspend_group=True):
+ """
+ (Not thread safe -- to be called from inside the key bindings.)
+ Suspend process.
+
+ :param suspend_group: When true, suspend the whole process group.
+ (This is the default, and probably what you want.)
+ """
+ # Only suspend when the opperating system supports it.
+ # (Not on Windows.)
+ if hasattr(signal, 'SIGTSTP'):
+ def run():
+ # Send `SIGSTP` to own process.
+ # This will cause it to suspend.
+
+ # Usually we want the whole process group to be suspended. This
+ # handles the case when input is piped from another process.
+ if suspend_group:
+ os.kill(0, signal.SIGTSTP)
+ else:
+ os.kill(os.getpid(), signal.SIGTSTP)
+
+ self.run_in_terminal(run)
+
+ def print_tokens(self, tokens, style=None):
+ """
+ Print a list of (Token, text) tuples to the output.
+ (When the UI is running, this method has to be called through
+ `run_in_terminal`, otherwise it will destroy the UI.)
+
+ :param style: Style class to use. Defaults to the active style in the CLI.
+ """
+ print_tokens(self.output, tokens, style or self.application.style)
+
+ @property
+ def is_exiting(self):
+ """
+ ``True`` when the exit flag as been set.
+ """
+ return self._exit_flag
+
+ @property
+ def is_aborting(self):
+ """
+ ``True`` when the abort flag as been set.
+ """
+ return self._abort_flag
+
+ @property
+ def is_returning(self):
+ """
+ ``True`` when a return value has been set.
+ """
+ return self._return_value is not None
+
+ def return_value(self):
+ """
+ Get the return value. Not that this method can throw an exception.
+ """
+ # Note that it's a method, not a property, because it can throw
+ # exceptions.
+ if self._return_value:
+ return self._return_value()
+
+ @property
+ def is_done(self):
+ return self.is_exiting or self.is_aborting or self.is_returning
+
+ def _create_async_completer(self, buffer):
+ """
+ Create function for asynchronous autocompletion.
+ (Autocomplete in other thread.)
+ """
+ complete_thread_running = [False] # By ref.
+
+ def completion_does_nothing(document, completion):
+ """
+ Return `True` if applying this completion doesn't have any effect.
+ (When it doesn't insert any new text.
+ """
+ text_before_cursor = document.text_before_cursor
+ replaced_text = text_before_cursor[
+ len(text_before_cursor) + completion.start_position:]
+ return replaced_text == completion.text
+
+ def async_completer(select_first=False, select_last=False,
+ insert_common_part=False, complete_event=None):
+ document = buffer.document
+ complete_event = complete_event or CompleteEvent(text_inserted=True)
+
+ # Don't start two threads at the same time.
+ if complete_thread_running[0]:
+ return
+
+ # Don't complete when we already have completions.
+ if buffer.complete_state or not buffer.completer:
+ return
+
+ # Otherwise, get completions in other thread.
+ complete_thread_running[0] = True
+
+ def run():
+ completions = list(buffer.completer.get_completions(document, complete_event))
+
+ def callback():
+ """
+ Set the new complete_state in a safe way. Don't replace an
+ existing complete_state if we had one. (The user could have
+ pressed 'Tab' in the meantime. Also don't set it if the text
+ was changed in the meantime.
+ """
+ complete_thread_running[0] = False
+
+ # When there is only one completion, which has nothing to add, ignore it.
+ if (len(completions) == 1 and
+ completion_does_nothing(document, completions[0])):
+ del completions[:]
+
+ # Set completions if the text was not yet changed.
+ if buffer.text == document.text and \
+ buffer.cursor_position == document.cursor_position and \
+ not buffer.complete_state:
+
+ set_completions = True
+ select_first_anyway = False
+
+ # When the common part has to be inserted, and there
+ # is a common part.
+ if insert_common_part:
+ common_part = get_common_complete_suffix(document, completions)
+ if common_part:
+ # Insert the common part, update completions.
+ buffer.insert_text(common_part)
+ if len(completions) > 1:
+ # (Don't call `async_completer` again, but
+ # recalculate completions. See:
+ # https://github.com/ipython/ipython/issues/9658)
+ completions[:] = [
+ c.new_completion_from_position(len(common_part))
+ for c in completions]
+ else:
+ set_completions = False
+ else:
+ # When we were asked to insert the "common"
+ # prefix, but there was no common suffix but
+ # still exactly one match, then select the
+ # first. (It could be that we have a completion
+ # which does * expansion, like '*.py', with
+ # exactly one match.)
+ if len(completions) == 1:
+ select_first_anyway = True
+
+ if set_completions:
+ buffer.set_completions(
+ completions=completions,
+ go_to_first=select_first or select_first_anyway,
+ go_to_last=select_last)
+ self.invalidate()
+ elif not buffer.complete_state:
+ # Otherwise, restart thread.
+ async_completer()
+
+ if self.eventloop:
+ self.eventloop.call_from_executor(callback)
+
+ self.eventloop.run_in_executor(run)
+ return async_completer
+
+ def _create_auto_suggest_function(self, buffer):
+ """
+ Create function for asynchronous auto suggestion.
+ (AutoSuggest in other thread.)
+ """
+ suggest_thread_running = [False] # By ref.
+
+ def async_suggestor():
+ document = buffer.document
+
+ # Don't start two threads at the same time.
+ if suggest_thread_running[0]:
+ return
+
+ # Don't suggest when we already have a suggestion.
+ if buffer.suggestion or not buffer.auto_suggest:
+ return
+
+ # Otherwise, get completions in other thread.
+ suggest_thread_running[0] = True
+
+ def run():
+ suggestion = buffer.auto_suggest.get_suggestion(self, buffer, document)
+
+ def callback():
+ suggest_thread_running[0] = False
+
+ # Set suggestion only if the text was not yet changed.
+ if buffer.text == document.text and \
+ buffer.cursor_position == document.cursor_position:
+
+ # Set suggestion and redraw interface.
+ buffer.suggestion = suggestion
+ self.invalidate()
+ else:
+ # Otherwise, restart thread.
+ async_suggestor()
+
+ if self.eventloop:
+ self.eventloop.call_from_executor(callback)
+
+ self.eventloop.run_in_executor(run)
+ return async_suggestor
+
+ def stdout_proxy(self, raw=False):
+ """
+ Create an :class:`_StdoutProxy` class which can be used as a patch for
+ `sys.stdout`. Writing to this proxy will make sure that the text
+ appears above the prompt, and that it doesn't destroy the output from
+ the renderer.
+
+ :param raw: (`bool`) When True, vt100 terminal escape sequences are not
+ removed/escaped.
+ """
+ return _StdoutProxy(self, raw=raw)
+
+ def patch_stdout_context(self, raw=False, patch_stdout=True, patch_stderr=True):
+ """
+ Return a context manager that will replace ``sys.stdout`` with a proxy
+ that makes sure that all printed text will appear above the prompt, and
+ that it doesn't destroy the output from the renderer.
+
+ :param patch_stdout: Replace `sys.stdout`.
+ :param patch_stderr: Replace `sys.stderr`.
+ """
+ return _PatchStdoutContext(
+ self.stdout_proxy(raw=raw),
+ patch_stdout=patch_stdout, patch_stderr=patch_stderr)
+
+ def create_eventloop_callbacks(self):
+ return _InterfaceEventLoopCallbacks(self)
+
+
+class _InterfaceEventLoopCallbacks(EventLoopCallbacks):
+ """
+ Callbacks on the :class:`.CommandLineInterface` object, to which an
+ eventloop can talk.
+ """
+ def __init__(self, cli):
+ assert isinstance(cli, CommandLineInterface)
+ self.cli = cli
+
+ @property
+ def _active_cli(self):
+ """
+ Return the active `CommandLineInterface`.
+ """
+ cli = self.cli
+
+ # If there is a sub CLI. That one is always active.
+ while cli._sub_cli:
+ cli = cli._sub_cli
+
+ return cli
+
+ def terminal_size_changed(self):
+ """
+ Report terminal size change. This will trigger a redraw.
+ """
+ self._active_cli._on_resize()
+
+ def input_timeout(self):
+ cli = self._active_cli
+ cli.on_input_timeout.fire()
+
+ def feed_key(self, key_press):
+ """
+ Feed a key press to the CommandLineInterface.
+ """
+ assert isinstance(key_press, KeyPress)
+ cli = self._active_cli
+
+ # Feed the key and redraw.
+ # (When the CLI is in 'done' state, it should return to the event loop
+ # as soon as possible. Ignore all key presses beyond this point.)
+ if not cli.is_done:
+ cli.input_processor.feed(key_press)
+ cli.input_processor.process_keys()
+
+
+class _PatchStdoutContext(object):
+ def __init__(self, new_stdout, patch_stdout=True, patch_stderr=True):
+ self.new_stdout = new_stdout
+ self.patch_stdout = patch_stdout
+ self.patch_stderr = patch_stderr
+
+ def __enter__(self):
+ self.original_stdout = sys.stdout
+ self.original_stderr = sys.stderr
+
+ if self.patch_stdout:
+ sys.stdout = self.new_stdout
+ if self.patch_stderr:
+ sys.stderr = self.new_stdout
+
+ def __exit__(self, *a, **kw):
+ if self.patch_stdout:
+ sys.stdout = self.original_stdout
+
+ if self.patch_stderr:
+ sys.stderr = self.original_stderr
+
+
+class _StdoutProxy(object):
+ """
+ Proxy for stdout, as returned by
+ :class:`CommandLineInterface.stdout_proxy`.
+ """
+ def __init__(self, cli, raw=False):
+ assert isinstance(cli, CommandLineInterface)
+ assert isinstance(raw, bool)
+
+ self._lock = threading.RLock()
+ self._cli = cli
+ self._raw = raw
+ self._buffer = []
+
+ self.errors = sys.__stdout__.errors
+ self.encoding = sys.__stdout__.encoding
+
+ def _do(self, func):
+ if self._cli._is_running:
+ run_in_terminal = functools.partial(self._cli.run_in_terminal, func)
+ self._cli.eventloop.call_from_executor(run_in_terminal)
+ else:
+ func()
+
+ def _write(self, data):
+ """
+ Note: print()-statements cause to multiple write calls.
+ (write('line') and write('\n')). Of course we don't want to call
+ `run_in_terminal` for every individual call, because that's too
+ expensive, and as long as the newline hasn't been written, the
+ text itself is again overwritter by the rendering of the input
+ command line. Therefor, we have a little buffer which holds the
+ text until a newline is written to stdout.
+ """
+ if '\n' in data:
+ # When there is a newline in the data, write everything before the
+ # newline, including the newline itself.
+ before, after = data.rsplit('\n', 1)
+ to_write = self._buffer + [before, '\n']
+ self._buffer = [after]
+
+ def run():
+ for s in to_write:
+ if self._raw:
+ self._cli.output.write_raw(s)
+ else:
+ self._cli.output.write(s)
+ self._do(run)
+ else:
+ # Otherwise, cache in buffer.
+ self._buffer.append(data)
+
+ def write(self, data):
+ with self._lock:
+ self._write(data)
+
+ def _flush(self):
+ def run():
+ for s in self._buffer:
+ if self._raw:
+ self._cli.output.write_raw(s)
+ else:
+ self._cli.output.write(s)
+ self._buffer = []
+ self._cli.output.flush()
+ self._do(run)
+
+ def flush(self):
+ """
+ Flush buffered output.
+ """
+ with self._lock:
+ self._flush()
+
+
+class _SubApplicationEventLoop(EventLoop):
+ """
+ Eventloop used by sub applications.
+
+ A sub application is an `Application` that is "spawned" by a parent
+ application. The parent application is suspended temporarily and the sub
+ application is displayed instead.
+
+ It doesn't need it's own event loop. The `EventLoopCallbacks` from the
+ parent application are redirected to the sub application. So if the event
+ loop that is run by the parent application detects input, the callbacks
+ will make sure that it's forwarded to the sub application.
+
+ When the sub application has a return value set, it will terminate
+ by calling the `stop` method of this event loop. This is used to
+ transfer control back to the parent application.
+ """
+ def __init__(self, cli, stop_callback):
+ assert isinstance(cli, CommandLineInterface)
+ assert callable(stop_callback)
+
+ self.cli = cli
+ self.stop_callback = stop_callback
+
+ def stop(self):
+ self.stop_callback()
+
+ def close(self):
+ pass
+
+ def run_in_executor(self, callback):
+ self.cli.eventloop.run_in_executor(callback)
+
+ def call_from_executor(self, callback, _max_postpone_until=None):
+ self.cli.eventloop.call_from_executor(
+ callback, _max_postpone_until=_max_postpone_until)
+
+ def add_reader(self, fd, callback):
+ self.cli.eventloop.add_reader(fd, callback)
+
+ def remove_reader(self, fd):
+ self.cli.eventloop.remove_reader(fd)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/__init__.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/__init__.py
new file mode 100644
index 0000000000..baffc48825
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/__init__.py
@@ -0,0 +1 @@
+from __future__ import unicode_literals
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/bindings/__init__.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/bindings/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/bindings/__init__.py
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/bindings/basic.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/bindings/basic.py
new file mode 100644
index 0000000000..401135dec0
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/bindings/basic.py
@@ -0,0 +1,407 @@
+# pylint: disable=function-redefined
+from __future__ import unicode_literals
+
+from prompt_toolkit.enums import DEFAULT_BUFFER
+from prompt_toolkit.filters import HasSelection, Condition, EmacsInsertMode, ViInsertMode
+from prompt_toolkit.keys import Keys
+from prompt_toolkit.layout.screen import Point
+from prompt_toolkit.mouse_events import MouseEventType, MouseEvent
+from prompt_toolkit.renderer import HeightIsUnknownError
+from prompt_toolkit.utils import suspend_to_background_supported, is_windows
+
+from .named_commands import get_by_name
+from ..registry import Registry
+
+
+__all__ = (
+ 'load_basic_bindings',
+ 'load_abort_and_exit_bindings',
+ 'load_basic_system_bindings',
+ 'load_auto_suggestion_bindings',
+)
+
+def if_no_repeat(event):
+ """ Callable that returns True when the previous event was delivered to
+ another handler. """
+ return not event.is_repeat
+
+
+def load_basic_bindings():
+ registry = Registry()
+ insert_mode = ViInsertMode() | EmacsInsertMode()
+ handle = registry.add_binding
+ has_selection = HasSelection()
+
+ @handle(Keys.ControlA)
+ @handle(Keys.ControlB)
+ @handle(Keys.ControlC)
+ @handle(Keys.ControlD)
+ @handle(Keys.ControlE)
+ @handle(Keys.ControlF)
+ @handle(Keys.ControlG)
+ @handle(Keys.ControlH)
+ @handle(Keys.ControlI)
+ @handle(Keys.ControlJ)
+ @handle(Keys.ControlK)
+ @handle(Keys.ControlL)
+ @handle(Keys.ControlM)
+ @handle(Keys.ControlN)
+ @handle(Keys.ControlO)
+ @handle(Keys.ControlP)
+ @handle(Keys.ControlQ)
+ @handle(Keys.ControlR)
+ @handle(Keys.ControlS)
+ @handle(Keys.ControlT)
+ @handle(Keys.ControlU)
+ @handle(Keys.ControlV)
+ @handle(Keys.ControlW)
+ @handle(Keys.ControlX)
+ @handle(Keys.ControlY)
+ @handle(Keys.ControlZ)
+ @handle(Keys.F1)
+ @handle(Keys.F2)
+ @handle(Keys.F3)
+ @handle(Keys.F4)
+ @handle(Keys.F5)
+ @handle(Keys.F6)
+ @handle(Keys.F7)
+ @handle(Keys.F8)
+ @handle(Keys.F9)
+ @handle(Keys.F10)
+ @handle(Keys.F11)
+ @handle(Keys.F12)
+ @handle(Keys.F13)
+ @handle(Keys.F14)
+ @handle(Keys.F15)
+ @handle(Keys.F16)
+ @handle(Keys.F17)
+ @handle(Keys.F18)
+ @handle(Keys.F19)
+ @handle(Keys.F20)
+ @handle(Keys.ControlSpace)
+ @handle(Keys.ControlBackslash)
+ @handle(Keys.ControlSquareClose)
+ @handle(Keys.ControlCircumflex)
+ @handle(Keys.ControlUnderscore)
+ @handle(Keys.Backspace)
+ @handle(Keys.Up)
+ @handle(Keys.Down)
+ @handle(Keys.Right)
+ @handle(Keys.Left)
+ @handle(Keys.ShiftUp)
+ @handle(Keys.ShiftDown)
+ @handle(Keys.ShiftRight)
+ @handle(Keys.ShiftLeft)
+ @handle(Keys.Home)
+ @handle(Keys.End)
+ @handle(Keys.Delete)
+ @handle(Keys.ShiftDelete)
+ @handle(Keys.ControlDelete)
+ @handle(Keys.PageUp)
+ @handle(Keys.PageDown)
+ @handle(Keys.BackTab)
+ @handle(Keys.Tab)
+ @handle(Keys.ControlLeft)
+ @handle(Keys.ControlRight)
+ @handle(Keys.ControlUp)
+ @handle(Keys.ControlDown)
+ @handle(Keys.Insert)
+ @handle(Keys.Ignore)
+ def _(event):
+ """
+ First, for any of these keys, Don't do anything by default. Also don't
+ catch them in the 'Any' handler which will insert them as data.
+
+ If people want to insert these characters as a literal, they can always
+ do by doing a quoted insert. (ControlQ in emacs mode, ControlV in Vi
+ mode.)
+ """
+ pass
+
+ # Readline-style bindings.
+ handle(Keys.Home)(get_by_name('beginning-of-line'))
+ handle(Keys.End)(get_by_name('end-of-line'))
+ handle(Keys.Left)(get_by_name('backward-char'))
+ handle(Keys.Right)(get_by_name('forward-char'))
+ handle(Keys.ControlUp)(get_by_name('previous-history'))
+ handle(Keys.ControlDown)(get_by_name('next-history'))
+ handle(Keys.ControlL)(get_by_name('clear-screen'))
+
+ handle(Keys.ControlK, filter=insert_mode)(get_by_name('kill-line'))
+ handle(Keys.ControlU, filter=insert_mode)(get_by_name('unix-line-discard'))
+ handle(Keys.ControlH, filter=insert_mode, save_before=if_no_repeat)(
+ get_by_name('backward-delete-char'))
+ handle(Keys.Backspace, filter=insert_mode, save_before=if_no_repeat)(
+ get_by_name('backward-delete-char'))
+ handle(Keys.Delete, filter=insert_mode, save_before=if_no_repeat)(
+ get_by_name('delete-char'))
+ handle(Keys.ShiftDelete, filter=insert_mode, save_before=if_no_repeat)(
+ get_by_name('delete-char'))
+ handle(Keys.Any, filter=insert_mode, save_before=if_no_repeat)(
+ get_by_name('self-insert'))
+ handle(Keys.ControlT, filter=insert_mode)(get_by_name('transpose-chars'))
+ handle(Keys.ControlW, filter=insert_mode)(get_by_name('unix-word-rubout'))
+ handle(Keys.ControlI, filter=insert_mode)(get_by_name('menu-complete'))
+ handle(Keys.BackTab, filter=insert_mode)(get_by_name('menu-complete-backward'))
+
+ handle(Keys.PageUp, filter= ~has_selection)(get_by_name('previous-history'))
+ handle(Keys.PageDown, filter= ~has_selection)(get_by_name('next-history'))
+
+ # CTRL keys.
+
+ text_before_cursor = Condition(lambda cli: cli.current_buffer.text)
+ handle(Keys.ControlD, filter=text_before_cursor & insert_mode)(get_by_name('delete-char'))
+
+ is_multiline = Condition(lambda cli: cli.current_buffer.is_multiline())
+ is_returnable = Condition(lambda cli: cli.current_buffer.accept_action.is_returnable)
+
+ @handle(Keys.ControlJ, filter=is_multiline & insert_mode)
+ def _(event):
+ " Newline (in case of multiline input. "
+ event.current_buffer.newline(copy_margin=not event.cli.in_paste_mode)
+
+ @handle(Keys.ControlJ, filter=~is_multiline & is_returnable)
+ def _(event):
+ " Enter, accept input. "
+ buff = event.current_buffer
+ buff.accept_action.validate_and_handle(event.cli, buff)
+
+ # Delete the word before the cursor.
+
+ @handle(Keys.Up)
+ def _(event):
+ event.current_buffer.auto_up(count=event.arg)
+
+ @handle(Keys.Down)
+ def _(event):
+ event.current_buffer.auto_down(count=event.arg)
+
+ @handle(Keys.Delete, filter=has_selection)
+ def _(event):
+ data = event.current_buffer.cut_selection()
+ event.cli.clipboard.set_data(data)
+
+ # Global bindings.
+
+ @handle(Keys.ControlZ)
+ def _(event):
+ """
+ By default, control-Z should literally insert Ctrl-Z.
+ (Ansi Ctrl-Z, code 26 in MSDOS means End-Of-File.
+ In a Python REPL for instance, it's possible to type
+ Control-Z followed by enter to quit.)
+
+ When the system bindings are loaded and suspend-to-background is
+ supported, that will override this binding.
+ """
+ event.current_buffer.insert_text(event.data)
+
+ @handle(Keys.CPRResponse, save_before=lambda e: False)
+ def _(event):
+ """
+ Handle incoming Cursor-Position-Request response.
+ """
+ # The incoming data looks like u'\x1b[35;1R'
+ # Parse row/col information.
+ row, col = map(int, event.data[2:-1].split(';'))
+
+ # Report absolute cursor position to the renderer.
+ event.cli.renderer.report_absolute_cursor_row(row)
+
+ @handle(Keys.BracketedPaste)
+ def _(event):
+ " Pasting from clipboard. "
+ data = event.data
+
+ # Be sure to use \n as line ending.
+ # Some terminals (Like iTerm2) seem to paste \r\n line endings in a
+ # bracketed paste. See: https://github.com/ipython/ipython/issues/9737
+ data = data.replace('\r\n', '\n')
+ data = data.replace('\r', '\n')
+
+ event.current_buffer.insert_text(data)
+
+ @handle(Keys.Any, filter=Condition(lambda cli: cli.quoted_insert), eager=True)
+ def _(event):
+ """
+ Handle quoted insert.
+ """
+ event.current_buffer.insert_text(event.data, overwrite=False)
+ event.cli.quoted_insert = False
+
+ return registry
+
+
+def load_mouse_bindings():
+ """
+ Key bindings, required for mouse support.
+ (Mouse events enter through the key binding system.)
+ """
+ registry = Registry()
+
+ @registry.add_binding(Keys.Vt100MouseEvent)
+ def _(event):
+ """
+ Handling of incoming mouse event.
+ """
+ # Typical: "Esc[MaB*"
+ # Urxvt: "Esc[96;14;13M"
+ # Xterm SGR: "Esc[<64;85;12M"
+
+ # Parse incoming packet.
+ if event.data[2] == 'M':
+ # Typical.
+ mouse_event, x, y = map(ord, event.data[3:])
+ mouse_event = {
+ 32: MouseEventType.MOUSE_DOWN,
+ 35: MouseEventType.MOUSE_UP,
+ 96: MouseEventType.SCROLL_UP,
+ 97: MouseEventType.SCROLL_DOWN,
+ }.get(mouse_event)
+
+ # Handle situations where `PosixStdinReader` used surrogateescapes.
+ if x >= 0xdc00: x-= 0xdc00
+ if y >= 0xdc00: y-= 0xdc00
+
+ x -= 32
+ y -= 32
+ else:
+ # Urxvt and Xterm SGR.
+ # When the '<' is not present, we are not using the Xterm SGR mode,
+ # but Urxvt instead.
+ data = event.data[2:]
+ if data[:1] == '<':
+ sgr = True
+ data = data[1:]
+ else:
+ sgr = False
+
+ # Extract coordinates.
+ mouse_event, x, y = map(int, data[:-1].split(';'))
+ m = data[-1]
+
+ # Parse event type.
+ if sgr:
+ mouse_event = {
+ (0, 'M'): MouseEventType.MOUSE_DOWN,
+ (0, 'm'): MouseEventType.MOUSE_UP,
+ (64, 'M'): MouseEventType.SCROLL_UP,
+ (65, 'M'): MouseEventType.SCROLL_DOWN,
+ }.get((mouse_event, m))
+ else:
+ mouse_event = {
+ 32: MouseEventType.MOUSE_DOWN,
+ 35: MouseEventType.MOUSE_UP,
+ 96: MouseEventType.SCROLL_UP,
+ 97: MouseEventType.SCROLL_DOWN,
+ }.get(mouse_event)
+
+ x -= 1
+ y -= 1
+
+ # Only handle mouse events when we know the window height.
+ if event.cli.renderer.height_is_known and mouse_event is not None:
+ # Take region above the layout into account. The reported
+ # coordinates are absolute to the visible part of the terminal.
+ try:
+ y -= event.cli.renderer.rows_above_layout
+ except HeightIsUnknownError:
+ return
+
+ # Call the mouse handler from the renderer.
+ handler = event.cli.renderer.mouse_handlers.mouse_handlers[x,y]
+ handler(event.cli, MouseEvent(position=Point(x=x, y=y),
+ event_type=mouse_event))
+
+ @registry.add_binding(Keys.WindowsMouseEvent)
+ def _(event):
+ """
+ Handling of mouse events for Windows.
+ """
+ assert is_windows() # This key binding should only exist for Windows.
+
+ # Parse data.
+ event_type, x, y = event.data.split(';')
+ x = int(x)
+ y = int(y)
+
+ # Make coordinates absolute to the visible part of the terminal.
+ screen_buffer_info = event.cli.renderer.output.get_win32_screen_buffer_info()
+ rows_above_cursor = screen_buffer_info.dwCursorPosition.Y - event.cli.renderer._cursor_pos.y
+ y -= rows_above_cursor
+
+ # Call the mouse event handler.
+ handler = event.cli.renderer.mouse_handlers.mouse_handlers[x,y]
+ handler(event.cli, MouseEvent(position=Point(x=x, y=y),
+ event_type=event_type))
+
+ return registry
+
+
+def load_abort_and_exit_bindings():
+ """
+ Basic bindings for abort (Ctrl-C) and exit (Ctrl-D).
+ """
+ registry = Registry()
+ handle = registry.add_binding
+
+ @handle(Keys.ControlC)
+ def _(event):
+ " Abort when Control-C has been pressed. "
+ event.cli.abort()
+
+ @Condition
+ def ctrl_d_condition(cli):
+ """ Ctrl-D binding is only active when the default buffer is selected
+ and empty. """
+ return (cli.current_buffer_name == DEFAULT_BUFFER and
+ not cli.current_buffer.text)
+
+ handle(Keys.ControlD, filter=ctrl_d_condition)(get_by_name('end-of-file'))
+
+ return registry
+
+
+def load_basic_system_bindings():
+ """
+ Basic system bindings (For both Emacs and Vi mode.)
+ """
+ registry = Registry()
+
+ suspend_supported = Condition(
+ lambda cli: suspend_to_background_supported())
+
+ @registry.add_binding(Keys.ControlZ, filter=suspend_supported)
+ def _(event):
+ """
+ Suspend process to background.
+ """
+ event.cli.suspend_to_background()
+
+ return registry
+
+
+def load_auto_suggestion_bindings():
+ """
+ Key bindings for accepting auto suggestion text.
+ """
+ registry = Registry()
+ handle = registry.add_binding
+
+ suggestion_available = Condition(
+ lambda cli:
+ cli.current_buffer.suggestion is not None and
+ cli.current_buffer.document.is_cursor_at_the_end)
+
+ @handle(Keys.ControlF, filter=suggestion_available)
+ @handle(Keys.ControlE, filter=suggestion_available)
+ @handle(Keys.Right, filter=suggestion_available)
+ def _(event):
+ " Accept suggestion. "
+ b = event.current_buffer
+ suggestion = b.suggestion
+
+ if suggestion:
+ b.insert_text(suggestion.text)
+
+ return registry
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/bindings/completion.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/bindings/completion.py
new file mode 100644
index 0000000000..4903900bc6
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/bindings/completion.py
@@ -0,0 +1,161 @@
+"""
+Key binding handlers for displaying completions.
+"""
+from __future__ import unicode_literals
+from prompt_toolkit.completion import CompleteEvent, get_common_complete_suffix
+from prompt_toolkit.utils import get_cwidth
+from prompt_toolkit.keys import Keys
+from prompt_toolkit.key_binding.registry import Registry
+
+import math
+
+__all__ = (
+ 'generate_completions',
+ 'display_completions_like_readline',
+)
+
+def generate_completions(event):
+ r"""
+ Tab-completion: where the first tab completes the common suffix and the
+ second tab lists all the completions.
+ """
+ b = event.current_buffer
+
+ # When already navigating through completions, select the next one.
+ if b.complete_state:
+ b.complete_next()
+ else:
+ event.cli.start_completion(insert_common_part=True, select_first=False)
+
+
+def display_completions_like_readline(event):
+ """
+ Key binding handler for readline-style tab completion.
+ This is meant to be as similar as possible to the way how readline displays
+ completions.
+
+ Generate the completions immediately (blocking) and display them above the
+ prompt in columns.
+
+ Usage::
+
+ # Call this handler when 'Tab' has been pressed.
+ registry.add_binding(Keys.ControlI)(display_completions_like_readline)
+ """
+ # Request completions.
+ b = event.current_buffer
+ if b.completer is None:
+ return
+ complete_event = CompleteEvent(completion_requested=True)
+ completions = list(b.completer.get_completions(b.document, complete_event))
+
+ # Calculate the common suffix.
+ common_suffix = get_common_complete_suffix(b.document, completions)
+
+ # One completion: insert it.
+ if len(completions) == 1:
+ b.delete_before_cursor(-completions[0].start_position)
+ b.insert_text(completions[0].text)
+ # Multiple completions with common part.
+ elif common_suffix:
+ b.insert_text(common_suffix)
+ # Otherwise: display all completions.
+ elif completions:
+ _display_completions_like_readline(event.cli, completions)
+
+
+def _display_completions_like_readline(cli, completions):
+ """
+ Display the list of completions in columns above the prompt.
+ This will ask for a confirmation if there are too many completions to fit
+ on a single page and provide a paginator to walk through them.
+ """
+ from prompt_toolkit.shortcuts import create_confirm_application
+ assert isinstance(completions, list)
+
+ # Get terminal dimensions.
+ term_size = cli.output.get_size()
+ term_width = term_size.columns
+ term_height = term_size.rows
+
+ # Calculate amount of required columns/rows for displaying the
+ # completions. (Keep in mind that completions are displayed
+ # alphabetically column-wise.)
+ max_compl_width = min(term_width,
+ max(get_cwidth(c.text) for c in completions) + 1)
+ column_count = max(1, term_width // max_compl_width)
+ completions_per_page = column_count * (term_height - 1)
+ page_count = int(math.ceil(len(completions) / float(completions_per_page)))
+ # Note: math.ceil can return float on Python2.
+
+ def display(page):
+ # Display completions.
+ page_completions = completions[page * completions_per_page:
+ (page+1) * completions_per_page]
+
+ page_row_count = int(math.ceil(len(page_completions) / float(column_count)))
+ page_columns = [page_completions[i * page_row_count:(i+1) * page_row_count]
+ for i in range(column_count)]
+
+ result = []
+ for r in range(page_row_count):
+ for c in range(column_count):
+ try:
+ result.append(page_columns[c][r].text.ljust(max_compl_width))
+ except IndexError:
+ pass
+ result.append('\n')
+ cli.output.write(''.join(result))
+ cli.output.flush()
+
+ # User interaction through an application generator function.
+ def run():
+ if len(completions) > completions_per_page:
+ # Ask confirmation if it doesn't fit on the screen.
+ message = 'Display all {} possibilities? (y on n) '.format(len(completions))
+ confirm = yield create_confirm_application(message)
+
+ if confirm:
+ # Display pages.
+ for page in range(page_count):
+ display(page)
+
+ if page != page_count - 1:
+ # Display --MORE-- and go to the next page.
+ show_more = yield _create_more_application()
+ if not show_more:
+ return
+ else:
+ cli.output.write('\n'); cli.output.flush()
+ else:
+ # Display all completions.
+ display(0)
+
+ cli.run_application_generator(run, render_cli_done=True)
+
+
+def _create_more_application():
+ """
+ Create an `Application` instance that displays the "--MORE--".
+ """
+ from prompt_toolkit.shortcuts import create_prompt_application
+ registry = Registry()
+
+ @registry.add_binding(' ')
+ @registry.add_binding('y')
+ @registry.add_binding('Y')
+ @registry.add_binding(Keys.ControlJ)
+ @registry.add_binding(Keys.ControlI) # Tab.
+ def _(event):
+ event.cli.set_return_value(True)
+
+ @registry.add_binding('n')
+ @registry.add_binding('N')
+ @registry.add_binding('q')
+ @registry.add_binding('Q')
+ @registry.add_binding(Keys.ControlC)
+ def _(event):
+ event.cli.set_return_value(False)
+
+ return create_prompt_application(
+ '--MORE--', key_bindings_registry=registry, erase_when_done=True)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/bindings/emacs.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/bindings/emacs.py
new file mode 100644
index 0000000000..bccdb04ff3
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/bindings/emacs.py
@@ -0,0 +1,452 @@
+# pylint: disable=function-redefined
+from __future__ import unicode_literals
+from prompt_toolkit.buffer import SelectionType, indent, unindent
+from prompt_toolkit.keys import Keys
+from prompt_toolkit.enums import IncrementalSearchDirection, SEARCH_BUFFER, SYSTEM_BUFFER
+from prompt_toolkit.filters import Condition, EmacsMode, HasSelection, EmacsInsertMode, HasFocus, HasArg
+from prompt_toolkit.completion import CompleteEvent
+
+from .scroll import scroll_page_up, scroll_page_down
+from .named_commands import get_by_name
+from ..registry import Registry, ConditionalRegistry
+
+__all__ = (
+ 'load_emacs_bindings',
+ 'load_emacs_search_bindings',
+ 'load_emacs_system_bindings',
+ 'load_extra_emacs_page_navigation_bindings',
+)
+
+
+def load_emacs_bindings():
+ """
+ Some e-macs extensions.
+ """
+ # Overview of Readline emacs commands:
+ # http://www.catonmat.net/download/readline-emacs-editing-mode-cheat-sheet.pdf
+ registry = ConditionalRegistry(Registry(), EmacsMode())
+ handle = registry.add_binding
+
+ insert_mode = EmacsInsertMode()
+ has_selection = HasSelection()
+
+ @handle(Keys.Escape)
+ def _(event):
+ """
+ By default, ignore escape key.
+
+ (If we don't put this here, and Esc is followed by a key which sequence
+ is not handled, we'll insert an Escape character in the input stream.
+ Something we don't want and happens to easily in emacs mode.
+ Further, people can always use ControlQ to do a quoted insert.)
+ """
+ pass
+
+ handle(Keys.ControlA)(get_by_name('beginning-of-line'))
+ handle(Keys.ControlB)(get_by_name('backward-char'))
+ handle(Keys.ControlDelete, filter=insert_mode)(get_by_name('kill-word'))
+ handle(Keys.ControlE)(get_by_name('end-of-line'))
+ handle(Keys.ControlF)(get_by_name('forward-char'))
+ handle(Keys.ControlLeft)(get_by_name('backward-word'))
+ handle(Keys.ControlRight)(get_by_name('forward-word'))
+ handle(Keys.ControlX, 'r', 'y', filter=insert_mode)(get_by_name('yank'))
+ handle(Keys.ControlY, filter=insert_mode)(get_by_name('yank'))
+ handle(Keys.Escape, 'b')(get_by_name('backward-word'))
+ handle(Keys.Escape, 'c', filter=insert_mode)(get_by_name('capitalize-word'))
+ handle(Keys.Escape, 'd', filter=insert_mode)(get_by_name('kill-word'))
+ handle(Keys.Escape, 'f')(get_by_name('forward-word'))
+ handle(Keys.Escape, 'l', filter=insert_mode)(get_by_name('downcase-word'))
+ handle(Keys.Escape, 'u', filter=insert_mode)(get_by_name('uppercase-word'))
+ handle(Keys.Escape, 'y', filter=insert_mode)(get_by_name('yank-pop'))
+ handle(Keys.Escape, Keys.ControlH, filter=insert_mode)(get_by_name('backward-kill-word'))
+ handle(Keys.Escape, Keys.Backspace, filter=insert_mode)(get_by_name('backward-kill-word'))
+ handle(Keys.Escape, '\\', filter=insert_mode)(get_by_name('delete-horizontal-space'))
+
+ handle(Keys.ControlUnderscore, save_before=(lambda e: False), filter=insert_mode)(
+ get_by_name('undo'))
+
+ handle(Keys.ControlX, Keys.ControlU, save_before=(lambda e: False), filter=insert_mode)(
+ get_by_name('undo'))
+
+
+ handle(Keys.Escape, '<', filter= ~has_selection)(get_by_name('beginning-of-history'))
+ handle(Keys.Escape, '>', filter= ~has_selection)(get_by_name('end-of-history'))
+
+ handle(Keys.Escape, '.', filter=insert_mode)(get_by_name('yank-last-arg'))
+ handle(Keys.Escape, '_', filter=insert_mode)(get_by_name('yank-last-arg'))
+ handle(Keys.Escape, Keys.ControlY, filter=insert_mode)(get_by_name('yank-nth-arg'))
+ handle(Keys.Escape, '#', filter=insert_mode)(get_by_name('insert-comment'))
+ handle(Keys.ControlO)(get_by_name('operate-and-get-next'))
+
+ # ControlQ does a quoted insert. Not that for vt100 terminals, you have to
+ # disable flow control by running ``stty -ixon``, otherwise Ctrl-Q and
+ # Ctrl-S are captured by the terminal.
+ handle(Keys.ControlQ, filter= ~has_selection)(get_by_name('quoted-insert'))
+
+ handle(Keys.ControlX, '(')(get_by_name('start-kbd-macro'))
+ handle(Keys.ControlX, ')')(get_by_name('end-kbd-macro'))
+ handle(Keys.ControlX, 'e')(get_by_name('call-last-kbd-macro'))
+
+ @handle(Keys.ControlN)
+ def _(event):
+ " Next line. "
+ event.current_buffer.auto_down()
+
+ @handle(Keys.ControlP)
+ def _(event):
+ " Previous line. "
+ event.current_buffer.auto_up(count=event.arg)
+
+ def handle_digit(c):
+ """
+ Handle input of arguments.
+ The first number needs to be preceeded by escape.
+ """
+ @handle(c, filter=HasArg())
+ @handle(Keys.Escape, c)
+ def _(event):
+ event.append_to_arg_count(c)
+
+ for c in '0123456789':
+ handle_digit(c)
+
+ @handle(Keys.Escape, '-', filter=~HasArg())
+ def _(event):
+ """
+ """
+ if event._arg is None:
+ event.append_to_arg_count('-')
+
+ @handle('-', filter=Condition(lambda cli: cli.input_processor.arg == '-'))
+ def _(event):
+ """
+ When '-' is typed again, after exactly '-' has been given as an
+ argument, ignore this.
+ """
+ event.cli.input_processor.arg = '-'
+
+ is_returnable = Condition(
+ lambda cli: cli.current_buffer.accept_action.is_returnable)
+
+ # Meta + Newline: always accept input.
+ handle(Keys.Escape, Keys.ControlJ, filter=insert_mode & is_returnable)(
+ get_by_name('accept-line'))
+
+ def character_search(buff, char, count):
+ if count < 0:
+ match = buff.document.find_backwards(char, in_current_line=True, count=-count)
+ else:
+ match = buff.document.find(char, in_current_line=True, count=count)
+
+ if match is not None:
+ buff.cursor_position += match
+
+ @handle(Keys.ControlSquareClose, Keys.Any)
+ def _(event):
+ " When Ctl-] + a character is pressed. go to that character. "
+ # Also named 'character-search'
+ character_search(event.current_buffer, event.data, event.arg)
+
+ @handle(Keys.Escape, Keys.ControlSquareClose, Keys.Any)
+ def _(event):
+ " Like Ctl-], but backwards. "
+ # Also named 'character-search-backward'
+ character_search(event.current_buffer, event.data, -event.arg)
+
+ @handle(Keys.Escape, 'a')
+ def _(event):
+ " Previous sentence. "
+ # TODO:
+
+ @handle(Keys.Escape, 'e')
+ def _(event):
+ " Move to end of sentence. "
+ # TODO:
+
+ @handle(Keys.Escape, 't', filter=insert_mode)
+ def _(event):
+ """
+ Swap the last two words before the cursor.
+ """
+ # TODO
+
+ @handle(Keys.Escape, '*', filter=insert_mode)
+ def _(event):
+ """
+ `meta-*`: Insert all possible completions of the preceding text.
+ """
+ buff = event.current_buffer
+
+ # List all completions.
+ complete_event = CompleteEvent(text_inserted=False, completion_requested=True)
+ completions = list(buff.completer.get_completions(buff.document, complete_event))
+
+ # Insert them.
+ text_to_insert = ' '.join(c.text for c in completions)
+ buff.insert_text(text_to_insert)
+
+ @handle(Keys.ControlX, Keys.ControlX)
+ def _(event):
+ """
+ Move cursor back and forth between the start and end of the current
+ line.
+ """
+ buffer = event.current_buffer
+
+ if buffer.document.is_cursor_at_the_end_of_line:
+ buffer.cursor_position += buffer.document.get_start_of_line_position(after_whitespace=False)
+ else:
+ buffer.cursor_position += buffer.document.get_end_of_line_position()
+
+ @handle(Keys.ControlSpace)
+ def _(event):
+ """
+ Start of the selection (if the current buffer is not empty).
+ """
+ # Take the current cursor position as the start of this selection.
+ buff = event.current_buffer
+ if buff.text:
+ buff.start_selection(selection_type=SelectionType.CHARACTERS)
+
+ @handle(Keys.ControlG, filter= ~has_selection)
+ def _(event):
+ """
+ Control + G: Cancel completion menu and validation state.
+ """
+ event.current_buffer.complete_state = None
+ event.current_buffer.validation_error = None
+
+ @handle(Keys.ControlG, filter=has_selection)
+ def _(event):
+ """
+ Cancel selection.
+ """
+ event.current_buffer.exit_selection()
+
+ @handle(Keys.ControlW, filter=has_selection)
+ @handle(Keys.ControlX, 'r', 'k', filter=has_selection)
+ def _(event):
+ """
+ Cut selected text.
+ """
+ data = event.current_buffer.cut_selection()
+ event.cli.clipboard.set_data(data)
+
+ @handle(Keys.Escape, 'w', filter=has_selection)
+ def _(event):
+ """
+ Copy selected text.
+ """
+ data = event.current_buffer.copy_selection()
+ event.cli.clipboard.set_data(data)
+
+ @handle(Keys.Escape, Keys.Left)
+ def _(event):
+ """
+ Cursor to start of previous word.
+ """
+ buffer = event.current_buffer
+ buffer.cursor_position += buffer.document.find_previous_word_beginning(count=event.arg) or 0
+
+ @handle(Keys.Escape, Keys.Right)
+ def _(event):
+ """
+ Cursor to start of next word.
+ """
+ buffer = event.current_buffer
+ buffer.cursor_position += buffer.document.find_next_word_beginning(count=event.arg) or \
+ buffer.document.get_end_of_document_position()
+
+ @handle(Keys.Escape, '/', filter=insert_mode)
+ def _(event):
+ """
+ M-/: Complete.
+ """
+ b = event.current_buffer
+ if b.complete_state:
+ b.complete_next()
+ else:
+ event.cli.start_completion(select_first=True)
+
+ @handle(Keys.ControlC, '>', filter=has_selection)
+ def _(event):
+ """
+ Indent selected text.
+ """
+ buffer = event.current_buffer
+
+ buffer.cursor_position += buffer.document.get_start_of_line_position(after_whitespace=True)
+
+ from_, to = buffer.document.selection_range()
+ from_, _ = buffer.document.translate_index_to_position(from_)
+ to, _ = buffer.document.translate_index_to_position(to)
+
+ indent(buffer, from_, to + 1, count=event.arg)
+
+ @handle(Keys.ControlC, '<', filter=has_selection)
+ def _(event):
+ """
+ Unindent selected text.
+ """
+ buffer = event.current_buffer
+
+ from_, to = buffer.document.selection_range()
+ from_, _ = buffer.document.translate_index_to_position(from_)
+ to, _ = buffer.document.translate_index_to_position(to)
+
+ unindent(buffer, from_, to + 1, count=event.arg)
+
+ return registry
+
+
+def load_emacs_open_in_editor_bindings():
+ """
+ Pressing C-X C-E will open the buffer in an external editor.
+ """
+ registry = Registry()
+
+ registry.add_binding(Keys.ControlX, Keys.ControlE,
+ filter=EmacsMode() & ~HasSelection())(
+ get_by_name('edit-and-execute-command'))
+
+ return registry
+
+
+def load_emacs_system_bindings():
+ registry = ConditionalRegistry(Registry(), EmacsMode())
+ handle = registry.add_binding
+
+ has_focus = HasFocus(SYSTEM_BUFFER)
+
+ @handle(Keys.Escape, '!', filter= ~has_focus)
+ def _(event):
+ """
+ M-'!' opens the system prompt.
+ """
+ event.cli.push_focus(SYSTEM_BUFFER)
+
+ @handle(Keys.Escape, filter=has_focus)
+ @handle(Keys.ControlG, filter=has_focus)
+ @handle(Keys.ControlC, filter=has_focus)
+ def _(event):
+ """
+ Cancel system prompt.
+ """
+ event.cli.buffers[SYSTEM_BUFFER].reset()
+ event.cli.pop_focus()
+
+ @handle(Keys.ControlJ, filter=has_focus)
+ def _(event):
+ """
+ Run system command.
+ """
+ system_line = event.cli.buffers[SYSTEM_BUFFER]
+ event.cli.run_system_command(system_line.text)
+ system_line.reset(append_to_history=True)
+
+ # Focus previous buffer again.
+ event.cli.pop_focus()
+
+ return registry
+
+
+def load_emacs_search_bindings(get_search_state=None):
+ registry = ConditionalRegistry(Registry(), EmacsMode())
+ handle = registry.add_binding
+
+ has_focus = HasFocus(SEARCH_BUFFER)
+
+ assert get_search_state is None or callable(get_search_state)
+
+ if not get_search_state:
+ def get_search_state(cli): return cli.search_state
+
+ @handle(Keys.ControlG, filter=has_focus)
+ @handle(Keys.ControlC, filter=has_focus)
+ # NOTE: the reason for not also binding Escape to this one, is that we want
+ # Alt+Enter to accept input directly in incremental search mode.
+ def _(event):
+ """
+ Abort an incremental search and restore the original line.
+ """
+ search_buffer = event.cli.buffers[SEARCH_BUFFER]
+
+ search_buffer.reset()
+ event.cli.pop_focus()
+
+ @handle(Keys.ControlJ, filter=has_focus)
+ @handle(Keys.Escape, filter=has_focus, eager=True)
+ def _(event):
+ """
+ When enter pressed in isearch, quit isearch mode. (Multiline
+ isearch would be too complicated.)
+ """
+ input_buffer = event.cli.buffers.previous(event.cli)
+ search_buffer = event.cli.buffers[SEARCH_BUFFER]
+
+ # Update search state.
+ if search_buffer.text:
+ get_search_state(event.cli).text = search_buffer.text
+
+ # Apply search.
+ input_buffer.apply_search(get_search_state(event.cli), include_current_position=True)
+
+ # Add query to history of search line.
+ search_buffer.append_to_history()
+ search_buffer.reset()
+
+ # Focus previous document again.
+ event.cli.pop_focus()
+
+ @handle(Keys.ControlR, filter= ~has_focus)
+ def _(event):
+ get_search_state(event.cli).direction = IncrementalSearchDirection.BACKWARD
+ event.cli.push_focus(SEARCH_BUFFER)
+
+ @handle(Keys.ControlS, filter= ~has_focus)
+ def _(event):
+ get_search_state(event.cli).direction = IncrementalSearchDirection.FORWARD
+ event.cli.push_focus(SEARCH_BUFFER)
+
+ def incremental_search(cli, direction, count=1):
+ " Apply search, but keep search buffer focussed. "
+ # Update search_state.
+ search_state = get_search_state(cli)
+ direction_changed = search_state.direction != direction
+
+ search_state.text = cli.buffers[SEARCH_BUFFER].text
+ search_state.direction = direction
+
+ # Apply search to current buffer.
+ if not direction_changed:
+ input_buffer = cli.buffers.previous(cli)
+ input_buffer.apply_search(search_state,
+ include_current_position=False, count=count)
+
+ @handle(Keys.ControlR, filter=has_focus)
+ @handle(Keys.Up, filter=has_focus)
+ def _(event):
+ incremental_search(event.cli, IncrementalSearchDirection.BACKWARD, count=event.arg)
+
+ @handle(Keys.ControlS, filter=has_focus)
+ @handle(Keys.Down, filter=has_focus)
+ def _(event):
+ incremental_search(event.cli, IncrementalSearchDirection.FORWARD, count=event.arg)
+
+ return registry
+
+
+def load_extra_emacs_page_navigation_bindings():
+ """
+ Key bindings, for scrolling up and down through pages.
+ This are separate bindings, because GNU readline doesn't have them.
+ """
+ registry = ConditionalRegistry(Registry(), EmacsMode())
+ handle = registry.add_binding
+
+ handle(Keys.ControlV)(scroll_page_down)
+ handle(Keys.PageDown)(scroll_page_down)
+ handle(Keys.Escape, 'v')(scroll_page_up)
+ handle(Keys.PageUp)(scroll_page_up)
+
+ return registry
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/bindings/named_commands.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/bindings/named_commands.py
new file mode 100644
index 0000000000..f80c439fc6
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/bindings/named_commands.py
@@ -0,0 +1,578 @@
+"""
+Key bindings which are also known by GNU readline by the given names.
+
+See: http://www.delorie.com/gnu/docs/readline/rlman_13.html
+"""
+from __future__ import unicode_literals
+from prompt_toolkit.enums import IncrementalSearchDirection, SEARCH_BUFFER
+from prompt_toolkit.selection import PasteMode
+from six.moves import range
+import six
+
+from .completion import generate_completions, display_completions_like_readline
+from prompt_toolkit.document import Document
+from prompt_toolkit.enums import EditingMode
+from prompt_toolkit.key_binding.input_processor import KeyPress
+from prompt_toolkit.keys import Keys
+
+__all__ = (
+ 'get_by_name',
+)
+
+
+# Registry that maps the Readline command names to their handlers.
+_readline_commands = {}
+
+def register(name):
+ """
+ Store handler in the `_readline_commands` dictionary.
+ """
+ assert isinstance(name, six.text_type)
+ def decorator(handler):
+ assert callable(handler)
+
+ _readline_commands[name] = handler
+ return handler
+ return decorator
+
+
+def get_by_name(name):
+ """
+ Return the handler for the (Readline) command with the given name.
+ """
+ try:
+ return _readline_commands[name]
+ except KeyError:
+ raise KeyError('Unknown readline command: %r' % name)
+
+#
+# Commands for moving
+# See: http://www.delorie.com/gnu/docs/readline/rlman_14.html
+#
+
+@register('beginning-of-line')
+def beginning_of_line(event):
+ " Move to the start of the current line. "
+ buff = event.current_buffer
+ buff.cursor_position += buff.document.get_start_of_line_position(after_whitespace=False)
+
+
+@register('end-of-line')
+def end_of_line(event):
+ " Move to the end of the line. "
+ buff = event.current_buffer
+ buff.cursor_position += buff.document.get_end_of_line_position()
+
+
+@register('forward-char')
+def forward_char(event):
+ " Move forward a character. "
+ buff = event.current_buffer
+ buff.cursor_position += buff.document.get_cursor_right_position(count=event.arg)
+
+
+@register('backward-char')
+def backward_char(event):
+ " Move back a character. "
+ buff = event.current_buffer
+ buff.cursor_position += buff.document.get_cursor_left_position(count=event.arg)
+
+
+@register('forward-word')
+def forward_word(event):
+ """
+ Move forward to the end of the next word. Words are composed of letters and
+ digits.
+ """
+ buff = event.current_buffer
+ pos = buff.document.find_next_word_ending(count=event.arg)
+
+ if pos:
+ buff.cursor_position += pos
+
+
+@register('backward-word')
+def backward_word(event):
+ """
+ Move back to the start of the current or previous word. Words are composed
+ of letters and digits.
+ """
+ buff = event.current_buffer
+ pos = buff.document.find_previous_word_beginning(count=event.arg)
+
+ if pos:
+ buff.cursor_position += pos
+
+
+@register('clear-screen')
+def clear_screen(event):
+ """
+ Clear the screen and redraw everything at the top of the screen.
+ """
+ event.cli.renderer.clear()
+
+
+@register('redraw-current-line')
+def redraw_current_line(event):
+ """
+ Refresh the current line.
+ (Readline defines this command, but prompt-toolkit doesn't have it.)
+ """
+ pass
+
+#
+# Commands for manipulating the history.
+# See: http://www.delorie.com/gnu/docs/readline/rlman_15.html
+#
+
+@register('accept-line')
+def accept_line(event):
+ " Accept the line regardless of where the cursor is. "
+ b = event.current_buffer
+ b.accept_action.validate_and_handle(event.cli, b)
+
+
+@register('previous-history')
+def previous_history(event):
+ " Move `back` through the history list, fetching the previous command. "
+ event.current_buffer.history_backward(count=event.arg)
+
+
+@register('next-history')
+def next_history(event):
+ " Move `forward` through the history list, fetching the next command. "
+ event.current_buffer.history_forward(count=event.arg)
+
+
+@register('beginning-of-history')
+def beginning_of_history(event):
+ " Move to the first line in the history. "
+ event.current_buffer.go_to_history(0)
+
+
+@register('end-of-history')
+def end_of_history(event):
+ """
+ Move to the end of the input history, i.e., the line currently being entered.
+ """
+ event.current_buffer.history_forward(count=10**100)
+ buff = event.current_buffer
+ buff.go_to_history(len(buff._working_lines) - 1)
+
+
+@register('reverse-search-history')
+def reverse_search_history(event):
+ """
+ Search backward starting at the current line and moving `up` through
+ the history as necessary. This is an incremental search.
+ """
+ event.cli.current_search_state.direction = IncrementalSearchDirection.BACKWARD
+ event.cli.push_focus(SEARCH_BUFFER)
+
+
+#
+# Commands for changing text
+#
+
+@register('end-of-file')
+def end_of_file(event):
+ """
+ Exit.
+ """
+ event.cli.exit()
+
+
+@register('delete-char')
+def delete_char(event):
+ " Delete character before the cursor. "
+ deleted = event.current_buffer.delete(count=event.arg)
+ if not deleted:
+ event.cli.output.bell()
+
+
+@register('backward-delete-char')
+def backward_delete_char(event):
+ " Delete the character behind the cursor. "
+ if event.arg < 0:
+ # When a negative argument has been given, this should delete in front
+ # of the cursor.
+ deleted = event.current_buffer.delete(count=-event.arg)
+ else:
+ deleted = event.current_buffer.delete_before_cursor(count=event.arg)
+
+ if not deleted:
+ event.cli.output.bell()
+
+
+@register('self-insert')
+def self_insert(event):
+ " Insert yourself. "
+ event.current_buffer.insert_text(event.data * event.arg)
+
+
+@register('transpose-chars')
+def transpose_chars(event):
+ """
+ Emulate Emacs transpose-char behavior: at the beginning of the buffer,
+ do nothing. At the end of a line or buffer, swap the characters before
+ the cursor. Otherwise, move the cursor right, and then swap the
+ characters before the cursor.
+ """
+ b = event.current_buffer
+ p = b.cursor_position
+ if p == 0:
+ return
+ elif p == len(b.text) or b.text[p] == '\n':
+ b.swap_characters_before_cursor()
+ else:
+ b.cursor_position += b.document.get_cursor_right_position()
+ b.swap_characters_before_cursor()
+
+
+@register('uppercase-word')
+def uppercase_word(event):
+ """
+ Uppercase the current (or following) word.
+ """
+ buff = event.current_buffer
+
+ for i in range(event.arg):
+ pos = buff.document.find_next_word_ending()
+ words = buff.document.text_after_cursor[:pos]
+ buff.insert_text(words.upper(), overwrite=True)
+
+
+@register('downcase-word')
+def downcase_word(event):
+ """
+ Lowercase the current (or following) word.
+ """
+ buff = event.current_buffer
+
+ for i in range(event.arg): # XXX: not DRY: see meta_c and meta_u!!
+ pos = buff.document.find_next_word_ending()
+ words = buff.document.text_after_cursor[:pos]
+ buff.insert_text(words.lower(), overwrite=True)
+
+
+@register('capitalize-word')
+def capitalize_word(event):
+ """
+ Capitalize the current (or following) word.
+ """
+ buff = event.current_buffer
+
+ for i in range(event.arg):
+ pos = buff.document.find_next_word_ending()
+ words = buff.document.text_after_cursor[:pos]
+ buff.insert_text(words.title(), overwrite=True)
+
+
+@register('quoted-insert')
+def quoted_insert(event):
+ """
+ Add the next character typed to the line verbatim. This is how to insert
+ key sequences like C-q, for example.
+ """
+ event.cli.quoted_insert = True
+
+
+#
+# Killing and yanking.
+#
+
+@register('kill-line')
+def kill_line(event):
+ """
+ Kill the text from the cursor to the end of the line.
+
+ If we are at the end of the line, this should remove the newline.
+ (That way, it is possible to delete multiple lines by executing this
+ command multiple times.)
+ """
+ buff = event.current_buffer
+ if event.arg < 0:
+ deleted = buff.delete_before_cursor(count=-buff.document.get_start_of_line_position())
+ else:
+ if buff.document.current_char == '\n':
+ deleted = buff.delete(1)
+ else:
+ deleted = buff.delete(count=buff.document.get_end_of_line_position())
+ event.cli.clipboard.set_text(deleted)
+
+
+@register('kill-word')
+def kill_word(event):
+ """
+ Kill from point to the end of the current word, or if between words, to the
+ end of the next word. Word boundaries are the same as forward-word.
+ """
+ buff = event.current_buffer
+ pos = buff.document.find_next_word_ending(count=event.arg)
+
+ if pos:
+ deleted = buff.delete(count=pos)
+ event.cli.clipboard.set_text(deleted)
+
+
+@register('unix-word-rubout')
+def unix_word_rubout(event, WORD=True):
+ """
+ Kill the word behind point, using whitespace as a word boundary.
+ Usually bound to ControlW.
+ """
+ buff = event.current_buffer
+ pos = buff.document.find_start_of_previous_word(count=event.arg, WORD=WORD)
+
+ if pos is None:
+ # Nothing found? delete until the start of the document. (The
+ # input starts with whitespace and no words were found before the
+ # cursor.)
+ pos = - buff.cursor_position
+
+ if pos:
+ deleted = buff.delete_before_cursor(count=-pos)
+
+ # If the previous key press was also Control-W, concatenate deleted
+ # text.
+ if event.is_repeat:
+ deleted += event.cli.clipboard.get_data().text
+
+ event.cli.clipboard.set_text(deleted)
+ else:
+ # Nothing to delete. Bell.
+ event.cli.output.bell()
+
+
+@register('backward-kill-word')
+def backward_kill_word(event):
+ """
+ Kills the word before point, using "not a letter nor a digit" as a word boundary.
+ Usually bound to M-Del or M-Backspace.
+ """
+ unix_word_rubout(event, WORD=False)
+
+
+@register('delete-horizontal-space')
+def delete_horizontal_space(event):
+ " Delete all spaces and tabs around point. "
+ buff = event.current_buffer
+ text_before_cursor = buff.document.text_before_cursor
+ text_after_cursor = buff.document.text_after_cursor
+
+ delete_before = len(text_before_cursor) - len(text_before_cursor.rstrip('\t '))
+ delete_after = len(text_after_cursor) - len(text_after_cursor.lstrip('\t '))
+
+ buff.delete_before_cursor(count=delete_before)
+ buff.delete(count=delete_after)
+
+
+@register('unix-line-discard')
+def unix_line_discard(event):
+ """
+ Kill backward from the cursor to the beginning of the current line.
+ """
+ buff = event.current_buffer
+
+ if buff.document.cursor_position_col == 0 and buff.document.cursor_position > 0:
+ buff.delete_before_cursor(count=1)
+ else:
+ deleted = buff.delete_before_cursor(count=-buff.document.get_start_of_line_position())
+ event.cli.clipboard.set_text(deleted)
+
+
+@register('yank')
+def yank(event):
+ """
+ Paste before cursor.
+ """
+ event.current_buffer.paste_clipboard_data(
+ event.cli.clipboard.get_data(), count=event.arg, paste_mode=PasteMode.EMACS)
+
+@register('yank-nth-arg')
+def yank_nth_arg(event):
+ """
+ Insert the first argument of the previous command. With an argument, insert
+ the nth word from the previous command (start counting at 0).
+ """
+ n = (event.arg if event.arg_present else None)
+ event.current_buffer.yank_nth_arg(n)
+
+
+@register('yank-last-arg')
+def yank_last_arg(event):
+ """
+ Like `yank_nth_arg`, but if no argument has been given, yank the last word
+ of each line.
+ """
+ n = (event.arg if event.arg_present else None)
+ event.current_buffer.yank_last_arg(n)
+
+@register('yank-pop')
+def yank_pop(event):
+ """
+ Rotate the kill ring, and yank the new top. Only works following yank or
+ yank-pop.
+ """
+ buff = event.current_buffer
+ doc_before_paste = buff.document_before_paste
+ clipboard = event.cli.clipboard
+
+ if doc_before_paste is not None:
+ buff.document = doc_before_paste
+ clipboard.rotate()
+ buff.paste_clipboard_data(
+ clipboard.get_data(), paste_mode=PasteMode.EMACS)
+
+#
+# Completion.
+#
+
+@register('complete')
+def complete(event):
+ " Attempt to perform completion. "
+ display_completions_like_readline(event)
+
+
+@register('menu-complete')
+def menu_complete(event):
+ """
+ Generate completions, or go to the next completion. (This is the default
+ way of completing input in prompt_toolkit.)
+ """
+ generate_completions(event)
+
+
+@register('menu-complete-backward')
+def menu_complete_backward(event):
+ " Move backward through the list of possible completions. "
+ event.current_buffer.complete_previous()
+
+#
+# Keyboard macros.
+#
+
+@register('start-kbd-macro')
+def start_kbd_macro(event):
+ """
+ Begin saving the characters typed into the current keyboard macro.
+ """
+ event.cli.input_processor.start_macro()
+
+
+@register('end-kbd-macro')
+def start_kbd_macro(event):
+ """
+ Stop saving the characters typed into the current keyboard macro and save
+ the definition.
+ """
+ event.cli.input_processor.end_macro()
+
+
+@register('call-last-kbd-macro')
+def start_kbd_macro(event):
+ """
+ Re-execute the last keyboard macro defined, by making the characters in the
+ macro appear as if typed at the keyboard.
+ """
+ event.cli.input_processor.call_macro()
+
+
+@register('print-last-kbd-macro')
+def print_last_kbd_macro(event):
+ " Print the last keboard macro. "
+ # TODO: Make the format suitable for the inputrc file.
+ def print_macro():
+ for k in event.cli.input_processor.macro:
+ print(k)
+ event.cli.run_in_terminal(print_macro)
+
+#
+# Miscellaneous Commands.
+#
+
+@register('undo')
+def undo(event):
+ " Incremental undo. "
+ event.current_buffer.undo()
+
+
+@register('insert-comment')
+def insert_comment(event):
+ """
+ Without numeric argument, comment all lines.
+ With numeric argument, uncomment all lines.
+ In any case accept the input.
+ """
+ buff = event.current_buffer
+
+ # Transform all lines.
+ if event.arg != 1:
+ def change(line):
+ return line[1:] if line.startswith('#') else line
+ else:
+ def change(line):
+ return '#' + line
+
+ buff.document = Document(
+ text='\n'.join(map(change, buff.text.splitlines())),
+ cursor_position=0)
+
+ # Accept input.
+ buff.accept_action.validate_and_handle(event.cli, buff)
+
+
+@register('vi-editing-mode')
+def vi_editing_mode(event):
+ " Switch to Vi editing mode. "
+ event.cli.editing_mode = EditingMode.VI
+
+
+@register('emacs-editing-mode')
+def emacs_editing_mode(event):
+ " Switch to Emacs editing mode. "
+ event.cli.editing_mode = EditingMode.EMACS
+
+
+@register('prefix-meta')
+def prefix_meta(event):
+ """
+ Metafy the next character typed. This is for keyboards without a meta key.
+
+ Sometimes people also want to bind other keys to Meta, e.g. 'jj'::
+
+ registry.add_key_binding('j', 'j', filter=ViInsertMode())(prefix_meta)
+ """
+ event.cli.input_processor.feed(KeyPress(Keys.Escape))
+
+
+@register('operate-and-get-next')
+def operate_and_get_next(event):
+ """
+ Accept the current line for execution and fetch the next line relative to
+ the current line from the history for editing.
+ """
+ buff = event.current_buffer
+ new_index = buff.working_index + 1
+
+ # Accept the current input. (This will also redraw the interface in the
+ # 'done' state.)
+ buff.accept_action.validate_and_handle(event.cli, buff)
+
+ # Set the new index at the start of the next run.
+ def set_working_index():
+ if new_index < len(buff._working_lines):
+ buff.working_index = new_index
+
+ event.cli.pre_run_callables.append(set_working_index)
+
+
+@register('edit-and-execute-command')
+def edit_and_execute(event):
+ """
+ Invoke an editor on the current command line, and accept the result.
+ """
+ buff = event.current_buffer
+
+ buff.open_in_editor(event.cli)
+ buff.accept_action.validate_and_handle(event.cli, buff)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/bindings/scroll.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/bindings/scroll.py
new file mode 100644
index 0000000000..2cc58129ff
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/bindings/scroll.py
@@ -0,0 +1,185 @@
+"""
+Key bindings, for scrolling up and down through pages.
+
+This are separate bindings, because GNU readline doesn't have them, but
+they are very useful for navigating through long multiline buffers, like in
+Vi, Emacs, etc...
+"""
+from __future__ import unicode_literals
+
+from prompt_toolkit.layout.utils import find_window_for_buffer_name
+from six.moves import range
+
+__all__ = (
+ 'scroll_forward',
+ 'scroll_backward',
+ 'scroll_half_page_up',
+ 'scroll_half_page_down',
+ 'scroll_one_line_up',
+ 'scroll_one_line_down',
+)
+
+
+def _current_window_for_event(event):
+ """
+ Return the `Window` for the currently focussed Buffer.
+ """
+ return find_window_for_buffer_name(event.cli, event.cli.current_buffer_name)
+
+
+def scroll_forward(event, half=False):
+ """
+ Scroll window down.
+ """
+ w = _current_window_for_event(event)
+ b = event.cli.current_buffer
+
+ if w and w.render_info:
+ info = w.render_info
+ ui_content = info.ui_content
+
+ # Height to scroll.
+ scroll_height = info.window_height
+ if half:
+ scroll_height //= 2
+
+ # Calculate how many lines is equivalent to that vertical space.
+ y = b.document.cursor_position_row + 1
+ height = 0
+ while y < ui_content.line_count:
+ line_height = info.get_height_for_line(y)
+
+ if height + line_height < scroll_height:
+ height += line_height
+ y += 1
+ else:
+ break
+
+ b.cursor_position = b.document.translate_row_col_to_index(y, 0)
+
+
+def scroll_backward(event, half=False):
+ """
+ Scroll window up.
+ """
+ w = _current_window_for_event(event)
+ b = event.cli.current_buffer
+
+ if w and w.render_info:
+ info = w.render_info
+
+ # Height to scroll.
+ scroll_height = info.window_height
+ if half:
+ scroll_height //= 2
+
+ # Calculate how many lines is equivalent to that vertical space.
+ y = max(0, b.document.cursor_position_row - 1)
+ height = 0
+ while y > 0:
+ line_height = info.get_height_for_line(y)
+
+ if height + line_height < scroll_height:
+ height += line_height
+ y -= 1
+ else:
+ break
+
+ b.cursor_position = b.document.translate_row_col_to_index(y, 0)
+
+
+def scroll_half_page_down(event):
+ """
+ Same as ControlF, but only scroll half a page.
+ """
+ scroll_forward(event, half=True)
+
+
+def scroll_half_page_up(event):
+ """
+ Same as ControlB, but only scroll half a page.
+ """
+ scroll_backward(event, half=True)
+
+
+def scroll_one_line_down(event):
+ """
+ scroll_offset += 1
+ """
+ w = find_window_for_buffer_name(event.cli, event.cli.current_buffer_name)
+ b = event.cli.current_buffer
+
+ if w:
+ # When the cursor is at the top, move to the next line. (Otherwise, only scroll.)
+ if w.render_info:
+ info = w.render_info
+
+ if w.vertical_scroll < info.content_height - info.window_height:
+ if info.cursor_position.y <= info.configured_scroll_offsets.top:
+ b.cursor_position += b.document.get_cursor_down_position()
+
+ w.vertical_scroll += 1
+
+
+def scroll_one_line_up(event):
+ """
+ scroll_offset -= 1
+ """
+ w = find_window_for_buffer_name(event.cli, event.cli.current_buffer_name)
+ b = event.cli.current_buffer
+
+ if w:
+ # When the cursor is at the bottom, move to the previous line. (Otherwise, only scroll.)
+ if w.render_info:
+ info = w.render_info
+
+ if w.vertical_scroll > 0:
+ first_line_height = info.get_height_for_line(info.first_visible_line())
+
+ cursor_up = info.cursor_position.y - (info.window_height - 1 - first_line_height -
+ info.configured_scroll_offsets.bottom)
+
+ # Move cursor up, as many steps as the height of the first line.
+ # TODO: not entirely correct yet, in case of line wrapping and many long lines.
+ for _ in range(max(0, cursor_up)):
+ b.cursor_position += b.document.get_cursor_up_position()
+
+ # Scroll window
+ w.vertical_scroll -= 1
+
+
+def scroll_page_down(event):
+ """
+ Scroll page down. (Prefer the cursor at the top of the page, after scrolling.)
+ """
+ w = _current_window_for_event(event)
+ b = event.cli.current_buffer
+
+ if w and w.render_info:
+ # Scroll down one page.
+ line_index = max(w.render_info.last_visible_line(), w.vertical_scroll + 1)
+ w.vertical_scroll = line_index
+
+ b.cursor_position = b.document.translate_row_col_to_index(line_index, 0)
+ b.cursor_position += b.document.get_start_of_line_position(after_whitespace=True)
+
+
+def scroll_page_up(event):
+ """
+ Scroll page up. (Prefer the cursor at the bottom of the page, after scrolling.)
+ """
+ w = _current_window_for_event(event)
+ b = event.cli.current_buffer
+
+ if w and w.render_info:
+ # Put cursor at the first visible line. (But make sure that the cursor
+ # moves at least one line up.)
+ line_index = max(0, min(w.render_info.first_visible_line(),
+ b.document.cursor_position_row - 1))
+
+ b.cursor_position = b.document.translate_row_col_to_index(line_index, 0)
+ b.cursor_position += b.document.get_start_of_line_position(after_whitespace=True)
+
+ # Set the scroll offset. We can safely set it to zero; the Window will
+ # make sure that it scrolls at least until the cursor becomes visible.
+ w.vertical_scroll = 0
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/bindings/utils.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/bindings/utils.py
new file mode 100644
index 0000000000..caf08c5c1b
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/bindings/utils.py
@@ -0,0 +1,25 @@
+from __future__ import unicode_literals
+from prompt_toolkit.filters import CLIFilter, Always
+
+__all__ = (
+ 'create_handle_decorator',
+)
+
+def create_handle_decorator(registry, filter=Always()):
+ """
+ Create a key handle decorator, which is compatible with `Registry.handle`,
+ but will chain the given filter to every key binding.
+
+ :param filter: `CLIFilter`
+ """
+ assert isinstance(filter, CLIFilter)
+
+ def handle(*keys, **kw):
+ # Chain the given filter to the filter of this specific binding.
+ if 'filter' in kw:
+ kw['filter'] = kw['filter'] & filter
+ else:
+ kw['filter'] = filter
+
+ return registry.add_binding(*keys, **kw)
+ return handle
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/bindings/vi.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/bindings/vi.py
new file mode 100644
index 0000000000..72568ee273
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/bindings/vi.py
@@ -0,0 +1,1903 @@
+# pylint: disable=function-redefined
+from __future__ import unicode_literals
+from prompt_toolkit.buffer import ClipboardData, indent, unindent, reshape_text
+from prompt_toolkit.document import Document
+from prompt_toolkit.enums import IncrementalSearchDirection, SEARCH_BUFFER, SYSTEM_BUFFER
+from prompt_toolkit.filters import Filter, Condition, HasArg, Always, IsReadOnly
+from prompt_toolkit.filters.cli import ViNavigationMode, ViInsertMode, ViInsertMultipleMode, ViReplaceMode, ViSelectionMode, ViWaitingForTextObjectMode, ViDigraphMode, ViMode
+from prompt_toolkit.key_binding.digraphs import DIGRAPHS
+from prompt_toolkit.key_binding.vi_state import CharacterFind, InputMode
+from prompt_toolkit.keys import Keys
+from prompt_toolkit.layout.utils import find_window_for_buffer_name
+from prompt_toolkit.selection import SelectionType, SelectionState, PasteMode
+
+from .scroll import scroll_forward, scroll_backward, scroll_half_page_up, scroll_half_page_down, scroll_one_line_up, scroll_one_line_down, scroll_page_up, scroll_page_down
+from .named_commands import get_by_name
+from ..registry import Registry, ConditionalRegistry, BaseRegistry
+
+import prompt_toolkit.filters as filters
+from six.moves import range
+import codecs
+import six
+import string
+
+try:
+ from itertools import accumulate
+except ImportError: # < Python 3.2
+ def accumulate(iterable):
+ " Super simpel 'accumulate' implementation. "
+ total = 0
+ for item in iterable:
+ total += item
+ yield total
+
+__all__ = (
+ 'load_vi_bindings',
+ 'load_vi_search_bindings',
+ 'load_vi_system_bindings',
+ 'load_extra_vi_page_navigation_bindings',
+)
+
+if six.PY2:
+ ascii_lowercase = string.ascii_lowercase.decode('ascii')
+else:
+ ascii_lowercase = string.ascii_lowercase
+
+vi_register_names = ascii_lowercase + '0123456789'
+
+
+class TextObjectType(object):
+ EXCLUSIVE = 'EXCLUSIVE'
+ INCLUSIVE = 'INCLUSIVE'
+ LINEWISE = 'LINEWISE'
+ BLOCK = 'BLOCK'
+
+
+class TextObject(object):
+ """
+ Return struct for functions wrapped in ``text_object``.
+ Both `start` and `end` are relative to the current cursor position.
+ """
+ def __init__(self, start, end=0, type=TextObjectType.EXCLUSIVE):
+ self.start = start
+ self.end = end
+ self.type = type
+
+ @property
+ def selection_type(self):
+ if self.type == TextObjectType.LINEWISE:
+ return SelectionType.LINES
+ if self.type == TextObjectType.BLOCK:
+ return SelectionType.BLOCK
+ else:
+ return SelectionType.CHARACTERS
+
+ def sorted(self):
+ """
+ Return a (start, end) tuple where start <= end.
+ """
+ if self.start < self.end:
+ return self.start, self.end
+ else:
+ return self.end, self.start
+
+ def operator_range(self, document):
+ """
+ Return a (start, end) tuple with start <= end that indicates the range
+ operators should operate on.
+ `buffer` is used to get start and end of line positions.
+ """
+ start, end = self.sorted()
+ doc = document
+
+ if (self.type == TextObjectType.EXCLUSIVE and
+ doc.translate_index_to_position(end + doc.cursor_position)[1] == 0):
+ # If the motion is exclusive and the end of motion is on the first
+ # column, the end position becomes end of previous line.
+ end -= 1
+ if self.type == TextObjectType.INCLUSIVE:
+ end += 1
+ if self.type == TextObjectType.LINEWISE:
+ # Select whole lines
+ row, col = doc.translate_index_to_position(start + doc.cursor_position)
+ start = doc.translate_row_col_to_index(row, 0) - doc.cursor_position
+ row, col = doc.translate_index_to_position(end + doc.cursor_position)
+ end = doc.translate_row_col_to_index(row, len(doc.lines[row])) - doc.cursor_position
+ return start, end
+
+ def get_line_numbers(self, buffer):
+ """
+ Return a (start_line, end_line) pair.
+ """
+ # Get absolute cursor positions from the text object.
+ from_, to = self.operator_range(buffer.document)
+ from_ += buffer.cursor_position
+ to += buffer.cursor_position
+
+ # Take the start of the lines.
+ from_, _ = buffer.document.translate_index_to_position(from_)
+ to, _ = buffer.document.translate_index_to_position(to)
+
+ return from_, to
+
+ def cut(self, buffer):
+ """
+ Turn text object into `ClipboardData` instance.
+ """
+ from_, to = self.operator_range(buffer.document)
+
+ from_ += buffer.cursor_position
+ to += buffer.cursor_position
+ to -= 1 # SelectionState does not include the end position, `operator_range` does.
+
+ document = Document(buffer.text, to, SelectionState(
+ original_cursor_position=from_, type=self.selection_type))
+
+ new_document, clipboard_data = document.cut_selection()
+ return new_document, clipboard_data
+
+
+def create_text_object_decorator(registry):
+ """
+ Create a decorator that can be used to register Vi text object implementations.
+ """
+ assert isinstance(registry, BaseRegistry)
+
+ operator_given = ViWaitingForTextObjectMode()
+ navigation_mode = ViNavigationMode()
+ selection_mode = ViSelectionMode()
+
+ def text_object_decorator(*keys, **kw):
+ """
+ Register a text object function.
+
+ Usage::
+
+ @text_object('w', filter=..., no_move_handler=False)
+ def handler(event):
+ # Return a text object for this key.
+ return TextObject(...)
+
+ :param no_move_handler: Disable the move handler in navigation mode.
+ (It's still active in selection mode.)
+ """
+ filter = kw.pop('filter', Always())
+ no_move_handler = kw.pop('no_move_handler', False)
+ no_selection_handler = kw.pop('no_selection_handler', False)
+ eager = kw.pop('eager', False)
+ assert not kw
+
+ def decorator(text_object_func):
+ assert callable(text_object_func)
+
+ @registry.add_binding(*keys, filter=operator_given & filter, eager=eager)
+ def _(event):
+ # Arguments are multiplied.
+ vi_state = event.cli.vi_state
+ event._arg = (vi_state.operator_arg or 1) * (event.arg or 1)
+
+ # Call the text object handler.
+ text_obj = text_object_func(event)
+ if text_obj is not None:
+ assert isinstance(text_obj, TextObject)
+
+ # Call the operator function with the text object.
+ vi_state.operator_func(event, text_obj)
+
+ # Clear operator.
+ event.cli.vi_state.operator_func = None
+ event.cli.vi_state.operator_arg = None
+
+ # Register a move operation. (Doesn't need an operator.)
+ if not no_move_handler:
+ @registry.add_binding(*keys, filter=~operator_given & filter & navigation_mode, eager=eager)
+ def _(event):
+ " Move handler for navigation mode. "
+ text_object = text_object_func(event)
+ event.current_buffer.cursor_position += text_object.start
+
+ # Register a move selection operation.
+ if not no_selection_handler:
+ @registry.add_binding(*keys, filter=~operator_given & filter & selection_mode, eager=eager)
+ def _(event):
+ " Move handler for selection mode. "
+ text_object = text_object_func(event)
+ buff = event.current_buffer
+
+ # When the text object has both a start and end position, like 'i(' or 'iw',
+ # Turn this into a selection, otherwise the cursor.
+ if text_object.end:
+ # Take selection positions from text object.
+ start, end = text_object.operator_range(buff.document)
+ start += buff.cursor_position
+ end += buff.cursor_position
+
+ buff.selection_state.original_cursor_position = start
+ buff.cursor_position = end
+
+ # Take selection type from text object.
+ if text_object.type == TextObjectType.LINEWISE:
+ buff.selection_state.type = SelectionType.LINES
+ else:
+ buff.selection_state.type = SelectionType.CHARACTERS
+ else:
+ event.current_buffer.cursor_position += text_object.start
+
+ # Make it possible to chain @text_object decorators.
+ return text_object_func
+
+ return decorator
+ return text_object_decorator
+
+
+def create_operator_decorator(registry):
+ """
+ Create a decorator that can be used for registering Vi operators.
+ """
+ assert isinstance(registry, BaseRegistry)
+
+ operator_given = ViWaitingForTextObjectMode()
+ navigation_mode = ViNavigationMode()
+ selection_mode = ViSelectionMode()
+
+ def operator_decorator(*keys, **kw):
+ """
+ Register a Vi operator.
+
+ Usage::
+
+ @operator('d', filter=...)
+ def handler(cli, text_object):
+ # Do something with the text object here.
+ """
+ filter = kw.pop('filter', Always())
+ eager = kw.pop('eager', False)
+ assert not kw
+
+ def decorator(operator_func):
+ @registry.add_binding(*keys, filter=~operator_given & filter & navigation_mode, eager=eager)
+ def _(event):
+ """
+ Handle operator in navigation mode.
+ """
+ # When this key binding is matched, only set the operator
+ # function in the ViState. We should execute it after a text
+ # object has been received.
+ event.cli.vi_state.operator_func = operator_func
+ event.cli.vi_state.operator_arg = event.arg
+
+ @registry.add_binding(*keys, filter=~operator_given & filter & selection_mode, eager=eager)
+ def _(event):
+ """
+ Handle operator in selection mode.
+ """
+ buff = event.current_buffer
+ selection_state = buff.selection_state
+
+ # Create text object from selection.
+ if selection_state.type == SelectionType.LINES:
+ text_obj_type = TextObjectType.LINEWISE
+ elif selection_state.type == SelectionType.BLOCK:
+ text_obj_type = TextObjectType.BLOCK
+ else:
+ text_obj_type = TextObjectType.INCLUSIVE
+
+ text_object = TextObject(
+ selection_state.original_cursor_position - buff.cursor_position,
+ type=text_obj_type)
+
+ # Execute operator.
+ operator_func(event, text_object)
+
+ # Quit selection mode.
+ buff.selection_state = None
+
+ return operator_func
+ return decorator
+ return operator_decorator
+
+
+def load_vi_bindings(get_search_state=None):
+ """
+ Vi extensions.
+
+ # Overview of Readline Vi commands:
+ # http://www.catonmat.net/download/bash-vi-editing-mode-cheat-sheet.pdf
+
+ :param get_search_state: None or a callable that takes a
+ CommandLineInterface and returns a SearchState.
+ """
+ # Note: Some key bindings have the "~IsReadOnly()" filter added. This
+ # prevents the handler to be executed when the focus is on a
+ # read-only buffer.
+ # This is however only required for those that change the ViState to
+ # INSERT mode. The `Buffer` class itself throws the
+ # `EditReadOnlyBuffer` exception for any text operations which is
+ # handled correctly. There is no need to add "~IsReadOnly" to all key
+ # bindings that do text manipulation.
+
+ registry = ConditionalRegistry(Registry(), ViMode())
+ handle = registry.add_binding
+
+ # Default get_search_state.
+ if get_search_state is None:
+ def get_search_state(cli): return cli.search_state
+
+ # (Note: Always take the navigation bindings in read-only mode, even when
+ # ViState says different.)
+ navigation_mode = ViNavigationMode()
+ insert_mode = ViInsertMode()
+ insert_multiple_mode = ViInsertMultipleMode()
+ replace_mode = ViReplaceMode()
+ selection_mode = ViSelectionMode()
+ operator_given = ViWaitingForTextObjectMode()
+ digraph_mode = ViDigraphMode()
+
+ vi_transform_functions = [
+ # Rot 13 transformation
+ (('g', '?'), Always(), lambda string: codecs.encode(string, 'rot_13')),
+
+ # To lowercase
+ (('g', 'u'), Always(), lambda string: string.lower()),
+
+ # To uppercase.
+ (('g', 'U'), Always(), lambda string: string.upper()),
+
+ # Swap case.
+ (('g', '~'), Always(), lambda string: string.swapcase()),
+ (('~', ), Condition(lambda cli: cli.vi_state.tilde_operator), lambda string: string.swapcase()),
+ ]
+
+ # Insert a character literally (quoted insert).
+ handle(Keys.ControlV, filter=insert_mode)(get_by_name('quoted-insert'))
+
+ @handle(Keys.Escape)
+ def _(event):
+ """
+ Escape goes to vi navigation mode.
+ """
+ buffer = event.current_buffer
+ vi_state = event.cli.vi_state
+
+ if vi_state.input_mode in (InputMode.INSERT, InputMode.REPLACE):
+ buffer.cursor_position += buffer.document.get_cursor_left_position()
+
+ vi_state.reset(InputMode.NAVIGATION)
+
+ if bool(buffer.selection_state):
+ buffer.exit_selection()
+
+ @handle('k', filter=selection_mode)
+ def _(event):
+ """
+ Arrow up in selection mode.
+ """
+ event.current_buffer.cursor_up(count=event.arg)
+
+ @handle('j', filter=selection_mode)
+ def _(event):
+ """
+ Arrow down in selection mode.
+ """
+ event.current_buffer.cursor_down(count=event.arg)
+
+ @handle(Keys.Up, filter=navigation_mode)
+ @handle(Keys.ControlP, filter=navigation_mode)
+ def _(event):
+ """
+ Arrow up and ControlP in navigation mode go up.
+ """
+ event.current_buffer.auto_up(count=event.arg)
+
+ @handle('k', filter=navigation_mode)
+ def _(event):
+ """
+ Go up, but if we enter a new history entry, move to the start of the
+ line.
+ """
+ event.current_buffer.auto_up(
+ count=event.arg, go_to_start_of_line_if_history_changes=True)
+
+ @handle(Keys.Down, filter=navigation_mode)
+ @handle(Keys.ControlN, filter=navigation_mode)
+ def _(event):
+ """
+ Arrow down and Control-N in navigation mode.
+ """
+ event.current_buffer.auto_down(count=event.arg)
+
+ @handle('j', filter=navigation_mode)
+ def _(event):
+ """
+ Go down, but if we enter a new history entry, go to the start of the line.
+ """
+ event.current_buffer.auto_down(
+ count=event.arg, go_to_start_of_line_if_history_changes=True)
+
+ @handle(Keys.ControlH, filter=navigation_mode)
+ @handle(Keys.Backspace, filter=navigation_mode)
+ def _(event):
+ """
+ In navigation-mode, move cursor.
+ """
+ event.current_buffer.cursor_position += \
+ event.current_buffer.document.get_cursor_left_position(count=event.arg)
+
+ @handle(Keys.ControlN, filter=insert_mode)
+ def _(event):
+ b = event.current_buffer
+
+ if b.complete_state:
+ b.complete_next()
+ else:
+ event.cli.start_completion(select_first=True)
+
+ @handle(Keys.ControlP, filter=insert_mode)
+ def _(event):
+ """
+ Control-P: To previous completion.
+ """
+ b = event.current_buffer
+
+ if b.complete_state:
+ b.complete_previous()
+ else:
+ event.cli.start_completion(select_last=True)
+
+ @handle(Keys.ControlY, filter=insert_mode)
+ def _(event):
+ """
+ Accept current completion.
+ """
+ event.current_buffer.complete_state = None
+
+ @handle(Keys.ControlE, filter=insert_mode)
+ def _(event):
+ """
+ Cancel completion. Go back to originally typed text.
+ """
+ event.current_buffer.cancel_completion()
+
+ @handle(Keys.ControlJ, filter=navigation_mode) # XXX: only if the selected buffer has a return handler.
+ def _(event):
+ """
+ In navigation mode, pressing enter will always return the input.
+ """
+ b = event.current_buffer
+
+ if b.accept_action.is_returnable:
+ b.accept_action.validate_and_handle(event.cli, b)
+
+ # ** In navigation mode **
+
+ # List of navigation commands: http://hea-www.harvard.edu/~fine/Tech/vi.html
+
+ @handle(Keys.Insert, filter=navigation_mode)
+ def _(event):
+ " Presing the Insert key. "
+ event.cli.vi_state.input_mode = InputMode.INSERT
+
+ @handle('a', filter=navigation_mode & ~IsReadOnly())
+ # ~IsReadOnly, because we want to stay in navigation mode for
+ # read-only buffers.
+ def _(event):
+ event.current_buffer.cursor_position += event.current_buffer.document.get_cursor_right_position()
+ event.cli.vi_state.input_mode = InputMode.INSERT
+
+ @handle('A', filter=navigation_mode & ~IsReadOnly())
+ def _(event):
+ event.current_buffer.cursor_position += event.current_buffer.document.get_end_of_line_position()
+ event.cli.vi_state.input_mode = InputMode.INSERT
+
+ @handle('C', filter=navigation_mode & ~IsReadOnly())
+ def _(event):
+ """
+ # Change to end of line.
+ # Same as 'c$' (which is implemented elsewhere.)
+ """
+ buffer = event.current_buffer
+
+ deleted = buffer.delete(count=buffer.document.get_end_of_line_position())
+ event.cli.clipboard.set_text(deleted)
+ event.cli.vi_state.input_mode = InputMode.INSERT
+
+ @handle('c', 'c', filter=navigation_mode & ~IsReadOnly())
+ @handle('S', filter=navigation_mode & ~IsReadOnly())
+ def _(event): # TODO: implement 'arg'
+ """
+ Change current line
+ """
+ buffer = event.current_buffer
+
+ # We copy the whole line.
+ data = ClipboardData(buffer.document.current_line, SelectionType.LINES)
+ event.cli.clipboard.set_data(data)
+
+ # But we delete after the whitespace
+ buffer.cursor_position += buffer.document.get_start_of_line_position(after_whitespace=True)
+ buffer.delete(count=buffer.document.get_end_of_line_position())
+ event.cli.vi_state.input_mode = InputMode.INSERT
+
+ @handle('D', filter=navigation_mode)
+ def _(event):
+ buffer = event.current_buffer
+ deleted = buffer.delete(count=buffer.document.get_end_of_line_position())
+ event.cli.clipboard.set_text(deleted)
+
+ @handle('d', 'd', filter=navigation_mode)
+ def _(event):
+ """
+ Delete line. (Or the following 'n' lines.)
+ """
+ buffer = event.current_buffer
+
+ # Split string in before/deleted/after text.
+ lines = buffer.document.lines
+
+ before = '\n'.join(lines[:buffer.document.cursor_position_row])
+ deleted = '\n'.join(lines[buffer.document.cursor_position_row:
+ buffer.document.cursor_position_row + event.arg])
+ after = '\n'.join(lines[buffer.document.cursor_position_row + event.arg:])
+
+ # Set new text.
+ if before and after:
+ before = before + '\n'
+
+ # Set text and cursor position.
+ buffer.document = Document(
+ text=before + after,
+ # Cursor At the start of the first 'after' line, after the leading whitespace.
+ cursor_position = len(before) + len(after) - len(after.lstrip(' ')))
+
+ # Set clipboard data
+ event.cli.clipboard.set_data(ClipboardData(deleted, SelectionType.LINES))
+
+ @handle('x', filter=selection_mode)
+ def _(event):
+ """
+ Cut selection.
+ ('x' is not an operator.)
+ """
+ clipboard_data = event.current_buffer.cut_selection()
+ event.cli.clipboard.set_data(clipboard_data)
+
+ @handle('i', filter=navigation_mode & ~IsReadOnly())
+ def _(event):
+ event.cli.vi_state.input_mode = InputMode.INSERT
+
+ @handle('I', filter=navigation_mode & ~IsReadOnly())
+ def _(event):
+ event.cli.vi_state.input_mode = InputMode.INSERT
+ event.current_buffer.cursor_position += \
+ event.current_buffer.document.get_start_of_line_position(after_whitespace=True)
+
+ @Condition
+ def in_block_selection(cli):
+ buff = cli.current_buffer
+ return buff.selection_state and buff.selection_state.type == SelectionType.BLOCK
+
+ @handle('I', filter=in_block_selection & ~IsReadOnly())
+ def go_to_block_selection(event, after=False):
+ " Insert in block selection mode. "
+ buff = event.current_buffer
+
+ # Store all cursor positions.
+ positions = []
+
+ if after:
+ def get_pos(from_to):
+ return from_to[1] + 1
+ else:
+ def get_pos(from_to):
+ return from_to[0]
+
+ for i, from_to in enumerate(buff.document.selection_ranges()):
+ positions.append(get_pos(from_to))
+ if i == 0:
+ buff.cursor_position = get_pos(from_to)
+
+ buff.multiple_cursor_positions = positions
+
+ # Go to 'INSERT_MULTIPLE' mode.
+ event.cli.vi_state.input_mode = InputMode.INSERT_MULTIPLE
+ buff.exit_selection()
+
+ @handle('A', filter=in_block_selection & ~IsReadOnly())
+ def _(event):
+ go_to_block_selection(event, after=True)
+
+ @handle('J', filter=navigation_mode & ~IsReadOnly())
+ def _(event):
+ " Join lines. "
+ for i in range(event.arg):
+ event.current_buffer.join_next_line()
+
+ @handle('g', 'J', filter=navigation_mode & ~IsReadOnly())
+ def _(event):
+ " Join lines without space. "
+ for i in range(event.arg):
+ event.current_buffer.join_next_line(separator='')
+
+ @handle('J', filter=selection_mode & ~IsReadOnly())
+ def _(event):
+ " Join selected lines. "
+ event.current_buffer.join_selected_lines()
+
+ @handle('g', 'J', filter=selection_mode & ~IsReadOnly())
+ def _(event):
+ " Join selected lines without space. "
+ event.current_buffer.join_selected_lines(separator='')
+
+ @handle('p', filter=navigation_mode)
+ def _(event):
+ """
+ Paste after
+ """
+ event.current_buffer.paste_clipboard_data(
+ event.cli.clipboard.get_data(),
+ count=event.arg,
+ paste_mode=PasteMode.VI_AFTER)
+
+ @handle('P', filter=navigation_mode)
+ def _(event):
+ """
+ Paste before
+ """
+ event.current_buffer.paste_clipboard_data(
+ event.cli.clipboard.get_data(),
+ count=event.arg,
+ paste_mode=PasteMode.VI_BEFORE)
+
+ @handle('"', Keys.Any, 'p', filter=navigation_mode)
+ def _(event):
+ " Paste from named register. "
+ c = event.key_sequence[1].data
+ if c in vi_register_names:
+ data = event.cli.vi_state.named_registers.get(c)
+ if data:
+ event.current_buffer.paste_clipboard_data(
+ data, count=event.arg, paste_mode=PasteMode.VI_AFTER)
+
+ @handle('"', Keys.Any, 'P', filter=navigation_mode)
+ def _(event):
+ " Paste (before) from named register. "
+ c = event.key_sequence[1].data
+ if c in vi_register_names:
+ data = event.cli.vi_state.named_registers.get(c)
+ if data:
+ event.current_buffer.paste_clipboard_data(
+ data, count=event.arg, paste_mode=PasteMode.VI_BEFORE)
+
+ @handle('r', Keys.Any, filter=navigation_mode)
+ def _(event):
+ """
+ Replace single character under cursor
+ """
+ event.current_buffer.insert_text(event.data * event.arg, overwrite=True)
+ event.current_buffer.cursor_position -= 1
+
+ @handle('R', filter=navigation_mode)
+ def _(event):
+ """
+ Go to 'replace'-mode.
+ """
+ event.cli.vi_state.input_mode = InputMode.REPLACE
+
+ @handle('s', filter=navigation_mode & ~IsReadOnly())
+ def _(event):
+ """
+ Substitute with new text
+ (Delete character(s) and go to insert mode.)
+ """
+ text = event.current_buffer.delete(count=event.arg)
+ event.cli.clipboard.set_text(text)
+ event.cli.vi_state.input_mode = InputMode.INSERT
+
+ @handle('u', filter=navigation_mode, save_before=(lambda e: False))
+ def _(event):
+ for i in range(event.arg):
+ event.current_buffer.undo()
+
+ @handle('V', filter=navigation_mode)
+ def _(event):
+ """
+ Start lines selection.
+ """
+ event.current_buffer.start_selection(selection_type=SelectionType.LINES)
+
+ @handle(Keys.ControlV, filter=navigation_mode)
+ def _(event):
+ " Enter block selection mode. "
+ event.current_buffer.start_selection(selection_type=SelectionType.BLOCK)
+
+ @handle('V', filter=selection_mode)
+ def _(event):
+ """
+ Exit line selection mode, or go from non line selection mode to line
+ selection mode.
+ """
+ selection_state = event.current_buffer.selection_state
+
+ if selection_state.type != SelectionType.LINES:
+ selection_state.type = SelectionType.LINES
+ else:
+ event.current_buffer.exit_selection()
+
+ @handle('v', filter=navigation_mode)
+ def _(event):
+ " Enter character selection mode. "
+ event.current_buffer.start_selection(selection_type=SelectionType.CHARACTERS)
+
+ @handle('v', filter=selection_mode)
+ def _(event):
+ """
+ Exit character selection mode, or go from non-character-selection mode
+ to character selection mode.
+ """
+ selection_state = event.current_buffer.selection_state
+
+ if selection_state.type != SelectionType.CHARACTERS:
+ selection_state.type = SelectionType.CHARACTERS
+ else:
+ event.current_buffer.exit_selection()
+
+ @handle(Keys.ControlV, filter=selection_mode)
+ def _(event):
+ """
+ Exit block selection mode, or go from non block selection mode to block
+ selection mode.
+ """
+ selection_state = event.current_buffer.selection_state
+
+ if selection_state.type != SelectionType.BLOCK:
+ selection_state.type = SelectionType.BLOCK
+ else:
+ event.current_buffer.exit_selection()
+
+
+ @handle('a', 'w', filter=selection_mode)
+ @handle('a', 'W', filter=selection_mode)
+ def _(event):
+ """
+ Switch from visual linewise mode to visual characterwise mode.
+ """
+ buffer = event.current_buffer
+
+ if buffer.selection_state and buffer.selection_state.type == SelectionType.LINES:
+ buffer.selection_state.type = SelectionType.CHARACTERS
+
+ @handle('x', filter=navigation_mode)
+ def _(event):
+ """
+ Delete character.
+ """
+ text = event.current_buffer.delete(count=event.arg)
+ event.cli.clipboard.set_text(text)
+
+ @handle('X', filter=navigation_mode)
+ def _(event):
+ text = event.current_buffer.delete_before_cursor()
+ event.cli.clipboard.set_text(text)
+
+ @handle('y', 'y', filter=navigation_mode)
+ @handle('Y', filter=navigation_mode)
+ def _(event):
+ """
+ Yank the whole line.
+ """
+ text = '\n'.join(event.current_buffer.document.lines_from_current[:event.arg])
+ event.cli.clipboard.set_data(ClipboardData(text, SelectionType.LINES))
+
+ @handle('+', filter=navigation_mode)
+ def _(event):
+ """
+ Move to first non whitespace of next line
+ """
+ buffer = event.current_buffer
+ buffer.cursor_position += buffer.document.get_cursor_down_position(count=event.arg)
+ buffer.cursor_position += buffer.document.get_start_of_line_position(after_whitespace=True)
+
+ @handle('-', filter=navigation_mode)
+ def _(event):
+ """
+ Move to first non whitespace of previous line
+ """
+ buffer = event.current_buffer
+ buffer.cursor_position += buffer.document.get_cursor_up_position(count=event.arg)
+ buffer.cursor_position += buffer.document.get_start_of_line_position(after_whitespace=True)
+
+ @handle('>', '>', filter=navigation_mode)
+ def _(event):
+ """
+ Indent lines.
+ """
+ buffer = event.current_buffer
+ current_row = buffer.document.cursor_position_row
+ indent(buffer, current_row, current_row + event.arg)
+
+ @handle('<', '<', filter=navigation_mode)
+ def _(event):
+ """
+ Unindent lines.
+ """
+ current_row = event.current_buffer.document.cursor_position_row
+ unindent(event.current_buffer, current_row, current_row + event.arg)
+
+ @handle('O', filter=navigation_mode & ~IsReadOnly())
+ def _(event):
+ """
+ Open line above and enter insertion mode
+ """
+ event.current_buffer.insert_line_above(
+ copy_margin=not event.cli.in_paste_mode)
+ event.cli.vi_state.input_mode = InputMode.INSERT
+
+ @handle('o', filter=navigation_mode & ~IsReadOnly())
+ def _(event):
+ """
+ Open line below and enter insertion mode
+ """
+ event.current_buffer.insert_line_below(
+ copy_margin=not event.cli.in_paste_mode)
+ event.cli.vi_state.input_mode = InputMode.INSERT
+
+ @handle('~', filter=navigation_mode)
+ def _(event):
+ """
+ Reverse case of current character and move cursor forward.
+ """
+ buffer = event.current_buffer
+ c = buffer.document.current_char
+
+ if c is not None and c != '\n':
+ buffer.insert_text(c.swapcase(), overwrite=True)
+
+ @handle('g', 'u', 'u', filter=navigation_mode & ~IsReadOnly())
+ def _(event):
+ " Lowercase current line. "
+ buff = event.current_buffer
+ buff.transform_current_line(lambda s: s.lower())
+
+ @handle('g', 'U', 'U', filter=navigation_mode & ~IsReadOnly())
+ def _(event):
+ " Uppercase current line. "
+ buff = event.current_buffer
+ buff.transform_current_line(lambda s: s.upper())
+
+ @handle('g', '~', '~', filter=navigation_mode & ~IsReadOnly())
+ def _(event):
+ " Swap case of the current line. "
+ buff = event.current_buffer
+ buff.transform_current_line(lambda s: s.swapcase())
+
+ @handle('#', filter=navigation_mode)
+ def _(event):
+ """
+ Go to previous occurence of this word.
+ """
+ b = event.cli.current_buffer
+
+ search_state = get_search_state(event.cli)
+ search_state.text = b.document.get_word_under_cursor()
+ search_state.direction = IncrementalSearchDirection.BACKWARD
+
+ b.apply_search(search_state, count=event.arg,
+ include_current_position=False)
+
+ @handle('*', filter=navigation_mode)
+ def _(event):
+ """
+ Go to next occurence of this word.
+ """
+ b = event.cli.current_buffer
+
+ search_state = get_search_state(event.cli)
+ search_state.text = b.document.get_word_under_cursor()
+ search_state.direction = IncrementalSearchDirection.FORWARD
+
+ b.apply_search(search_state, count=event.arg,
+ include_current_position=False)
+
+ @handle('(', filter=navigation_mode)
+ def _(event):
+ # TODO: go to begin of sentence.
+ # XXX: should become text_object.
+ pass
+
+ @handle(')', filter=navigation_mode)
+ def _(event):
+ # TODO: go to end of sentence.
+ # XXX: should become text_object.
+ pass
+
+ operator = create_operator_decorator(registry)
+ text_object = create_text_object_decorator(registry)
+
+ @text_object(Keys.Any, filter=operator_given)
+ def _(event):
+ """
+ Unknown key binding while waiting for a text object.
+ """
+ event.cli.output.bell()
+
+ #
+ # *** Operators ***
+ #
+
+ def create_delete_and_change_operators(delete_only, with_register=False):
+ """
+ Delete and change operators.
+
+ :param delete_only: Create an operator that deletes, but doesn't go to insert mode.
+ :param with_register: Copy the deleted text to this named register instead of the clipboard.
+ """
+ if with_register:
+ handler_keys = ('"', Keys.Any, 'cd'[delete_only])
+ else:
+ handler_keys = 'cd'[delete_only]
+
+ @operator(*handler_keys, filter=~IsReadOnly())
+ def delete_or_change_operator(event, text_object):
+ clipboard_data = None
+ buff = event.current_buffer
+
+ if text_object:
+ new_document, clipboard_data = text_object.cut(buff)
+ buff.document = new_document
+
+ # Set deleted/changed text to clipboard or named register.
+ if clipboard_data and clipboard_data.text:
+ if with_register:
+ reg_name = event.key_sequence[1].data
+ if reg_name in vi_register_names:
+ event.cli.vi_state.named_registers[reg_name] = clipboard_data
+ else:
+ event.cli.clipboard.set_data(clipboard_data)
+
+ # Only go back to insert mode in case of 'change'.
+ if not delete_only:
+ event.cli.vi_state.input_mode = InputMode.INSERT
+
+ create_delete_and_change_operators(False, False)
+ create_delete_and_change_operators(False, True)
+ create_delete_and_change_operators(True, False)
+ create_delete_and_change_operators(True, True)
+
+ def create_transform_handler(filter, transform_func, *a):
+ @operator(*a, filter=filter & ~IsReadOnly())
+ def _(event, text_object):
+ """
+ Apply transformation (uppercase, lowercase, rot13, swap case).
+ """
+ buff = event.current_buffer
+ start, end = text_object.operator_range(buff.document)
+
+ if start < end:
+ # Transform.
+ buff.transform_region(
+ buff.cursor_position + start,
+ buff.cursor_position + end,
+ transform_func)
+
+ # Move cursor
+ buff.cursor_position += (text_object.end or text_object.start)
+
+ for k, f, func in vi_transform_functions:
+ create_transform_handler(f, func, *k)
+
+ @operator('y')
+ def yank_handler(event, text_object):
+ """
+ Yank operator. (Copy text.)
+ """
+ _, clipboard_data = text_object.cut(event.current_buffer)
+ if clipboard_data.text:
+ event.cli.clipboard.set_data(clipboard_data)
+
+ @operator('"', Keys.Any, 'y')
+ def _(event, text_object):
+ " Yank selection to named register. "
+ c = event.key_sequence[1].data
+ if c in vi_register_names:
+ _, clipboard_data = text_object.cut(event.current_buffer)
+ event.cli.vi_state.named_registers[c] = clipboard_data
+
+ @operator('>')
+ def _(event, text_object):
+ """
+ Indent.
+ """
+ buff = event.current_buffer
+ from_, to = text_object.get_line_numbers(buff)
+ indent(buff, from_, to + 1, count=event.arg)
+
+ @operator('<')
+ def _(event, text_object):
+ """
+ Unindent.
+ """
+ buff = event.current_buffer
+ from_, to = text_object.get_line_numbers(buff)
+ unindent(buff, from_, to + 1, count=event.arg)
+
+ @operator('g', 'q')
+ def _(event, text_object):
+ """
+ Reshape text.
+ """
+ buff = event.current_buffer
+ from_, to = text_object.get_line_numbers(buff)
+ reshape_text(buff, from_, to)
+
+ #
+ # *** Text objects ***
+ #
+
+ @text_object('b')
+ def _(event):
+ """ Move one word or token left. """
+ return TextObject(event.current_buffer.document.find_start_of_previous_word(count=event.arg) or 0)
+
+ @text_object('B')
+ def _(event):
+ """ Move one non-blank word left """
+ return TextObject(event.current_buffer.document.find_start_of_previous_word(count=event.arg, WORD=True) or 0)
+
+ @text_object('$')
+ def key_dollar(event):
+ """ 'c$', 'd$' and '$': Delete/change/move until end of line. """
+ return TextObject(event.current_buffer.document.get_end_of_line_position())
+
+ @text_object('w')
+ def _(event):
+ """ 'word' forward. 'cw', 'dw', 'w': Delete/change/move one word. """
+ return TextObject(event.current_buffer.document.find_next_word_beginning(count=event.arg) or
+ event.current_buffer.document.get_end_of_document_position())
+
+ @text_object('W')
+ def _(event):
+ """ 'WORD' forward. 'cW', 'dW', 'W': Delete/change/move one WORD. """
+ return TextObject(event.current_buffer.document.find_next_word_beginning(count=event.arg, WORD=True) or
+ event.current_buffer.document.get_end_of_document_position())
+
+ @text_object('e')
+ def _(event):
+ """ End of 'word': 'ce', 'de', 'e' """
+ end = event.current_buffer.document.find_next_word_ending(count=event.arg)
+ return TextObject(end - 1 if end else 0, type=TextObjectType.INCLUSIVE)
+
+ @text_object('E')
+ def _(event):
+ """ End of 'WORD': 'cE', 'dE', 'E' """
+ end = event.current_buffer.document.find_next_word_ending(count=event.arg, WORD=True)
+ return TextObject(end - 1 if end else 0, type=TextObjectType.INCLUSIVE)
+
+ @text_object('i', 'w', no_move_handler=True)
+ def _(event):
+ """ Inner 'word': ciw and diw """
+ start, end = event.current_buffer.document.find_boundaries_of_current_word()
+ return TextObject(start, end)
+
+ @text_object('a', 'w', no_move_handler=True)
+ def _(event):
+ """ A 'word': caw and daw """
+ start, end = event.current_buffer.document.find_boundaries_of_current_word(include_trailing_whitespace=True)
+ return TextObject(start, end)
+
+ @text_object('i', 'W', no_move_handler=True)
+ def _(event):
+ """ Inner 'WORD': ciW and diW """
+ start, end = event.current_buffer.document.find_boundaries_of_current_word(WORD=True)
+ return TextObject(start, end)
+
+ @text_object('a', 'W', no_move_handler=True)
+ def _(event):
+ """ A 'WORD': caw and daw """
+ start, end = event.current_buffer.document.find_boundaries_of_current_word(WORD=True, include_trailing_whitespace=True)
+ return TextObject(start, end)
+
+ @text_object('a', 'p', no_move_handler=True)
+ def _(event):
+ """
+ Auto paragraph.
+ """
+ start = event.current_buffer.document.start_of_paragraph()
+ end = event.current_buffer.document.end_of_paragraph(count=event.arg)
+ return TextObject(start, end)
+
+ @text_object('^')
+ def key_circumflex(event):
+ """ 'c^', 'd^' and '^': Soft start of line, after whitespace. """
+ return TextObject(event.current_buffer.document.get_start_of_line_position(after_whitespace=True))
+
+ @text_object('0')
+ def key_zero(event):
+ """
+ 'c0', 'd0': Hard start of line, before whitespace.
+ (The move '0' key is implemented elsewhere, because a '0' could also change the `arg`.)
+ """
+ return TextObject(event.current_buffer.document.get_start_of_line_position(after_whitespace=False))
+
+ def create_ci_ca_handles(ci_start, ci_end, inner, key=None):
+ # TODO: 'dat', 'dit', (tags (like xml)
+ """
+ Delete/Change string between this start and stop character. But keep these characters.
+ This implements all the ci", ci<, ci{, ci(, di", di<, ca", ca<, ... combinations.
+ """
+ def handler(event):
+ if ci_start == ci_end:
+ # Quotes
+ start = event.current_buffer.document.find_backwards(ci_start, in_current_line=False)
+ end = event.current_buffer.document.find(ci_end, in_current_line=False)
+ else:
+ # Brackets
+ start = event.current_buffer.document.find_enclosing_bracket_left(ci_start, ci_end)
+ end = event.current_buffer.document.find_enclosing_bracket_right(ci_start, ci_end)
+
+ if start is not None and end is not None:
+ offset = 0 if inner else 1
+ return TextObject(start + 1 - offset, end + offset)
+ else:
+ # Nothing found.
+ return TextObject(0)
+
+ if key is None:
+ text_object('ai'[inner], ci_start, no_move_handler=True)(handler)
+ text_object('ai'[inner], ci_end, no_move_handler=True)(handler)
+ else:
+ text_object('ai'[inner], key, no_move_handler=True)(handler)
+
+ for inner in (False, True):
+ for ci_start, ci_end in [('"', '"'), ("'", "'"), ("`", "`"),
+ ('[', ']'), ('<', '>'), ('{', '}'), ('(', ')')]:
+ create_ci_ca_handles(ci_start, ci_end, inner)
+
+ create_ci_ca_handles('(', ')', inner, 'b') # 'dab', 'dib'
+ create_ci_ca_handles('{', '}', inner, 'B') # 'daB', 'diB'
+
+ @text_object('{')
+ def _(event):
+ """
+ Move to previous blank-line separated section.
+ Implements '{', 'c{', 'd{', 'y{'
+ """
+ index = event.current_buffer.document.start_of_paragraph(
+ count=event.arg, before=True)
+ return TextObject(index)
+
+ @text_object('}')
+ def _(event):
+ """
+ Move to next blank-line separated section.
+ Implements '}', 'c}', 'd}', 'y}'
+ """
+ index = event.current_buffer.document.end_of_paragraph(count=event.arg, after=True)
+ return TextObject(index)
+
+ @text_object('f', Keys.Any)
+ def _(event):
+ """
+ Go to next occurance of character. Typing 'fx' will move the
+ cursor to the next occurance of character. 'x'.
+ """
+ event.cli.vi_state.last_character_find = CharacterFind(event.data, False)
+ match = event.current_buffer.document.find(
+ event.data, in_current_line=True, count=event.arg)
+ if match:
+ return TextObject(match, type=TextObjectType.INCLUSIVE)
+ else:
+ return TextObject(0)
+
+ @text_object('F', Keys.Any)
+ def _(event):
+ """
+ Go to previous occurance of character. Typing 'Fx' will move the
+ cursor to the previous occurance of character. 'x'.
+ """
+ event.cli.vi_state.last_character_find = CharacterFind(event.data, True)
+ return TextObject(event.current_buffer.document.find_backwards(
+ event.data, in_current_line=True, count=event.arg) or 0)
+
+ @text_object('t', Keys.Any)
+ def _(event):
+ """
+ Move right to the next occurance of c, then one char backward.
+ """
+ event.cli.vi_state.last_character_find = CharacterFind(event.data, False)
+ match = event.current_buffer.document.find(
+ event.data, in_current_line=True, count=event.arg)
+ if match:
+ return TextObject(match - 1, type=TextObjectType.INCLUSIVE)
+ else:
+ return TextObject(0)
+
+ @text_object('T', Keys.Any)
+ def _(event):
+ """
+ Move left to the previous occurance of c, then one char forward.
+ """
+ event.cli.vi_state.last_character_find = CharacterFind(event.data, True)
+ match = event.current_buffer.document.find_backwards(
+ event.data, in_current_line=True, count=event.arg)
+ return TextObject(match + 1 if match else 0)
+
+ def repeat(reverse):
+ """
+ Create ',' and ';' commands.
+ """
+ @text_object(',' if reverse else ';')
+ def _(event):
+ # Repeat the last 'f'/'F'/'t'/'T' command.
+ pos = 0
+ vi_state = event.cli.vi_state
+
+ type = TextObjectType.EXCLUSIVE
+
+ if vi_state.last_character_find:
+ char = vi_state.last_character_find.character
+ backwards = vi_state.last_character_find.backwards
+
+ if reverse:
+ backwards = not backwards
+
+ if backwards:
+ pos = event.current_buffer.document.find_backwards(char, in_current_line=True, count=event.arg)
+ else:
+ pos = event.current_buffer.document.find(char, in_current_line=True, count=event.arg)
+ type = TextObjectType.INCLUSIVE
+ if pos:
+ return TextObject(pos, type=type)
+ else:
+ return TextObject(0)
+ repeat(True)
+ repeat(False)
+
+ @text_object('h')
+ @text_object(Keys.Left)
+ def _(event):
+ """ Implements 'ch', 'dh', 'h': Cursor left. """
+ return TextObject(event.current_buffer.document.get_cursor_left_position(count=event.arg))
+
+ @text_object('j', no_move_handler=True, no_selection_handler=True)
+ # Note: We also need `no_selection_handler`, because we in
+ # selection mode, we prefer the other 'j' binding that keeps
+ # `buffer.preferred_column`.
+ def _(event):
+ """ Implements 'cj', 'dj', 'j', ... Cursor up. """
+ return TextObject(event.current_buffer.document.get_cursor_down_position(count=event.arg),
+ type=TextObjectType.LINEWISE)
+
+ @text_object('k', no_move_handler=True, no_selection_handler=True)
+ def _(event):
+ """ Implements 'ck', 'dk', 'k', ... Cursor up. """
+ return TextObject(event.current_buffer.document.get_cursor_up_position(count=event.arg),
+ type=TextObjectType.LINEWISE)
+
+ @text_object('l')
+ @text_object(' ')
+ @text_object(Keys.Right)
+ def _(event):
+ """ Implements 'cl', 'dl', 'l', 'c ', 'd ', ' '. Cursor right. """
+ return TextObject(event.current_buffer.document.get_cursor_right_position(count=event.arg))
+
+ @text_object('H')
+ def _(event):
+ """
+ Moves to the start of the visible region. (Below the scroll offset.)
+ Implements 'cH', 'dH', 'H'.
+ """
+ w = find_window_for_buffer_name(event.cli, event.cli.current_buffer_name)
+ b = event.current_buffer
+
+ if w and w.render_info:
+ # When we find a Window that has BufferControl showing this window,
+ # move to the start of the visible area.
+ pos = (b.document.translate_row_col_to_index(
+ w.render_info.first_visible_line(after_scroll_offset=True), 0) -
+ b.cursor_position)
+
+ else:
+ # Otherwise, move to the start of the input.
+ pos = -len(b.document.text_before_cursor)
+ return TextObject(pos, type=TextObjectType.LINEWISE)
+
+ @text_object('M')
+ def _(event):
+ """
+ Moves cursor to the vertical center of the visible region.
+ Implements 'cM', 'dM', 'M'.
+ """
+ w = find_window_for_buffer_name(event.cli, event.cli.current_buffer_name)
+ b = event.current_buffer
+
+ if w and w.render_info:
+ # When we find a Window that has BufferControl showing this window,
+ # move to the center of the visible area.
+ pos = (b.document.translate_row_col_to_index(
+ w.render_info.center_visible_line(), 0) -
+ b.cursor_position)
+
+ else:
+ # Otherwise, move to the start of the input.
+ pos = -len(b.document.text_before_cursor)
+ return TextObject(pos, type=TextObjectType.LINEWISE)
+
+ @text_object('L')
+ def _(event):
+ """
+ Moves to the end of the visible region. (Above the scroll offset.)
+ """
+ w = find_window_for_buffer_name(event.cli, event.cli.current_buffer_name)
+ b = event.current_buffer
+
+ if w and w.render_info:
+ # When we find a Window that has BufferControl showing this window,
+ # move to the end of the visible area.
+ pos = (b.document.translate_row_col_to_index(
+ w.render_info.last_visible_line(before_scroll_offset=True), 0) -
+ b.cursor_position)
+
+ else:
+ # Otherwise, move to the end of the input.
+ pos = len(b.document.text_after_cursor)
+ return TextObject(pos, type=TextObjectType.LINEWISE)
+
+ @text_object('n', no_move_handler=True)
+ def _(event):
+ " Search next. "
+ buff = event.current_buffer
+ cursor_position = buff.get_search_position(
+ get_search_state(event.cli), include_current_position=False,
+ count=event.arg)
+ return TextObject(cursor_position - buff.cursor_position)
+
+ @handle('n', filter=navigation_mode)
+ def _(event):
+ " Search next in navigation mode. (This goes through the history.) "
+ event.current_buffer.apply_search(
+ get_search_state(event.cli), include_current_position=False,
+ count=event.arg)
+
+ @text_object('N', no_move_handler=True)
+ def _(event):
+ " Search previous. "
+ buff = event.current_buffer
+ cursor_position = buff.get_search_position(
+ ~get_search_state(event.cli), include_current_position=False,
+ count=event.arg)
+ return TextObject(cursor_position - buff.cursor_position)
+
+ @handle('N', filter=navigation_mode)
+ def _(event):
+ " Search previous in navigation mode. (This goes through the history.) "
+ event.current_buffer.apply_search(
+ ~get_search_state(event.cli), include_current_position=False,
+ count=event.arg)
+
+ @handle('z', '+', filter=navigation_mode|selection_mode)
+ @handle('z', 't', filter=navigation_mode|selection_mode)
+ @handle('z', Keys.ControlJ, filter=navigation_mode|selection_mode)
+ def _(event):
+ """
+ Scrolls the window to makes the current line the first line in the visible region.
+ """
+ w = find_window_for_buffer_name(event.cli, event.cli.current_buffer_name)
+ b = event.cli.current_buffer
+ w.vertical_scroll = b.document.cursor_position_row
+
+ @handle('z', '-', filter=navigation_mode|selection_mode)
+ @handle('z', 'b', filter=navigation_mode|selection_mode)
+ def _(event):
+ """
+ Scrolls the window to makes the current line the last line in the visible region.
+ """
+ w = find_window_for_buffer_name(event.cli, event.cli.current_buffer_name)
+
+ # We can safely set the scroll offset to zero; the Window will meke
+ # sure that it scrolls at least enough to make the cursor visible
+ # again.
+ w.vertical_scroll = 0
+
+ @handle('z', 'z', filter=navigation_mode|selection_mode)
+ def _(event):
+ """
+ Center Window vertically around cursor.
+ """
+ w = find_window_for_buffer_name(event.cli, event.cli.current_buffer_name)
+ b = event.cli.current_buffer
+
+ if w and w.render_info:
+ info = w.render_info
+
+ # Calculate the offset that we need in order to position the row
+ # containing the cursor in the center.
+ scroll_height = info.window_height // 2
+
+ y = max(0, b.document.cursor_position_row - 1)
+ height = 0
+ while y > 0:
+ line_height = info.get_height_for_line(y)
+
+ if height + line_height < scroll_height:
+ height += line_height
+ y -= 1
+ else:
+ break
+
+ w.vertical_scroll = y
+
+ @text_object('%')
+ def _(event):
+ """
+ Implements 'c%', 'd%', '%, 'y%' (Move to corresponding bracket.)
+ If an 'arg' has been given, go this this % position in the file.
+ """
+ buffer = event.current_buffer
+
+ if event._arg:
+ # If 'arg' has been given, the meaning of % is to go to the 'x%'
+ # row in the file.
+ if 0 < event.arg <= 100:
+ absolute_index = buffer.document.translate_row_col_to_index(
+ int((event.arg * buffer.document.line_count - 1) / 100), 0)
+ return TextObject(absolute_index - buffer.document.cursor_position, type=TextObjectType.LINEWISE)
+ else:
+ return TextObject(0) # Do nothing.
+
+ else:
+ # Move to the corresponding opening/closing bracket (()'s, []'s and {}'s).
+ match = buffer.document.find_matching_bracket_position()
+ if match:
+ return TextObject(match, type=TextObjectType.INCLUSIVE)
+ else:
+ return TextObject(0)
+
+ @text_object('|')
+ def _(event):
+ # Move to the n-th column (you may specify the argument n by typing
+ # it on number keys, for example, 20|).
+ return TextObject(event.current_buffer.document.get_column_cursor_position(event.arg - 1))
+
+ @text_object('g', 'g')
+ def _(event):
+ """
+ Implements 'gg', 'cgg', 'ygg'
+ """
+ d = event.current_buffer.document
+
+ if event._arg:
+ # Move to the given line.
+ return TextObject(d.translate_row_col_to_index(event.arg - 1, 0) - d.cursor_position, type=TextObjectType.LINEWISE)
+ else:
+ # Move to the top of the input.
+ return TextObject(d.get_start_of_document_position(), type=TextObjectType.LINEWISE)
+
+ @text_object('g', '_')
+ def _(event):
+ """
+ Go to last non-blank of line.
+ 'g_', 'cg_', 'yg_', etc..
+ """
+ return TextObject(
+ event.current_buffer.document.last_non_blank_of_current_line_position(), type=TextObjectType.INCLUSIVE)
+
+ @text_object('g', 'e')
+ def _(event):
+ """
+ Go to last character of previous word.
+ 'ge', 'cge', 'yge', etc..
+ """
+ prev_end = event.current_buffer.document.find_previous_word_ending(count=event.arg)
+ return TextObject(prev_end - 1 if prev_end is not None else 0, type=TextObjectType.INCLUSIVE)
+
+ @text_object('g', 'E')
+ def _(event):
+ """
+ Go to last character of previous WORD.
+ 'gE', 'cgE', 'ygE', etc..
+ """
+ prev_end = event.current_buffer.document.find_previous_word_ending(count=event.arg, WORD=True)
+ return TextObject(prev_end - 1 if prev_end is not None else 0, type=TextObjectType.INCLUSIVE)
+
+ @text_object('g', 'm')
+ def _(event):
+ """
+ Like g0, but half a screenwidth to the right. (Or as much as possible.)
+ """
+ w = find_window_for_buffer_name(event.cli, event.cli.current_buffer_name)
+ buff = event.current_buffer
+
+ if w and w.render_info:
+ width = w.render_info.window_width
+ start = buff.document.get_start_of_line_position(after_whitespace=False)
+ start += int(min(width / 2, len(buff.document.current_line)))
+
+ return TextObject(start, type=TextObjectType.INCLUSIVE)
+ return TextObject(0)
+
+ @text_object('G')
+ def _(event):
+ """
+ Go to the end of the document. (If no arg has been given.)
+ """
+ buf = event.current_buffer
+ return TextObject(buf.document.translate_row_col_to_index(buf.document.line_count - 1, 0) -
+ buf.cursor_position, type=TextObjectType.LINEWISE)
+
+ #
+ # *** Other ***
+ #
+
+ @handle('G', filter=HasArg())
+ def _(event):
+ """
+ If an argument is given, move to this line in the history. (for
+ example, 15G)
+ """
+ event.current_buffer.go_to_history(event.arg - 1)
+
+ for n in '123456789':
+ @handle(n, filter=navigation_mode|selection_mode|operator_given)
+ def _(event):
+ """
+ Always handle numberics in navigation mode as arg.
+ """
+ event.append_to_arg_count(event.data)
+
+ @handle('0', filter=(navigation_mode|selection_mode|operator_given) & HasArg())
+ def _(event):
+ " Zero when an argument was already give. "
+ event.append_to_arg_count(event.data)
+
+ @handle(Keys.Any, filter=replace_mode)
+ def _(event):
+ """
+ Insert data at cursor position.
+ """
+ event.current_buffer.insert_text(event.data, overwrite=True)
+
+ @handle(Keys.Any, filter=insert_multiple_mode,
+ save_before=(lambda e: not e.is_repeat))
+ def _(event):
+ """
+ Insert data at multiple cursor positions at once.
+ (Usually a result of pressing 'I' or 'A' in block-selection mode.)
+ """
+ buff = event.current_buffer
+ original_text = buff.text
+
+ # Construct new text.
+ text = []
+ p = 0
+
+ for p2 in buff.multiple_cursor_positions:
+ text.append(original_text[p:p2])
+ text.append(event.data)
+ p = p2
+
+ text.append(original_text[p:])
+
+ # Shift all cursor positions.
+ new_cursor_positions = [
+ p + i + 1 for i, p in enumerate(buff.multiple_cursor_positions)]
+
+ # Set result.
+ buff.text = ''.join(text)
+ buff.multiple_cursor_positions = new_cursor_positions
+ buff.cursor_position += 1
+
+ @handle(Keys.Backspace, filter=insert_multiple_mode)
+ def _(event):
+ " Backspace, using multiple cursors. "
+ buff = event.current_buffer
+ original_text = buff.text
+
+ # Construct new text.
+ deleted_something = False
+ text = []
+ p = 0
+
+ for p2 in buff.multiple_cursor_positions:
+ if p2 > 0 and original_text[p2 - 1] != '\n': # Don't delete across lines.
+ text.append(original_text[p:p2 - 1])
+ deleted_something = True
+ else:
+ text.append(original_text[p:p2])
+ p = p2
+
+ text.append(original_text[p:])
+
+ if deleted_something:
+ # Shift all cursor positions.
+ lengths = [len(part) for part in text[:-1]]
+ new_cursor_positions = list(accumulate(lengths))
+
+ # Set result.
+ buff.text = ''.join(text)
+ buff.multiple_cursor_positions = new_cursor_positions
+ buff.cursor_position -= 1
+ else:
+ event.cli.output.bell()
+
+ @handle(Keys.Delete, filter=insert_multiple_mode)
+ def _(event):
+ " Delete, using multiple cursors. "
+ buff = event.current_buffer
+ original_text = buff.text
+
+ # Construct new text.
+ deleted_something = False
+ text = []
+ new_cursor_positions = []
+ p = 0
+
+ for p2 in buff.multiple_cursor_positions:
+ text.append(original_text[p:p2])
+ if p2 >= len(original_text) or original_text[p2] == '\n':
+ # Don't delete across lines.
+ p = p2
+ else:
+ p = p2 + 1
+ deleted_something = True
+
+ text.append(original_text[p:])
+
+ if deleted_something:
+ # Shift all cursor positions.
+ lengths = [len(part) for part in text[:-1]]
+ new_cursor_positions = list(accumulate(lengths))
+
+ # Set result.
+ buff.text = ''.join(text)
+ buff.multiple_cursor_positions = new_cursor_positions
+ else:
+ event.cli.output.bell()
+
+
+ @handle(Keys.ControlX, Keys.ControlL, filter=insert_mode)
+ def _(event):
+ """
+ Pressing the ControlX - ControlL sequence in Vi mode does line
+ completion based on the other lines in the document and the history.
+ """
+ event.current_buffer.start_history_lines_completion()
+
+ @handle(Keys.ControlX, Keys.ControlF, filter=insert_mode)
+ def _(event):
+ """
+ Complete file names.
+ """
+ # TODO
+ pass
+
+ @handle(Keys.ControlK, filter=insert_mode|replace_mode)
+ def _(event):
+ " Go into digraph mode. "
+ event.cli.vi_state.waiting_for_digraph = True
+
+ @Condition
+ def digraph_symbol_1_given(cli):
+ return cli.vi_state.digraph_symbol1 is not None
+
+ @handle(Keys.Any, filter=digraph_mode & ~digraph_symbol_1_given)
+ def _(event):
+ event.cli.vi_state.digraph_symbol1 = event.data
+
+ @handle(Keys.Any, filter=digraph_mode & digraph_symbol_1_given)
+ def _(event):
+ " Insert digraph. "
+ try:
+ # Lookup.
+ code = (event.cli.vi_state.digraph_symbol1, event.data)
+ if code not in DIGRAPHS:
+ code = code[::-1] # Try reversing.
+ symbol = DIGRAPHS[code]
+ except KeyError:
+ # Unkown digraph.
+ event.cli.output.bell()
+ else:
+ # Insert digraph.
+ overwrite = event.cli.vi_state.input_mode == InputMode.REPLACE
+ event.current_buffer.insert_text(
+ six.unichr(symbol), overwrite=overwrite)
+ event.cli.vi_state.waiting_for_digraph = False
+ finally:
+ event.cli.vi_state.waiting_for_digraph = False
+ event.cli.vi_state.digraph_symbol1 = None
+
+ return registry
+
+
+def load_vi_open_in_editor_bindings():
+ """
+ Pressing 'v' in navigation mode will open the buffer in an external editor.
+ """
+ registry = Registry()
+ navigation_mode = ViNavigationMode()
+
+ registry.add_binding('v', filter=navigation_mode)(
+ get_by_name('edit-and-execute-command'))
+ return registry
+
+
+def load_vi_system_bindings():
+ registry = ConditionalRegistry(Registry(), ViMode())
+ handle = registry.add_binding
+
+ has_focus = filters.HasFocus(SYSTEM_BUFFER)
+ navigation_mode = ViNavigationMode()
+
+ @handle('!', filter=~has_focus & navigation_mode)
+ def _(event):
+ """
+ '!' opens the system prompt.
+ """
+ event.cli.push_focus(SYSTEM_BUFFER)
+ event.cli.vi_state.input_mode = InputMode.INSERT
+
+ @handle(Keys.Escape, filter=has_focus)
+ @handle(Keys.ControlC, filter=has_focus)
+ def _(event):
+ """
+ Cancel system prompt.
+ """
+ event.cli.vi_state.input_mode = InputMode.NAVIGATION
+ event.cli.buffers[SYSTEM_BUFFER].reset()
+ event.cli.pop_focus()
+
+ @handle(Keys.ControlJ, filter=has_focus)
+ def _(event):
+ """
+ Run system command.
+ """
+ event.cli.vi_state.input_mode = InputMode.NAVIGATION
+
+ system_buffer = event.cli.buffers[SYSTEM_BUFFER]
+ event.cli.run_system_command(system_buffer.text)
+ system_buffer.reset(append_to_history=True)
+
+ # Focus previous buffer again.
+ event.cli.pop_focus()
+
+ return registry
+
+
+def load_vi_search_bindings(get_search_state=None,
+ search_buffer_name=SEARCH_BUFFER):
+ assert get_search_state is None or callable(get_search_state)
+
+ if not get_search_state:
+ def get_search_state(cli): return cli.search_state
+
+ registry = ConditionalRegistry(Registry(), ViMode())
+ handle = registry.add_binding
+
+ has_focus = filters.HasFocus(search_buffer_name)
+ navigation_mode = ViNavigationMode()
+ selection_mode = ViSelectionMode()
+
+ reverse_vi_search_direction = Condition(
+ lambda cli: cli.application.reverse_vi_search_direction(cli))
+
+ @handle('/', filter=(navigation_mode|selection_mode)&~reverse_vi_search_direction)
+ @handle('?', filter=(navigation_mode|selection_mode)&reverse_vi_search_direction)
+ @handle(Keys.ControlS, filter=~has_focus)
+ def _(event):
+ """
+ Vi-style forward search.
+ """
+ # Set the ViState.
+ get_search_state(event.cli).direction = IncrementalSearchDirection.FORWARD
+ event.cli.vi_state.input_mode = InputMode.INSERT
+
+ # Focus search buffer.
+ event.cli.push_focus(search_buffer_name)
+
+ @handle('?', filter=(navigation_mode|selection_mode)&~reverse_vi_search_direction)
+ @handle('/', filter=(navigation_mode|selection_mode)&reverse_vi_search_direction)
+ @handle(Keys.ControlR, filter=~has_focus)
+ def _(event):
+ """
+ Vi-style backward search.
+ """
+ # Set the ViState.
+ get_search_state(event.cli).direction = IncrementalSearchDirection.BACKWARD
+
+ # Focus search buffer.
+ event.cli.push_focus(search_buffer_name)
+ event.cli.vi_state.input_mode = InputMode.INSERT
+
+ @handle(Keys.ControlJ, filter=has_focus)
+ @handle(Keys.Escape, filter=has_focus)
+ def _(event):
+ """
+ Apply the search. (At the / or ? prompt.)
+ """
+ input_buffer = event.cli.buffers.previous(event.cli)
+ search_buffer = event.cli.buffers[search_buffer_name]
+
+ # Update search state.
+ if search_buffer.text:
+ get_search_state(event.cli).text = search_buffer.text
+
+ # Apply search.
+ input_buffer.apply_search(get_search_state(event.cli))
+
+ # Add query to history of search line.
+ search_buffer.append_to_history()
+ search_buffer.reset()
+
+ # Focus previous document again.
+ event.cli.vi_state.input_mode = InputMode.NAVIGATION
+ event.cli.pop_focus()
+
+ def incremental_search(cli, direction, count=1):
+ " Apply search, but keep search buffer focussed. "
+ # Update search_state.
+ search_state = get_search_state(cli)
+ direction_changed = search_state.direction != direction
+
+ search_state.text = cli.buffers[search_buffer_name].text
+ search_state.direction = direction
+
+ # Apply search to current buffer.
+ if not direction_changed:
+ input_buffer = cli.buffers.previous(cli)
+ input_buffer.apply_search(search_state,
+ include_current_position=False, count=count)
+
+ @handle(Keys.ControlR, filter=has_focus)
+ def _(event):
+ incremental_search(event.cli, IncrementalSearchDirection.BACKWARD, count=event.arg)
+
+ @handle(Keys.ControlS, filter=has_focus)
+ def _(event):
+ incremental_search(event.cli, IncrementalSearchDirection.FORWARD, count=event.arg)
+
+ def search_buffer_is_empty(cli):
+ """ Returns True when the search buffer is empty. """
+ return cli.buffers[search_buffer_name].text == ''
+
+ @handle(Keys.ControlC, filter=has_focus)
+ @handle(Keys.ControlH, filter=has_focus & Condition(search_buffer_is_empty))
+ @handle(Keys.Backspace, filter=has_focus & Condition(search_buffer_is_empty))
+ def _(event):
+ """
+ Cancel search.
+ """
+ event.cli.vi_state.input_mode = InputMode.NAVIGATION
+
+ event.cli.pop_focus()
+ event.cli.buffers[search_buffer_name].reset()
+
+ return registry
+
+
+def load_extra_vi_page_navigation_bindings():
+ """
+ Key bindings, for scrolling up and down through pages.
+ This are separate bindings, because GNU readline doesn't have them.
+ """
+ registry = ConditionalRegistry(Registry(), ViMode())
+ handle = registry.add_binding
+
+ handle(Keys.ControlF)(scroll_forward)
+ handle(Keys.ControlB)(scroll_backward)
+ handle(Keys.ControlD)(scroll_half_page_down)
+ handle(Keys.ControlU)(scroll_half_page_up)
+ handle(Keys.ControlE)(scroll_one_line_down)
+ handle(Keys.ControlY)(scroll_one_line_up)
+ handle(Keys.PageDown)(scroll_page_down)
+ handle(Keys.PageUp)(scroll_page_up)
+
+ return registry
+
+
+class ViStateFilter(Filter):
+ " Deprecated! "
+ def __init__(self, get_vi_state, mode):
+ self.get_vi_state = get_vi_state
+ self.mode = mode
+
+ def __call__(self, cli):
+ return self.get_vi_state(cli).input_mode == self.mode
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/defaults.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/defaults.py
new file mode 100644
index 0000000000..fb2c1070f7
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/defaults.py
@@ -0,0 +1,119 @@
+"""
+Default key bindings.::
+
+ registry = load_key_bindings()
+ app = Application(key_bindings_registry=registry)
+"""
+from __future__ import unicode_literals
+from prompt_toolkit.key_binding.registry import ConditionalRegistry, MergedRegistry
+from prompt_toolkit.key_binding.bindings.basic import load_basic_bindings, load_abort_and_exit_bindings, load_basic_system_bindings, load_auto_suggestion_bindings, load_mouse_bindings
+from prompt_toolkit.key_binding.bindings.emacs import load_emacs_bindings, load_emacs_system_bindings, load_emacs_search_bindings, load_emacs_open_in_editor_bindings, load_extra_emacs_page_navigation_bindings
+from prompt_toolkit.key_binding.bindings.vi import load_vi_bindings, load_vi_system_bindings, load_vi_search_bindings, load_vi_open_in_editor_bindings, load_extra_vi_page_navigation_bindings
+from prompt_toolkit.filters import to_cli_filter
+
+__all__ = (
+ 'load_key_bindings',
+ 'load_key_bindings_for_prompt',
+)
+
+
+def load_key_bindings(
+ get_search_state=None,
+ enable_abort_and_exit_bindings=False,
+ enable_system_bindings=False,
+ enable_search=False,
+ enable_open_in_editor=False,
+ enable_extra_page_navigation=False,
+ enable_auto_suggest_bindings=False):
+ """
+ Create a Registry object that contains the default key bindings.
+
+ :param enable_abort_and_exit_bindings: Filter to enable Ctrl-C and Ctrl-D.
+ :param enable_system_bindings: Filter to enable the system bindings (meta-!
+ prompt and Control-Z suspension.)
+ :param enable_search: Filter to enable the search bindings.
+ :param enable_open_in_editor: Filter to enable open-in-editor.
+ :param enable_open_in_editor: Filter to enable open-in-editor.
+ :param enable_extra_page_navigation: Filter for enabling extra page
+ navigation. (Bindings for up/down scrolling through long pages, like in
+ Emacs or Vi.)
+ :param enable_auto_suggest_bindings: Filter to enable fish-style suggestions.
+ """
+
+ assert get_search_state is None or callable(get_search_state)
+
+ # Accept both Filters and booleans as input.
+ enable_abort_and_exit_bindings = to_cli_filter(enable_abort_and_exit_bindings)
+ enable_system_bindings = to_cli_filter(enable_system_bindings)
+ enable_search = to_cli_filter(enable_search)
+ enable_open_in_editor = to_cli_filter(enable_open_in_editor)
+ enable_extra_page_navigation = to_cli_filter(enable_extra_page_navigation)
+ enable_auto_suggest_bindings = to_cli_filter(enable_auto_suggest_bindings)
+
+ registry = MergedRegistry([
+ # Load basic bindings.
+ load_basic_bindings(),
+ load_mouse_bindings(),
+
+ ConditionalRegistry(load_abort_and_exit_bindings(),
+ enable_abort_and_exit_bindings),
+
+ ConditionalRegistry(load_basic_system_bindings(),
+ enable_system_bindings),
+
+ # Load emacs bindings.
+ load_emacs_bindings(),
+
+ ConditionalRegistry(load_emacs_open_in_editor_bindings(),
+ enable_open_in_editor),
+
+ ConditionalRegistry(load_emacs_search_bindings(get_search_state=get_search_state),
+ enable_search),
+
+ ConditionalRegistry(load_emacs_system_bindings(),
+ enable_system_bindings),
+
+ ConditionalRegistry(load_extra_emacs_page_navigation_bindings(),
+ enable_extra_page_navigation),
+
+ # Load Vi bindings.
+ load_vi_bindings(get_search_state=get_search_state),
+
+ ConditionalRegistry(load_vi_open_in_editor_bindings(),
+ enable_open_in_editor),
+
+ ConditionalRegistry(load_vi_search_bindings(get_search_state=get_search_state),
+ enable_search),
+
+ ConditionalRegistry(load_vi_system_bindings(),
+ enable_system_bindings),
+
+ ConditionalRegistry(load_extra_vi_page_navigation_bindings(),
+ enable_extra_page_navigation),
+
+ # Suggestion bindings.
+ # (This has to come at the end, because the Vi bindings also have an
+ # implementation for the "right arrow", but we really want the
+ # suggestion binding when a suggestion is available.)
+ ConditionalRegistry(load_auto_suggestion_bindings(),
+ enable_auto_suggest_bindings),
+ ])
+
+ return registry
+
+
+def load_key_bindings_for_prompt(**kw):
+ """
+ Create a ``Registry`` object with the defaults key bindings for an input
+ prompt.
+
+ This activates the key bindings for abort/exit (Ctrl-C/Ctrl-D),
+ incremental search and auto suggestions.
+
+ (Not for full screen applications.)
+ """
+ kw.setdefault('enable_abort_and_exit_bindings', True)
+ kw.setdefault('enable_search', True)
+ kw.setdefault('enable_auto_suggest_bindings', True)
+
+ return load_key_bindings(**kw)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/digraphs.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/digraphs.py
new file mode 100644
index 0000000000..36c6b15103
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/digraphs.py
@@ -0,0 +1,1378 @@
+# encoding: utf-8
+from __future__ import unicode_literals
+"""
+Vi Digraphs.
+This is a list of special characters that can be inserted in Vi insert mode by
+pressing Control-K followed by to normal characters.
+
+Taken from Neovim and translated to Python:
+https://raw.githubusercontent.com/neovim/neovim/master/src/nvim/digraph.c
+"""
+__all__ = ('DIGRAPHS', )
+
+# digraphs for Unicode from RFC1345
+# (also work for ISO-8859-1 aka latin1)
+DIGRAPHS = {
+ ('N', 'U'): 0x00,
+ ('S', 'H'): 0x01,
+ ('S', 'X'): 0x02,
+ ('E', 'X'): 0x03,
+ ('E', 'T'): 0x04,
+ ('E', 'Q'): 0x05,
+ ('A', 'K'): 0x06,
+ ('B', 'L'): 0x07,
+ ('B', 'S'): 0x08,
+ ('H', 'T'): 0x09,
+ ('L', 'F'): 0x0a,
+ ('V', 'T'): 0x0b,
+ ('F', 'F'): 0x0c,
+ ('C', 'R'): 0x0d,
+ ('S', 'O'): 0x0e,
+ ('S', 'I'): 0x0f,
+ ('D', 'L'): 0x10,
+ ('D', '1'): 0x11,
+ ('D', '2'): 0x12,
+ ('D', '3'): 0x13,
+ ('D', '4'): 0x14,
+ ('N', 'K'): 0x15,
+ ('S', 'Y'): 0x16,
+ ('E', 'B'): 0x17,
+ ('C', 'N'): 0x18,
+ ('E', 'M'): 0x19,
+ ('S', 'B'): 0x1a,
+ ('E', 'C'): 0x1b,
+ ('F', 'S'): 0x1c,
+ ('G', 'S'): 0x1d,
+ ('R', 'S'): 0x1e,
+ ('U', 'S'): 0x1f,
+ ('S', 'P'): 0x20,
+ ('N', 'b'): 0x23,
+ ('D', 'O'): 0x24,
+ ('A', 't'): 0x40,
+ ('<', '('): 0x5b,
+ ('/', '/'): 0x5c,
+ (')', '>'): 0x5d,
+ ('\'', '>'): 0x5e,
+ ('\'', '!'): 0x60,
+ ('(', '!'): 0x7b,
+ ('!', '!'): 0x7c,
+ ('!', ')'): 0x7d,
+ ('\'', '?'): 0x7e,
+ ('D', 'T'): 0x7f,
+ ('P', 'A'): 0x80,
+ ('H', 'O'): 0x81,
+ ('B', 'H'): 0x82,
+ ('N', 'H'): 0x83,
+ ('I', 'N'): 0x84,
+ ('N', 'L'): 0x85,
+ ('S', 'A'): 0x86,
+ ('E', 'S'): 0x87,
+ ('H', 'S'): 0x88,
+ ('H', 'J'): 0x89,
+ ('V', 'S'): 0x8a,
+ ('P', 'D'): 0x8b,
+ ('P', 'U'): 0x8c,
+ ('R', 'I'): 0x8d,
+ ('S', '2'): 0x8e,
+ ('S', '3'): 0x8f,
+ ('D', 'C'): 0x90,
+ ('P', '1'): 0x91,
+ ('P', '2'): 0x92,
+ ('T', 'S'): 0x93,
+ ('C', 'C'): 0x94,
+ ('M', 'W'): 0x95,
+ ('S', 'G'): 0x96,
+ ('E', 'G'): 0x97,
+ ('S', 'S'): 0x98,
+ ('G', 'C'): 0x99,
+ ('S', 'C'): 0x9a,
+ ('C', 'I'): 0x9b,
+ ('S', 'T'): 0x9c,
+ ('O', 'C'): 0x9d,
+ ('P', 'M'): 0x9e,
+ ('A', 'C'): 0x9f,
+ ('N', 'S'): 0xa0,
+ ('!', 'I'): 0xa1,
+ ('C', 't'): 0xa2,
+ ('P', 'd'): 0xa3,
+ ('C', 'u'): 0xa4,
+ ('Y', 'e'): 0xa5,
+ ('B', 'B'): 0xa6,
+ ('S', 'E'): 0xa7,
+ ('\'', ':'): 0xa8,
+ ('C', 'o'): 0xa9,
+ ('-', 'a'): 0xaa,
+ ('<', '<'): 0xab,
+ ('N', 'O'): 0xac,
+ ('-', '-'): 0xad,
+ ('R', 'g'): 0xae,
+ ('\'', 'm'): 0xaf,
+ ('D', 'G'): 0xb0,
+ ('+', '-'): 0xb1,
+ ('2', 'S'): 0xb2,
+ ('3', 'S'): 0xb3,
+ ('\'', '\''): 0xb4,
+ ('M', 'y'): 0xb5,
+ ('P', 'I'): 0xb6,
+ ('.', 'M'): 0xb7,
+ ('\'', ','): 0xb8,
+ ('1', 'S'): 0xb9,
+ ('-', 'o'): 0xba,
+ ('>', '>'): 0xbb,
+ ('1', '4'): 0xbc,
+ ('1', '2'): 0xbd,
+ ('3', '4'): 0xbe,
+ ('?', 'I'): 0xbf,
+ ('A', '!'): 0xc0,
+ ('A', '\''): 0xc1,
+ ('A', '>'): 0xc2,
+ ('A', '?'): 0xc3,
+ ('A', ':'): 0xc4,
+ ('A', 'A'): 0xc5,
+ ('A', 'E'): 0xc6,
+ ('C', ','): 0xc7,
+ ('E', '!'): 0xc8,
+ ('E', '\''): 0xc9,
+ ('E', '>'): 0xca,
+ ('E', ':'): 0xcb,
+ ('I', '!'): 0xcc,
+ ('I', '\''): 0xcd,
+ ('I', '>'): 0xce,
+ ('I', ':'): 0xcf,
+ ('D', '-'): 0xd0,
+ ('N', '?'): 0xd1,
+ ('O', '!'): 0xd2,
+ ('O', '\''): 0xd3,
+ ('O', '>'): 0xd4,
+ ('O', '?'): 0xd5,
+ ('O', ':'): 0xd6,
+ ('*', 'X'): 0xd7,
+ ('O', '/'): 0xd8,
+ ('U', '!'): 0xd9,
+ ('U', '\''): 0xda,
+ ('U', '>'): 0xdb,
+ ('U', ':'): 0xdc,
+ ('Y', '\''): 0xdd,
+ ('T', 'H'): 0xde,
+ ('s', 's'): 0xdf,
+ ('a', '!'): 0xe0,
+ ('a', '\''): 0xe1,
+ ('a', '>'): 0xe2,
+ ('a', '?'): 0xe3,
+ ('a', ':'): 0xe4,
+ ('a', 'a'): 0xe5,
+ ('a', 'e'): 0xe6,
+ ('c', ','): 0xe7,
+ ('e', '!'): 0xe8,
+ ('e', '\''): 0xe9,
+ ('e', '>'): 0xea,
+ ('e', ':'): 0xeb,
+ ('i', '!'): 0xec,
+ ('i', '\''): 0xed,
+ ('i', '>'): 0xee,
+ ('i', ':'): 0xef,
+ ('d', '-'): 0xf0,
+ ('n', '?'): 0xf1,
+ ('o', '!'): 0xf2,
+ ('o', '\''): 0xf3,
+ ('o', '>'): 0xf4,
+ ('o', '?'): 0xf5,
+ ('o', ':'): 0xf6,
+ ('-', ':'): 0xf7,
+ ('o', '/'): 0xf8,
+ ('u', '!'): 0xf9,
+ ('u', '\''): 0xfa,
+ ('u', '>'): 0xfb,
+ ('u', ':'): 0xfc,
+ ('y', '\''): 0xfd,
+ ('t', 'h'): 0xfe,
+ ('y', ':'): 0xff,
+
+ ('A', '-'): 0x0100,
+ ('a', '-'): 0x0101,
+ ('A', '('): 0x0102,
+ ('a', '('): 0x0103,
+ ('A', ';'): 0x0104,
+ ('a', ';'): 0x0105,
+ ('C', '\''): 0x0106,
+ ('c', '\''): 0x0107,
+ ('C', '>'): 0x0108,
+ ('c', '>'): 0x0109,
+ ('C', '.'): 0x010a,
+ ('c', '.'): 0x010b,
+ ('C', '<'): 0x010c,
+ ('c', '<'): 0x010d,
+ ('D', '<'): 0x010e,
+ ('d', '<'): 0x010f,
+ ('D', '/'): 0x0110,
+ ('d', '/'): 0x0111,
+ ('E', '-'): 0x0112,
+ ('e', '-'): 0x0113,
+ ('E', '('): 0x0114,
+ ('e', '('): 0x0115,
+ ('E', '.'): 0x0116,
+ ('e', '.'): 0x0117,
+ ('E', ';'): 0x0118,
+ ('e', ';'): 0x0119,
+ ('E', '<'): 0x011a,
+ ('e', '<'): 0x011b,
+ ('G', '>'): 0x011c,
+ ('g', '>'): 0x011d,
+ ('G', '('): 0x011e,
+ ('g', '('): 0x011f,
+ ('G', '.'): 0x0120,
+ ('g', '.'): 0x0121,
+ ('G', ','): 0x0122,
+ ('g', ','): 0x0123,
+ ('H', '>'): 0x0124,
+ ('h', '>'): 0x0125,
+ ('H', '/'): 0x0126,
+ ('h', '/'): 0x0127,
+ ('I', '?'): 0x0128,
+ ('i', '?'): 0x0129,
+ ('I', '-'): 0x012a,
+ ('i', '-'): 0x012b,
+ ('I', '('): 0x012c,
+ ('i', '('): 0x012d,
+ ('I', ';'): 0x012e,
+ ('i', ';'): 0x012f,
+ ('I', '.'): 0x0130,
+ ('i', '.'): 0x0131,
+ ('I', 'J'): 0x0132,
+ ('i', 'j'): 0x0133,
+ ('J', '>'): 0x0134,
+ ('j', '>'): 0x0135,
+ ('K', ','): 0x0136,
+ ('k', ','): 0x0137,
+ ('k', 'k'): 0x0138,
+ ('L', '\''): 0x0139,
+ ('l', '\''): 0x013a,
+ ('L', ','): 0x013b,
+ ('l', ','): 0x013c,
+ ('L', '<'): 0x013d,
+ ('l', '<'): 0x013e,
+ ('L', '.'): 0x013f,
+ ('l', '.'): 0x0140,
+ ('L', '/'): 0x0141,
+ ('l', '/'): 0x0142,
+ ('N', '\''): 0x0143,
+ ('n', '\''): 0x0144,
+ ('N', ','): 0x0145,
+ ('n', ','): 0x0146,
+ ('N', '<'): 0x0147,
+ ('n', '<'): 0x0148,
+ ('\'', 'n'): 0x0149,
+ ('N', 'G'): 0x014a,
+ ('n', 'g'): 0x014b,
+ ('O', '-'): 0x014c,
+ ('o', '-'): 0x014d,
+ ('O', '('): 0x014e,
+ ('o', '('): 0x014f,
+ ('O', '"'): 0x0150,
+ ('o', '"'): 0x0151,
+ ('O', 'E'): 0x0152,
+ ('o', 'e'): 0x0153,
+ ('R', '\''): 0x0154,
+ ('r', '\''): 0x0155,
+ ('R', ','): 0x0156,
+ ('r', ','): 0x0157,
+ ('R', '<'): 0x0158,
+ ('r', '<'): 0x0159,
+ ('S', '\''): 0x015a,
+ ('s', '\''): 0x015b,
+ ('S', '>'): 0x015c,
+ ('s', '>'): 0x015d,
+ ('S', ','): 0x015e,
+ ('s', ','): 0x015f,
+ ('S', '<'): 0x0160,
+ ('s', '<'): 0x0161,
+ ('T', ','): 0x0162,
+ ('t', ','): 0x0163,
+ ('T', '<'): 0x0164,
+ ('t', '<'): 0x0165,
+ ('T', '/'): 0x0166,
+ ('t', '/'): 0x0167,
+ ('U', '?'): 0x0168,
+ ('u', '?'): 0x0169,
+ ('U', '-'): 0x016a,
+ ('u', '-'): 0x016b,
+ ('U', '('): 0x016c,
+ ('u', '('): 0x016d,
+ ('U', '0'): 0x016e,
+ ('u', '0'): 0x016f,
+ ('U', '"'): 0x0170,
+ ('u', '"'): 0x0171,
+ ('U', ';'): 0x0172,
+ ('u', ';'): 0x0173,
+ ('W', '>'): 0x0174,
+ ('w', '>'): 0x0175,
+ ('Y', '>'): 0x0176,
+ ('y', '>'): 0x0177,
+ ('Y', ':'): 0x0178,
+ ('Z', '\''): 0x0179,
+ ('z', '\''): 0x017a,
+ ('Z', '.'): 0x017b,
+ ('z', '.'): 0x017c,
+ ('Z', '<'): 0x017d,
+ ('z', '<'): 0x017e,
+ ('O', '9'): 0x01a0,
+ ('o', '9'): 0x01a1,
+ ('O', 'I'): 0x01a2,
+ ('o', 'i'): 0x01a3,
+ ('y', 'r'): 0x01a6,
+ ('U', '9'): 0x01af,
+ ('u', '9'): 0x01b0,
+ ('Z', '/'): 0x01b5,
+ ('z', '/'): 0x01b6,
+ ('E', 'D'): 0x01b7,
+ ('A', '<'): 0x01cd,
+ ('a', '<'): 0x01ce,
+ ('I', '<'): 0x01cf,
+ ('i', '<'): 0x01d0,
+ ('O', '<'): 0x01d1,
+ ('o', '<'): 0x01d2,
+ ('U', '<'): 0x01d3,
+ ('u', '<'): 0x01d4,
+ ('A', '1'): 0x01de,
+ ('a', '1'): 0x01df,
+ ('A', '7'): 0x01e0,
+ ('a', '7'): 0x01e1,
+ ('A', '3'): 0x01e2,
+ ('a', '3'): 0x01e3,
+ ('G', '/'): 0x01e4,
+ ('g', '/'): 0x01e5,
+ ('G', '<'): 0x01e6,
+ ('g', '<'): 0x01e7,
+ ('K', '<'): 0x01e8,
+ ('k', '<'): 0x01e9,
+ ('O', ';'): 0x01ea,
+ ('o', ';'): 0x01eb,
+ ('O', '1'): 0x01ec,
+ ('o', '1'): 0x01ed,
+ ('E', 'Z'): 0x01ee,
+ ('e', 'z'): 0x01ef,
+ ('j', '<'): 0x01f0,
+ ('G', '\''): 0x01f4,
+ ('g', '\''): 0x01f5,
+ (';', 'S'): 0x02bf,
+ ('\'', '<'): 0x02c7,
+ ('\'', '('): 0x02d8,
+ ('\'', '.'): 0x02d9,
+ ('\'', '0'): 0x02da,
+ ('\'', ';'): 0x02db,
+ ('\'', '"'): 0x02dd,
+ ('A', '%'): 0x0386,
+ ('E', '%'): 0x0388,
+ ('Y', '%'): 0x0389,
+ ('I', '%'): 0x038a,
+ ('O', '%'): 0x038c,
+ ('U', '%'): 0x038e,
+ ('W', '%'): 0x038f,
+ ('i', '3'): 0x0390,
+ ('A', '*'): 0x0391,
+ ('B', '*'): 0x0392,
+ ('G', '*'): 0x0393,
+ ('D', '*'): 0x0394,
+ ('E', '*'): 0x0395,
+ ('Z', '*'): 0x0396,
+ ('Y', '*'): 0x0397,
+ ('H', '*'): 0x0398,
+ ('I', '*'): 0x0399,
+ ('K', '*'): 0x039a,
+ ('L', '*'): 0x039b,
+ ('M', '*'): 0x039c,
+ ('N', '*'): 0x039d,
+ ('C', '*'): 0x039e,
+ ('O', '*'): 0x039f,
+ ('P', '*'): 0x03a0,
+ ('R', '*'): 0x03a1,
+ ('S', '*'): 0x03a3,
+ ('T', '*'): 0x03a4,
+ ('U', '*'): 0x03a5,
+ ('F', '*'): 0x03a6,
+ ('X', '*'): 0x03a7,
+ ('Q', '*'): 0x03a8,
+ ('W', '*'): 0x03a9,
+ ('J', '*'): 0x03aa,
+ ('V', '*'): 0x03ab,
+ ('a', '%'): 0x03ac,
+ ('e', '%'): 0x03ad,
+ ('y', '%'): 0x03ae,
+ ('i', '%'): 0x03af,
+ ('u', '3'): 0x03b0,
+ ('a', '*'): 0x03b1,
+ ('b', '*'): 0x03b2,
+ ('g', '*'): 0x03b3,
+ ('d', '*'): 0x03b4,
+ ('e', '*'): 0x03b5,
+ ('z', '*'): 0x03b6,
+ ('y', '*'): 0x03b7,
+ ('h', '*'): 0x03b8,
+ ('i', '*'): 0x03b9,
+ ('k', '*'): 0x03ba,
+ ('l', '*'): 0x03bb,
+ ('m', '*'): 0x03bc,
+ ('n', '*'): 0x03bd,
+ ('c', '*'): 0x03be,
+ ('o', '*'): 0x03bf,
+ ('p', '*'): 0x03c0,
+ ('r', '*'): 0x03c1,
+ ('*', 's'): 0x03c2,
+ ('s', '*'): 0x03c3,
+ ('t', '*'): 0x03c4,
+ ('u', '*'): 0x03c5,
+ ('f', '*'): 0x03c6,
+ ('x', '*'): 0x03c7,
+ ('q', '*'): 0x03c8,
+ ('w', '*'): 0x03c9,
+ ('j', '*'): 0x03ca,
+ ('v', '*'): 0x03cb,
+ ('o', '%'): 0x03cc,
+ ('u', '%'): 0x03cd,
+ ('w', '%'): 0x03ce,
+ ('\'', 'G'): 0x03d8,
+ (',', 'G'): 0x03d9,
+ ('T', '3'): 0x03da,
+ ('t', '3'): 0x03db,
+ ('M', '3'): 0x03dc,
+ ('m', '3'): 0x03dd,
+ ('K', '3'): 0x03de,
+ ('k', '3'): 0x03df,
+ ('P', '3'): 0x03e0,
+ ('p', '3'): 0x03e1,
+ ('\'', '%'): 0x03f4,
+ ('j', '3'): 0x03f5,
+ ('I', 'O'): 0x0401,
+ ('D', '%'): 0x0402,
+ ('G', '%'): 0x0403,
+ ('I', 'E'): 0x0404,
+ ('D', 'S'): 0x0405,
+ ('I', 'I'): 0x0406,
+ ('Y', 'I'): 0x0407,
+ ('J', '%'): 0x0408,
+ ('L', 'J'): 0x0409,
+ ('N', 'J'): 0x040a,
+ ('T', 's'): 0x040b,
+ ('K', 'J'): 0x040c,
+ ('V', '%'): 0x040e,
+ ('D', 'Z'): 0x040f,
+ ('A', '='): 0x0410,
+ ('B', '='): 0x0411,
+ ('V', '='): 0x0412,
+ ('G', '='): 0x0413,
+ ('D', '='): 0x0414,
+ ('E', '='): 0x0415,
+ ('Z', '%'): 0x0416,
+ ('Z', '='): 0x0417,
+ ('I', '='): 0x0418,
+ ('J', '='): 0x0419,
+ ('K', '='): 0x041a,
+ ('L', '='): 0x041b,
+ ('M', '='): 0x041c,
+ ('N', '='): 0x041d,
+ ('O', '='): 0x041e,
+ ('P', '='): 0x041f,
+ ('R', '='): 0x0420,
+ ('S', '='): 0x0421,
+ ('T', '='): 0x0422,
+ ('U', '='): 0x0423,
+ ('F', '='): 0x0424,
+ ('H', '='): 0x0425,
+ ('C', '='): 0x0426,
+ ('C', '%'): 0x0427,
+ ('S', '%'): 0x0428,
+ ('S', 'c'): 0x0429,
+ ('=', '"'): 0x042a,
+ ('Y', '='): 0x042b,
+ ('%', '"'): 0x042c,
+ ('J', 'E'): 0x042d,
+ ('J', 'U'): 0x042e,
+ ('J', 'A'): 0x042f,
+ ('a', '='): 0x0430,
+ ('b', '='): 0x0431,
+ ('v', '='): 0x0432,
+ ('g', '='): 0x0433,
+ ('d', '='): 0x0434,
+ ('e', '='): 0x0435,
+ ('z', '%'): 0x0436,
+ ('z', '='): 0x0437,
+ ('i', '='): 0x0438,
+ ('j', '='): 0x0439,
+ ('k', '='): 0x043a,
+ ('l', '='): 0x043b,
+ ('m', '='): 0x043c,
+ ('n', '='): 0x043d,
+ ('o', '='): 0x043e,
+ ('p', '='): 0x043f,
+ ('r', '='): 0x0440,
+ ('s', '='): 0x0441,
+ ('t', '='): 0x0442,
+ ('u', '='): 0x0443,
+ ('f', '='): 0x0444,
+ ('h', '='): 0x0445,
+ ('c', '='): 0x0446,
+ ('c', '%'): 0x0447,
+ ('s', '%'): 0x0448,
+ ('s', 'c'): 0x0449,
+ ('=', '\''): 0x044a,
+ ('y', '='): 0x044b,
+ ('%', '\''): 0x044c,
+ ('j', 'e'): 0x044d,
+ ('j', 'u'): 0x044e,
+ ('j', 'a'): 0x044f,
+ ('i', 'o'): 0x0451,
+ ('d', '%'): 0x0452,
+ ('g', '%'): 0x0453,
+ ('i', 'e'): 0x0454,
+ ('d', 's'): 0x0455,
+ ('i', 'i'): 0x0456,
+ ('y', 'i'): 0x0457,
+ ('j', '%'): 0x0458,
+ ('l', 'j'): 0x0459,
+ ('n', 'j'): 0x045a,
+ ('t', 's'): 0x045b,
+ ('k', 'j'): 0x045c,
+ ('v', '%'): 0x045e,
+ ('d', 'z'): 0x045f,
+ ('Y', '3'): 0x0462,
+ ('y', '3'): 0x0463,
+ ('O', '3'): 0x046a,
+ ('o', '3'): 0x046b,
+ ('F', '3'): 0x0472,
+ ('f', '3'): 0x0473,
+ ('V', '3'): 0x0474,
+ ('v', '3'): 0x0475,
+ ('C', '3'): 0x0480,
+ ('c', '3'): 0x0481,
+ ('G', '3'): 0x0490,
+ ('g', '3'): 0x0491,
+ ('A', '+'): 0x05d0,
+ ('B', '+'): 0x05d1,
+ ('G', '+'): 0x05d2,
+ ('D', '+'): 0x05d3,
+ ('H', '+'): 0x05d4,
+ ('W', '+'): 0x05d5,
+ ('Z', '+'): 0x05d6,
+ ('X', '+'): 0x05d7,
+ ('T', 'j'): 0x05d8,
+ ('J', '+'): 0x05d9,
+ ('K', '%'): 0x05da,
+ ('K', '+'): 0x05db,
+ ('L', '+'): 0x05dc,
+ ('M', '%'): 0x05dd,
+ ('M', '+'): 0x05de,
+ ('N', '%'): 0x05df,
+ ('N', '+'): 0x05e0,
+ ('S', '+'): 0x05e1,
+ ('E', '+'): 0x05e2,
+ ('P', '%'): 0x05e3,
+ ('P', '+'): 0x05e4,
+ ('Z', 'j'): 0x05e5,
+ ('Z', 'J'): 0x05e6,
+ ('Q', '+'): 0x05e7,
+ ('R', '+'): 0x05e8,
+ ('S', 'h'): 0x05e9,
+ ('T', '+'): 0x05ea,
+ (',', '+'): 0x060c,
+ (';', '+'): 0x061b,
+ ('?', '+'): 0x061f,
+ ('H', '\''): 0x0621,
+ ('a', 'M'): 0x0622,
+ ('a', 'H'): 0x0623,
+ ('w', 'H'): 0x0624,
+ ('a', 'h'): 0x0625,
+ ('y', 'H'): 0x0626,
+ ('a', '+'): 0x0627,
+ ('b', '+'): 0x0628,
+ ('t', 'm'): 0x0629,
+ ('t', '+'): 0x062a,
+ ('t', 'k'): 0x062b,
+ ('g', '+'): 0x062c,
+ ('h', 'k'): 0x062d,
+ ('x', '+'): 0x062e,
+ ('d', '+'): 0x062f,
+ ('d', 'k'): 0x0630,
+ ('r', '+'): 0x0631,
+ ('z', '+'): 0x0632,
+ ('s', '+'): 0x0633,
+ ('s', 'n'): 0x0634,
+ ('c', '+'): 0x0635,
+ ('d', 'd'): 0x0636,
+ ('t', 'j'): 0x0637,
+ ('z', 'H'): 0x0638,
+ ('e', '+'): 0x0639,
+ ('i', '+'): 0x063a,
+ ('+', '+'): 0x0640,
+ ('f', '+'): 0x0641,
+ ('q', '+'): 0x0642,
+ ('k', '+'): 0x0643,
+ ('l', '+'): 0x0644,
+ ('m', '+'): 0x0645,
+ ('n', '+'): 0x0646,
+ ('h', '+'): 0x0647,
+ ('w', '+'): 0x0648,
+ ('j', '+'): 0x0649,
+ ('y', '+'): 0x064a,
+ (':', '+'): 0x064b,
+ ('"', '+'): 0x064c,
+ ('=', '+'): 0x064d,
+ ('/', '+'): 0x064e,
+ ('\'', '+'): 0x064f,
+ ('1', '+'): 0x0650,
+ ('3', '+'): 0x0651,
+ ('0', '+'): 0x0652,
+ ('a', 'S'): 0x0670,
+ ('p', '+'): 0x067e,
+ ('v', '+'): 0x06a4,
+ ('g', 'f'): 0x06af,
+ ('0', 'a'): 0x06f0,
+ ('1', 'a'): 0x06f1,
+ ('2', 'a'): 0x06f2,
+ ('3', 'a'): 0x06f3,
+ ('4', 'a'): 0x06f4,
+ ('5', 'a'): 0x06f5,
+ ('6', 'a'): 0x06f6,
+ ('7', 'a'): 0x06f7,
+ ('8', 'a'): 0x06f8,
+ ('9', 'a'): 0x06f9,
+ ('B', '.'): 0x1e02,
+ ('b', '.'): 0x1e03,
+ ('B', '_'): 0x1e06,
+ ('b', '_'): 0x1e07,
+ ('D', '.'): 0x1e0a,
+ ('d', '.'): 0x1e0b,
+ ('D', '_'): 0x1e0e,
+ ('d', '_'): 0x1e0f,
+ ('D', ','): 0x1e10,
+ ('d', ','): 0x1e11,
+ ('F', '.'): 0x1e1e,
+ ('f', '.'): 0x1e1f,
+ ('G', '-'): 0x1e20,
+ ('g', '-'): 0x1e21,
+ ('H', '.'): 0x1e22,
+ ('h', '.'): 0x1e23,
+ ('H', ':'): 0x1e26,
+ ('h', ':'): 0x1e27,
+ ('H', ','): 0x1e28,
+ ('h', ','): 0x1e29,
+ ('K', '\''): 0x1e30,
+ ('k', '\''): 0x1e31,
+ ('K', '_'): 0x1e34,
+ ('k', '_'): 0x1e35,
+ ('L', '_'): 0x1e3a,
+ ('l', '_'): 0x1e3b,
+ ('M', '\''): 0x1e3e,
+ ('m', '\''): 0x1e3f,
+ ('M', '.'): 0x1e40,
+ ('m', '.'): 0x1e41,
+ ('N', '.'): 0x1e44,
+ ('n', '.'): 0x1e45,
+ ('N', '_'): 0x1e48,
+ ('n', '_'): 0x1e49,
+ ('P', '\''): 0x1e54,
+ ('p', '\''): 0x1e55,
+ ('P', '.'): 0x1e56,
+ ('p', '.'): 0x1e57,
+ ('R', '.'): 0x1e58,
+ ('r', '.'): 0x1e59,
+ ('R', '_'): 0x1e5e,
+ ('r', '_'): 0x1e5f,
+ ('S', '.'): 0x1e60,
+ ('s', '.'): 0x1e61,
+ ('T', '.'): 0x1e6a,
+ ('t', '.'): 0x1e6b,
+ ('T', '_'): 0x1e6e,
+ ('t', '_'): 0x1e6f,
+ ('V', '?'): 0x1e7c,
+ ('v', '?'): 0x1e7d,
+ ('W', '!'): 0x1e80,
+ ('w', '!'): 0x1e81,
+ ('W', '\''): 0x1e82,
+ ('w', '\''): 0x1e83,
+ ('W', ':'): 0x1e84,
+ ('w', ':'): 0x1e85,
+ ('W', '.'): 0x1e86,
+ ('w', '.'): 0x1e87,
+ ('X', '.'): 0x1e8a,
+ ('x', '.'): 0x1e8b,
+ ('X', ':'): 0x1e8c,
+ ('x', ':'): 0x1e8d,
+ ('Y', '.'): 0x1e8e,
+ ('y', '.'): 0x1e8f,
+ ('Z', '>'): 0x1e90,
+ ('z', '>'): 0x1e91,
+ ('Z', '_'): 0x1e94,
+ ('z', '_'): 0x1e95,
+ ('h', '_'): 0x1e96,
+ ('t', ':'): 0x1e97,
+ ('w', '0'): 0x1e98,
+ ('y', '0'): 0x1e99,
+ ('A', '2'): 0x1ea2,
+ ('a', '2'): 0x1ea3,
+ ('E', '2'): 0x1eba,
+ ('e', '2'): 0x1ebb,
+ ('E', '?'): 0x1ebc,
+ ('e', '?'): 0x1ebd,
+ ('I', '2'): 0x1ec8,
+ ('i', '2'): 0x1ec9,
+ ('O', '2'): 0x1ece,
+ ('o', '2'): 0x1ecf,
+ ('U', '2'): 0x1ee6,
+ ('u', '2'): 0x1ee7,
+ ('Y', '!'): 0x1ef2,
+ ('y', '!'): 0x1ef3,
+ ('Y', '2'): 0x1ef6,
+ ('y', '2'): 0x1ef7,
+ ('Y', '?'): 0x1ef8,
+ ('y', '?'): 0x1ef9,
+ (';', '\''): 0x1f00,
+ (',', '\''): 0x1f01,
+ (';', '!'): 0x1f02,
+ (',', '!'): 0x1f03,
+ ('?', ';'): 0x1f04,
+ ('?', ','): 0x1f05,
+ ('!', ':'): 0x1f06,
+ ('?', ':'): 0x1f07,
+ ('1', 'N'): 0x2002,
+ ('1', 'M'): 0x2003,
+ ('3', 'M'): 0x2004,
+ ('4', 'M'): 0x2005,
+ ('6', 'M'): 0x2006,
+ ('1', 'T'): 0x2009,
+ ('1', 'H'): 0x200a,
+ ('-', '1'): 0x2010,
+ ('-', 'N'): 0x2013,
+ ('-', 'M'): 0x2014,
+ ('-', '3'): 0x2015,
+ ('!', '2'): 0x2016,
+ ('=', '2'): 0x2017,
+ ('\'', '6'): 0x2018,
+ ('\'', '9'): 0x2019,
+ ('.', '9'): 0x201a,
+ ('9', '\''): 0x201b,
+ ('"', '6'): 0x201c,
+ ('"', '9'): 0x201d,
+ (':', '9'): 0x201e,
+ ('9', '"'): 0x201f,
+ ('/', '-'): 0x2020,
+ ('/', '='): 0x2021,
+ ('.', '.'): 0x2025,
+ ('%', '0'): 0x2030,
+ ('1', '\''): 0x2032,
+ ('2', '\''): 0x2033,
+ ('3', '\''): 0x2034,
+ ('1', '"'): 0x2035,
+ ('2', '"'): 0x2036,
+ ('3', '"'): 0x2037,
+ ('C', 'a'): 0x2038,
+ ('<', '1'): 0x2039,
+ ('>', '1'): 0x203a,
+ (':', 'X'): 0x203b,
+ ('\'', '-'): 0x203e,
+ ('/', 'f'): 0x2044,
+ ('0', 'S'): 0x2070,
+ ('4', 'S'): 0x2074,
+ ('5', 'S'): 0x2075,
+ ('6', 'S'): 0x2076,
+ ('7', 'S'): 0x2077,
+ ('8', 'S'): 0x2078,
+ ('9', 'S'): 0x2079,
+ ('+', 'S'): 0x207a,
+ ('-', 'S'): 0x207b,
+ ('=', 'S'): 0x207c,
+ ('(', 'S'): 0x207d,
+ (')', 'S'): 0x207e,
+ ('n', 'S'): 0x207f,
+ ('0', 's'): 0x2080,
+ ('1', 's'): 0x2081,
+ ('2', 's'): 0x2082,
+ ('3', 's'): 0x2083,
+ ('4', 's'): 0x2084,
+ ('5', 's'): 0x2085,
+ ('6', 's'): 0x2086,
+ ('7', 's'): 0x2087,
+ ('8', 's'): 0x2088,
+ ('9', 's'): 0x2089,
+ ('+', 's'): 0x208a,
+ ('-', 's'): 0x208b,
+ ('=', 's'): 0x208c,
+ ('(', 's'): 0x208d,
+ (')', 's'): 0x208e,
+ ('L', 'i'): 0x20a4,
+ ('P', 't'): 0x20a7,
+ ('W', '='): 0x20a9,
+ ('=', 'e'): 0x20ac, # euro
+ ('E', 'u'): 0x20ac, # euro
+ ('=', 'R'): 0x20bd, # rouble
+ ('=', 'P'): 0x20bd, # rouble
+ ('o', 'C'): 0x2103,
+ ('c', 'o'): 0x2105,
+ ('o', 'F'): 0x2109,
+ ('N', '0'): 0x2116,
+ ('P', 'O'): 0x2117,
+ ('R', 'x'): 0x211e,
+ ('S', 'M'): 0x2120,
+ ('T', 'M'): 0x2122,
+ ('O', 'm'): 0x2126,
+ ('A', 'O'): 0x212b,
+ ('1', '3'): 0x2153,
+ ('2', '3'): 0x2154,
+ ('1', '5'): 0x2155,
+ ('2', '5'): 0x2156,
+ ('3', '5'): 0x2157,
+ ('4', '5'): 0x2158,
+ ('1', '6'): 0x2159,
+ ('5', '6'): 0x215a,
+ ('1', '8'): 0x215b,
+ ('3', '8'): 0x215c,
+ ('5', '8'): 0x215d,
+ ('7', '8'): 0x215e,
+ ('1', 'R'): 0x2160,
+ ('2', 'R'): 0x2161,
+ ('3', 'R'): 0x2162,
+ ('4', 'R'): 0x2163,
+ ('5', 'R'): 0x2164,
+ ('6', 'R'): 0x2165,
+ ('7', 'R'): 0x2166,
+ ('8', 'R'): 0x2167,
+ ('9', 'R'): 0x2168,
+ ('a', 'R'): 0x2169,
+ ('b', 'R'): 0x216a,
+ ('c', 'R'): 0x216b,
+ ('1', 'r'): 0x2170,
+ ('2', 'r'): 0x2171,
+ ('3', 'r'): 0x2172,
+ ('4', 'r'): 0x2173,
+ ('5', 'r'): 0x2174,
+ ('6', 'r'): 0x2175,
+ ('7', 'r'): 0x2176,
+ ('8', 'r'): 0x2177,
+ ('9', 'r'): 0x2178,
+ ('a', 'r'): 0x2179,
+ ('b', 'r'): 0x217a,
+ ('c', 'r'): 0x217b,
+ ('<', '-'): 0x2190,
+ ('-', '!'): 0x2191,
+ ('-', '>'): 0x2192,
+ ('-', 'v'): 0x2193,
+ ('<', '>'): 0x2194,
+ ('U', 'D'): 0x2195,
+ ('<', '='): 0x21d0,
+ ('=', '>'): 0x21d2,
+ ('=', '='): 0x21d4,
+ ('F', 'A'): 0x2200,
+ ('d', 'P'): 0x2202,
+ ('T', 'E'): 0x2203,
+ ('/', '0'): 0x2205,
+ ('D', 'E'): 0x2206,
+ ('N', 'B'): 0x2207,
+ ('(', '-'): 0x2208,
+ ('-', ')'): 0x220b,
+ ('*', 'P'): 0x220f,
+ ('+', 'Z'): 0x2211,
+ ('-', '2'): 0x2212,
+ ('-', '+'): 0x2213,
+ ('*', '-'): 0x2217,
+ ('O', 'b'): 0x2218,
+ ('S', 'b'): 0x2219,
+ ('R', 'T'): 0x221a,
+ ('0', '('): 0x221d,
+ ('0', '0'): 0x221e,
+ ('-', 'L'): 0x221f,
+ ('-', 'V'): 0x2220,
+ ('P', 'P'): 0x2225,
+ ('A', 'N'): 0x2227,
+ ('O', 'R'): 0x2228,
+ ('(', 'U'): 0x2229,
+ (')', 'U'): 0x222a,
+ ('I', 'n'): 0x222b,
+ ('D', 'I'): 0x222c,
+ ('I', 'o'): 0x222e,
+ ('.', ':'): 0x2234,
+ (':', '.'): 0x2235,
+ (':', 'R'): 0x2236,
+ (':', ':'): 0x2237,
+ ('?', '1'): 0x223c,
+ ('C', 'G'): 0x223e,
+ ('?', '-'): 0x2243,
+ ('?', '='): 0x2245,
+ ('?', '2'): 0x2248,
+ ('=', '?'): 0x224c,
+ ('H', 'I'): 0x2253,
+ ('!', '='): 0x2260,
+ ('=', '3'): 0x2261,
+ ('=', '<'): 0x2264,
+ ('>', '='): 0x2265,
+ ('<', '*'): 0x226a,
+ ('*', '>'): 0x226b,
+ ('!', '<'): 0x226e,
+ ('!', '>'): 0x226f,
+ ('(', 'C'): 0x2282,
+ (')', 'C'): 0x2283,
+ ('(', '_'): 0x2286,
+ (')', '_'): 0x2287,
+ ('0', '.'): 0x2299,
+ ('0', '2'): 0x229a,
+ ('-', 'T'): 0x22a5,
+ ('.', 'P'): 0x22c5,
+ (':', '3'): 0x22ee,
+ ('.', '3'): 0x22ef,
+ ('E', 'h'): 0x2302,
+ ('<', '7'): 0x2308,
+ ('>', '7'): 0x2309,
+ ('7', '<'): 0x230a,
+ ('7', '>'): 0x230b,
+ ('N', 'I'): 0x2310,
+ ('(', 'A'): 0x2312,
+ ('T', 'R'): 0x2315,
+ ('I', 'u'): 0x2320,
+ ('I', 'l'): 0x2321,
+ ('<', '/'): 0x2329,
+ ('/', '>'): 0x232a,
+ ('V', 's'): 0x2423,
+ ('1', 'h'): 0x2440,
+ ('3', 'h'): 0x2441,
+ ('2', 'h'): 0x2442,
+ ('4', 'h'): 0x2443,
+ ('1', 'j'): 0x2446,
+ ('2', 'j'): 0x2447,
+ ('3', 'j'): 0x2448,
+ ('4', 'j'): 0x2449,
+ ('1', '.'): 0x2488,
+ ('2', '.'): 0x2489,
+ ('3', '.'): 0x248a,
+ ('4', '.'): 0x248b,
+ ('5', '.'): 0x248c,
+ ('6', '.'): 0x248d,
+ ('7', '.'): 0x248e,
+ ('8', '.'): 0x248f,
+ ('9', '.'): 0x2490,
+ ('h', 'h'): 0x2500,
+ ('H', 'H'): 0x2501,
+ ('v', 'v'): 0x2502,
+ ('V', 'V'): 0x2503,
+ ('3', '-'): 0x2504,
+ ('3', '_'): 0x2505,
+ ('3', '!'): 0x2506,
+ ('3', '/'): 0x2507,
+ ('4', '-'): 0x2508,
+ ('4', '_'): 0x2509,
+ ('4', '!'): 0x250a,
+ ('4', '/'): 0x250b,
+ ('d', 'r'): 0x250c,
+ ('d', 'R'): 0x250d,
+ ('D', 'r'): 0x250e,
+ ('D', 'R'): 0x250f,
+ ('d', 'l'): 0x2510,
+ ('d', 'L'): 0x2511,
+ ('D', 'l'): 0x2512,
+ ('L', 'D'): 0x2513,
+ ('u', 'r'): 0x2514,
+ ('u', 'R'): 0x2515,
+ ('U', 'r'): 0x2516,
+ ('U', 'R'): 0x2517,
+ ('u', 'l'): 0x2518,
+ ('u', 'L'): 0x2519,
+ ('U', 'l'): 0x251a,
+ ('U', 'L'): 0x251b,
+ ('v', 'r'): 0x251c,
+ ('v', 'R'): 0x251d,
+ ('V', 'r'): 0x2520,
+ ('V', 'R'): 0x2523,
+ ('v', 'l'): 0x2524,
+ ('v', 'L'): 0x2525,
+ ('V', 'l'): 0x2528,
+ ('V', 'L'): 0x252b,
+ ('d', 'h'): 0x252c,
+ ('d', 'H'): 0x252f,
+ ('D', 'h'): 0x2530,
+ ('D', 'H'): 0x2533,
+ ('u', 'h'): 0x2534,
+ ('u', 'H'): 0x2537,
+ ('U', 'h'): 0x2538,
+ ('U', 'H'): 0x253b,
+ ('v', 'h'): 0x253c,
+ ('v', 'H'): 0x253f,
+ ('V', 'h'): 0x2542,
+ ('V', 'H'): 0x254b,
+ ('F', 'D'): 0x2571,
+ ('B', 'D'): 0x2572,
+ ('T', 'B'): 0x2580,
+ ('L', 'B'): 0x2584,
+ ('F', 'B'): 0x2588,
+ ('l', 'B'): 0x258c,
+ ('R', 'B'): 0x2590,
+ ('.', 'S'): 0x2591,
+ (':', 'S'): 0x2592,
+ ('?', 'S'): 0x2593,
+ ('f', 'S'): 0x25a0,
+ ('O', 'S'): 0x25a1,
+ ('R', 'O'): 0x25a2,
+ ('R', 'r'): 0x25a3,
+ ('R', 'F'): 0x25a4,
+ ('R', 'Y'): 0x25a5,
+ ('R', 'H'): 0x25a6,
+ ('R', 'Z'): 0x25a7,
+ ('R', 'K'): 0x25a8,
+ ('R', 'X'): 0x25a9,
+ ('s', 'B'): 0x25aa,
+ ('S', 'R'): 0x25ac,
+ ('O', 'r'): 0x25ad,
+ ('U', 'T'): 0x25b2,
+ ('u', 'T'): 0x25b3,
+ ('P', 'R'): 0x25b6,
+ ('T', 'r'): 0x25b7,
+ ('D', 't'): 0x25bc,
+ ('d', 'T'): 0x25bd,
+ ('P', 'L'): 0x25c0,
+ ('T', 'l'): 0x25c1,
+ ('D', 'b'): 0x25c6,
+ ('D', 'w'): 0x25c7,
+ ('L', 'Z'): 0x25ca,
+ ('0', 'm'): 0x25cb,
+ ('0', 'o'): 0x25ce,
+ ('0', 'M'): 0x25cf,
+ ('0', 'L'): 0x25d0,
+ ('0', 'R'): 0x25d1,
+ ('S', 'n'): 0x25d8,
+ ('I', 'c'): 0x25d9,
+ ('F', 'd'): 0x25e2,
+ ('B', 'd'): 0x25e3,
+ ('*', '2'): 0x2605,
+ ('*', '1'): 0x2606,
+ ('<', 'H'): 0x261c,
+ ('>', 'H'): 0x261e,
+ ('0', 'u'): 0x263a,
+ ('0', 'U'): 0x263b,
+ ('S', 'U'): 0x263c,
+ ('F', 'm'): 0x2640,
+ ('M', 'l'): 0x2642,
+ ('c', 'S'): 0x2660,
+ ('c', 'H'): 0x2661,
+ ('c', 'D'): 0x2662,
+ ('c', 'C'): 0x2663,
+ ('M', 'd'): 0x2669,
+ ('M', '8'): 0x266a,
+ ('M', '2'): 0x266b,
+ ('M', 'b'): 0x266d,
+ ('M', 'x'): 0x266e,
+ ('M', 'X'): 0x266f,
+ ('O', 'K'): 0x2713,
+ ('X', 'X'): 0x2717,
+ ('-', 'X'): 0x2720,
+ ('I', 'S'): 0x3000,
+ (',', '_'): 0x3001,
+ ('.', '_'): 0x3002,
+ ('+', '"'): 0x3003,
+ ('+', '_'): 0x3004,
+ ('*', '_'): 0x3005,
+ (';', '_'): 0x3006,
+ ('0', '_'): 0x3007,
+ ('<', '+'): 0x300a,
+ ('>', '+'): 0x300b,
+ ('<', '\''): 0x300c,
+ ('>', '\''): 0x300d,
+ ('<', '"'): 0x300e,
+ ('>', '"'): 0x300f,
+ ('(', '"'): 0x3010,
+ (')', '"'): 0x3011,
+ ('=', 'T'): 0x3012,
+ ('=', '_'): 0x3013,
+ ('(', '\''): 0x3014,
+ (')', '\''): 0x3015,
+ ('(', 'I'): 0x3016,
+ (')', 'I'): 0x3017,
+ ('-', '?'): 0x301c,
+ ('A', '5'): 0x3041,
+ ('a', '5'): 0x3042,
+ ('I', '5'): 0x3043,
+ ('i', '5'): 0x3044,
+ ('U', '5'): 0x3045,
+ ('u', '5'): 0x3046,
+ ('E', '5'): 0x3047,
+ ('e', '5'): 0x3048,
+ ('O', '5'): 0x3049,
+ ('o', '5'): 0x304a,
+ ('k', 'a'): 0x304b,
+ ('g', 'a'): 0x304c,
+ ('k', 'i'): 0x304d,
+ ('g', 'i'): 0x304e,
+ ('k', 'u'): 0x304f,
+ ('g', 'u'): 0x3050,
+ ('k', 'e'): 0x3051,
+ ('g', 'e'): 0x3052,
+ ('k', 'o'): 0x3053,
+ ('g', 'o'): 0x3054,
+ ('s', 'a'): 0x3055,
+ ('z', 'a'): 0x3056,
+ ('s', 'i'): 0x3057,
+ ('z', 'i'): 0x3058,
+ ('s', 'u'): 0x3059,
+ ('z', 'u'): 0x305a,
+ ('s', 'e'): 0x305b,
+ ('z', 'e'): 0x305c,
+ ('s', 'o'): 0x305d,
+ ('z', 'o'): 0x305e,
+ ('t', 'a'): 0x305f,
+ ('d', 'a'): 0x3060,
+ ('t', 'i'): 0x3061,
+ ('d', 'i'): 0x3062,
+ ('t', 'U'): 0x3063,
+ ('t', 'u'): 0x3064,
+ ('d', 'u'): 0x3065,
+ ('t', 'e'): 0x3066,
+ ('d', 'e'): 0x3067,
+ ('t', 'o'): 0x3068,
+ ('d', 'o'): 0x3069,
+ ('n', 'a'): 0x306a,
+ ('n', 'i'): 0x306b,
+ ('n', 'u'): 0x306c,
+ ('n', 'e'): 0x306d,
+ ('n', 'o'): 0x306e,
+ ('h', 'a'): 0x306f,
+ ('b', 'a'): 0x3070,
+ ('p', 'a'): 0x3071,
+ ('h', 'i'): 0x3072,
+ ('b', 'i'): 0x3073,
+ ('p', 'i'): 0x3074,
+ ('h', 'u'): 0x3075,
+ ('b', 'u'): 0x3076,
+ ('p', 'u'): 0x3077,
+ ('h', 'e'): 0x3078,
+ ('b', 'e'): 0x3079,
+ ('p', 'e'): 0x307a,
+ ('h', 'o'): 0x307b,
+ ('b', 'o'): 0x307c,
+ ('p', 'o'): 0x307d,
+ ('m', 'a'): 0x307e,
+ ('m', 'i'): 0x307f,
+ ('m', 'u'): 0x3080,
+ ('m', 'e'): 0x3081,
+ ('m', 'o'): 0x3082,
+ ('y', 'A'): 0x3083,
+ ('y', 'a'): 0x3084,
+ ('y', 'U'): 0x3085,
+ ('y', 'u'): 0x3086,
+ ('y', 'O'): 0x3087,
+ ('y', 'o'): 0x3088,
+ ('r', 'a'): 0x3089,
+ ('r', 'i'): 0x308a,
+ ('r', 'u'): 0x308b,
+ ('r', 'e'): 0x308c,
+ ('r', 'o'): 0x308d,
+ ('w', 'A'): 0x308e,
+ ('w', 'a'): 0x308f,
+ ('w', 'i'): 0x3090,
+ ('w', 'e'): 0x3091,
+ ('w', 'o'): 0x3092,
+ ('n', '5'): 0x3093,
+ ('v', 'u'): 0x3094,
+ ('"', '5'): 0x309b,
+ ('0', '5'): 0x309c,
+ ('*', '5'): 0x309d,
+ ('+', '5'): 0x309e,
+ ('a', '6'): 0x30a1,
+ ('A', '6'): 0x30a2,
+ ('i', '6'): 0x30a3,
+ ('I', '6'): 0x30a4,
+ ('u', '6'): 0x30a5,
+ ('U', '6'): 0x30a6,
+ ('e', '6'): 0x30a7,
+ ('E', '6'): 0x30a8,
+ ('o', '6'): 0x30a9,
+ ('O', '6'): 0x30aa,
+ ('K', 'a'): 0x30ab,
+ ('G', 'a'): 0x30ac,
+ ('K', 'i'): 0x30ad,
+ ('G', 'i'): 0x30ae,
+ ('K', 'u'): 0x30af,
+ ('G', 'u'): 0x30b0,
+ ('K', 'e'): 0x30b1,
+ ('G', 'e'): 0x30b2,
+ ('K', 'o'): 0x30b3,
+ ('G', 'o'): 0x30b4,
+ ('S', 'a'): 0x30b5,
+ ('Z', 'a'): 0x30b6,
+ ('S', 'i'): 0x30b7,
+ ('Z', 'i'): 0x30b8,
+ ('S', 'u'): 0x30b9,
+ ('Z', 'u'): 0x30ba,
+ ('S', 'e'): 0x30bb,
+ ('Z', 'e'): 0x30bc,
+ ('S', 'o'): 0x30bd,
+ ('Z', 'o'): 0x30be,
+ ('T', 'a'): 0x30bf,
+ ('D', 'a'): 0x30c0,
+ ('T', 'i'): 0x30c1,
+ ('D', 'i'): 0x30c2,
+ ('T', 'U'): 0x30c3,
+ ('T', 'u'): 0x30c4,
+ ('D', 'u'): 0x30c5,
+ ('T', 'e'): 0x30c6,
+ ('D', 'e'): 0x30c7,
+ ('T', 'o'): 0x30c8,
+ ('D', 'o'): 0x30c9,
+ ('N', 'a'): 0x30ca,
+ ('N', 'i'): 0x30cb,
+ ('N', 'u'): 0x30cc,
+ ('N', 'e'): 0x30cd,
+ ('N', 'o'): 0x30ce,
+ ('H', 'a'): 0x30cf,
+ ('B', 'a'): 0x30d0,
+ ('P', 'a'): 0x30d1,
+ ('H', 'i'): 0x30d2,
+ ('B', 'i'): 0x30d3,
+ ('P', 'i'): 0x30d4,
+ ('H', 'u'): 0x30d5,
+ ('B', 'u'): 0x30d6,
+ ('P', 'u'): 0x30d7,
+ ('H', 'e'): 0x30d8,
+ ('B', 'e'): 0x30d9,
+ ('P', 'e'): 0x30da,
+ ('H', 'o'): 0x30db,
+ ('B', 'o'): 0x30dc,
+ ('P', 'o'): 0x30dd,
+ ('M', 'a'): 0x30de,
+ ('M', 'i'): 0x30df,
+ ('M', 'u'): 0x30e0,
+ ('M', 'e'): 0x30e1,
+ ('M', 'o'): 0x30e2,
+ ('Y', 'A'): 0x30e3,
+ ('Y', 'a'): 0x30e4,
+ ('Y', 'U'): 0x30e5,
+ ('Y', 'u'): 0x30e6,
+ ('Y', 'O'): 0x30e7,
+ ('Y', 'o'): 0x30e8,
+ ('R', 'a'): 0x30e9,
+ ('R', 'i'): 0x30ea,
+ ('R', 'u'): 0x30eb,
+ ('R', 'e'): 0x30ec,
+ ('R', 'o'): 0x30ed,
+ ('W', 'A'): 0x30ee,
+ ('W', 'a'): 0x30ef,
+ ('W', 'i'): 0x30f0,
+ ('W', 'e'): 0x30f1,
+ ('W', 'o'): 0x30f2,
+ ('N', '6'): 0x30f3,
+ ('V', 'u'): 0x30f4,
+ ('K', 'A'): 0x30f5,
+ ('K', 'E'): 0x30f6,
+ ('V', 'a'): 0x30f7,
+ ('V', 'i'): 0x30f8,
+ ('V', 'e'): 0x30f9,
+ ('V', 'o'): 0x30fa,
+ ('.', '6'): 0x30fb,
+ ('-', '6'): 0x30fc,
+ ('*', '6'): 0x30fd,
+ ('+', '6'): 0x30fe,
+ ('b', '4'): 0x3105,
+ ('p', '4'): 0x3106,
+ ('m', '4'): 0x3107,
+ ('f', '4'): 0x3108,
+ ('d', '4'): 0x3109,
+ ('t', '4'): 0x310a,
+ ('n', '4'): 0x310b,
+ ('l', '4'): 0x310c,
+ ('g', '4'): 0x310d,
+ ('k', '4'): 0x310e,
+ ('h', '4'): 0x310f,
+ ('j', '4'): 0x3110,
+ ('q', '4'): 0x3111,
+ ('x', '4'): 0x3112,
+ ('z', 'h'): 0x3113,
+ ('c', 'h'): 0x3114,
+ ('s', 'h'): 0x3115,
+ ('r', '4'): 0x3116,
+ ('z', '4'): 0x3117,
+ ('c', '4'): 0x3118,
+ ('s', '4'): 0x3119,
+ ('a', '4'): 0x311a,
+ ('o', '4'): 0x311b,
+ ('e', '4'): 0x311c,
+ ('a', 'i'): 0x311e,
+ ('e', 'i'): 0x311f,
+ ('a', 'u'): 0x3120,
+ ('o', 'u'): 0x3121,
+ ('a', 'n'): 0x3122,
+ ('e', 'n'): 0x3123,
+ ('a', 'N'): 0x3124,
+ ('e', 'N'): 0x3125,
+ ('e', 'r'): 0x3126,
+ ('i', '4'): 0x3127,
+ ('u', '4'): 0x3128,
+ ('i', 'u'): 0x3129,
+ ('v', '4'): 0x312a,
+ ('n', 'G'): 0x312b,
+ ('g', 'n'): 0x312c,
+ ('1', 'c'): 0x3220,
+ ('2', 'c'): 0x3221,
+ ('3', 'c'): 0x3222,
+ ('4', 'c'): 0x3223,
+ ('5', 'c'): 0x3224,
+ ('6', 'c'): 0x3225,
+ ('7', 'c'): 0x3226,
+ ('8', 'c'): 0x3227,
+ ('9', 'c'): 0x3228,
+
+ # code points 0xe000 - 0xefff excluded, they have no assigned
+ # characters, only used in proposals.
+ ('f', 'f'): 0xfb00,
+ ('f', 'i'): 0xfb01,
+ ('f', 'l'): 0xfb02,
+ ('f', 't'): 0xfb05,
+ ('s', 't'): 0xfb06,
+
+ # Vim 5.x compatible digraphs that don't conflict with the above
+ ('~', '!'): 161,
+ ('c', '|'): 162,
+ ('$', '$'): 163,
+ ('o', 'x'): 164, # currency symbol in ISO 8859-1
+ ('Y', '-'): 165,
+ ('|', '|'): 166,
+ ('c', 'O'): 169,
+ ('-', ','): 172,
+ ('-', '='): 175,
+ ('~', 'o'): 176,
+ ('2', '2'): 178,
+ ('3', '3'): 179,
+ ('p', 'p'): 182,
+ ('~', '.'): 183,
+ ('1', '1'): 185,
+ ('~', '?'): 191,
+ ('A', '`'): 192,
+ ('A', '^'): 194,
+ ('A', '~'): 195,
+ ('A', '"'): 196,
+ ('A', '@'): 197,
+ ('E', '`'): 200,
+ ('E', '^'): 202,
+ ('E', '"'): 203,
+ ('I', '`'): 204,
+ ('I', '^'): 206,
+ ('I', '"'): 207,
+ ('N', '~'): 209,
+ ('O', '`'): 210,
+ ('O', '^'): 212,
+ ('O', '~'): 213,
+ ('/', '\\'): 215, # multiplication symbol in ISO 8859-1
+ ('U', '`'): 217,
+ ('U', '^'): 219,
+ ('I', 'p'): 222,
+ ('a', '`'): 224,
+ ('a', '^'): 226,
+ ('a', '~'): 227,
+ ('a', '"'): 228,
+ ('a', '@'): 229,
+ ('e', '`'): 232,
+ ('e', '^'): 234,
+ ('e', '"'): 235,
+ ('i', '`'): 236,
+ ('i', '^'): 238,
+ ('n', '~'): 241,
+ ('o', '`'): 242,
+ ('o', '^'): 244,
+ ('o', '~'): 245,
+ ('u', '`'): 249,
+ ('u', '^'): 251,
+ ('y', '"'): 255,
+}
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/input_processor.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/input_processor.py
new file mode 100644
index 0000000000..51a3110827
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/input_processor.py
@@ -0,0 +1,372 @@
+# *** encoding: utf-8 ***
+"""
+An :class:`~.InputProcessor` receives callbacks for the keystrokes parsed from
+the input in the :class:`~prompt_toolkit.inputstream.InputStream` instance.
+
+The `InputProcessor` will according to the implemented keybindings call the
+correct callbacks when new key presses are feed through `feed`.
+"""
+from __future__ import unicode_literals
+from prompt_toolkit.buffer import EditReadOnlyBuffer
+from prompt_toolkit.filters.cli import ViNavigationMode
+from prompt_toolkit.keys import Keys, Key
+from prompt_toolkit.utils import Event
+
+from .registry import BaseRegistry
+
+from collections import deque
+from six.moves import range
+import weakref
+import six
+
+__all__ = (
+ 'InputProcessor',
+ 'KeyPress',
+)
+
+
+class KeyPress(object):
+ """
+ :param key: A `Keys` instance or text (one character).
+ :param data: The received string on stdin. (Often vt100 escape codes.)
+ """
+ def __init__(self, key, data=None):
+ assert isinstance(key, (six.text_type, Key))
+ assert data is None or isinstance(data, six.text_type)
+
+ if data is None:
+ data = key.name if isinstance(key, Key) else key
+
+ self.key = key
+ self.data = data
+
+ def __repr__(self):
+ return '%s(key=%r, data=%r)' % (
+ self.__class__.__name__, self.key, self.data)
+
+ def __eq__(self, other):
+ return self.key == other.key and self.data == other.data
+
+
+class InputProcessor(object):
+ """
+ Statemachine that receives :class:`KeyPress` instances and according to the
+ key bindings in the given :class:`Registry`, calls the matching handlers.
+
+ ::
+
+ p = InputProcessor(registry)
+
+ # Send keys into the processor.
+ p.feed(KeyPress(Keys.ControlX, '\x18'))
+ p.feed(KeyPress(Keys.ControlC, '\x03')
+
+ # Process all the keys in the queue.
+ p.process_keys()
+
+ # Now the ControlX-ControlC callback will be called if this sequence is
+ # registered in the registry.
+
+ :param registry: `BaseRegistry` instance.
+ :param cli_ref: weakref to `CommandLineInterface`.
+ """
+ def __init__(self, registry, cli_ref):
+ assert isinstance(registry, BaseRegistry)
+
+ self._registry = registry
+ self._cli_ref = cli_ref
+
+ self.beforeKeyPress = Event(self)
+ self.afterKeyPress = Event(self)
+
+ # The queue of keys not yet send to our _process generator/state machine.
+ self.input_queue = deque()
+
+ # The key buffer that is matched in the generator state machine.
+ # (This is at at most the amount of keys that make up for one key binding.)
+ self.key_buffer = []
+
+ # Simple macro recording. (Like readline does.)
+ self.record_macro = False
+ self.macro = []
+
+ self.reset()
+
+ def reset(self):
+ self._previous_key_sequence = []
+ self._previous_handler = None
+
+ self._process_coroutine = self._process()
+ self._process_coroutine.send(None)
+
+ #: Readline argument (for repetition of commands.)
+ #: https://www.gnu.org/software/bash/manual/html_node/Readline-Arguments.html
+ self.arg = None
+
+ def start_macro(self):
+ " Start recording macro. "
+ self.record_macro = True
+ self.macro = []
+
+ def end_macro(self):
+ " End recording macro. "
+ self.record_macro = False
+
+ def call_macro(self):
+ for k in self.macro:
+ self.feed(k)
+
+ def _get_matches(self, key_presses):
+ """
+ For a list of :class:`KeyPress` instances. Give the matching handlers
+ that would handle this.
+ """
+ keys = tuple(k.key for k in key_presses)
+ cli = self._cli_ref()
+
+ # Try match, with mode flag
+ return [b for b in self._registry.get_bindings_for_keys(keys) if b.filter(cli)]
+
+ def _is_prefix_of_longer_match(self, key_presses):
+ """
+ For a list of :class:`KeyPress` instances. Return True if there is any
+ handler that is bound to a suffix of this keys.
+ """
+ keys = tuple(k.key for k in key_presses)
+ cli = self._cli_ref()
+
+ # Get the filters for all the key bindings that have a longer match.
+ # Note that we transform it into a `set`, because we don't care about
+ # the actual bindings and executing it more than once doesn't make
+ # sense. (Many key bindings share the same filter.)
+ filters = set(b.filter for b in self._registry.get_bindings_starting_with_keys(keys))
+
+ # When any key binding is active, return True.
+ return any(f(cli) for f in filters)
+
+ def _process(self):
+ """
+ Coroutine implementing the key match algorithm. Key strokes are sent
+ into this generator, and it calls the appropriate handlers.
+ """
+ buffer = self.key_buffer
+ retry = False
+
+ while True:
+ if retry:
+ retry = False
+ else:
+ buffer.append((yield))
+
+ # If we have some key presses, check for matches.
+ if buffer:
+ is_prefix_of_longer_match = self._is_prefix_of_longer_match(buffer)
+ matches = self._get_matches(buffer)
+
+ # When eager matches were found, give priority to them and also
+ # ignore all the longer matches.
+ eager_matches = [m for m in matches if m.eager(self._cli_ref())]
+
+ if eager_matches:
+ matches = eager_matches
+ is_prefix_of_longer_match = False
+
+ # Exact matches found, call handler.
+ if not is_prefix_of_longer_match and matches:
+ self._call_handler(matches[-1], key_sequence=buffer[:])
+ del buffer[:] # Keep reference.
+
+ # No match found.
+ elif not is_prefix_of_longer_match and not matches:
+ retry = True
+ found = False
+
+ # Loop over the input, try longest match first and shift.
+ for i in range(len(buffer), 0, -1):
+ matches = self._get_matches(buffer[:i])
+ if matches:
+ self._call_handler(matches[-1], key_sequence=buffer[:i])
+ del buffer[:i]
+ found = True
+ break
+
+ if not found:
+ del buffer[:1]
+
+ def feed(self, key_press):
+ """
+ Add a new :class:`KeyPress` to the input queue.
+ (Don't forget to call `process_keys` in order to process the queue.)
+ """
+ assert isinstance(key_press, KeyPress)
+ self.input_queue.append(key_press)
+
+ def process_keys(self):
+ """
+ Process all the keys in the `input_queue`.
+ (To be called after `feed`.)
+
+ Note: because of the `feed`/`process_keys` separation, it is
+ possible to call `feed` from inside a key binding.
+ This function keeps looping until the queue is empty.
+ """
+ while self.input_queue:
+ key_press = self.input_queue.popleft()
+
+ if key_press.key != Keys.CPRResponse:
+ self.beforeKeyPress.fire()
+
+ self._process_coroutine.send(key_press)
+
+ if key_press.key != Keys.CPRResponse:
+ self.afterKeyPress.fire()
+
+ # Invalidate user interface.
+ cli = self._cli_ref()
+ if cli:
+ cli.invalidate()
+
+ def _call_handler(self, handler, key_sequence=None):
+ was_recording = self.record_macro
+ arg = self.arg
+ self.arg = None
+
+ event = KeyPressEvent(
+ weakref.ref(self), arg=arg, key_sequence=key_sequence,
+ previous_key_sequence=self._previous_key_sequence,
+ is_repeat=(handler == self._previous_handler))
+
+ # Save the state of the current buffer.
+ cli = event.cli # Can be `None` (In unit-tests only.)
+
+ if handler.save_before(event) and cli:
+ cli.current_buffer.save_to_undo_stack()
+
+ # Call handler.
+ try:
+ handler.call(event)
+ self._fix_vi_cursor_position(event)
+
+ except EditReadOnlyBuffer:
+ # When a key binding does an attempt to change a buffer which is
+ # read-only, we can just silently ignore that.
+ pass
+
+ self._previous_key_sequence = key_sequence
+ self._previous_handler = handler
+
+ # Record the key sequence in our macro. (Only if we're in macro mode
+ # before and after executing the key.)
+ if self.record_macro and was_recording:
+ self.macro.extend(key_sequence)
+
+ def _fix_vi_cursor_position(self, event):
+ """
+ After every command, make sure that if we are in Vi navigation mode, we
+ never put the cursor after the last character of a line. (Unless it's
+ an empty line.)
+ """
+ cli = self._cli_ref()
+ if cli:
+ buff = cli.current_buffer
+ preferred_column = buff.preferred_column
+
+ if (ViNavigationMode()(event.cli) and
+ buff.document.is_cursor_at_the_end_of_line and
+ len(buff.document.current_line) > 0):
+ buff.cursor_position -= 1
+
+ # Set the preferred_column for arrow up/down again.
+ # (This was cleared after changing the cursor position.)
+ buff.preferred_column = preferred_column
+
+
+
+class KeyPressEvent(object):
+ """
+ Key press event, delivered to key bindings.
+
+ :param input_processor_ref: Weak reference to the `InputProcessor`.
+ :param arg: Repetition argument.
+ :param key_sequence: List of `KeyPress` instances.
+ :param previouskey_sequence: Previous list of `KeyPress` instances.
+ :param is_repeat: True when the previous event was delivered to the same handler.
+ """
+ def __init__(self, input_processor_ref, arg=None, key_sequence=None,
+ previous_key_sequence=None, is_repeat=False):
+ self._input_processor_ref = input_processor_ref
+ self.key_sequence = key_sequence
+ self.previous_key_sequence = previous_key_sequence
+
+ #: True when the previous key sequence was handled by the same handler.
+ self.is_repeat = is_repeat
+
+ self._arg = arg
+
+ def __repr__(self):
+ return 'KeyPressEvent(arg=%r, key_sequence=%r, is_repeat=%r)' % (
+ self.arg, self.key_sequence, self.is_repeat)
+
+ @property
+ def data(self):
+ return self.key_sequence[-1].data
+
+ @property
+ def input_processor(self):
+ return self._input_processor_ref()
+
+ @property
+ def cli(self):
+ """
+ Command line interface.
+ """
+ return self.input_processor._cli_ref()
+
+ @property
+ def current_buffer(self):
+ """
+ The current buffer.
+ """
+ return self.cli.current_buffer
+
+ @property
+ def arg(self):
+ """
+ Repetition argument.
+ """
+ if self._arg == '-':
+ return -1
+
+ result = int(self._arg or 1)
+
+ # Don't exceed a million.
+ if int(result) >= 1000000:
+ result = 1
+
+ return result
+
+ @property
+ def arg_present(self):
+ """
+ True if repetition argument was explicitly provided.
+ """
+ return self._arg is not None
+
+ def append_to_arg_count(self, data):
+ """
+ Add digit to the input argument.
+
+ :param data: the typed digit as string
+ """
+ assert data in '-0123456789'
+ current = self._arg
+
+ if data == '-':
+ assert current is None or current == '-'
+ result = data
+ elif current is None:
+ result = data
+ else:
+ result = "%s%s" % (current, data)
+
+ self.input_processor.arg = result
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/manager.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/manager.py
new file mode 100644
index 0000000000..83612c2a5c
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/manager.py
@@ -0,0 +1,96 @@
+"""
+DEPRECATED:
+Use `prompt_toolkit.key_binding.defaults.load_key_bindings` instead.
+
+:class:`KeyBindingManager` is a utility (or shortcut) for loading all the key
+bindings in a key binding registry, with a logic set of filters to quickly to
+quickly change from Vi to Emacs key bindings at runtime.
+
+You don't have to use this, but it's practical.
+
+Usage::
+
+ manager = KeyBindingManager()
+ app = Application(key_bindings_registry=manager.registry)
+"""
+from __future__ import unicode_literals
+from .defaults import load_key_bindings
+from prompt_toolkit.filters import to_cli_filter
+from prompt_toolkit.key_binding.registry import Registry, ConditionalRegistry, MergedRegistry
+
+__all__ = (
+ 'KeyBindingManager',
+)
+
+
+class KeyBindingManager(object):
+ """
+ Utility for loading all key bindings into memory.
+
+ :param registry: Optional `Registry` instance.
+ :param enable_abort_and_exit_bindings: Filter to enable Ctrl-C and Ctrl-D.
+ :param enable_system_bindings: Filter to enable the system bindings
+ (meta-! prompt and Control-Z suspension.)
+ :param enable_search: Filter to enable the search bindings.
+ :param enable_open_in_editor: Filter to enable open-in-editor.
+ :param enable_open_in_editor: Filter to enable open-in-editor.
+ :param enable_extra_page_navigation: Filter for enabling extra page navigation.
+ (Bindings for up/down scrolling through long pages, like in Emacs or Vi.)
+ :param enable_auto_suggest_bindings: Filter to enable fish-style suggestions.
+
+ :param enable_vi_mode: Deprecated!
+ """
+ def __init__(self,
+ registry=None, # XXX: not used anymore.
+ enable_vi_mode=None, # (`enable_vi_mode` is deprecated.)
+ enable_all=True, #
+ get_search_state=None,
+ enable_abort_and_exit_bindings=False,
+ enable_system_bindings=False,
+ enable_search=False,
+ enable_open_in_editor=False,
+ enable_extra_page_navigation=False,
+ enable_auto_suggest_bindings=False):
+
+ assert registry is None or isinstance(registry, Registry)
+ assert get_search_state is None or callable(get_search_state)
+ enable_all = to_cli_filter(enable_all)
+
+ defaults = load_key_bindings(
+ get_search_state=get_search_state,
+ enable_abort_and_exit_bindings=enable_abort_and_exit_bindings,
+ enable_system_bindings=enable_system_bindings,
+ enable_search=enable_search,
+ enable_open_in_editor=enable_open_in_editor,
+ enable_extra_page_navigation=enable_extra_page_navigation,
+ enable_auto_suggest_bindings=enable_auto_suggest_bindings)
+
+ # Note, we wrap this whole thing again in a MergedRegistry, because we
+ # don't want the `enable_all` settings to apply on items that were
+ # added to the registry as a whole.
+ self.registry = MergedRegistry([
+ ConditionalRegistry(defaults, enable_all)
+ ])
+
+ @classmethod
+ def for_prompt(cls, **kw):
+ """
+ Create a ``KeyBindingManager`` with the defaults for an input prompt.
+ This activates the key bindings for abort/exit (Ctrl-C/Ctrl-D),
+ incremental search and auto suggestions.
+
+ (Not for full screen applications.)
+ """
+ kw.setdefault('enable_abort_and_exit_bindings', True)
+ kw.setdefault('enable_search', True)
+ kw.setdefault('enable_auto_suggest_bindings', True)
+
+ return cls(**kw)
+
+ def reset(self, cli):
+ # For backwards compatibility.
+ pass
+
+ def get_vi_state(self, cli):
+ # Deprecated!
+ return cli.vi_state
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/registry.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/registry.py
new file mode 100644
index 0000000000..24d0e729a1
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/registry.py
@@ -0,0 +1,350 @@
+"""
+Key bindings registry.
+
+A `Registry` object is a container that holds a list of key bindings. It has a
+very efficient internal data structure for checking which key bindings apply
+for a pressed key.
+
+Typical usage::
+
+ r = Registry()
+
+ @r.add_binding(Keys.ControlX, Keys.ControlC, filter=INSERT)
+ def handler(event):
+ # Handle ControlX-ControlC key sequence.
+ pass
+
+
+It is also possible to combine multiple registries. We do this in the default
+key bindings. There are some registries that contain Emacs bindings, while
+others contain the Vi bindings. They are merged together using a
+`MergedRegistry`.
+
+We also have a `ConditionalRegistry` object that can enable/disable a group of
+key bindings at once.
+"""
+from __future__ import unicode_literals
+from abc import ABCMeta, abstractmethod
+
+from prompt_toolkit.cache import SimpleCache
+from prompt_toolkit.filters import CLIFilter, to_cli_filter, Never
+from prompt_toolkit.keys import Key, Keys
+
+from six import text_type, with_metaclass
+
+__all__ = (
+ 'BaseRegistry',
+ 'Registry',
+ 'ConditionalRegistry',
+ 'MergedRegistry',
+)
+
+
+class _Binding(object):
+ """
+ (Immutable binding class.)
+ """
+ def __init__(self, keys, handler, filter=None, eager=None, save_before=None):
+ assert isinstance(keys, tuple)
+ assert callable(handler)
+ assert isinstance(filter, CLIFilter)
+ assert isinstance(eager, CLIFilter)
+ assert callable(save_before)
+
+ self.keys = keys
+ self.handler = handler
+ self.filter = filter
+ self.eager = eager
+ self.save_before = save_before
+
+ def call(self, event):
+ return self.handler(event)
+
+ def __repr__(self):
+ return '%s(keys=%r, handler=%r)' % (
+ self.__class__.__name__, self.keys, self.handler)
+
+
+class BaseRegistry(with_metaclass(ABCMeta, object)):
+ """
+ Interface for a Registry.
+ """
+ _version = 0 # For cache invalidation.
+
+ @abstractmethod
+ def get_bindings_for_keys(self, keys):
+ pass
+
+ @abstractmethod
+ def get_bindings_starting_with_keys(self, keys):
+ pass
+
+ # `add_binding` and `remove_binding` don't have to be part of this
+ # interface.
+
+
+class Registry(BaseRegistry):
+ """
+ Key binding registry.
+ """
+ def __init__(self):
+ self.key_bindings = []
+ self._get_bindings_for_keys_cache = SimpleCache(maxsize=10000)
+ self._get_bindings_starting_with_keys_cache = SimpleCache(maxsize=1000)
+ self._version = 0 # For cache invalidation.
+
+ def _clear_cache(self):
+ self._version += 1
+ self._get_bindings_for_keys_cache.clear()
+ self._get_bindings_starting_with_keys_cache.clear()
+
+ def add_binding(self, *keys, **kwargs):
+ """
+ Decorator for annotating key bindings.
+
+ :param filter: :class:`~prompt_toolkit.filters.CLIFilter` to determine
+ when this key binding is active.
+ :param eager: :class:`~prompt_toolkit.filters.CLIFilter` or `bool`.
+ When True, ignore potential longer matches when this key binding is
+ hit. E.g. when there is an active eager key binding for Ctrl-X,
+ execute the handler immediately and ignore the key binding for
+ Ctrl-X Ctrl-E of which it is a prefix.
+ :param save_before: Callable that takes an `Event` and returns True if
+ we should save the current buffer, before handling the event.
+ (That's the default.)
+ """
+ filter = to_cli_filter(kwargs.pop('filter', True))
+ eager = to_cli_filter(kwargs.pop('eager', False))
+ save_before = kwargs.pop('save_before', lambda e: True)
+ to_cli_filter(kwargs.pop('invalidate_ui', True)) # Deprecated! (ignored.)
+
+ assert not kwargs
+ assert keys
+ assert all(isinstance(k, (Key, text_type)) for k in keys), \
+ 'Key bindings should consist of Key and string (unicode) instances.'
+ assert callable(save_before)
+
+ if isinstance(filter, Never):
+ # When a filter is Never, it will always stay disabled, so in that case
+ # don't bother putting it in the registry. It will slow down every key
+ # press otherwise.
+ def decorator(func):
+ return func
+ else:
+ def decorator(func):
+ self.key_bindings.append(
+ _Binding(keys, func, filter=filter, eager=eager,
+ save_before=save_before))
+ self._clear_cache()
+
+ return func
+ return decorator
+
+ def remove_binding(self, function):
+ """
+ Remove a key binding.
+
+ This expects a function that was given to `add_binding` method as
+ parameter. Raises `ValueError` when the given function was not
+ registered before.
+ """
+ assert callable(function)
+
+ for b in self.key_bindings:
+ if b.handler == function:
+ self.key_bindings.remove(b)
+ self._clear_cache()
+ return
+
+ # No key binding found for this function. Raise ValueError.
+ raise ValueError('Binding not found: %r' % (function, ))
+
+ def get_bindings_for_keys(self, keys):
+ """
+ Return a list of key bindings that can handle this key.
+ (This return also inactive bindings, so the `filter` still has to be
+ called, for checking it.)
+
+ :param keys: tuple of keys.
+ """
+ def get():
+ result = []
+ for b in self.key_bindings:
+ if len(keys) == len(b.keys):
+ match = True
+ any_count = 0
+
+ for i, j in zip(b.keys, keys):
+ if i != j and i != Keys.Any:
+ match = False
+ break
+
+ if i == Keys.Any:
+ any_count += 1
+
+ if match:
+ result.append((any_count, b))
+
+ # Place bindings that have more 'Any' occurences in them at the end.
+ result = sorted(result, key=lambda item: -item[0])
+
+ return [item[1] for item in result]
+
+ return self._get_bindings_for_keys_cache.get(keys, get)
+
+ def get_bindings_starting_with_keys(self, keys):
+ """
+ Return a list of key bindings that handle a key sequence starting with
+ `keys`. (It does only return bindings for which the sequences are
+ longer than `keys`. And like `get_bindings_for_keys`, it also includes
+ inactive bindings.)
+
+ :param keys: tuple of keys.
+ """
+ def get():
+ result = []
+ for b in self.key_bindings:
+ if len(keys) < len(b.keys):
+ match = True
+ for i, j in zip(b.keys, keys):
+ if i != j and i != Keys.Any:
+ match = False
+ break
+ if match:
+ result.append(b)
+ return result
+
+ return self._get_bindings_starting_with_keys_cache.get(keys, get)
+
+
+class _AddRemoveMixin(BaseRegistry):
+ """
+ Common part for ConditionalRegistry and MergedRegistry.
+ """
+ def __init__(self):
+ # `Registry` to be synchronized with all the others.
+ self._registry2 = Registry()
+ self._last_version = None
+
+ # The 'extra' registry. Mostly for backwards compatibility.
+ self._extra_registry = Registry()
+
+ def _update_cache(self):
+ raise NotImplementedError
+
+ # For backwards, compatibility, we allow adding bindings to both
+ # ConditionalRegistry and MergedRegistry. This is however not the
+ # recommended way. Better is to create a new registry and merge them
+ # together using MergedRegistry.
+
+ def add_binding(self, *k, **kw):
+ return self._extra_registry.add_binding(*k, **kw)
+
+ def remove_binding(self, *k, **kw):
+ return self._extra_registry.remove_binding(*k, **kw)
+
+ # Proxy methods to self._registry2.
+
+ @property
+ def key_bindings(self):
+ self._update_cache()
+ return self._registry2.key_bindings
+
+ @property
+ def _version(self):
+ self._update_cache()
+ return self._last_version
+
+ def get_bindings_for_keys(self, *a, **kw):
+ self._update_cache()
+ return self._registry2.get_bindings_for_keys(*a, **kw)
+
+ def get_bindings_starting_with_keys(self, *a, **kw):
+ self._update_cache()
+ return self._registry2.get_bindings_starting_with_keys(*a, **kw)
+
+
+class ConditionalRegistry(_AddRemoveMixin):
+ """
+ Wraps around a `Registry`. Disable/enable all the key bindings according to
+ the given (additional) filter.::
+
+ @Condition
+ def setting_is_true(cli):
+ return True # or False
+
+ registy = ConditionalRegistry(registry, setting_is_true)
+
+ When new key bindings are added to this object. They are also
+ enable/disabled according to the given `filter`.
+
+ :param registries: List of `Registry` objects.
+ :param filter: `CLIFilter` object.
+ """
+ def __init__(self, registry=None, filter=True):
+ registry = registry or Registry()
+ assert isinstance(registry, BaseRegistry)
+
+ _AddRemoveMixin.__init__(self)
+
+ self.registry = registry
+ self.filter = to_cli_filter(filter)
+
+ def _update_cache(self):
+ " If the original registry was changed. Update our copy version. "
+ expected_version = (self.registry._version, self._extra_registry._version)
+
+ if self._last_version != expected_version:
+ registry2 = Registry()
+
+ # Copy all bindings from `self.registry`, adding our condition.
+ for reg in (self.registry, self._extra_registry):
+ for b in reg.key_bindings:
+ registry2.key_bindings.append(
+ _Binding(
+ keys=b.keys,
+ handler=b.handler,
+ filter=self.filter & b.filter,
+ eager=b.eager,
+ save_before=b.save_before))
+
+ self._registry2 = registry2
+ self._last_version = expected_version
+
+
+class MergedRegistry(_AddRemoveMixin):
+ """
+ Merge multiple registries of key bindings into one.
+
+ This class acts as a proxy to multiple `Registry` objects, but behaves as
+ if this is just one bigger `Registry`.
+
+ :param registries: List of `Registry` objects.
+ """
+ def __init__(self, registries):
+ assert all(isinstance(r, BaseRegistry) for r in registries)
+
+ _AddRemoveMixin.__init__(self)
+
+ self.registries = registries
+
+ def _update_cache(self):
+ """
+ If one of the original registries was changed. Update our merged
+ version.
+ """
+ expected_version = (
+ tuple(r._version for r in self.registries) +
+ (self._extra_registry._version, ))
+
+ if self._last_version != expected_version:
+ registry2 = Registry()
+
+ for reg in self.registries:
+ registry2.key_bindings.extend(reg.key_bindings)
+
+ # Copy all bindings from `self._extra_registry`.
+ registry2.key_bindings.extend(self._extra_registry.key_bindings)
+
+ self._registry2 = registry2
+ self._last_version = expected_version
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/vi_state.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/vi_state.py
new file mode 100644
index 0000000000..92ce3cbd29
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/key_binding/vi_state.py
@@ -0,0 +1,61 @@
+from __future__ import unicode_literals
+
+__all__ = (
+ 'InputMode',
+ 'CharacterFind',
+ 'ViState',
+)
+
+
+class InputMode(object):
+ INSERT = 'vi-insert'
+ INSERT_MULTIPLE = 'vi-insert-multiple'
+ NAVIGATION = 'vi-navigation'
+ REPLACE = 'vi-replace'
+
+
+class CharacterFind(object):
+ def __init__(self, character, backwards=False):
+ self.character = character
+ self.backwards = backwards
+
+
+class ViState(object):
+ """
+ Mutable class to hold the state of the Vi navigation.
+ """
+ def __init__(self):
+ #: None or CharacterFind instance. (This is used to repeat the last
+ #: search in Vi mode, by pressing the 'n' or 'N' in navigation mode.)
+ self.last_character_find = None
+
+ # When an operator is given and we are waiting for text object,
+ # -- e.g. in the case of 'dw', after the 'd' --, an operator callback
+ # is set here.
+ self.operator_func = None
+ self.operator_arg = None
+
+ #: Named registers. Maps register name (e.g. 'a') to
+ #: :class:`ClipboardData` instances.
+ self.named_registers = {}
+
+ #: The Vi mode we're currently in to.
+ self.input_mode = InputMode.INSERT
+
+ #: Waiting for digraph.
+ self.waiting_for_digraph = False
+ self.digraph_symbol1 = None # (None or a symbol.)
+
+ #: When true, make ~ act as an operator.
+ self.tilde_operator = False
+
+ def reset(self, mode=InputMode.INSERT):
+ """
+ Reset state, go back to the given mode. INSERT by default.
+ """
+ # Go back to insert mode.
+ self.input_mode = mode
+
+ self.waiting_for_digraph = False
+ self.operator_func = None
+ self.operator_arg = None
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/keys.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/keys.py
new file mode 100644
index 0000000000..d5df9bff41
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/keys.py
@@ -0,0 +1,129 @@
+from __future__ import unicode_literals
+
+__all__ = (
+ 'Key',
+ 'Keys',
+)
+
+
+class Key(object):
+ def __init__(self, name):
+
+ #: Descriptive way of writing keys in configuration files. e.g. <C-A>
+ #: for ``Control-A``.
+ self.name = name
+
+ def __repr__(self):
+ return '%s(%r)' % (self.__class__.__name__, self.name)
+
+
+class Keys(object):
+ Escape = Key('<Escape>')
+
+ ControlA = Key('<C-A>')
+ ControlB = Key('<C-B>')
+ ControlC = Key('<C-C>')
+ ControlD = Key('<C-D>')
+ ControlE = Key('<C-E>')
+ ControlF = Key('<C-F>')
+ ControlG = Key('<C-G>')
+ ControlH = Key('<C-H>')
+ ControlI = Key('<C-I>') # Tab
+ ControlJ = Key('<C-J>') # Enter
+ ControlK = Key('<C-K>')
+ ControlL = Key('<C-L>')
+ ControlM = Key('<C-M>') # Enter
+ ControlN = Key('<C-N>')
+ ControlO = Key('<C-O>')
+ ControlP = Key('<C-P>')
+ ControlQ = Key('<C-Q>')
+ ControlR = Key('<C-R>')
+ ControlS = Key('<C-S>')
+ ControlT = Key('<C-T>')
+ ControlU = Key('<C-U>')
+ ControlV = Key('<C-V>')
+ ControlW = Key('<C-W>')
+ ControlX = Key('<C-X>')
+ ControlY = Key('<C-Y>')
+ ControlZ = Key('<C-Z>')
+
+ ControlSpace = Key('<C-Space>')
+ ControlBackslash = Key('<C-Backslash>')
+ ControlSquareClose = Key('<C-SquareClose>')
+ ControlCircumflex = Key('<C-Circumflex>')
+ ControlUnderscore = Key('<C-Underscore>')
+ ControlLeft = Key('<C-Left>')
+ ControlRight = Key('<C-Right>')
+ ControlUp = Key('<C-Up>')
+ ControlDown = Key('<C-Down>')
+
+ Up = Key('<Up>')
+ Down = Key('<Down>')
+ Right = Key('<Right>')
+ Left = Key('<Left>')
+
+ ShiftLeft = Key('<ShiftLeft>')
+ ShiftUp = Key('<ShiftUp>')
+ ShiftDown = Key('<ShiftDown>')
+ ShiftRight = Key('<ShiftRight>')
+
+ Home = Key('<Home>')
+ End = Key('<End>')
+ Delete = Key('<Delete>')
+ ShiftDelete = Key('<ShiftDelete>')
+ ControlDelete = Key('<C-Delete>')
+ PageUp = Key('<PageUp>')
+ PageDown = Key('<PageDown>')
+ BackTab = Key('<BackTab>') # shift + tab
+ Insert = Key('<Insert>')
+ Backspace = Key('<Backspace>')
+
+ # Aliases.
+ Tab = ControlI
+ Enter = ControlJ
+ # XXX: Actually Enter equals ControlM, not ControlJ,
+ # However, in prompt_toolkit, we made the mistake of translating
+ # \r into \n during the input, so everyone is now handling the
+ # enter key by binding ControlJ.
+
+ # From now on, it's better to bind `Keys.Enter` everywhere,
+ # because that's future compatible, and will still work when we
+ # stop replacing \r by \n.
+
+ F1 = Key('<F1>')
+ F2 = Key('<F2>')
+ F3 = Key('<F3>')
+ F4 = Key('<F4>')
+ F5 = Key('<F5>')
+ F6 = Key('<F6>')
+ F7 = Key('<F7>')
+ F8 = Key('<F8>')
+ F9 = Key('<F9>')
+ F10 = Key('<F10>')
+ F11 = Key('<F11>')
+ F12 = Key('<F12>')
+ F13 = Key('<F13>')
+ F14 = Key('<F14>')
+ F15 = Key('<F15>')
+ F16 = Key('<F16>')
+ F17 = Key('<F17>')
+ F18 = Key('<F18>')
+ F19 = Key('<F19>')
+ F20 = Key('<F20>')
+ F21 = Key('<F21>')
+ F22 = Key('<F22>')
+ F23 = Key('<F23>')
+ F24 = Key('<F24>')
+
+ # Matches any key.
+ Any = Key('<Any>')
+
+ # Special
+ CPRResponse = Key('<Cursor-Position-Response>')
+ Vt100MouseEvent = Key('<Vt100-Mouse-Event>')
+ WindowsMouseEvent = Key('<Windows-Mouse-Event>')
+ BracketedPaste = Key('<Bracketed-Paste>')
+
+ # Key which is ignored. (The key binding for this key should not do
+ # anything.)
+ Ignore = Key('<Ignore>')
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/__init__.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/__init__.py
new file mode 100644
index 0000000000..0dec5ecfaf
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/__init__.py
@@ -0,0 +1,51 @@
+"""
+Command line layout definitions
+-------------------------------
+
+The layout of a command line interface is defined by a Container instance.
+There are two main groups of classes here. Containers and controls:
+
+- A container can contain other containers or controls, it can have multiple
+ children and it decides about the dimensions.
+- A control is responsible for rendering the actual content to a screen.
+ A control can propose some dimensions, but it's the container who decides
+ about the dimensions -- or when the control consumes more space -- which part
+ of the control will be visible.
+
+
+Container classes::
+
+ - Container (Abstract base class)
+ |- HSplit (Horizontal split)
+ |- VSplit (Vertical split)
+ |- FloatContainer (Container which can also contain menus and other floats)
+ `- Window (Container which contains one actual control
+
+Control classes::
+
+ - UIControl (Abstract base class)
+ |- TokenListControl (Renders a simple list of tokens)
+ |- FillControl (Fills control with one token/character.)
+ `- BufferControl (Renders an input buffer.)
+
+
+Usually, you end up wrapping every control inside a `Window` object, because
+that's the only way to render it in a layout.
+
+There are some prepared toolbars which are ready to use::
+
+- SystemToolbar (Shows the 'system' input buffer, for entering system commands.)
+- ArgToolbar (Shows the input 'arg', for repetition of input commands.)
+- SearchToolbar (Shows the 'search' input buffer, for incremental search.)
+- CompletionsToolbar (Shows the completions of the current buffer.)
+- ValidationToolbar (Shows validation errors of the current buffer.)
+
+And one prepared menu:
+
+- CompletionsMenu
+
+"""
+from __future__ import unicode_literals
+
+from .containers import Float, FloatContainer, HSplit, VSplit, Window, ConditionalContainer
+from .controls import TokenListControl, FillControl, BufferControl
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/containers.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/containers.py
new file mode 100644
index 0000000000..0bdafe18e0
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/containers.py
@@ -0,0 +1,1665 @@
+"""
+Container for the layout.
+(Containers can contain other containers or user interface controls.)
+"""
+from __future__ import unicode_literals
+
+from abc import ABCMeta, abstractmethod
+from six import with_metaclass
+from six.moves import range
+
+from .controls import UIControl, TokenListControl, UIContent
+from .dimension import LayoutDimension, sum_layout_dimensions, max_layout_dimensions
+from .margins import Margin
+from .screen import Point, WritePosition, _CHAR_CACHE
+from .utils import token_list_to_text, explode_tokens
+from prompt_toolkit.cache import SimpleCache
+from prompt_toolkit.filters import to_cli_filter, ViInsertMode, EmacsInsertMode
+from prompt_toolkit.mouse_events import MouseEvent, MouseEventType
+from prompt_toolkit.reactive import Integer
+from prompt_toolkit.token import Token
+from prompt_toolkit.utils import take_using_weights, get_cwidth
+
+__all__ = (
+ 'Container',
+ 'HSplit',
+ 'VSplit',
+ 'FloatContainer',
+ 'Float',
+ 'Window',
+ 'WindowRenderInfo',
+ 'ConditionalContainer',
+ 'ScrollOffsets',
+ 'ColorColumn',
+)
+
+Transparent = Token.Transparent
+
+
+class Container(with_metaclass(ABCMeta, object)):
+ """
+ Base class for user interface layout.
+ """
+ @abstractmethod
+ def reset(self):
+ """
+ Reset the state of this container and all the children.
+ (E.g. reset scroll offsets, etc...)
+ """
+
+ @abstractmethod
+ def preferred_width(self, cli, max_available_width):
+ """
+ Return a :class:`~prompt_toolkit.layout.dimension.LayoutDimension` that
+ represents the desired width for this container.
+
+ :param cli: :class:`~prompt_toolkit.interface.CommandLineInterface`.
+ """
+
+ @abstractmethod
+ def preferred_height(self, cli, width, max_available_height):
+ """
+ Return a :class:`~prompt_toolkit.layout.dimension.LayoutDimension` that
+ represents the desired height for this container.
+
+ :param cli: :class:`~prompt_toolkit.interface.CommandLineInterface`.
+ """
+
+ @abstractmethod
+ def write_to_screen(self, cli, screen, mouse_handlers, write_position):
+ """
+ Write the actual content to the screen.
+
+ :param cli: :class:`~prompt_toolkit.interface.CommandLineInterface`.
+ :param screen: :class:`~prompt_toolkit.layout.screen.Screen`
+ :param mouse_handlers: :class:`~prompt_toolkit.layout.mouse_handlers.MouseHandlers`.
+ """
+
+ @abstractmethod
+ def walk(self, cli):
+ """
+ Walk through all the layout nodes (and their children) and yield them.
+ """
+
+
+def _window_too_small():
+ " Create a `Window` that displays the 'Window too small' text. "
+ return Window(TokenListControl.static(
+ [(Token.WindowTooSmall, ' Window too small... ')]))
+
+
+class HSplit(Container):
+ """
+ Several layouts, one stacked above/under the other.
+
+ :param children: List of child :class:`.Container` objects.
+ :param window_too_small: A :class:`.Container` object that is displayed if
+ there is not enough space for all the children. By default, this is a
+ "Window too small" message.
+ :param get_dimensions: (`None` or a callable that takes a
+ `CommandLineInterface` and returns a list of `LayoutDimension`
+ instances.) By default the dimensions are taken from the children and
+ divided by the available space. However, when `get_dimensions` is specified,
+ this is taken instead.
+ :param report_dimensions_callback: When rendering, this function is called
+ with the `CommandLineInterface` and the list of used dimensions. (As a
+ list of integers.)
+ """
+ def __init__(self, children, window_too_small=None,
+ get_dimensions=None, report_dimensions_callback=None):
+ assert all(isinstance(c, Container) for c in children)
+ assert window_too_small is None or isinstance(window_too_small, Container)
+ assert get_dimensions is None or callable(get_dimensions)
+ assert report_dimensions_callback is None or callable(report_dimensions_callback)
+
+ self.children = children
+ self.window_too_small = window_too_small or _window_too_small()
+ self.get_dimensions = get_dimensions
+ self.report_dimensions_callback = report_dimensions_callback
+
+ def preferred_width(self, cli, max_available_width):
+ if self.children:
+ dimensions = [c.preferred_width(cli, max_available_width) for c in self.children]
+ return max_layout_dimensions(dimensions)
+ else:
+ return LayoutDimension(0)
+
+ def preferred_height(self, cli, width, max_available_height):
+ dimensions = [c.preferred_height(cli, width, max_available_height) for c in self.children]
+ return sum_layout_dimensions(dimensions)
+
+ def reset(self):
+ for c in self.children:
+ c.reset()
+
+ def write_to_screen(self, cli, screen, mouse_handlers, write_position):
+ """
+ Render the prompt to a `Screen` instance.
+
+ :param screen: The :class:`~prompt_toolkit.layout.screen.Screen` class
+ to which the output has to be written.
+ """
+ sizes = self._divide_heigths(cli, write_position)
+
+ if self.report_dimensions_callback:
+ self.report_dimensions_callback(cli, sizes)
+
+ if sizes is None:
+ self.window_too_small.write_to_screen(
+ cli, screen, mouse_handlers, write_position)
+ else:
+ # Draw child panes.
+ ypos = write_position.ypos
+ xpos = write_position.xpos
+ width = write_position.width
+
+ for s, c in zip(sizes, self.children):
+ c.write_to_screen(cli, screen, mouse_handlers, WritePosition(xpos, ypos, width, s))
+ ypos += s
+
+ def _divide_heigths(self, cli, write_position):
+ """
+ Return the heights for all rows.
+ Or None when there is not enough space.
+ """
+ if not self.children:
+ return []
+
+ # Calculate heights.
+ given_dimensions = self.get_dimensions(cli) if self.get_dimensions else None
+
+ def get_dimension_for_child(c, index):
+ if given_dimensions and given_dimensions[index] is not None:
+ return given_dimensions[index]
+ else:
+ return c.preferred_height(cli, write_position.width, write_position.extended_height)
+
+ dimensions = [get_dimension_for_child(c, index) for index, c in enumerate(self.children)]
+
+ # Sum dimensions
+ sum_dimensions = sum_layout_dimensions(dimensions)
+
+ # If there is not enough space for both.
+ # Don't do anything.
+ if sum_dimensions.min > write_position.extended_height:
+ return
+
+ # Find optimal sizes. (Start with minimal size, increase until we cover
+ # the whole height.)
+ sizes = [d.min for d in dimensions]
+
+ child_generator = take_using_weights(
+ items=list(range(len(dimensions))),
+ weights=[d.weight for d in dimensions])
+
+ i = next(child_generator)
+
+ while sum(sizes) < min(write_position.extended_height, sum_dimensions.preferred):
+ # Increase until we meet at least the 'preferred' size.
+ if sizes[i] < dimensions[i].preferred:
+ sizes[i] += 1
+ i = next(child_generator)
+
+ if not any([cli.is_returning, cli.is_exiting, cli.is_aborting]):
+ while sum(sizes) < min(write_position.height, sum_dimensions.max):
+ # Increase until we use all the available space. (or until "max")
+ if sizes[i] < dimensions[i].max:
+ sizes[i] += 1
+ i = next(child_generator)
+
+ return sizes
+
+ def walk(self, cli):
+ """ Walk through children. """
+ yield self
+ for c in self.children:
+ for i in c.walk(cli):
+ yield i
+
+
+class VSplit(Container):
+ """
+ Several layouts, one stacked left/right of the other.
+
+ :param children: List of child :class:`.Container` objects.
+ :param window_too_small: A :class:`.Container` object that is displayed if
+ there is not enough space for all the children. By default, this is a
+ "Window too small" message.
+ :param get_dimensions: (`None` or a callable that takes a
+ `CommandLineInterface` and returns a list of `LayoutDimension`
+ instances.) By default the dimensions are taken from the children and
+ divided by the available space. However, when `get_dimensions` is specified,
+ this is taken instead.
+ :param report_dimensions_callback: When rendering, this function is called
+ with the `CommandLineInterface` and the list of used dimensions. (As a
+ list of integers.)
+ """
+ def __init__(self, children, window_too_small=None,
+ get_dimensions=None, report_dimensions_callback=None):
+ assert all(isinstance(c, Container) for c in children)
+ assert window_too_small is None or isinstance(window_too_small, Container)
+ assert get_dimensions is None or callable(get_dimensions)
+ assert report_dimensions_callback is None or callable(report_dimensions_callback)
+
+ self.children = children
+ self.window_too_small = window_too_small or _window_too_small()
+ self.get_dimensions = get_dimensions
+ self.report_dimensions_callback = report_dimensions_callback
+
+ def preferred_width(self, cli, max_available_width):
+ dimensions = [c.preferred_width(cli, max_available_width) for c in self.children]
+ return sum_layout_dimensions(dimensions)
+
+ def preferred_height(self, cli, width, max_available_height):
+ sizes = self._divide_widths(cli, width)
+ if sizes is None:
+ return LayoutDimension()
+ else:
+ dimensions = [c.preferred_height(cli, s, max_available_height)
+ for s, c in zip(sizes, self.children)]
+ return max_layout_dimensions(dimensions)
+
+ def reset(self):
+ for c in self.children:
+ c.reset()
+
+ def _divide_widths(self, cli, width):
+ """
+ Return the widths for all columns.
+ Or None when there is not enough space.
+ """
+ if not self.children:
+ return []
+
+ # Calculate widths.
+ given_dimensions = self.get_dimensions(cli) if self.get_dimensions else None
+
+ def get_dimension_for_child(c, index):
+ if given_dimensions and given_dimensions[index] is not None:
+ return given_dimensions[index]
+ else:
+ return c.preferred_width(cli, width)
+
+ dimensions = [get_dimension_for_child(c, index) for index, c in enumerate(self.children)]
+
+ # Sum dimensions
+ sum_dimensions = sum_layout_dimensions(dimensions)
+
+ # If there is not enough space for both.
+ # Don't do anything.
+ if sum_dimensions.min > width:
+ return
+
+ # Find optimal sizes. (Start with minimal size, increase until we cover
+ # the whole height.)
+ sizes = [d.min for d in dimensions]
+
+ child_generator = take_using_weights(
+ items=list(range(len(dimensions))),
+ weights=[d.weight for d in dimensions])
+
+ i = next(child_generator)
+
+ while sum(sizes) < min(width, sum_dimensions.preferred):
+ # Increase until we meet at least the 'preferred' size.
+ if sizes[i] < dimensions[i].preferred:
+ sizes[i] += 1
+ i = next(child_generator)
+
+ while sum(sizes) < min(width, sum_dimensions.max):
+ # Increase until we use all the available space.
+ if sizes[i] < dimensions[i].max:
+ sizes[i] += 1
+ i = next(child_generator)
+
+ return sizes
+
+ def write_to_screen(self, cli, screen, mouse_handlers, write_position):
+ """
+ Render the prompt to a `Screen` instance.
+
+ :param screen: The :class:`~prompt_toolkit.layout.screen.Screen` class
+ to which the output has to be written.
+ """
+ if not self.children:
+ return
+
+ sizes = self._divide_widths(cli, write_position.width)
+
+ if self.report_dimensions_callback:
+ self.report_dimensions_callback(cli, sizes)
+
+ # If there is not enough space.
+ if sizes is None:
+ self.window_too_small.write_to_screen(
+ cli, screen, mouse_handlers, write_position)
+ return
+
+ # Calculate heights, take the largest possible, but not larger than write_position.extended_height.
+ heights = [child.preferred_height(cli, width, write_position.extended_height).preferred
+ for width, child in zip(sizes, self.children)]
+ height = max(write_position.height, min(write_position.extended_height, max(heights)))
+
+ # Draw child panes.
+ ypos = write_position.ypos
+ xpos = write_position.xpos
+
+ for s, c in zip(sizes, self.children):
+ c.write_to_screen(cli, screen, mouse_handlers, WritePosition(xpos, ypos, s, height))
+ xpos += s
+
+ def walk(self, cli):
+ """ Walk through children. """
+ yield self
+ for c in self.children:
+ for i in c.walk(cli):
+ yield i
+
+
+class FloatContainer(Container):
+ """
+ Container which can contain another container for the background, as well
+ as a list of floating containers on top of it.
+
+ Example Usage::
+
+ FloatContainer(content=Window(...),
+ floats=[
+ Float(xcursor=True,
+ ycursor=True,
+ layout=CompletionMenu(...))
+ ])
+ """
+ def __init__(self, content, floats):
+ assert isinstance(content, Container)
+ assert all(isinstance(f, Float) for f in floats)
+
+ self.content = content
+ self.floats = floats
+
+ def reset(self):
+ self.content.reset()
+
+ for f in self.floats:
+ f.content.reset()
+
+ def preferred_width(self, cli, write_position):
+ return self.content.preferred_width(cli, write_position)
+
+ def preferred_height(self, cli, width, max_available_height):
+ """
+ Return the preferred height of the float container.
+ (We don't care about the height of the floats, they should always fit
+ into the dimensions provided by the container.)
+ """
+ return self.content.preferred_height(cli, width, max_available_height)
+
+ def write_to_screen(self, cli, screen, mouse_handlers, write_position):
+ self.content.write_to_screen(cli, screen, mouse_handlers, write_position)
+
+ for fl in self.floats:
+ # When a menu_position was given, use this instead of the cursor
+ # position. (These cursor positions are absolute, translate again
+ # relative to the write_position.)
+ # Note: This should be inside the for-loop, because one float could
+ # set the cursor position to be used for the next one.
+ cursor_position = screen.menu_position or screen.cursor_position
+ cursor_position = Point(x=cursor_position.x - write_position.xpos,
+ y=cursor_position.y - write_position.ypos)
+
+ fl_width = fl.get_width(cli)
+ fl_height = fl.get_height(cli)
+
+ # Left & width given.
+ if fl.left is not None and fl_width is not None:
+ xpos = fl.left
+ width = fl_width
+ # Left & right given -> calculate width.
+ elif fl.left is not None and fl.right is not None:
+ xpos = fl.left
+ width = write_position.width - fl.left - fl.right
+ # Width & right given -> calculate left.
+ elif fl_width is not None and fl.right is not None:
+ xpos = write_position.width - fl.right - fl_width
+ width = fl_width
+ elif fl.xcursor:
+ width = fl_width
+ if width is None:
+ width = fl.content.preferred_width(cli, write_position.width).preferred
+ width = min(write_position.width, width)
+
+ xpos = cursor_position.x
+ if xpos + width > write_position.width:
+ xpos = max(0, write_position.width - width)
+ # Only width given -> center horizontally.
+ elif fl_width:
+ xpos = int((write_position.width - fl_width) / 2)
+ width = fl_width
+ # Otherwise, take preferred width from float content.
+ else:
+ width = fl.content.preferred_width(cli, write_position.width).preferred
+
+ if fl.left is not None:
+ xpos = fl.left
+ elif fl.right is not None:
+ xpos = max(0, write_position.width - width - fl.right)
+ else: # Center horizontally.
+ xpos = max(0, int((write_position.width - width) / 2))
+
+ # Trim.
+ width = min(width, write_position.width - xpos)
+
+ # Top & height given.
+ if fl.top is not None and fl_height is not None:
+ ypos = fl.top
+ height = fl_height
+ # Top & bottom given -> calculate height.
+ elif fl.top is not None and fl.bottom is not None:
+ ypos = fl.top
+ height = write_position.height - fl.top - fl.bottom
+ # Height & bottom given -> calculate top.
+ elif fl_height is not None and fl.bottom is not None:
+ ypos = write_position.height - fl_height - fl.bottom
+ height = fl_height
+ # Near cursor
+ elif fl.ycursor:
+ ypos = cursor_position.y + 1
+
+ height = fl_height
+ if height is None:
+ height = fl.content.preferred_height(
+ cli, width, write_position.extended_height).preferred
+
+ # Reduce height if not enough space. (We can use the
+ # extended_height when the content requires it.)
+ if height > write_position.extended_height - ypos:
+ if write_position.extended_height - ypos + 1 >= ypos:
+ # When the space below the cursor is more than
+ # the space above, just reduce the height.
+ height = write_position.extended_height - ypos
+ else:
+ # Otherwise, fit the float above the cursor.
+ height = min(height, cursor_position.y)
+ ypos = cursor_position.y - height
+
+ # Only height given -> center vertically.
+ elif fl_width:
+ ypos = int((write_position.height - fl_height) / 2)
+ height = fl_height
+ # Otherwise, take preferred height from content.
+ else:
+ height = fl.content.preferred_height(
+ cli, width, write_position.extended_height).preferred
+
+ if fl.top is not None:
+ ypos = fl.top
+ elif fl.bottom is not None:
+ ypos = max(0, write_position.height - height - fl.bottom)
+ else: # Center vertically.
+ ypos = max(0, int((write_position.height - height) / 2))
+
+ # Trim.
+ height = min(height, write_position.height - ypos)
+
+ # Write float.
+ # (xpos and ypos can be negative: a float can be partially visible.)
+ if height > 0 and width > 0:
+ wp = WritePosition(xpos=xpos + write_position.xpos,
+ ypos=ypos + write_position.ypos,
+ width=width, height=height)
+
+ if not fl.hide_when_covering_content or self._area_is_empty(screen, wp):
+ fl.content.write_to_screen(cli, screen, mouse_handlers, wp)
+
+ def _area_is_empty(self, screen, write_position):
+ """
+ Return True when the area below the write position is still empty.
+ (For floats that should not hide content underneath.)
+ """
+ wp = write_position
+ Transparent = Token.Transparent
+
+ for y in range(wp.ypos, wp.ypos + wp.height):
+ if y in screen.data_buffer:
+ row = screen.data_buffer[y]
+
+ for x in range(wp.xpos, wp.xpos + wp.width):
+ c = row[x]
+ if c.char != ' ' or c.token != Transparent:
+ return False
+
+ return True
+
+ def walk(self, cli):
+ """ Walk through children. """
+ yield self
+
+ for i in self.content.walk(cli):
+ yield i
+
+ for f in self.floats:
+ for i in f.content.walk(cli):
+ yield i
+
+
+class Float(object):
+ """
+ Float for use in a :class:`.FloatContainer`.
+
+ :param content: :class:`.Container` instance.
+ :param hide_when_covering_content: Hide the float when it covers content underneath.
+ """
+ def __init__(self, top=None, right=None, bottom=None, left=None,
+ width=None, height=None, get_width=None, get_height=None,
+ xcursor=False, ycursor=False, content=None,
+ hide_when_covering_content=False):
+ assert isinstance(content, Container)
+ assert width is None or get_width is None
+ assert height is None or get_height is None
+
+ self.left = left
+ self.right = right
+ self.top = top
+ self.bottom = bottom
+
+ self._width = width
+ self._height = height
+
+ self._get_width = get_width
+ self._get_height = get_height
+
+ self.xcursor = xcursor
+ self.ycursor = ycursor
+
+ self.content = content
+ self.hide_when_covering_content = hide_when_covering_content
+
+ def get_width(self, cli):
+ if self._width:
+ return self._width
+ if self._get_width:
+ return self._get_width(cli)
+
+ def get_height(self, cli):
+ if self._height:
+ return self._height
+ if self._get_height:
+ return self._get_height(cli)
+
+ def __repr__(self):
+ return 'Float(content=%r)' % self.content
+
+
+class WindowRenderInfo(object):
+ """
+ Render information, for the last render time of this control.
+ It stores mapping information between the input buffers (in case of a
+ :class:`~prompt_toolkit.layout.controls.BufferControl`) and the actual
+ render position on the output screen.
+
+ (Could be used for implementation of the Vi 'H' and 'L' key bindings as
+ well as implementing mouse support.)
+
+ :param ui_content: The original :class:`.UIContent` instance that contains
+ the whole input, without clipping. (ui_content)
+ :param horizontal_scroll: The horizontal scroll of the :class:`.Window` instance.
+ :param vertical_scroll: The vertical scroll of the :class:`.Window` instance.
+ :param window_width: The width of the window that displays the content,
+ without the margins.
+ :param window_height: The height of the window that displays the content.
+ :param configured_scroll_offsets: The scroll offsets as configured for the
+ :class:`Window` instance.
+ :param visible_line_to_row_col: Mapping that maps the row numbers on the
+ displayed screen (starting from zero for the first visible line) to
+ (row, col) tuples pointing to the row and column of the :class:`.UIContent`.
+ :param rowcol_to_yx: Mapping that maps (row, column) tuples representing
+ coordinates of the :class:`UIContent` to (y, x) absolute coordinates at
+ the rendered screen.
+ """
+ def __init__(self, ui_content, horizontal_scroll, vertical_scroll,
+ window_width, window_height,
+ configured_scroll_offsets,
+ visible_line_to_row_col, rowcol_to_yx,
+ x_offset, y_offset, wrap_lines):
+ assert isinstance(ui_content, UIContent)
+ assert isinstance(horizontal_scroll, int)
+ assert isinstance(vertical_scroll, int)
+ assert isinstance(window_width, int)
+ assert isinstance(window_height, int)
+ assert isinstance(configured_scroll_offsets, ScrollOffsets)
+ assert isinstance(visible_line_to_row_col, dict)
+ assert isinstance(rowcol_to_yx, dict)
+ assert isinstance(x_offset, int)
+ assert isinstance(y_offset, int)
+ assert isinstance(wrap_lines, bool)
+
+ self.ui_content = ui_content
+ self.vertical_scroll = vertical_scroll
+ self.window_width = window_width # Width without margins.
+ self.window_height = window_height
+
+ self.configured_scroll_offsets = configured_scroll_offsets
+ self.visible_line_to_row_col = visible_line_to_row_col
+ self.wrap_lines = wrap_lines
+
+ self._rowcol_to_yx = rowcol_to_yx # row/col from input to absolute y/x
+ # screen coordinates.
+ self._x_offset = x_offset
+ self._y_offset = y_offset
+
+ @property
+ def visible_line_to_input_line(self):
+ return dict(
+ (visible_line, rowcol[0])
+ for visible_line, rowcol in self.visible_line_to_row_col.items())
+
+ @property
+ def cursor_position(self):
+ """
+ Return the cursor position coordinates, relative to the left/top corner
+ of the rendered screen.
+ """
+ cpos = self.ui_content.cursor_position
+ y, x = self._rowcol_to_yx[cpos.y, cpos.x]
+ return Point(x=x - self._x_offset, y=y - self._y_offset)
+
+ @property
+ def applied_scroll_offsets(self):
+ """
+ Return a :class:`.ScrollOffsets` instance that indicates the actual
+ offset. This can be less than or equal to what's configured. E.g, when
+ the cursor is completely at the top, the top offset will be zero rather
+ than what's configured.
+ """
+ if self.displayed_lines[0] == 0:
+ top = 0
+ else:
+ # Get row where the cursor is displayed.
+ y = self.input_line_to_visible_line[self.ui_content.cursor_position.y]
+ top = min(y, self.configured_scroll_offsets.top)
+
+ return ScrollOffsets(
+ top=top,
+ bottom=min(self.ui_content.line_count - self.displayed_lines[-1] - 1,
+ self.configured_scroll_offsets.bottom),
+
+ # For left/right, it probably doesn't make sense to return something.
+ # (We would have to calculate the widths of all the lines and keep
+ # double width characters in mind.)
+ left=0, right=0)
+
+ @property
+ def displayed_lines(self):
+ """
+ List of all the visible rows. (Line numbers of the input buffer.)
+ The last line may not be entirely visible.
+ """
+ return sorted(row for row, col in self.visible_line_to_row_col.values())
+
+ @property
+ def input_line_to_visible_line(self):
+ """
+ Return the dictionary mapping the line numbers of the input buffer to
+ the lines of the screen. When a line spans several rows at the screen,
+ the first row appears in the dictionary.
+ """
+ result = {}
+ for k, v in self.visible_line_to_input_line.items():
+ if v in result:
+ result[v] = min(result[v], k)
+ else:
+ result[v] = k
+ return result
+
+ def first_visible_line(self, after_scroll_offset=False):
+ """
+ Return the line number (0 based) of the input document that corresponds
+ with the first visible line.
+ """
+ if after_scroll_offset:
+ return self.displayed_lines[self.applied_scroll_offsets.top]
+ else:
+ return self.displayed_lines[0]
+
+ def last_visible_line(self, before_scroll_offset=False):
+ """
+ Like `first_visible_line`, but for the last visible line.
+ """
+ if before_scroll_offset:
+ return self.displayed_lines[-1 - self.applied_scroll_offsets.bottom]
+ else:
+ return self.displayed_lines[-1]
+
+ def center_visible_line(self, before_scroll_offset=False,
+ after_scroll_offset=False):
+ """
+ Like `first_visible_line`, but for the center visible line.
+ """
+ return (self.first_visible_line(after_scroll_offset) +
+ (self.last_visible_line(before_scroll_offset) -
+ self.first_visible_line(after_scroll_offset)) // 2
+ )
+
+ @property
+ def content_height(self):
+ """
+ The full height of the user control.
+ """
+ return self.ui_content.line_count
+
+ @property
+ def full_height_visible(self):
+ """
+ True when the full height is visible (There is no vertical scroll.)
+ """
+ return self.vertical_scroll == 0 and self.last_visible_line() == self.content_height
+
+ @property
+ def top_visible(self):
+ """
+ True when the top of the buffer is visible.
+ """
+ return self.vertical_scroll == 0
+
+ @property
+ def bottom_visible(self):
+ """
+ True when the bottom of the buffer is visible.
+ """
+ return self.last_visible_line() == self.content_height - 1
+
+ @property
+ def vertical_scroll_percentage(self):
+ """
+ Vertical scroll as a percentage. (0 means: the top is visible,
+ 100 means: the bottom is visible.)
+ """
+ if self.bottom_visible:
+ return 100
+ else:
+ return (100 * self.vertical_scroll // self.content_height)
+
+ def get_height_for_line(self, lineno):
+ """
+ Return the height of the given line.
+ (The height that it would take, if this line became visible.)
+ """
+ if self.wrap_lines:
+ return self.ui_content.get_height_for_line(lineno, self.window_width)
+ else:
+ return 1
+
+
+class ScrollOffsets(object):
+ """
+ Scroll offsets for the :class:`.Window` class.
+
+ Note that left/right offsets only make sense if line wrapping is disabled.
+ """
+ def __init__(self, top=0, bottom=0, left=0, right=0):
+ assert isinstance(top, Integer)
+ assert isinstance(bottom, Integer)
+ assert isinstance(left, Integer)
+ assert isinstance(right, Integer)
+
+ self._top = top
+ self._bottom = bottom
+ self._left = left
+ self._right = right
+
+ @property
+ def top(self):
+ return int(self._top)
+
+ @property
+ def bottom(self):
+ return int(self._bottom)
+
+ @property
+ def left(self):
+ return int(self._left)
+
+ @property
+ def right(self):
+ return int(self._right)
+
+ def __repr__(self):
+ return 'ScrollOffsets(top=%r, bottom=%r, left=%r, right=%r)' % (
+ self.top, self.bottom, self.left, self.right)
+
+
+class ColorColumn(object):
+ def __init__(self, position, token=Token.ColorColumn):
+ self.position = position
+ self.token = token
+
+
+_in_insert_mode = ViInsertMode() | EmacsInsertMode()
+
+
+class Window(Container):
+ """
+ Container that holds a control.
+
+ :param content: :class:`~prompt_toolkit.layout.controls.UIControl` instance.
+ :param width: :class:`~prompt_toolkit.layout.dimension.LayoutDimension` instance.
+ :param height: :class:`~prompt_toolkit.layout.dimension.LayoutDimension` instance.
+ :param get_width: callable which takes a `CommandLineInterface` and returns a `LayoutDimension`.
+ :param get_height: callable which takes a `CommandLineInterface` and returns a `LayoutDimension`.
+ :param dont_extend_width: When `True`, don't take up more width then the
+ preferred width reported by the control.
+ :param dont_extend_height: When `True`, don't take up more width then the
+ preferred height reported by the control.
+ :param left_margins: A list of :class:`~prompt_toolkit.layout.margins.Margin`
+ instance to be displayed on the left. For instance:
+ :class:`~prompt_toolkit.layout.margins.NumberredMargin` can be one of
+ them in order to show line numbers.
+ :param right_margins: Like `left_margins`, but on the other side.
+ :param scroll_offsets: :class:`.ScrollOffsets` instance, representing the
+ preferred amount of lines/columns to be always visible before/after the
+ cursor. When both top and bottom are a very high number, the cursor
+ will be centered vertically most of the time.
+ :param allow_scroll_beyond_bottom: A `bool` or
+ :class:`~prompt_toolkit.filters.CLIFilter` instance. When True, allow
+ scrolling so far, that the top part of the content is not visible
+ anymore, while there is still empty space available at the bottom of
+ the window. In the Vi editor for instance, this is possible. You will
+ see tildes while the top part of the body is hidden.
+ :param wrap_lines: A `bool` or :class:`~prompt_toolkit.filters.CLIFilter`
+ instance. When True, don't scroll horizontally, but wrap lines instead.
+ :param get_vertical_scroll: Callable that takes this window
+ instance as input and returns a preferred vertical scroll.
+ (When this is `None`, the scroll is only determined by the last and
+ current cursor position.)
+ :param get_horizontal_scroll: Callable that takes this window
+ instance as input and returns a preferred vertical scroll.
+ :param always_hide_cursor: A `bool` or
+ :class:`~prompt_toolkit.filters.CLIFilter` instance. When True, never
+ display the cursor, even when the user control specifies a cursor
+ position.
+ :param cursorline: A `bool` or :class:`~prompt_toolkit.filters.CLIFilter`
+ instance. When True, display a cursorline.
+ :param cursorcolumn: A `bool` or :class:`~prompt_toolkit.filters.CLIFilter`
+ instance. When True, display a cursorcolumn.
+ :param get_colorcolumns: A callable that takes a `CommandLineInterface` and
+ returns a a list of :class:`.ColorColumn` instances that describe the
+ columns to be highlighted.
+ :param cursorline_token: The token to be used for highlighting the current line,
+ if `cursorline` is True.
+ :param cursorcolumn_token: The token to be used for highlighting the current line,
+ if `cursorcolumn` is True.
+ """
+ def __init__(self, content, width=None, height=None, get_width=None,
+ get_height=None, dont_extend_width=False, dont_extend_height=False,
+ left_margins=None, right_margins=None, scroll_offsets=None,
+ allow_scroll_beyond_bottom=False, wrap_lines=False,
+ get_vertical_scroll=None, get_horizontal_scroll=None, always_hide_cursor=False,
+ cursorline=False, cursorcolumn=False, get_colorcolumns=None,
+ cursorline_token=Token.CursorLine, cursorcolumn_token=Token.CursorColumn):
+ assert isinstance(content, UIControl)
+ assert width is None or isinstance(width, LayoutDimension)
+ assert height is None or isinstance(height, LayoutDimension)
+ assert get_width is None or callable(get_width)
+ assert get_height is None or callable(get_height)
+ assert width is None or get_width is None
+ assert height is None or get_height is None
+ assert scroll_offsets is None or isinstance(scroll_offsets, ScrollOffsets)
+ assert left_margins is None or all(isinstance(m, Margin) for m in left_margins)
+ assert right_margins is None or all(isinstance(m, Margin) for m in right_margins)
+ assert get_vertical_scroll is None or callable(get_vertical_scroll)
+ assert get_horizontal_scroll is None or callable(get_horizontal_scroll)
+ assert get_colorcolumns is None or callable(get_colorcolumns)
+
+ self.allow_scroll_beyond_bottom = to_cli_filter(allow_scroll_beyond_bottom)
+ self.always_hide_cursor = to_cli_filter(always_hide_cursor)
+ self.wrap_lines = to_cli_filter(wrap_lines)
+ self.cursorline = to_cli_filter(cursorline)
+ self.cursorcolumn = to_cli_filter(cursorcolumn)
+
+ self.content = content
+ self.dont_extend_width = dont_extend_width
+ self.dont_extend_height = dont_extend_height
+ self.left_margins = left_margins or []
+ self.right_margins = right_margins or []
+ self.scroll_offsets = scroll_offsets or ScrollOffsets()
+ self.get_vertical_scroll = get_vertical_scroll
+ self.get_horizontal_scroll = get_horizontal_scroll
+ self._width = get_width or (lambda cli: width)
+ self._height = get_height or (lambda cli: height)
+ self.get_colorcolumns = get_colorcolumns or (lambda cli: [])
+ self.cursorline_token = cursorline_token
+ self.cursorcolumn_token = cursorcolumn_token
+
+ # Cache for the screens generated by the margin.
+ self._ui_content_cache = SimpleCache(maxsize=8)
+ self._margin_width_cache = SimpleCache(maxsize=1)
+
+ self.reset()
+
+ def __repr__(self):
+ return 'Window(content=%r)' % self.content
+
+ def reset(self):
+ self.content.reset()
+
+ #: Scrolling position of the main content.
+ self.vertical_scroll = 0
+ self.horizontal_scroll = 0
+
+ # Vertical scroll 2: this is the vertical offset that a line is
+ # scrolled if a single line (the one that contains the cursor) consumes
+ # all of the vertical space.
+ self.vertical_scroll_2 = 0
+
+ #: Keep render information (mappings between buffer input and render
+ #: output.)
+ self.render_info = None
+
+ def _get_margin_width(self, cli, margin):
+ """
+ Return the width for this margin.
+ (Calculate only once per render time.)
+ """
+ # Margin.get_width, needs to have a UIContent instance.
+ def get_ui_content():
+ return self._get_ui_content(cli, width=0, height=0)
+
+ def get_width():
+ return margin.get_width(cli, get_ui_content)
+
+ key = (margin, cli.render_counter)
+ return self._margin_width_cache.get(key, get_width)
+
+ def preferred_width(self, cli, max_available_width):
+ # Calculate the width of the margin.
+ total_margin_width = sum(self._get_margin_width(cli, m) for m in
+ self.left_margins + self.right_margins)
+
+ # Window of the content. (Can be `None`.)
+ preferred_width = self.content.preferred_width(
+ cli, max_available_width - total_margin_width)
+
+ if preferred_width is not None:
+ # Include width of the margins.
+ preferred_width += total_margin_width
+
+ # Merge.
+ return self._merge_dimensions(
+ dimension=self._width(cli),
+ preferred=preferred_width,
+ dont_extend=self.dont_extend_width)
+
+ def preferred_height(self, cli, width, max_available_height):
+ total_margin_width = sum(self._get_margin_width(cli, m) for m in
+ self.left_margins + self.right_margins)
+ wrap_lines = self.wrap_lines(cli)
+
+ return self._merge_dimensions(
+ dimension=self._height(cli),
+ preferred=self.content.preferred_height(
+ cli, width - total_margin_width, max_available_height, wrap_lines),
+ dont_extend=self.dont_extend_height)
+
+ @staticmethod
+ def _merge_dimensions(dimension, preferred=None, dont_extend=False):
+ """
+ Take the LayoutDimension from this `Window` class and the received
+ preferred size from the `UIControl` and return a `LayoutDimension` to
+ report to the parent container.
+ """
+ dimension = dimension or LayoutDimension()
+
+ # When a preferred dimension was explicitly given to the Window,
+ # ignore the UIControl.
+ if dimension.preferred_specified:
+ preferred = dimension.preferred
+
+ # When a 'preferred' dimension is given by the UIControl, make sure
+ # that it stays within the bounds of the Window.
+ if preferred is not None:
+ if dimension.max:
+ preferred = min(preferred, dimension.max)
+
+ if dimension.min:
+ preferred = max(preferred, dimension.min)
+
+ # When a `dont_extend` flag has been given, use the preferred dimension
+ # also as the max dimension.
+ if dont_extend and preferred is not None:
+ max_ = min(dimension.max, preferred)
+ else:
+ max_ = dimension.max
+
+ return LayoutDimension(
+ min=dimension.min, max=max_,
+ preferred=preferred, weight=dimension.weight)
+
+ def _get_ui_content(self, cli, width, height):
+ """
+ Create a `UIContent` instance.
+ """
+ def get_content():
+ return self.content.create_content(cli, width=width, height=height)
+
+ key = (cli.render_counter, width, height)
+ return self._ui_content_cache.get(key, get_content)
+
+ def _get_digraph_char(self, cli):
+ " Return `False`, or the Digraph symbol to be used. "
+ if cli.quoted_insert:
+ return '^'
+ if cli.vi_state.waiting_for_digraph:
+ if cli.vi_state.digraph_symbol1:
+ return cli.vi_state.digraph_symbol1
+ return '?'
+ return False
+
+ def write_to_screen(self, cli, screen, mouse_handlers, write_position):
+ """
+ Write window to screen. This renders the user control, the margins and
+ copies everything over to the absolute position at the given screen.
+ """
+ # Calculate margin sizes.
+ left_margin_widths = [self._get_margin_width(cli, m) for m in self.left_margins]
+ right_margin_widths = [self._get_margin_width(cli, m) for m in self.right_margins]
+ total_margin_width = sum(left_margin_widths + right_margin_widths)
+
+ # Render UserControl.
+ ui_content = self.content.create_content(
+ cli, write_position.width - total_margin_width, write_position.height)
+ assert isinstance(ui_content, UIContent)
+
+ # Scroll content.
+ wrap_lines = self.wrap_lines(cli)
+ scroll_func = self._scroll_when_linewrapping if wrap_lines else self._scroll_without_linewrapping
+
+ scroll_func(
+ ui_content, write_position.width - total_margin_width, write_position.height, cli)
+
+ # Write body
+ visible_line_to_row_col, rowcol_to_yx = self._copy_body(
+ cli, ui_content, screen, write_position,
+ sum(left_margin_widths), write_position.width - total_margin_width,
+ self.vertical_scroll, self.horizontal_scroll,
+ has_focus=self.content.has_focus(cli),
+ wrap_lines=wrap_lines, highlight_lines=True,
+ vertical_scroll_2=self.vertical_scroll_2,
+ always_hide_cursor=self.always_hide_cursor(cli))
+
+ # Remember render info. (Set before generating the margins. They need this.)
+ x_offset=write_position.xpos + sum(left_margin_widths)
+ y_offset=write_position.ypos
+
+ self.render_info = WindowRenderInfo(
+ ui_content=ui_content,
+ horizontal_scroll=self.horizontal_scroll,
+ vertical_scroll=self.vertical_scroll,
+ window_width=write_position.width - total_margin_width,
+ window_height=write_position.height,
+ configured_scroll_offsets=self.scroll_offsets,
+ visible_line_to_row_col=visible_line_to_row_col,
+ rowcol_to_yx=rowcol_to_yx,
+ x_offset=x_offset,
+ y_offset=y_offset,
+ wrap_lines=wrap_lines)
+
+ # Set mouse handlers.
+ def mouse_handler(cli, mouse_event):
+ """ Wrapper around the mouse_handler of the `UIControl` that turns
+ screen coordinates into line coordinates. """
+ # Find row/col position first.
+ yx_to_rowcol = dict((v, k) for k, v in rowcol_to_yx.items())
+ y = mouse_event.position.y
+ x = mouse_event.position.x
+
+ # If clicked below the content area, look for a position in the
+ # last line instead.
+ max_y = write_position.ypos + len(visible_line_to_row_col) - 1
+ y = min(max_y, y)
+
+ while x >= 0:
+ try:
+ row, col = yx_to_rowcol[y, x]
+ except KeyError:
+ # Try again. (When clicking on the right side of double
+ # width characters, or on the right side of the input.)
+ x -= 1
+ else:
+ # Found position, call handler of UIControl.
+ result = self.content.mouse_handler(
+ cli, MouseEvent(position=Point(x=col, y=row),
+ event_type=mouse_event.event_type))
+ break
+ else:
+ # nobreak.
+ # (No x/y coordinate found for the content. This happens in
+ # case of a FillControl, that only specifies a background, but
+ # doesn't have a content. Report (0,0) instead.)
+ result = self.content.mouse_handler(
+ cli, MouseEvent(position=Point(x=0, y=0),
+ event_type=mouse_event.event_type))
+
+ # If it returns NotImplemented, handle it here.
+ if result == NotImplemented:
+ return self._mouse_handler(cli, mouse_event)
+
+ return result
+
+ mouse_handlers.set_mouse_handler_for_range(
+ x_min=write_position.xpos + sum(left_margin_widths),
+ x_max=write_position.xpos + write_position.width - total_margin_width,
+ y_min=write_position.ypos,
+ y_max=write_position.ypos + write_position.height,
+ handler=mouse_handler)
+
+ # Render and copy margins.
+ move_x = 0
+
+ def render_margin(m, width):
+ " Render margin. Return `Screen`. "
+ # Retrieve margin tokens.
+ tokens = m.create_margin(cli, self.render_info, width, write_position.height)
+
+ # Turn it into a UIContent object.
+ # already rendered those tokens using this size.)
+ return TokenListControl.static(tokens).create_content(
+ cli, width + 1, write_position.height)
+
+ for m, width in zip(self.left_margins, left_margin_widths):
+ # Create screen for margin.
+ margin_screen = render_margin(m, width)
+
+ # Copy and shift X.
+ self._copy_margin(cli, margin_screen, screen, write_position, move_x, width)
+ move_x += width
+
+ move_x = write_position.width - sum(right_margin_widths)
+
+ for m, width in zip(self.right_margins, right_margin_widths):
+ # Create screen for margin.
+ margin_screen = render_margin(m, width)
+
+ # Copy and shift X.
+ self._copy_margin(cli, margin_screen, screen, write_position, move_x, width)
+ move_x += width
+
+ def _copy_body(self, cli, ui_content, new_screen, write_position, move_x,
+ width, vertical_scroll=0, horizontal_scroll=0,
+ has_focus=False, wrap_lines=False, highlight_lines=False,
+ vertical_scroll_2=0, always_hide_cursor=False):
+ """
+ Copy the UIContent into the output screen.
+ """
+ xpos = write_position.xpos + move_x
+ ypos = write_position.ypos
+ line_count = ui_content.line_count
+ new_buffer = new_screen.data_buffer
+ empty_char = _CHAR_CACHE['', Token]
+ ZeroWidthEscape = Token.ZeroWidthEscape
+
+ # Map visible line number to (row, col) of input.
+ # 'col' will always be zero if line wrapping is off.
+ visible_line_to_row_col = {}
+ rowcol_to_yx = {} # Maps (row, col) from the input to (y, x) screen coordinates.
+
+ # Fill background with default_char first.
+ default_char = ui_content.default_char
+
+ if default_char:
+ for y in range(ypos, ypos + write_position.height):
+ new_buffer_row = new_buffer[y]
+ for x in range(xpos, xpos + width):
+ new_buffer_row[x] = default_char
+
+ # Copy content.
+ def copy():
+ y = - vertical_scroll_2
+ lineno = vertical_scroll
+
+ while y < write_position.height and lineno < line_count:
+ # Take the next line and copy it in the real screen.
+ line = ui_content.get_line(lineno)
+
+ col = 0
+ x = -horizontal_scroll
+
+ visible_line_to_row_col[y] = (lineno, horizontal_scroll)
+ new_buffer_row = new_buffer[y + ypos]
+
+ for token, text in line:
+ # Remember raw VT escape sequences. (E.g. FinalTerm's
+ # escape sequences.)
+ if token == ZeroWidthEscape:
+ new_screen.zero_width_escapes[y + ypos][x + xpos] += text
+ continue
+
+ for c in text:
+ char = _CHAR_CACHE[c, token]
+ char_width = char.width
+
+ # Wrap when the line width is exceeded.
+ if wrap_lines and x + char_width > width:
+ visible_line_to_row_col[y + 1] = (
+ lineno, visible_line_to_row_col[y][1] + x)
+ y += 1
+ x = -horizontal_scroll # This would be equal to zero.
+ # (horizontal_scroll=0 when wrap_lines.)
+ new_buffer_row = new_buffer[y + ypos]
+
+ if y >= write_position.height:
+ return y # Break out of all for loops.
+
+ # Set character in screen and shift 'x'.
+ if x >= 0 and y >= 0 and x < write_position.width:
+ new_buffer_row[x + xpos] = char
+
+ # When we print a multi width character, make sure
+ # to erase the neighbous positions in the screen.
+ # (The empty string if different from everything,
+ # so next redraw this cell will repaint anyway.)
+ if char_width > 1:
+ for i in range(1, char_width):
+ new_buffer_row[x + xpos + i] = empty_char
+
+ # If this is a zero width characters, then it's
+ # probably part of a decomposed unicode character.
+ # See: https://en.wikipedia.org/wiki/Unicode_equivalence
+ # Merge it in the previous cell.
+ elif char_width == 0 and x - 1 >= 0:
+ prev_char = new_buffer_row[x + xpos - 1]
+ char2 = _CHAR_CACHE[prev_char.char + c, prev_char.token]
+ new_buffer_row[x + xpos - 1] = char2
+
+ # Keep track of write position for each character.
+ rowcol_to_yx[lineno, col] = (y + ypos, x + xpos)
+
+ col += 1
+ x += char_width
+
+ lineno += 1
+ y += 1
+ return y
+
+ y = copy()
+
+ def cursor_pos_to_screen_pos(row, col):
+ " Translate row/col from UIContent to real Screen coordinates. "
+ try:
+ y, x = rowcol_to_yx[row, col]
+ except KeyError:
+ # Normally this should never happen. (It is a bug, if it happens.)
+ # But to be sure, return (0, 0)
+ return Point(y=0, x=0)
+
+ # raise ValueError(
+ # 'Invalid position. row=%r col=%r, vertical_scroll=%r, '
+ # 'horizontal_scroll=%r, height=%r' %
+ # (row, col, vertical_scroll, horizontal_scroll, write_position.height))
+ else:
+ return Point(y=y, x=x)
+
+ # Set cursor and menu positions.
+ if ui_content.cursor_position:
+ screen_cursor_position = cursor_pos_to_screen_pos(
+ ui_content.cursor_position.y, ui_content.cursor_position.x)
+
+ if has_focus:
+ new_screen.cursor_position = screen_cursor_position
+
+ if always_hide_cursor:
+ new_screen.show_cursor = False
+ else:
+ new_screen.show_cursor = ui_content.show_cursor
+
+ self._highlight_digraph(cli, new_screen)
+
+ if highlight_lines:
+ self._highlight_cursorlines(
+ cli, new_screen, screen_cursor_position, xpos, ypos, width,
+ write_position.height)
+
+ # Draw input characters from the input processor queue.
+ if has_focus and ui_content.cursor_position:
+ self._show_input_processor_key_buffer(cli, new_screen)
+
+ # Set menu position.
+ if not new_screen.menu_position and ui_content.menu_position:
+ new_screen.menu_position = cursor_pos_to_screen_pos(
+ ui_content.menu_position.y, ui_content.menu_position.x)
+
+ # Update output screne height.
+ new_screen.height = max(new_screen.height, ypos + write_position.height)
+
+ return visible_line_to_row_col, rowcol_to_yx
+
+ def _highlight_digraph(self, cli, new_screen):
+ """
+ When we are in Vi digraph mode, put a question mark underneath the
+ cursor.
+ """
+ digraph_char = self._get_digraph_char(cli)
+ if digraph_char:
+ cpos = new_screen.cursor_position
+ new_screen.data_buffer[cpos.y][cpos.x] = \
+ _CHAR_CACHE[digraph_char, Token.Digraph]
+
+ def _show_input_processor_key_buffer(self, cli, new_screen):
+ """
+ When the user is typing a key binding that consists of several keys,
+ display the last pressed key if the user is in insert mode and the key
+ is meaningful to be displayed.
+ E.g. Some people want to bind 'jj' to escape in Vi insert mode. But the
+ first 'j' needs to be displayed in order to get some feedback.
+ """
+ key_buffer = cli.input_processor.key_buffer
+
+ if key_buffer and _in_insert_mode(cli) and not cli.is_done:
+ # The textual data for the given key. (Can be a VT100 escape
+ # sequence.)
+ data = key_buffer[-1].data
+
+ # Display only if this is a 1 cell width character.
+ if get_cwidth(data) == 1:
+ cpos = new_screen.cursor_position
+ new_screen.data_buffer[cpos.y][cpos.x] = \
+ _CHAR_CACHE[data, Token.PartialKeyBinding]
+
+ def _highlight_cursorlines(self, cli, new_screen, cpos, x, y, width, height):
+ """
+ Highlight cursor row/column.
+ """
+ cursor_line_token = (':', ) + self.cursorline_token
+ cursor_column_token = (':', ) + self.cursorcolumn_token
+
+ data_buffer = new_screen.data_buffer
+
+ # Highlight cursor line.
+ if self.cursorline(cli):
+ row = data_buffer[cpos.y]
+ for x in range(x, x + width):
+ original_char = row[x]
+ row[x] = _CHAR_CACHE[
+ original_char.char, original_char.token + cursor_line_token]
+
+ # Highlight cursor column.
+ if self.cursorcolumn(cli):
+ for y2 in range(y, y + height):
+ row = data_buffer[y2]
+ original_char = row[cpos.x]
+ row[cpos.x] = _CHAR_CACHE[
+ original_char.char, original_char.token + cursor_column_token]
+
+ # Highlight color columns
+ for cc in self.get_colorcolumns(cli):
+ assert isinstance(cc, ColorColumn)
+ color_column_token = (':', ) + cc.token
+ column = cc.position
+
+ for y2 in range(y, y + height):
+ row = data_buffer[y2]
+ original_char = row[column]
+ row[column] = _CHAR_CACHE[
+ original_char.char, original_char.token + color_column_token]
+
+ def _copy_margin(self, cli, lazy_screen, new_screen, write_position, move_x, width):
+ """
+ Copy characters from the margin screen to the real screen.
+ """
+ xpos = write_position.xpos + move_x
+ ypos = write_position.ypos
+
+ margin_write_position = WritePosition(xpos, ypos, width, write_position.height)
+ self._copy_body(cli, lazy_screen, new_screen, margin_write_position, 0, width)
+
+ def _scroll_when_linewrapping(self, ui_content, width, height, cli):
+ """
+ Scroll to make sure the cursor position is visible and that we maintain
+ the requested scroll offset.
+
+ Set `self.horizontal_scroll/vertical_scroll`.
+ """
+ scroll_offsets_bottom = self.scroll_offsets.bottom
+ scroll_offsets_top = self.scroll_offsets.top
+
+ # We don't have horizontal scrolling.
+ self.horizontal_scroll = 0
+
+ # If the current line consumes more than the whole window height,
+ # then we have to scroll vertically inside this line. (We don't take
+ # the scroll offsets into account for this.)
+ # Also, ignore the scroll offsets in this case. Just set the vertical
+ # scroll to this line.
+ if ui_content.get_height_for_line(ui_content.cursor_position.y, width) > height - scroll_offsets_top:
+ # Calculate the height of the text before the cursor, with the line
+ # containing the cursor included, and the character belowe the
+ # cursor included as well.
+ line = explode_tokens(ui_content.get_line(ui_content.cursor_position.y))
+ text_before_cursor = token_list_to_text(line[:ui_content.cursor_position.x + 1])
+ text_before_height = UIContent.get_height_for_text(text_before_cursor, width)
+
+ # Adjust scroll offset.
+ self.vertical_scroll = ui_content.cursor_position.y
+ self.vertical_scroll_2 = min(text_before_height - 1, self.vertical_scroll_2)
+ self.vertical_scroll_2 = max(0, text_before_height - height, self.vertical_scroll_2)
+ return
+ else:
+ self.vertical_scroll_2 = 0
+
+ # Current line doesn't consume the whole height. Take scroll offsets into account.
+ def get_min_vertical_scroll():
+ # Make sure that the cursor line is not below the bottom.
+ # (Calculate how many lines can be shown between the cursor and the .)
+ used_height = 0
+ prev_lineno = ui_content.cursor_position.y
+
+ for lineno in range(ui_content.cursor_position.y, -1, -1):
+ used_height += ui_content.get_height_for_line(lineno, width)
+
+ if used_height > height - scroll_offsets_bottom:
+ return prev_lineno
+ else:
+ prev_lineno = lineno
+ return 0
+
+ def get_max_vertical_scroll():
+ # Make sure that the cursor line is not above the top.
+ prev_lineno = ui_content.cursor_position.y
+ used_height = 0
+
+ for lineno in range(ui_content.cursor_position.y - 1, -1, -1):
+ used_height += ui_content.get_height_for_line(lineno, width)
+
+ if used_height > scroll_offsets_top:
+ return prev_lineno
+ else:
+ prev_lineno = lineno
+ return prev_lineno
+
+ def get_topmost_visible():
+ """
+ Calculate the upper most line that can be visible, while the bottom
+ is still visible. We should not allow scroll more than this if
+ `allow_scroll_beyond_bottom` is false.
+ """
+ prev_lineno = ui_content.line_count - 1
+ used_height = 0
+ for lineno in range(ui_content.line_count - 1, -1, -1):
+ used_height += ui_content.get_height_for_line(lineno, width)
+ if used_height > height:
+ return prev_lineno
+ else:
+ prev_lineno = lineno
+ return prev_lineno
+
+ # Scroll vertically. (Make sure that the whole line which contains the
+ # cursor is visible.
+ topmost_visible = get_topmost_visible()
+
+ # Note: the `min(topmost_visible, ...)` is to make sure that we
+ # don't require scrolling up because of the bottom scroll offset,
+ # when we are at the end of the document.
+ self.vertical_scroll = max(self.vertical_scroll, min(topmost_visible, get_min_vertical_scroll()))
+ self.vertical_scroll = min(self.vertical_scroll, get_max_vertical_scroll())
+
+ # Disallow scrolling beyond bottom?
+ if not self.allow_scroll_beyond_bottom(cli):
+ self.vertical_scroll = min(self.vertical_scroll, topmost_visible)
+
+ def _scroll_without_linewrapping(self, ui_content, width, height, cli):
+ """
+ Scroll to make sure the cursor position is visible and that we maintain
+ the requested scroll offset.
+
+ Set `self.horizontal_scroll/vertical_scroll`.
+ """
+ cursor_position = ui_content.cursor_position or Point(0, 0)
+
+ # Without line wrapping, we will never have to scroll vertically inside
+ # a single line.
+ self.vertical_scroll_2 = 0
+
+ if ui_content.line_count == 0:
+ self.vertical_scroll = 0
+ self.horizontal_scroll = 0
+ return
+ else:
+ current_line_text = token_list_to_text(ui_content.get_line(cursor_position.y))
+
+ def do_scroll(current_scroll, scroll_offset_start, scroll_offset_end,
+ cursor_pos, window_size, content_size):
+ " Scrolling algorithm. Used for both horizontal and vertical scrolling. "
+ # Calculate the scroll offset to apply.
+ # This can obviously never be more than have the screen size. Also, when the
+ # cursor appears at the top or bottom, we don't apply the offset.
+ scroll_offset_start = int(min(scroll_offset_start, window_size / 2, cursor_pos))
+ scroll_offset_end = int(min(scroll_offset_end, window_size / 2,
+ content_size - 1 - cursor_pos))
+
+ # Prevent negative scroll offsets.
+ if current_scroll < 0:
+ current_scroll = 0
+
+ # Scroll back if we scrolled to much and there's still space to show more of the document.
+ if (not self.allow_scroll_beyond_bottom(cli) and
+ current_scroll > content_size - window_size):
+ current_scroll = max(0, content_size - window_size)
+
+ # Scroll up if cursor is before visible part.
+ if current_scroll > cursor_pos - scroll_offset_start:
+ current_scroll = max(0, cursor_pos - scroll_offset_start)
+
+ # Scroll down if cursor is after visible part.
+ if current_scroll < (cursor_pos + 1) - window_size + scroll_offset_end:
+ current_scroll = (cursor_pos + 1) - window_size + scroll_offset_end
+
+ return current_scroll
+
+ # When a preferred scroll is given, take that first into account.
+ if self.get_vertical_scroll:
+ self.vertical_scroll = self.get_vertical_scroll(self)
+ assert isinstance(self.vertical_scroll, int)
+ if self.get_horizontal_scroll:
+ self.horizontal_scroll = self.get_horizontal_scroll(self)
+ assert isinstance(self.horizontal_scroll, int)
+
+ # Update horizontal/vertical scroll to make sure that the cursor
+ # remains visible.
+ offsets = self.scroll_offsets
+
+ self.vertical_scroll = do_scroll(
+ current_scroll=self.vertical_scroll,
+ scroll_offset_start=offsets.top,
+ scroll_offset_end=offsets.bottom,
+ cursor_pos=ui_content.cursor_position.y,
+ window_size=height,
+ content_size=ui_content.line_count)
+
+ self.horizontal_scroll = do_scroll(
+ current_scroll=self.horizontal_scroll,
+ scroll_offset_start=offsets.left,
+ scroll_offset_end=offsets.right,
+ cursor_pos=get_cwidth(current_line_text[:ui_content.cursor_position.x]),
+ window_size=width,
+ # We can only analyse the current line. Calculating the width off
+ # all the lines is too expensive.
+ content_size=max(get_cwidth(current_line_text), self.horizontal_scroll + width))
+
+ def _mouse_handler(self, cli, mouse_event):
+ """
+ Mouse handler. Called when the UI control doesn't handle this
+ particular event.
+ """
+ if mouse_event.event_type == MouseEventType.SCROLL_DOWN:
+ self._scroll_down(cli)
+ elif mouse_event.event_type == MouseEventType.SCROLL_UP:
+ self._scroll_up(cli)
+
+ def _scroll_down(self, cli):
+ " Scroll window down. "
+ info = self.render_info
+
+ if self.vertical_scroll < info.content_height - info.window_height:
+ if info.cursor_position.y <= info.configured_scroll_offsets.top:
+ self.content.move_cursor_down(cli)
+
+ self.vertical_scroll += 1
+
+ def _scroll_up(self, cli):
+ " Scroll window up. "
+ info = self.render_info
+
+ if info.vertical_scroll > 0:
+ # TODO: not entirely correct yet in case of line wrapping and long lines.
+ if info.cursor_position.y >= info.window_height - 1 - info.configured_scroll_offsets.bottom:
+ self.content.move_cursor_up(cli)
+
+ self.vertical_scroll -= 1
+
+ def walk(self, cli):
+ # Only yield self. A window doesn't have children.
+ yield self
+
+
+class ConditionalContainer(Container):
+ """
+ Wrapper around any other container that can change the visibility. The
+ received `filter` determines whether the given container should be
+ displayed or not.
+
+ :param content: :class:`.Container` instance.
+ :param filter: :class:`~prompt_toolkit.filters.CLIFilter` instance.
+ """
+ def __init__(self, content, filter):
+ assert isinstance(content, Container)
+
+ self.content = content
+ self.filter = to_cli_filter(filter)
+
+ def __repr__(self):
+ return 'ConditionalContainer(%r, filter=%r)' % (self.content, self.filter)
+
+ def reset(self):
+ self.content.reset()
+
+ def preferred_width(self, cli, max_available_width):
+ if self.filter(cli):
+ return self.content.preferred_width(cli, max_available_width)
+ else:
+ return LayoutDimension.exact(0)
+
+ def preferred_height(self, cli, width, max_available_height):
+ if self.filter(cli):
+ return self.content.preferred_height(cli, width, max_available_height)
+ else:
+ return LayoutDimension.exact(0)
+
+ def write_to_screen(self, cli, screen, mouse_handlers, write_position):
+ if self.filter(cli):
+ return self.content.write_to_screen(cli, screen, mouse_handlers, write_position)
+
+ def walk(self, cli):
+ return self.content.walk(cli)
+
+
+# Deprecated alias for 'Container'.
+Layout = Container
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/controls.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/controls.py
new file mode 100644
index 0000000000..ca74931dbc
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/controls.py
@@ -0,0 +1,730 @@
+"""
+User interface Controls for the layout.
+"""
+from __future__ import unicode_literals
+
+from abc import ABCMeta, abstractmethod
+from collections import namedtuple
+from six import with_metaclass
+from six.moves import range
+
+from prompt_toolkit.cache import SimpleCache
+from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER
+from prompt_toolkit.filters import to_cli_filter
+from prompt_toolkit.mouse_events import MouseEventType
+from prompt_toolkit.search_state import SearchState
+from prompt_toolkit.selection import SelectionType
+from prompt_toolkit.token import Token
+from prompt_toolkit.utils import get_cwidth
+
+from .lexers import Lexer, SimpleLexer
+from .processors import Processor
+from .screen import Char, Point
+from .utils import token_list_width, split_lines, token_list_to_text
+
+import six
+import time
+
+
+__all__ = (
+ 'BufferControl',
+ 'FillControl',
+ 'TokenListControl',
+ 'UIControl',
+ 'UIContent',
+)
+
+
+class UIControl(with_metaclass(ABCMeta, object)):
+ """
+ Base class for all user interface controls.
+ """
+ def reset(self):
+ # Default reset. (Doesn't have to be implemented.)
+ pass
+
+ def preferred_width(self, cli, max_available_width):
+ return None
+
+ def preferred_height(self, cli, width, max_available_height, wrap_lines):
+ return None
+
+ def has_focus(self, cli):
+ """
+ Return ``True`` when this user control has the focus.
+
+ If so, the cursor will be displayed according to the cursor position
+ reported by :meth:`.UIControl.create_content`. If the created content
+ has the property ``show_cursor=False``, the cursor will be hidden from
+ the output.
+ """
+ return False
+
+ @abstractmethod
+ def create_content(self, cli, width, height):
+ """
+ Generate the content for this user control.
+
+ Returns a :class:`.UIContent` instance.
+ """
+
+ def mouse_handler(self, cli, mouse_event):
+ """
+ Handle mouse events.
+
+ When `NotImplemented` is returned, it means that the given event is not
+ handled by the `UIControl` itself. The `Window` or key bindings can
+ decide to handle this event as scrolling or changing focus.
+
+ :param cli: `CommandLineInterface` instance.
+ :param mouse_event: `MouseEvent` instance.
+ """
+ return NotImplemented
+
+ def move_cursor_down(self, cli):
+ """
+ Request to move the cursor down.
+ This happens when scrolling down and the cursor is completely at the
+ top.
+ """
+
+ def move_cursor_up(self, cli):
+ """
+ Request to move the cursor up.
+ """
+
+
+class UIContent(object):
+ """
+ Content generated by a user control. This content consists of a list of
+ lines.
+
+ :param get_line: Callable that returns the current line. This is a list of
+ (Token, text) tuples.
+ :param line_count: The number of lines.
+ :param cursor_position: a :class:`.Point` for the cursor position.
+ :param menu_position: a :class:`.Point` for the menu position.
+ :param show_cursor: Make the cursor visible.
+ :param default_char: The default :class:`.Char` for filling the background.
+ """
+ def __init__(self, get_line=None, line_count=0,
+ cursor_position=None, menu_position=None, show_cursor=True,
+ default_char=None):
+ assert callable(get_line)
+ assert isinstance(line_count, six.integer_types)
+ assert cursor_position is None or isinstance(cursor_position, Point)
+ assert menu_position is None or isinstance(menu_position, Point)
+ assert default_char is None or isinstance(default_char, Char)
+
+ self.get_line = get_line
+ self.line_count = line_count
+ self.cursor_position = cursor_position or Point(0, 0)
+ self.menu_position = menu_position
+ self.show_cursor = show_cursor
+ self.default_char = default_char
+
+ # Cache for line heights. Maps (lineno, width) -> height.
+ self._line_heights = {}
+
+ def __getitem__(self, lineno):
+ " Make it iterable (iterate line by line). "
+ if lineno < self.line_count:
+ return self.get_line(lineno)
+ else:
+ raise IndexError
+
+ def get_height_for_line(self, lineno, width):
+ """
+ Return the height that a given line would need if it is rendered in a
+ space with the given width.
+ """
+ try:
+ return self._line_heights[lineno, width]
+ except KeyError:
+ text = token_list_to_text(self.get_line(lineno))
+ result = self.get_height_for_text(text, width)
+
+ # Cache and return
+ self._line_heights[lineno, width] = result
+ return result
+
+ @staticmethod
+ def get_height_for_text(text, width):
+ # Get text width for this line.
+ line_width = get_cwidth(text)
+
+ # Calculate height.
+ try:
+ quotient, remainder = divmod(line_width, width)
+ except ZeroDivisionError:
+ # Return something very big.
+ # (This can happen, when the Window gets very small.)
+ return 10 ** 10
+ else:
+ if remainder:
+ quotient += 1 # Like math.ceil.
+ return max(1, quotient)
+
+
+class TokenListControl(UIControl):
+ """
+ Control that displays a list of (Token, text) tuples.
+ (It's mostly optimized for rather small widgets, like toolbars, menus, etc...)
+
+ Mouse support:
+
+ The list of tokens can also contain tuples of three items, looking like:
+ (Token, text, handler). When mouse support is enabled and the user
+ clicks on this token, then the given handler is called. That handler
+ should accept two inputs: (CommandLineInterface, MouseEvent) and it
+ should either handle the event or return `NotImplemented` in case we
+ want the containing Window to handle this event.
+
+ :param get_tokens: Callable that takes a `CommandLineInterface` instance
+ and returns the list of (Token, text) tuples to be displayed right now.
+ :param default_char: default :class:`.Char` (character and Token) to use
+ for the background when there is more space available than `get_tokens`
+ returns.
+ :param get_default_char: Like `default_char`, but this is a callable that
+ takes a :class:`prompt_toolkit.interface.CommandLineInterface` and
+ returns a :class:`.Char` instance.
+ :param has_focus: `bool` or `CLIFilter`, when this evaluates to `True`,
+ this UI control will take the focus. The cursor will be shown in the
+ upper left corner of this control, unless `get_token` returns a
+ ``Token.SetCursorPosition`` token somewhere in the token list, then the
+ cursor will be shown there.
+ """
+ def __init__(self, get_tokens, default_char=None, get_default_char=None,
+ align_right=False, align_center=False, has_focus=False):
+ assert callable(get_tokens)
+ assert default_char is None or isinstance(default_char, Char)
+ assert get_default_char is None or callable(get_default_char)
+ assert not (default_char and get_default_char)
+
+ self.align_right = to_cli_filter(align_right)
+ self.align_center = to_cli_filter(align_center)
+ self._has_focus_filter = to_cli_filter(has_focus)
+
+ self.get_tokens = get_tokens
+
+ # Construct `get_default_char` callable.
+ if default_char:
+ get_default_char = lambda _: default_char
+ elif not get_default_char:
+ get_default_char = lambda _: Char(' ', Token.Transparent)
+
+ self.get_default_char = get_default_char
+
+ #: Cache for the content.
+ self._content_cache = SimpleCache(maxsize=18)
+ self._token_cache = SimpleCache(maxsize=1)
+ # Only cache one token list. We don't need the previous item.
+
+ # Render info for the mouse support.
+ self._tokens = None
+
+ def reset(self):
+ self._tokens = None
+
+ def __repr__(self):
+ return '%s(%r)' % (self.__class__.__name__, self.get_tokens)
+
+ def _get_tokens_cached(self, cli):
+ """
+ Get tokens, but only retrieve tokens once during one render run.
+ (This function is called several times during one rendering, because
+ we also need those for calculating the dimensions.)
+ """
+ return self._token_cache.get(
+ cli.render_counter, lambda: self.get_tokens(cli))
+
+ def has_focus(self, cli):
+ return self._has_focus_filter(cli)
+
+ def preferred_width(self, cli, max_available_width):
+ """
+ Return the preferred width for this control.
+ That is the width of the longest line.
+ """
+ text = token_list_to_text(self._get_tokens_cached(cli))
+ line_lengths = [get_cwidth(l) for l in text.split('\n')]
+ return max(line_lengths)
+
+ def preferred_height(self, cli, width, max_available_height, wrap_lines):
+ content = self.create_content(cli, width, None)
+ return content.line_count
+
+ def create_content(self, cli, width, height):
+ # Get tokens
+ tokens_with_mouse_handlers = self._get_tokens_cached(cli)
+
+ default_char = self.get_default_char(cli)
+
+ # Wrap/align right/center parameters.
+ right = self.align_right(cli)
+ center = self.align_center(cli)
+
+ def process_line(line):
+ " Center or right align a single line. "
+ used_width = token_list_width(line)
+ padding = width - used_width
+ if center:
+ padding = int(padding / 2)
+ return [(default_char.token, default_char.char * padding)] + line
+
+ if right or center:
+ token_lines_with_mouse_handlers = []
+
+ for line in split_lines(tokens_with_mouse_handlers):
+ token_lines_with_mouse_handlers.append(process_line(line))
+ else:
+ token_lines_with_mouse_handlers = list(split_lines(tokens_with_mouse_handlers))
+
+ # Strip mouse handlers from tokens.
+ token_lines = [
+ [tuple(item[:2]) for item in line]
+ for line in token_lines_with_mouse_handlers
+ ]
+
+ # Keep track of the tokens with mouse handler, for later use in
+ # `mouse_handler`.
+ self._tokens = tokens_with_mouse_handlers
+
+ # If there is a `Token.SetCursorPosition` in the token list, set the
+ # cursor position here.
+ def get_cursor_position():
+ SetCursorPosition = Token.SetCursorPosition
+
+ for y, line in enumerate(token_lines):
+ x = 0
+ for token, text in line:
+ if token == SetCursorPosition:
+ return Point(x=x, y=y)
+ x += len(text)
+ return None
+
+ # Create content, or take it from the cache.
+ key = (default_char.char, default_char.token,
+ tuple(tokens_with_mouse_handlers), width, right, center)
+
+ def get_content():
+ return UIContent(get_line=lambda i: token_lines[i],
+ line_count=len(token_lines),
+ default_char=default_char,
+ cursor_position=get_cursor_position())
+
+ return self._content_cache.get(key, get_content)
+
+ @classmethod
+ def static(cls, tokens):
+ def get_static_tokens(cli):
+ return tokens
+ return cls(get_static_tokens)
+
+ def mouse_handler(self, cli, mouse_event):
+ """
+ Handle mouse events.
+
+ (When the token list contained mouse handlers and the user clicked on
+ on any of these, the matching handler is called. This handler can still
+ return `NotImplemented` in case we want the `Window` to handle this
+ particular event.)
+ """
+ if self._tokens:
+ # Read the generator.
+ tokens_for_line = list(split_lines(self._tokens))
+
+ try:
+ tokens = tokens_for_line[mouse_event.position.y]
+ except IndexError:
+ return NotImplemented
+ else:
+ # Find position in the token list.
+ xpos = mouse_event.position.x
+
+ # Find mouse handler for this character.
+ count = 0
+ for item in tokens:
+ count += len(item[1])
+ if count >= xpos:
+ if len(item) >= 3:
+ # Handler found. Call it.
+ # (Handler can return NotImplemented, so return
+ # that result.)
+ handler = item[2]
+ return handler(cli, mouse_event)
+ else:
+ break
+
+ # Otherwise, don't handle here.
+ return NotImplemented
+
+
+class FillControl(UIControl):
+ """
+ Fill whole control with characters with this token.
+ (Also helpful for debugging.)
+
+ :param char: :class:`.Char` instance to use for filling.
+ :param get_char: A callable that takes a CommandLineInterface and returns a
+ :class:`.Char` object.
+ """
+ def __init__(self, character=None, token=Token, char=None, get_char=None): # 'character' and 'token' parameters are deprecated.
+ assert char is None or isinstance(char, Char)
+ assert get_char is None or callable(get_char)
+ assert not (char and get_char)
+
+ self.char = char
+
+ if character:
+ # Passing (character=' ', token=token) is deprecated.
+ self.character = character
+ self.token = token
+
+ self.get_char = lambda cli: Char(character, token)
+ elif get_char:
+ # When 'get_char' is given.
+ self.get_char = get_char
+ else:
+ # When 'char' is given.
+ self.char = self.char or Char()
+ self.get_char = lambda cli: self.char
+ self.char = char
+
+ def __repr__(self):
+ if self.char:
+ return '%s(char=%r)' % (self.__class__.__name__, self.char)
+ else:
+ return '%s(get_char=%r)' % (self.__class__.__name__, self.get_char)
+
+ def reset(self):
+ pass
+
+ def has_focus(self, cli):
+ return False
+
+ def create_content(self, cli, width, height):
+ def get_line(i):
+ return []
+
+ return UIContent(
+ get_line=get_line,
+ line_count=100 ** 100, # Something very big.
+ default_char=self.get_char(cli))
+
+
+_ProcessedLine = namedtuple('_ProcessedLine', 'tokens source_to_display display_to_source')
+
+
+class BufferControl(UIControl):
+ """
+ Control for visualising the content of a `Buffer`.
+
+ :param input_processors: list of :class:`~prompt_toolkit.layout.processors.Processor`.
+ :param lexer: :class:`~prompt_toolkit.layout.lexers.Lexer` instance for syntax highlighting.
+ :param preview_search: `bool` or `CLIFilter`: Show search while typing.
+ :param get_search_state: Callable that takes a CommandLineInterface and
+ returns the SearchState to be used. (If not CommandLineInterface.search_state.)
+ :param buffer_name: String representing the name of the buffer to display.
+ :param default_char: :class:`.Char` instance to use to fill the background. This is
+ transparent by default.
+ :param focus_on_click: Focus this buffer when it's click, but not yet focussed.
+ """
+ def __init__(self,
+ buffer_name=DEFAULT_BUFFER,
+ input_processors=None,
+ lexer=None,
+ preview_search=False,
+ search_buffer_name=SEARCH_BUFFER,
+ get_search_state=None,
+ menu_position=None,
+ default_char=None,
+ focus_on_click=False):
+ assert input_processors is None or all(isinstance(i, Processor) for i in input_processors)
+ assert menu_position is None or callable(menu_position)
+ assert lexer is None or isinstance(lexer, Lexer)
+ assert get_search_state is None or callable(get_search_state)
+ assert default_char is None or isinstance(default_char, Char)
+
+ self.preview_search = to_cli_filter(preview_search)
+ self.get_search_state = get_search_state
+ self.focus_on_click = to_cli_filter(focus_on_click)
+
+ self.input_processors = input_processors or []
+ self.buffer_name = buffer_name
+ self.menu_position = menu_position
+ self.lexer = lexer or SimpleLexer()
+ self.default_char = default_char or Char(token=Token.Transparent)
+ self.search_buffer_name = search_buffer_name
+
+ #: Cache for the lexer.
+ #: Often, due to cursor movement, undo/redo and window resizing
+ #: operations, it happens that a short time, the same document has to be
+ #: lexed. This is a faily easy way to cache such an expensive operation.
+ self._token_cache = SimpleCache(maxsize=8)
+
+ self._xy_to_cursor_position = None
+ self._last_click_timestamp = None
+ self._last_get_processed_line = None
+
+ def _buffer(self, cli):
+ """
+ The buffer object that contains the 'main' content.
+ """
+ return cli.buffers[self.buffer_name]
+
+ def has_focus(self, cli):
+ # This control gets the focussed if the actual `Buffer` instance has the
+ # focus or when any of the `InputProcessor` classes tells us that it
+ # wants the focus. (E.g. in case of a reverse-search, where the actual
+ # search buffer may not be displayed, but the "reverse-i-search" text
+ # should get the focus.)
+ return cli.current_buffer_name == self.buffer_name or \
+ any(i.has_focus(cli) for i in self.input_processors)
+
+ def preferred_width(self, cli, max_available_width):
+ """
+ This should return the preferred width.
+
+ Note: We don't specify a preferred width according to the content,
+ because it would be too expensive. Calculating the preferred
+ width can be done by calculating the longest line, but this would
+ require applying all the processors to each line. This is
+ unfeasible for a larger document, and doing it for small
+ documents only would result in inconsistent behaviour.
+ """
+ return None
+
+ def preferred_height(self, cli, width, max_available_height, wrap_lines):
+ # Calculate the content height, if it was drawn on a screen with the
+ # given width.
+ height = 0
+ content = self.create_content(cli, width, None)
+
+ # When line wrapping is off, the height should be equal to the amount
+ # of lines.
+ if not wrap_lines:
+ return content.line_count
+
+ # When the number of lines exceeds the max_available_height, just
+ # return max_available_height. No need to calculate anything.
+ if content.line_count >= max_available_height:
+ return max_available_height
+
+ for i in range(content.line_count):
+ height += content.get_height_for_line(i, width)
+
+ if height >= max_available_height:
+ return max_available_height
+
+ return height
+
+ def _get_tokens_for_line_func(self, cli, document):
+ """
+ Create a function that returns the tokens for a given line.
+ """
+ # Cache using `document.text`.
+ def get_tokens_for_line():
+ return self.lexer.lex_document(cli, document)
+
+ return self._token_cache.get(document.text, get_tokens_for_line)
+
+ def _create_get_processed_line_func(self, cli, document):
+ """
+ Create a function that takes a line number of the current document and
+ returns a _ProcessedLine(processed_tokens, source_to_display, display_to_source)
+ tuple.
+ """
+ def transform(lineno, tokens):
+ " Transform the tokens for a given line number. "
+ source_to_display_functions = []
+ display_to_source_functions = []
+
+ # Get cursor position at this line.
+ if document.cursor_position_row == lineno:
+ cursor_column = document.cursor_position_col
+ else:
+ cursor_column = None
+
+ def source_to_display(i):
+ """ Translate x position from the buffer to the x position in the
+ processed token list. """
+ for f in source_to_display_functions:
+ i = f(i)
+ return i
+
+ # Apply each processor.
+ for p in self.input_processors:
+ transformation = p.apply_transformation(
+ cli, document, lineno, source_to_display, tokens)
+ tokens = transformation.tokens
+
+ if cursor_column:
+ cursor_column = transformation.source_to_display(cursor_column)
+
+ display_to_source_functions.append(transformation.display_to_source)
+ source_to_display_functions.append(transformation.source_to_display)
+
+ def display_to_source(i):
+ for f in reversed(display_to_source_functions):
+ i = f(i)
+ return i
+
+ return _ProcessedLine(tokens, source_to_display, display_to_source)
+
+ def create_func():
+ get_line = self._get_tokens_for_line_func(cli, document)
+ cache = {}
+
+ def get_processed_line(i):
+ try:
+ return cache[i]
+ except KeyError:
+ processed_line = transform(i, get_line(i))
+ cache[i] = processed_line
+ return processed_line
+ return get_processed_line
+
+ return create_func()
+
+ def create_content(self, cli, width, height):
+ """
+ Create a UIContent.
+ """
+ buffer = self._buffer(cli)
+
+ # Get the document to be shown. If we are currently searching (the
+ # search buffer has focus, and the preview_search filter is enabled),
+ # then use the search document, which has possibly a different
+ # text/cursor position.)
+ def preview_now():
+ """ True when we should preview a search. """
+ return bool(self.preview_search(cli) and
+ cli.buffers[self.search_buffer_name].text)
+
+ if preview_now():
+ if self.get_search_state:
+ ss = self.get_search_state(cli)
+ else:
+ ss = cli.search_state
+
+ document = buffer.document_for_search(SearchState(
+ text=cli.current_buffer.text,
+ direction=ss.direction,
+ ignore_case=ss.ignore_case))
+ else:
+ document = buffer.document
+
+ get_processed_line = self._create_get_processed_line_func(cli, document)
+ self._last_get_processed_line = get_processed_line
+
+ def translate_rowcol(row, col):
+ " Return the content column for this coordinate. "
+ return Point(y=row, x=get_processed_line(row).source_to_display(col))
+
+ def get_line(i):
+ " Return the tokens for a given line number. "
+ tokens = get_processed_line(i).tokens
+
+ # Add a space at the end, because that is a possible cursor
+ # position. (When inserting after the input.) We should do this on
+ # all the lines, not just the line containing the cursor. (Because
+ # otherwise, line wrapping/scrolling could change when moving the
+ # cursor around.)
+ tokens = tokens + [(self.default_char.token, ' ')]
+ return tokens
+
+ content = UIContent(
+ get_line=get_line,
+ line_count=document.line_count,
+ cursor_position=translate_rowcol(document.cursor_position_row,
+ document.cursor_position_col),
+ default_char=self.default_char)
+
+ # If there is an auto completion going on, use that start point for a
+ # pop-up menu position. (But only when this buffer has the focus --
+ # there is only one place for a menu, determined by the focussed buffer.)
+ if cli.current_buffer_name == self.buffer_name:
+ menu_position = self.menu_position(cli) if self.menu_position else None
+ if menu_position is not None:
+ assert isinstance(menu_position, int)
+ menu_row, menu_col = buffer.document.translate_index_to_position(menu_position)
+ content.menu_position = translate_rowcol(menu_row, menu_col)
+ elif buffer.complete_state:
+ # Position for completion menu.
+ # Note: We use 'min', because the original cursor position could be
+ # behind the input string when the actual completion is for
+ # some reason shorter than the text we had before. (A completion
+ # can change and shorten the input.)
+ menu_row, menu_col = buffer.document.translate_index_to_position(
+ min(buffer.cursor_position,
+ buffer.complete_state.original_document.cursor_position))
+ content.menu_position = translate_rowcol(menu_row, menu_col)
+ else:
+ content.menu_position = None
+
+ return content
+
+ def mouse_handler(self, cli, mouse_event):
+ """
+ Mouse handler for this control.
+ """
+ buffer = self._buffer(cli)
+ position = mouse_event.position
+
+ # Focus buffer when clicked.
+ if self.has_focus(cli):
+ if self._last_get_processed_line:
+ processed_line = self._last_get_processed_line(position.y)
+
+ # Translate coordinates back to the cursor position of the
+ # original input.
+ xpos = processed_line.display_to_source(position.x)
+ index = buffer.document.translate_row_col_to_index(position.y, xpos)
+
+ # Set the cursor position.
+ if mouse_event.event_type == MouseEventType.MOUSE_DOWN:
+ buffer.exit_selection()
+ buffer.cursor_position = index
+
+ elif mouse_event.event_type == MouseEventType.MOUSE_UP:
+ # When the cursor was moved to another place, select the text.
+ # (The >1 is actually a small but acceptable workaround for
+ # selecting text in Vi navigation mode. In navigation mode,
+ # the cursor can never be after the text, so the cursor
+ # will be repositioned automatically.)
+ if abs(buffer.cursor_position - index) > 1:
+ buffer.start_selection(selection_type=SelectionType.CHARACTERS)
+ buffer.cursor_position = index
+
+ # Select word around cursor on double click.
+ # Two MOUSE_UP events in a short timespan are considered a double click.
+ double_click = self._last_click_timestamp and time.time() - self._last_click_timestamp < .3
+ self._last_click_timestamp = time.time()
+
+ if double_click:
+ start, end = buffer.document.find_boundaries_of_current_word()
+ buffer.cursor_position += start
+ buffer.start_selection(selection_type=SelectionType.CHARACTERS)
+ buffer.cursor_position += end - start
+ else:
+ # Don't handle scroll events here.
+ return NotImplemented
+
+ # Not focussed, but focussing on click events.
+ else:
+ if self.focus_on_click(cli) and mouse_event.event_type == MouseEventType.MOUSE_UP:
+ # Focus happens on mouseup. (If we did this on mousedown, the
+ # up event will be received at the point where this widget is
+ # focussed and be handled anyway.)
+ cli.focus(self.buffer_name)
+ else:
+ return NotImplemented
+
+ def move_cursor_down(self, cli):
+ b = self._buffer(cli)
+ b.cursor_position += b.document.get_cursor_down_position()
+
+ def move_cursor_up(self, cli):
+ b = self._buffer(cli)
+ b.cursor_position += b.document.get_cursor_up_position()
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/dimension.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/dimension.py
new file mode 100644
index 0000000000..717ad7a81f
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/dimension.py
@@ -0,0 +1,92 @@
+"""
+Layout dimensions are used to give the minimum, maximum and preferred
+dimensions for containers and controls.
+"""
+from __future__ import unicode_literals
+
+__all__ = (
+ 'LayoutDimension',
+ 'sum_layout_dimensions',
+ 'max_layout_dimensions',
+)
+
+
+class LayoutDimension(object):
+ """
+ Specified dimension (width/height) of a user control or window.
+
+ The layout engine tries to honor the preferred size. If that is not
+ possible, because the terminal is larger or smaller, it tries to keep in
+ between min and max.
+
+ :param min: Minimum size.
+ :param max: Maximum size.
+ :param weight: For a VSplit/HSplit, the actual size will be determined
+ by taking the proportion of weights from all the children.
+ E.g. When there are two children, one width a weight of 1,
+ and the other with a weight of 2. The second will always be
+ twice as big as the first, if the min/max values allow it.
+ :param preferred: Preferred size.
+ """
+ def __init__(self, min=None, max=None, weight=1, preferred=None):
+ assert isinstance(weight, int) and weight > 0 # Cannot be a float.
+
+ self.min_specified = min is not None
+ self.max_specified = max is not None
+ self.preferred_specified = preferred is not None
+
+ if min is None:
+ min = 0 # Smallest possible value.
+ if max is None: # 0-values are allowed, so use "is None"
+ max = 1000 ** 10 # Something huge.
+ if preferred is None:
+ preferred = min
+
+ self.min = min
+ self.max = max
+ self.preferred = preferred
+ self.weight = weight
+
+ # Make sure that the 'preferred' size is always in the min..max range.
+ if self.preferred < self.min:
+ self.preferred = self.min
+
+ if self.preferred > self.max:
+ self.preferred = self.max
+
+ @classmethod
+ def exact(cls, amount):
+ """
+ Return a :class:`.LayoutDimension` with an exact size. (min, max and
+ preferred set to ``amount``).
+ """
+ return cls(min=amount, max=amount, preferred=amount)
+
+ def __repr__(self):
+ return 'LayoutDimension(min=%r, max=%r, preferred=%r, weight=%r)' % (
+ self.min, self.max, self.preferred, self.weight)
+
+ def __add__(self, other):
+ return sum_layout_dimensions([self, other])
+
+
+def sum_layout_dimensions(dimensions):
+ """
+ Sum a list of :class:`.LayoutDimension` instances.
+ """
+ min = sum([d.min for d in dimensions if d.min is not None])
+ max = sum([d.max for d in dimensions if d.max is not None])
+ preferred = sum([d.preferred for d in dimensions])
+
+ return LayoutDimension(min=min, max=max, preferred=preferred)
+
+
+def max_layout_dimensions(dimensions):
+ """
+ Take the maximum of a list of :class:`.LayoutDimension` instances.
+ """
+ min_ = max([d.min for d in dimensions if d.min is not None])
+ max_ = max([d.max for d in dimensions if d.max is not None])
+ preferred = max([d.preferred for d in dimensions])
+
+ return LayoutDimension(min=min_, max=max_, preferred=preferred)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/lexers.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/lexers.py
new file mode 100644
index 0000000000..a928fd8226
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/lexers.py
@@ -0,0 +1,320 @@
+"""
+Lexer interface and implementation.
+Used for syntax highlighting.
+"""
+from __future__ import unicode_literals
+from abc import ABCMeta, abstractmethod
+from six import with_metaclass
+from six.moves import range
+
+from prompt_toolkit.token import Token
+from prompt_toolkit.filters import to_cli_filter
+from .utils import split_lines
+
+import re
+import six
+
+__all__ = (
+ 'Lexer',
+ 'SimpleLexer',
+ 'PygmentsLexer',
+ 'SyntaxSync',
+ 'SyncFromStart',
+ 'RegexSync',
+)
+
+
+class Lexer(with_metaclass(ABCMeta, object)):
+ """
+ Base class for all lexers.
+ """
+ @abstractmethod
+ def lex_document(self, cli, document):
+ """
+ Takes a :class:`~prompt_toolkit.document.Document` and returns a
+ callable that takes a line number and returns the tokens for that line.
+ """
+
+
+class SimpleLexer(Lexer):
+ """
+ Lexer that doesn't do any tokenizing and returns the whole input as one token.
+
+ :param token: The `Token` for this lexer.
+ """
+ # `default_token` parameter is deprecated!
+ def __init__(self, token=Token, default_token=None):
+ self.token = token
+
+ if default_token is not None:
+ self.token = default_token
+
+ def lex_document(self, cli, document):
+ lines = document.lines
+
+ def get_line(lineno):
+ " Return the tokens for the given line. "
+ try:
+ return [(self.token, lines[lineno])]
+ except IndexError:
+ return []
+ return get_line
+
+
+class SyntaxSync(with_metaclass(ABCMeta, object)):
+ """
+ Syntax synchroniser. This is a tool that finds a start position for the
+ lexer. This is especially important when editing big documents; we don't
+ want to start the highlighting by running the lexer from the beginning of
+ the file. That is very slow when editing.
+ """
+ @abstractmethod
+ def get_sync_start_position(self, document, lineno):
+ """
+ Return the position from where we can start lexing as a (row, column)
+ tuple.
+
+ :param document: `Document` instance that contains all the lines.
+ :param lineno: The line that we want to highlight. (We need to return
+ this line, or an earlier position.)
+ """
+
+class SyncFromStart(SyntaxSync):
+ """
+ Always start the syntax highlighting from the beginning.
+ """
+ def get_sync_start_position(self, document, lineno):
+ return 0, 0
+
+
+class RegexSync(SyntaxSync):
+ """
+ Synchronize by starting at a line that matches the given regex pattern.
+ """
+ # Never go more than this amount of lines backwards for synchronisation.
+ # That would be too CPU intensive.
+ MAX_BACKWARDS = 500
+
+ # Start lexing at the start, if we are in the first 'n' lines and no
+ # synchronisation position was found.
+ FROM_START_IF_NO_SYNC_POS_FOUND = 100
+
+ def __init__(self, pattern):
+ assert isinstance(pattern, six.text_type)
+ self._compiled_pattern = re.compile(pattern)
+
+ def get_sync_start_position(self, document, lineno):
+ " Scan backwards, and find a possible position to start. "
+ pattern = self._compiled_pattern
+ lines = document.lines
+
+ # Scan upwards, until we find a point where we can start the syntax
+ # synchronisation.
+ for i in range(lineno, max(-1, lineno - self.MAX_BACKWARDS), -1):
+ match = pattern.match(lines[i])
+ if match:
+ return i, match.start()
+
+ # No synchronisation point found. If we aren't that far from the
+ # beginning, start at the very beginning, otherwise, just try to start
+ # at the current line.
+ if lineno < self.FROM_START_IF_NO_SYNC_POS_FOUND:
+ return 0, 0
+ else:
+ return lineno, 0
+
+ @classmethod
+ def from_pygments_lexer_cls(cls, lexer_cls):
+ """
+ Create a :class:`.RegexSync` instance for this Pygments lexer class.
+ """
+ patterns = {
+ # For Python, start highlighting at any class/def block.
+ 'Python': r'^\s*(class|def)\s+',
+ 'Python 3': r'^\s*(class|def)\s+',
+
+ # For HTML, start at any open/close tag definition.
+ 'HTML': r'<[/a-zA-Z]',
+
+ # For javascript, start at a function.
+ 'JavaScript': r'\bfunction\b'
+
+ # TODO: Add definitions for other languages.
+ # By default, we start at every possible line.
+ }
+ p = patterns.get(lexer_cls.name, '^')
+ return cls(p)
+
+
+class PygmentsLexer(Lexer):
+ """
+ Lexer that calls a pygments lexer.
+
+ Example::
+
+ from pygments.lexers import HtmlLexer
+ lexer = PygmentsLexer(HtmlLexer)
+
+ Note: Don't forget to also load a Pygments compatible style. E.g.::
+
+ from prompt_toolkit.styles.from_pygments import style_from_pygments
+ from pygments.styles import get_style_by_name
+ style = style_from_pygments(get_style_by_name('monokai'))
+
+ :param pygments_lexer_cls: A `Lexer` from Pygments.
+ :param sync_from_start: Start lexing at the start of the document. This
+ will always give the best results, but it will be slow for bigger
+ documents. (When the last part of the document is display, then the
+ whole document will be lexed by Pygments on every key stroke.) It is
+ recommended to disable this for inputs that are expected to be more
+ than 1,000 lines.
+ :param syntax_sync: `SyntaxSync` object.
+ """
+ # Minimum amount of lines to go backwards when starting the parser.
+ # This is important when the lines are retrieved in reverse order, or when
+ # scrolling upwards. (Due to the complexity of calculating the vertical
+ # scroll offset in the `Window` class, lines are not always retrieved in
+ # order.)
+ MIN_LINES_BACKWARDS = 50
+
+ # When a parser was started this amount of lines back, read the parser
+ # until we get the current line. Otherwise, start a new parser.
+ # (This should probably be bigger than MIN_LINES_BACKWARDS.)
+ REUSE_GENERATOR_MAX_DISTANCE = 100
+
+ def __init__(self, pygments_lexer_cls, sync_from_start=True, syntax_sync=None):
+ assert syntax_sync is None or isinstance(syntax_sync, SyntaxSync)
+
+ self.pygments_lexer_cls = pygments_lexer_cls
+ self.sync_from_start = to_cli_filter(sync_from_start)
+
+ # Instantiate the Pygments lexer.
+ self.pygments_lexer = pygments_lexer_cls(
+ stripnl=False,
+ stripall=False,
+ ensurenl=False)
+
+ # Create syntax sync instance.
+ self.syntax_sync = syntax_sync or RegexSync.from_pygments_lexer_cls(pygments_lexer_cls)
+
+ @classmethod
+ def from_filename(cls, filename, sync_from_start=True):
+ """
+ Create a `Lexer` from a filename.
+ """
+ # Inline imports: the Pygments dependency is optional!
+ from pygments.util import ClassNotFound
+ from pygments.lexers import get_lexer_for_filename
+
+ try:
+ pygments_lexer = get_lexer_for_filename(filename)
+ except ClassNotFound:
+ return SimpleLexer()
+ else:
+ return cls(pygments_lexer.__class__, sync_from_start=sync_from_start)
+
+ def lex_document(self, cli, document):
+ """
+ Create a lexer function that takes a line number and returns the list
+ of (Token, text) tuples as the Pygments lexer returns for that line.
+ """
+ # Cache of already lexed lines.
+ cache = {}
+
+ # Pygments generators that are currently lexing.
+ line_generators = {} # Map lexer generator to the line number.
+
+ def get_syntax_sync():
+ " The Syntax synchronisation objcet that we currently use. "
+ if self.sync_from_start(cli):
+ return SyncFromStart()
+ else:
+ return self.syntax_sync
+
+ def find_closest_generator(i):
+ " Return a generator close to line 'i', or None if none was fonud. "
+ for generator, lineno in line_generators.items():
+ if lineno < i and i - lineno < self.REUSE_GENERATOR_MAX_DISTANCE:
+ return generator
+
+ def create_line_generator(start_lineno, column=0):
+ """
+ Create a generator that yields the lexed lines.
+ Each iteration it yields a (line_number, [(token, text), ...]) tuple.
+ """
+ def get_tokens():
+ text = '\n'.join(document.lines[start_lineno:])[column:]
+
+ # We call `get_tokens_unprocessed`, because `get_tokens` will
+ # still replace \r\n and \r by \n. (We don't want that,
+ # Pygments should return exactly the same amount of text, as we
+ # have given as input.)
+ for _, t, v in self.pygments_lexer.get_tokens_unprocessed(text):
+ yield t, v
+
+ return enumerate(split_lines(get_tokens()), start_lineno)
+
+ def get_generator(i):
+ """
+ Find an already started generator that is close, or create a new one.
+ """
+ # Find closest line generator.
+ generator = find_closest_generator(i)
+ if generator:
+ return generator
+
+ # No generator found. Determine starting point for the syntax
+ # synchronisation first.
+
+ # Go at least x lines back. (Make scrolling upwards more
+ # efficient.)
+ i = max(0, i - self.MIN_LINES_BACKWARDS)
+
+ if i == 0:
+ row = 0
+ column = 0
+ else:
+ row, column = get_syntax_sync().get_sync_start_position(document, i)
+
+ # Find generator close to this point, or otherwise create a new one.
+ generator = find_closest_generator(i)
+ if generator:
+ return generator
+ else:
+ generator = create_line_generator(row, column)
+
+ # If the column is not 0, ignore the first line. (Which is
+ # incomplete. This happens when the synchronisation algorithm tells
+ # us to start parsing in the middle of a line.)
+ if column:
+ next(generator)
+ row += 1
+
+ line_generators[generator] = row
+ return generator
+
+ def get_line(i):
+ " Return the tokens for a given line number. "
+ try:
+ return cache[i]
+ except KeyError:
+ generator = get_generator(i)
+
+ # Exhaust the generator, until we find the requested line.
+ for num, line in generator:
+ cache[num] = line
+ if num == i:
+ line_generators[generator] = i
+
+ # Remove the next item from the cache.
+ # (It could happen that it's already there, because of
+ # another generator that started filling these lines,
+ # but we want to synchronise these lines with the
+ # current lexer's state.)
+ if num + 1 in cache:
+ del cache[num + 1]
+
+ return cache[num]
+ return []
+
+ return get_line
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/margins.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/margins.py
new file mode 100644
index 0000000000..2934dfc9a7
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/margins.py
@@ -0,0 +1,253 @@
+"""
+Margin implementations for a :class:`~prompt_toolkit.layout.containers.Window`.
+"""
+from __future__ import unicode_literals
+
+from abc import ABCMeta, abstractmethod
+from six import with_metaclass
+from six.moves import range
+
+from prompt_toolkit.filters import to_cli_filter
+from prompt_toolkit.token import Token
+from prompt_toolkit.utils import get_cwidth
+from .utils import token_list_to_text
+
+__all__ = (
+ 'Margin',
+ 'NumberredMargin',
+ 'ScrollbarMargin',
+ 'ConditionalMargin',
+ 'PromptMargin',
+)
+
+
+class Margin(with_metaclass(ABCMeta, object)):
+ """
+ Base interface for a margin.
+ """
+ @abstractmethod
+ def get_width(self, cli, get_ui_content):
+ """
+ Return the width that this margin is going to consume.
+
+ :param cli: :class:`.CommandLineInterface` instance.
+ :param get_ui_content: Callable that asks the user control to create
+ a :class:`.UIContent` instance. This can be used for instance to
+ obtain the number of lines.
+ """
+ return 0
+
+ @abstractmethod
+ def create_margin(self, cli, window_render_info, width, height):
+ """
+ Creates a margin.
+ This should return a list of (Token, text) tuples.
+
+ :param cli: :class:`.CommandLineInterface` instance.
+ :param window_render_info:
+ :class:`~prompt_toolkit.layout.containers.WindowRenderInfo`
+ instance, generated after rendering and copying the visible part of
+ the :class:`~prompt_toolkit.layout.controls.UIControl` into the
+ :class:`~prompt_toolkit.layout.containers.Window`.
+ :param width: The width that's available for this margin. (As reported
+ by :meth:`.get_width`.)
+ :param height: The height that's available for this margin. (The height
+ of the :class:`~prompt_toolkit.layout.containers.Window`.)
+ """
+ return []
+
+
+class NumberredMargin(Margin):
+ """
+ Margin that displays the line numbers.
+
+ :param relative: Number relative to the cursor position. Similar to the Vi
+ 'relativenumber' option.
+ :param display_tildes: Display tildes after the end of the document, just
+ like Vi does.
+ """
+ def __init__(self, relative=False, display_tildes=False):
+ self.relative = to_cli_filter(relative)
+ self.display_tildes = to_cli_filter(display_tildes)
+
+ def get_width(self, cli, get_ui_content):
+ line_count = get_ui_content().line_count
+ return max(3, len('%s' % line_count) + 1)
+
+ def create_margin(self, cli, window_render_info, width, height):
+ relative = self.relative(cli)
+
+ token = Token.LineNumber
+ token_current = Token.LineNumber.Current
+
+ # Get current line number.
+ current_lineno = window_render_info.ui_content.cursor_position.y
+
+ # Construct margin.
+ result = []
+ last_lineno = None
+
+ for y, lineno in enumerate(window_render_info.displayed_lines):
+ # Only display line number if this line is not a continuation of the previous line.
+ if lineno != last_lineno:
+ if lineno is None:
+ pass
+ elif lineno == current_lineno:
+ # Current line.
+ if relative:
+ # Left align current number in relative mode.
+ result.append((token_current, '%i' % (lineno + 1)))
+ else:
+ result.append((token_current, ('%i ' % (lineno + 1)).rjust(width)))
+ else:
+ # Other lines.
+ if relative:
+ lineno = abs(lineno - current_lineno) - 1
+
+ result.append((token, ('%i ' % (lineno + 1)).rjust(width)))
+
+ last_lineno = lineno
+ result.append((Token, '\n'))
+
+ # Fill with tildes.
+ if self.display_tildes(cli):
+ while y < window_render_info.window_height:
+ result.append((Token.Tilde, '~\n'))
+ y += 1
+
+ return result
+
+
+class ConditionalMargin(Margin):
+ """
+ Wrapper around other :class:`.Margin` classes to show/hide them.
+ """
+ def __init__(self, margin, filter):
+ assert isinstance(margin, Margin)
+
+ self.margin = margin
+ self.filter = to_cli_filter(filter)
+
+ def get_width(self, cli, ui_content):
+ if self.filter(cli):
+ return self.margin.get_width(cli, ui_content)
+ else:
+ return 0
+
+ def create_margin(self, cli, window_render_info, width, height):
+ if width and self.filter(cli):
+ return self.margin.create_margin(cli, window_render_info, width, height)
+ else:
+ return []
+
+
+class ScrollbarMargin(Margin):
+ """
+ Margin displaying a scrollbar.
+
+ :param display_arrows: Display scroll up/down arrows.
+ """
+ def __init__(self, display_arrows=False):
+ self.display_arrows = to_cli_filter(display_arrows)
+
+ def get_width(self, cli, ui_content):
+ return 1
+
+ def create_margin(self, cli, window_render_info, width, height):
+ total_height = window_render_info.content_height
+ display_arrows = self.display_arrows(cli)
+
+ window_height = window_render_info.window_height
+ if display_arrows:
+ window_height -= 2
+
+ try:
+ items_per_row = float(total_height) / min(total_height, window_height)
+ except ZeroDivisionError:
+ return []
+ else:
+ def is_scroll_button(row):
+ " True if we should display a button on this row. "
+ current_row_middle = int((row + .5) * items_per_row)
+ return current_row_middle in window_render_info.displayed_lines
+
+ # Up arrow.
+ result = []
+ if display_arrows:
+ result.extend([
+ (Token.Scrollbar.Arrow, '^'),
+ (Token.Scrollbar, '\n')
+ ])
+
+ # Scrollbar body.
+ for i in range(window_height):
+ if is_scroll_button(i):
+ result.append((Token.Scrollbar.Button, ' '))
+ else:
+ result.append((Token.Scrollbar, ' '))
+ result.append((Token, '\n'))
+
+ # Down arrow
+ if display_arrows:
+ result.append((Token.Scrollbar.Arrow, 'v'))
+
+ return result
+
+
+class PromptMargin(Margin):
+ """
+ Create margin that displays a prompt.
+ This can display one prompt at the first line, and a continuation prompt
+ (e.g, just dots) on all the following lines.
+
+ :param get_prompt_tokens: Callable that takes a CommandLineInterface as
+ input and returns a list of (Token, type) tuples to be shown as the
+ prompt at the first line.
+ :param get_continuation_tokens: Callable that takes a CommandLineInterface
+ and a width as input and returns a list of (Token, type) tuples for the
+ next lines of the input.
+ :param show_numbers: (bool or :class:`~prompt_toolkit.filters.CLIFilter`)
+ Display line numbers instead of the continuation prompt.
+ """
+ def __init__(self, get_prompt_tokens, get_continuation_tokens=None,
+ show_numbers=False):
+ assert callable(get_prompt_tokens)
+ assert get_continuation_tokens is None or callable(get_continuation_tokens)
+ show_numbers = to_cli_filter(show_numbers)
+
+ self.get_prompt_tokens = get_prompt_tokens
+ self.get_continuation_tokens = get_continuation_tokens
+ self.show_numbers = show_numbers
+
+ def get_width(self, cli, ui_content):
+ " Width to report to the `Window`. "
+ # Take the width from the first line.
+ text = token_list_to_text(self.get_prompt_tokens(cli))
+ return get_cwidth(text)
+
+ def create_margin(self, cli, window_render_info, width, height):
+ # First line.
+ tokens = self.get_prompt_tokens(cli)[:]
+
+ # Next lines. (Show line numbering when numbering is enabled.)
+ if self.get_continuation_tokens:
+ # Note: we turn this into a list, to make sure that we fail early
+ # in case `get_continuation_tokens` returns something else,
+ # like `None`.
+ tokens2 = list(self.get_continuation_tokens(cli, width))
+ else:
+ tokens2 = []
+
+ show_numbers = self.show_numbers(cli)
+ last_y = None
+
+ for y in window_render_info.displayed_lines[1:]:
+ tokens.append((Token, '\n'))
+ if show_numbers:
+ if y != last_y:
+ tokens.append((Token.LineNumber, ('%i ' % (y + 1)).rjust(width)))
+ else:
+ tokens.extend(tokens2)
+ last_y = y
+
+ return tokens
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/menus.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/menus.py
new file mode 100644
index 0000000000..a916846e45
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/menus.py
@@ -0,0 +1,496 @@
+from __future__ import unicode_literals
+
+from six.moves import zip_longest, range
+from prompt_toolkit.filters import HasCompletions, IsDone, Condition, to_cli_filter
+from prompt_toolkit.mouse_events import MouseEventType
+from prompt_toolkit.token import Token
+from prompt_toolkit.utils import get_cwidth
+
+from .containers import Window, HSplit, ConditionalContainer, ScrollOffsets
+from .controls import UIControl, UIContent
+from .dimension import LayoutDimension
+from .margins import ScrollbarMargin
+from .screen import Point, Char
+
+import math
+
+__all__ = (
+ 'CompletionsMenu',
+ 'MultiColumnCompletionsMenu',
+)
+
+
+class CompletionsMenuControl(UIControl):
+ """
+ Helper for drawing the complete menu to the screen.
+
+ :param scroll_offset: Number (integer) representing the preferred amount of
+ completions to be displayed before and after the current one. When this
+ is a very high number, the current completion will be shown in the
+ middle most of the time.
+ """
+ # Preferred minimum size of the menu control.
+ # The CompletionsMenu class defines a width of 8, and there is a scrollbar
+ # of 1.)
+ MIN_WIDTH = 7
+
+ def __init__(self):
+ self.token = Token.Menu.Completions
+
+ def has_focus(self, cli):
+ return False
+
+ def preferred_width(self, cli, max_available_width):
+ complete_state = cli.current_buffer.complete_state
+ if complete_state:
+ menu_width = self._get_menu_width(500, complete_state)
+ menu_meta_width = self._get_menu_meta_width(500, complete_state)
+
+ return menu_width + menu_meta_width
+ else:
+ return 0
+
+ def preferred_height(self, cli, width, max_available_height, wrap_lines):
+ complete_state = cli.current_buffer.complete_state
+ if complete_state:
+ return len(complete_state.current_completions)
+ else:
+ return 0
+
+ def create_content(self, cli, width, height):
+ """
+ Create a UIContent object for this control.
+ """
+ complete_state = cli.current_buffer.complete_state
+ if complete_state:
+ completions = complete_state.current_completions
+ index = complete_state.complete_index # Can be None!
+
+ # Calculate width of completions menu.
+ menu_width = self._get_menu_width(width, complete_state)
+ menu_meta_width = self._get_menu_meta_width(width - menu_width, complete_state)
+ show_meta = self._show_meta(complete_state)
+
+ def get_line(i):
+ c = completions[i]
+ is_current_completion = (i == index)
+ result = self._get_menu_item_tokens(c, is_current_completion, menu_width)
+
+ if show_meta:
+ result += self._get_menu_item_meta_tokens(c, is_current_completion, menu_meta_width)
+ return result
+
+ return UIContent(get_line=get_line,
+ cursor_position=Point(x=0, y=index or 0),
+ line_count=len(completions),
+ default_char=Char(' ', self.token))
+
+ return UIContent()
+
+ def _show_meta(self, complete_state):
+ """
+ Return ``True`` if we need to show a column with meta information.
+ """
+ return any(c.display_meta for c in complete_state.current_completions)
+
+ def _get_menu_width(self, max_width, complete_state):
+ """
+ Return the width of the main column.
+ """
+ return min(max_width, max(self.MIN_WIDTH, max(get_cwidth(c.display)
+ for c in complete_state.current_completions) + 2))
+
+ def _get_menu_meta_width(self, max_width, complete_state):
+ """
+ Return the width of the meta column.
+ """
+ if self._show_meta(complete_state):
+ return min(max_width, max(get_cwidth(c.display_meta)
+ for c in complete_state.current_completions) + 2)
+ else:
+ return 0
+
+ def _get_menu_item_tokens(self, completion, is_current_completion, width):
+ if is_current_completion:
+ token = self.token.Completion.Current
+ else:
+ token = self.token.Completion
+
+ text, tw = _trim_text(completion.display, width - 2)
+ padding = ' ' * (width - 2 - tw)
+ return [(token, ' %s%s ' % (text, padding))]
+
+ def _get_menu_item_meta_tokens(self, completion, is_current_completion, width):
+ if is_current_completion:
+ token = self.token.Meta.Current
+ else:
+ token = self.token.Meta
+
+ text, tw = _trim_text(completion.display_meta, width - 2)
+ padding = ' ' * (width - 2 - tw)
+ return [(token, ' %s%s ' % (text, padding))]
+
+ def mouse_handler(self, cli, mouse_event):
+ """
+ Handle mouse events: clicking and scrolling.
+ """
+ b = cli.current_buffer
+
+ if mouse_event.event_type == MouseEventType.MOUSE_UP:
+ # Select completion.
+ b.go_to_completion(mouse_event.position.y)
+ b.complete_state = None
+
+ elif mouse_event.event_type == MouseEventType.SCROLL_DOWN:
+ # Scroll up.
+ b.complete_next(count=3, disable_wrap_around=True)
+
+ elif mouse_event.event_type == MouseEventType.SCROLL_UP:
+ # Scroll down.
+ b.complete_previous(count=3, disable_wrap_around=True)
+
+
+def _trim_text(text, max_width):
+ """
+ Trim the text to `max_width`, append dots when the text is too long.
+ Returns (text, width) tuple.
+ """
+ width = get_cwidth(text)
+
+ # When the text is too wide, trim it.
+ if width > max_width:
+ # When there are no double width characters, just use slice operation.
+ if len(text) == width:
+ trimmed_text = (text[:max(1, max_width-3)] + '...')[:max_width]
+ return trimmed_text, len(trimmed_text)
+
+ # Otherwise, loop until we have the desired width. (Rather
+ # inefficient, but ok for now.)
+ else:
+ trimmed_text = ''
+ for c in text:
+ if get_cwidth(trimmed_text + c) <= max_width - 3:
+ trimmed_text += c
+ trimmed_text += '...'
+
+ return (trimmed_text, get_cwidth(trimmed_text))
+ else:
+ return text, width
+
+
+class CompletionsMenu(ConditionalContainer):
+ def __init__(self, max_height=None, scroll_offset=0, extra_filter=True, display_arrows=False):
+ extra_filter = to_cli_filter(extra_filter)
+ display_arrows = to_cli_filter(display_arrows)
+
+ super(CompletionsMenu, self).__init__(
+ content=Window(
+ content=CompletionsMenuControl(),
+ width=LayoutDimension(min=8),
+ height=LayoutDimension(min=1, max=max_height),
+ scroll_offsets=ScrollOffsets(top=scroll_offset, bottom=scroll_offset),
+ right_margins=[ScrollbarMargin(display_arrows=display_arrows)],
+ dont_extend_width=True,
+ ),
+ # Show when there are completions but not at the point we are
+ # returning the input.
+ filter=HasCompletions() & ~IsDone() & extra_filter)
+
+
+class MultiColumnCompletionMenuControl(UIControl):
+ """
+ Completion menu that displays all the completions in several columns.
+ When there are more completions than space for them to be displayed, an
+ arrow is shown on the left or right side.
+
+ `min_rows` indicates how many rows will be available in any possible case.
+ When this is langer than one, in will try to use less columns and more
+ rows until this value is reached.
+ Be careful passing in a too big value, if less than the given amount of
+ rows are available, more columns would have been required, but
+ `preferred_width` doesn't know about that and reports a too small value.
+ This results in less completions displayed and additional scrolling.
+ (It's a limitation of how the layout engine currently works: first the
+ widths are calculated, then the heights.)
+
+ :param suggested_max_column_width: The suggested max width of a column.
+ The column can still be bigger than this, but if there is place for two
+ columns of this width, we will display two columns. This to avoid that
+ if there is one very wide completion, that it doesn't significantly
+ reduce the amount of columns.
+ """
+ _required_margin = 3 # One extra padding on the right + space for arrows.
+
+ def __init__(self, min_rows=3, suggested_max_column_width=30):
+ assert isinstance(min_rows, int) and min_rows >= 1
+
+ self.min_rows = min_rows
+ self.suggested_max_column_width = suggested_max_column_width
+ self.token = Token.Menu.Completions
+ self.scroll = 0
+
+ # Info of last rendering.
+ self._rendered_rows = 0
+ self._rendered_columns = 0
+ self._total_columns = 0
+ self._render_pos_to_completion = {}
+ self._render_left_arrow = False
+ self._render_right_arrow = False
+ self._render_width = 0
+
+ def reset(self):
+ self.scroll = 0
+
+ def has_focus(self, cli):
+ return False
+
+ def preferred_width(self, cli, max_available_width):
+ """
+ Preferred width: prefer to use at least min_rows, but otherwise as much
+ as possible horizontally.
+ """
+ complete_state = cli.current_buffer.complete_state
+ column_width = self._get_column_width(complete_state)
+ result = int(column_width * math.ceil(len(complete_state.current_completions) / float(self.min_rows)))
+
+ # When the desired width is still more than the maximum available,
+ # reduce by removing columns until we are less than the available
+ # width.
+ while result > column_width and result > max_available_width - self._required_margin:
+ result -= column_width
+ return result + self._required_margin
+
+ def preferred_height(self, cli, width, max_available_height, wrap_lines):
+ """
+ Preferred height: as much as needed in order to display all the completions.
+ """
+ complete_state = cli.current_buffer.complete_state
+ column_width = self._get_column_width(complete_state)
+ column_count = max(1, (width - self._required_margin) // column_width)
+
+ return int(math.ceil(len(complete_state.current_completions) / float(column_count)))
+
+ def create_content(self, cli, width, height):
+ """
+ Create a UIContent object for this menu.
+ """
+ complete_state = cli.current_buffer.complete_state
+ column_width = self._get_column_width(complete_state)
+ self._render_pos_to_completion = {}
+
+ def grouper(n, iterable, fillvalue=None):
+ " grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx "
+ args = [iter(iterable)] * n
+ return zip_longest(fillvalue=fillvalue, *args)
+
+ def is_current_completion(completion):
+ " Returns True when this completion is the currently selected one. "
+ return complete_state.complete_index is not None and c == complete_state.current_completion
+
+ # Space required outside of the regular columns, for displaying the
+ # left and right arrow.
+ HORIZONTAL_MARGIN_REQUIRED = 3
+
+ if complete_state:
+ # There should be at least one column, but it cannot be wider than
+ # the available width.
+ column_width = min(width - HORIZONTAL_MARGIN_REQUIRED, column_width)
+
+ # However, when the columns tend to be very wide, because there are
+ # some very wide entries, shrink it anyway.
+ if column_width > self.suggested_max_column_width:
+ # `column_width` can still be bigger that `suggested_max_column_width`,
+ # but if there is place for two columns, we divide by two.
+ column_width //= (column_width // self.suggested_max_column_width)
+
+ visible_columns = max(1, (width - self._required_margin) // column_width)
+
+ columns_ = list(grouper(height, complete_state.current_completions))
+ rows_ = list(zip(*columns_))
+
+ # Make sure the current completion is always visible: update scroll offset.
+ selected_column = (complete_state.complete_index or 0) // height
+ self.scroll = min(selected_column, max(self.scroll, selected_column - visible_columns + 1))
+
+ render_left_arrow = self.scroll > 0
+ render_right_arrow = self.scroll < len(rows_[0]) - visible_columns
+
+ # Write completions to screen.
+ tokens_for_line = []
+
+ for row_index, row in enumerate(rows_):
+ tokens = []
+ middle_row = row_index == len(rows_) // 2
+
+ # Draw left arrow if we have hidden completions on the left.
+ if render_left_arrow:
+ tokens += [(Token.Scrollbar, '<' if middle_row else ' ')]
+
+ # Draw row content.
+ for column_index, c in enumerate(row[self.scroll:][:visible_columns]):
+ if c is not None:
+ tokens += self._get_menu_item_tokens(c, is_current_completion(c), column_width)
+
+ # Remember render position for mouse click handler.
+ for x in range(column_width):
+ self._render_pos_to_completion[(column_index * column_width + x, row_index)] = c
+ else:
+ tokens += [(self.token.Completion, ' ' * column_width)]
+
+ # Draw trailing padding. (_get_menu_item_tokens only returns padding on the left.)
+ tokens += [(self.token.Completion, ' ')]
+
+ # Draw right arrow if we have hidden completions on the right.
+ if render_right_arrow:
+ tokens += [(Token.Scrollbar, '>' if middle_row else ' ')]
+
+ # Newline.
+ tokens_for_line.append(tokens)
+
+ else:
+ tokens = []
+
+ self._rendered_rows = height
+ self._rendered_columns = visible_columns
+ self._total_columns = len(columns_)
+ self._render_left_arrow = render_left_arrow
+ self._render_right_arrow = render_right_arrow
+ self._render_width = column_width * visible_columns + render_left_arrow + render_right_arrow + 1
+
+ def get_line(i):
+ return tokens_for_line[i]
+
+ return UIContent(get_line=get_line, line_count=len(rows_))
+
+ def _get_column_width(self, complete_state):
+ """
+ Return the width of each column.
+ """
+ return max(get_cwidth(c.display) for c in complete_state.current_completions) + 1
+
+ def _get_menu_item_tokens(self, completion, is_current_completion, width):
+ if is_current_completion:
+ token = self.token.Completion.Current
+ else:
+ token = self.token.Completion
+
+ text, tw = _trim_text(completion.display, width)
+ padding = ' ' * (width - tw - 1)
+
+ return [(token, ' %s%s' % (text, padding))]
+
+ def mouse_handler(self, cli, mouse_event):
+ """
+ Handle scoll and click events.
+ """
+ b = cli.current_buffer
+
+ def scroll_left():
+ b.complete_previous(count=self._rendered_rows, disable_wrap_around=True)
+ self.scroll = max(0, self.scroll - 1)
+
+ def scroll_right():
+ b.complete_next(count=self._rendered_rows, disable_wrap_around=True)
+ self.scroll = min(self._total_columns - self._rendered_columns, self.scroll + 1)
+
+ if mouse_event.event_type == MouseEventType.SCROLL_DOWN:
+ scroll_right()
+
+ elif mouse_event.event_type == MouseEventType.SCROLL_UP:
+ scroll_left()
+
+ elif mouse_event.event_type == MouseEventType.MOUSE_UP:
+ x = mouse_event.position.x
+ y = mouse_event.position.y
+
+ # Mouse click on left arrow.
+ if x == 0:
+ if self._render_left_arrow:
+ scroll_left()
+
+ # Mouse click on right arrow.
+ elif x == self._render_width - 1:
+ if self._render_right_arrow:
+ scroll_right()
+
+ # Mouse click on completion.
+ else:
+ completion = self._render_pos_to_completion.get((x, y))
+ if completion:
+ b.apply_completion(completion)
+
+
+class MultiColumnCompletionsMenu(HSplit):
+ """
+ Container that displays the completions in several columns.
+ When `show_meta` (a :class:`~prompt_toolkit.filters.CLIFilter`) evaluates
+ to True, it shows the meta information at the bottom.
+ """
+ def __init__(self, min_rows=3, suggested_max_column_width=30, show_meta=True, extra_filter=True):
+ show_meta = to_cli_filter(show_meta)
+ extra_filter = to_cli_filter(extra_filter)
+
+ # Display filter: show when there are completions but not at the point
+ # we are returning the input.
+ full_filter = HasCompletions() & ~IsDone() & extra_filter
+
+ any_completion_has_meta = Condition(lambda cli:
+ any(c.display_meta for c in cli.current_buffer.complete_state.current_completions))
+
+ # Create child windows.
+ completions_window = ConditionalContainer(
+ content=Window(
+ content=MultiColumnCompletionMenuControl(
+ min_rows=min_rows, suggested_max_column_width=suggested_max_column_width),
+ width=LayoutDimension(min=8),
+ height=LayoutDimension(min=1)),
+ filter=full_filter)
+
+ meta_window = ConditionalContainer(
+ content=Window(content=_SelectedCompletionMetaControl()),
+ filter=show_meta & full_filter & any_completion_has_meta)
+
+ # Initialise split.
+ super(MultiColumnCompletionsMenu, self).__init__([
+ completions_window,
+ meta_window
+ ])
+
+
+class _SelectedCompletionMetaControl(UIControl):
+ """
+ Control that shows the meta information of the selected token.
+ """
+ def preferred_width(self, cli, max_available_width):
+ """
+ Report the width of the longest meta text as the preferred width of this control.
+
+ It could be that we use less width, but this way, we're sure that the
+ layout doesn't change when we select another completion (E.g. that
+ completions are suddenly shown in more or fewer columns.)
+ """
+ if cli.current_buffer.complete_state:
+ state = cli.current_buffer.complete_state
+ return 2 + max(get_cwidth(c.display_meta) for c in state.current_completions)
+ else:
+ return 0
+
+ def preferred_height(self, cli, width, max_available_height, wrap_lines):
+ return 1
+
+ def create_content(self, cli, width, height):
+ tokens = self._get_tokens(cli)
+
+ def get_line(i):
+ return tokens
+
+ return UIContent(get_line=get_line, line_count=1 if tokens else 0)
+
+ def _get_tokens(self, cli):
+ token = Token.Menu.Completions.MultiColumnMeta
+ state = cli.current_buffer.complete_state
+
+ if state and state.current_completion and state.current_completion.display_meta:
+ return [(token, ' %s ' % state.current_completion.display_meta)]
+
+ return []
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/mouse_handlers.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/mouse_handlers.py
new file mode 100644
index 0000000000..d443bf8315
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/mouse_handlers.py
@@ -0,0 +1,29 @@
+from __future__ import unicode_literals
+
+from itertools import product
+from collections import defaultdict
+
+__all__ = (
+ 'MouseHandlers',
+)
+
+
+class MouseHandlers(object):
+ """
+ Two dimentional raster of callbacks for mouse events.
+ """
+ def __init__(self):
+ def dummy_callback(cli, mouse_event):
+ """
+ :param mouse_event: `MouseEvent` instance.
+ """
+
+ # Map (x,y) tuples to handlers.
+ self.mouse_handlers = defaultdict(lambda: dummy_callback)
+
+ def set_mouse_handler_for_range(self, x_min, x_max, y_min, y_max, handler=None):
+ """
+ Set mouse handler for a region.
+ """
+ for x, y in product(range(x_min, x_max), range(y_min, y_max)):
+ self.mouse_handlers[x,y] = handler
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/processors.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/processors.py
new file mode 100644
index 0000000000..0b8bc9c223
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/processors.py
@@ -0,0 +1,605 @@
+"""
+Processors are little transformation blocks that transform the token list from
+a buffer before the BufferControl will render it to the screen.
+
+They can insert tokens before or after, or highlight fragments by replacing the
+token types.
+"""
+from __future__ import unicode_literals
+from abc import ABCMeta, abstractmethod
+from six import with_metaclass
+from six.moves import range
+
+from prompt_toolkit.cache import SimpleCache
+from prompt_toolkit.document import Document
+from prompt_toolkit.enums import SEARCH_BUFFER
+from prompt_toolkit.filters import to_cli_filter, ViInsertMultipleMode
+from prompt_toolkit.layout.utils import token_list_to_text
+from prompt_toolkit.reactive import Integer
+from prompt_toolkit.token import Token
+
+from .utils import token_list_len, explode_tokens
+
+import re
+
+__all__ = (
+ 'Processor',
+ 'Transformation',
+
+ 'HighlightSearchProcessor',
+ 'HighlightSelectionProcessor',
+ 'PasswordProcessor',
+ 'HighlightMatchingBracketProcessor',
+ 'DisplayMultipleCursors',
+ 'BeforeInput',
+ 'AfterInput',
+ 'AppendAutoSuggestion',
+ 'ConditionalProcessor',
+ 'ShowLeadingWhiteSpaceProcessor',
+ 'ShowTrailingWhiteSpaceProcessor',
+ 'TabsProcessor',
+)
+
+
+class Processor(with_metaclass(ABCMeta, object)):
+ """
+ Manipulate the tokens for a given line in a
+ :class:`~prompt_toolkit.layout.controls.BufferControl`.
+ """
+ @abstractmethod
+ def apply_transformation(self, cli, document, lineno, source_to_display, tokens):
+ """
+ Apply transformation. Returns a :class:`.Transformation` instance.
+
+ :param cli: :class:`.CommandLineInterface` instance.
+ :param lineno: The number of the line to which we apply the processor.
+ :param source_to_display: A function that returns the position in the
+ `tokens` for any position in the source string. (This takes
+ previous processors into account.)
+ :param tokens: List of tokens that we can transform. (Received from the
+ previous processor.)
+ """
+ return Transformation(tokens)
+
+ def has_focus(self, cli):
+ """
+ Processors can override the focus.
+ (Used for the reverse-i-search prefix in DefaultPrompt.)
+ """
+ return False
+
+
+class Transformation(object):
+ """
+ Transformation result, as returned by :meth:`.Processor.apply_transformation`.
+
+ Important: Always make sure that the length of `document.text` is equal to
+ the length of all the text in `tokens`!
+
+ :param tokens: The transformed tokens. To be displayed, or to pass to the
+ next processor.
+ :param source_to_display: Cursor position transformation from original string to
+ transformed string.
+ :param display_to_source: Cursor position transformed from source string to
+ original string.
+ """
+ def __init__(self, tokens, source_to_display=None, display_to_source=None):
+ self.tokens = tokens
+ self.source_to_display = source_to_display or (lambda i: i)
+ self.display_to_source = display_to_source or (lambda i: i)
+
+
+class HighlightSearchProcessor(Processor):
+ """
+ Processor that highlights search matches in the document.
+ Note that this doesn't support multiline search matches yet.
+
+ :param preview_search: A Filter; when active it indicates that we take
+ the search text in real time while the user is typing, instead of the
+ last active search state.
+ """
+ def __init__(self, preview_search=False, search_buffer_name=SEARCH_BUFFER,
+ get_search_state=None):
+ self.preview_search = to_cli_filter(preview_search)
+ self.search_buffer_name = search_buffer_name
+ self.get_search_state = get_search_state or (lambda cli: cli.search_state)
+
+ def _get_search_text(self, cli):
+ """
+ The text we are searching for.
+ """
+ # When the search buffer has focus, take that text.
+ if self.preview_search(cli) and cli.buffers[self.search_buffer_name].text:
+ return cli.buffers[self.search_buffer_name].text
+ # Otherwise, take the text of the last active search.
+ else:
+ return self.get_search_state(cli).text
+
+ def apply_transformation(self, cli, document, lineno, source_to_display, tokens):
+ search_text = self._get_search_text(cli)
+ searchmatch_current_token = (':', ) + Token.SearchMatch.Current
+ searchmatch_token = (':', ) + Token.SearchMatch
+
+ if search_text and not cli.is_returning:
+ # For each search match, replace the Token.
+ line_text = token_list_to_text(tokens)
+ tokens = explode_tokens(tokens)
+
+ flags = re.IGNORECASE if cli.is_ignoring_case else 0
+
+ # Get cursor column.
+ if document.cursor_position_row == lineno:
+ cursor_column = source_to_display(document.cursor_position_col)
+ else:
+ cursor_column = None
+
+ for match in re.finditer(re.escape(search_text), line_text, flags=flags):
+ if cursor_column is not None:
+ on_cursor = match.start() <= cursor_column < match.end()
+ else:
+ on_cursor = False
+
+ for i in range(match.start(), match.end()):
+ old_token, text = tokens[i]
+ if on_cursor:
+ tokens[i] = (old_token + searchmatch_current_token, tokens[i][1])
+ else:
+ tokens[i] = (old_token + searchmatch_token, tokens[i][1])
+
+ return Transformation(tokens)
+
+
+class HighlightSelectionProcessor(Processor):
+ """
+ Processor that highlights the selection in the document.
+ """
+ def apply_transformation(self, cli, document, lineno, source_to_display, tokens):
+ selected_token = (':', ) + Token.SelectedText
+
+ # In case of selection, highlight all matches.
+ selection_at_line = document.selection_range_at_line(lineno)
+
+ if selection_at_line:
+ from_, to = selection_at_line
+ from_ = source_to_display(from_)
+ to = source_to_display(to)
+
+ tokens = explode_tokens(tokens)
+
+ if from_ == 0 and to == 0 and len(tokens) == 0:
+ # When this is an empty line, insert a space in order to
+ # visualiase the selection.
+ return Transformation([(Token.SelectedText, ' ')])
+ else:
+ for i in range(from_, to + 1):
+ if i < len(tokens):
+ old_token, old_text = tokens[i]
+ tokens[i] = (old_token + selected_token, old_text)
+
+ return Transformation(tokens)
+
+
+class PasswordProcessor(Processor):
+ """
+ Processor that turns masks the input. (For passwords.)
+
+ :param char: (string) Character to be used. "*" by default.
+ """
+ def __init__(self, char='*'):
+ self.char = char
+
+ def apply_transformation(self, cli, document, lineno, source_to_display, tokens):
+ tokens = [(token, self.char * len(text)) for token, text in tokens]
+ return Transformation(tokens)
+
+
+class HighlightMatchingBracketProcessor(Processor):
+ """
+ When the cursor is on or right after a bracket, it highlights the matching
+ bracket.
+
+ :param max_cursor_distance: Only highlight matching brackets when the
+ cursor is within this distance. (From inside a `Processor`, we can't
+ know which lines will be visible on the screen. But we also don't want
+ to scan the whole document for matching brackets on each key press, so
+ we limit to this value.)
+ """
+ _closing_braces = '])}>'
+
+ def __init__(self, chars='[](){}<>', max_cursor_distance=1000):
+ self.chars = chars
+ self.max_cursor_distance = max_cursor_distance
+
+ self._positions_cache = SimpleCache(maxsize=8)
+
+ def _get_positions_to_highlight(self, document):
+ """
+ Return a list of (row, col) tuples that need to be highlighted.
+ """
+ # Try for the character under the cursor.
+ if document.current_char and document.current_char in self.chars:
+ pos = document.find_matching_bracket_position(
+ start_pos=document.cursor_position - self.max_cursor_distance,
+ end_pos=document.cursor_position + self.max_cursor_distance)
+
+ # Try for the character before the cursor.
+ elif (document.char_before_cursor and document.char_before_cursor in
+ self._closing_braces and document.char_before_cursor in self.chars):
+ document = Document(document.text, document.cursor_position - 1)
+
+ pos = document.find_matching_bracket_position(
+ start_pos=document.cursor_position - self.max_cursor_distance,
+ end_pos=document.cursor_position + self.max_cursor_distance)
+ else:
+ pos = None
+
+ # Return a list of (row, col) tuples that need to be highlighted.
+ if pos:
+ pos += document.cursor_position # pos is relative.
+ row, col = document.translate_index_to_position(pos)
+ return [(row, col), (document.cursor_position_row, document.cursor_position_col)]
+ else:
+ return []
+
+ def apply_transformation(self, cli, document, lineno, source_to_display, tokens):
+ # Get the highlight positions.
+ key = (cli.render_counter, document.text, document.cursor_position)
+ positions = self._positions_cache.get(
+ key, lambda: self._get_positions_to_highlight(document))
+
+ # Apply if positions were found at this line.
+ if positions:
+ for row, col in positions:
+ if row == lineno:
+ col = source_to_display(col)
+ tokens = explode_tokens(tokens)
+ token, text = tokens[col]
+
+ if col == document.cursor_position_col:
+ token += (':', ) + Token.MatchingBracket.Cursor
+ else:
+ token += (':', ) + Token.MatchingBracket.Other
+
+ tokens[col] = (token, text)
+
+ return Transformation(tokens)
+
+
+class DisplayMultipleCursors(Processor):
+ """
+ When we're in Vi block insert mode, display all the cursors.
+ """
+ _insert_multiple = ViInsertMultipleMode()
+
+ def __init__(self, buffer_name):
+ self.buffer_name = buffer_name
+
+ def apply_transformation(self, cli, document, lineno, source_to_display, tokens):
+ buff = cli.buffers[self.buffer_name]
+
+ if self._insert_multiple(cli):
+ positions = buff.multiple_cursor_positions
+ tokens = explode_tokens(tokens)
+
+ # If any cursor appears on the current line, highlight that.
+ start_pos = document.translate_row_col_to_index(lineno, 0)
+ end_pos = start_pos + len(document.lines[lineno])
+
+ token_suffix = (':', ) + Token.MultipleCursors.Cursor
+
+ for p in positions:
+ if start_pos <= p < end_pos:
+ column = source_to_display(p - start_pos)
+
+ # Replace token.
+ token, text = tokens[column]
+ token += token_suffix
+ tokens[column] = (token, text)
+ elif p == end_pos:
+ tokens.append((token_suffix, ' '))
+
+ return Transformation(tokens)
+ else:
+ return Transformation(tokens)
+
+
+class BeforeInput(Processor):
+ """
+ Insert tokens before the input.
+
+ :param get_tokens: Callable that takes a
+ :class:`~prompt_toolkit.interface.CommandLineInterface` and returns the
+ list of tokens to be inserted.
+ """
+ def __init__(self, get_tokens):
+ assert callable(get_tokens)
+ self.get_tokens = get_tokens
+
+ def apply_transformation(self, cli, document, lineno, source_to_display, tokens):
+ if lineno == 0:
+ tokens_before = self.get_tokens(cli)
+ tokens = tokens_before + tokens
+
+ shift_position = token_list_len(tokens_before)
+ source_to_display = lambda i: i + shift_position
+ display_to_source = lambda i: i - shift_position
+ else:
+ source_to_display = None
+ display_to_source = None
+
+ return Transformation(tokens, source_to_display=source_to_display,
+ display_to_source=display_to_source)
+
+ @classmethod
+ def static(cls, text, token=Token):
+ """
+ Create a :class:`.BeforeInput` instance that always inserts the same
+ text.
+ """
+ def get_static_tokens(cli):
+ return [(token, text)]
+ return cls(get_static_tokens)
+
+ def __repr__(self):
+ return '%s(get_tokens=%r)' % (
+ self.__class__.__name__, self.get_tokens)
+
+
+class AfterInput(Processor):
+ """
+ Insert tokens after the input.
+
+ :param get_tokens: Callable that takes a
+ :class:`~prompt_toolkit.interface.CommandLineInterface` and returns the
+ list of tokens to be appended.
+ """
+ def __init__(self, get_tokens):
+ assert callable(get_tokens)
+ self.get_tokens = get_tokens
+
+ def apply_transformation(self, cli, document, lineno, source_to_display, tokens):
+ # Insert tokens after the last line.
+ if lineno == document.line_count - 1:
+ return Transformation(tokens=tokens + self.get_tokens(cli))
+ else:
+ return Transformation(tokens=tokens)
+
+ @classmethod
+ def static(cls, text, token=Token):
+ """
+ Create a :class:`.AfterInput` instance that always inserts the same
+ text.
+ """
+ def get_static_tokens(cli):
+ return [(token, text)]
+ return cls(get_static_tokens)
+
+ def __repr__(self):
+ return '%s(get_tokens=%r)' % (
+ self.__class__.__name__, self.get_tokens)
+
+
+class AppendAutoSuggestion(Processor):
+ """
+ Append the auto suggestion to the input.
+ (The user can then press the right arrow the insert the suggestion.)
+
+ :param buffer_name: The name of the buffer from where we should take the
+ auto suggestion. If not given, we take the current buffer.
+ """
+ def __init__(self, buffer_name=None, token=Token.AutoSuggestion):
+ self.buffer_name = buffer_name
+ self.token = token
+
+ def _get_buffer(self, cli):
+ if self.buffer_name:
+ return cli.buffers[self.buffer_name]
+ else:
+ return cli.current_buffer
+
+ def apply_transformation(self, cli, document, lineno, source_to_display, tokens):
+ # Insert tokens after the last line.
+ if lineno == document.line_count - 1:
+ buffer = self._get_buffer(cli)
+
+ if buffer.suggestion and buffer.document.is_cursor_at_the_end:
+ suggestion = buffer.suggestion.text
+ else:
+ suggestion = ''
+
+ return Transformation(tokens=tokens + [(self.token, suggestion)])
+ else:
+ return Transformation(tokens=tokens)
+
+
+class ShowLeadingWhiteSpaceProcessor(Processor):
+ """
+ Make leading whitespace visible.
+
+ :param get_char: Callable that takes a :class:`CommandLineInterface`
+ instance and returns one character.
+ :param token: Token to be used.
+ """
+ def __init__(self, get_char=None, token=Token.LeadingWhiteSpace):
+ assert get_char is None or callable(get_char)
+
+ if get_char is None:
+ def get_char(cli):
+ if '\xb7'.encode(cli.output.encoding(), 'replace') == b'?':
+ return '.'
+ else:
+ return '\xb7'
+
+ self.token = token
+ self.get_char = get_char
+
+ def apply_transformation(self, cli, document, lineno, source_to_display, tokens):
+ # Walk through all te tokens.
+ if tokens and token_list_to_text(tokens).startswith(' '):
+ t = (self.token, self.get_char(cli))
+ tokens = explode_tokens(tokens)
+
+ for i in range(len(tokens)):
+ if tokens[i][1] == ' ':
+ tokens[i] = t
+ else:
+ break
+
+ return Transformation(tokens)
+
+
+class ShowTrailingWhiteSpaceProcessor(Processor):
+ """
+ Make trailing whitespace visible.
+
+ :param get_char: Callable that takes a :class:`CommandLineInterface`
+ instance and returns one character.
+ :param token: Token to be used.
+ """
+ def __init__(self, get_char=None, token=Token.TrailingWhiteSpace):
+ assert get_char is None or callable(get_char)
+
+ if get_char is None:
+ def get_char(cli):
+ if '\xb7'.encode(cli.output.encoding(), 'replace') == b'?':
+ return '.'
+ else:
+ return '\xb7'
+
+ self.token = token
+ self.get_char = get_char
+
+
+ def apply_transformation(self, cli, document, lineno, source_to_display, tokens):
+ if tokens and tokens[-1][1].endswith(' '):
+ t = (self.token, self.get_char(cli))
+ tokens = explode_tokens(tokens)
+
+ # Walk backwards through all te tokens and replace whitespace.
+ for i in range(len(tokens) - 1, -1, -1):
+ char = tokens[i][1]
+ if char == ' ':
+ tokens[i] = t
+ else:
+ break
+
+ return Transformation(tokens)
+
+
+class TabsProcessor(Processor):
+ """
+ Render tabs as spaces (instead of ^I) or make them visible (for instance,
+ by replacing them with dots.)
+
+ :param tabstop: (Integer) Horizontal space taken by a tab.
+ :param get_char1: Callable that takes a `CommandLineInterface` and return a
+ character (text of length one). This one is used for the first space
+ taken by the tab.
+ :param get_char2: Like `get_char1`, but for the rest of the space.
+ """
+ def __init__(self, tabstop=4, get_char1=None, get_char2=None, token=Token.Tab):
+ assert isinstance(tabstop, Integer)
+ assert get_char1 is None or callable(get_char1)
+ assert get_char2 is None or callable(get_char2)
+
+ self.get_char1 = get_char1 or get_char2 or (lambda cli: '|')
+ self.get_char2 = get_char2 or get_char1 or (lambda cli: '\u2508')
+ self.tabstop = tabstop
+ self.token = token
+
+ def apply_transformation(self, cli, document, lineno, source_to_display, tokens):
+ tabstop = int(self.tabstop)
+ token = self.token
+
+ # Create separator for tabs.
+ separator1 = self.get_char1(cli)
+ separator2 = self.get_char2(cli)
+
+ # Transform tokens.
+ tokens = explode_tokens(tokens)
+
+ position_mappings = {}
+ result_tokens = []
+ pos = 0
+
+ for i, token_and_text in enumerate(tokens):
+ position_mappings[i] = pos
+
+ if token_and_text[1] == '\t':
+ # Calculate how many characters we have to insert.
+ count = tabstop - (pos % tabstop)
+ if count == 0:
+ count = tabstop
+
+ # Insert tab.
+ result_tokens.append((token, separator1))
+ result_tokens.append((token, separator2 * (count - 1)))
+ pos += count
+ else:
+ result_tokens.append(token_and_text)
+ pos += 1
+
+ position_mappings[len(tokens)] = pos
+
+ def source_to_display(from_position):
+ " Maps original cursor position to the new one. "
+ return position_mappings[from_position]
+
+ def display_to_source(display_pos):
+ " Maps display cursor position to the original one. "
+ position_mappings_reversed = dict((v, k) for k, v in position_mappings.items())
+
+ while display_pos >= 0:
+ try:
+ return position_mappings_reversed[display_pos]
+ except KeyError:
+ display_pos -= 1
+ return 0
+
+ return Transformation(
+ result_tokens,
+ source_to_display=source_to_display,
+ display_to_source=display_to_source)
+
+
+class ConditionalProcessor(Processor):
+ """
+ Processor that applies another processor, according to a certain condition.
+ Example::
+
+ # Create a function that returns whether or not the processor should
+ # currently be applied.
+ def highlight_enabled(cli):
+ return true_or_false
+
+ # Wrapt it in a `ConditionalProcessor` for usage in a `BufferControl`.
+ BufferControl(input_processors=[
+ ConditionalProcessor(HighlightSearchProcessor(),
+ Condition(highlight_enabled))])
+
+ :param processor: :class:`.Processor` instance.
+ :param filter: :class:`~prompt_toolkit.filters.CLIFilter` instance.
+ """
+ def __init__(self, processor, filter):
+ assert isinstance(processor, Processor)
+
+ self.processor = processor
+ self.filter = to_cli_filter(filter)
+
+ def apply_transformation(self, cli, document, lineno, source_to_display, tokens):
+ # Run processor when enabled.
+ if self.filter(cli):
+ return self.processor.apply_transformation(
+ cli, document, lineno, source_to_display, tokens)
+ else:
+ return Transformation(tokens)
+
+ def has_focus(self, cli):
+ if self.filter(cli):
+ return self.processor.has_focus(cli)
+ else:
+ return False
+
+ def __repr__(self):
+ return '%s(processor=%r, filter=%r)' % (
+ self.__class__.__name__, self.processor, self.filter)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/prompt.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/prompt.py
new file mode 100644
index 0000000000..7d00ec513e
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/prompt.py
@@ -0,0 +1,111 @@
+from __future__ import unicode_literals
+
+from six import text_type
+
+from prompt_toolkit.enums import IncrementalSearchDirection, SEARCH_BUFFER
+from prompt_toolkit.token import Token
+
+from .utils import token_list_len
+from .processors import Processor, Transformation
+
+__all__ = (
+ 'DefaultPrompt',
+)
+
+
+class DefaultPrompt(Processor):
+ """
+ Default prompt. This one shows the 'arg' and reverse search like
+ Bash/readline normally do.
+
+ There are two ways to instantiate a ``DefaultPrompt``. For a prompt
+ with a static message, do for instance::
+
+ prompt = DefaultPrompt.from_message('prompt> ')
+
+ For a dynamic prompt, generated from a token list function::
+
+ def get_tokens(cli):
+ return [(Token.A, 'text'), (Token.B, 'text2')]
+
+ prompt = DefaultPrompt(get_tokens)
+ """
+ def __init__(self, get_tokens):
+ assert callable(get_tokens)
+ self.get_tokens = get_tokens
+
+ @classmethod
+ def from_message(cls, message='> '):
+ """
+ Create a default prompt with a static message text.
+ """
+ assert isinstance(message, text_type)
+
+ def get_message_tokens(cli):
+ return [(Token.Prompt, message)]
+ return cls(get_message_tokens)
+
+ def apply_transformation(self, cli, document, lineno, source_to_display, tokens):
+ # Get text before cursor.
+ if cli.is_searching:
+ before = _get_isearch_tokens(cli)
+
+ elif cli.input_processor.arg is not None:
+ before = _get_arg_tokens(cli)
+
+ else:
+ before = self.get_tokens(cli)
+
+ # Insert before buffer text.
+ shift_position = token_list_len(before)
+
+ # Only show the prompt before the first line. For the following lines,
+ # only indent using spaces.
+ if lineno != 0:
+ before = [(Token.Prompt, ' ' * shift_position)]
+
+ return Transformation(
+ tokens=before + tokens,
+ source_to_display=lambda i: i + shift_position,
+ display_to_source=lambda i: i - shift_position)
+
+ def has_focus(self, cli):
+ # Obtain focus when the CLI is searching.
+
+ # Usually, when using this `DefaultPrompt`, we don't have a
+ # `BufferControl` instance that displays the content of the search
+ # buffer. Instead the search text is displayed before the current text.
+ # So, we can still show the cursor here, while it's actually not this
+ # buffer that's focussed.
+ return cli.is_searching
+
+
+def _get_isearch_tokens(cli):
+ def before():
+ if cli.search_state.direction == IncrementalSearchDirection.BACKWARD:
+ text = 'reverse-i-search'
+ else:
+ text = 'i-search'
+
+ return [(Token.Prompt.Search, '(%s)`' % text)]
+
+ def text():
+ return [(Token.Prompt.Search.Text, cli.buffers[SEARCH_BUFFER].text)]
+
+ def after():
+ return [(Token.Prompt.Search, '`: ')]
+
+ return before() + text() + after()
+
+
+def _get_arg_tokens(cli):
+ """
+ Tokens for the arg-prompt.
+ """
+ arg = cli.input_processor.arg
+
+ return [
+ (Token.Prompt.Arg, '(arg: '),
+ (Token.Prompt.Arg.Text, str(arg)),
+ (Token.Prompt.Arg, ') '),
+ ]
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/screen.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/screen.py
new file mode 100644
index 0000000000..95561f5de7
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/screen.py
@@ -0,0 +1,151 @@
+from __future__ import unicode_literals
+
+from prompt_toolkit.cache import FastDictCache
+from prompt_toolkit.token import Token
+from prompt_toolkit.utils import get_cwidth
+
+from collections import defaultdict, namedtuple
+
+__all__ = (
+ 'Point',
+ 'Size',
+ 'Screen',
+ 'Char',
+)
+
+
+Point = namedtuple('Point', 'y x')
+Size = namedtuple('Size', 'rows columns')
+
+
+class Char(object):
+ """
+ Represent a single character in a :class:`.Screen`.
+
+ This should be considered immutable.
+ """
+ __slots__ = ('char', 'token', 'width')
+
+ # If we end up having one of these special control sequences in the input string,
+ # we should display them as follows:
+ # Usually this happens after a "quoted insert".
+ display_mappings = {
+ '\x00': '^@', # Control space
+ '\x01': '^A',
+ '\x02': '^B',
+ '\x03': '^C',
+ '\x04': '^D',
+ '\x05': '^E',
+ '\x06': '^F',
+ '\x07': '^G',
+ '\x08': '^H',
+ '\x09': '^I',
+ '\x0a': '^J',
+ '\x0b': '^K',
+ '\x0c': '^L',
+ '\x0d': '^M',
+ '\x0e': '^N',
+ '\x0f': '^O',
+ '\x10': '^P',
+ '\x11': '^Q',
+ '\x12': '^R',
+ '\x13': '^S',
+ '\x14': '^T',
+ '\x15': '^U',
+ '\x16': '^V',
+ '\x17': '^W',
+ '\x18': '^X',
+ '\x19': '^Y',
+ '\x1a': '^Z',
+ '\x1b': '^[', # Escape
+ '\x1c': '^\\',
+ '\x1d': '^]',
+ '\x1f': '^_',
+ '\x7f': '^?', # Backspace
+ }
+
+ def __init__(self, char=' ', token=Token):
+ # If this character has to be displayed otherwise, take that one.
+ char = self.display_mappings.get(char, char)
+
+ self.char = char
+ self.token = token
+
+ # Calculate width. (We always need this, so better to store it directly
+ # as a member for performance.)
+ self.width = get_cwidth(char)
+
+ def __eq__(self, other):
+ return self.char == other.char and self.token == other.token
+
+ def __ne__(self, other):
+ # Not equal: We don't do `not char.__eq__` here, because of the
+ # performance of calling yet another function.
+ return self.char != other.char or self.token != other.token
+
+ def __repr__(self):
+ return '%s(%r, %r)' % (self.__class__.__name__, self.char, self.token)
+
+
+_CHAR_CACHE = FastDictCache(Char, size=1000 * 1000)
+Transparent = Token.Transparent
+
+
+class Screen(object):
+ """
+ Two dimentional buffer of :class:`.Char` instances.
+ """
+ def __init__(self, default_char=None, initial_width=0, initial_height=0):
+ if default_char is None:
+ default_char = _CHAR_CACHE[' ', Transparent]
+
+ self.data_buffer = defaultdict(lambda: defaultdict(lambda: default_char))
+
+ #: Escape sequences to be injected.
+ self.zero_width_escapes = defaultdict(lambda: defaultdict(lambda: ''))
+
+ #: Position of the cursor.
+ self.cursor_position = Point(y=0, x=0)
+
+ #: Visibility of the cursor.
+ self.show_cursor = True
+
+ #: (Optional) Where to position the menu. E.g. at the start of a completion.
+ #: (We can't use the cursor position, because we don't want the
+ #: completion menu to change its position when we browse through all the
+ #: completions.)
+ self.menu_position = None
+
+ #: Currently used width/height of the screen. This will increase when
+ #: data is written to the screen.
+ self.width = initial_width or 0
+ self.height = initial_height or 0
+
+ def replace_all_tokens(self, token):
+ """
+ For all the characters in the screen. Set the token to the given `token`.
+ """
+ b = self.data_buffer
+
+ for y, row in b.items():
+ for x, char in row.items():
+ b[y][x] = _CHAR_CACHE[char.char, token]
+
+
+class WritePosition(object):
+ def __init__(self, xpos, ypos, width, height, extended_height=None):
+ assert height >= 0
+ assert extended_height is None or extended_height >= 0
+ assert width >= 0
+ # xpos and ypos can be negative. (A float can be partially visible.)
+
+ self.xpos = xpos
+ self.ypos = ypos
+ self.width = width
+ self.height = height
+ self.extended_height = extended_height or height
+
+ def __repr__(self):
+ return '%s(%r, %r, %r, %r, %r)' % (
+ self.__class__.__name__,
+ self.xpos, self.ypos, self.width, self.height, self.extended_height)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/toolbars.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/toolbars.py
new file mode 100644
index 0000000000..2e77c2fa16
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/toolbars.py
@@ -0,0 +1,209 @@
+from __future__ import unicode_literals
+
+from ..enums import IncrementalSearchDirection
+
+from .processors import BeforeInput
+
+from .lexers import SimpleLexer
+from .dimension import LayoutDimension
+from .controls import BufferControl, TokenListControl, UIControl, UIContent
+from .containers import Window, ConditionalContainer
+from .screen import Char
+from .utils import token_list_len
+from prompt_toolkit.enums import SEARCH_BUFFER, SYSTEM_BUFFER
+from prompt_toolkit.filters import HasFocus, HasArg, HasCompletions, HasValidationError, HasSearch, Always, IsDone
+from prompt_toolkit.token import Token
+
+__all__ = (
+ 'TokenListToolbar',
+ 'ArgToolbar',
+ 'CompletionsToolbar',
+ 'SearchToolbar',
+ 'SystemToolbar',
+ 'ValidationToolbar',
+)
+
+
+class TokenListToolbar(ConditionalContainer):
+ def __init__(self, get_tokens, filter=Always(), **kw):
+ super(TokenListToolbar, self).__init__(
+ content=Window(
+ TokenListControl(get_tokens, **kw),
+ height=LayoutDimension.exact(1)),
+ filter=filter)
+
+
+class SystemToolbarControl(BufferControl):
+ def __init__(self):
+ token = Token.Toolbar.System
+
+ super(SystemToolbarControl, self).__init__(
+ buffer_name=SYSTEM_BUFFER,
+ default_char=Char(token=token),
+ lexer=SimpleLexer(token=token.Text),
+ input_processors=[BeforeInput.static('Shell command: ', token)],)
+
+
+class SystemToolbar(ConditionalContainer):
+ def __init__(self):
+ super(SystemToolbar, self).__init__(
+ content=Window(
+ SystemToolbarControl(),
+ height=LayoutDimension.exact(1)),
+ filter=HasFocus(SYSTEM_BUFFER) & ~IsDone())
+
+
+class ArgToolbarControl(TokenListControl):
+ def __init__(self):
+ def get_tokens(cli):
+ arg = cli.input_processor.arg
+ if arg == '-':
+ arg = '-1'
+
+ return [
+ (Token.Toolbar.Arg, 'Repeat: '),
+ (Token.Toolbar.Arg.Text, arg),
+ ]
+
+ super(ArgToolbarControl, self).__init__(get_tokens)
+
+
+class ArgToolbar(ConditionalContainer):
+ def __init__(self):
+ super(ArgToolbar, self).__init__(
+ content=Window(
+ ArgToolbarControl(),
+ height=LayoutDimension.exact(1)),
+ filter=HasArg())
+
+
+class SearchToolbarControl(BufferControl):
+ """
+ :param vi_mode: Display '/' and '?' instead of I-search.
+ """
+ def __init__(self, vi_mode=False):
+ token = Token.Toolbar.Search
+
+ def get_before_input(cli):
+ if not cli.is_searching:
+ text = ''
+ elif cli.search_state.direction == IncrementalSearchDirection.BACKWARD:
+ text = ('?' if vi_mode else 'I-search backward: ')
+ else:
+ text = ('/' if vi_mode else 'I-search: ')
+
+ return [(token, text)]
+
+ super(SearchToolbarControl, self).__init__(
+ buffer_name=SEARCH_BUFFER,
+ input_processors=[BeforeInput(get_before_input)],
+ default_char=Char(token=token),
+ lexer=SimpleLexer(token=token.Text))
+
+
+class SearchToolbar(ConditionalContainer):
+ def __init__(self, vi_mode=False):
+ super(SearchToolbar, self).__init__(
+ content=Window(
+ SearchToolbarControl(vi_mode=vi_mode),
+ height=LayoutDimension.exact(1)),
+ filter=HasSearch() & ~IsDone())
+
+
+class CompletionsToolbarControl(UIControl):
+ token = Token.Toolbar.Completions
+
+ def create_content(self, cli, width, height):
+ complete_state = cli.current_buffer.complete_state
+ if complete_state:
+ completions = complete_state.current_completions
+ index = complete_state.complete_index # Can be None!
+
+ # Width of the completions without the left/right arrows in the margins.
+ content_width = width - 6
+
+ # Booleans indicating whether we stripped from the left/right
+ cut_left = False
+ cut_right = False
+
+ # Create Menu content.
+ tokens = []
+
+ for i, c in enumerate(completions):
+ # When there is no more place for the next completion
+ if token_list_len(tokens) + len(c.display) >= content_width:
+ # If the current one was not yet displayed, page to the next sequence.
+ if i <= (index or 0):
+ tokens = []
+ cut_left = True
+ # If the current one is visible, stop here.
+ else:
+ cut_right = True
+ break
+
+ tokens.append((self.token.Completion.Current if i == index else self.token.Completion, c.display))
+ tokens.append((self.token, ' '))
+
+ # Extend/strip until the content width.
+ tokens.append((self.token, ' ' * (content_width - token_list_len(tokens))))
+ tokens = tokens[:content_width]
+
+ # Return tokens
+ all_tokens = [
+ (self.token, ' '),
+ (self.token.Arrow, '<' if cut_left else ' '),
+ (self.token, ' '),
+ ] + tokens + [
+ (self.token, ' '),
+ (self.token.Arrow, '>' if cut_right else ' '),
+ (self.token, ' '),
+ ]
+ else:
+ all_tokens = []
+
+ def get_line(i):
+ return all_tokens
+
+ return UIContent(get_line=get_line, line_count=1)
+
+
+class CompletionsToolbar(ConditionalContainer):
+ def __init__(self, extra_filter=Always()):
+ super(CompletionsToolbar, self).__init__(
+ content=Window(
+ CompletionsToolbarControl(),
+ height=LayoutDimension.exact(1)),
+ filter=HasCompletions() & ~IsDone() & extra_filter)
+
+
+class ValidationToolbarControl(TokenListControl):
+ def __init__(self, show_position=False):
+ token = Token.Toolbar.Validation
+
+ def get_tokens(cli):
+ buffer = cli.current_buffer
+
+ if buffer.validation_error:
+ row, column = buffer.document.translate_index_to_position(
+ buffer.validation_error.cursor_position)
+
+ if show_position:
+ text = '%s (line=%s column=%s)' % (
+ buffer.validation_error.message, row + 1, column + 1)
+ else:
+ text = buffer.validation_error.message
+
+ return [(token, text)]
+ else:
+ return []
+
+ super(ValidationToolbarControl, self).__init__(get_tokens)
+
+
+class ValidationToolbar(ConditionalContainer):
+ def __init__(self, show_position=False):
+ super(ValidationToolbar, self).__init__(
+ content=Window(
+ ValidationToolbarControl(show_position=show_position),
+ height=LayoutDimension.exact(1)),
+ filter=HasValidationError() & ~IsDone())
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/utils.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/utils.py
new file mode 100644
index 0000000000..a4fb7ed0f5
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/layout/utils.py
@@ -0,0 +1,181 @@
+from __future__ import unicode_literals
+
+from prompt_toolkit.utils import get_cwidth
+from prompt_toolkit.token import Token
+
+__all__ = (
+ 'token_list_len',
+ 'token_list_width',
+ 'token_list_to_text',
+ 'explode_tokens',
+ 'split_lines',
+ 'find_window_for_buffer_name',
+)
+
+
+def token_list_len(tokenlist):
+ """
+ Return the amount of characters in this token list.
+
+ :param tokenlist: List of (token, text) or (token, text, mouse_handler)
+ tuples.
+ """
+ ZeroWidthEscape = Token.ZeroWidthEscape
+ return sum(len(item[1]) for item in tokenlist if item[0] != ZeroWidthEscape)
+
+
+def token_list_width(tokenlist):
+ """
+ Return the character width of this token list.
+ (Take double width characters into account.)
+
+ :param tokenlist: List of (token, text) or (token, text, mouse_handler)
+ tuples.
+ """
+ ZeroWidthEscape = Token.ZeroWidthEscape
+ return sum(get_cwidth(c) for item in tokenlist for c in item[1] if item[0] != ZeroWidthEscape)
+
+
+def token_list_to_text(tokenlist):
+ """
+ Concatenate all the text parts again.
+ """
+ ZeroWidthEscape = Token.ZeroWidthEscape
+ return ''.join(item[1] for item in tokenlist if item[0] != ZeroWidthEscape)
+
+
+def iter_token_lines(tokenlist):
+ """
+ Iterator that yields tokenlists for each line.
+ """
+ line = []
+ for token, c in explode_tokens(tokenlist):
+ line.append((token, c))
+
+ if c == '\n':
+ yield line
+ line = []
+
+ yield line
+
+
+def split_lines(tokenlist):
+ """
+ Take a single list of (Token, text) tuples and yield one such list for each
+ line. Just like str.split, this will yield at least one item.
+
+ :param tokenlist: List of (token, text) or (token, text, mouse_handler)
+ tuples.
+ """
+ line = []
+
+ for item in tokenlist:
+ # For (token, text) tuples.
+ if len(item) == 2:
+ token, string = item
+ parts = string.split('\n')
+
+ for part in parts[:-1]:
+ if part:
+ line.append((token, part))
+ yield line
+ line = []
+
+ line.append((token, parts[-1]))
+ # Note that parts[-1] can be empty, and that's fine. It happens
+ # in the case of [(Token.SetCursorPosition, '')].
+
+ # For (token, text, mouse_handler) tuples.
+ # I know, partly copy/paste, but understandable and more efficient
+ # than many tests.
+ else:
+ token, string, mouse_handler = item
+ parts = string.split('\n')
+
+ for part in parts[:-1]:
+ if part:
+ line.append((token, part, mouse_handler))
+ yield line
+ line = []
+
+ line.append((token, parts[-1], mouse_handler))
+
+ # Always yield the last line, even when this is an empty line. This ensures
+ # that when `tokenlist` ends with a newline character, an additional empty
+ # line is yielded. (Otherwise, there's no way to differentiate between the
+ # cases where `tokenlist` does and doesn't end with a newline.)
+ yield line
+
+
+class _ExplodedList(list):
+ """
+ Wrapper around a list, that marks it as 'exploded'.
+
+ As soon as items are added or the list is extended, the new items are
+ automatically exploded as well.
+ """
+ def __init__(self, *a, **kw):
+ super(_ExplodedList, self).__init__(*a, **kw)
+ self.exploded = True
+
+ def append(self, item):
+ self.extend([item])
+
+ def extend(self, lst):
+ super(_ExplodedList, self).extend(explode_tokens(lst))
+
+ def insert(self, index, item):
+ raise NotImplementedError # TODO
+
+ # TODO: When creating a copy() or [:], return also an _ExplodedList.
+
+ def __setitem__(self, index, value):
+ """
+ Ensure that when `(Token, 'long string')` is set, the string will be
+ exploded.
+ """
+ if not isinstance(index, slice):
+ index = slice(index, index + 1)
+ value = explode_tokens([value])
+ super(_ExplodedList, self).__setitem__(index, value)
+
+
+def explode_tokens(tokenlist):
+ """
+ Turn a list of (token, text) tuples into another list where each string is
+ exactly one character.
+
+ It should be fine to call this function several times. Calling this on a
+ list that is already exploded, is a null operation.
+
+ :param tokenlist: List of (token, text) tuples.
+ """
+ # When the tokenlist is already exploded, don't explode again.
+ if getattr(tokenlist, 'exploded', False):
+ return tokenlist
+
+ result = []
+
+ for token, string in tokenlist:
+ for c in string:
+ result.append((token, c))
+
+ return _ExplodedList(result)
+
+
+def find_window_for_buffer_name(cli, buffer_name):
+ """
+ Look for a :class:`~prompt_toolkit.layout.containers.Window` in the Layout
+ that contains the :class:`~prompt_toolkit.layout.controls.BufferControl`
+ for the given buffer and return it. If no such Window is found, return None.
+ """
+ from prompt_toolkit.interface import CommandLineInterface
+ assert isinstance(cli, CommandLineInterface)
+
+ from .containers import Window
+ from .controls import BufferControl
+
+ for l in cli.layout.walk(cli):
+ if isinstance(l, Window) and isinstance(l.content, BufferControl):
+ if l.content.buffer_name == buffer_name:
+ return l
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/mouse_events.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/mouse_events.py
new file mode 100644
index 0000000000..f42276ce9f
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/mouse_events.py
@@ -0,0 +1,48 @@
+"""
+Mouse events.
+
+
+How it works
+------------
+
+The renderer has a 2 dimensional grid of mouse event handlers.
+(`prompt_toolkit.layout.MouseHandlers`.) When the layout is rendered, the
+`Window` class will make sure that this grid will also be filled with
+callbacks. For vt100 terminals, mouse events are received through stdin, just
+like any other key press. There is a handler among the key bindings that
+catches these events and forwards them to such a mouse event handler. It passes
+through the `Window` class where the coordinates are translated from absolute
+coordinates to coordinates relative to the user control, and there
+`UIControl.mouse_handler` is called.
+"""
+from __future__ import unicode_literals
+
+__all__ = (
+ 'MouseEventType',
+ 'MouseEvent'
+)
+
+
+class MouseEventType:
+ MOUSE_UP = 'MOUSE_UP'
+ MOUSE_DOWN = 'MOUSE_DOWN'
+ SCROLL_UP = 'SCROLL_UP'
+ SCROLL_DOWN = 'SCROLL_DOWN'
+
+
+MouseEventTypes = MouseEventType # Deprecated: plural for backwards compatibility.
+
+
+class MouseEvent(object):
+ """
+ Mouse event, sent to `UIControl.mouse_handler`.
+
+ :param position: `Point` instance.
+ :param event_type: `MouseEventType`.
+ """
+ def __init__(self, position, event_type):
+ self.position = position
+ self.event_type = event_type
+
+ def __repr__(self):
+ return 'MouseEvent(%r, %r)' % (self.position, self.event_type)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/output.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/output.py
new file mode 100644
index 0000000000..072fb0677f
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/output.py
@@ -0,0 +1,192 @@
+"""
+Interface for an output.
+"""
+from __future__ import unicode_literals
+from abc import ABCMeta, abstractmethod
+from six import with_metaclass
+from prompt_toolkit.layout.screen import Size
+
+__all__ = (
+ 'Output',
+)
+
+
+class Output(with_metaclass(ABCMeta, object)):
+ """
+ Base class defining the output interface for a
+ :class:`~prompt_toolkit.renderer.Renderer`.
+
+ Actual implementations are
+ :class:`~prompt_toolkit.terminal.vt100_output.Vt100_Output` and
+ :class:`~prompt_toolkit.terminal.win32_output.Win32Output`.
+ """
+ @abstractmethod
+ def fileno(self):
+ " Return the file descriptor to which we can write for the output. "
+
+ @abstractmethod
+ def encoding(self):
+ """
+ Return the encoding for this output, e.g. 'utf-8'.
+ (This is used mainly to know which characters are supported by the
+ output the data, so that the UI can provide alternatives, when
+ required.)
+ """
+
+ @abstractmethod
+ def write(self, data):
+ " Write text (Terminal escape sequences will be removed/escaped.) "
+
+ @abstractmethod
+ def write_raw(self, data):
+ " Write text. "
+
+ @abstractmethod
+ def set_title(self, title):
+ " Set terminal title. "
+
+ @abstractmethod
+ def clear_title(self):
+ " Clear title again. (or restore previous title.) "
+
+ @abstractmethod
+ def flush(self):
+ " Write to output stream and flush. "
+
+ @abstractmethod
+ def erase_screen(self):
+ """
+ Erases the screen with the background colour and moves the cursor to
+ home.
+ """
+
+ @abstractmethod
+ def enter_alternate_screen(self):
+ " Go to the alternate screen buffer. (For full screen applications). "
+
+ @abstractmethod
+ def quit_alternate_screen(self):
+ " Leave the alternate screen buffer. "
+
+ @abstractmethod
+ def enable_mouse_support(self):
+ " Enable mouse. "
+
+ @abstractmethod
+ def disable_mouse_support(self):
+ " Disable mouse. "
+
+ @abstractmethod
+ def erase_end_of_line(self):
+ """
+ Erases from the current cursor position to the end of the current line.
+ """
+
+ @abstractmethod
+ def erase_down(self):
+ """
+ Erases the screen from the current line down to the bottom of the
+ screen.
+ """
+
+ @abstractmethod
+ def reset_attributes(self):
+ " Reset color and styling attributes. "
+
+ @abstractmethod
+ def set_attributes(self, attrs):
+ " Set new color and styling attributes. "
+
+ @abstractmethod
+ def disable_autowrap(self):
+ " Disable auto line wrapping. "
+
+ @abstractmethod
+ def enable_autowrap(self):
+ " Enable auto line wrapping. "
+
+ @abstractmethod
+ def cursor_goto(self, row=0, column=0):
+ " Move cursor position. "
+
+ @abstractmethod
+ def cursor_up(self, amount):
+ " Move cursor `amount` place up. "
+
+ @abstractmethod
+ def cursor_down(self, amount):
+ " Move cursor `amount` place down. "
+
+ @abstractmethod
+ def cursor_forward(self, amount):
+ " Move cursor `amount` place forward. "
+
+ @abstractmethod
+ def cursor_backward(self, amount):
+ " Move cursor `amount` place backward. "
+
+ @abstractmethod
+ def hide_cursor(self):
+ " Hide cursor. "
+
+ @abstractmethod
+ def show_cursor(self):
+ " Show cursor. "
+
+ def ask_for_cpr(self):
+ """
+ Asks for a cursor position report (CPR).
+ (VT100 only.)
+ """
+
+ def bell(self):
+ " Sound bell. "
+
+ def enable_bracketed_paste(self):
+ " For vt100 only. "
+
+ def disable_bracketed_paste(self):
+ " For vt100 only. "
+
+
+class DummyOutput(Output):
+ """
+ For testing. An output class that doesn't render anything.
+ """
+ def fileno(self):
+ " There is no sensible default for fileno(). "
+ raise NotImplementedError
+
+ def encoding(self):
+ return 'utf-8'
+
+ def write(self, data): pass
+ def write_raw(self, data): pass
+ def set_title(self, title): pass
+ def clear_title(self): pass
+ def flush(self): pass
+ def erase_screen(self): pass
+ def enter_alternate_screen(self): pass
+ def quit_alternate_screen(self): pass
+ def enable_mouse_support(self): pass
+ def disable_mouse_support(self): pass
+ def erase_end_of_line(self): pass
+ def erase_down(self): pass
+ def reset_attributes(self): pass
+ def set_attributes(self, attrs): pass
+ def disable_autowrap(self): pass
+ def enable_autowrap(self): pass
+ def cursor_goto(self, row=0, column=0): pass
+ def cursor_up(self, amount): pass
+ def cursor_down(self, amount): pass
+ def cursor_forward(self, amount): pass
+ def cursor_backward(self, amount): pass
+ def hide_cursor(self): pass
+ def show_cursor(self): pass
+ def ask_for_cpr(self): pass
+ def bell(self): pass
+ def enable_bracketed_paste(self): pass
+ def disable_bracketed_paste(self): pass
+
+ def get_size(self):
+ return Size(rows=40, columns=80)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/reactive.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/reactive.py
new file mode 100644
index 0000000000..ec3aa06712
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/reactive.py
@@ -0,0 +1,56 @@
+"""
+Prompt_toolkit is designed a way that the amount of changing state is reduced
+to a minimum. Where possible, code is written in a pure functional way. In
+general, this results in code where the flow is very easy to follow: the value
+of a variable can be deducted from its first assignment.
+
+However, often, practicality and performance beat purity and some classes still
+have a changing state. In order to not having to care too much about
+transferring states between several components we use some reactive
+programming. Actually some kind of data binding.
+
+We introduce two types:
+
+- Filter: for binding a boolean state. They can be chained using & and |
+ operators. Have a look in the ``filters`` module. Resolving the actual value
+ of a filter happens by calling it.
+
+- Integer: for binding integer values. Reactive operations (like addition and
+ substraction) are not suppported. Resolving the actual value happens by
+ casting it to int, like ``int(integer)``. This way, it is possible to use
+ normal integers as well for static values.
+"""
+from __future__ import unicode_literals
+from abc import ABCMeta, abstractmethod
+from six import with_metaclass
+
+
+class Integer(with_metaclass(ABCMeta, object)):
+ """
+ Reactive integer -- anything that can be resolved to an ``int``.
+ """
+ @abstractmethod
+ def __int__(self):
+ return 0
+
+ @classmethod
+ def from_callable(cls, func):
+ """
+ Create an Integer-like object that calls the given function when it is
+ resolved to an int.
+ """
+ return _IntegerFromCallable(func)
+
+
+Integer.register(int)
+
+
+class _IntegerFromCallable(Integer):
+ def __init__(self, func=0):
+ self.func = func
+
+ def __repr__(self):
+ return 'Integer.from_callable(%r)' % self.func
+
+ def __int__(self):
+ return int(self.func())
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/renderer.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/renderer.py
new file mode 100644
index 0000000000..7a8fde55b3
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/renderer.py
@@ -0,0 +1,535 @@
+"""
+Renders the command line on the console.
+(Redraws parts of the input line that were changed.)
+"""
+from __future__ import unicode_literals
+
+from prompt_toolkit.filters import to_cli_filter
+from prompt_toolkit.layout.mouse_handlers import MouseHandlers
+from prompt_toolkit.layout.screen import Point, Screen, WritePosition
+from prompt_toolkit.output import Output
+from prompt_toolkit.styles import Style
+from prompt_toolkit.token import Token
+from prompt_toolkit.utils import is_windows
+
+from six.moves import range
+
+__all__ = (
+ 'Renderer',
+ 'print_tokens',
+)
+
+
+def _output_screen_diff(output, screen, current_pos, previous_screen=None, last_token=None,
+ is_done=False, use_alternate_screen=False, attrs_for_token=None, size=None,
+ previous_width=0): # XXX: drop is_done
+ """
+ Render the diff between this screen and the previous screen.
+
+ This takes two `Screen` instances. The one that represents the output like
+ it was during the last rendering and one that represents the current
+ output raster. Looking at these two `Screen` instances, this function will
+ render the difference by calling the appropriate methods of the `Output`
+ object that only paint the changes to the terminal.
+
+ This is some performance-critical code which is heavily optimized.
+ Don't change things without profiling first.
+
+ :param current_pos: Current cursor position.
+ :param last_token: `Token` instance that represents the output attributes of
+ the last drawn character. (Color/attributes.)
+ :param attrs_for_token: :class:`._TokenToAttrsCache` instance.
+ :param width: The width of the terminal.
+ :param prevous_width: The width of the terminal during the last rendering.
+ """
+ width, height = size.columns, size.rows
+
+ #: Remember the last printed character.
+ last_token = [last_token] # nonlocal
+
+ #: Variable for capturing the output.
+ write = output.write
+ write_raw = output.write_raw
+
+ # Create locals for the most used output methods.
+ # (Save expensive attribute lookups.)
+ _output_set_attributes = output.set_attributes
+ _output_reset_attributes = output.reset_attributes
+ _output_cursor_forward = output.cursor_forward
+ _output_cursor_up = output.cursor_up
+ _output_cursor_backward = output.cursor_backward
+
+ # Hide cursor before rendering. (Avoid flickering.)
+ output.hide_cursor()
+
+ def reset_attributes():
+ " Wrapper around Output.reset_attributes. "
+ _output_reset_attributes()
+ last_token[0] = None # Forget last char after resetting attributes.
+
+ def move_cursor(new):
+ " Move cursor to this `new` point. Returns the given Point. "
+ current_x, current_y = current_pos.x, current_pos.y
+
+ if new.y > current_y:
+ # Use newlines instead of CURSOR_DOWN, because this meight add new lines.
+ # CURSOR_DOWN will never create new lines at the bottom.
+ # Also reset attributes, otherwise the newline could draw a
+ # background color.
+ reset_attributes()
+ write('\r\n' * (new.y - current_y))
+ current_x = 0
+ _output_cursor_forward(new.x)
+ return new
+ elif new.y < current_y:
+ _output_cursor_up(current_y - new.y)
+
+ if current_x >= width - 1:
+ write('\r')
+ _output_cursor_forward(new.x)
+ elif new.x < current_x or current_x >= width - 1:
+ _output_cursor_backward(current_x - new.x)
+ elif new.x > current_x:
+ _output_cursor_forward(new.x - current_x)
+
+ return new
+
+ def output_char(char):
+ """
+ Write the output of this character.
+ """
+ # If the last printed character has the same token, it also has the
+ # same style, so we don't output it.
+ the_last_token = last_token[0]
+
+ if the_last_token and the_last_token == char.token:
+ write(char.char)
+ else:
+ _output_set_attributes(attrs_for_token[char.token])
+ write(char.char)
+ last_token[0] = char.token
+
+ # Render for the first time: reset styling.
+ if not previous_screen:
+ reset_attributes()
+
+ # Disable autowrap. (When entering a the alternate screen, or anytime when
+ # we have a prompt. - In the case of a REPL, like IPython, people can have
+ # background threads, and it's hard for debugging if their output is not
+ # wrapped.)
+ if not previous_screen or not use_alternate_screen:
+ output.disable_autowrap()
+
+ # When the previous screen has a different size, redraw everything anyway.
+ # Also when we are done. (We meight take up less rows, so clearing is important.)
+ if is_done or not previous_screen or previous_width != width: # XXX: also consider height??
+ current_pos = move_cursor(Point(0, 0))
+ reset_attributes()
+ output.erase_down()
+
+ previous_screen = Screen()
+
+ # Get height of the screen.
+ # (height changes as we loop over data_buffer, so remember the current value.)
+ # (Also make sure to clip the height to the size of the output.)
+ current_height = min(screen.height, height)
+
+ # Loop over the rows.
+ row_count = min(max(screen.height, previous_screen.height), height)
+ c = 0 # Column counter.
+
+ for y in range(row_count):
+ new_row = screen.data_buffer[y]
+ previous_row = previous_screen.data_buffer[y]
+ zero_width_escapes_row = screen.zero_width_escapes[y]
+
+ new_max_line_len = min(width - 1, max(new_row.keys()) if new_row else 0)
+ previous_max_line_len = min(width - 1, max(previous_row.keys()) if previous_row else 0)
+
+ # Loop over the columns.
+ c = 0
+ while c < new_max_line_len + 1:
+ new_char = new_row[c]
+ old_char = previous_row[c]
+ char_width = (new_char.width or 1)
+
+ # When the old and new character at this position are different,
+ # draw the output. (Because of the performance, we don't call
+ # `Char.__ne__`, but inline the same expression.)
+ if new_char.char != old_char.char or new_char.token != old_char.token:
+ current_pos = move_cursor(Point(y=y, x=c))
+
+ # Send injected escape sequences to output.
+ if c in zero_width_escapes_row:
+ write_raw(zero_width_escapes_row[c])
+
+ output_char(new_char)
+ current_pos = current_pos._replace(x=current_pos.x + char_width)
+
+ c += char_width
+
+ # If the new line is shorter, trim it.
+ if previous_screen and new_max_line_len < previous_max_line_len:
+ current_pos = move_cursor(Point(y=y, x=new_max_line_len+1))
+ reset_attributes()
+ output.erase_end_of_line()
+
+ # Correctly reserve vertical space as required by the layout.
+ # When this is a new screen (drawn for the first time), or for some reason
+ # higher than the previous one. Move the cursor once to the bottom of the
+ # output. That way, we're sure that the terminal scrolls up, even when the
+ # lower lines of the canvas just contain whitespace.
+
+ # The most obvious reason that we actually want this behaviour is the avoid
+ # the artifact of the input scrolling when the completion menu is shown.
+ # (If the scrolling is actually wanted, the layout can still be build in a
+ # way to behave that way by setting a dynamic height.)
+ if current_height > previous_screen.height:
+ current_pos = move_cursor(Point(y=current_height - 1, x=0))
+
+ # Move cursor:
+ if is_done:
+ current_pos = move_cursor(Point(y=current_height, x=0))
+ output.erase_down()
+ else:
+ current_pos = move_cursor(screen.cursor_position)
+
+ if is_done or not use_alternate_screen:
+ output.enable_autowrap()
+
+ # Always reset the color attributes. This is important because a background
+ # thread could print data to stdout and we want that to be displayed in the
+ # default colors. (Also, if a background color has been set, many terminals
+ # give weird artifacs on resize events.)
+ reset_attributes()
+
+ if screen.show_cursor or is_done:
+ output.show_cursor()
+
+ return current_pos, last_token[0]
+
+
+class HeightIsUnknownError(Exception):
+ " Information unavailable. Did not yet receive the CPR response. "
+
+
+class _TokenToAttrsCache(dict):
+ """
+ A cache structure that maps Pygments Tokens to :class:`.Attr`.
+ (This is an important speed up.)
+ """
+ def __init__(self, get_style_for_token):
+ self.get_style_for_token = get_style_for_token
+
+ def __missing__(self, token):
+ try:
+ result = self.get_style_for_token(token)
+ except KeyError:
+ result = None
+
+ self[token] = result
+ return result
+
+
+class Renderer(object):
+ """
+ Typical usage:
+
+ ::
+
+ output = Vt100_Output.from_pty(sys.stdout)
+ r = Renderer(style, output)
+ r.render(cli, layout=...)
+ """
+ def __init__(self, style, output, use_alternate_screen=False, mouse_support=False):
+ assert isinstance(style, Style)
+ assert isinstance(output, Output)
+
+ self.style = style
+ self.output = output
+ self.use_alternate_screen = use_alternate_screen
+ self.mouse_support = to_cli_filter(mouse_support)
+
+ self._in_alternate_screen = False
+ self._mouse_support_enabled = False
+ self._bracketed_paste_enabled = False
+
+ # Waiting for CPR flag. True when we send the request, but didn't got a
+ # response.
+ self.waiting_for_cpr = False
+
+ self.reset(_scroll=True)
+
+ def reset(self, _scroll=False, leave_alternate_screen=True):
+ # Reset position
+ self._cursor_pos = Point(x=0, y=0)
+
+ # Remember the last screen instance between renderers. This way,
+ # we can create a `diff` between two screens and only output the
+ # difference. It's also to remember the last height. (To show for
+ # instance a toolbar at the bottom position.)
+ self._last_screen = None
+ self._last_size = None
+ self._last_token = None
+
+ # When the style hash changes, we have to do a full redraw as well as
+ # clear the `_attrs_for_token` dictionary.
+ self._last_style_hash = None
+ self._attrs_for_token = None
+
+ # Default MouseHandlers. (Just empty.)
+ self.mouse_handlers = MouseHandlers()
+
+ # Remember the last title. Only set the title when it changes.
+ self._last_title = None
+
+ #: Space from the top of the layout, until the bottom of the terminal.
+ #: We don't know this until a `report_absolute_cursor_row` call.
+ self._min_available_height = 0
+
+ # In case of Windown, also make sure to scroll to the current cursor
+ # position. (Only when rendering the first time.)
+ if is_windows() and _scroll:
+ self.output.scroll_buffer_to_prompt()
+
+ # Quit alternate screen.
+ if self._in_alternate_screen and leave_alternate_screen:
+ self.output.quit_alternate_screen()
+ self._in_alternate_screen = False
+
+ # Disable mouse support.
+ if self._mouse_support_enabled:
+ self.output.disable_mouse_support()
+ self._mouse_support_enabled = False
+
+ # Disable bracketed paste.
+ if self._bracketed_paste_enabled:
+ self.output.disable_bracketed_paste()
+ self._bracketed_paste_enabled = False
+
+ # Flush output. `disable_mouse_support` needs to write to stdout.
+ self.output.flush()
+
+ @property
+ def height_is_known(self):
+ """
+ True when the height from the cursor until the bottom of the terminal
+ is known. (It's often nicer to draw bottom toolbars only if the height
+ is known, in order to avoid flickering when the CPR response arrives.)
+ """
+ return self.use_alternate_screen or self._min_available_height > 0 or \
+ is_windows() # On Windows, we don't have to wait for a CPR.
+
+ @property
+ def rows_above_layout(self):
+ """
+ Return the number of rows visible in the terminal above the layout.
+ """
+ if self._in_alternate_screen:
+ return 0
+ elif self._min_available_height > 0:
+ total_rows = self.output.get_size().rows
+ last_screen_height = self._last_screen.height if self._last_screen else 0
+ return total_rows - max(self._min_available_height, last_screen_height)
+ else:
+ raise HeightIsUnknownError('Rows above layout is unknown.')
+
+ def request_absolute_cursor_position(self):
+ """
+ Get current cursor position.
+ For vt100: Do CPR request. (answer will arrive later.)
+ For win32: Do API call. (Answer comes immediately.)
+ """
+ # Only do this request when the cursor is at the top row. (after a
+ # clear or reset). We will rely on that in `report_absolute_cursor_row`.
+ assert self._cursor_pos.y == 0
+
+ # For Win32, we have an API call to get the number of rows below the
+ # cursor.
+ if is_windows():
+ self._min_available_height = self.output.get_rows_below_cursor_position()
+ else:
+ if self.use_alternate_screen:
+ self._min_available_height = self.output.get_size().rows
+ else:
+ # Asks for a cursor position report (CPR).
+ self.waiting_for_cpr = True
+ self.output.ask_for_cpr()
+
+ def report_absolute_cursor_row(self, row):
+ """
+ To be called when we know the absolute cursor position.
+ (As an answer of a "Cursor Position Request" response.)
+ """
+ # Calculate the amount of rows from the cursor position until the
+ # bottom of the terminal.
+ total_rows = self.output.get_size().rows
+ rows_below_cursor = total_rows - row + 1
+
+ # Set the
+ self._min_available_height = rows_below_cursor
+
+ self.waiting_for_cpr = False
+
+ def render(self, cli, layout, is_done=False):
+ """
+ Render the current interface to the output.
+
+ :param is_done: When True, put the cursor at the end of the interface. We
+ won't print any changes to this part.
+ """
+ output = self.output
+
+ # Enter alternate screen.
+ if self.use_alternate_screen and not self._in_alternate_screen:
+ self._in_alternate_screen = True
+ output.enter_alternate_screen()
+
+ # Enable bracketed paste.
+ if not self._bracketed_paste_enabled:
+ self.output.enable_bracketed_paste()
+ self._bracketed_paste_enabled = True
+
+ # Enable/disable mouse support.
+ needs_mouse_support = self.mouse_support(cli)
+
+ if needs_mouse_support and not self._mouse_support_enabled:
+ output.enable_mouse_support()
+ self._mouse_support_enabled = True
+
+ elif not needs_mouse_support and self._mouse_support_enabled:
+ output.disable_mouse_support()
+ self._mouse_support_enabled = False
+
+ # Create screen and write layout to it.
+ size = output.get_size()
+ screen = Screen()
+ screen.show_cursor = False # Hide cursor by default, unless one of the
+ # containers decides to display it.
+ mouse_handlers = MouseHandlers()
+
+ if is_done:
+ height = 0 # When we are done, we don't necessary want to fill up until the bottom.
+ else:
+ height = self._last_screen.height if self._last_screen else 0
+ height = max(self._min_available_height, height)
+
+ # When te size changes, don't consider the previous screen.
+ if self._last_size != size:
+ self._last_screen = None
+
+ # When we render using another style, do a full repaint. (Forget about
+ # the previous rendered screen.)
+ # (But note that we still use _last_screen to calculate the height.)
+ if self.style.invalidation_hash() != self._last_style_hash:
+ self._last_screen = None
+ self._attrs_for_token = None
+ if self._attrs_for_token is None:
+ self._attrs_for_token = _TokenToAttrsCache(self.style.get_attrs_for_token)
+ self._last_style_hash = self.style.invalidation_hash()
+
+ layout.write_to_screen(cli, screen, mouse_handlers, WritePosition(
+ xpos=0,
+ ypos=0,
+ width=size.columns,
+ height=(size.rows if self.use_alternate_screen else height),
+ extended_height=size.rows,
+ ))
+
+ # When grayed. Replace all tokens in the new screen.
+ if cli.is_aborting or cli.is_exiting:
+ screen.replace_all_tokens(Token.Aborted)
+
+ # Process diff and write to output.
+ self._cursor_pos, self._last_token = _output_screen_diff(
+ output, screen, self._cursor_pos,
+ self._last_screen, self._last_token, is_done,
+ use_alternate_screen=self.use_alternate_screen,
+ attrs_for_token=self._attrs_for_token,
+ size=size,
+ previous_width=(self._last_size.columns if self._last_size else 0))
+ self._last_screen = screen
+ self._last_size = size
+ self.mouse_handlers = mouse_handlers
+
+ # Write title if it changed.
+ new_title = cli.terminal_title
+
+ if new_title != self._last_title:
+ if new_title is None:
+ self.output.clear_title()
+ else:
+ self.output.set_title(new_title)
+ self._last_title = new_title
+
+ output.flush()
+
+ def erase(self, leave_alternate_screen=True, erase_title=True):
+ """
+ Hide all output and put the cursor back at the first line. This is for
+ instance used for running a system command (while hiding the CLI) and
+ later resuming the same CLI.)
+
+ :param leave_alternate_screen: When True, and when inside an alternate
+ screen buffer, quit the alternate screen.
+ :param erase_title: When True, clear the title from the title bar.
+ """
+ output = self.output
+
+ output.cursor_backward(self._cursor_pos.x)
+ output.cursor_up(self._cursor_pos.y)
+ output.erase_down()
+ output.reset_attributes()
+ output.enable_autowrap()
+ output.flush()
+
+ # Erase title.
+ if self._last_title and erase_title:
+ output.clear_title()
+
+ self.reset(leave_alternate_screen=leave_alternate_screen)
+
+ def clear(self):
+ """
+ Clear screen and go to 0,0
+ """
+ # Erase current output first.
+ self.erase()
+
+ # Send "Erase Screen" command and go to (0, 0).
+ output = self.output
+
+ output.erase_screen()
+ output.cursor_goto(0, 0)
+ output.flush()
+
+ self.request_absolute_cursor_position()
+
+
+def print_tokens(output, tokens, style):
+ """
+ Print a list of (Token, text) tuples in the given style to the output.
+ """
+ assert isinstance(output, Output)
+ assert isinstance(style, Style)
+
+ # Reset first.
+ output.reset_attributes()
+ output.enable_autowrap()
+
+ # Print all (token, text) tuples.
+ attrs_for_token = _TokenToAttrsCache(style.get_attrs_for_token)
+
+ for token, text in tokens:
+ attrs = attrs_for_token[token]
+
+ if attrs:
+ output.set_attributes(attrs)
+ else:
+ output.reset_attributes()
+
+ output.write(text)
+
+ # Reset again.
+ output.reset_attributes()
+ output.flush()
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/search_state.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/search_state.py
new file mode 100644
index 0000000000..3c494ea1a9
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/search_state.py
@@ -0,0 +1,36 @@
+from .enums import IncrementalSearchDirection
+from .filters import to_simple_filter
+
+__all__ = (
+ 'SearchState',
+)
+
+
+class SearchState(object):
+ """
+ A search 'query'.
+ """
+ __slots__ = ('text', 'direction', 'ignore_case')
+
+ def __init__(self, text='', direction=IncrementalSearchDirection.FORWARD, ignore_case=False):
+ ignore_case = to_simple_filter(ignore_case)
+
+ self.text = text
+ self.direction = direction
+ self.ignore_case = ignore_case
+
+ def __repr__(self):
+ return '%s(%r, direction=%r, ignore_case=%r)' % (
+ self.__class__.__name__, self.text, self.direction, self.ignore_case)
+
+ def __invert__(self):
+ """
+ Create a new SearchState where backwards becomes forwards and the other
+ way around.
+ """
+ if self.direction == IncrementalSearchDirection.BACKWARD:
+ direction = IncrementalSearchDirection.FORWARD
+ else:
+ direction = IncrementalSearchDirection.BACKWARD
+
+ return SearchState(text=self.text, direction=direction, ignore_case=self.ignore_case)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/selection.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/selection.py
new file mode 100644
index 0000000000..6582921222
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/selection.py
@@ -0,0 +1,47 @@
+"""
+Data structures for the selection.
+"""
+from __future__ import unicode_literals
+
+__all__ = (
+ 'SelectionType',
+ 'PasteMode',
+ 'SelectionState',
+)
+
+
+class SelectionType(object):
+ """
+ Type of selection.
+ """
+ #: Characters. (Visual in Vi.)
+ CHARACTERS = 'CHARACTERS'
+
+ #: Whole lines. (Visual-Line in Vi.)
+ LINES = 'LINES'
+
+ #: A block selection. (Visual-Block in Vi.)
+ BLOCK = 'BLOCK'
+
+
+class PasteMode(object):
+ EMACS = 'EMACS' # Yank like emacs.
+ VI_AFTER = 'VI_AFTER' # When pressing 'p' in Vi.
+ VI_BEFORE = 'VI_BEFORE' # When pressing 'P' in Vi.
+
+
+class SelectionState(object):
+ """
+ State of the current selection.
+
+ :param original_cursor_position: int
+ :param type: :class:`~.SelectionType`
+ """
+ def __init__(self, original_cursor_position=0, type=SelectionType.CHARACTERS):
+ self.original_cursor_position = original_cursor_position
+ self.type = type
+
+ def __repr__(self):
+ return '%s(original_cursor_position=%r, type=%r)' % (
+ self.__class__.__name__,
+ self.original_cursor_position, self.type)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/shortcuts.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/shortcuts.py
new file mode 100644
index 0000000000..9893624c6e
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/shortcuts.py
@@ -0,0 +1,717 @@
+"""
+Shortcuts for retrieving input from the user.
+
+If you are using this library for retrieving some input from the user (as a
+pure Python replacement for GNU readline), probably for 90% of the use cases,
+the :func:`.prompt` function is all you need. It's the easiest shortcut which
+does a lot of the underlying work like creating a
+:class:`~prompt_toolkit.interface.CommandLineInterface` instance for you.
+
+When is this not sufficient:
+ - When you want to have more complicated layouts (maybe with sidebars or
+ multiple toolbars. Or visibility of certain user interface controls
+ according to some conditions.)
+ - When you wish to have multiple input buffers. (If you would create an
+ editor like a Vi clone.)
+ - Something else that requires more customization than what is possible
+ with the parameters of `prompt`.
+
+In that case, study the code in this file and build your own
+`CommandLineInterface` instance. It's not too complicated.
+"""
+from __future__ import unicode_literals
+
+from .buffer import Buffer, AcceptAction
+from .document import Document
+from .enums import DEFAULT_BUFFER, SEARCH_BUFFER, EditingMode
+from .filters import IsDone, HasFocus, RendererHeightIsKnown, to_simple_filter, to_cli_filter, Condition
+from .history import InMemoryHistory
+from .interface import CommandLineInterface, Application, AbortAction
+from .key_binding.defaults import load_key_bindings_for_prompt
+from .key_binding.registry import Registry
+from .keys import Keys
+from .layout import Window, HSplit, FloatContainer, Float
+from .layout.containers import ConditionalContainer
+from .layout.controls import BufferControl, TokenListControl
+from .layout.dimension import LayoutDimension
+from .layout.lexers import PygmentsLexer
+from .layout.margins import PromptMargin, ConditionalMargin
+from .layout.menus import CompletionsMenu, MultiColumnCompletionsMenu
+from .layout.processors import PasswordProcessor, ConditionalProcessor, AppendAutoSuggestion, HighlightSearchProcessor, HighlightSelectionProcessor, DisplayMultipleCursors
+from .layout.prompt import DefaultPrompt
+from .layout.screen import Char
+from .layout.toolbars import ValidationToolbar, SystemToolbar, ArgToolbar, SearchToolbar
+from .layout.utils import explode_tokens
+from .renderer import print_tokens as renderer_print_tokens
+from .styles import DEFAULT_STYLE, Style, style_from_dict
+from .token import Token
+from .utils import is_conemu_ansi, is_windows, DummyContext
+
+from six import text_type, exec_, PY2
+
+import os
+import sys
+import textwrap
+import threading
+import time
+
+try:
+ from pygments.lexer import Lexer as pygments_Lexer
+ from pygments.style import Style as pygments_Style
+except ImportError:
+ pygments_Lexer = None
+ pygments_Style = None
+
+if is_windows():
+ from .terminal.win32_output import Win32Output
+ from .terminal.conemu_output import ConEmuOutput
+else:
+ from .terminal.vt100_output import Vt100_Output
+
+
+__all__ = (
+ 'create_eventloop',
+ 'create_output',
+ 'create_prompt_layout',
+ 'create_prompt_application',
+ 'prompt',
+ 'prompt_async',
+ 'create_confirm_application',
+ 'run_application',
+ 'confirm',
+ 'print_tokens',
+ 'clear',
+)
+
+
+def create_eventloop(inputhook=None, recognize_win32_paste=True):
+ """
+ Create and return an
+ :class:`~prompt_toolkit.eventloop.base.EventLoop` instance for a
+ :class:`~prompt_toolkit.interface.CommandLineInterface`.
+ """
+ if is_windows():
+ from prompt_toolkit.eventloop.win32 import Win32EventLoop as Loop
+ return Loop(inputhook=inputhook, recognize_paste=recognize_win32_paste)
+ else:
+ from prompt_toolkit.eventloop.posix import PosixEventLoop as Loop
+ return Loop(inputhook=inputhook)
+
+
+def create_output(stdout=None, true_color=False, ansi_colors_only=None):
+ """
+ Return an :class:`~prompt_toolkit.output.Output` instance for the command
+ line.
+
+ :param true_color: When True, use 24bit colors instead of 256 colors.
+ (`bool` or :class:`~prompt_toolkit.filters.SimpleFilter`.)
+ :param ansi_colors_only: When True, restrict to 16 ANSI colors only.
+ (`bool` or :class:`~prompt_toolkit.filters.SimpleFilter`.)
+ """
+ stdout = stdout or sys.__stdout__
+ true_color = to_simple_filter(true_color)
+
+ if is_windows():
+ if is_conemu_ansi():
+ return ConEmuOutput(stdout)
+ else:
+ return Win32Output(stdout)
+ else:
+ term = os.environ.get('TERM', '')
+ if PY2:
+ term = term.decode('utf-8')
+
+ return Vt100_Output.from_pty(
+ stdout, true_color=true_color,
+ ansi_colors_only=ansi_colors_only, term=term)
+
+
+def create_asyncio_eventloop(loop=None):
+ """
+ Returns an asyncio :class:`~prompt_toolkit.eventloop.EventLoop` instance
+ for usage in a :class:`~prompt_toolkit.interface.CommandLineInterface`. It
+ is a wrapper around an asyncio loop.
+
+ :param loop: The asyncio eventloop (or `None` if the default asyncioloop
+ should be used.)
+ """
+ # Inline import, to make sure the rest doesn't break on Python 2. (Where
+ # asyncio is not available.)
+ if is_windows():
+ from prompt_toolkit.eventloop.asyncio_win32 import Win32AsyncioEventLoop as AsyncioEventLoop
+ else:
+ from prompt_toolkit.eventloop.asyncio_posix import PosixAsyncioEventLoop as AsyncioEventLoop
+
+ return AsyncioEventLoop(loop)
+
+
+def _split_multiline_prompt(get_prompt_tokens):
+ """
+ Take a `get_prompt_tokens` function and return three new functions instead.
+ One that tells whether this prompt consists of multiple lines; one that
+ returns the tokens to be shown on the lines above the input; and another
+ one with the tokens to be shown at the first line of the input.
+ """
+ def has_before_tokens(cli):
+ for token, char in get_prompt_tokens(cli):
+ if '\n' in char:
+ return True
+ return False
+
+ def before(cli):
+ result = []
+ found_nl = False
+ for token, char in reversed(explode_tokens(get_prompt_tokens(cli))):
+ if found_nl:
+ result.insert(0, (token, char))
+ elif char == '\n':
+ found_nl = True
+ return result
+
+ def first_input_line(cli):
+ result = []
+ for token, char in reversed(explode_tokens(get_prompt_tokens(cli))):
+ if char == '\n':
+ break
+ else:
+ result.insert(0, (token, char))
+ return result
+
+ return has_before_tokens, before, first_input_line
+
+
+class _RPrompt(Window):
+ " The prompt that is displayed on the right side of the Window. "
+ def __init__(self, get_tokens=None):
+ get_tokens = get_tokens or (lambda cli: [])
+
+ super(_RPrompt, self).__init__(
+ TokenListControl(get_tokens, align_right=True))
+
+
+def create_prompt_layout(message='', lexer=None, is_password=False,
+ reserve_space_for_menu=8,
+ get_prompt_tokens=None, get_continuation_tokens=None,
+ get_rprompt_tokens=None,
+ get_bottom_toolbar_tokens=None,
+ display_completions_in_columns=False,
+ extra_input_processors=None, multiline=False,
+ wrap_lines=True):
+ """
+ Create a :class:`.Container` instance for a prompt.
+
+ :param message: Text to be used as prompt.
+ :param lexer: :class:`~prompt_toolkit.layout.lexers.Lexer` to be used for
+ the highlighting.
+ :param is_password: `bool` or :class:`~prompt_toolkit.filters.CLIFilter`.
+ When True, display input as '*'.
+ :param reserve_space_for_menu: Space to be reserved for the menu. When >0,
+ make sure that a minimal height is allocated in the terminal, in order
+ to display the completion menu.
+ :param get_prompt_tokens: An optional callable that returns the tokens to be
+ shown in the menu. (To be used instead of a `message`.)
+ :param get_continuation_tokens: An optional callable that takes a
+ CommandLineInterface and width as input and returns a list of (Token,
+ text) tuples to be used for the continuation.
+ :param get_bottom_toolbar_tokens: An optional callable that returns the
+ tokens for a toolbar at the bottom.
+ :param display_completions_in_columns: `bool` or
+ :class:`~prompt_toolkit.filters.CLIFilter`. Display the completions in
+ multiple columns.
+ :param multiline: `bool` or :class:`~prompt_toolkit.filters.CLIFilter`.
+ When True, prefer a layout that is more adapted for multiline input.
+ Text after newlines is automatically indented, and search/arg input is
+ shown below the input, instead of replacing the prompt.
+ :param wrap_lines: `bool` or :class:`~prompt_toolkit.filters.CLIFilter`.
+ When True (the default), automatically wrap long lines instead of
+ scrolling horizontally.
+ """
+ assert isinstance(message, text_type), 'Please provide a unicode string.'
+ assert get_bottom_toolbar_tokens is None or callable(get_bottom_toolbar_tokens)
+ assert get_prompt_tokens is None or callable(get_prompt_tokens)
+ assert get_rprompt_tokens is None or callable(get_rprompt_tokens)
+ assert not (message and get_prompt_tokens)
+
+ display_completions_in_columns = to_cli_filter(display_completions_in_columns)
+ multiline = to_cli_filter(multiline)
+
+ if get_prompt_tokens is None:
+ get_prompt_tokens = lambda _: [(Token.Prompt, message)]
+
+ has_before_tokens, get_prompt_tokens_1, get_prompt_tokens_2 = \
+ _split_multiline_prompt(get_prompt_tokens)
+
+ # `lexer` is supposed to be a `Lexer` instance. But if a Pygments lexer
+ # class is given, turn it into a PygmentsLexer. (Important for
+ # backwards-compatibility.)
+ try:
+ if pygments_Lexer and issubclass(lexer, pygments_Lexer):
+ lexer = PygmentsLexer(lexer, sync_from_start=True)
+ except TypeError: # Happens when lexer is `None` or an instance of something else.
+ pass
+
+ # Create processors list.
+ input_processors = [
+ ConditionalProcessor(
+ # By default, only highlight search when the search
+ # input has the focus. (Note that this doesn't mean
+ # there is no search: the Vi 'n' binding for instance
+ # still allows to jump to the next match in
+ # navigation mode.)
+ HighlightSearchProcessor(preview_search=True),
+ HasFocus(SEARCH_BUFFER)),
+ HighlightSelectionProcessor(),
+ ConditionalProcessor(AppendAutoSuggestion(), HasFocus(DEFAULT_BUFFER) & ~IsDone()),
+ ConditionalProcessor(PasswordProcessor(), is_password),
+ DisplayMultipleCursors(DEFAULT_BUFFER),
+ ]
+
+ if extra_input_processors:
+ input_processors.extend(extra_input_processors)
+
+ # Show the prompt before the input (using the DefaultPrompt processor.
+ # This also replaces it with reverse-i-search and 'arg' when required.
+ # (Only for single line mode.)
+ # (DefaultPrompt should always be at the end of the processors.)
+ input_processors.append(ConditionalProcessor(
+ DefaultPrompt(get_prompt_tokens_2), ~multiline))
+
+ # Create bottom toolbar.
+ if get_bottom_toolbar_tokens:
+ toolbars = [ConditionalContainer(
+ Window(TokenListControl(get_bottom_toolbar_tokens,
+ default_char=Char(' ', Token.Toolbar)),
+ height=LayoutDimension.exact(1)),
+ filter=~IsDone() & RendererHeightIsKnown())]
+ else:
+ toolbars = []
+
+ def get_height(cli):
+ # If there is an autocompletion menu to be shown, make sure that our
+ # layout has at least a minimal height in order to display it.
+ if reserve_space_for_menu and not cli.is_done:
+ buff = cli.current_buffer
+
+ # Reserve the space, either when there are completions, or when
+ # `complete_while_typing` is true and we expect completions very
+ # soon.
+ if buff.complete_while_typing() or buff.complete_state is not None:
+ return LayoutDimension(min=reserve_space_for_menu)
+
+ return LayoutDimension()
+
+ # Create and return Container instance.
+ return HSplit([
+ # The main input, with completion menus floating on top of it.
+ FloatContainer(
+ HSplit([
+ ConditionalContainer(
+ Window(
+ TokenListControl(get_prompt_tokens_1),
+ dont_extend_height=True),
+ Condition(has_before_tokens)
+ ),
+ Window(
+ BufferControl(
+ input_processors=input_processors,
+ lexer=lexer,
+ # Enable preview_search, we want to have immediate feedback
+ # in reverse-i-search mode.
+ preview_search=True),
+ get_height=get_height,
+ left_margins=[
+ # In multiline mode, use the window margin to display
+ # the prompt and continuation tokens.
+ ConditionalMargin(
+ PromptMargin(get_prompt_tokens_2, get_continuation_tokens),
+ filter=multiline
+ )
+ ],
+ wrap_lines=wrap_lines,
+ ),
+ ]),
+ [
+ # Completion menus.
+ Float(xcursor=True,
+ ycursor=True,
+ content=CompletionsMenu(
+ max_height=16,
+ scroll_offset=1,
+ extra_filter=HasFocus(DEFAULT_BUFFER) &
+ ~display_completions_in_columns)),
+ Float(xcursor=True,
+ ycursor=True,
+ content=MultiColumnCompletionsMenu(
+ extra_filter=HasFocus(DEFAULT_BUFFER) &
+ display_completions_in_columns,
+ show_meta=True)),
+
+ # The right prompt.
+ Float(right=0, top=0, hide_when_covering_content=True,
+ content=_RPrompt(get_rprompt_tokens)),
+ ]
+ ),
+ ValidationToolbar(),
+ SystemToolbar(),
+
+ # In multiline mode, we use two toolbars for 'arg' and 'search'.
+ ConditionalContainer(ArgToolbar(), multiline),
+ ConditionalContainer(SearchToolbar(), multiline),
+ ] + toolbars)
+
+
+def create_prompt_application(
+ message='',
+ multiline=False,
+ wrap_lines=True,
+ is_password=False,
+ vi_mode=False,
+ editing_mode=EditingMode.EMACS,
+ complete_while_typing=True,
+ enable_history_search=False,
+ lexer=None,
+ enable_system_bindings=False,
+ enable_open_in_editor=False,
+ validator=None,
+ completer=None,
+ reserve_space_for_menu=8,
+ auto_suggest=None,
+ style=None,
+ history=None,
+ clipboard=None,
+ get_prompt_tokens=None,
+ get_continuation_tokens=None,
+ get_rprompt_tokens=None,
+ get_bottom_toolbar_tokens=None,
+ display_completions_in_columns=False,
+ get_title=None,
+ mouse_support=False,
+ extra_input_processors=None,
+ key_bindings_registry=None,
+ on_abort=AbortAction.RAISE_EXCEPTION,
+ on_exit=AbortAction.RAISE_EXCEPTION,
+ accept_action=AcceptAction.RETURN_DOCUMENT,
+ erase_when_done=False,
+ default=''):
+ """
+ Create an :class:`~Application` instance for a prompt.
+
+ (It is meant to cover 90% of the prompt use cases, where no extreme
+ customization is required. For more complex input, it is required to create
+ a custom :class:`~Application` instance.)
+
+ :param message: Text to be shown before the prompt.
+ :param mulitiline: Allow multiline input. Pressing enter will insert a
+ newline. (This requires Meta+Enter to accept the input.)
+ :param wrap_lines: `bool` or :class:`~prompt_toolkit.filters.CLIFilter`.
+ When True (the default), automatically wrap long lines instead of
+ scrolling horizontally.
+ :param is_password: Show asterisks instead of the actual typed characters.
+ :param editing_mode: ``EditingMode.VI`` or ``EditingMode.EMACS``.
+ :param vi_mode: `bool`, if True, Identical to ``editing_mode=EditingMode.VI``.
+ :param complete_while_typing: `bool` or
+ :class:`~prompt_toolkit.filters.SimpleFilter`. Enable autocompletion
+ while typing.
+ :param enable_history_search: `bool` or
+ :class:`~prompt_toolkit.filters.SimpleFilter`. Enable up-arrow parting
+ string matching.
+ :param lexer: :class:`~prompt_toolkit.layout.lexers.Lexer` to be used for
+ the syntax highlighting.
+ :param validator: :class:`~prompt_toolkit.validation.Validator` instance
+ for input validation.
+ :param completer: :class:`~prompt_toolkit.completion.Completer` instance
+ for input completion.
+ :param reserve_space_for_menu: Space to be reserved for displaying the menu.
+ (0 means that no space needs to be reserved.)
+ :param auto_suggest: :class:`~prompt_toolkit.auto_suggest.AutoSuggest`
+ instance for input suggestions.
+ :param style: :class:`.Style` instance for the color scheme.
+ :param enable_system_bindings: `bool` or
+ :class:`~prompt_toolkit.filters.CLIFilter`. Pressing Meta+'!' will show
+ a system prompt.
+ :param enable_open_in_editor: `bool` or
+ :class:`~prompt_toolkit.filters.CLIFilter`. Pressing 'v' in Vi mode or
+ C-X C-E in emacs mode will open an external editor.
+ :param history: :class:`~prompt_toolkit.history.History` instance.
+ :param clipboard: :class:`~prompt_toolkit.clipboard.base.Clipboard` instance.
+ (e.g. :class:`~prompt_toolkit.clipboard.in_memory.InMemoryClipboard`)
+ :param get_bottom_toolbar_tokens: Optional callable which takes a
+ :class:`~prompt_toolkit.interface.CommandLineInterface` and returns a
+ list of tokens for the bottom toolbar.
+ :param display_completions_in_columns: `bool` or
+ :class:`~prompt_toolkit.filters.CLIFilter`. Display the completions in
+ multiple columns.
+ :param get_title: Callable that returns the title to be displayed in the
+ terminal.
+ :param mouse_support: `bool` or :class:`~prompt_toolkit.filters.CLIFilter`
+ to enable mouse support.
+ :param default: The default text to be shown in the input buffer. (This can
+ be edited by the user.)
+ """
+ if key_bindings_registry is None:
+ key_bindings_registry = load_key_bindings_for_prompt(
+ enable_system_bindings=enable_system_bindings,
+ enable_open_in_editor=enable_open_in_editor)
+
+ # Ensure backwards-compatibility, when `vi_mode` is passed.
+ if vi_mode:
+ editing_mode = EditingMode.VI
+
+ # Make sure that complete_while_typing is disabled when enable_history_search
+ # is enabled. (First convert to SimpleFilter, to avoid doing bitwise operations
+ # on bool objects.)
+ complete_while_typing = to_simple_filter(complete_while_typing)
+ enable_history_search = to_simple_filter(enable_history_search)
+ multiline = to_simple_filter(multiline)
+
+ complete_while_typing = complete_while_typing & ~enable_history_search
+
+ # Accept Pygments styles as well for backwards compatibility.
+ try:
+ if pygments_Style and issubclass(style, pygments_Style):
+ style = style_from_dict(style.styles)
+ except TypeError: # Happens when style is `None` or an instance of something else.
+ pass
+
+ # Create application
+ return Application(
+ layout=create_prompt_layout(
+ message=message,
+ lexer=lexer,
+ is_password=is_password,
+ reserve_space_for_menu=(reserve_space_for_menu if completer is not None else 0),
+ multiline=Condition(lambda cli: multiline()),
+ get_prompt_tokens=get_prompt_tokens,
+ get_continuation_tokens=get_continuation_tokens,
+ get_rprompt_tokens=get_rprompt_tokens,
+ get_bottom_toolbar_tokens=get_bottom_toolbar_tokens,
+ display_completions_in_columns=display_completions_in_columns,
+ extra_input_processors=extra_input_processors,
+ wrap_lines=wrap_lines),
+ buffer=Buffer(
+ enable_history_search=enable_history_search,
+ complete_while_typing=complete_while_typing,
+ is_multiline=multiline,
+ history=(history or InMemoryHistory()),
+ validator=validator,
+ completer=completer,
+ auto_suggest=auto_suggest,
+ accept_action=accept_action,
+ initial_document=Document(default),
+ ),
+ style=style or DEFAULT_STYLE,
+ clipboard=clipboard,
+ key_bindings_registry=key_bindings_registry,
+ get_title=get_title,
+ mouse_support=mouse_support,
+ editing_mode=editing_mode,
+ erase_when_done=erase_when_done,
+ reverse_vi_search_direction=True,
+ on_abort=on_abort,
+ on_exit=on_exit)
+
+
+def prompt(message='', **kwargs):
+ """
+ Get input from the user and return it.
+
+ This is a wrapper around a lot of ``prompt_toolkit`` functionality and can
+ be a replacement for `raw_input`. (or GNU readline.)
+
+ If you want to keep your history across several calls, create one
+ :class:`~prompt_toolkit.history.History` instance and pass it every time.
+
+ This function accepts many keyword arguments. Except for the following,
+ they are a proxy to the arguments of :func:`.create_prompt_application`.
+
+ :param patch_stdout: Replace ``sys.stdout`` by a proxy that ensures that
+ print statements from other threads won't destroy the prompt. (They
+ will be printed above the prompt instead.)
+ :param return_asyncio_coroutine: When True, return a asyncio coroutine. (Python >3.3)
+ :param true_color: When True, use 24bit colors instead of 256 colors.
+ :param refresh_interval: (number; in seconds) When given, refresh the UI
+ every so many seconds.
+ """
+ patch_stdout = kwargs.pop('patch_stdout', False)
+ return_asyncio_coroutine = kwargs.pop('return_asyncio_coroutine', False)
+ true_color = kwargs.pop('true_color', False)
+ refresh_interval = kwargs.pop('refresh_interval', 0)
+ eventloop = kwargs.pop('eventloop', None)
+
+ application = create_prompt_application(message, **kwargs)
+
+ return run_application(application,
+ patch_stdout=patch_stdout,
+ return_asyncio_coroutine=return_asyncio_coroutine,
+ true_color=true_color,
+ refresh_interval=refresh_interval,
+ eventloop=eventloop)
+
+
+def run_application(
+ application, patch_stdout=False, return_asyncio_coroutine=False,
+ true_color=False, refresh_interval=0, eventloop=None):
+ """
+ Run a prompt toolkit application.
+
+ :param patch_stdout: Replace ``sys.stdout`` by a proxy that ensures that
+ print statements from other threads won't destroy the prompt. (They
+ will be printed above the prompt instead.)
+ :param return_asyncio_coroutine: When True, return a asyncio coroutine. (Python >3.3)
+ :param true_color: When True, use 24bit colors instead of 256 colors.
+ :param refresh_interval: (number; in seconds) When given, refresh the UI
+ every so many seconds.
+ """
+ assert isinstance(application, Application)
+
+ if return_asyncio_coroutine:
+ eventloop = create_asyncio_eventloop()
+ else:
+ eventloop = eventloop or create_eventloop()
+
+ # Create CommandLineInterface.
+ cli = CommandLineInterface(
+ application=application,
+ eventloop=eventloop,
+ output=create_output(true_color=true_color))
+
+ # Set up refresh interval.
+ if refresh_interval:
+ done = [False]
+ def start_refresh_loop(cli):
+ def run():
+ while not done[0]:
+ time.sleep(refresh_interval)
+ cli.request_redraw()
+ t = threading.Thread(target=run)
+ t.daemon = True
+ t.start()
+
+ def stop_refresh_loop(cli):
+ done[0] = True
+
+ cli.on_start += start_refresh_loop
+ cli.on_stop += stop_refresh_loop
+
+ # Replace stdout.
+ patch_context = cli.patch_stdout_context(raw=True) if patch_stdout else DummyContext()
+
+ # Read input and return it.
+ if return_asyncio_coroutine:
+ # Create an asyncio coroutine and call it.
+ exec_context = {'patch_context': patch_context, 'cli': cli,
+ 'Document': Document}
+ exec_(textwrap.dedent('''
+ def prompt_coro():
+ # Inline import, because it slows down startup when asyncio is not
+ # needed.
+ import asyncio
+
+ @asyncio.coroutine
+ def run():
+ with patch_context:
+ result = yield from cli.run_async()
+
+ if isinstance(result, Document): # Backwards-compatibility.
+ return result.text
+ return result
+ return run()
+ '''), exec_context)
+
+ return exec_context['prompt_coro']()
+ else:
+ try:
+ with patch_context:
+ result = cli.run()
+
+ if isinstance(result, Document): # Backwards-compatibility.
+ return result.text
+ return result
+ finally:
+ eventloop.close()
+
+
+def prompt_async(message='', **kwargs):
+ """
+ Similar to :func:`.prompt`, but return an asyncio coroutine instead.
+ """
+ kwargs['return_asyncio_coroutine'] = True
+ return prompt(message, **kwargs)
+
+
+def create_confirm_application(message):
+ """
+ Create a confirmation `Application` that returns True/False.
+ """
+ registry = Registry()
+
+ @registry.add_binding('y')
+ @registry.add_binding('Y')
+ def _(event):
+ event.cli.buffers[DEFAULT_BUFFER].text = 'y'
+ event.cli.set_return_value(True)
+
+ @registry.add_binding('n')
+ @registry.add_binding('N')
+ @registry.add_binding(Keys.ControlC)
+ def _(event):
+ event.cli.buffers[DEFAULT_BUFFER].text = 'n'
+ event.cli.set_return_value(False)
+
+ return create_prompt_application(message, key_bindings_registry=registry)
+
+
+def confirm(message='Confirm (y or n) '):
+ """
+ Display a confirmation prompt.
+ """
+ assert isinstance(message, text_type)
+
+ app = create_confirm_application(message)
+ return run_application(app)
+
+
+def print_tokens(tokens, style=None, true_color=False, file=None):
+ """
+ Print a list of (Token, text) tuples in the given style to the output.
+ E.g.::
+
+ style = style_from_dict({
+ Token.Hello: '#ff0066',
+ Token.World: '#884444 italic',
+ })
+ tokens = [
+ (Token.Hello, 'Hello'),
+ (Token.World, 'World'),
+ ]
+ print_tokens(tokens, style=style)
+
+ :param tokens: List of ``(Token, text)`` tuples.
+ :param style: :class:`.Style` instance for the color scheme.
+ :param true_color: When True, use 24bit colors instead of 256 colors.
+ :param file: The output file. This can be `sys.stdout` or `sys.stderr`.
+ """
+ if style is None:
+ style = DEFAULT_STYLE
+ assert isinstance(style, Style)
+
+ output = create_output(true_color=true_color, stdout=file)
+ renderer_print_tokens(output, tokens, style)
+
+
+def clear():
+ """
+ Clear the screen.
+ """
+ out = create_output()
+ out.erase_screen()
+ out.cursor_goto(0, 0)
+ out.flush()
+
+
+# Deprecated alias for `prompt`.
+get_input = prompt
+# Deprecated alias for create_prompt_layout
+create_default_layout = create_prompt_layout
+# Deprecated alias for create_prompt_application
+create_default_application = create_prompt_application
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/styles/__init__.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/styles/__init__.py
new file mode 100644
index 0000000000..9d641f0447
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/styles/__init__.py
@@ -0,0 +1,21 @@
+"""
+Styling for prompt_toolkit applications.
+"""
+from __future__ import unicode_literals
+
+from .base import *
+from .defaults import *
+from .from_dict import *
+from .from_pygments import *
+from .utils import *
+
+
+#: The default built-in style.
+#: (For backwards compatibility, when Pygments is installed, this includes the
+#: default Pygments style.)
+try:
+ import pygments
+except ImportError:
+ DEFAULT_STYLE = style_from_dict(DEFAULT_STYLE_EXTENSIONS)
+else:
+ DEFAULT_STYLE = style_from_pygments()
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/styles/base.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/styles/base.py
new file mode 100644
index 0000000000..e9ddaa524d
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/styles/base.py
@@ -0,0 +1,86 @@
+"""
+The base classes for the styling.
+"""
+from __future__ import unicode_literals
+from abc import ABCMeta, abstractmethod
+from collections import namedtuple
+from six import with_metaclass
+
+__all__ = (
+ 'Attrs',
+ 'DEFAULT_ATTRS',
+ 'ANSI_COLOR_NAMES',
+ 'Style',
+ 'DynamicStyle',
+)
+
+
+#: Style attributes.
+Attrs = namedtuple('Attrs', 'color bgcolor bold underline italic blink reverse')
+"""
+:param color: Hexadecimal string. E.g. '000000' or Ansi color name: e.g. 'ansiblue'
+:param bgcolor: Hexadecimal string. E.g. 'ffffff' or Ansi color name: e.g. 'ansired'
+:param bold: Boolean
+:param underline: Boolean
+:param italic: Boolean
+:param blink: Boolean
+:param reverse: Boolean
+"""
+
+#: The default `Attrs`.
+DEFAULT_ATTRS = Attrs(color=None, bgcolor=None, bold=False, underline=False,
+ italic=False, blink=False, reverse=False)
+
+
+#: ``Attrs.bgcolor/fgcolor`` can be in either 'ffffff' format, or can be any of
+#: the following in case we want to take colors from the 8/16 color palette.
+#: Usually, in that case, the terminal application allows to configure the RGB
+#: values for these names.
+ANSI_COLOR_NAMES = [
+ 'ansiblack', 'ansiwhite', 'ansidefault',
+
+ # Low intensity.
+ 'ansired', 'ansigreen', 'ansiyellow', 'ansiblue', 'ansifuchsia', 'ansiturquoise', 'ansilightgray',
+
+ # High intensity. (Not supported everywhere.)
+ 'ansidarkgray', 'ansidarkred', 'ansidarkgreen', 'ansibrown', 'ansidarkblue',
+ 'ansipurple', 'ansiteal',
+]
+
+
+class Style(with_metaclass(ABCMeta, object)):
+ """
+ Abstract base class for prompt_toolkit styles.
+ """
+ @abstractmethod
+ def get_attrs_for_token(self, token):
+ """
+ Return :class:`.Attrs` for the given token.
+ """
+
+ @abstractmethod
+ def invalidation_hash(self):
+ """
+ Invalidation hash for the style. When this changes over time, the
+ renderer knows that something in the style changed, and that everything
+ has to be redrawn.
+ """
+
+
+class DynamicStyle(Style):
+ """
+ Style class that can dynamically returns an other Style.
+
+ :param get_style: Callable that returns a :class:`.Style` instance.
+ """
+ def __init__(self, get_style):
+ self.get_style = get_style
+
+ def get_attrs_for_token(self, token):
+ style = self.get_style()
+ assert isinstance(style, Style)
+
+ return style.get_attrs_for_token(token)
+
+ def invalidation_hash(self):
+ return self.get_style().invalidation_hash()
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/styles/defaults.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/styles/defaults.py
new file mode 100644
index 0000000000..5357eaa111
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/styles/defaults.py
@@ -0,0 +1,95 @@
+"""
+The default styling.
+"""
+from __future__ import unicode_literals
+
+from prompt_toolkit.token import Token
+
+__all__ = (
+ 'DEFAULT_STYLE_EXTENSIONS',
+ 'default_style_extensions',
+)
+
+
+#: Styling of prompt-toolkit specific tokens, that are not know by the default
+#: Pygments style.
+DEFAULT_STYLE_EXTENSIONS = {
+ # Highlighting of search matches in document.
+ Token.SearchMatch: 'noinherit reverse',
+ Token.SearchMatch.Current: 'noinherit #ffffff bg:#448844 underline',
+
+ # Highlighting of select text in document.
+ Token.SelectedText: 'reverse',
+
+ Token.CursorColumn: 'bg:#dddddd',
+ Token.CursorLine: 'underline',
+ Token.ColorColumn: 'bg:#ccaacc',
+
+ # Highlighting of matching brackets.
+ Token.MatchingBracket: '',
+ Token.MatchingBracket.Other: '#000000 bg:#aacccc',
+ Token.MatchingBracket.Cursor: '#ff8888 bg:#880000',
+
+ Token.MultipleCursors.Cursor: '#000000 bg:#ccccaa',
+
+ # Line numbers.
+ Token.LineNumber: '#888888',
+ Token.LineNumber.Current: 'bold',
+ Token.Tilde: '#8888ff',
+
+ # Default prompt.
+ Token.Prompt: '',
+ Token.Prompt.Arg: 'noinherit',
+ Token.Prompt.Search: 'noinherit',
+ Token.Prompt.Search.Text: '',
+
+ # Search toolbar.
+ Token.Toolbar.Search: 'bold',
+ Token.Toolbar.Search.Text: 'nobold',
+
+ # System toolbar
+ Token.Toolbar.System: 'bold',
+ Token.Toolbar.System.Text: 'nobold',
+
+ # "arg" toolbar.
+ Token.Toolbar.Arg: 'bold',
+ Token.Toolbar.Arg.Text: 'nobold',
+
+ # Validation toolbar.
+ Token.Toolbar.Validation: 'bg:#550000 #ffffff',
+ Token.WindowTooSmall: 'bg:#550000 #ffffff',
+
+ # Completions toolbar.
+ Token.Toolbar.Completions: 'bg:#bbbbbb #000000',
+ Token.Toolbar.Completions.Arrow: 'bg:#bbbbbb #000000 bold',
+ Token.Toolbar.Completions.Completion: 'bg:#bbbbbb #000000',
+ Token.Toolbar.Completions.Completion.Current: 'bg:#444444 #ffffff',
+
+ # Completions menu.
+ Token.Menu.Completions: 'bg:#bbbbbb #000000',
+ Token.Menu.Completions.Completion: '',
+ Token.Menu.Completions.Completion.Current: 'bg:#888888 #ffffff',
+ Token.Menu.Completions.Meta: 'bg:#999999 #000000',
+ Token.Menu.Completions.Meta.Current: 'bg:#aaaaaa #000000',
+ Token.Menu.Completions.MultiColumnMeta: 'bg:#aaaaaa #000000',
+
+ # Scrollbars.
+ Token.Scrollbar: 'bg:#888888',
+ Token.Scrollbar.Button: 'bg:#444444',
+ Token.Scrollbar.Arrow: 'bg:#222222 #888888 bold',
+
+ # Auto suggestion text.
+ Token.AutoSuggestion: '#666666',
+
+ # Trailing whitespace and tabs.
+ Token.TrailingWhiteSpace: '#999999',
+ Token.Tab: '#999999',
+
+ # When Control-C has been pressed. Grayed.
+ Token.Aborted: '#888888',
+
+ # Entering a Vi digraph.
+ Token.Digraph: '#4444ff',
+}
+
+default_style_extensions = DEFAULT_STYLE_EXTENSIONS # Old name.
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/styles/from_dict.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/styles/from_dict.py
new file mode 100644
index 0000000000..b50325710f
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/styles/from_dict.py
@@ -0,0 +1,151 @@
+"""
+Tool for creating styles from a dictionary.
+
+This is very similar to the Pygments style dictionary, with some additions:
+- Support for reverse and blink.
+- Support for ANSI color names. (These will map directly to the 16 terminal
+ colors.)
+"""
+try:
+ from collections.abc import Mapping
+except ImportError:
+ from collections import Mapping
+
+from .base import Style, DEFAULT_ATTRS, ANSI_COLOR_NAMES
+from .defaults import DEFAULT_STYLE_EXTENSIONS
+from .utils import merge_attrs, split_token_in_parts
+from six.moves import range
+
+__all__ = (
+ 'style_from_dict',
+)
+
+
+def _colorformat(text):
+ """
+ Parse/validate color format.
+
+ Like in Pygments, but also support the ANSI color names.
+ (These will map to the colors of the 16 color palette.)
+ """
+ if text[0:1] == '#':
+ col = text[1:]
+ if col in ANSI_COLOR_NAMES:
+ return col
+ elif len(col) == 6:
+ return col
+ elif len(col) == 3:
+ return col[0]*2 + col[1]*2 + col[2]*2
+ elif text == '':
+ return text
+
+ raise ValueError('Wrong color format %r' % text)
+
+
+def style_from_dict(style_dict, include_defaults=True):
+ """
+ Create a ``Style`` instance from a dictionary or other mapping.
+
+ The dictionary is equivalent to the ``Style.styles`` dictionary from
+ pygments, with a few additions: it supports 'reverse' and 'blink'.
+
+ Usage::
+
+ style_from_dict({
+ Token: '#ff0000 bold underline',
+ Token.Title: 'blink',
+ Token.SomethingElse: 'reverse',
+ })
+
+ :param include_defaults: Include the defaults (built-in) styling for
+ selected text, etc...)
+ """
+ assert isinstance(style_dict, Mapping)
+
+ if include_defaults:
+ s2 = {}
+ s2.update(DEFAULT_STYLE_EXTENSIONS)
+ s2.update(style_dict)
+ style_dict = s2
+
+ # Expand token inheritance and turn style description into Attrs.
+ token_to_attrs = {}
+
+ # (Loop through the tokens in order. Sorting makes sure that
+ # we process the parent first.)
+ for ttype, styledef in sorted(style_dict.items()):
+ # Start from parent Attrs or default Attrs.
+ attrs = DEFAULT_ATTRS
+
+ if 'noinherit' not in styledef:
+ for i in range(1, len(ttype) + 1):
+ try:
+ attrs = token_to_attrs[ttype[:-i]]
+ except KeyError:
+ pass
+ else:
+ break
+
+ # Now update with the given attributes.
+ for part in styledef.split():
+ if part == 'noinherit':
+ pass
+ elif part == 'bold':
+ attrs = attrs._replace(bold=True)
+ elif part == 'nobold':
+ attrs = attrs._replace(bold=False)
+ elif part == 'italic':
+ attrs = attrs._replace(italic=True)
+ elif part == 'noitalic':
+ attrs = attrs._replace(italic=False)
+ elif part == 'underline':
+ attrs = attrs._replace(underline=True)
+ elif part == 'nounderline':
+ attrs = attrs._replace(underline=False)
+
+ # prompt_toolkit extensions. Not in Pygments.
+ elif part == 'blink':
+ attrs = attrs._replace(blink=True)
+ elif part == 'noblink':
+ attrs = attrs._replace(blink=False)
+ elif part == 'reverse':
+ attrs = attrs._replace(reverse=True)
+ elif part == 'noreverse':
+ attrs = attrs._replace(reverse=False)
+
+ # Pygments properties that we ignore.
+ elif part in ('roman', 'sans', 'mono'):
+ pass
+ elif part.startswith('border:'):
+ pass
+
+ # Colors.
+
+ elif part.startswith('bg:'):
+ attrs = attrs._replace(bgcolor=_colorformat(part[3:]))
+ else:
+ attrs = attrs._replace(color=_colorformat(part))
+
+ token_to_attrs[ttype] = attrs
+
+ return _StyleFromDict(token_to_attrs)
+
+
+class _StyleFromDict(Style):
+ """
+ Turn a dictionary that maps `Token` to `Attrs` into a style class.
+
+ :param token_to_attrs: Dictionary that maps `Token` to `Attrs`.
+ """
+ def __init__(self, token_to_attrs):
+ self.token_to_attrs = token_to_attrs
+
+ def get_attrs_for_token(self, token):
+ # Split Token.
+ list_of_attrs = []
+ for token in split_token_in_parts(token):
+ list_of_attrs.append(self.token_to_attrs.get(token, DEFAULT_ATTRS))
+ return merge_attrs(list_of_attrs)
+
+ def invalidation_hash(self):
+ return id(self.token_to_attrs)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/styles/from_pygments.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/styles/from_pygments.py
new file mode 100644
index 0000000000..0af8c53cf2
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/styles/from_pygments.py
@@ -0,0 +1,77 @@
+"""
+Adaptor for building prompt_toolkit styles, starting from a Pygments style.
+
+Usage::
+
+ from pygments.styles.tango import TangoStyle
+ style = style_from_pygments(pygments_style_cls=TangoStyle)
+"""
+from __future__ import unicode_literals
+
+from .base import Style
+from .from_dict import style_from_dict
+
+__all__ = (
+ 'PygmentsStyle',
+ 'style_from_pygments',
+)
+
+
+# Following imports are only needed when a ``PygmentsStyle`` class is used.
+try:
+ from pygments.style import Style as pygments_Style
+ from pygments.styles.default import DefaultStyle as pygments_DefaultStyle
+except ImportError:
+ pygments_Style = None
+ pygments_DefaultStyle = None
+
+
+def style_from_pygments(style_cls=pygments_DefaultStyle,
+ style_dict=None,
+ include_defaults=True):
+ """
+ Shortcut to create a :class:`.Style` instance from a Pygments style class
+ and a style dictionary.
+
+ Example::
+
+ from prompt_toolkit.styles.from_pygments import style_from_pygments
+ from pygments.styles import get_style_by_name
+ style = style_from_pygments(get_style_by_name('monokai'))
+
+ :param style_cls: Pygments style class to start from.
+ :param style_dict: Dictionary for this style. `{Token: style}`.
+ :param include_defaults: (`bool`) Include prompt_toolkit extensions.
+ """
+ assert style_dict is None or isinstance(style_dict, dict)
+ assert style_cls is None or issubclass(style_cls, pygments_Style)
+
+ styles_dict = {}
+
+ if style_cls is not None:
+ styles_dict.update(style_cls.styles)
+
+ if style_dict is not None:
+ styles_dict.update(style_dict)
+
+ return style_from_dict(styles_dict, include_defaults=include_defaults)
+
+
+class PygmentsStyle(Style):
+ " Deprecated. "
+ def __new__(cls, pygments_style_cls):
+ assert issubclass(pygments_style_cls, pygments_Style)
+ return style_from_dict(pygments_style_cls.styles)
+
+ def invalidation_hash(self):
+ pass
+
+ @classmethod
+ def from_defaults(cls, style_dict=None,
+ pygments_style_cls=pygments_DefaultStyle,
+ include_extensions=True):
+ " Deprecated. "
+ return style_from_pygments(
+ style_cls=pygments_style_cls,
+ style_dict=style_dict,
+ include_defaults=include_extensions)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/styles/utils.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/styles/utils.py
new file mode 100644
index 0000000000..6087e76abe
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/styles/utils.py
@@ -0,0 +1,45 @@
+from __future__ import unicode_literals
+from .base import DEFAULT_ATTRS, Attrs
+
+__all__ = (
+ 'split_token_in_parts',
+ 'merge_attrs',
+)
+
+
+def split_token_in_parts(token):
+ """
+ Take a Token, and turn it in a list of tokens, by splitting
+ it on ':' (taking that as a separator.)
+ """
+ result = []
+ current = []
+ for part in token + (':', ):
+ if part == ':':
+ if current:
+ result.append(tuple(current))
+ current = []
+ else:
+ current.append(part)
+
+ return result
+
+
+def merge_attrs(list_of_attrs):
+ """
+ Take a list of :class:`.Attrs` instances and merge them into one.
+ Every `Attr` in the list can override the styling of the previous one.
+ """
+ result = DEFAULT_ATTRS
+
+ for attr in list_of_attrs:
+ result = Attrs(
+ color=attr.color or result.color,
+ bgcolor=attr.bgcolor or result.bgcolor,
+ bold=attr.bold or result.bold,
+ underline=attr.underline or result.underline,
+ italic=attr.italic or result.italic,
+ blink=attr.blink or result.blink,
+ reverse=attr.reverse or result.reverse)
+
+ return result
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/terminal/__init__.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/terminal/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/terminal/__init__.py
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/terminal/conemu_output.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/terminal/conemu_output.py
new file mode 100644
index 0000000000..16b7bde8f3
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/terminal/conemu_output.py
@@ -0,0 +1,42 @@
+from __future__ import unicode_literals
+
+from prompt_toolkit.renderer import Output
+
+from .win32_output import Win32Output
+from .vt100_output import Vt100_Output
+
+__all__ = (
+ 'ConEmuOutput',
+)
+
+
+class ConEmuOutput(object):
+ """
+ ConEmu (Windows) output abstraction.
+
+ ConEmu is a Windows console application, but it also supports ANSI escape
+ sequences. This output class is actually a proxy to both `Win32Output` and
+ `Vt100_Output`. It uses `Win32Output` for console sizing and scrolling, but
+ all cursor movements and scrolling happens through the `Vt100_Output`.
+
+ This way, we can have 256 colors in ConEmu and Cmder. Rendering will be
+ even a little faster as well.
+
+ http://conemu.github.io/
+ http://gooseberrycreative.com/cmder/
+ """
+ def __init__(self, stdout):
+ self.win32_output = Win32Output(stdout)
+ self.vt100_output = Vt100_Output(stdout, lambda: None)
+
+ def __getattr__(self, name):
+ if name in ('get_size', 'get_rows_below_cursor_position',
+ 'enable_mouse_support', 'disable_mouse_support',
+ 'scroll_buffer_to_prompt', 'get_win32_screen_buffer_info',
+ 'enable_bracketed_paste', 'disable_bracketed_paste'):
+ return getattr(self.win32_output, name)
+ else:
+ return getattr(self.vt100_output, name)
+
+
+Output.register(ConEmuOutput)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/terminal/vt100_input.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/terminal/vt100_input.py
new file mode 100644
index 0000000000..74841312fa
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/terminal/vt100_input.py
@@ -0,0 +1,520 @@
+"""
+Parser for VT100 input stream.
+"""
+from __future__ import unicode_literals
+
+import os
+import re
+import six
+import termios
+import tty
+
+from six.moves import range
+
+from ..keys import Keys
+from ..key_binding.input_processor import KeyPress
+
+__all__ = (
+ 'InputStream',
+ 'raw_mode',
+ 'cooked_mode',
+)
+
+_DEBUG_RENDERER_INPUT = False
+_DEBUG_RENDERER_INPUT_FILENAME = 'prompt-toolkit-render-input.log'
+
+
+# Regex matching any CPR response
+# (Note that we use '\Z' instead of '$', because '$' could include a trailing
+# newline.)
+_cpr_response_re = re.compile('^' + re.escape('\x1b[') + r'\d+;\d+R\Z')
+
+# Mouse events:
+# Typical: "Esc[MaB*" Urxvt: "Esc[96;14;13M" and for Xterm SGR: "Esc[<64;85;12M"
+_mouse_event_re = re.compile('^' + re.escape('\x1b[') + r'(<?[\d;]+[mM]|M...)\Z')
+
+# Regex matching any valid prefix of a CPR response.
+# (Note that it doesn't contain the last character, the 'R'. The prefix has to
+# be shorter.)
+_cpr_response_prefix_re = re.compile('^' + re.escape('\x1b[') + r'[\d;]*\Z')
+
+_mouse_event_prefix_re = re.compile('^' + re.escape('\x1b[') + r'(<?[\d;]*|M.{0,2})\Z')
+
+
+class _Flush(object):
+ """ Helper object to indicate flush operation to the parser. """
+ pass
+
+
+# Mapping of vt100 escape codes to Keys.
+ANSI_SEQUENCES = {
+ '\x1b': Keys.Escape,
+
+ '\x00': Keys.ControlSpace, # Control-Space (Also for Ctrl-@)
+ '\x01': Keys.ControlA, # Control-A (home)
+ '\x02': Keys.ControlB, # Control-B (emacs cursor left)
+ '\x03': Keys.ControlC, # Control-C (interrupt)
+ '\x04': Keys.ControlD, # Control-D (exit)
+ '\x05': Keys.ControlE, # Contrel-E (end)
+ '\x06': Keys.ControlF, # Control-F (cursor forward)
+ '\x07': Keys.ControlG, # Control-G
+ '\x08': Keys.ControlH, # Control-H (8) (Identical to '\b')
+ '\x09': Keys.ControlI, # Control-I (9) (Identical to '\t')
+ '\x0a': Keys.ControlJ, # Control-J (10) (Identical to '\n')
+ '\x0b': Keys.ControlK, # Control-K (delete until end of line; vertical tab)
+ '\x0c': Keys.ControlL, # Control-L (clear; form feed)
+ '\x0d': Keys.ControlM, # Control-M (13) (Identical to '\r')
+ '\x0e': Keys.ControlN, # Control-N (14) (history forward)
+ '\x0f': Keys.ControlO, # Control-O (15)
+ '\x10': Keys.ControlP, # Control-P (16) (history back)
+ '\x11': Keys.ControlQ, # Control-Q
+ '\x12': Keys.ControlR, # Control-R (18) (reverse search)
+ '\x13': Keys.ControlS, # Control-S (19) (forward search)
+ '\x14': Keys.ControlT, # Control-T
+ '\x15': Keys.ControlU, # Control-U
+ '\x16': Keys.ControlV, # Control-V
+ '\x17': Keys.ControlW, # Control-W
+ '\x18': Keys.ControlX, # Control-X
+ '\x19': Keys.ControlY, # Control-Y (25)
+ '\x1a': Keys.ControlZ, # Control-Z
+
+ '\x1c': Keys.ControlBackslash, # Both Control-\ and Ctrl-|
+ '\x1d': Keys.ControlSquareClose, # Control-]
+ '\x1e': Keys.ControlCircumflex, # Control-^
+ '\x1f': Keys.ControlUnderscore, # Control-underscore (Also for Ctrl-hypen.)
+ '\x7f': Keys.Backspace, # (127) Backspace
+ '\x1b[A': Keys.Up,
+ '\x1b[B': Keys.Down,
+ '\x1b[C': Keys.Right,
+ '\x1b[D': Keys.Left,
+ '\x1b[H': Keys.Home,
+ '\x1bOH': Keys.Home,
+ '\x1b[F': Keys.End,
+ '\x1bOF': Keys.End,
+ '\x1b[3~': Keys.Delete,
+ '\x1b[3;2~': Keys.ShiftDelete, # xterm, gnome-terminal.
+ '\x1b[3;5~': Keys.ControlDelete, # xterm, gnome-terminal.
+ '\x1b[1~': Keys.Home, # tmux
+ '\x1b[4~': Keys.End, # tmux
+ '\x1b[5~': Keys.PageUp,
+ '\x1b[6~': Keys.PageDown,
+ '\x1b[7~': Keys.Home, # xrvt
+ '\x1b[8~': Keys.End, # xrvt
+ '\x1b[Z': Keys.BackTab, # shift + tab
+ '\x1b[2~': Keys.Insert,
+
+ '\x1bOP': Keys.F1,
+ '\x1bOQ': Keys.F2,
+ '\x1bOR': Keys.F3,
+ '\x1bOS': Keys.F4,
+ '\x1b[[A': Keys.F1, # Linux console.
+ '\x1b[[B': Keys.F2, # Linux console.
+ '\x1b[[C': Keys.F3, # Linux console.
+ '\x1b[[D': Keys.F4, # Linux console.
+ '\x1b[[E': Keys.F5, # Linux console.
+ '\x1b[11~': Keys.F1, # rxvt-unicode
+ '\x1b[12~': Keys.F2, # rxvt-unicode
+ '\x1b[13~': Keys.F3, # rxvt-unicode
+ '\x1b[14~': Keys.F4, # rxvt-unicode
+ '\x1b[15~': Keys.F5,
+ '\x1b[17~': Keys.F6,
+ '\x1b[18~': Keys.F7,
+ '\x1b[19~': Keys.F8,
+ '\x1b[20~': Keys.F9,
+ '\x1b[21~': Keys.F10,
+ '\x1b[23~': Keys.F11,
+ '\x1b[24~': Keys.F12,
+ '\x1b[25~': Keys.F13,
+ '\x1b[26~': Keys.F14,
+ '\x1b[28~': Keys.F15,
+ '\x1b[29~': Keys.F16,
+ '\x1b[31~': Keys.F17,
+ '\x1b[32~': Keys.F18,
+ '\x1b[33~': Keys.F19,
+ '\x1b[34~': Keys.F20,
+
+ # Xterm
+ '\x1b[1;2P': Keys.F13,
+ '\x1b[1;2Q': Keys.F14,
+ # '\x1b[1;2R': Keys.F15, # Conflicts with CPR response.
+ '\x1b[1;2S': Keys.F16,
+ '\x1b[15;2~': Keys.F17,
+ '\x1b[17;2~': Keys.F18,
+ '\x1b[18;2~': Keys.F19,
+ '\x1b[19;2~': Keys.F20,
+ '\x1b[20;2~': Keys.F21,
+ '\x1b[21;2~': Keys.F22,
+ '\x1b[23;2~': Keys.F23,
+ '\x1b[24;2~': Keys.F24,
+
+ '\x1b[1;5A': Keys.ControlUp, # Cursor Mode
+ '\x1b[1;5B': Keys.ControlDown, # Cursor Mode
+ '\x1b[1;5C': Keys.ControlRight, # Cursor Mode
+ '\x1b[1;5D': Keys.ControlLeft, # Cursor Mode
+
+ '\x1b[1;2A': Keys.ShiftUp,
+ '\x1b[1;2B': Keys.ShiftDown,
+ '\x1b[1;2C': Keys.ShiftRight,
+ '\x1b[1;2D': Keys.ShiftLeft,
+
+ # Tmux sends following keystrokes when control+arrow is pressed, but for
+ # Emacs ansi-term sends the same sequences for normal arrow keys. Consider
+ # it a normal arrow press, because that's more important.
+ '\x1bOA': Keys.Up,
+ '\x1bOB': Keys.Down,
+ '\x1bOC': Keys.Right,
+ '\x1bOD': Keys.Left,
+
+ '\x1b[5A': Keys.ControlUp,
+ '\x1b[5B': Keys.ControlDown,
+ '\x1b[5C': Keys.ControlRight,
+ '\x1b[5D': Keys.ControlLeft,
+
+ '\x1bOc': Keys.ControlRight, # rxvt
+ '\x1bOd': Keys.ControlLeft, # rxvt
+
+ '\x1b[200~': Keys.BracketedPaste, # Start of bracketed paste.
+
+ # Meta + arrow keys. Several terminals handle this differently.
+ # The following sequences are for xterm and gnome-terminal.
+ # (Iterm sends ESC followed by the normal arrow_up/down/left/right
+ # sequences, and the OSX Terminal sends ESCb and ESCf for "alt
+ # arrow_left" and "alt arrow_right." We don't handle these
+ # explicitely, in here, because would could not distinguesh between
+ # pressing ESC (to go to Vi navigation mode), followed by just the
+ # 'b' or 'f' key. These combinations are handled in
+ # the input processor.)
+ '\x1b[1;3D': (Keys.Escape, Keys.Left),
+ '\x1b[1;3C': (Keys.Escape, Keys.Right),
+ '\x1b[1;3A': (Keys.Escape, Keys.Up),
+ '\x1b[1;3B': (Keys.Escape, Keys.Down),
+
+ # Sequences generated by numpad 5. Not sure what it means. (It doesn't
+ # appear in 'infocmp'. Just ignore.
+ '\x1b[E': Keys.Ignore, # Xterm.
+ '\x1b[G': Keys.Ignore, # Linux console.
+}
+
+
+class _IsPrefixOfLongerMatchCache(dict):
+ """
+ Dictiory that maps input sequences to a boolean indicating whether there is
+ any key that start with this characters.
+ """
+ def __missing__(self, prefix):
+ # (hard coded) If this could be a prefix of a CPR response, return
+ # True.
+ if (_cpr_response_prefix_re.match(prefix) or _mouse_event_prefix_re.match(prefix)):
+ result = True
+ else:
+ # If this could be a prefix of anything else, also return True.
+ result = any(v for k, v in ANSI_SEQUENCES.items() if k.startswith(prefix) and k != prefix)
+
+ self[prefix] = result
+ return result
+
+
+_IS_PREFIX_OF_LONGER_MATCH_CACHE = _IsPrefixOfLongerMatchCache()
+
+
+class InputStream(object):
+ """
+ Parser for VT100 input stream.
+
+ Feed the data through the `feed` method and the correct callbacks of the
+ `input_processor` will be called.
+
+ ::
+
+ def callback(key):
+ pass
+ i = InputStream(callback)
+ i.feed('data\x01...')
+
+ :attr input_processor: :class:`~prompt_toolkit.key_binding.InputProcessor` instance.
+ """
+ # Lookup table of ANSI escape sequences for a VT100 terminal
+ # Hint: in order to know what sequences your terminal writes to stdin, run
+ # "od -c" and start typing.
+ def __init__(self, feed_key_callback):
+ assert callable(feed_key_callback)
+
+ self.feed_key_callback = feed_key_callback
+ self.reset()
+
+ if _DEBUG_RENDERER_INPUT:
+ self.LOG = open(_DEBUG_RENDERER_INPUT_FILENAME, 'ab')
+
+ def reset(self, request=False):
+ self._in_bracketed_paste = False
+ self._start_parser()
+
+ def _start_parser(self):
+ """
+ Start the parser coroutine.
+ """
+ self._input_parser = self._input_parser_generator()
+ self._input_parser.send(None)
+
+ def _get_match(self, prefix):
+ """
+ Return the key that maps to this prefix.
+ """
+ # (hard coded) If we match a CPR response, return Keys.CPRResponse.
+ # (This one doesn't fit in the ANSI_SEQUENCES, because it contains
+ # integer variables.)
+ if _cpr_response_re.match(prefix):
+ return Keys.CPRResponse
+
+ elif _mouse_event_re.match(prefix):
+ return Keys.Vt100MouseEvent
+
+ # Otherwise, use the mappings.
+ try:
+ return ANSI_SEQUENCES[prefix]
+ except KeyError:
+ return None
+
+ def _input_parser_generator(self):
+ """
+ Coroutine (state machine) for the input parser.
+ """
+ prefix = ''
+ retry = False
+ flush = False
+
+ while True:
+ flush = False
+
+ if retry:
+ retry = False
+ else:
+ # Get next character.
+ c = yield
+
+ if c == _Flush:
+ flush = True
+ else:
+ prefix += c
+
+ # If we have some data, check for matches.
+ if prefix:
+ is_prefix_of_longer_match = _IS_PREFIX_OF_LONGER_MATCH_CACHE[prefix]
+ match = self._get_match(prefix)
+
+ # Exact matches found, call handlers..
+ if (flush or not is_prefix_of_longer_match) and match:
+ self._call_handler(match, prefix)
+ prefix = ''
+
+ # No exact match found.
+ elif (flush or not is_prefix_of_longer_match) and not match:
+ found = False
+ retry = True
+
+ # Loop over the input, try the longest match first and
+ # shift.
+ for i in range(len(prefix), 0, -1):
+ match= self._get_match(prefix[:i])
+ if match:
+ self._call_handler(match, prefix[:i])
+ prefix = prefix[i:]
+ found = True
+
+ if not found:
+ self._call_handler(prefix[0], prefix[0])
+ prefix = prefix[1:]
+
+ def _call_handler(self, key, insert_text):
+ """
+ Callback to handler.
+ """
+ if isinstance(key, tuple):
+ for k in key:
+ self._call_handler(k, insert_text)
+ else:
+ if key == Keys.BracketedPaste:
+ self._in_bracketed_paste = True
+ self._paste_buffer = ''
+ else:
+ self.feed_key_callback(KeyPress(key, insert_text))
+
+ def feed(self, data):
+ """
+ Feed the input stream.
+
+ :param data: Input string (unicode).
+ """
+ assert isinstance(data, six.text_type)
+
+ if _DEBUG_RENDERER_INPUT:
+ self.LOG.write(repr(data).encode('utf-8') + b'\n')
+ self.LOG.flush()
+
+ # Handle bracketed paste. (We bypass the parser that matches all other
+ # key presses and keep reading input until we see the end mark.)
+ # This is much faster then parsing character by character.
+ if self._in_bracketed_paste:
+ self._paste_buffer += data
+ end_mark = '\x1b[201~'
+
+ if end_mark in self._paste_buffer:
+ end_index = self._paste_buffer.index(end_mark)
+
+ # Feed content to key bindings.
+ paste_content = self._paste_buffer[:end_index]
+ self.feed_key_callback(KeyPress(Keys.BracketedPaste, paste_content))
+
+ # Quit bracketed paste mode and handle remaining input.
+ self._in_bracketed_paste = False
+ remaining = self._paste_buffer[end_index + len(end_mark):]
+ self._paste_buffer = ''
+
+ self.feed(remaining)
+
+ # Handle normal input character by character.
+ else:
+ for i, c in enumerate(data):
+ if self._in_bracketed_paste:
+ # Quit loop and process from this position when the parser
+ # entered bracketed paste.
+ self.feed(data[i:])
+ break
+ else:
+ # Replace \r by \n. (Some clients send \r instead of \n
+ # when enter is pressed. E.g. telnet and some other
+ # terminals.)
+
+ # XXX: We should remove this in a future version. It *is*
+ # now possible to recognise the difference.
+ # (We remove ICRNL/INLCR/IGNCR below.)
+ # However, this breaks IPython and maybe other applications,
+ # because they bind ControlJ (\n) for handling the Enter key.
+
+ # When this is removed, replace Enter=ControlJ by
+ # Enter=ControlM in keys.py.
+ if c == '\r':
+ c = '\n'
+ self._input_parser.send(c)
+
+ def flush(self):
+ """
+ Flush the buffer of the input stream.
+
+ This will allow us to handle the escape key (or maybe meta) sooner.
+ The input received by the escape key is actually the same as the first
+ characters of e.g. Arrow-Up, so without knowing what follows the escape
+ sequence, we don't know whether escape has been pressed, or whether
+ it's something else. This flush function should be called after a
+ timeout, and processes everything that's still in the buffer as-is, so
+ without assuming any characters will folow.
+ """
+ self._input_parser.send(_Flush)
+
+ def feed_and_flush(self, data):
+ """
+ Wrapper around ``feed`` and ``flush``.
+ """
+ self.feed(data)
+ self.flush()
+
+
+class raw_mode(object):
+ """
+ ::
+
+ with raw_mode(stdin):
+ ''' the pseudo-terminal stdin is now used in raw mode '''
+
+ We ignore errors when executing `tcgetattr` fails.
+ """
+ # There are several reasons for ignoring errors:
+ # 1. To avoid the "Inappropriate ioctl for device" crash if somebody would
+ # execute this code (In a Python REPL, for instance):
+ #
+ # import os; f = open(os.devnull); os.dup2(f.fileno(), 0)
+ #
+ # The result is that the eventloop will stop correctly, because it has
+ # to logic to quit when stdin is closed. However, we should not fail at
+ # this point. See:
+ # https://github.com/jonathanslenders/python-prompt-toolkit/pull/393
+ # https://github.com/jonathanslenders/python-prompt-toolkit/issues/392
+
+ # 2. Related, when stdin is an SSH pipe, and no full terminal was allocated.
+ # See: https://github.com/jonathanslenders/python-prompt-toolkit/pull/165
+ def __init__(self, fileno):
+ self.fileno = fileno
+ try:
+ self.attrs_before = termios.tcgetattr(fileno)
+ except termios.error:
+ # Ignore attribute errors.
+ self.attrs_before = None
+
+ def __enter__(self):
+ # NOTE: On os X systems, using pty.setraw() fails. Therefor we are using this:
+ try:
+ newattr = termios.tcgetattr(self.fileno)
+ except termios.error:
+ pass
+ else:
+ newattr[tty.LFLAG] = self._patch_lflag(newattr[tty.LFLAG])
+ newattr[tty.IFLAG] = self._patch_iflag(newattr[tty.IFLAG])
+
+ # VMIN defines the number of characters read at a time in
+ # non-canonical mode. It seems to default to 1 on Linux, but on
+ # Solaris and derived operating systems it defaults to 4. (This is
+ # because the VMIN slot is the same as the VEOF slot, which
+ # defaults to ASCII EOT = Ctrl-D = 4.)
+ newattr[tty.CC][termios.VMIN] = 1
+
+ termios.tcsetattr(self.fileno, termios.TCSANOW, newattr)
+
+ # Put the terminal in cursor mode. (Instead of application mode.)
+ os.write(self.fileno, b'\x1b[?1l')
+
+ @classmethod
+ def _patch_lflag(cls, attrs):
+ return attrs & ~(termios.ECHO | termios.ICANON | termios.IEXTEN | termios.ISIG)
+
+ @classmethod
+ def _patch_iflag(cls, attrs):
+ return attrs & ~(
+ # Disable XON/XOFF flow control on output and input.
+ # (Don't capture Ctrl-S and Ctrl-Q.)
+ # Like executing: "stty -ixon."
+ termios.IXON | termios.IXOFF |
+
+ # Don't translate carriage return into newline on input.
+ termios.ICRNL | termios.INLCR | termios.IGNCR
+ )
+
+ def __exit__(self, *a, **kw):
+ if self.attrs_before is not None:
+ try:
+ termios.tcsetattr(self.fileno, termios.TCSANOW, self.attrs_before)
+ except termios.error:
+ pass
+
+ # # Put the terminal in application mode.
+ # self._stdout.write('\x1b[?1h')
+
+
+class cooked_mode(raw_mode):
+ """
+ The opposide of ``raw_mode``, used when we need cooked mode inside a
+ `raw_mode` block. Used in `CommandLineInterface.run_in_terminal`.::
+
+ with cooked_mode(stdin):
+ ''' the pseudo-terminal stdin is now used in cooked mode. '''
+ """
+ @classmethod
+ def _patch_lflag(cls, attrs):
+ return attrs | (termios.ECHO | termios.ICANON | termios.IEXTEN | termios.ISIG)
+
+ @classmethod
+ def _patch_iflag(cls, attrs):
+ # Turn the ICRNL flag back on. (Without this, calling `input()` in
+ # run_in_terminal doesn't work and displays ^M instead. Ptpython
+ # evaluates commands using `run_in_terminal`, so it's important that
+ # they translate ^M back into ^J.)
+ return attrs | termios.ICRNL
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/terminal/vt100_output.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/terminal/vt100_output.py
new file mode 100644
index 0000000000..b800aaacec
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/terminal/vt100_output.py
@@ -0,0 +1,632 @@
+"""
+Output for vt100 terminals.
+
+A lot of thanks, regarding outputting of colors, goes to the Pygments project:
+(We don't rely on Pygments anymore, because many things are very custom, and
+everything has been highly optimized.)
+http://pygments.org/
+"""
+from __future__ import unicode_literals
+
+from prompt_toolkit.filters import to_simple_filter, Condition
+from prompt_toolkit.layout.screen import Size
+from prompt_toolkit.renderer import Output
+from prompt_toolkit.styles import ANSI_COLOR_NAMES
+
+from six.moves import range
+import array
+import errno
+import os
+import six
+
+__all__ = (
+ 'Vt100_Output',
+)
+
+
+FG_ANSI_COLORS = {
+ 'ansidefault': 39,
+
+ # Low intensity.
+ 'ansiblack': 30,
+ 'ansidarkred': 31,
+ 'ansidarkgreen': 32,
+ 'ansibrown': 33,
+ 'ansidarkblue': 34,
+ 'ansipurple': 35,
+ 'ansiteal': 36,
+ 'ansilightgray': 37,
+
+ # High intensity.
+ 'ansidarkgray': 90,
+ 'ansired': 91,
+ 'ansigreen': 92,
+ 'ansiyellow': 93,
+ 'ansiblue': 94,
+ 'ansifuchsia': 95,
+ 'ansiturquoise': 96,
+ 'ansiwhite': 97,
+}
+
+BG_ANSI_COLORS = {
+ 'ansidefault': 49,
+
+ # Low intensity.
+ 'ansiblack': 40,
+ 'ansidarkred': 41,
+ 'ansidarkgreen': 42,
+ 'ansibrown': 43,
+ 'ansidarkblue': 44,
+ 'ansipurple': 45,
+ 'ansiteal': 46,
+ 'ansilightgray': 47,
+
+ # High intensity.
+ 'ansidarkgray': 100,
+ 'ansired': 101,
+ 'ansigreen': 102,
+ 'ansiyellow': 103,
+ 'ansiblue': 104,
+ 'ansifuchsia': 105,
+ 'ansiturquoise': 106,
+ 'ansiwhite': 107,
+}
+
+
+ANSI_COLORS_TO_RGB = {
+ 'ansidefault': (0x00, 0x00, 0x00), # Don't use, 'default' doesn't really have a value.
+ 'ansiblack': (0x00, 0x00, 0x00),
+ 'ansidarkgray': (0x7f, 0x7f, 0x7f),
+ 'ansiwhite': (0xff, 0xff, 0xff),
+ 'ansilightgray': (0xe5, 0xe5, 0xe5),
+
+ # Low intensity.
+ 'ansidarkred': (0xcd, 0x00, 0x00),
+ 'ansidarkgreen': (0x00, 0xcd, 0x00),
+ 'ansibrown': (0xcd, 0xcd, 0x00),
+ 'ansidarkblue': (0x00, 0x00, 0xcd),
+ 'ansipurple': (0xcd, 0x00, 0xcd),
+ 'ansiteal': (0x00, 0xcd, 0xcd),
+
+ # High intensity.
+ 'ansired': (0xff, 0x00, 0x00),
+ 'ansigreen': (0x00, 0xff, 0x00),
+ 'ansiyellow': (0xff, 0xff, 0x00),
+ 'ansiblue': (0x00, 0x00, 0xff),
+ 'ansifuchsia': (0xff, 0x00, 0xff),
+ 'ansiturquoise': (0x00, 0xff, 0xff),
+}
+
+
+assert set(FG_ANSI_COLORS) == set(ANSI_COLOR_NAMES)
+assert set(BG_ANSI_COLORS) == set(ANSI_COLOR_NAMES)
+assert set(ANSI_COLORS_TO_RGB) == set(ANSI_COLOR_NAMES)
+
+
+def _get_closest_ansi_color(r, g, b, exclude=()):
+ """
+ Find closest ANSI color. Return it by name.
+
+ :param r: Red (Between 0 and 255.)
+ :param g: Green (Between 0 and 255.)
+ :param b: Blue (Between 0 and 255.)
+ :param exclude: A tuple of color names to exclude. (E.g. ``('ansired', )``.)
+ """
+ assert isinstance(exclude, tuple)
+
+ # When we have a bit of saturation, avoid the gray-like colors, otherwise,
+ # too often the distance to the gray color is less.
+ saturation = abs(r - g) + abs(g - b) + abs(b - r) # Between 0..510
+
+ if saturation > 30:
+ exclude += ('ansilightgray', 'ansidarkgray', 'ansiwhite', 'ansiblack')
+
+ # Take the closest color.
+ # (Thanks to Pygments for this part.)
+ distance = 257*257*3 # "infinity" (>distance from #000000 to #ffffff)
+ match = 'ansidefault'
+
+ for name, (r2, g2, b2) in ANSI_COLORS_TO_RGB.items():
+ if name != 'ansidefault' and name not in exclude:
+ d = (r - r2) ** 2 + (g - g2) ** 2 + (b - b2) ** 2
+
+ if d < distance:
+ match = name
+ distance = d
+
+ return match
+
+
+class _16ColorCache(dict):
+ """
+ Cache which maps (r, g, b) tuples to 16 ansi colors.
+
+ :param bg: Cache for background colors, instead of foreground.
+ """
+ def __init__(self, bg=False):
+ assert isinstance(bg, bool)
+ self.bg = bg
+
+ def get_code(self, value, exclude=()):
+ """
+ Return a (ansi_code, ansi_name) tuple. (E.g. ``(44, 'ansiblue')``.) for
+ a given (r,g,b) value.
+ """
+ key = (value, exclude)
+ if key not in self:
+ self[key] = self._get(value, exclude)
+ return self[key]
+
+ def _get(self, value, exclude=()):
+ r, g, b = value
+ match = _get_closest_ansi_color(r, g, b, exclude=exclude)
+
+ # Turn color name into code.
+ if self.bg:
+ code = BG_ANSI_COLORS[match]
+ else:
+ code = FG_ANSI_COLORS[match]
+
+ self[value] = code
+ return code, match
+
+
+class _256ColorCache(dict):
+ """
+ Cach which maps (r, g, b) tuples to 256 colors.
+ """
+ def __init__(self):
+ # Build color table.
+ colors = []
+
+ # colors 0..15: 16 basic colors
+ colors.append((0x00, 0x00, 0x00)) # 0
+ colors.append((0xcd, 0x00, 0x00)) # 1
+ colors.append((0x00, 0xcd, 0x00)) # 2
+ colors.append((0xcd, 0xcd, 0x00)) # 3
+ colors.append((0x00, 0x00, 0xee)) # 4
+ colors.append((0xcd, 0x00, 0xcd)) # 5
+ colors.append((0x00, 0xcd, 0xcd)) # 6
+ colors.append((0xe5, 0xe5, 0xe5)) # 7
+ colors.append((0x7f, 0x7f, 0x7f)) # 8
+ colors.append((0xff, 0x00, 0x00)) # 9
+ colors.append((0x00, 0xff, 0x00)) # 10
+ colors.append((0xff, 0xff, 0x00)) # 11
+ colors.append((0x5c, 0x5c, 0xff)) # 12
+ colors.append((0xff, 0x00, 0xff)) # 13
+ colors.append((0x00, 0xff, 0xff)) # 14
+ colors.append((0xff, 0xff, 0xff)) # 15
+
+ # colors 16..232: the 6x6x6 color cube
+ valuerange = (0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff)
+
+ for i in range(217):
+ r = valuerange[(i // 36) % 6]
+ g = valuerange[(i // 6) % 6]
+ b = valuerange[i % 6]
+ colors.append((r, g, b))
+
+ # colors 233..253: grayscale
+ for i in range(1, 22):
+ v = 8 + i * 10
+ colors.append((v, v, v))
+
+ self.colors = colors
+
+ def __missing__(self, value):
+ r, g, b = value
+
+ # Find closest color.
+ # (Thanks to Pygments for this!)
+ distance = 257*257*3 # "infinity" (>distance from #000000 to #ffffff)
+ match = 0
+
+ for i, (r2, g2, b2) in enumerate(self.colors):
+ d = (r - r2) ** 2 + (g - g2) ** 2 + (b - b2) ** 2
+
+ if d < distance:
+ match = i
+ distance = d
+
+ # Turn color name into code.
+ self[value] = match
+ return match
+
+
+_16_fg_colors = _16ColorCache(bg=False)
+_16_bg_colors = _16ColorCache(bg=True)
+_256_colors = _256ColorCache()
+
+
+class _EscapeCodeCache(dict):
+ """
+ Cache for VT100 escape codes. It maps
+ (fgcolor, bgcolor, bold, underline, reverse) tuples to VT100 escape sequences.
+
+ :param true_color: When True, use 24bit colors instead of 256 colors.
+ """
+ def __init__(self, true_color=False, ansi_colors_only=False):
+ assert isinstance(true_color, bool)
+ self.true_color = true_color
+ self.ansi_colors_only = to_simple_filter(ansi_colors_only)
+
+ def __missing__(self, attrs):
+ fgcolor, bgcolor, bold, underline, italic, blink, reverse = attrs
+ parts = []
+
+ parts.extend(self._colors_to_code(fgcolor, bgcolor))
+
+ if bold:
+ parts.append('1')
+ if italic:
+ parts.append('3')
+ if blink:
+ parts.append('5')
+ if underline:
+ parts.append('4')
+ if reverse:
+ parts.append('7')
+
+ if parts:
+ result = '\x1b[0;' + ';'.join(parts) + 'm'
+ else:
+ result = '\x1b[0m'
+
+ self[attrs] = result
+ return result
+
+ def _color_name_to_rgb(self, color):
+ " Turn 'ffffff', into (0xff, 0xff, 0xff). "
+ try:
+ rgb = int(color, 16)
+ except ValueError:
+ raise
+ else:
+ r = (rgb >> 16) & 0xff
+ g = (rgb >> 8) & 0xff
+ b = rgb & 0xff
+ return r, g, b
+
+ def _colors_to_code(self, fg_color, bg_color):
+ " Return a tuple with the vt100 values that represent this color. "
+ # When requesting ANSI colors only, and both fg/bg color were converted
+ # to ANSI, ensure that the foreground and background color are not the
+ # same. (Unless they were explicitely defined to be the same color.)
+ fg_ansi = [()]
+
+ def get(color, bg):
+ table = BG_ANSI_COLORS if bg else FG_ANSI_COLORS
+
+ if color is None:
+ return ()
+
+ # 16 ANSI colors. (Given by name.)
+ elif color in table:
+ return (table[color], )
+
+ # RGB colors. (Defined as 'ffffff'.)
+ else:
+ try:
+ rgb = self._color_name_to_rgb(color)
+ except ValueError:
+ return ()
+
+ # When only 16 colors are supported, use that.
+ if self.ansi_colors_only():
+ if bg: # Background.
+ if fg_color != bg_color:
+ exclude = (fg_ansi[0], )
+ else:
+ exclude = ()
+ code, name = _16_bg_colors.get_code(rgb, exclude=exclude)
+ return (code, )
+ else: # Foreground.
+ code, name = _16_fg_colors.get_code(rgb)
+ fg_ansi[0] = name
+ return (code, )
+
+ # True colors. (Only when this feature is enabled.)
+ elif self.true_color:
+ r, g, b = rgb
+ return (48 if bg else 38, 2, r, g, b)
+
+ # 256 RGB colors.
+ else:
+ return (48 if bg else 38, 5, _256_colors[rgb])
+
+ result = []
+ result.extend(get(fg_color, False))
+ result.extend(get(bg_color, True))
+
+ return map(six.text_type, result)
+
+
+def _get_size(fileno):
+ # Thanks to fabric (fabfile.org), and
+ # http://sqizit.bartletts.id.au/2011/02/14/pseudo-terminals-in-python/
+ """
+ Get the size of this pseudo terminal.
+
+ :param fileno: stdout.fileno()
+ :returns: A (rows, cols) tuple.
+ """
+ # Inline imports, because these modules are not available on Windows.
+ # (This file is used by ConEmuOutput, which is used on Windows.)
+ import fcntl
+ import termios
+
+ # Buffer for the C call
+ buf = array.array(b'h' if six.PY2 else u'h', [0, 0, 0, 0])
+
+ # Do TIOCGWINSZ (Get)
+ # Note: We should not pass 'True' as a fourth parameter to 'ioctl'. (True
+ # is the default.) This causes segmentation faults on some systems.
+ # See: https://github.com/jonathanslenders/python-prompt-toolkit/pull/364
+ fcntl.ioctl(fileno, termios.TIOCGWINSZ, buf)
+
+ # Return rows, cols
+ return buf[0], buf[1]
+
+
+class Vt100_Output(Output):
+ """
+ :param get_size: A callable which returns the `Size` of the output terminal.
+ :param stdout: Any object with has a `write` and `flush` method + an 'encoding' property.
+ :param true_color: Use 24bit color instead of 256 colors. (Can be a :class:`SimpleFilter`.)
+ When `ansi_colors_only` is set, only 16 colors are used.
+ :param ansi_colors_only: Restrict to 16 ANSI colors only.
+ :param term: The terminal environment variable. (xterm, xterm-256color, linux, ...)
+ :param write_binary: Encode the output before writing it. If `True` (the
+ default), the `stdout` object is supposed to expose an `encoding` attribute.
+ """
+ def __init__(self, stdout, get_size, true_color=False,
+ ansi_colors_only=None, term=None, write_binary=True):
+ assert callable(get_size)
+ assert term is None or isinstance(term, six.text_type)
+ assert all(hasattr(stdout, a) for a in ('write', 'flush'))
+
+ if write_binary:
+ assert hasattr(stdout, 'encoding')
+
+ self._buffer = []
+ self.stdout = stdout
+ self.write_binary = write_binary
+ self.get_size = get_size
+ self.true_color = to_simple_filter(true_color)
+ self.term = term or 'xterm'
+
+ # ANSI colors only?
+ if ansi_colors_only is None:
+ # When not given, use the following default.
+ ANSI_COLORS_ONLY = bool(os.environ.get(
+ 'PROMPT_TOOLKIT_ANSI_COLORS_ONLY', False))
+
+ @Condition
+ def ansi_colors_only():
+ return ANSI_COLORS_ONLY or term in ('linux', 'eterm-color')
+ else:
+ ansi_colors_only = to_simple_filter(ansi_colors_only)
+
+ self.ansi_colors_only = ansi_colors_only
+
+ # Cache for escape codes.
+ self._escape_code_cache = _EscapeCodeCache(ansi_colors_only=ansi_colors_only)
+ self._escape_code_cache_true_color = _EscapeCodeCache(
+ true_color=True, ansi_colors_only=ansi_colors_only)
+
+ @classmethod
+ def from_pty(cls, stdout, true_color=False, ansi_colors_only=None, term=None):
+ """
+ Create an Output class from a pseudo terminal.
+ (This will take the dimensions by reading the pseudo
+ terminal attributes.)
+ """
+ assert stdout.isatty()
+ def get_size():
+ rows, columns = _get_size(stdout.fileno())
+ # If terminal (incorrectly) reports its size as 0, pick a reasonable default.
+ # See https://github.com/ipython/ipython/issues/10071
+ return Size(rows=(rows or 24), columns=(columns or 80))
+
+ return cls(stdout, get_size, true_color=true_color,
+ ansi_colors_only=ansi_colors_only, term=term)
+
+ def fileno(self):
+ " Return file descriptor. "
+ return self.stdout.fileno()
+
+ def encoding(self):
+ " Return encoding used for stdout. "
+ return self.stdout.encoding
+
+ def write_raw(self, data):
+ """
+ Write raw data to output.
+ """
+ self._buffer.append(data)
+
+ def write(self, data):
+ """
+ Write text to output.
+ (Removes vt100 escape codes. -- used for safely writing text.)
+ """
+ self._buffer.append(data.replace('\x1b', '?'))
+
+ def set_title(self, title):
+ """
+ Set terminal title.
+ """
+ if self.term not in ('linux', 'eterm-color'): # Not supported by the Linux console.
+ self.write_raw('\x1b]2;%s\x07' % title.replace('\x1b', '').replace('\x07', ''))
+
+ def clear_title(self):
+ self.set_title('')
+
+ def erase_screen(self):
+ """
+ Erases the screen with the background colour and moves the cursor to
+ home.
+ """
+ self.write_raw('\x1b[2J')
+
+ def enter_alternate_screen(self):
+ self.write_raw('\x1b[?1049h\x1b[H')
+
+ def quit_alternate_screen(self):
+ self.write_raw('\x1b[?1049l')
+
+ def enable_mouse_support(self):
+ self.write_raw('\x1b[?1000h')
+
+ # Enable urxvt Mouse mode. (For terminals that understand this.)
+ self.write_raw('\x1b[?1015h')
+
+ # Also enable Xterm SGR mouse mode. (For terminals that understand this.)
+ self.write_raw('\x1b[?1006h')
+
+ # Note: E.g. lxterminal understands 1000h, but not the urxvt or sgr
+ # extensions.
+
+ def disable_mouse_support(self):
+ self.write_raw('\x1b[?1000l')
+ self.write_raw('\x1b[?1015l')
+ self.write_raw('\x1b[?1006l')
+
+ def erase_end_of_line(self):
+ """
+ Erases from the current cursor position to the end of the current line.
+ """
+ self.write_raw('\x1b[K')
+
+ def erase_down(self):
+ """
+ Erases the screen from the current line down to the bottom of the
+ screen.
+ """
+ self.write_raw('\x1b[J')
+
+ def reset_attributes(self):
+ self.write_raw('\x1b[0m')
+
+ def set_attributes(self, attrs):
+ """
+ Create new style and output.
+
+ :param attrs: `Attrs` instance.
+ """
+ if self.true_color() and not self.ansi_colors_only():
+ self.write_raw(self._escape_code_cache_true_color[attrs])
+ else:
+ self.write_raw(self._escape_code_cache[attrs])
+
+ def disable_autowrap(self):
+ self.write_raw('\x1b[?7l')
+
+ def enable_autowrap(self):
+ self.write_raw('\x1b[?7h')
+
+ def enable_bracketed_paste(self):
+ self.write_raw('\x1b[?2004h')
+
+ def disable_bracketed_paste(self):
+ self.write_raw('\x1b[?2004l')
+
+ def cursor_goto(self, row=0, column=0):
+ """ Move cursor position. """
+ self.write_raw('\x1b[%i;%iH' % (row, column))
+
+ def cursor_up(self, amount):
+ if amount == 0:
+ pass
+ elif amount == 1:
+ self.write_raw('\x1b[A')
+ else:
+ self.write_raw('\x1b[%iA' % amount)
+
+ def cursor_down(self, amount):
+ if amount == 0:
+ pass
+ elif amount == 1:
+ # Note: Not the same as '\n', '\n' can cause the window content to
+ # scroll.
+ self.write_raw('\x1b[B')
+ else:
+ self.write_raw('\x1b[%iB' % amount)
+
+ def cursor_forward(self, amount):
+ if amount == 0:
+ pass
+ elif amount == 1:
+ self.write_raw('\x1b[C')
+ else:
+ self.write_raw('\x1b[%iC' % amount)
+
+ def cursor_backward(self, amount):
+ if amount == 0:
+ pass
+ elif amount == 1:
+ self.write_raw('\b') # '\x1b[D'
+ else:
+ self.write_raw('\x1b[%iD' % amount)
+
+ def hide_cursor(self):
+ self.write_raw('\x1b[?25l')
+
+ def show_cursor(self):
+ self.write_raw('\x1b[?12l\x1b[?25h') # Stop blinking cursor and show.
+
+ def flush(self):
+ """
+ Write to output stream and flush.
+ """
+ if not self._buffer:
+ return
+
+ data = ''.join(self._buffer)
+
+ try:
+ # (We try to encode ourself, because that way we can replace
+ # characters that don't exist in the character set, avoiding
+ # UnicodeEncodeError crashes. E.g. u'\xb7' does not appear in 'ascii'.)
+ # My Arch Linux installation of july 2015 reported 'ANSI_X3.4-1968'
+ # for sys.stdout.encoding in xterm.
+ if self.write_binary:
+ if hasattr(self.stdout, 'buffer'):
+ out = self.stdout.buffer # Py3.
+ else:
+ out = self.stdout
+ out.write(data.encode(self.stdout.encoding or 'utf-8', 'replace'))
+ else:
+ self.stdout.write(data)
+
+ self.stdout.flush()
+ except IOError as e:
+ if e.args and e.args[0] == errno.EINTR:
+ # Interrupted system call. Can happpen in case of a window
+ # resize signal. (Just ignore. The resize handler will render
+ # again anyway.)
+ pass
+ elif e.args and e.args[0] == 0:
+ # This can happen when there is a lot of output and the user
+ # sends a KeyboardInterrupt by pressing Control-C. E.g. in
+ # a Python REPL when we execute "while True: print('test')".
+ # (The `ptpython` REPL uses this `Output` class instead of
+ # `stdout` directly -- in order to be network transparent.)
+ # So, just ignore.
+ pass
+ else:
+ raise
+
+ self._buffer = []
+
+ def ask_for_cpr(self):
+ """
+ Asks for a cursor position report (CPR).
+ """
+ self.write_raw('\x1b[6n')
+ self.flush()
+
+ def bell(self):
+ " Sound bell. "
+ self.write_raw('\a')
+ self.flush()
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/terminal/win32_input.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/terminal/win32_input.py
new file mode 100644
index 0000000000..410e5fa517
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/terminal/win32_input.py
@@ -0,0 +1,364 @@
+from __future__ import unicode_literals
+from ctypes import windll, pointer
+from ctypes.wintypes import DWORD, HANDLE
+from six.moves import range
+
+from prompt_toolkit.key_binding.input_processor import KeyPress
+from prompt_toolkit.keys import Keys
+from prompt_toolkit.mouse_events import MouseEventType
+from prompt_toolkit.win32_types import EventTypes, KEY_EVENT_RECORD, MOUSE_EVENT_RECORD, INPUT_RECORD, STD_INPUT_HANDLE
+
+import msvcrt
+import os
+import sys
+import six
+
+__all__ = (
+ 'ConsoleInputReader',
+ 'raw_mode',
+ 'cooked_mode'
+)
+
+
+class ConsoleInputReader(object):
+ """
+ :param recognize_paste: When True, try to discover paste actions and turn
+ the event into a BracketedPaste.
+ """
+ # Keys with character data.
+ mappings = {
+ b'\x1b': Keys.Escape,
+
+ b'\x00': Keys.ControlSpace, # Control-Space (Also for Ctrl-@)
+ b'\x01': Keys.ControlA, # Control-A (home)
+ b'\x02': Keys.ControlB, # Control-B (emacs cursor left)
+ b'\x03': Keys.ControlC, # Control-C (interrupt)
+ b'\x04': Keys.ControlD, # Control-D (exit)
+ b'\x05': Keys.ControlE, # Contrel-E (end)
+ b'\x06': Keys.ControlF, # Control-F (cursor forward)
+ b'\x07': Keys.ControlG, # Control-G
+ b'\x08': Keys.ControlH, # Control-H (8) (Identical to '\b')
+ b'\x09': Keys.ControlI, # Control-I (9) (Identical to '\t')
+ b'\x0a': Keys.ControlJ, # Control-J (10) (Identical to '\n')
+ b'\x0b': Keys.ControlK, # Control-K (delete until end of line; vertical tab)
+ b'\x0c': Keys.ControlL, # Control-L (clear; form feed)
+ b'\x0d': Keys.ControlJ, # Control-J NOTE: Windows sends \r instead of
+ # \n when pressing enter. We turn it into \n
+ # to be compatible with other platforms.
+ b'\x0e': Keys.ControlN, # Control-N (14) (history forward)
+ b'\x0f': Keys.ControlO, # Control-O (15)
+ b'\x10': Keys.ControlP, # Control-P (16) (history back)
+ b'\x11': Keys.ControlQ, # Control-Q
+ b'\x12': Keys.ControlR, # Control-R (18) (reverse search)
+ b'\x13': Keys.ControlS, # Control-S (19) (forward search)
+ b'\x14': Keys.ControlT, # Control-T
+ b'\x15': Keys.ControlU, # Control-U
+ b'\x16': Keys.ControlV, # Control-V
+ b'\x17': Keys.ControlW, # Control-W
+ b'\x18': Keys.ControlX, # Control-X
+ b'\x19': Keys.ControlY, # Control-Y (25)
+ b'\x1a': Keys.ControlZ, # Control-Z
+
+ b'\x1c': Keys.ControlBackslash, # Both Control-\ and Ctrl-|
+ b'\x1d': Keys.ControlSquareClose, # Control-]
+ b'\x1e': Keys.ControlCircumflex, # Control-^
+ b'\x1f': Keys.ControlUnderscore, # Control-underscore (Also for Ctrl-hypen.)
+ b'\x7f': Keys.Backspace, # (127) Backspace
+ }
+
+ # Keys that don't carry character data.
+ keycodes = {
+ # Home/End
+ 33: Keys.PageUp,
+ 34: Keys.PageDown,
+ 35: Keys.End,
+ 36: Keys.Home,
+
+ # Arrows
+ 37: Keys.Left,
+ 38: Keys.Up,
+ 39: Keys.Right,
+ 40: Keys.Down,
+
+ 45: Keys.Insert,
+ 46: Keys.Delete,
+
+ # F-keys.
+ 112: Keys.F1,
+ 113: Keys.F2,
+ 114: Keys.F3,
+ 115: Keys.F4,
+ 116: Keys.F5,
+ 117: Keys.F6,
+ 118: Keys.F7,
+ 119: Keys.F8,
+ 120: Keys.F9,
+ 121: Keys.F10,
+ 122: Keys.F11,
+ 123: Keys.F12,
+ }
+
+ LEFT_ALT_PRESSED = 0x0002
+ RIGHT_ALT_PRESSED = 0x0001
+ SHIFT_PRESSED = 0x0010
+ LEFT_CTRL_PRESSED = 0x0008
+ RIGHT_CTRL_PRESSED = 0x0004
+
+ def __init__(self, recognize_paste=True):
+ self._fdcon = None
+ self.recognize_paste = recognize_paste
+
+ # When stdin is a tty, use that handle, otherwise, create a handle from
+ # CONIN$.
+ if sys.stdin.isatty():
+ self.handle = HANDLE(windll.kernel32.GetStdHandle(STD_INPUT_HANDLE))
+ else:
+ self._fdcon = os.open('CONIN$', os.O_RDWR | os.O_BINARY)
+ self.handle = HANDLE(msvcrt.get_osfhandle(self._fdcon))
+
+ def close(self):
+ " Close fdcon. "
+ if self._fdcon is not None:
+ os.close(self._fdcon)
+
+ def read(self):
+ """
+ Return a list of `KeyPress` instances. It won't return anything when
+ there was nothing to read. (This function doesn't block.)
+
+ http://msdn.microsoft.com/en-us/library/windows/desktop/ms684961(v=vs.85).aspx
+ """
+ max_count = 2048 # Max events to read at the same time.
+
+ read = DWORD(0)
+ arrtype = INPUT_RECORD * max_count
+ input_records = arrtype()
+
+ # Get next batch of input event.
+ windll.kernel32.ReadConsoleInputW(
+ self.handle, pointer(input_records), max_count, pointer(read))
+
+ # First, get all the keys from the input buffer, in order to determine
+ # whether we should consider this a paste event or not.
+ all_keys = list(self._get_keys(read, input_records))
+
+ if self.recognize_paste and self._is_paste(all_keys):
+ gen = iter(all_keys)
+ for k in gen:
+ # Pasting: if the current key consists of text or \n, turn it
+ # into a BracketedPaste.
+ data = []
+ while k and (isinstance(k.key, six.text_type) or
+ k.key == Keys.ControlJ):
+ data.append(k.data)
+ try:
+ k = next(gen)
+ except StopIteration:
+ k = None
+
+ if data:
+ yield KeyPress(Keys.BracketedPaste, ''.join(data))
+ if k is not None:
+ yield k
+ else:
+ for k in all_keys:
+ yield k
+
+ def _get_keys(self, read, input_records):
+ """
+ Generator that yields `KeyPress` objects from the input records.
+ """
+ for i in range(read.value):
+ ir = input_records[i]
+
+ # Get the right EventType from the EVENT_RECORD.
+ # (For some reason the Windows console application 'cmder'
+ # [http://gooseberrycreative.com/cmder/] can return '0' for
+ # ir.EventType. -- Just ignore that.)
+ if ir.EventType in EventTypes:
+ ev = getattr(ir.Event, EventTypes[ir.EventType])
+
+ # Process if this is a key event. (We also have mouse, menu and
+ # focus events.)
+ if type(ev) == KEY_EVENT_RECORD and ev.KeyDown:
+ for key_press in self._event_to_key_presses(ev):
+ yield key_press
+
+ elif type(ev) == MOUSE_EVENT_RECORD:
+ for key_press in self._handle_mouse(ev):
+ yield key_press
+
+ @staticmethod
+ def _is_paste(keys):
+ """
+ Return `True` when we should consider this list of keys as a paste
+ event. Pasted text on windows will be turned into a
+ `Keys.BracketedPaste` event. (It's not 100% correct, but it is probably
+ the best possible way to detect pasting of text and handle that
+ correctly.)
+ """
+ # Consider paste when it contains at least one newline and at least one
+ # other character.
+ text_count = 0
+ newline_count = 0
+
+ for k in keys:
+ if isinstance(k.key, six.text_type):
+ text_count += 1
+ if k.key == Keys.ControlJ:
+ newline_count += 1
+
+ return newline_count >= 1 and text_count > 1
+
+ def _event_to_key_presses(self, ev):
+ """
+ For this `KEY_EVENT_RECORD`, return a list of `KeyPress` instances.
+ """
+ assert type(ev) == KEY_EVENT_RECORD and ev.KeyDown
+
+ result = None
+
+ u_char = ev.uChar.UnicodeChar
+ ascii_char = u_char.encode('utf-8')
+
+ # NOTE: We don't use `ev.uChar.AsciiChar`. That appears to be latin-1
+ # encoded. See also:
+ # https://github.com/ipython/ipython/issues/10004
+ # https://github.com/jonathanslenders/python-prompt-toolkit/issues/389
+
+ if u_char == '\x00':
+ if ev.VirtualKeyCode in self.keycodes:
+ result = KeyPress(self.keycodes[ev.VirtualKeyCode], '')
+ else:
+ if ascii_char in self.mappings:
+ if self.mappings[ascii_char] == Keys.ControlJ:
+ u_char = '\n' # Windows sends \n, turn into \r for unix compatibility.
+ result = KeyPress(self.mappings[ascii_char], u_char)
+ else:
+ result = KeyPress(u_char, u_char)
+
+ # Correctly handle Control-Arrow keys.
+ if (ev.ControlKeyState & self.LEFT_CTRL_PRESSED or
+ ev.ControlKeyState & self.RIGHT_CTRL_PRESSED) and result:
+ if result.key == Keys.Left:
+ result.key = Keys.ControlLeft
+
+ if result.key == Keys.Right:
+ result.key = Keys.ControlRight
+
+ if result.key == Keys.Up:
+ result.key = Keys.ControlUp
+
+ if result.key == Keys.Down:
+ result.key = Keys.ControlDown
+
+ # Turn 'Tab' into 'BackTab' when shift was pressed.
+ if ev.ControlKeyState & self.SHIFT_PRESSED and result:
+ if result.key == Keys.Tab:
+ result.key = Keys.BackTab
+
+ # Turn 'Space' into 'ControlSpace' when control was pressed.
+ if (ev.ControlKeyState & self.LEFT_CTRL_PRESSED or
+ ev.ControlKeyState & self.RIGHT_CTRL_PRESSED) and result and result.data == ' ':
+ result = KeyPress(Keys.ControlSpace, ' ')
+
+ # Turn Control-Enter into META-Enter. (On a vt100 terminal, we cannot
+ # detect this combination. But it's really practical on Windows.)
+ if (ev.ControlKeyState & self.LEFT_CTRL_PRESSED or
+ ev.ControlKeyState & self.RIGHT_CTRL_PRESSED) and result and \
+ result.key == Keys.ControlJ:
+ return [KeyPress(Keys.Escape, ''), result]
+
+ # Return result. If alt was pressed, prefix the result with an
+ # 'Escape' key, just like unix VT100 terminals do.
+
+ # NOTE: Only replace the left alt with escape. The right alt key often
+ # acts as altgr and is used in many non US keyboard layouts for
+ # typing some special characters, like a backslash. We don't want
+ # all backslashes to be prefixed with escape. (Esc-\ has a
+ # meaning in E-macs, for instance.)
+ if result:
+ meta_pressed = ev.ControlKeyState & self.LEFT_ALT_PRESSED
+
+ if meta_pressed:
+ return [KeyPress(Keys.Escape, ''), result]
+ else:
+ return [result]
+
+ else:
+ return []
+
+ def _handle_mouse(self, ev):
+ """
+ Handle mouse events. Return a list of KeyPress instances.
+ """
+ FROM_LEFT_1ST_BUTTON_PRESSED = 0x1
+
+ result = []
+
+ # Check event type.
+ if ev.ButtonState == FROM_LEFT_1ST_BUTTON_PRESSED:
+ # On a key press, generate both the mouse down and up event.
+ for event_type in [MouseEventType.MOUSE_DOWN, MouseEventType.MOUSE_UP]:
+ data = ';'.join([
+ event_type,
+ str(ev.MousePosition.X),
+ str(ev.MousePosition.Y)
+ ])
+ result.append(KeyPress(Keys.WindowsMouseEvent, data))
+
+ return result
+
+
+class raw_mode(object):
+ """
+ ::
+
+ with raw_mode(stdin):
+ ''' the windows terminal is now in 'raw' mode. '''
+
+ The ``fileno`` attribute is ignored. This is to be compatble with the
+ `raw_input` method of `.vt100_input`.
+ """
+ def __init__(self, fileno=None):
+ self.handle = HANDLE(windll.kernel32.GetStdHandle(STD_INPUT_HANDLE))
+
+ def __enter__(self):
+ # Remember original mode.
+ original_mode = DWORD()
+ windll.kernel32.GetConsoleMode(self.handle, pointer(original_mode))
+ self.original_mode = original_mode
+
+ self._patch()
+
+ def _patch(self):
+ # Set raw
+ ENABLE_ECHO_INPUT = 0x0004
+ ENABLE_LINE_INPUT = 0x0002
+ ENABLE_PROCESSED_INPUT = 0x0001
+
+ windll.kernel32.SetConsoleMode(
+ self.handle, self.original_mode.value &
+ ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT))
+
+ def __exit__(self, *a, **kw):
+ # Restore original mode
+ windll.kernel32.SetConsoleMode(self.handle, self.original_mode)
+
+
+class cooked_mode(raw_mode):
+ """
+ ::
+
+ with cooked_mode(stdin):
+ ''' the pseudo-terminal stdin is now used in raw mode '''
+ """
+ def _patch(self):
+ # Set cooked.
+ ENABLE_ECHO_INPUT = 0x0004
+ ENABLE_LINE_INPUT = 0x0002
+ ENABLE_PROCESSED_INPUT = 0x0001
+
+ windll.kernel32.SetConsoleMode(
+ self.handle, self.original_mode.value |
+ (ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT))
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/terminal/win32_output.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/terminal/win32_output.py
new file mode 100644
index 0000000000..d4dddbab42
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/terminal/win32_output.py
@@ -0,0 +1,556 @@
+from __future__ import unicode_literals
+
+from ctypes import windll, byref, ArgumentError, c_char, c_long, c_ulong, c_uint, pointer
+from ctypes.wintypes import DWORD, HANDLE
+
+from prompt_toolkit.renderer import Output
+from prompt_toolkit.styles import ANSI_COLOR_NAMES
+from prompt_toolkit.win32_types import CONSOLE_SCREEN_BUFFER_INFO, STD_OUTPUT_HANDLE, STD_INPUT_HANDLE, COORD, SMALL_RECT
+
+import os
+import six
+
+__all__ = (
+ 'Win32Output',
+)
+
+
+def _coord_byval(coord):
+ """
+ Turns a COORD object into a c_long.
+ This will cause it to be passed by value instead of by reference. (That is what I think at least.)
+
+ When runing ``ptipython`` is run (only with IPython), we often got the following error::
+
+ Error in 'SetConsoleCursorPosition'.
+ ArgumentError("argument 2: <class 'TypeError'>: wrong type",)
+ argument 2: <class 'TypeError'>: wrong type
+
+ It was solved by turning ``COORD`` parameters into a ``c_long`` like this.
+
+ More info: http://msdn.microsoft.com/en-us/library/windows/desktop/ms686025(v=vs.85).aspx
+ """
+ return c_long(coord.Y * 0x10000 | coord.X & 0xFFFF)
+
+
+#: If True: write the output of the renderer also to the following file. This
+#: is very useful for debugging. (e.g.: to see that we don't write more bytes
+#: than required.)
+_DEBUG_RENDER_OUTPUT = False
+_DEBUG_RENDER_OUTPUT_FILENAME = r'prompt-toolkit-windows-output.log'
+
+
+class NoConsoleScreenBufferError(Exception):
+ """
+ Raised when the application is not running inside a Windows Console, but
+ the user tries to instantiate Win32Output.
+ """
+ def __init__(self):
+ # Are we running in 'xterm' on Windows, like git-bash for instance?
+ xterm = 'xterm' in os.environ.get('TERM', '')
+
+ if xterm:
+ message = ('Found %s, while expecting a Windows console. '
+ 'Maybe try to run this program using "winpty" '
+ 'or run it in cmd.exe instead. Or otherwise, '
+ 'in case of Cygwin, use the Python executable '
+ 'that is compiled for Cygwin.' % os.environ['TERM'])
+ else:
+ message = 'No Windows console found. Are you running cmd.exe?'
+ super(NoConsoleScreenBufferError, self).__init__(message)
+
+
+class Win32Output(Output):
+ """
+ I/O abstraction for rendering to Windows consoles.
+ (cmd.exe and similar.)
+ """
+ def __init__(self, stdout, use_complete_width=False):
+ self.use_complete_width = use_complete_width
+
+ self._buffer = []
+ self.stdout = stdout
+ self.hconsole = HANDLE(windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE))
+
+ self._in_alternate_screen = False
+
+ self.color_lookup_table = ColorLookupTable()
+
+ # Remember the default console colors.
+ info = self.get_win32_screen_buffer_info()
+ self.default_attrs = info.wAttributes if info else 15
+
+ if _DEBUG_RENDER_OUTPUT:
+ self.LOG = open(_DEBUG_RENDER_OUTPUT_FILENAME, 'ab')
+
+ def fileno(self):
+ " Return file descriptor. "
+ return self.stdout.fileno()
+
+ def encoding(self):
+ " Return encoding used for stdout. "
+ return self.stdout.encoding
+
+ def write(self, data):
+ self._buffer.append(data)
+
+ def write_raw(self, data):
+ " For win32, there is no difference between write and write_raw. "
+ self.write(data)
+
+ def get_size(self):
+ from prompt_toolkit.layout.screen import Size
+ info = self.get_win32_screen_buffer_info()
+
+ # We take the width of the *visible* region as the size. Not the width
+ # of the complete screen buffer. (Unless use_complete_width has been
+ # set.)
+ if self.use_complete_width:
+ width = info.dwSize.X
+ else:
+ width = info.srWindow.Right - info.srWindow.Left
+
+ height = info.srWindow.Bottom - info.srWindow.Top + 1
+
+ # We avoid the right margin, windows will wrap otherwise.
+ maxwidth = info.dwSize.X - 1
+ width = min(maxwidth, width)
+
+ # Create `Size` object.
+ return Size(rows=height, columns=width)
+
+ def _winapi(self, func, *a, **kw):
+ """
+ Flush and call win API function.
+ """
+ self.flush()
+
+ if _DEBUG_RENDER_OUTPUT:
+ self.LOG.write(('%r' % func.__name__).encode('utf-8') + b'\n')
+ self.LOG.write(b' ' + ', '.join(['%r' % i for i in a]).encode('utf-8') + b'\n')
+ self.LOG.write(b' ' + ', '.join(['%r' % type(i) for i in a]).encode('utf-8') + b'\n')
+ self.LOG.flush()
+
+ try:
+ return func(*a, **kw)
+ except ArgumentError as e:
+ if _DEBUG_RENDER_OUTPUT:
+ self.LOG.write((' Error in %r %r %s\n' % (func.__name__, e, e)).encode('utf-8'))
+
+ def get_win32_screen_buffer_info(self):
+ """
+ Return Screen buffer info.
+ """
+ # NOTE: We don't call the `GetConsoleScreenBufferInfo` API through
+ # `self._winapi`. Doing so causes Python to crash on certain 64bit
+ # Python versions. (Reproduced with 64bit Python 2.7.6, on Windows
+ # 10). It is not clear why. Possibly, it has to do with passing
+ # these objects as an argument, or through *args.
+
+ # The Python documentation contains the following - possibly related - warning:
+ # ctypes does not support passing unions or structures with
+ # bit-fields to functions by value. While this may work on 32-bit
+ # x86, it's not guaranteed by the library to work in the general
+ # case. Unions and structures with bit-fields should always be
+ # passed to functions by pointer.
+
+ # Also see:
+ # - https://github.com/ipython/ipython/issues/10070
+ # - https://github.com/jonathanslenders/python-prompt-toolkit/issues/406
+ # - https://github.com/jonathanslenders/python-prompt-toolkit/issues/86
+
+ self.flush()
+ sbinfo = CONSOLE_SCREEN_BUFFER_INFO()
+ success = windll.kernel32.GetConsoleScreenBufferInfo(self.hconsole, byref(sbinfo))
+
+ # success = self._winapi(windll.kernel32.GetConsoleScreenBufferInfo,
+ # self.hconsole, byref(sbinfo))
+
+ if success:
+ return sbinfo
+ else:
+ raise NoConsoleScreenBufferError
+
+ def set_title(self, title):
+ """
+ Set terminal title.
+ """
+ assert isinstance(title, six.text_type)
+ self._winapi(windll.kernel32.SetConsoleTitleW, title)
+
+ def clear_title(self):
+ self._winapi(windll.kernel32.SetConsoleTitleW, '')
+
+ def erase_screen(self):
+ start = COORD(0, 0)
+ sbinfo = self.get_win32_screen_buffer_info()
+ length = sbinfo.dwSize.X * sbinfo.dwSize.Y
+
+ self.cursor_goto(row=0, column=0)
+ self._erase(start, length)
+
+ def erase_down(self):
+ sbinfo = self.get_win32_screen_buffer_info()
+ size = sbinfo.dwSize
+
+ start = sbinfo.dwCursorPosition
+ length = ((size.X - size.X) + size.X * (size.Y - sbinfo.dwCursorPosition.Y))
+
+ self._erase(start, length)
+
+ def erase_end_of_line(self):
+ """
+ """
+ sbinfo = self.get_win32_screen_buffer_info()
+ start = sbinfo.dwCursorPosition
+ length = sbinfo.dwSize.X - sbinfo.dwCursorPosition.X
+
+ self._erase(start, length)
+
+ def _erase(self, start, length):
+ chars_written = c_ulong()
+
+ self._winapi(windll.kernel32.FillConsoleOutputCharacterA,
+ self.hconsole, c_char(b' '), DWORD(length), _coord_byval(start),
+ byref(chars_written))
+
+ # Reset attributes.
+ sbinfo = self.get_win32_screen_buffer_info()
+ self._winapi(windll.kernel32.FillConsoleOutputAttribute,
+ self.hconsole, sbinfo.wAttributes, length, _coord_byval(start),
+ byref(chars_written))
+
+ def reset_attributes(self):
+ " Reset the console foreground/background color. "
+ self._winapi(windll.kernel32.SetConsoleTextAttribute, self.hconsole,
+ self.default_attrs)
+
+ def set_attributes(self, attrs):
+ fgcolor, bgcolor, bold, underline, italic, blink, reverse = attrs
+
+ # Start from the default attributes.
+ attrs = self.default_attrs
+
+ # Override the last four bits: foreground color.
+ if fgcolor is not None:
+ attrs = attrs & ~0xf
+ attrs |= self.color_lookup_table.lookup_fg_color(fgcolor)
+
+ # Override the next four bits: background color.
+ if bgcolor is not None:
+ attrs = attrs & ~0xf0
+ attrs |= self.color_lookup_table.lookup_bg_color(bgcolor)
+
+ # Reverse: swap these four bits groups.
+ if reverse:
+ attrs = (attrs & ~0xff) | ((attrs & 0xf) << 4) | ((attrs & 0xf0) >> 4)
+
+ self._winapi(windll.kernel32.SetConsoleTextAttribute, self.hconsole, attrs)
+
+ def disable_autowrap(self):
+ # Not supported by Windows.
+ pass
+
+ def enable_autowrap(self):
+ # Not supported by Windows.
+ pass
+
+ def cursor_goto(self, row=0, column=0):
+ pos = COORD(x=column, y=row)
+ self._winapi(windll.kernel32.SetConsoleCursorPosition, self.hconsole, _coord_byval(pos))
+
+ def cursor_up(self, amount):
+ sr = self.get_win32_screen_buffer_info().dwCursorPosition
+ pos = COORD(sr.X, sr.Y - amount)
+ self._winapi(windll.kernel32.SetConsoleCursorPosition, self.hconsole, _coord_byval(pos))
+
+ def cursor_down(self, amount):
+ self.cursor_up(-amount)
+
+ def cursor_forward(self, amount):
+ sr = self.get_win32_screen_buffer_info().dwCursorPosition
+# assert sr.X + amount >= 0, 'Negative cursor position: x=%r amount=%r' % (sr.X, amount)
+
+ pos = COORD(max(0, sr.X + amount), sr.Y)
+ self._winapi(windll.kernel32.SetConsoleCursorPosition, self.hconsole, _coord_byval(pos))
+
+ def cursor_backward(self, amount):
+ self.cursor_forward(-amount)
+
+ def flush(self):
+ """
+ Write to output stream and flush.
+ """
+ if not self._buffer:
+ # Only flush stdout buffer. (It could be that Python still has
+ # something in its buffer. -- We want to be sure to print that in
+ # the correct color.)
+ self.stdout.flush()
+ return
+
+ data = ''.join(self._buffer)
+
+ if _DEBUG_RENDER_OUTPUT:
+ self.LOG.write(('%r' % data).encode('utf-8') + b'\n')
+ self.LOG.flush()
+
+ # Print characters one by one. This appears to be the best soluton
+ # in oder to avoid traces of vertical lines when the completion
+ # menu disappears.
+ for b in data:
+ written = DWORD()
+
+ retval = windll.kernel32.WriteConsoleW(self.hconsole, b, 1, byref(written), None)
+ assert retval != 0
+
+ self._buffer = []
+
+ def get_rows_below_cursor_position(self):
+ info = self.get_win32_screen_buffer_info()
+ return info.srWindow.Bottom - info.dwCursorPosition.Y + 1
+
+ def scroll_buffer_to_prompt(self):
+ """
+ To be called before drawing the prompt. This should scroll the console
+ to left, with the cursor at the bottom (if possible).
+ """
+ # Get current window size
+ info = self.get_win32_screen_buffer_info()
+ sr = info.srWindow
+ cursor_pos = info.dwCursorPosition
+
+ result = SMALL_RECT()
+
+ # Scroll to the left.
+ result.Left = 0
+ result.Right = sr.Right - sr.Left
+
+ # Scroll vertical
+ win_height = sr.Bottom - sr.Top
+ if 0 < sr.Bottom - cursor_pos.Y < win_height - 1:
+ # no vertical scroll if cursor already on the screen
+ result.Bottom = sr.Bottom
+ else:
+ result.Bottom = max(win_height, cursor_pos.Y)
+ result.Top = result.Bottom - win_height
+
+ # Scroll API
+ self._winapi(windll.kernel32.SetConsoleWindowInfo, self.hconsole, True, byref(result))
+
+ def enter_alternate_screen(self):
+ """
+ Go to alternate screen buffer.
+ """
+ if not self._in_alternate_screen:
+ GENERIC_READ = 0x80000000
+ GENERIC_WRITE = 0x40000000
+
+ # Create a new console buffer and activate that one.
+ handle = HANDLE(self._winapi(windll.kernel32.CreateConsoleScreenBuffer, GENERIC_READ|GENERIC_WRITE,
+ DWORD(0), None, DWORD(1), None))
+
+ self._winapi(windll.kernel32.SetConsoleActiveScreenBuffer, handle)
+ self.hconsole = handle
+ self._in_alternate_screen = True
+
+ def quit_alternate_screen(self):
+ """
+ Make stdout again the active buffer.
+ """
+ if self._in_alternate_screen:
+ stdout = HANDLE(self._winapi(windll.kernel32.GetStdHandle, STD_OUTPUT_HANDLE))
+ self._winapi(windll.kernel32.SetConsoleActiveScreenBuffer, stdout)
+ self._winapi(windll.kernel32.CloseHandle, self.hconsole)
+ self.hconsole = stdout
+ self._in_alternate_screen = False
+
+ def enable_mouse_support(self):
+ ENABLE_MOUSE_INPUT = 0x10
+ handle = HANDLE(windll.kernel32.GetStdHandle(STD_INPUT_HANDLE))
+
+ original_mode = DWORD()
+ self._winapi(windll.kernel32.GetConsoleMode, handle, pointer(original_mode))
+ self._winapi(windll.kernel32.SetConsoleMode, handle, original_mode.value | ENABLE_MOUSE_INPUT)
+
+ def disable_mouse_support(self):
+ ENABLE_MOUSE_INPUT = 0x10
+ handle = HANDLE(windll.kernel32.GetStdHandle(STD_INPUT_HANDLE))
+
+ original_mode = DWORD()
+ self._winapi(windll.kernel32.GetConsoleMode, handle, pointer(original_mode))
+ self._winapi(windll.kernel32.SetConsoleMode, handle, original_mode.value & ~ ENABLE_MOUSE_INPUT)
+
+ def hide_cursor(self):
+ pass
+
+ def show_cursor(self):
+ pass
+
+ @classmethod
+ def win32_refresh_window(cls):
+ """
+ Call win32 API to refresh the whole Window.
+
+ This is sometimes necessary when the application paints background
+ for completion menus. When the menu disappears, it leaves traces due
+ to a bug in the Windows Console. Sending a repaint request solves it.
+ """
+ # Get console handle
+ handle = HANDLE(windll.kernel32.GetConsoleWindow())
+
+ RDW_INVALIDATE = 0x0001
+ windll.user32.RedrawWindow(handle, None, None, c_uint(RDW_INVALIDATE))
+
+
+class FOREGROUND_COLOR:
+ BLACK = 0x0000
+ BLUE = 0x0001
+ GREEN = 0x0002
+ CYAN = 0x0003
+ RED = 0x0004
+ MAGENTA = 0x0005
+ YELLOW = 0x0006
+ GRAY = 0x0007
+ INTENSITY = 0x0008 # Foreground color is intensified.
+
+
+class BACKROUND_COLOR:
+ BLACK = 0x0000
+ BLUE = 0x0010
+ GREEN = 0x0020
+ CYAN = 0x0030
+ RED = 0x0040
+ MAGENTA = 0x0050
+ YELLOW = 0x0060
+ GRAY = 0x0070
+ INTENSITY = 0x0080 # Background color is intensified.
+
+
+def _create_ansi_color_dict(color_cls):
+ " Create a table that maps the 16 named ansi colors to their Windows code. "
+ return {
+ 'ansidefault': color_cls.BLACK,
+ 'ansiblack': color_cls.BLACK,
+ 'ansidarkgray': color_cls.BLACK | color_cls.INTENSITY,
+ 'ansilightgray': color_cls.GRAY,
+ 'ansiwhite': color_cls.GRAY | color_cls.INTENSITY,
+
+ # Low intensity.
+ 'ansidarkred': color_cls.RED,
+ 'ansidarkgreen': color_cls.GREEN,
+ 'ansibrown': color_cls.YELLOW,
+ 'ansidarkblue': color_cls.BLUE,
+ 'ansipurple': color_cls.MAGENTA,
+ 'ansiteal': color_cls.CYAN,
+
+ # High intensity.
+ 'ansired': color_cls.RED | color_cls.INTENSITY,
+ 'ansigreen': color_cls.GREEN | color_cls.INTENSITY,
+ 'ansiyellow': color_cls.YELLOW | color_cls.INTENSITY,
+ 'ansiblue': color_cls.BLUE | color_cls.INTENSITY,
+ 'ansifuchsia': color_cls.MAGENTA | color_cls.INTENSITY,
+ 'ansiturquoise': color_cls.CYAN | color_cls.INTENSITY,
+ }
+
+FG_ANSI_COLORS = _create_ansi_color_dict(FOREGROUND_COLOR)
+BG_ANSI_COLORS = _create_ansi_color_dict(BACKROUND_COLOR)
+
+assert set(FG_ANSI_COLORS) == set(ANSI_COLOR_NAMES)
+assert set(BG_ANSI_COLORS) == set(ANSI_COLOR_NAMES)
+
+
+class ColorLookupTable(object):
+ """
+ Inspired by pygments/formatters/terminal256.py
+ """
+ def __init__(self):
+ self._win32_colors = self._build_color_table()
+ self.best_match = {} # Cache
+
+ @staticmethod
+ def _build_color_table():
+ """
+ Build an RGB-to-256 color conversion table
+ """
+ FG = FOREGROUND_COLOR
+ BG = BACKROUND_COLOR
+
+ return [
+ (0x00, 0x00, 0x00, FG.BLACK, BG.BLACK),
+ (0x00, 0x00, 0xaa, FG.BLUE, BG.BLUE),
+ (0x00, 0xaa, 0x00, FG.GREEN, BG.GREEN),
+ (0x00, 0xaa, 0xaa, FG.CYAN, BG.CYAN),
+ (0xaa, 0x00, 0x00, FG.RED, BG.RED),
+ (0xaa, 0x00, 0xaa, FG.MAGENTA, BG.MAGENTA),
+ (0xaa, 0xaa, 0x00, FG.YELLOW, BG.YELLOW),
+ (0x88, 0x88, 0x88, FG.GRAY, BG.GRAY),
+
+ (0x44, 0x44, 0xff, FG.BLUE | FG.INTENSITY, BG.BLUE | BG.INTENSITY),
+ (0x44, 0xff, 0x44, FG.GREEN | FG.INTENSITY, BG.GREEN | BG.INTENSITY),
+ (0x44, 0xff, 0xff, FG.CYAN | FG.INTENSITY, BG.CYAN | BG.INTENSITY),
+ (0xff, 0x44, 0x44, FG.RED | FG.INTENSITY, BG.RED | BG.INTENSITY),
+ (0xff, 0x44, 0xff, FG.MAGENTA | FG.INTENSITY, BG.MAGENTA | BG.INTENSITY),
+ (0xff, 0xff, 0x44, FG.YELLOW | FG.INTENSITY, BG.YELLOW | BG.INTENSITY),
+
+ (0x44, 0x44, 0x44, FG.BLACK | FG.INTENSITY, BG.BLACK | BG.INTENSITY),
+ (0xff, 0xff, 0xff, FG.GRAY | FG.INTENSITY, BG.GRAY | BG.INTENSITY),
+ ]
+
+ def _closest_color(self, r, g, b):
+ distance = 257 * 257 * 3 # "infinity" (>distance from #000000 to #ffffff)
+ fg_match = 0
+ bg_match = 0
+
+ for r_, g_, b_, fg_, bg_ in self._win32_colors:
+ rd = r - r_
+ gd = g - g_
+ bd = b - b_
+
+ d = rd * rd + gd * gd + bd * bd
+
+ if d < distance:
+ fg_match = fg_
+ bg_match = bg_
+ distance = d
+ return fg_match, bg_match
+
+ def _color_indexes(self, color):
+ indexes = self.best_match.get(color, None)
+ if indexes is None:
+ try:
+ rgb = int(str(color), 16)
+ except ValueError:
+ rgb = 0
+
+ r = (rgb >> 16) & 0xff
+ g = (rgb >> 8) & 0xff
+ b = rgb & 0xff
+ indexes = self._closest_color(r, g, b)
+ self.best_match[color] = indexes
+ return indexes
+
+ def lookup_fg_color(self, fg_color):
+ """
+ Return the color for use in the
+ `windll.kernel32.SetConsoleTextAttribute` API call.
+
+ :param fg_color: Foreground as text. E.g. 'ffffff' or 'red'
+ """
+ # Foreground.
+ if fg_color in FG_ANSI_COLORS:
+ return FG_ANSI_COLORS[fg_color]
+ else:
+ return self._color_indexes(fg_color)[0]
+
+ def lookup_bg_color(self, bg_color):
+ """
+ Return the color for use in the
+ `windll.kernel32.SetConsoleTextAttribute` API call.
+
+ :param bg_color: Background as text. E.g. 'ffffff' or 'red'
+ """
+ # Background.
+ if bg_color in BG_ANSI_COLORS:
+ return BG_ANSI_COLORS[bg_color]
+ else:
+ return self._color_indexes(bg_color)[1]
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/token.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/token.py
new file mode 100644
index 0000000000..5170daf38a
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/token.py
@@ -0,0 +1,47 @@
+"""
+The Token class, interchangeable with ``pygments.token``.
+
+A `Token` has some semantics for a piece of text that is given a style through
+a :class:`~prompt_toolkit.styles.Style` class. A pygments lexer for instance,
+returns a list of (Token, text) tuples. Each fragment of text has a token
+assigned, which when combined with a style sheet, will determine the fine
+style.
+"""
+
+# If we don't need any lexers or style classes from Pygments, we don't want
+# Pygments to be installed for only the following 10 lines of code. So, there
+# is some duplication, but this should stay compatible with Pygments.
+
+__all__ = (
+ 'Token',
+ 'ZeroWidthEscape',
+)
+
+
+class _TokenType(tuple):
+ def __getattr__(self, val):
+ if not val or not val[0].isupper():
+ return tuple.__getattribute__(self, val)
+
+ new = _TokenType(self + (val,))
+ setattr(self, val, new)
+ return new
+
+ def __repr__(self):
+ return 'Token' + (self and '.' or '') + '.'.join(self)
+
+
+# Prefer the Token class from Pygments. If Pygments is not installed, use our
+# minimalistic Token class.
+try:
+ from pygments.token import Token
+except ImportError:
+ Token = _TokenType()
+
+
+# Built-in tokens:
+
+#: `ZeroWidthEscape` can be used for raw VT escape sequences that don't
+#: cause the cursor position to move. (E.g. FinalTerm's escape sequences
+#: for shell integration.)
+ZeroWidthEscape = Token.ZeroWidthEscape
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/utils.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/utils.py
new file mode 100644
index 0000000000..3cd931883c
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/utils.py
@@ -0,0 +1,240 @@
+from __future__ import unicode_literals
+import inspect
+import os
+import signal
+import sys
+import threading
+import weakref
+
+from wcwidth import wcwidth
+from six.moves import range
+
+
+__all__ = (
+ 'Event',
+ 'DummyContext',
+ 'get_cwidth',
+ 'suspend_to_background_supported',
+ 'is_conemu_ansi',
+ 'is_windows',
+ 'in_main_thread',
+ 'take_using_weights',
+ 'test_callable_args',
+)
+
+
+class Event(object):
+ """
+ Simple event to which event handlers can be attached. For instance::
+
+ class Cls:
+ def __init__(self):
+ # Define event. The first parameter is the sender.
+ self.event = Event(self)
+
+ obj = Cls()
+
+ def handler(sender):
+ pass
+
+ # Add event handler by using the += operator.
+ obj.event += handler
+
+ # Fire event.
+ obj.event()
+ """
+ def __init__(self, sender, handler=None):
+ self.sender = sender
+ self._handlers = []
+
+ if handler is not None:
+ self += handler
+
+ def __call__(self):
+ " Fire event. "
+ for handler in self._handlers:
+ handler(self.sender)
+
+ def fire(self):
+ " Alias for just calling the event. "
+ self()
+
+ def __iadd__(self, handler):
+ """
+ Add another handler to this callback.
+ (Handler should be a callable that takes exactly one parameter: the
+ sender object.)
+ """
+ # Test handler.
+ assert callable(handler)
+ if not test_callable_args(handler, [None]):
+ raise TypeError("%r doesn't take exactly one argument." % handler)
+
+ # Add to list of event handlers.
+ self._handlers.append(handler)
+ return self
+
+ def __isub__(self, handler):
+ """
+ Remove a handler from this callback.
+ """
+ self._handlers.remove(handler)
+ return self
+
+
+# Cache of signatures. Improves the performance of `test_callable_args`.
+_signatures_cache = weakref.WeakKeyDictionary()
+
+
+def test_callable_args(func, args):
+ """
+ Return True when this function can be called with the given arguments.
+ """
+ assert isinstance(args, (list, tuple))
+ signature = getattr(inspect, 'signature', None)
+
+ if signature is not None:
+ # For Python 3, use inspect.signature.
+ try:
+ sig = _signatures_cache[func]
+ except KeyError:
+ sig = signature(func)
+ _signatures_cache[func] = sig
+
+ try:
+ sig.bind(*args)
+ except TypeError:
+ return False
+ else:
+ return True
+ else:
+ # For older Python versions, fall back to using getargspec.
+ spec = inspect.getargspec(func)
+
+ # Drop the 'self'
+ def drop_self(spec):
+ args, varargs, varkw, defaults = spec
+ if args[0:1] == ['self']:
+ args = args[1:]
+ return inspect.ArgSpec(args, varargs, varkw, defaults)
+
+ spec = drop_self(spec)
+
+ # When taking *args, always return True.
+ if spec.varargs is not None:
+ return True
+
+ # Test whether the given amount of args is between the min and max
+ # accepted argument counts.
+ return len(spec.args) - len(spec.defaults or []) <= len(args) <= len(spec.args)
+
+
+class DummyContext(object):
+ """
+ (contextlib.nested is not available on Py3)
+ """
+ def __enter__(self):
+ pass
+
+ def __exit__(self, *a):
+ pass
+
+
+class _CharSizesCache(dict):
+ """
+ Cache for wcwidth sizes.
+ """
+ def __missing__(self, string):
+ # Note: We use the `max(0, ...` because some non printable control
+ # characters, like e.g. Ctrl-underscore get a -1 wcwidth value.
+ # It can be possible that these characters end up in the input
+ # text.
+ if len(string) == 1:
+ result = max(0, wcwidth(string))
+ else:
+ result = sum(max(0, wcwidth(c)) for c in string)
+
+ # Cache for short strings.
+ # (It's hard to tell what we can consider short...)
+ if len(string) < 256:
+ self[string] = result
+
+ return result
+
+
+_CHAR_SIZES_CACHE = _CharSizesCache()
+
+
+def get_cwidth(string):
+ """
+ Return width of a string. Wrapper around ``wcwidth``.
+ """
+ return _CHAR_SIZES_CACHE[string]
+
+
+def suspend_to_background_supported():
+ """
+ Returns `True` when the Python implementation supports
+ suspend-to-background. This is typically `False' on Windows systems.
+ """
+ return hasattr(signal, 'SIGTSTP')
+
+
+def is_windows():
+ """
+ True when we are using Windows.
+ """
+ return sys.platform.startswith('win') # E.g. 'win32', not 'darwin' or 'linux2'
+
+
+def is_conemu_ansi():
+ """
+ True when the ConEmu Windows console is used.
+ """
+ return is_windows() and os.environ.get('ConEmuANSI', 'OFF') == 'ON'
+
+
+def in_main_thread():
+ """
+ True when the current thread is the main thread.
+ """
+ return threading.current_thread().__class__.__name__ == '_MainThread'
+
+
+def take_using_weights(items, weights):
+ """
+ Generator that keeps yielding items from the items list, in proportion to
+ their weight. For instance::
+
+ # Getting the first 70 items from this generator should have yielded 10
+ # times A, 20 times B and 40 times C, all distributed equally..
+ take_using_weights(['A', 'B', 'C'], [5, 10, 20])
+
+ :param items: List of items to take from.
+ :param weights: Integers representing the weight. (Numbers have to be
+ integers, not floats.)
+ """
+ assert isinstance(items, list)
+ assert isinstance(weights, list)
+ assert all(isinstance(i, int) for i in weights)
+ assert len(items) == len(weights)
+ assert len(items) > 0
+
+ already_taken = [0 for i in items]
+ item_count = len(items)
+ max_weight = max(weights)
+
+ i = 0
+ while True:
+ # Each iteration of this loop, we fill up until by (total_weight/max_weight).
+ adding = True
+ while adding:
+ adding = False
+
+ for item_i, item, weight in zip(range(item_count), items, weights):
+ if already_taken[item_i] < i * weight / float(max_weight):
+ yield item
+ already_taken[item_i] += 1
+ adding = True
+
+ i += 1
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/validation.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/validation.py
new file mode 100644
index 0000000000..0027873f88
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/validation.py
@@ -0,0 +1,64 @@
+"""
+Input validation for a `Buffer`.
+(Validators will be called before accepting input.)
+"""
+from __future__ import unicode_literals
+from .filters import to_simple_filter
+
+from abc import ABCMeta, abstractmethod
+from six import with_metaclass
+
+__all__ = (
+ 'ConditionalValidator',
+ 'ValidationError',
+ 'Validator',
+)
+
+
+class ValidationError(Exception):
+ """
+ Error raised by :meth:`.Validator.validate`.
+
+ :param cursor_position: The cursor position where the error occured.
+ :param message: Text.
+ """
+ def __init__(self, cursor_position=0, message=''):
+ super(ValidationError, self).__init__(message)
+ self.cursor_position = cursor_position
+ self.message = message
+
+ def __repr__(self):
+ return '%s(cursor_position=%r, message=%r)' % (
+ self.__class__.__name__, self.cursor_position, self.message)
+
+
+class Validator(with_metaclass(ABCMeta, object)):
+ """
+ Abstract base class for an input validator.
+ """
+ @abstractmethod
+ def validate(self, document):
+ """
+ Validate the input.
+ If invalid, this should raise a :class:`.ValidationError`.
+
+ :param document: :class:`~prompt_toolkit.document.Document` instance.
+ """
+ pass
+
+
+class ConditionalValidator(Validator):
+ """
+ Validator that can be switched on/off according to
+ a filter. (This wraps around another validator.)
+ """
+ def __init__(self, validator, filter):
+ assert isinstance(validator, Validator)
+
+ self.validator = validator
+ self.filter = to_simple_filter(filter)
+
+ def validate(self, document):
+ # Call the validator only if the filter is active.
+ if self.filter():
+ self.validator.validate(document)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/win32_types.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/win32_types.py
new file mode 100644
index 0000000000..ba2d90df8e
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/win32_types.py
@@ -0,0 +1,155 @@
+from ctypes import Union, Structure, c_char, c_short, c_long, c_ulong
+from ctypes.wintypes import DWORD, BOOL, LPVOID, WORD, WCHAR
+
+
+# Input/Output standard device numbers. Note that these are not handle objects.
+# It's the `windll.kernel32.GetStdHandle` system call that turns them into a
+# real handle object.
+STD_INPUT_HANDLE = c_ulong(-10)
+STD_OUTPUT_HANDLE = c_ulong(-11)
+STD_ERROR_HANDLE = c_ulong(-12)
+
+
+class COORD(Structure):
+ """
+ Struct in wincon.h
+ http://msdn.microsoft.com/en-us/library/windows/desktop/ms682119(v=vs.85).aspx
+ """
+ _fields_ = [
+ ('X', c_short), # Short
+ ('Y', c_short), # Short
+ ]
+
+ def __repr__(self):
+ return '%s(X=%r, Y=%r, type_x=%r, type_y=%r)' % (
+ self.__class__.__name__, self.X, self.Y, type(self.X), type(self.Y))
+
+
+class UNICODE_OR_ASCII(Union):
+ _fields_ = [
+ ('AsciiChar', c_char),
+ ('UnicodeChar', WCHAR),
+ ]
+
+
+class KEY_EVENT_RECORD(Structure):
+ """
+ http://msdn.microsoft.com/en-us/library/windows/desktop/ms684166(v=vs.85).aspx
+ """
+ _fields_ = [
+ ('KeyDown', c_long), # bool
+ ('RepeatCount', c_short), # word
+ ('VirtualKeyCode', c_short), # word
+ ('VirtualScanCode', c_short), # word
+ ('uChar', UNICODE_OR_ASCII), # Unicode or ASCII.
+ ('ControlKeyState', c_long) # double word
+ ]
+
+
+class MOUSE_EVENT_RECORD(Structure):
+ """
+ http://msdn.microsoft.com/en-us/library/windows/desktop/ms684239(v=vs.85).aspx
+ """
+ _fields_ = [
+ ('MousePosition', COORD),
+ ('ButtonState', c_long), # dword
+ ('ControlKeyState', c_long), # dword
+ ('EventFlags', c_long) # dword
+ ]
+
+
+class WINDOW_BUFFER_SIZE_RECORD(Structure):
+ """
+ http://msdn.microsoft.com/en-us/library/windows/desktop/ms687093(v=vs.85).aspx
+ """
+ _fields_ = [
+ ('Size', COORD)
+ ]
+
+
+class MENU_EVENT_RECORD(Structure):
+ """
+ http://msdn.microsoft.com/en-us/library/windows/desktop/ms684213(v=vs.85).aspx
+ """
+ _fields_ = [
+ ('CommandId', c_long) # uint
+ ]
+
+
+class FOCUS_EVENT_RECORD(Structure):
+ """
+ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683149(v=vs.85).aspx
+ """
+ _fields_ = [
+ ('SetFocus', c_long) # bool
+ ]
+
+
+class EVENT_RECORD(Union):
+ _fields_ = [
+ ('KeyEvent', KEY_EVENT_RECORD),
+ ('MouseEvent', MOUSE_EVENT_RECORD),
+ ('WindowBufferSizeEvent', WINDOW_BUFFER_SIZE_RECORD),
+ ('MenuEvent', MENU_EVENT_RECORD),
+ ('FocusEvent', FOCUS_EVENT_RECORD)
+ ]
+
+
+class INPUT_RECORD(Structure):
+ """
+ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683499(v=vs.85).aspx
+ """
+ _fields_ = [
+ ('EventType', c_short), # word
+ ('Event', EVENT_RECORD) # Union.
+ ]
+
+
+EventTypes = {
+ 1: 'KeyEvent',
+ 2: 'MouseEvent',
+ 4: 'WindowBufferSizeEvent',
+ 8: 'MenuEvent',
+ 16: 'FocusEvent'
+}
+
+
+class SMALL_RECT(Structure):
+ """struct in wincon.h."""
+ _fields_ = [
+ ("Left", c_short),
+ ("Top", c_short),
+ ("Right", c_short),
+ ("Bottom", c_short),
+ ]
+
+
+class CONSOLE_SCREEN_BUFFER_INFO(Structure):
+ """struct in wincon.h."""
+ _fields_ = [
+ ("dwSize", COORD),
+ ("dwCursorPosition", COORD),
+ ("wAttributes", WORD),
+ ("srWindow", SMALL_RECT),
+ ("dwMaximumWindowSize", COORD),
+ ]
+
+ def __str__(self):
+ return '(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)' % (
+ self.dwSize.Y, self.dwSize.X,
+ self.dwCursorPosition.Y, self.dwCursorPosition.X,
+ self.wAttributes,
+ self.srWindow.Top, self.srWindow.Left, self.srWindow.Bottom, self.srWindow.Right,
+ self.dwMaximumWindowSize.Y, self.dwMaximumWindowSize.X,
+ )
+
+
+class SECURITY_ATTRIBUTES(Structure):
+ """
+ http://msdn.microsoft.com/en-us/library/windows/desktop/aa379560(v=vs.85).aspx
+ """
+ _fields_ = [
+ ('nLength', DWORD),
+ ('lpSecurityDescriptor', LPVOID),
+ ('bInheritHandle', BOOL),
+ ]