Exemple #1
0
def load_basic_system_bindings(registry, filter=Always()):
    """
    Basic system bindings (For both Emacs and Vi mode.)
    """
    assert isinstance(filter, CLIFilter)
    handle = create_handle_decorator(registry, filter)

    suspend_supported = Condition(
        lambda cli: suspend_to_background_supported())

    @handle(Keys.ControlZ, filter=suspend_supported)
    def _(event):
        """
        Suspend process to background.
        """
        event.cli.suspend_to_background()
Exemple #2
0
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
Exemple #3
0
    def init_prompt_toolkit_cli(self):
        if self.simple_prompt or ('JUPYTER_CONSOLE_TEST' in os.environ):
            # Simple restricted interface for tests so we can find prompts with
            # pexpect. Multi-line input not supported.
            async def prompt():
                prompt = 'In [%d]: ' % self.execution_count
                raw = await async_input(prompt)
                return raw

            self.prompt_for_code = prompt
            self.print_out_prompt = \
                lambda: print('Out[%d]: ' % self.execution_count, end='')
            return

        kb = KeyBindings()
        insert_mode = vi_insert_mode | emacs_insert_mode

        @kb.add("enter",
                filter=(has_focus(DEFAULT_BUFFER)
                        & ~has_selection
                        & insert_mode))
        def _(event):
            b = event.current_buffer
            d = b.document
            if not (d.on_last_line or d.cursor_position_row >=
                    d.line_count - d.empty_line_count_at_the_end()):
                b.newline()
                return

            # Pressing enter flushes any pending display. This also ensures
            # the displayed execution_count is correct.
            self.handle_iopub()

            more, indent = self.check_complete(d.text)

            if (not more) and b.accept_handler:
                b.validate_and_handle()
            else:
                b.insert_text('\n' + indent)

        @kb.add("c-c", filter=has_focus(DEFAULT_BUFFER))
        def _(event):
            event.current_buffer.reset()

        @kb.add("c-\\", filter=has_focus(DEFAULT_BUFFER))
        def _(event):
            raise EOFError

        @kb.add("c-z",
                filter=Condition(lambda: suspend_to_background_supported()))
        def _(event):
            event.cli.suspend_to_background()

        @kb.add("c-o", filter=(has_focus(DEFAULT_BUFFER) & emacs_insert_mode))
        def _(event):
            event.current_buffer.insert_text("\n")

        # Pre-populate history from IPython's history database
        history = InMemoryHistory()
        last_cell = u""
        for _, _, cell in self.history_manager.get_tail(
                self.history_load_length, include_latest=True):
            # Ignore blank lines and consecutive duplicates
            cell = cell.rstrip()
            if cell and (cell != last_cell):
                history.append_string(cell)

        style_overrides = {
            Token.Prompt: '#009900',
            Token.PromptNum: '#00ff00 bold',
            Token.OutPrompt: '#ff2200',
            Token.OutPromptNum: '#ff0000 bold',
            Token.RemotePrompt: '#999900',
        }
        if self.highlighting_style:
            style_cls = get_style_by_name(self.highlighting_style)
        else:
            style_cls = get_style_by_name('default')
            # The default theme needs to be visible on both a dark background
            # and a light background, because we can't tell what the terminal
            # looks like. These tweaks to the default theme help with that.
            style_overrides.update({
                Token.Number: '#007700',
                Token.Operator: 'noinherit',
                Token.String: '#BB6622',
                Token.Name.Function: '#2080D0',
                Token.Name.Class: 'bold #2080D0',
                Token.Name.Namespace: 'bold #2080D0',
            })
        style_overrides.update(self.highlighting_style_overrides)
        style = merge_styles([
            style_from_pygments_cls(style_cls),
            style_from_pygments_dict(style_overrides),
        ])

        editing_mode = getattr(EditingMode, self.editing_mode.upper())
        langinfo = self.kernel_info.get('language_info', {})
        lexer = langinfo.get('pygments_lexer', langinfo.get('name', 'text'))

        # If enabled in the settings, highlight matching brackets
        # when the DEFAULT_BUFFER has the focus
        input_processors = [
            ConditionalProcessor(
                processor=HighlightMatchingBracketProcessor(chars='[](){}'),
                filter=has_focus(DEFAULT_BUFFER) & ~is_done
                & Condition(lambda: self.highlight_matching_brackets))
        ]

        # Tell prompt_toolkit to use the asyncio event loop.
        # Obsolete in prompt_toolkit.v3
        if not PTK3:
            use_asyncio_event_loop()

        self.pt_cli = PromptSession(
            message=(lambda: PygmentsTokens(self.get_prompt_tokens())),
            multiline=True,
            complete_style=self.pt_complete_style,
            editing_mode=editing_mode,
            lexer=PygmentsLexer(get_pygments_lexer(lexer)),
            prompt_continuation=(
                lambda width, lineno, is_soft_wrap: PygmentsTokens(
                    self.get_continuation_tokens(width))),
            key_bindings=kb,
            history=history,
            completer=JupyterPTCompleter(self.Completer),
            enable_history_search=True,
            style=style,
            input_processors=input_processors,
            color_depth=(ColorDepth.TRUE_COLOR if self.true_color else None),
        )
Exemple #4
0
    def init_prompt_toolkit_cli(self):
        if self.simple_prompt or ('JUPYTER_CONSOLE_TEST' in os.environ):
            # Simple restricted interface for tests so we can find prompts with
            # pexpect. Multi-line input not supported.
            def prompt():
                return cast_unicode_py2(
                    input('In [%d]: ' % self.execution_count))

            self.prompt_for_code = prompt
            self.print_out_prompt = \
                lambda: print('Out[%d]: ' % self.execution_count, end='')
            return

        kbmanager = KeyBindingManager.for_prompt()
        insert_mode = ViInsertMode() | EmacsInsertMode()
        # Ctrl+J == Enter, seemingly
        @kbmanager.registry.add_binding(Keys.ControlJ,
                                        filter=(HasFocus(DEFAULT_BUFFER)
                                                & ~HasSelection()
                                                & insert_mode))
        def _(event):
            b = event.current_buffer
            d = b.document
            if not (d.on_last_line or d.cursor_position_row >=
                    d.line_count - d.empty_line_count_at_the_end()):
                b.newline()
                return

            # Pressing enter flushes any pending display. This also ensures
            # the displayed execution_count is correct.
            self.handle_iopub()

            more, indent = self.check_complete(d.text)

            if (not more) and b.accept_action.is_returnable:
                b.accept_action.validate_and_handle(event.cli, b)
            else:
                b.insert_text('\n' + indent)

        @kbmanager.registry.add_binding(Keys.ControlC,
                                        filter=HasFocus(DEFAULT_BUFFER))
        def _(event):
            event.current_buffer.reset()

        @kbmanager.registry.add_binding(Keys.ControlBackslash,
                                        filter=HasFocus(DEFAULT_BUFFER))
        def _(event):
            raise EOFError

        @kbmanager.registry.add_binding(
            Keys.ControlZ,
            filter=Condition(lambda cli: suspend_to_background_supported()))
        def _(event):
            event.cli.suspend_to_background()

        # Pre-populate history from IPython's history database
        history = InMemoryHistory()
        last_cell = u""
        for _, _, cell in self.history_manager.get_tail(
                self.history_load_length, include_latest=True):
            # Ignore blank lines and consecutive duplicates
            cell = cast_unicode_py2(cell.rstrip())
            if cell and (cell != last_cell):
                history.append(cell)

        style_overrides = {
            Token.Prompt: '#009900',
            Token.PromptNum: '#00ff00 bold',
            Token.OutPrompt: '#ff2200',
            Token.OutPromptNum: '#ff0000 bold',
        }
        if self.highlighting_style:
            style_cls = get_style_by_name(self.highlighting_style)
        else:
            style_cls = get_style_by_name('default')
            # The default theme needs to be visible on both a dark background
            # and a light background, because we can't tell what the terminal
            # looks like. These tweaks to the default theme help with that.
            style_overrides.update({
                Token.Number: '#007700',
                Token.Operator: 'noinherit',
                Token.String: '#BB6622',
                Token.Name.Function: '#2080D0',
                Token.Name.Class: 'bold #2080D0',
                Token.Name.Namespace: 'bold #2080D0',
            })
        style_overrides.update(self.highlighting_style_overrides)
        style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
                                            style_dict=style_overrides)

        editing_mode = getattr(EditingMode, self.editing_mode.upper())
        langinfo = self.kernel_info.get('language_info', {})
        lexer = langinfo.get('pygments_lexer', langinfo.get('name', 'text'))
        app = create_prompt_application(
            multiline=True,
            editing_mode=editing_mode,
            lexer=PygmentsLexer(get_pygments_lexer(lexer)),
            get_prompt_tokens=self.get_prompt_tokens,
            get_continuation_tokens=self.get_continuation_tokens,
            key_bindings_registry=kbmanager.registry,
            history=history,
            completer=JupyterPTCompleter(self.Completer),
            enable_history_search=True,
            style=style,
        )

        self._eventloop = create_eventloop()
        self.pt_cli = CommandLineInterface(
            app,
            eventloop=self._eventloop,
            output=create_output(true_color=self.true_color),
        )
Exemple #5
0
def create_key_bindings(pager):
    kb = KeyBindings()
    handle = kb.add

    @Condition
    def has_colon():
        return pager.in_colon_mode

    @Condition
    def default_focus():
        app = get_app()
        return app.layout.current_window == pager.current_source_info.window

    @Condition
    def displaying_help():
        return pager.displaying_help

    for c in '01234556789':

        @handle(c, filter=default_focus)
        def _(event, c=c):
            event.append_to_arg_count(c)

    @handle('q', filter=default_focus)
    @handle('Q', filter=default_focus | has_colon)
    @handle('Z', 'Z', filter=default_focus)
    def _(event):
        " Quit. "
        if pager.displaying_help:
            pager.quit_help()
        else:
            event.app.exit()

    @handle(' ', filter=default_focus)
    @handle('f', filter=default_focus)
    @handle(Keys.ControlF, filter=default_focus)
    @handle(Keys.ControlV, filter=default_focus)
    def _(event):
        " Page down."
        scroll_page_down(event)

    @handle('b', filter=default_focus)
    @handle(Keys.ControlB, filter=default_focus)
    @handle(Keys.Escape, 'v', filter=default_focus)
    def _(event):
        " Page up."
        scroll_page_up(event)

    @handle('d', filter=default_focus)
    @handle(Keys.ControlD, filter=default_focus)
    def _(event):
        " Half page down."
        scroll_half_page_down(event)

    @handle('u', filter=default_focus)
    @handle(Keys.ControlU, filter=default_focus)
    def _(event):
        " Half page up."
        scroll_half_page_up(event)

    @handle('e', filter=default_focus)
    @handle('j', filter=default_focus)
    @handle(Keys.ControlE, filter=default_focus)
    @handle(Keys.ControlN, filter=default_focus)
    @handle(Keys.ControlJ, filter=default_focus)
    @handle(Keys.ControlM, filter=default_focus)
    @handle(Keys.Down, filter=default_focus)
    def _(event):
        " Scoll one line down."
        if event.arg > 1:
            # When an argument is given, go this amount of lines down.
            event.current_buffer.auto_down(count=event.arg)
        else:
            scroll_one_line_down(event)

    @handle('y', filter=default_focus)
    @handle('k', filter=default_focus)
    @handle(Keys.ControlY, filter=default_focus)
    @handle(Keys.ControlK, filter=default_focus)
    @handle(Keys.ControlP, filter=default_focus)
    @handle(Keys.Up, filter=default_focus)
    def _(event):
        " Scoll one line up."
        if event.arg > 1:
            event.current_buffer.auto_up(count=event.arg)
        else:
            scroll_one_line_up(event)

    @handle(Keys.Escape, 'u')
    def _(event):
        " Toggle search highlighting. "
        pager.highlight_search = not pager.highlight_search

    @handle('=', filter=default_focus)
    @handle(Keys.ControlG, filter=default_focus)
    @handle('f', filter=has_colon)
    def _(event):
        " Print the current file name. "
        pager.message = ' {} '.format(pager.current_source.get_name())

    @handle('h', filter=default_focus & ~displaying_help)
    @handle('H', filter=default_focus & ~displaying_help)
    def _(event):
        " Display Help. "
        pager.display_help()

    @handle('g', filter=default_focus)
    @handle('<', filter=default_focus)
    @handle(Keys.Escape, '<', filter=default_focus)
    def _(event):
        " Go to the first line of the file. "
        event.current_buffer.cursor_position = 0

    @handle('G', filter=default_focus)
    @handle('>', filter=default_focus)
    @handle(Keys.Escape, '>', filter=default_focus)
    def _(event):
        " Go to the last line of the file. "
        b = event.current_buffer
        b.cursor_position = len(b.text)

    @handle('m', Keys.Any, filter=default_focus)
    def _(event):
        " Mark current position. "
        source_info = pager.current_source_info

        source_info.marks[event.data] = (event.current_buffer.cursor_position,
                                         source_info.window.vertical_scroll)

    @handle("'", Keys.Any, filter=default_focus)
    def _(event):
        " Go to a previously marked position. "
        go_to_mark(event, event.data)

    @handle(Keys.ControlX, Keys.ControlX, filter=default_focus)
    def _(event):
        " Same as '. "
        go_to_mark(event, '.')

    def go_to_mark(event, mark):
        b = event.current_buffer
        source_info = pager.current_source_info
        try:
            if mark == '^':  # Start of file.
                cursor_pos, vertical_scroll = 0, 0
            elif mark == '$':  # End of file - mark.
                cursor_pos, vertical_scroll = len(b.text), 0
            else:  # Custom mark.
                cursor_pos, vertical_scroll = source_info.marks[mark]
        except KeyError:
            pass  # TODO: show warning.
        else:
            b.cursor_position = cursor_pos
            source_info.window.vertical_scroll = vertical_scroll

    @handle('F', filter=default_focus)
    def _(event):
        " Forward forever, like 'tail -f'. "
        pager.forward_forever = True

    @handle('r', filter=default_focus)
    @handle('R', filter=default_focus)
    def _(event):
        event.app.renderer.clear()

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

    @handle('backspace',
            filter=has_focus(pager.search_buffer)
            & Condition(search_buffer_is_empty))
    def _(event):
        " Cancel search when backspace is pressed. "
        stop_search()

    @handle(Keys.Left, filter=default_focus)
    @handle(Keys.Escape, '(', filter=default_focus)
    def _(event):
        " Scroll half page to the left. "
        w = event.app.layout.current_window
        b = event.app.current_buffer

        if w and w.render_info:
            info = w.render_info
            amount = info.window_width // 2

            # Move cursor horizontally.
            value = b.cursor_position - min(
                amount, len(b.document.current_line_before_cursor))
            b.cursor_position = value

            # Scroll.
            w.horizontal_scroll = max(0, w.horizontal_scroll - amount)

    @handle(Keys.Right, filter=default_focus)
    @handle(Keys.Escape, ')', filter=default_focus)
    def _(event):
        " Scroll half page to the right. "
        w = event.app.layout.current_window
        b = event.app.current_buffer

        if w and w.render_info:
            info = w.render_info
            amount = info.window_width // 2

            # Move the cursor first to a visible line that is long enough to
            # have the cursor visible after scrolling. (Otherwise, the Window
            # will scroll back.)
            xpos = w.horizontal_scroll + amount

            for line in info.displayed_lines:
                if len(b.document.lines[line]) >= xpos:
                    b.cursor_position = b.document.translate_row_col_to_index(
                        line, xpos)
                    break

            # Scroll.
            w.horizontal_scroll = max(0, w.horizontal_scroll + amount)

    @handle(':', filter=default_focus & ~displaying_help)
    def _(event):
        pager.in_colon_mode = True

    @handle('n', filter=has_colon)
    def _(event):
        " Go to next file. "
        pager.focus_next_source()

    @handle('p', filter=has_colon)
    def _(event):
        " Go to previous file. "
        pager.focus_previous_source()

    @handle('e', filter=has_colon)
    @handle(Keys.ControlX, Keys.ControlV, filter=default_focus)
    def _(event):
        event.app.layout.focus(pager.layout.examine_control)
        pager.in_colon_mode = False

    @handle('d', filter=has_colon)
    def _(event):
        pager.remove_current_source()

    @handle('backspace', filter=has_colon)
    @handle('q', filter=has_colon)
    def _(event):
        pager.in_colon_mode = False

    @handle(Keys.Any, filter=has_colon)
    def _(event):
        pager.in_colon_mode = False
        pager.message = 'No command.'

    @handle(Keys.ControlC, filter=has_focus('EXAMINE'))
    @handle(Keys.ControlG, filter=has_focus('EXAMINE'))
    def _(event):
        " Cancel 'Examine' input. "
        event.app.layout.focus(pager.current_source_info.window)

    @handle(Keys.ControlZ,
            filter=Condition(lambda: suspend_to_background_supported()))
    def _(event):
        " Suspend to bakground. "
        event.app.suspend_to_background()

    return kb
Exemple #6
0
def create_key_bindings(pager: "Pager") -> KeyBindings:
    kb = KeyBindings()
    handle = kb.add

    @Condition
    def has_colon() -> bool:
        return pager.in_colon_mode

    @Condition
    def default_focus() -> bool:
        app = get_app()
        return app.layout.current_window == pager.current_source_info.window

    @Condition
    def displaying_help() -> bool:
        return pager.displaying_help

    for c in "01234556789":

        @handle(c, filter=default_focus)
        def _handle_arg(event: E, c: str = c) -> None:
            event.append_to_arg_count(c)

    @handle("q", filter=default_focus, eager=True)
    # Eager because q-any is a registered Vi key binding.
    @handle("Q", filter=default_focus | has_colon)
    @handle("Z", "Z", filter=default_focus)
    def _quit(event: E) -> None:
        " Quit. "
        if pager.displaying_help:
            pager.quit_help()
        else:
            event.app.exit()

    @handle(" ", filter=default_focus)
    @handle("f", filter=default_focus)
    @handle("c-f", filter=default_focus)
    @handle("c-v", filter=default_focus)
    def _pagedown(event: E) -> None:
        " Page down."
        scroll_page_down(event)

    @handle("b", filter=default_focus)
    @handle("c-b", filter=default_focus)
    @handle("escape", "v", filter=default_focus)
    def _pageup(event: E) -> None:
        " Page up."
        scroll_page_up(event)

    @handle("d", filter=default_focus)
    @handle("c-d", filter=default_focus)
    def _halfdown(event: E) -> None:
        " Half page down."
        scroll_half_page_down(event)

    @handle("u", filter=default_focus)
    @handle("c-u", filter=default_focus)
    def _halfup(event: E) -> None:
        " Half page up."
        scroll_half_page_up(event)

    @handle("e", filter=default_focus)
    @handle("j", filter=default_focus)
    @handle("c-e", filter=default_focus)
    @handle("c-n", filter=default_focus)
    @handle("c-j", filter=default_focus)
    @handle("c-m", filter=default_focus)
    @handle("down", filter=default_focus)
    def _down(event: E) -> None:
        " Scoll one line down."
        if event.arg > 1:
            # When an argument is given, go this amount of lines down.
            event.current_buffer.auto_down(count=event.arg)
        else:
            scroll_one_line_down(event)

    @handle("y", filter=default_focus)
    @handle("k", filter=default_focus)
    @handle("c-y", filter=default_focus)
    @handle("c-k", filter=default_focus)
    @handle("c-p", filter=default_focus)
    @handle("up", filter=default_focus)
    def _up(event: E) -> None:
        " Scoll one line up."
        if event.arg > 1:
            event.current_buffer.auto_up(count=event.arg)
        else:
            scroll_one_line_up(event)

    @handle(Keys.Escape, "u")
    def _toggle_highlighting(event: E) -> None:
        " Toggle search highlighting. "
        pager.highlight_search = not pager.highlight_search

    @handle("=", filter=default_focus)
    @handle(Keys.ControlG, filter=default_focus)
    @handle("f", filter=has_colon)
    def _print_filename(event: E) -> None:
        " Print the current file name. "
        pager.message = " {} ".format(pager.current_source.get_name())

    @handle("h", filter=default_focus & ~displaying_help)
    @handle("H", filter=default_focus & ~displaying_help)
    def _help(event: E) -> None:
        " Display Help. "
        pager.display_help()

    @handle("g", filter=default_focus)
    @handle("<", filter=default_focus)
    @handle("escape", "<", filter=default_focus)
    def _firstline(event: E) -> None:
        " Go to the first line of the file. "
        event.current_buffer.cursor_position = 0

    @handle("G", filter=default_focus)
    @handle(">", filter=default_focus)
    @handle("escape", ">", filter=default_focus)
    def _lastline(event: E) -> None:
        " Go to the last line of the file. "
        b = event.current_buffer
        b.cursor_position = len(b.text)

    @handle("m", Keys.Any, filter=default_focus)
    def _mark(event: E) -> None:
        " Mark current position. "
        source_info = pager.current_source_info

        source_info.marks[event.data] = (
            event.current_buffer.cursor_position,
            source_info.window.vertical_scroll,
        )

    @handle("'", Keys.Any, filter=default_focus)
    def _goto_mark(event: E) -> None:
        " Go to a previously marked position. "
        go_to_mark(event, event.data)

    @handle("c-x", Keys.ControlX, filter=default_focus)
    def _gotomark_dot(event: E) -> None:
        " Same as '. "
        go_to_mark(event, ".")

    def go_to_mark(event: E, mark: str) -> None:
        b = event.current_buffer
        source_info = pager.current_source_info
        try:
            if mark == "^":  # Start of file.
                cursor_pos, vertical_scroll = 0, 0
            elif mark == "$":  # End of file - mark.
                cursor_pos, vertical_scroll = len(b.text), 0
            else:  # Custom mark.
                cursor_pos, vertical_scroll = source_info.marks[mark]
        except KeyError:
            pass  # TODO: show warning.
        else:
            b.cursor_position = cursor_pos
            source_info.window.vertical_scroll = vertical_scroll

    @handle("F", filter=default_focus)
    def _follow(event: E) -> None:
        " Forward forever, like 'tail -f'. "
        pager.forward_forever = True

    @handle("r", filter=default_focus)
    @handle("R", filter=default_focus)
    def _repaint(event: E) -> None:
        event.app.renderer.clear()

    @Condition
    def search_buffer_is_empty() -> bool:
        " Returns True when the search buffer is empty. "
        return pager.search_buffer.text == ""

    @handle(
        "backspace",
        filter=has_focus(pager.search_buffer) & search_buffer_is_empty,
    )
    def _cancel_search(event: E) -> None:
        " Cancel search when backspace is pressed. "
        stop_search()

    @Condition
    def line_wrapping_enable() -> bool:
        return pager.current_source_info.wrap_lines

    @handle("left", filter=default_focus & ~line_wrapping_enable)
    @handle("escape", "(", filter=default_focus & ~line_wrapping_enable)
    def _left(event: E) -> None:
        " Scroll half page to the left. "
        w = event.app.layout.current_window
        b = event.app.current_buffer

        if w and w.render_info:
            info = w.render_info
            amount = info.window_width // 2

            # Move cursor horizontally.
            value = b.cursor_position - min(
                amount, len(b.document.current_line_before_cursor))
            b.cursor_position = value

            # Scroll.
            w.horizontal_scroll = max(0, w.horizontal_scroll - amount)

    @handle("right", filter=default_focus & ~line_wrapping_enable)
    @handle("escape", ")", filter=default_focus & ~line_wrapping_enable)
    def _right(event: E) -> None:
        " Scroll half page to the right. "
        w = event.app.layout.current_window
        b = event.app.current_buffer

        if w and w.render_info:
            info = w.render_info
            amount = info.window_width // 2

            # Move the cursor first to a visible line that is long enough to
            # have the cursor visible after scrolling. (Otherwise, the Window
            # will scroll back.)
            xpos = w.horizontal_scroll + amount

            for line in info.displayed_lines:
                if len(b.document.lines[line]) >= xpos:
                    b.cursor_position = b.document.translate_row_col_to_index(
                        line, xpos)
                    break

            # Scroll.
            w.horizontal_scroll = max(0, w.horizontal_scroll + amount)

    @handle(":", filter=default_focus & ~displaying_help)
    def _colon(event: E) -> None:
        pager.in_colon_mode = True

    @handle("n", filter=has_colon)
    def _next_file(event: E) -> None:
        " Go to next file. "
        pager.focus_next_source()

    @handle("p", filter=has_colon)
    def _previous_file(event: E) -> None:
        " Go to previous file. "
        pager.focus_previous_source()

    @handle("e", filter=has_colon)
    @handle(Keys.ControlX, Keys.ControlV, filter=default_focus)
    def _examine(event: E) -> None:
        event.app.layout.focus(pager.layout.examine_control)
        pager.in_colon_mode = False

    @handle("d", filter=has_colon)
    def _remove_source(event: E) -> None:
        pager.remove_current_source()

    @handle("backspace", filter=has_colon)
    @handle("q", filter=has_colon, eager=True)
    def _cancel_colon(event: E) -> None:
        pager.in_colon_mode = False

    @handle(Keys.Any, filter=has_colon)
    def _any(event: E) -> None:
        pager.in_colon_mode = False
        pager.message = "No command."

    @handle("c-c", filter=has_focus("EXAMINE"))
    @handle("c-g", filter=has_focus("EXAMINE"))
    def _cancel_examine(event: E) -> None:
        " Cancel 'Examine' input. "
        event.app.layout.focus(pager.current_source_info.window)

    @handle("c-z", filter=Condition(lambda: suspend_to_background_supported()))
    def _suspend(event: E) -> None:
        " Suspend to bakground. "
        event.app.suspend_to_background()

    @handle("w")
    def _suspend(event: E) -> None:
        " Enable/disable line wrapping. "
        source_info = pager.current_source_info
        source_info.wrap_lines = not source_info.wrap_lines

    return kb
Exemple #7
0
def create_key_bindings(pager):
    registry = load_key_bindings(enable_search=True,
                                 enable_extra_page_navigation=True,
                                 enable_system_bindings=True)
    handle = registry.add_binding

    @Condition
    def has_colon(cli):
        return pager.in_colon_mode

    @Condition
    def default_focus(cli):
        return (cli.current_buffer_name.startswith('source')
                and not pager.in_colon_mode)

    @Condition
    def displaying_help(cli):
        return pager.displaying_help

    for c in '01234556789':

        @handle(c, filter=default_focus)
        def _(event, c=c):
            event.append_to_arg_count(c)

    @handle('q', filter=default_focus | has_colon)
    @handle('Q', filter=default_focus | has_colon)
    @handle('Z', 'Z', filter=default_focus)
    def _(event):
        " Quit. "
        if pager.displaying_help:
            pager.quit_help()
        else:
            event.cli.set_return_value(None)

    @handle(' ', filter=default_focus)
    @handle('f', filter=default_focus)
    @handle(Keys.ControlF, filter=default_focus)
    @handle(Keys.ControlV, filter=default_focus)
    def _(event):
        " Page down."
        scroll_page_down(event)

    @handle('b', filter=default_focus)
    @handle(Keys.ControlB, filter=default_focus)
    @handle(Keys.Escape, 'v', filter=default_focus)
    def _(event):
        " Page up."
        scroll_page_up(event)

    @handle('d', filter=default_focus)
    @handle(Keys.ControlD, filter=default_focus)
    def _(event):
        " Half page down."
        scroll_half_page_down(event)

    @handle('u', filter=default_focus)
    @handle(Keys.ControlU, filter=default_focus)
    def _(event):
        " Half page up."
        scroll_half_page_up(event)

    @handle('e', filter=default_focus)
    @handle('j', filter=default_focus)
    @handle(Keys.ControlE, filter=default_focus)
    @handle(Keys.ControlN, filter=default_focus)
    @handle(Keys.ControlJ, filter=default_focus)
    @handle(Keys.ControlM, filter=default_focus)
    @handle(Keys.Down, filter=default_focus)
    def _(event):
        " Scoll one line down."
        if event.arg > 1:
            # When an argument is given, go this amount of lines down.
            event.current_buffer.auto_down(count=event.arg)
        else:
            scroll_one_line_down(event)

    @handle('y', filter=default_focus)
    @handle('k', filter=default_focus)
    @handle(Keys.ControlY, filter=default_focus)
    @handle(Keys.ControlK, filter=default_focus)
    @handle(Keys.ControlP, filter=default_focus)
    @handle(Keys.Up, filter=default_focus)
    def _(event):
        " Scoll one line up."
        if event.arg > 1:
            event.current_buffer.auto_up(count=event.arg)
        else:
            scroll_one_line_up(event)

    @handle('/', filter=default_focus)
    def _(event):
        " Start searching forward. "
        event.cli.search_state.direction = IncrementalSearchDirection.FORWARD
        event.cli.vi_state.input_mode = InputMode.INSERT
        event.cli.push_focus(SEARCH_BUFFER)

    @handle('?', filter=default_focus)
    def _(event):
        " Start searching backwards. "
        event.cli.search_state.direction = IncrementalSearchDirection.BACKWARD
        event.cli.vi_state.input_mode = InputMode.INSERT
        event.cli.push_focus(SEARCH_BUFFER)

    @handle('n', filter=default_focus)
    def _(event):
        " Search next. "
        event.current_buffer.apply_search(event.cli.search_state,
                                          include_current_position=False,
                                          count=event.arg)

    @handle('N', filter=default_focus)
    def _(event):
        " Search previous. "
        event.current_buffer.apply_search(~event.cli.search_state,
                                          include_current_position=False,
                                          count=event.arg)

    @handle(Keys.Escape, 'u')
    def _(event):
        " Toggle search highlighting. "
        pager.highlight_search = not pager.highlight_search

    @handle('=', filter=default_focus)
    @handle(Keys.ControlG, filter=default_focus)
    @handle('f', filter=has_colon)
    def _(event):
        " Print the current file name. "
        pager.message = ' {} '.format(pager.source.get_name())

    @handle('h', filter=default_focus & ~displaying_help)
    @handle('H', filter=default_focus & ~displaying_help)
    def _(event):
        " Display Help. "
        pager.display_help()

    @handle('g', filter=default_focus)
    @handle('<', filter=default_focus)
    @handle(Keys.Escape, '<', filter=default_focus)
    def _(event):
        " Go to the first line of the file. "
        event.current_buffer.cursor_position = 0

    @handle('G', filter=default_focus)
    @handle('>', filter=default_focus)
    @handle(Keys.Escape, '>', filter=default_focus)
    def _(event):
        " Go to the last line of the file. "
        b = event.current_buffer
        b.cursor_position = len(b.text)

    @handle('m', Keys.Any, filter=default_focus)
    def _(event):
        " Mark current position. "
        pager.marks[event.data] = (event.current_buffer.cursor_position,
                                   pager.layout.buffer_window.vertical_scroll)

    @handle("'", Keys.Any, filter=default_focus)
    def _(event):
        " Go to a previously marked position. "
        go_to_mark(event, event.data)

    @handle(Keys.ControlX, Keys.ControlX, filter=default_focus)
    def _(event):
        " Same as '. "
        go_to_mark(event, '.')

    def go_to_mark(event, mark):
        b = event.current_buffer
        try:
            if mark == '^':  # Start of file.
                cursor_pos, vertical_scroll = 0, 0
            elif mark == '$':  # End of file - mark.
                cursor_pos, vertical_scroll = len(b.text), 0
            else:  # Custom mark.
                cursor_pos, vertical_scroll = pager.marks[mark]
        except KeyError:
            pass  # TODO: show warning.
        else:
            b.cursor_position = cursor_pos
            pager.layout.buffer_window.vertical_scroll = vertical_scroll

    @handle('F', filter=default_focus)
    def _(event):
        " Forward forever, like 'tail -f'. "
        pager.forward_forever = True

    @handle(Keys.ControlR, filter=default_focus)
    @handle('r', filter=default_focus)
    @handle('R', filter=default_focus)
    def _(event):
        event.cli.renderer.clear()

    def search_buffer_is_empty(cli):
        " Returns True when the search buffer is empty. "
        return cli.buffers[SEARCH_BUFFER].text == ''

    @handle(Keys.Backspace,
            filter=HasFocus(SEARCH_BUFFER) & Condition(search_buffer_is_empty))
    def _(event):
        " Cancel search when backspace is pressed. "
        event.cli.vi_state.input_mode = InputMode.NAVIGATION
        event.cli.pop_focus()
        event.cli.buffers[SEARCH_BUFFER].reset()

    @handle(Keys.Left, filter=default_focus)
    @handle(Keys.Escape, '(', filter=default_focus)
    def _(event):
        " Scroll half page to the left. "
        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
            amount = info.window_width // 2

            # Move cursor horizontally.
            value = b.cursor_position - min(
                amount, len(b.document.current_line_before_cursor))
            b.cursor_position = value

            # Scroll.
            w.horizontal_scroll = max(0, w.horizontal_scroll - amount)

    @handle(Keys.Right, filter=default_focus)
    @handle(Keys.Escape, ')', filter=default_focus)
    def _(event):
        " Scroll half page to the right. "
        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
            amount = info.window_width // 2

            # Move the cursor first to a visible line that is long enough to
            # have the cursor visible after scrolling. (Otherwise, the Window
            # will scroll back.)
            xpos = w.horizontal_scroll + amount

            for line in info.displayed_lines:
                if len(b.document.lines[line]) >= xpos:
                    b.cursor_position = b.document.translate_row_col_to_index(
                        line, xpos)
                    break

            # Scroll.
            w.horizontal_scroll = max(0, w.horizontal_scroll + amount)

    @handle(':', filter=default_focus & ~displaying_help)
    def _(event):
        pager.in_colon_mode = True

    @handle('n', filter=has_colon)
    def _(event):
        " Go to next file. "
        pager.focus_next_source()

    @handle('p', filter=has_colon)
    def _(event):
        " Go to previous file. "
        pager.focus_previous_source()

    @handle('e', filter=has_colon)
    @handle(Keys.ControlX, Keys.ControlV, filter=default_focus)
    def _(event):
        pager.buffers.focus(event.cli, 'EXAMINE')
        pager.in_colon_mode = False

    @handle('d', filter=has_colon)
    def _(event):
        pager.remove_current_source()

    @handle(Keys.Any, filter=has_colon)
    @handle(Keys.Backspace, filter=has_colon)
    def _(event):
        pager.in_colon_mode = False
        pager.message = 'No command.'

    @handle(Keys.ControlC, filter=HasFocus('EXAMINE'))
    @handle(Keys.ControlG, filter=HasFocus('EXAMINE'))
    def _(event):
        " Cancel 'Examine' input. "
        pager.buffers.focus(event.cli,
                            pager.source_info[pager.source].buffer_name)

    @handle(Keys.ControlZ,
            filter=Condition(lambda cli: suspend_to_background_supported()))
    def _(event):
        " Suspend to bakground. "
        event.cli.suspend_to_background()

    return registry