Пример #1
0
    def __init__(self,
                 registry=None,
                 enable_vi_mode=Never(),
                 enable_system_bindings=Never(),
                 enable_search=Always(),
                 enable_open_in_editor=Never(),
                 enable_all=Always()):

        # Accept both Filters and booleans as input.
        enable_vi_mode = to_cli_filter(enable_vi_mode)
        enable_system_bindings = to_cli_filter(enable_system_bindings)
        enable_open_in_editor = to_cli_filter(enable_open_in_editor)
        enable_all = to_cli_filter(enable_all)

        # Create registry.
        assert registry is None or isinstance(registry, Registry)
        self.registry = registry or Registry()

        # Emacs mode filter is the opposite of Vi mode.
        enable_emacs_mode = ~enable_vi_mode

        # Vi state. (Object to keep track of in which Vi mode we are.)
        self.vi_state = ViState()

        # Load basic bindings.
        load_basic_bindings(self.registry, enable_all)

        load_basic_system_bindings(self.registry,
                                   enable_system_bindings & enable_all)

        # Load emacs bindings.
        load_emacs_bindings(self.registry, enable_emacs_mode & enable_all)

        load_emacs_open_in_editor_bindings(
            self.registry,
            enable_emacs_mode & enable_open_in_editor & enable_all)

        load_emacs_search_bindings(
            self.registry, enable_emacs_mode & enable_search & enable_all)

        load_emacs_system_bindings(
            self.registry,
            enable_emacs_mode & enable_system_bindings & enable_all)

        # Load Vi bindings.
        load_vi_bindings(self.registry,
                         self.vi_state,
                         enable_visual_key=~enable_open_in_editor,
                         filter=enable_vi_mode & enable_all)

        load_vi_open_in_editor_bindings(
            self.registry, self.vi_state,
            enable_vi_mode & enable_open_in_editor & enable_all)

        load_vi_search_bindings(self.registry, self.vi_state,
                                enable_vi_mode & enable_search & enable_all)

        load_vi_system_bindings(
            self.registry, self.vi_state,
            enable_vi_mode & enable_system_bindings & enable_all)
Пример #2
0
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)
Пример #3
0
def load_confirm_exit_bindings(python_input):
    """
    Handle yes/no key presses when the exit confirmation is shown.
    """
    registry = Registry()

    handle = registry.add_binding
    confirmation_visible = Condition(
        lambda cli: python_input.show_exit_confirmation)

    @handle('y', filter=confirmation_visible)
    @handle('Y', filter=confirmation_visible)
    @handle(Keys.ControlJ, filter=confirmation_visible)
    @handle(Keys.ControlD, filter=confirmation_visible)
    def _(event):
        """
        Really quit.
        """
        event.cli.exit()

    @handle(Keys.Any, filter=confirmation_visible)
    def _(event):
        """
        Cancel exit.
        """
        python_input.show_exit_confirmation = False

    return registry
Пример #4
0
def registry(handlers):
    registry = Registry()
    registry.add_binding(Keys.ControlX,
                         Keys.ControlC)(handlers.controlx_controlc)
    registry.add_binding(Keys.ControlX)(handlers.control_x)
    registry.add_binding(Keys.ControlD)(handlers.control_d)
    registry.add_binding(Keys.ControlSquareClose,
                         Keys.Any)(handlers.control_square_close_any)

    return registry
Пример #5
0
def select(message, *options):
    """
    Display a confirmation prompt.
    """
    styling = style_from_dict({
        Token.Key: 'bold',
        Token.DefaultKey: 'bold underline',
    })

    def _get_tokens(cli):
        yield (Token, message + ': ')
        for i, option in enumerate(options):
            if i:
                yield (Token, ', ')
            if option.default:
                yield (Token.DefaultKey, option.caption[0].upper())
            else:
                yield (Token.Key, option.caption[0].upper())
            yield (Token.Caption, option.caption[1:])
        yield (Token, '? ')

    def _event(option, event):
        event.cli.buffers[DEFAULT_BUFFER].text = option.output
        event.cli.set_return_value(option.value)

    registry = Registry()

    for option in options:
        handler = functools.partial(_event, option)
        for char in option.chars:
            registry.add_binding(char)(handler)
        if option.fallback:
            registry.add_binding(Keys.ControlC)(handler)
            registry.add_binding(Keys.Escape)(handler)
        if option.default:
            registry.add_binding(Keys.Enter)(handler)

    sys.stdout.flush()

    return run_application(
        create_prompt_application(
            get_prompt_tokens=_get_tokens,
            style=styling,
            key_bindings_registry=registry,
        ))
def registry(handlers):
    registry = Registry()
    registry.add_binding(Keys.ControlX, Keys.ControlC)(handlers.controlx_controlc)
    registry.add_binding(Keys.ControlX)(handlers.control_x)
    registry.add_binding(Keys.ControlD)(handlers.control_d)
    registry.add_binding(Keys.ControlSquareClose, Keys.Any)(handlers.control_square_close_any)

    return registry
Пример #7
0
def load_list_bindings():
    registry = Registry()
    handle = registry.add_binding

    @handle(Keys.Enter)
    def _accept_selection(event):
        buf = event.current_buffer
        buf.accept_action.validate_and_handle(event.cli, buf)

    return registry
Пример #8
0
def test_previous_key_sequence(processor, handlers):
    """
    test whether we receive the correct previous_key_sequence.
    """
    events = []

    def handler(event):
        events.append(event)

    # Build registry.
    registry = Registry()
    registry.add_binding('a', 'a')(handler)
    registry.add_binding('b', 'b')(handler)
    processor = InputProcessor(registry, lambda: None)

    # Create processor and feed keys.
    processor.feed(KeyPress('a', 'a'))
    processor.feed(KeyPress('a', 'a'))
    processor.feed(KeyPress('b', 'b'))
    processor.feed(KeyPress('b', 'b'))
    processor.process_keys()

    # Test.
    assert len(events) == 2
    assert len(events[0].key_sequence) == 2
    assert events[0].key_sequence[0].key == 'a'
    assert events[0].key_sequence[0].data == 'a'
    assert events[0].key_sequence[1].key == 'a'
    assert events[0].key_sequence[1].data == 'a'
    assert events[0].previous_key_sequence == []

    assert len(events[1].key_sequence) == 2
    assert events[1].key_sequence[0].key == 'b'
    assert events[1].key_sequence[0].data == 'b'
    assert events[1].key_sequence[1].key == 'b'
    assert events[1].key_sequence[1].data == 'b'
    assert len(events[1].previous_key_sequence) == 2
    assert events[1].previous_key_sequence[0].key == 'a'
    assert events[1].previous_key_sequence[0].data == 'a'
    assert events[1].previous_key_sequence[1].key == 'a'
    assert events[1].previous_key_sequence[1].data == 'a'
Пример #9
0
def load_sidebar_bindings(python_input):
    """
    Load bindings for the navigation in the sidebar.
    """
    registry = Registry()

    handle = registry.add_binding
    sidebar_visible = Condition(lambda cli: python_input.show_sidebar)

    @handle(Keys.Up, filter=sidebar_visible)
    @handle(Keys.ControlP, filter=sidebar_visible)
    @handle('k', filter=sidebar_visible)
    def _(event):
        " Go to previous option. "
        python_input.selected_option_index = (
            (python_input.selected_option_index - 1) %
            python_input.option_count)

    @handle(Keys.Down, filter=sidebar_visible)
    @handle(Keys.ControlN, filter=sidebar_visible)
    @handle('j', filter=sidebar_visible)
    def _(event):
        " Go to next option. "
        python_input.selected_option_index = (
            (python_input.selected_option_index + 1) %
            python_input.option_count)

    @handle(Keys.Right, filter=sidebar_visible)
    @handle('l', filter=sidebar_visible)
    @handle(' ', filter=sidebar_visible)
    def _(event):
        " Select next value for current option. "
        option = python_input.selected_option
        option.activate_next()

    @handle(Keys.Left, filter=sidebar_visible)
    @handle('h', filter=sidebar_visible)
    def _(event):
        " Select previous value for current option. "
        option = python_input.selected_option
        option.activate_previous()

    @handle(Keys.ControlC, filter=sidebar_visible)
    @handle(Keys.ControlG, filter=sidebar_visible)
    @handle(Keys.ControlD, filter=sidebar_visible)
    @handle(Keys.ControlJ, filter=sidebar_visible)
    @handle(Keys.Escape, filter=sidebar_visible)
    def _(event):
        " Hide sidebar. "
        python_input.show_sidebar = False

    return registry
Пример #10
0
    def setUp(self):
        class Handlers(object):
            def __init__(self):
                self.called = []

            def __getattr__(self, name):
                def func(event):
                    self.called.append(name)

                return func

        self.handlers = Handlers()

        self.registry = Registry()
        self.registry.add_binding(Keys.ControlX, Keys.ControlC)(
            self.handlers.controlx_controlc)
        self.registry.add_binding(Keys.ControlX)(self.handlers.control_x)
        self.registry.add_binding(Keys.ControlD)(self.handlers.control_d)
        self.registry.add_binding(Keys.ControlSquareClose, Keys.Any)(
            self.handlers.control_square_close_any)

        self.processor = InputProcessor(self.registry, lambda: None)
Пример #11
0
def load_cpr_bindings():
    registry = Registry()
    handle = registry.add_binding

    @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)

    return registry
Пример #12
0
    def setUp(self):
        class Handlers(object):
            def __init__(self):
                self.called = []

            def __getattr__(self, name):
                def func(event):
                    self.called.append(name)
                return func

        self.handlers = Handlers()

        self.registry = Registry()
        self.registry.add_binding(Keys.ControlX, Keys.ControlC)(self.handlers.controlx_controlc)
        self.registry.add_binding(Keys.ControlX)(self.handlers.control_x)
        self.registry.add_binding(Keys.ControlD)(self.handlers.control_d)
        self.registry.add_binding(Keys.ControlSquareClose, Keys.Any)(self.handlers.control_square_close_any)

        self.processor = InputProcessor(self.registry, lambda: None)
Пример #13
0
    def __init__(self,
                 registry=None,
                 enable_vi_mode=Never(),
                 enable_system_prompt=Never(),
                 enable_search=Always(),
                 enable_open_in_editor=Never()):

        assert registry is None or isinstance(registry, Registry)
        assert isinstance(enable_vi_mode, CLIFilter)
        assert isinstance(enable_system_prompt, CLIFilter)
        assert isinstance(enable_open_in_editor, CLIFilter)

        self.registry = registry or Registry()

        # Emacs mode filter is the opposite of Vi mode.
        enable_emacs_mode = ~enable_vi_mode

        # Vi state. (Object to keep track of in which Vi mode we are.)
        self.vi_state = ViState()

        # Load emacs bindings.
        load_emacs_bindings(self.registry, enable_emacs_mode)

        load_emacs_open_in_editor_bindings(
            self.registry, enable_emacs_mode & enable_open_in_editor)

        load_emacs_search_bindings(self.registry,
                                   enable_emacs_mode & enable_search)

        load_emacs_system_bindings(self.registry,
                                   enable_emacs_mode & enable_system_prompt)

        # Load Vi bindings.
        load_vi_bindings(self.registry, self.vi_state, enable_vi_mode)

        load_vi_open_in_editor_bindings(self.registry, self.vi_state,
                                        enable_vi_mode & enable_open_in_editor)

        load_vi_search_bindings(self.registry, self.vi_state,
                                enable_vi_mode & enable_search)

        load_vi_system_bindings(self.registry, self.vi_state,
                                enable_vi_mode & enable_system_prompt)
Пример #14
0
    def __init__(self, registry=None, enable_vi_mode=False,
                 enable_system_prompt=False, enable_search=True,
                 enable_open_in_editor=False):

        self.registry = registry or Registry()

        # Flags. You can change these anytime.
        self.enable_vi_mode = enable_vi_mode
        self.enable_system_prompt = enable_system_prompt
        self.enable_search = enable_search
        self.enable_open_in_editor = enable_open_in_editor

        # Create set of filters to enable/disable sets of key bindings at
        # runtime.
        vi_mode_enabled = ViModeEnabled(self)
        emacs_mode_enabled = ~ vi_mode_enabled
        system_prompt_enabled = SystemPromptEnabled(self)
        search_enabled = SearchEnabled(self)
        open_in_editor_enabled = OpenInEditorEnabled(self)

        # Vi state. (Object to keep track of in which Vi mode we are.)
        self.vi_state = ViState()

        # Load all bindings in the registry with the correct filters.
        load_emacs_bindings(self.registry, emacs_mode_enabled)
        load_emacs_open_in_editor_bindings(
            self.registry, emacs_mode_enabled & open_in_editor_enabled)
        load_emacs_search_bindings(
            self.registry, emacs_mode_enabled & search_enabled)
        load_emacs_system_bindings(
            self.registry, emacs_mode_enabled & system_prompt_enabled)

        load_vi_bindings(self.registry, self.vi_state, vi_mode_enabled)
        load_vi_open_in_editor_bindings(
            self.registry, self.vi_state,
            vi_mode_enabled & open_in_editor_enabled)
        load_vi_search_bindings(
            self.registry, self.vi_state,
            vi_mode_enabled & search_enabled)
        load_vi_system_bindings(
            self.registry, self.vi_state,
            vi_mode_enabled & system_prompt_enabled)
Пример #15
0
def load_checkbox_bindings():
    registry = Registry()
    handle = registry.add_binding

    @handle('a')
    def _select_all(event):
        event.current_buffer.select_all()

    @handle('i')
    def _invert_selection(event):
        event.current_buffer.invert_selection()

    @handle(' ')
    def _select(event):
        buf = event.current_buffer
        buf.toggle(buf.cursor)

    @handle(Keys.Enter)
    def _accept_selection(event):
        buf = event.current_buffer
        buf.accept_action.validate_and_handle(event.cli, buf)

    return registry
Пример #16
0
def load_scroll_bindings():
    registry = Registry()
    handle = registry.add_binding

    @handle('k')
    @handle('K')
    @handle(Keys.Up)
    def _list_cursor_up(event):
        event.current_buffer.list_cursor_up()

    @handle('j')
    @handle('J')
    @handle(Keys.Down)
    def _list_cursor_down(event):
        event.current_buffer.list_cursor_down()

    for key in string.digits[1:]:

        def _set_cursor(event, num):
            event.current_buffer.cursor = num

        handle('%s' % key)(partial(_set_cursor, num=int(key) - 1))

    return registry
Пример #17
0
    def __init__(
            self,
            registry=None,
            enable_vi_mode=None,  # (`enable_vi_mode` is deprecated.)
            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,
            enable_all=True):

        assert registry is None or isinstance(registry, Registry)
        assert get_search_state is None or callable(get_search_state)

        # Create registry.
        self.registry = registry or Registry()

        # 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)
        enable_all = to_cli_filter(enable_all)

        # Load basic bindings.
        load_basic_bindings(self.registry, enable_all)
        load_mouse_bindings(self.registry, enable_all)

        load_abort_and_exit_bindings(
            self.registry, enable_abort_and_exit_bindings & enable_all)

        load_basic_system_bindings(self.registry,
                                   enable_system_bindings & enable_all)

        # Load emacs bindings.
        load_emacs_bindings(self.registry, enable_all)

        load_emacs_open_in_editor_bindings(self.registry,
                                           enable_open_in_editor & enable_all)

        load_emacs_search_bindings(self.registry,
                                   filter=enable_search & enable_all,
                                   get_search_state=get_search_state)

        load_emacs_system_bindings(self.registry,
                                   enable_system_bindings & enable_all)

        load_extra_emacs_page_navigation_bindings(
            self.registry, enable_extra_page_navigation & enable_all)

        # Load Vi bindings.
        load_vi_bindings(self.registry,
                         enable_visual_key=~enable_open_in_editor,
                         filter=enable_all,
                         get_search_state=get_search_state)

        load_vi_open_in_editor_bindings(self.registry,
                                        enable_open_in_editor & enable_all)

        load_vi_search_bindings(self.registry,
                                filter=enable_search & enable_all,
                                get_search_state=get_search_state)

        load_vi_system_bindings(self.registry,
                                enable_system_bindings & enable_all)

        load_extra_vi_page_navigation_bindings(
            self.registry, enable_extra_page_navigation & enable_all)

        # 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.)
        load_auto_suggestion_bindings(
            self.registry, enable_auto_suggest_bindings & enable_all)
Пример #18
0
    def __init__(self,
                 registry=None,
                 enable_vi_mode=False,
                 get_vi_state=None,
                 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,
                 enable_all=True):

        assert registry is None or isinstance(registry, Registry)
        assert get_vi_state is None or callable(get_vi_state)
        assert get_search_state is None or callable(get_search_state)

        # Create registry.
        self.registry = registry or Registry()

        # Vi state. (Object to keep track of in which Vi mode we are.)
        if get_vi_state is None:
            vi_state = ViState(
            )  # Stateful. Should be defined outside the function below.

            def get_vi_state(cli):
                return vi_state

        self.get_vi_state = get_vi_state

        # Accept both Filters and booleans as input.
        enable_vi_mode = to_cli_filter(enable_vi_mode)
        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)
        enable_all = to_cli_filter(enable_all)

        # Emacs mode filter is the opposite of Vi mode.
        enable_emacs_mode = ~enable_vi_mode

        # Load basic bindings.
        load_basic_bindings(self.registry, enable_all)

        load_abort_and_exit_bindings(
            self.registry, enable_abort_and_exit_bindings & enable_all)

        load_basic_system_bindings(self.registry,
                                   enable_system_bindings & enable_all)

        # Load emacs bindings.
        load_emacs_bindings(self.registry, enable_emacs_mode & enable_all)

        load_emacs_open_in_editor_bindings(
            self.registry,
            enable_emacs_mode & enable_open_in_editor & enable_all)

        load_emacs_search_bindings(self.registry,
                                   filter=enable_emacs_mode & enable_search
                                   & enable_all,
                                   get_search_state=get_search_state)

        load_emacs_system_bindings(
            self.registry,
            enable_emacs_mode & enable_system_bindings & enable_all)

        load_extra_emacs_page_navigation_bindings(
            self.registry,
            enable_emacs_mode & enable_extra_page_navigation & enable_all)

        # Load Vi bindings.
        load_vi_bindings(self.registry,
                         self.get_vi_state,
                         enable_visual_key=~enable_open_in_editor,
                         filter=enable_vi_mode & enable_all,
                         get_search_state=get_search_state)

        load_vi_open_in_editor_bindings(
            self.registry, self.get_vi_state,
            enable_vi_mode & enable_open_in_editor & enable_all)

        load_vi_search_bindings(self.registry,
                                self.get_vi_state,
                                filter=enable_vi_mode & enable_search
                                & enable_all,
                                get_search_state=get_search_state)

        load_vi_system_bindings(
            self.registry, self.get_vi_state,
            enable_vi_mode & enable_system_bindings & enable_all)

        load_extra_vi_page_navigation_bindings(
            self.registry,
            enable_vi_mode & enable_extra_page_navigation & enable_all)

        # 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.)
        load_auto_suggestion_bindings(
            self.registry, enable_auto_suggest_bindings & enable_all)
Пример #19
0
class KeyBindingTest(unittest.TestCase):
    def setUp(self):
        class Handlers(object):
            def __init__(self):
                self.called = []

            def __getattr__(self, name):
                def func(event):
                    self.called.append(name)
                return func

        self.handlers = Handlers()

        self.registry = Registry()
        self.registry.add_binding(Keys.ControlX, Keys.ControlC)(self.handlers.controlx_controlc)
        self.registry.add_binding(Keys.ControlX)(self.handlers.control_x)
        self.registry.add_binding(Keys.ControlD)(self.handlers.control_d)
        self.registry.add_binding(Keys.ControlSquareClose, Keys.Any)(self.handlers.control_square_close_any)

        self.processor = InputProcessor(self.registry, lambda: None)

    def test_feed_simple(self):
        self.processor.feed_key(KeyPress(Keys.ControlX, '\x18'))
        self.processor.feed_key(KeyPress(Keys.ControlC, '\x03'))

        self.assertEqual(self.handlers.called, ['controlx_controlc'])

    def test_feed_several(self):
        # First an unknown key first.
        self.processor.feed_key(KeyPress(Keys.ControlQ, ''))
        self.assertEqual(self.handlers.called, [])

        # Followed by a know key sequence.
        self.processor.feed_key(KeyPress(Keys.ControlX, ''))
        self.processor.feed_key(KeyPress(Keys.ControlC, ''))
        self.assertEqual(self.handlers.called, ['controlx_controlc'])

        # Followed by another unknown sequence.
        self.processor.feed_key(KeyPress(Keys.ControlR, ''))
        self.processor.feed_key(KeyPress(Keys.ControlS, ''))

        # Followed again by a know key sequence.
        self.processor.feed_key(KeyPress(Keys.ControlD, ''))
        self.assertEqual(self.handlers.called, ['controlx_controlc', 'control_d'])

    def test_control_square_closed_any(self):
        self.processor.feed_key(KeyPress(Keys.ControlSquareClose, ''))
        self.processor.feed_key(KeyPress('C', 'C'))

        self.assertEqual(self.handlers.called, ['control_square_close_any'])

    def test_common_prefix(self):
        # Sending Control_X should not yet do anything, because there is
        # another sequence starting with that as well.
        self.processor.feed_key(KeyPress(Keys.ControlX, ''))
        self.assertEqual(self.handlers.called, [])

        # When another key is pressed, we know that we did not meant the longer
        # "ControlX ControlC" sequence and the callbacks are called.
        self.processor.feed_key(KeyPress(Keys.ControlD, ''))

        self.assertEqual(self.handlers.called, ['control_x', 'control_d'])
Пример #20
0
def load_python_bindings(python_input):
    """
    Custom key bindings.
    """
    registry = Registry()

    sidebar_visible = Condition(lambda cli: python_input.show_sidebar)
    handle = registry.add_binding
    has_selection = HasSelection()
    vi_mode_enabled = Condition(lambda cli: python_input.vi_mode)

    @handle(Keys.ControlL)
    def _(event):
        """
        Clear whole screen and render again -- also when the sidebar is visible.
        """
        event.cli.renderer.clear()

    @handle(Keys.F2)
    def _(event):
        """
        Show/hide sidebar.
        """
        python_input.show_sidebar = not python_input.show_sidebar

    @handle(Keys.F3)
    def _(event):
        """
        Select from the history.
        """
        python_input.enter_history(event.cli)

    @handle(Keys.F4)
    def _(event):
        """
        Toggle between Vi and Emacs mode.
        """
        python_input.vi_mode = not python_input.vi_mode

    @handle(Keys.F6)
    def _(event):
        """
        Enable/Disable paste mode.
        """
        python_input.paste_mode = not python_input.paste_mode

    @handle(Keys.Tab,
            filter=~sidebar_visible & ~has_selection
            & TabShouldInsertWhitespaceFilter())
    def _(event):
        """
        When tab should insert whitespace, do that instead of completion.
        """
        event.cli.current_buffer.insert_text('    ')

    @handle(Keys.ControlJ,
            filter=~sidebar_visible & ~has_selection &
            (ViInsertMode() | EmacsInsertMode()) & HasFocus(DEFAULT_BUFFER)
            & IsMultiline())
    def _(event):
        """
        Behaviour of the Enter key.

        Auto indent after newline/Enter.
        (When not in Vi navigaton mode, and when multiline is enabled.)
        """
        b = event.current_buffer
        empty_lines_required = python_input.accept_input_on_enter or 10000

        def at_the_end(b):
            """ we consider the cursor at the end when there is no text after
            the cursor, or only whitespace. """
            text = b.document.text_after_cursor
            return text == '' or (text.isspace() and not '\n' in text)

        if python_input.paste_mode:
            # In paste mode, always insert text.
            b.insert_text('\n')

        elif at_the_end(b) and b.document.text.replace(' ', '').endswith(
                '\n' * (empty_lines_required - 1)):
            if b.validate():
                # When the cursor is at the end, and we have an empty line:
                # drop the empty lines, but return the value.
                b.document = Document(text=b.text.rstrip(),
                                      cursor_position=len(b.text.rstrip()))

                b.accept_action.validate_and_handle(event.cli, b)
        else:
            auto_newline(b)

    @handle(
        Keys.ControlD,
        filter=~sidebar_visible
        & Condition(lambda cli:
                    # Only when the `confirm_exit` flag is set.
                    python_input.confirm_exit and
                    # And the current buffer is empty.
                    cli.current_buffer_name == DEFAULT_BUFFER and not cli.
                    current_buffer.text))
    def _(event):
        """
        Override Control-D exit, to ask for confirmation.
        """
        python_input.show_exit_confirmation = True

    return registry
Пример #21
0
def _load_search_bindings(pymux):
    """
    Load the key bindings for searching. (Vi and Emacs)

    This is different from the ones of prompt_toolkit, because we have a
    individual search buffers for each pane.
    """
    registry = Registry()
    is_searching = InScrollBufferSearching(pymux)
    in_scroll_buffer_not_searching = InScrollBufferNotSearching(pymux)

    def search_buffer_is_empty(cli):
        """ Returns True when the search buffer is empty. """
        return pymux.arrangement.get_active_pane(cli).search_buffer.text == ''

    @registry.add_binding(Keys.ControlG, filter=is_searching)
    @registry.add_binding(Keys.ControlC, filter=is_searching)
    @registry.add_binding(Keys.Backspace,
                          filter=is_searching
                          & Condition(search_buffer_is_empty))
    def _(event):
        """
        Abort an incremental search and restore the original line.
        """
        pane = pymux.arrangement.get_active_pane(event.cli)
        pane.search_buffer.reset()
        pane.is_searching = False

    @registry.add_binding(Keys.ControlJ, filter=is_searching)
    def _(event):
        """
        When enter pressed in isearch, accept search.
        """
        pane = pymux.arrangement.get_active_pane(event.cli)

        input_buffer = pane.scroll_buffer
        search_buffer = pane.search_buffer

        # Update search state.
        if search_buffer.text:
            pane.search_state.text = search_buffer.text

        # Apply search.
        input_buffer.apply_search(pane.search_state,
                                  include_current_position=True)

        # Add query to history of search line.
        search_buffer.append_to_history()

        # Focus previous document again.
        pane.search_buffer.reset()
        pane.is_searching = False

    def enter_search(cli):
        cli.vi_state.input_mode = InputMode.INSERT

        pane = pymux.arrangement.get_active_pane(cli)
        pane.is_searching = True
        return pane.search_state

    @registry.add_binding(Keys.ControlR, filter=in_scroll_buffer_not_searching)
    @registry.add_binding('?', filter=in_scroll_buffer_not_searching)
    def _(event):
        " Enter reverse search. "
        search_state = enter_search(event.cli)
        search_state.direction = IncrementalSearchDirection.BACKWARD

    @registry.add_binding(Keys.ControlS, filter=in_scroll_buffer_not_searching)
    @registry.add_binding('/', filter=in_scroll_buffer_not_searching)
    def _(event):
        " Enter forward search. "
        search_state = enter_search(event.cli)
        search_state.direction = IncrementalSearchDirection.FORWARD

    @registry.add_binding(Keys.ControlR, filter=is_searching)
    @registry.add_binding(Keys.Up, filter=is_searching)
    def _(event):
        " Repeat reverse search. (While searching.) "
        pane = pymux.arrangement.get_active_pane(event.cli)

        # Update search_state.
        search_state = pane.search_state
        direction_changed = search_state.direction != IncrementalSearchDirection.BACKWARD

        search_state.text = pane.search_buffer.text
        search_state.direction = IncrementalSearchDirection.BACKWARD

        # Apply search to current buffer.
        if not direction_changed:
            pane.scroll_buffer.apply_search(pane.search_state,
                                            include_current_position=False,
                                            count=event.arg)

    @registry.add_binding(Keys.ControlS, filter=is_searching)
    @registry.add_binding(Keys.Down, filter=is_searching)
    def _(event):
        " Repeat forward search. (While searching.) "
        pane = pymux.arrangement.get_active_pane(event.cli)

        # Update search_state.
        search_state = pane.search_state
        direction_changed = search_state.direction != IncrementalSearchDirection.FORWARD

        search_state.text = pane.search_buffer.text
        search_state.direction = IncrementalSearchDirection.FORWARD

        # Apply search to current buffer.
        if not direction_changed:
            pane.scroll_buffer.apply_search(pane.search_state,
                                            include_current_position=False,
                                            count=event.arg)

    return registry
Пример #22
0
    def _load_builtins(self):
        """
        Fill the Registry with the hard coded key bindings.
        """
        pymux = self.pymux
        registry = Registry()

        # Create filters.
        has_prefix = HasPrefix(pymux)
        waits_for_confirmation = WaitsForConfirmation(pymux)
        prompt_or_command_focus = HasFocus(COMMAND) | HasFocus(PROMPT)
        display_pane_numbers = Condition(
            lambda cli: pymux.display_pane_numbers)
        in_scroll_buffer_not_searching = InScrollBufferNotSearching(pymux)
        pane_input_allowed = ~(prompt_or_command_focus | has_prefix
                               | waits_for_confirmation | display_pane_numbers
                               | InScrollBuffer(pymux))

        @registry.add_binding(Keys.Any,
                              filter=pane_input_allowed,
                              invalidate_ui=False)
        def _(event):
            """
            When a pane has the focus, key bindings are redirected to the
            process running inside the pane.
            """
            # NOTE: we don't invalidate the UI, because for pymux itself,
            #       nothing in the output changes yet. It's the application in
            #       the pane that will probably echo back the typed characters.
            #       When we receive them, they are draw to the UI and it's
            #       invalidated.
            w = pymux.arrangement.get_active_window(event.cli)
            pane = w.active_pane

            if pane.clock_mode:
                # Leave clock mode on key press.
                pane.clock_mode = False
                pymux.invalidate()
            else:
                # Write input to pane. If 'synchronize_panes' is on, write
                # input to all panes in the current window.
                panes = w.panes if w.synchronize_panes else [pane]
                for p in panes:
                    p.process.write_key(event.key_sequence[0].key)

        @registry.add_binding(Keys.BracketedPaste,
                              filter=pane_input_allowed,
                              invalidate_ui=False)
        def _(event):
            """
            Pasting to the active pane. (Using bracketed paste.)
            """
            w = pymux.arrangement.get_active_window(event.cli)
            pane = w.active_pane

            if not pane.clock_mode:
                # Paste input to pane. If 'synchronize_panes' is on, paste
                # input to all panes in the current window.
                panes = w.panes if w.synchronize_panes else [pane]
                for p in panes:
                    p.process.write_input(event.data, paste=True)

        @registry.add_binding(Keys.Any, filter=has_prefix)
        def _(event):
            " Ignore unknown Ctrl-B prefixed key sequences. "
            pymux.get_client_state(event.cli).has_prefix = False

        @registry.add_binding(Keys.ControlC,
                              filter=prompt_or_command_focus & ~has_prefix)
        @registry.add_binding(Keys.ControlG,
                              filter=prompt_or_command_focus & ~has_prefix)
        @registry.add_binding(
            Keys.Backspace,
            filter=HasFocus(COMMAND) & ~has_prefix
            & Condition(lambda cli: cli.buffers[COMMAND].text == ''))
        def _(event):
            " Leave command mode. "
            pymux.leave_command_mode(event.cli, append_to_history=False)

        @registry.add_binding('y', filter=waits_for_confirmation)
        @registry.add_binding('Y', filter=waits_for_confirmation)
        def _(event):
            """
            Confirm command.
            """
            client_state = pymux.get_client_state(event.cli)

            command = client_state.confirm_command
            client_state.confirm_command = None
            client_state.confirm_text = None

            pymux.handle_command(event.cli, command)

        @registry.add_binding('n', filter=waits_for_confirmation)
        @registry.add_binding('N', filter=waits_for_confirmation)
        @registry.add_binding(Keys.ControlC, filter=waits_for_confirmation)
        def _(event):
            """
            Cancel command.
            """
            client_state = pymux.get_client_state(event.cli)
            client_state.confirm_command = None
            client_state.confirm_text = None

        @registry.add_binding(Keys.ControlC,
                              filter=in_scroll_buffer_not_searching)
        @registry.add_binding(Keys.ControlJ,
                              filter=in_scroll_buffer_not_searching)
        @registry.add_binding('q', filter=in_scroll_buffer_not_searching)
        def _(event):
            " Exit scroll buffer. "
            pane = pymux.arrangement.get_active_pane(event.cli)
            pane.exit_scroll_buffer()

        @registry.add_binding(' ', filter=in_scroll_buffer_not_searching)
        def _(event):
            " Enter selection mode when pressing space in copy mode. "
            event.current_buffer.start_selection(
                selection_type=SelectionType.CHARACTERS)

        @registry.add_binding(Keys.ControlJ,
                              filter=in_scroll_buffer_not_searching
                              & HasSelection())
        def _(event):
            " Copy selection when pressing Enter. "
            clipboard_data = event.current_buffer.copy_selection()
            event.cli.clipboard.set_data(clipboard_data)

        @registry.add_binding('v',
                              filter=in_scroll_buffer_not_searching
                              & HasSelection())
        def _(event):
            " Toggle between selection types. "
            types = [
                SelectionType.LINES, SelectionType.BLOCK,
                SelectionType.CHARACTERS
            ]
            selection_state = event.current_buffer.selection_state

            try:
                index = types.index(selection_state.type)
            except ValueError:  # Not in list.
                index = 0

            selection_state.type = types[(index + 1) % len(types)]

        @registry.add_binding(Keys.Any, filter=display_pane_numbers)
        def _(event):
            " When the pane numbers are shown. Any key press should hide them. "
            pymux.display_pane_numbers = False

        return registry
Пример #23
0
class KeyBindingTest(unittest.TestCase):
    def setUp(self):
        class Handlers(object):
            def __init__(self):
                self.called = []

            def __getattr__(self, name):
                def func(event):
                    self.called.append(name)

                return func

        self.handlers = Handlers()

        self.registry = Registry()
        self.registry.add_binding(Keys.ControlX, Keys.ControlC)(
            self.handlers.controlx_controlc)
        self.registry.add_binding(Keys.ControlX)(self.handlers.control_x)
        self.registry.add_binding(Keys.ControlD)(self.handlers.control_d)
        self.registry.add_binding(Keys.ControlSquareClose, Keys.Any)(
            self.handlers.control_square_close_any)

        self.processor = InputProcessor(self.registry, lambda: None)

    def test_feed_simple(self):
        self.processor.feed_key(KeyPress(Keys.ControlX, '\x18'))
        self.processor.feed_key(KeyPress(Keys.ControlC, '\x03'))

        self.assertEqual(self.handlers.called, ['controlx_controlc'])

    def test_feed_several(self):
        # First an unknown key first.
        self.processor.feed_key(KeyPress(Keys.ControlQ, ''))
        self.assertEqual(self.handlers.called, [])

        # Followed by a know key sequence.
        self.processor.feed_key(KeyPress(Keys.ControlX, ''))
        self.processor.feed_key(KeyPress(Keys.ControlC, ''))
        self.assertEqual(self.handlers.called, ['controlx_controlc'])

        # Followed by another unknown sequence.
        self.processor.feed_key(KeyPress(Keys.ControlR, ''))
        self.processor.feed_key(KeyPress(Keys.ControlS, ''))

        # Followed again by a know key sequence.
        self.processor.feed_key(KeyPress(Keys.ControlD, ''))
        self.assertEqual(self.handlers.called,
                         ['controlx_controlc', 'control_d'])

    def test_control_square_closed_any(self):
        self.processor.feed_key(KeyPress(Keys.ControlSquareClose, ''))
        self.processor.feed_key(KeyPress('C', 'C'))

        self.assertEqual(self.handlers.called, ['control_square_close_any'])

    def test_common_prefix(self):
        # Sending Control_X should not yet do anything, because there is
        # another sequence starting with that as well.
        self.processor.feed_key(KeyPress(Keys.ControlX, ''))
        self.assertEqual(self.handlers.called, [])

        # When another key is pressed, we know that we did not meant the longer
        # "ControlX ControlC" sequence and the callbacks are called.
        self.processor.feed_key(KeyPress(Keys.ControlD, ''))

        self.assertEqual(self.handlers.called, ['control_x', 'control_d'])
Пример #24
0
    def _gen_bindings(self):
        registry = Registry()
        bind = registry.add_binding

        def bind_with_help(*args, name, info='', **kwargs):
            def dec(func):
                _info = func.__doc__ or info
                self._help_items.append(HelpItem(name, *args, info=_info))

                return bind(*args, **kwargs)(func)

            return dec

        def ensure_cursor_bounds(buffer, pos, valids=None):
            buffer_stat = self.stat_buffer_state.current_stat

            if not buffer_stat:
                return

            if valids is None:
                valids = self.stat_constraints.get_cursor_bounds(buffer_stat)

            if pos not in valids:
                valids = sorted(valids)
                pos_index = bisect.bisect_left(valids, pos)
                requested_pos = pos
                pos = valids[min(pos_index, len(valids) - 1)]

                # if we wind up at the same spot, check to see if there's a non-sequential spot
                if buffer.cursor_position == pos:
                    moving_left = requested_pos < pos

                    if moving_left and pos > valids[0]:
                        pos = valids[max(0, pos_index - 1)]

                    if not moving_left and pos < valids[-1]:
                        pos = valids[min(pos_index + 1, len(valids) - 1)]

            buffer.cursor_position = pos

        @Condition
        def _in_stat_buffer(cli):
            return cli.current_buffer_name.endswith("_STAT_BUFFER")

        @Condition
        def _in_normal_stat_buffer(cli):
            return not cli.current_buffer_name.startswith(
                tuple(name.upper() for group in statinfo.groups[2:]
                      for name in group))

        # Navigation binds

        @bind(Keys.Left)
        @self.stat_constraints.listen
        def _(event):
            buff = event.current_buffer
            new_pos = buff.cursor_position + buff.document.get_cursor_left_position(
                count=event.arg)
            ensure_cursor_bounds(buff, new_pos)

        @bind(Keys.Right)
        @self.stat_constraints.listen
        def _(event):
            buff = event.current_buffer
            new_pos = buff.cursor_position + buff.document.get_cursor_right_position(
                count=event.arg)
            ensure_cursor_bounds(buff, new_pos)

        @bind(Keys.Up)
        @self.stat_constraints.listen
        def _(event):
            current_buffer = event.cli.current_buffer
            from_stat_buff = _in_normal_stat_buffer(event.cli)

            self._focus(self.stat_buffer_state.up())

            buff = event.cli.current_buffer
            ensure_cursor_bounds(buff, buff.cursor_position)

            if _in_normal_stat_buffer(event.cli) and from_stat_buff:
                buff.cursor_position = current_buffer.cursor_position

        @bind(Keys.Down)
        @self.stat_constraints.listen
        def _(event):
            current_buffer = event.cli.current_buffer
            from_stat_buff = _in_normal_stat_buffer(event.cli)

            self._focus(self.stat_buffer_state.down())

            buff = event.cli.current_buffer
            ensure_cursor_bounds(buff, buff.cursor_position)

            if _in_normal_stat_buffer(event.cli) and from_stat_buff:
                buff.cursor_position = current_buffer.cursor_position

        @bind(Keys.Enter, filter=_in_stat_buffer)
        @self.stat_constraints.listen
        def _(event):
            pass

        # Control binds

        @bind(Keys.ControlD)
        # @bind(Keys.ControlC)
        def _(event):
            event.cli.set_return_value(None)

        @bind(Keys.PageUp)
        def _(event):
            self._scroll_up()

        @bind(Keys.PageDown)
        def _(event):
            self._scroll_down()

        @bind_with_help('?', name='Help', info="Shows the help screen")
        def _(event):
            if self._help_showing:
                self._help_showing = False
                self._update_info_text()
                return

            self.set_info_text(help_text)
            self._help_showing = True

        @bind_with_help('n', name='Reroll')
        def _(event):
            l = self.reroll()

        @bind_with_help('y',
                        name='Accept Stats',
                        info="Accept current stats in game")
        def _(event):
            ...  # TODO

        @bind_with_help('r', name='Refresh stats')
        def _(event):
            self.set_stats(**self.hook.zip(self.hook.read_all()))

        @bind_with_help(Keys.ControlZ, name='Undo', info="TODO: undo buffer")
        def _(event):
            self.print("I'll get to writing undo eventually")

        @bind_with_help(Keys.ControlY, name='Redo', info="TODO: undo buffer")
        def _(event):
            ...  # TODO

        # Testing/Debug binds

        @bind_with_help('`', name='Embed IPython')
        def _(event):
            def do():
                # noinspection PyStatementEffect
                self, event  # behold the magic of closures and scope

                __import__('IPython').embed()
                os.system('cls')

            event.cli.run_in_terminal(do)

        @bind_with_help('t', name='Reroll test')
        def _(event):
            def do():
                self.print("Running reroll test")

                num = 50
                self.hook.reset_reroll_count()
                rrbase = self.hook._read_rerolls()
                t0 = time.time()

                for x in range(num):
                    self.reroll()
                    self.print("Rerolled")

                t1 = time.time()

                rrcount = self.hook._read_rerolls() - rrbase
                self.print(f'Rolled {num} ({rrcount}) times in {t1-t0:.4f}',
                           'sec')

            self.run_in_executor(do)

        @bind(',')
        def _(event):
            self.print("Showing cursor")
            memhook.Cursor.show()

        @bind('.')
        def _(event):
            self.print("Hiding cursor")
            memhook.Cursor.hide()

        @bind('-')
        def _(event):
            self.print("got random stats")
            self.set_stats(**memhook.get_random_stats())

        return registry