Exemplo n.º 1
0
class TerminalInteractiveShell(InteractiveShell):
    space_for_menu = Integer(
        6,
        help='Number of line at the bottom of the screen '
        'to reserve for the completion menu').tag(config=True)

    def _space_for_menu_changed(self, old, new):
        self._update_layout()

    pt_cli = None
    debugger_history = None
    _pt_app = None

    simple_prompt = Bool(
        _use_simple_prompt,
        help=
        """Use `raw_input` for the REPL, without completion and prompt colors.

            Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
            IPython own testing machinery, and emacs inferior-shell integration through elpy.

            This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
            environment variable is set, or the current terminal is not a tty."""
    ).tag(config=True)

    @property
    def debugger_cls(self):
        return Pdb if self.simple_prompt else TerminalPdb

    confirm_exit = Bool(
        True,
        help="""
        Set to confirm when you try to exit IPython with an EOF (Control-D
        in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
        you can force a direct exit without any confirmation.""",
    ).tag(config=True)

    editing_mode = Unicode(
        'emacs',
        help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
    ).tag(config=True)

    mouse_support = Bool(
        False,
        help=
        "Enable mouse support in the prompt\n(Note: prevents selecting text with the mouse)"
    ).tag(config=True)

    highlighting_style = Union(
        [Unicode('legacy'), Type(klass=Style)],
        help="""The name or class of a Pygments style to use for syntax
        highlighting: \n %s""" % ', '.join(get_all_styles())).tag(config=True)

    @observe('highlighting_style')
    @observe('colors')
    def _highlighting_style_changed(self, change):
        self.refresh_style()

    def refresh_style(self):
        self._style = self._make_style_from_name_or_cls(
            self.highlighting_style)

    highlighting_style_overrides = Dict(
        help="Override highlighting format for specific tokens").tag(
            config=True)

    true_color = Bool(
        False,
        help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
              "If your terminal supports true color, the following command "
              "should print 'TRUECOLOR' in orange: "
              "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")).tag(
                  config=True)

    editor = Unicode(
        get_default_editor(),
        help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
    ).tag(config=True)

    prompts_class = Type(
        Prompts,
        help='Class used to generate Prompt token for prompt_toolkit').tag(
            config=True)

    prompts = Instance(Prompts)

    @default('prompts')
    def _prompts_default(self):
        return self.prompts_class(self)

    @observe('prompts')
    def _(self, change):
        self._update_layout()

    @default('displayhook_class')
    def _displayhook_class_default(self):
        return RichPromptDisplayHook

    term_title = Bool(
        True, help="Automatically set the terminal title").tag(config=True)

    term_title_format = Unicode(
        "IPython: {cwd}",
        help=
        "Customize the terminal title format.  This is a python format string. "
        + "Available substitutions are: {cwd}.").tag(config=True)

    display_completions = Enum(
        ('column', 'multicolumn', 'readlinelike'),
        help=
        ("Options for displaying tab completions, 'column', 'multicolumn', and "
         "'readlinelike'. These options are for `prompt_toolkit`, see "
         "`prompt_toolkit` documentation for more information."),
        default_value='multicolumn').tag(config=True)

    highlight_matching_brackets = Bool(
        True,
        help="Highlight matching brackets.",
    ).tag(config=True)

    extra_open_editor_shortcuts = Bool(
        False,
        help=
        "Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. "
        "This is in addition to the F2 binding, which is always enabled.").tag(
            config=True)

    handle_return = Any(
        None,
        help="Provide an alternative handler to be called when the user presses "
        "Return. This is an advanced option intended for debugging, which "
        "may be changed or removed in later releases.").tag(config=True)

    @observe('term_title')
    def init_term_title(self, change=None):
        # Enable or disable the terminal title.
        if self.term_title:
            toggle_set_term_title(True)
            set_term_title(self.term_title_format.format(cwd=abbrev_cwd()))
        else:
            toggle_set_term_title(False)

    def init_display_formatter(self):
        super(TerminalInteractiveShell, self).init_display_formatter()
        # terminal only supports plain text
        self.display_formatter.active_types = ['text/plain']
        # disable `_ipython_display_`
        self.display_formatter.ipython_display_formatter.enabled = False

    def init_prompt_toolkit_cli(self):
        if self.simple_prompt:
            # Fall back to plain non-interactive output for tests.
            # This is very limited, and only accepts a single line.
            def prompt():
                isp = self.input_splitter
                prompt_text = "".join(x[1]
                                      for x in self.prompts.in_prompt_tokens())
                prompt_continuation = "".join(
                    x[1] for x in self.prompts.continuation_prompt_tokens())
                while isp.push_accepts_more():
                    line = cast_unicode_py2(input(prompt_text))
                    isp.push(line)
                    prompt_text = prompt_continuation
                return isp.source_reset()

            self.prompt_for_code = prompt
            return

        # Set up keyboard shortcuts
        kbmanager = KeyBindingManager.for_prompt(
            enable_open_in_editor=self.extra_open_editor_shortcuts, )
        register_ipython_shortcuts(kbmanager.registry, self)

        # 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(cell)
                last_cell = cell

        self._style = self._make_style_from_name_or_cls(
            self.highlighting_style)
        self.style = DynamicStyle(lambda: self._style)

        editing_mode = getattr(EditingMode, self.editing_mode.upper())

        def patch_stdout(**kwargs):
            return self.pt_cli.patch_stdout_context(**kwargs)

        self._pt_app = create_prompt_application(
            editing_mode=editing_mode,
            key_bindings_registry=kbmanager.registry,
            history=history,
            completer=IPythonPTCompleter(shell=self,
                                         patch_stdout=patch_stdout),
            enable_history_search=True,
            style=self.style,
            mouse_support=self.mouse_support,
            **self._layout_options())
        self._eventloop = create_eventloop(self.inputhook)
        self.pt_cli = CommandLineInterface(
            self._pt_app,
            eventloop=self._eventloop,
            output=create_output(true_color=self.true_color))

    def _make_style_from_name_or_cls(self, name_or_cls):
        """
        Small wrapper that make an IPython compatible style from a style name

        We need that to add style for prompt ... etc.
        """
        style_overrides = {}
        if name_or_cls == 'legacy':
            legacy = self.colors.lower()
            if legacy == 'linux':
                style_cls = get_style_by_name('monokai')
                style_overrides = _style_overrides_linux
            elif legacy == 'lightbg':
                style_overrides = _style_overrides_light_bg
                style_cls = get_style_by_name('pastie')
            elif legacy == 'neutral':
                # 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_cls = get_style_by_name('default')
                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',
                    Token.Prompt: '#009900',
                    Token.PromptNum: '#00ff00 bold',
                    Token.OutPrompt: '#990000',
                    Token.OutPromptNum: '#ff0000 bold',
                })

                # Hack: Due to limited color support on the Windows console
                # the prompt colors will be wrong without this
                if os.name == 'nt':
                    style_overrides.update({
                        Token.Prompt: '#ansidarkgreen',
                        Token.PromptNum: '#ansigreen bold',
                        Token.OutPrompt: '#ansidarkred',
                        Token.OutPromptNum: '#ansired bold',
                    })
            elif legacy == 'nocolor':
                style_cls = _NoStyle
                style_overrides = {}
            else:
                raise ValueError('Got unknown colors: ', legacy)
        else:
            if isinstance(name_or_cls, str):
                style_cls = get_style_by_name(name_or_cls)
            else:
                style_cls = name_or_cls
            style_overrides = {
                Token.Prompt: '#009900',
                Token.PromptNum: '#00ff00 bold',
                Token.OutPrompt: '#990000',
                Token.OutPromptNum: '#ff0000 bold',
            }
        style_overrides.update(self.highlighting_style_overrides)
        style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
                                            style_dict=style_overrides)

        return style

    def _layout_options(self):
        """
        Return the current layout option for the current Terminal InteractiveShell
        """
        return {
            'lexer':
            IPythonPTLexer(),
            'reserve_space_for_menu':
            self.space_for_menu,
            'get_prompt_tokens':
            self.prompts.in_prompt_tokens,
            'get_continuation_tokens':
            self.prompts.continuation_prompt_tokens,
            'multiline':
            True,
            'display_completions_in_columns':
            (self.display_completions == 'multicolumn'),

            # Highlight matching brackets, but only when this setting is
            # enabled, and only when the DEFAULT_BUFFER has the focus.
            'extra_input_processors': [
                ConditionalProcessor(
                    processor=HighlightMatchingBracketProcessor(
                        chars='[](){}'),
                    filter=HasFocus(DEFAULT_BUFFER) & ~IsDone()
                    & Condition(lambda cli: self.highlight_matching_brackets))
            ],
        }

    def _update_layout(self):
        """
        Ask for a re computation of the application layout, if for example ,
        some configuration options have changed.
        """
        if self._pt_app:
            self._pt_app.layout = create_prompt_layout(
                **self._layout_options())

    def prompt_for_code(self):
        with self.pt_cli.patch_stdout_context(raw=True):
            document = self.pt_cli.run(pre_run=self.pre_prompt,
                                       reset_current_buffer=True)
        return document.text

    def enable_win_unicode_console(self):
        if sys.version_info >= (3, 6):
            # Since PEP 528, Python uses the unicode APIs for the Windows
            # console by default, so WUC shouldn't be needed.
            return

        import win_unicode_console
        win_unicode_console.enable()

    def init_io(self):
        if sys.platform not in {'win32', 'cli'}:
            return

        self.enable_win_unicode_console()

        import colorama
        colorama.init()

        # For some reason we make these wrappers around stdout/stderr.
        # For now, we need to reset them so all output gets coloured.
        # https://github.com/ipython/ipython/issues/8669
        # io.std* are deprecated, but don't show our own deprecation warnings
        # during initialization of the deprecated API.
        with warnings.catch_warnings():
            warnings.simplefilter('ignore', DeprecationWarning)
            io.stdout = io.IOStream(sys.stdout)
            io.stderr = io.IOStream(sys.stderr)

    def init_magics(self):
        super(TerminalInteractiveShell, self).init_magics()
        self.register_magics(TerminalMagics)

    def init_alias(self):
        # The parent class defines aliases that can be safely used with any
        # frontend.
        super(TerminalInteractiveShell, self).init_alias()

        # Now define aliases that only make sense on the terminal, because they
        # need direct access to the console in a way that we can't emulate in
        # GUI or web frontend
        if os.name == 'posix':
            for cmd in ['clear', 'more', 'less', 'man']:
                self.alias_manager.soft_define_alias(cmd, cmd)

    def __init__(self, *args, **kwargs):
        super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
        self.init_prompt_toolkit_cli()
        self.init_term_title()
        self.keep_running = True

        self.debugger_history = InMemoryHistory()

    def ask_exit(self):
        self.keep_running = False

    rl_next_input = None

    def pre_prompt(self):
        if self.rl_next_input:
            # We can't set the buffer here, because it will be reset just after
            # this. Adding a callable to pre_run_callables does what we need
            # after the buffer is reset.
            s = self.rl_next_input

            def set_doc():
                self.pt_cli.application.buffer.document = Document(s)

            if hasattr(self.pt_cli, 'pre_run_callables'):
                self.pt_cli.pre_run_callables.append(set_doc)
            else:
                # Older version of prompt_toolkit; it's OK to set the document
                # directly here.
                set_doc()
            self.rl_next_input = None

    def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):

        if display_banner is not DISPLAY_BANNER_DEPRECATED:
            warn(
                'interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.',
                DeprecationWarning,
                stacklevel=2)

        self.keep_running = True
        while self.keep_running:
            print(self.separate_in, end='')

            try:
                code = self.prompt_for_code()
            except EOFError:
                if (not self.confirm_exit) \
                        or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
                    self.ask_exit()

            else:
                if code:
                    self.run_cell(code, store_history=True)

    def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
        # An extra layer of protection in case someone mashing Ctrl-C breaks
        # out of our internal code.
        if display_banner is not DISPLAY_BANNER_DEPRECATED:
            warn(
                'mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.',
                DeprecationWarning,
                stacklevel=2)
        while True:
            try:
                self.interact()
                break
            except KeyboardInterrupt as e:
                print("\n%s escaped interact()\n" % type(e).__name__)
            finally:
                # An interrupt during the eventloop will mess up the
                # internal state of the prompt_toolkit library.
                # Stopping the eventloop fixes this, see
                # https://github.com/ipython/ipython/pull/9867
                if hasattr(self, '_eventloop'):
                    self._eventloop.stop()

    _inputhook = None

    def inputhook(self, context):
        if self._inputhook is not None:
            self._inputhook(context)

    active_eventloop = None

    def enable_gui(self, gui=None):
        if gui:
            self.active_eventloop, self._inputhook =\
                get_inputhook_name_and_func(gui)
        else:
            self.active_eventloop = self._inputhook = None

    # Run !system commands directly, not through pipes, so terminal programs
    # work correctly.
    system = InteractiveShell.system_raw

    def auto_rewrite_input(self, cmd):
        """Overridden from the parent class to use fancy rewriting prompt"""
        if not self.show_rewritten_input:
            return

        tokens = self.prompts.rewrite_prompt_tokens()
        if self.pt_cli:
            self.pt_cli.print_tokens(tokens)
            print(cmd)
        else:
            prompt = ''.join(s for t, s in tokens)
            print(prompt, cmd, sep='')

    _prompts_before = None

    def switch_doctest_mode(self, mode):
        """Switch prompts to classic for %doctest_mode"""
        if mode:
            self._prompts_before = self.prompts
            self.prompts = ClassicPrompts(self)
        elif self._prompts_before:
            self.prompts = self._prompts_before
            self._prompts_before = None
        self._update_layout()
Exemplo n.º 2
0
def embed(globals=None, locals=None, configure=None,
          vi_mode=False, history_filename=None, title=None,
          startup_paths=None, patch_stdout=False, return_asyncio_coroutine=False):
    """
    Call this to embed  Python shell at the current point in your program.
    It's similar to `IPython.embed` and `bpython.embed`. ::

        from prompt_toolkit.contrib.repl import embed
        embed(globals(), locals())

    :param vi_mode: Boolean. Use Vi instead of Emacs key bindings.
    :param configure: Callable that will be called with the `PythonRepl` as a first
                      argument, to trigger configuration.
    :param title: Title to be displayed in the terminal titlebar. (None or string.)
    """
    assert configure is None or callable(configure)

    # Default globals/locals
    if globals is None:
        globals = {
            '__name__': '__main__',
            '__package__': None,
            '__doc__': None,
            '__builtins__': six.moves.builtins,
        }

    locals = locals or globals

    def get_globals():
        return globals

    def get_locals():
        return locals

    # Create eventloop.
    if return_asyncio_coroutine:
        eventloop = create_asyncio_eventloop()
    else:
        eventloop = create_eventloop()

    # Create REPL.
    repl = PythonRepl(get_globals, get_locals, vi_mode=vi_mode,
                      history_filename=history_filename,
                      startup_paths=startup_paths)

    if title:
        repl.terminal_title = title

    if configure:
        configure(repl)

    cli = CommandLineInterface(application=repl.create_application(), eventloop=eventloop)

    # Start repl.
    patch_context = cli.patch_stdout_context() if patch_stdout else DummyContext()

    if return_asyncio_coroutine:
        def coroutine():
            with patch_context:
                for future in cli.run_async():
                    yield future
        return coroutine()
    else:
        with patch_context:
            cli.run()
Exemplo n.º 3
0
    def prompt(self, 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
        `~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
        create_prompt_application().

        Parameters
        ----------
        patch_stdout : file-like, optional
            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.)
        return_asyncio_coroutine : bool, optional
            When True, return a asyncio coroutine. (Python >3.3)

        Notes
        -----
        This method was forked from the mainline prompt-toolkit repo.
        Copyright (c) 2014, Jonathan Slenders, All rights reserved.
        """
        patch_stdout = kwargs.pop('patch_stdout', False)
        return_asyncio_coroutine = kwargs.pop('return_asyncio_coroutine',
                                              False)
        if return_asyncio_coroutine:
            eventloop = create_asyncio_eventloop()
        else:
            eventloop = kwargs.pop('eventloop', None) or create_eventloop()

        # Create CommandLineInterface.
        if self.cli is None:
            if self.major_minor < (0, 57):
                kwargs.pop('reserve_space_for_menu', None)
            if self.major_minor <= (0, 57):
                kwargs.pop('get_rprompt_tokens', None)
                kwargs.pop('get_continuation_tokens', None)
            # VI_Mode handling changed in prompt_toolkit v1.0
            if self.major_minor >= (1, 0):
                from prompt_toolkit.enums import EditingMode
                if builtins.__xonsh_env__.get('VI_MODE'):
                    editing_mode = EditingMode.VI
                else:
                    editing_mode = EditingMode.EMACS
                kwargs['editing_mode'] = editing_mode
                kwargs['vi_mode'] = builtins.__xonsh_env__.get('VI_MODE')
            cli = CommandLineInterface(application=create_prompt_application(
                message, **kwargs),
                                       eventloop=eventloop,
                                       output=create_output())
            self.cli = cli
        else:
            cli = self.cli

        # Replace stdout.
        patch_context = cli.patch_stdout_context(
        ) 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}
            exec(
                textwrap.dedent('''
            import asyncio
            @asyncio.coroutine
            def prompt_coro():
                with patch_context:
                    document = yield from cli.run_async(reset_current_buffer=False)
                    if document:
                        return document.text
            '''), exec_context)
            return exec_context['prompt_coro']()
        else:
            # Note: We pass `reset_current_buffer=False`, because that way
            # it's easy to give DEFAULT_BUFFER a default value, without it
            # getting erased. We don't have to reset anyway, because this is
            # the first and only time that this CommandLineInterface will run.
            try:
                with patch_context:
                    document = cli.run(reset_current_buffer=False)

                    if document:
                        return document.text
            finally:
                eventloop.close()
Exemplo n.º 4
0
    def prompt(self, 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
        `~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
        create_prompt_application().

        Parameters
        ----------
        patch_stdout : file-like, optional
            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.)
        return_asyncio_coroutine : bool, optional
            When True, return a asyncio coroutine. (Python >3.3)

        Notes
        -----
        This method was forked from the mainline prompt-toolkit repo.
        Copyright (c) 2014, Jonathan Slenders, All rights reserved.
        """
        patch_stdout = kwargs.pop("patch_stdout", False)
        return_asyncio_coroutine = kwargs.pop("return_asyncio_coroutine", False)
        if return_asyncio_coroutine:
            eventloop = create_asyncio_eventloop()
        else:
            eventloop = kwargs.pop("eventloop", None) or create_eventloop()

        # Create CommandLineInterface.
        if self.cli is None:
            if builtins.__xonsh__.env.get("VI_MODE"):
                editing_mode = EditingMode.VI
            else:
                editing_mode = EditingMode.EMACS
            kwargs["editing_mode"] = editing_mode
            cli = CommandLineInterface(
                application=create_prompt_application(message, **kwargs),
                eventloop=eventloop,
                output=create_output(),
            )
            self.cli = cli
        else:
            cli = self.cli

        # Replace stdout.
        patch_context = cli.patch_stdout_context() 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}
            exec(
                textwrap.dedent(
                    """
            import asyncio
            @asyncio.coroutine
            def prompt_coro():
                with patch_context:
                    document = yield from cli.run_async(reset_current_buffer=False)
                    if document:
                        return document.text
            """
                ),
                exec_context,
            )
            return exec_context["prompt_coro"]()
        else:
            # Note: We pass `reset_current_buffer=False`, because that way
            # it's easy to give DEFAULT_BUFFER a default value, without it
            # getting erased. We don't have to reset anyway, because this is
            # the first and only time that this CommandLineInterface will run.
            try:
                with patch_context:
                    document = cli.run(reset_current_buffer=False)
                    if document:
                        return document.text
            except Exception:
                xt.print_exception()
                # return something to prevent xonsh crash when any
                # exceptions raise
                return ""
            finally:
                eventloop.close()
Exemplo n.º 5
0
    def prompt(self, 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
        `~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
        create_prompt_application().

        Parameters
        ----------
        patch_stdout : file-like, optional
            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.)
        return_asyncio_coroutine : bool, optional
            When True, return a asyncio coroutine. (Python >3.3)

        Notes
        -----
        This method was forked from the mainline prompt-toolkit repo.
        Copyright (c) 2014, Jonathan Slenders, All rights reserved.
        """
        patch_stdout = kwargs.pop('patch_stdout', False)
        return_asyncio_coroutine = kwargs.pop('return_asyncio_coroutine', False)
        if return_asyncio_coroutine:
            eventloop = create_asyncio_eventloop()
        else:
            eventloop = kwargs.pop('eventloop', None) or create_eventloop()

        # Create CommandLineInterface.
        if self.cli is None:
            if self.major_minor < (0, 57):
                kwargs.pop('reserve_space_for_menu', None)
            if self.major_minor <= (0, 57):
                kwargs.pop('get_rprompt_tokens', None)
                kwargs.pop('get_continuation_tokens', None)
            # VI_Mode handling changed in prompt_toolkit v1.0
            if self.major_minor >= (1, 0):
                from prompt_toolkit.enums import EditingMode
                if builtins.__xonsh_env__.get('VI_MODE'):
                    editing_mode = EditingMode.VI
                else:
                    editing_mode = EditingMode.EMACS
                kwargs['editing_mode'] = editing_mode
                kwargs['vi_mode'] = builtins.__xonsh_env__.get('VI_MODE')
            cli = CommandLineInterface(
                application=create_prompt_application(message, **kwargs),
                eventloop=eventloop,
                output=create_output())
            self.cli = cli
        else:
            cli = self.cli

        # Replace stdout.
        patch_context = cli.patch_stdout_context() 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}
            exec(textwrap.dedent('''
            import asyncio
            @asyncio.coroutine
            def prompt_coro():
                with patch_context:
                    document = yield from cli.run_async(reset_current_buffer=False)
                    if document:
                        return document.text
            '''), exec_context)
            return exec_context['prompt_coro']()
        else:
            # Note: We pass `reset_current_buffer=False`, because that way
            # it's easy to give DEFAULT_BUFFER a default value, without it
            # getting erased. We don't have to reset anyway, because this is
            # the first and only time that this CommandLineInterface will run.
            try:
                with patch_context:
                    document = cli.run(reset_current_buffer=False)

                    if document:
                        return document.text
            finally:
                eventloop.close()
Exemplo n.º 6
0
class TerminalInteractiveShell(InteractiveShell):
    space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
                                                  'to reserve for the completion menu'
                            ).tag(config=True)

    def _space_for_menu_changed(self, old, new):
        self._update_layout()

    pt_cli = None
    debugger_history = None
    _pt_app = None

    simple_prompt = Bool(_use_simple_prompt,
        help="""Use `raw_input` for the REPL, without completion and prompt colors.

            Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
            IPython own testing machinery, and emacs inferior-shell integration through elpy.

            This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
            environment variable is set, or the current terminal is not a tty."""
            ).tag(config=True)

    @property
    def debugger_cls(self):
        return Pdb if self.simple_prompt else TerminalPdb

    confirm_exit = Bool(True,
        help="""
        Set to confirm when you try to exit IPython with an EOF (Control-D
        in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
        you can force a direct exit without any confirmation.""",
    ).tag(config=True)

    editing_mode = Unicode('emacs',
        help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
    ).tag(config=True)

    mouse_support = Bool(False,
        help="Enable mouse support in the prompt\n(Note: prevents selecting text with the mouse)"
    ).tag(config=True)

    # We don't load the list of styles for the help string, because loading
    # Pygments plugins takes time and can cause unexpected errors.
    highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
        help="""The name or class of a Pygments style to use for syntax
        highlighting. To see available styles, run `pygmentize -L styles`."""
    ).tag(config=True)


    @observe('highlighting_style')
    @observe('colors')
    def _highlighting_style_changed(self, change):
        self.refresh_style()

    def refresh_style(self):
        self._style = self._make_style_from_name_or_cls(self.highlighting_style)


    highlighting_style_overrides = Dict(
        help="Override highlighting format for specific tokens"
    ).tag(config=True)

    true_color = Bool(False,
        help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
              "If your terminal supports true color, the following command "
              "should print 'TRUECOLOR' in orange: "
              "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
    ).tag(config=True)

    editor = Unicode(get_default_editor(),
        help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
    ).tag(config=True)

    prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)

    prompts = Instance(Prompts)

    @default('prompts')
    def _prompts_default(self):
        return self.prompts_class(self)

    @observe('prompts')
    def _(self, change):
        self._update_layout()

    @default('displayhook_class')
    def _displayhook_class_default(self):
        return RichPromptDisplayHook

    term_title = Bool(True,
        help="Automatically set the terminal title"
    ).tag(config=True)

    term_title_format = Unicode("IPython: {cwd}",
        help="Customize the terminal title format.  This is a python format string. " +
             "Available substitutions are: {cwd}."
    ).tag(config=True)

    display_completions = Enum(('column', 'multicolumn','readlinelike'),
        help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
                "'readlinelike'. These options are for `prompt_toolkit`, see "
                "`prompt_toolkit` documentation for more information."
                ),
        default_value='multicolumn').tag(config=True)

    highlight_matching_brackets = Bool(True,
        help="Highlight matching brackets.",
    ).tag(config=True)

    extra_open_editor_shortcuts = Bool(False,
        help="Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. "
             "This is in addition to the F2 binding, which is always enabled."
    ).tag(config=True)

    handle_return = Any(None,
        help="Provide an alternative handler to be called when the user presses "
             "Return. This is an advanced option intended for debugging, which "
             "may be changed or removed in later releases."
    ).tag(config=True)

    @observe('term_title')
    def init_term_title(self, change=None):
        # Enable or disable the terminal title.
        if self.term_title:
            toggle_set_term_title(True)
            set_term_title(self.term_title_format.format(cwd=abbrev_cwd()))
        else:
            toggle_set_term_title(False)

    def init_display_formatter(self):
        super(TerminalInteractiveShell, self).init_display_formatter()
        # terminal only supports plain text
        self.display_formatter.active_types = ['text/plain']
        # disable `_ipython_display_`
        self.display_formatter.ipython_display_formatter.enabled = False

    def init_prompt_toolkit_cli(self):
        if self.simple_prompt:
            # Fall back to plain non-interactive output for tests.
            # This is very limited, and only accepts a single line.
            def prompt():
                isp = self.input_splitter
                prompt_text = "".join(x[1] for x in self.prompts.in_prompt_tokens())
                prompt_continuation = "".join(x[1] for x in self.prompts.continuation_prompt_tokens())
                while isp.push_accepts_more():
                    line = input(prompt_text)
                    isp.push(line)
                    prompt_text = prompt_continuation
                return isp.source_reset()
            self.prompt_for_code = prompt
            return

        # Set up keyboard shortcuts
        kbmanager = KeyBindingManager.for_prompt(
            enable_open_in_editor=self.extra_open_editor_shortcuts,
        )
        register_ipython_shortcuts(kbmanager.registry, self)

        # 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(cell)
                last_cell = cell

        self._style = self._make_style_from_name_or_cls(self.highlighting_style)
        self.style = DynamicStyle(lambda: self._style)

        editing_mode = getattr(EditingMode, self.editing_mode.upper())

        def patch_stdout(**kwargs):
            return self.pt_cli.patch_stdout_context(**kwargs)

        self._pt_app = create_prompt_application(
                            editing_mode=editing_mode,
                            key_bindings_registry=kbmanager.registry,
                            history=history,
                            completer=IPythonPTCompleter(shell=self,
                                                    patch_stdout=patch_stdout),
                            enable_history_search=True,
                            style=self.style,
                            mouse_support=self.mouse_support,
                            **self._layout_options()
        )
        self._eventloop = create_eventloop(self.inputhook)
        self.pt_cli = CommandLineInterface(
            self._pt_app, eventloop=self._eventloop,
            output=create_output(true_color=self.true_color))

    def _make_style_from_name_or_cls(self, name_or_cls):
        """
        Small wrapper that make an IPython compatible style from a style name

        We need that to add style for prompt ... etc.
        """
        style_overrides = {}
        if name_or_cls == 'legacy':
            legacy = self.colors.lower()
            if legacy == 'linux':
                style_cls = get_style_by_name('monokai')
                style_overrides = _style_overrides_linux
            elif legacy == 'lightbg':
                style_overrides = _style_overrides_light_bg
                style_cls = get_style_by_name('pastie')
            elif legacy == 'neutral':
                # 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_cls = get_style_by_name('default')
                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',
                    Token.Prompt: '#009900',
                    Token.PromptNum: '#00ff00 bold',
                    Token.OutPrompt: '#990000',
                    Token.OutPromptNum: '#ff0000 bold',
                })

                # Hack: Due to limited color support on the Windows console
                # the prompt colors will be wrong without this
                if os.name == 'nt':
                    style_overrides.update({
                        Token.Prompt: '#ansidarkgreen',
                        Token.PromptNum: '#ansigreen bold',
                        Token.OutPrompt: '#ansidarkred',
                        Token.OutPromptNum: '#ansired bold',
                    })
            elif legacy =='nocolor':
                style_cls=_NoStyle
                style_overrides = {}
            else :
                raise ValueError('Got unknown colors: ', legacy)
        else :
            if isinstance(name_or_cls, str):
                style_cls = get_style_by_name(name_or_cls)
            else:
                style_cls = name_or_cls
            style_overrides = {
                Token.Prompt: '#009900',
                Token.PromptNum: '#00ff00 bold',
                Token.OutPrompt: '#990000',
                Token.OutPromptNum: '#ff0000 bold',
            }
        style_overrides.update(self.highlighting_style_overrides)
        style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
                                            style_dict=style_overrides)

        return style

    def _layout_options(self):
        """
        Return the current layout option for the current Terminal InteractiveShell
        """
        return {
                'lexer':IPythonPTLexer(),
                'reserve_space_for_menu':self.space_for_menu,
                'get_prompt_tokens':self.prompts.in_prompt_tokens,
                'get_continuation_tokens':self.prompts.continuation_prompt_tokens,
                'multiline':True,
                'display_completions_in_columns': (self.display_completions == 'multicolumn'),

                # Highlight matching brackets, but only when this setting is
                # enabled, and only when the DEFAULT_BUFFER has the focus.
                'extra_input_processors': [ConditionalProcessor(
                        processor=HighlightMatchingBracketProcessor(chars='[](){}'),
                        filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
                            Condition(lambda cli: self.highlight_matching_brackets))],
                }

    def _update_layout(self):
        """
        Ask for a re computation of the application layout, if for example ,
        some configuration options have changed.
        """
        if self._pt_app:
            self._pt_app.layout = create_prompt_layout(**self._layout_options())

    def prompt_for_code(self):
        with self.pt_cli.patch_stdout_context(raw=True):
            document = self.pt_cli.run(
                pre_run=self.pre_prompt, reset_current_buffer=True)
        return document.text

    def enable_win_unicode_console(self):
        if sys.version_info >= (3, 6):
            # Since PEP 528, Python uses the unicode APIs for the Windows
            # console by default, so WUC shouldn't be needed.
            return

        import win_unicode_console
        win_unicode_console.enable()

    def init_io(self):
        if sys.platform not in {'win32', 'cli'}:
            return

        self.enable_win_unicode_console()

        import colorama
        colorama.init()

        # For some reason we make these wrappers around stdout/stderr.
        # For now, we need to reset them so all output gets coloured.
        # https://github.com/ipython/ipython/issues/8669
        # io.std* are deprecated, but don't show our own deprecation warnings
        # during initialization of the deprecated API.
        with warnings.catch_warnings():
            warnings.simplefilter('ignore', DeprecationWarning)
            io.stdout = io.IOStream(sys.stdout)
            io.stderr = io.IOStream(sys.stderr)

    def init_magics(self):
        super(TerminalInteractiveShell, self).init_magics()
        self.register_magics(TerminalMagics)

    def init_alias(self):
        # The parent class defines aliases that can be safely used with any
        # frontend.
        super(TerminalInteractiveShell, self).init_alias()

        # Now define aliases that only make sense on the terminal, because they
        # need direct access to the console in a way that we can't emulate in
        # GUI or web frontend
        if os.name == 'posix':
            for cmd in ['clear', 'more', 'less', 'man']:
                self.alias_manager.soft_define_alias(cmd, cmd)


    def __init__(self, *args, **kwargs):
        super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
        self.init_prompt_toolkit_cli()
        self.init_term_title()
        self.keep_running = True

        self.debugger_history = InMemoryHistory()

    def ask_exit(self):
        self.keep_running = False

    rl_next_input = None

    def pre_prompt(self):
        if self.rl_next_input:
            # We can't set the buffer here, because it will be reset just after
            # this. Adding a callable to pre_run_callables does what we need
            # after the buffer is reset.
            s = self.rl_next_input
            def set_doc():
                self.pt_cli.application.buffer.document = Document(s)
            if hasattr(self.pt_cli, 'pre_run_callables'):
                self.pt_cli.pre_run_callables.append(set_doc)
            else:
                # Older version of prompt_toolkit; it's OK to set the document
                # directly here.
                set_doc()
            self.rl_next_input = None

    def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):

        if display_banner is not DISPLAY_BANNER_DEPRECATED:
            warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)

        self.keep_running = True
        while self.keep_running:
            print(self.separate_in, end='')

            try:
                code = self.prompt_for_code()
            except EOFError:
                if (not self.confirm_exit) \
                        or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
                    self.ask_exit()

            else:
                if code:
                    self.run_cell(code, store_history=True)

    def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
        # An extra layer of protection in case someone mashing Ctrl-C breaks
        # out of our internal code.
        if display_banner is not DISPLAY_BANNER_DEPRECATED:
            warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
        while True:
            try:
                self.interact()
                break
            except KeyboardInterrupt as e:
                print("\n%s escaped interact()\n" % type(e).__name__)
            finally:
                # An interrupt during the eventloop will mess up the
                # internal state of the prompt_toolkit library.
                # Stopping the eventloop fixes this, see
                # https://github.com/ipython/ipython/pull/9867
                if hasattr(self, '_eventloop'):
                    self._eventloop.stop()

    _inputhook = None
    def inputhook(self, context):
        if self._inputhook is not None:
            self._inputhook(context)

    active_eventloop = None
    def enable_gui(self, gui=None):
        if gui:
            self.active_eventloop, self._inputhook =\
                get_inputhook_name_and_func(gui)
        else:
            self.active_eventloop = self._inputhook = None

    # Run !system commands directly, not through pipes, so terminal programs
    # work correctly.
    system = InteractiveShell.system_raw

    def auto_rewrite_input(self, cmd):
        """Overridden from the parent class to use fancy rewriting prompt"""
        if not self.show_rewritten_input:
            return

        tokens = self.prompts.rewrite_prompt_tokens()
        if self.pt_cli:
            self.pt_cli.print_tokens(tokens)
            print(cmd)
        else:
            prompt = ''.join(s for t, s in tokens)
            print(prompt, cmd, sep='')

    _prompts_before = None
    def switch_doctest_mode(self, mode):
        """Switch prompts to classic for %doctest_mode"""
        if mode:
            self._prompts_before = self.prompts
            self.prompts = ClassicPrompts(self)
        elif self._prompts_before:
            self.prompts = self._prompts_before
            self._prompts_before = None
        self._update_layout()
Exemplo n.º 7
0
    def prompt(self, 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
        `~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
        create_prompt_application().

        Parameters
        ----------
        patch_stdout : file-like, optional
            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.)
        return_asyncio_coroutine : bool, optional
            When True, return a asyncio coroutine. (Python >3.3)

        Notes
        -----
        This method was forked from the mainline prompt-toolkit repo.
        Copyright (c) 2014, Jonathan Slenders, All rights reserved.
        """
        patch_stdout = kwargs.pop("patch_stdout", False)
        return_asyncio_coroutine = kwargs.pop("return_asyncio_coroutine",
                                              False)
        if return_asyncio_coroutine:
            eventloop = create_asyncio_eventloop()
        else:
            eventloop = kwargs.pop("eventloop", None) or create_eventloop()

        # Create CommandLineInterface.
        if self.cli is None:
            if builtins.__xonsh_env__.get("VI_MODE"):
                editing_mode = EditingMode.VI
            else:
                editing_mode = EditingMode.EMACS
            kwargs["editing_mode"] = editing_mode
            cli = CommandLineInterface(
                application=create_prompt_application(message, **kwargs),
                eventloop=eventloop,
                output=create_output(),
            )
            self.cli = cli
        else:
            cli = self.cli

        # Replace stdout.
        patch_context = cli.patch_stdout_context(
        ) 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}
            exec(
                textwrap.dedent("""
            import asyncio
            @asyncio.coroutine
            def prompt_coro():
                with patch_context:
                    document = yield from cli.run_async(reset_current_buffer=False)
                    if document:
                        return document.text
            """),
                exec_context,
            )
            return exec_context["prompt_coro"]()
        else:
            # Note: We pass `reset_current_buffer=False`, because that way
            # it's easy to give DEFAULT_BUFFER a default value, without it
            # getting erased. We don't have to reset anyway, because this is
            # the first and only time that this CommandLineInterface will run.
            try:
                with patch_context:
                    document = cli.run(reset_current_buffer=False)
                    if document:
                        return document.text
            except Exception:
                xt.print_exception()
                # return something to prevent xonsh crash when any
                # exceptions raise
                return ""
            finally:
                eventloop.close()
Exemplo n.º 8
0
def embed(globals=None,
          locals=None,
          configure=None,
          vi_mode=False,
          history_filename=None,
          title=None,
          startup_paths=None,
          patch_stdout=False,
          return_asyncio_coroutine=False):
    """
    Call this to embed  Python shell at the current point in your program.
    It's similar to `IPython.embed` and `bpython.embed`. ::

        from prompt_toolkit.contrib.repl import embed
        embed(globals(), locals())

    :param vi_mode: Boolean. Use Vi instead of Emacs key bindings.
    :param configure: Callable that will be called with the `PythonRepl` as a first
                      argument, to trigger configuration.
    :param title: Title to be displayed in the terminal titlebar. (None or string.)
    """
    assert configure is None or callable(configure)

    # Default globals/locals
    if globals is None:
        globals = {
            '__name__': '__main__',
            '__package__': None,
            '__doc__': None,
            '__builtins__': six.moves.builtins,
        }

    locals = locals or globals

    def get_globals():
        return globals

    def get_locals():
        return locals

    # Create eventloop.
    if return_asyncio_coroutine:
        eventloop = create_asyncio_eventloop()
    else:
        eventloop = create_eventloop()

    # Create REPL.
    repl = PythonRepl(get_globals,
                      get_locals,
                      vi_mode=vi_mode,
                      history_filename=history_filename,
                      startup_paths=startup_paths)

    if title:
        repl.terminal_title = title

    if configure:
        configure(repl)

    cli = CommandLineInterface(application=repl.create_application(),
                               eventloop=eventloop)

    # Start repl.
    patch_context = cli.patch_stdout_context(
    ) if patch_stdout else DummyContext()

    if return_asyncio_coroutine:

        def coroutine():
            with patch_context:
                for future in cli.run_async():
                    yield future

        return coroutine()
    else:
        with patch_context:
            cli.run()
Exemplo n.º 9
0
class Pager(object):
    """
    The Pager main application.

    Usage::
        p = Pager()
        p.add_source(...)
        p.run()

    :param source: :class:`.Source` instance.
    :param lexer: Prompt_toolkit `lexer` instance.
    :param vi_mode: Enable Vi key bindings.
    :param style: Prompt_toolkit `Style` instance.
    :param search_text: `None` or the search string that is highlighted.
    """
    def __init__(self,
                 vi_mode=False,
                 style=None,
                 search_text=None,
                 titlebar_tokens=None):
        assert isinstance(vi_mode, bool)
        assert style is None or isinstance(style, Style)

        self.sources = []
        self.current_source = 0  # Index in `self.sources`.
        self.vi_mode = vi_mode
        self.highlight_search = True
        self.in_colon_mode = False
        self.message = None
        self.displaying_help = False
        self.search_text = search_text
        self.display_titlebar = bool(titlebar_tokens)
        self.titlebar_tokens = titlebar_tokens or []

        # When this is True, always make sure that the cursor goes to the
        # bottom of the visible content. This is similar to 'tail -f'.
        self.forward_forever = False

        # Status information for all sources. Source -> _SourceInfo.
        # (Remember this info as long as the Source object exists.)
        self.source_info = weakref.WeakKeyDictionary()

        # Create prompt_toolkit stuff.
        self.buffers = BufferMapping({})

        def open_file(cli, buff):
            # Open file.
            self.open_file(buff.text)

            # Focus main buffer again.
            self.buffers.focus(cli, self.source_info[self.source].buffer_name)
            buff.reset()

        self.buffers['EXAMINE'] = Buffer(
            # Buffer for the 'Examine:' input.
            completer=PathCompleter(expanduser=True),
            accept_action=AcceptAction(open_file))

        self.layout = Layout(self)

        registry = create_key_bindings(self)
        self.application = Application(layout=self.layout.container,
                                       buffers=self.buffers,
                                       key_bindings_registry=registry,
                                       style=style or create_style(),
                                       mouse_support=True,
                                       on_render=self._on_render,
                                       use_alternate_screen=True,
                                       on_initialize=self._on_cli_initialize)

        self.cli = None
        self.eventloop = None

    def _on_cli_initialize(self, cli):
        """
        Called when a CommandLineInterface has been created.
        """
        def synchronize(_=None):
            if self.vi_mode:
                cli.editing_mode = EditingMode.VI
            else:
                cli.editing_mode = EditingMode.EMACS

        cli.input_processor.beforeKeyPress += synchronize
        cli.input_processor.afterKeyPress += synchronize
        synchronize()

    @classmethod
    def from_pipe(cls, lexer=None):
        """
        Create a pager from another process that pipes in our stdin.
        """
        assert not sys.stdin.isatty()
        self = cls()
        self.add_source(PipeSource(fileno=sys.stdin.fileno(), lexer=lexer))
        return self

    @property
    def source(self):
        " The current `Source`. "
        return self.sources[self.current_source]

    @property
    def line_tokens(self):
        return self.source_info[self.source].line_tokens

    @property
    def waiting_for_input_stream(self):
        return self.source_info[self.source].waiting_for_input_stream

    @property
    def marks(self):
        return self.source_info[self.source].marks

    def open_file(self, filename):
        """
        Open this file.
        """
        lexer = PygmentsLexer.from_filename(filename, sync_from_start=False)

        try:
            source = FileSource(filename, lexer=lexer)
        except IOError as e:
            self.message = '{}'.format(e)
        else:
            self.add_source(source)

    def add_source(self, source):
        """
        Add a new :class:`.Source` instance.
        """
        assert isinstance(source, Source)

        source_info = _SourceInfo()
        self.source_info[source] = source_info

        self.buffers[source_info.buffer_name] = source_info.buffer
        self.sources.append(source)

        # Focus
        self.current_source = len(self.sources) - 1
        self.buffers.focus(None, source_info.buffer_name)

    def remove_current_source(self):
        """
        Remove the current source from the pager.
        (If >1 source is left.)
        """
        if len(self.sources) > 1:
            current_source = self.source

            # Focus the previous source.
            self.focus_previous_source()

            # Remove the last source.
            buffer_name = self.source_info[current_source].buffer_name
            self.sources.remove(current_source)
            del self.buffers[buffer_name]
        else:
            self.message = "Can't remove the last buffer."

    def focus_previous_source(self):
        self.current_source = (self.current_source - 1) % len(self.sources)
        self.buffers.focus(None, self.source_info[self.source].buffer_name)
        self.in_colon_mode = False

    def focus_next_source(self):
        self.current_source = (self.current_source + 1) % len(self.sources)
        self.buffers.focus(None, self.source_info[self.source].buffer_name)
        self.in_colon_mode = False

    def display_help(self):
        """
        Display help text.
        """
        if not self.displaying_help:
            source = StringSource(HELP, lexer=PygmentsLexer(RstLexer))
            self.add_source(source)
            self.displaying_help = True

    def quit_help(self):
        """
        Hide the help text.
        """
        if self.displaying_help:
            self.remove_current_source()
            self.displaying_help = False

    def _on_render(self, cli):
        """
        Each time when the rendering is done, we should see whether we need to
        read more data from the input pipe.
        """
        # When the bottom is visible, read more input.
        # Try at least `info.window_height`, if this amount of data is
        # available.
        info = self.layout.dynamic_body.get_render_info()
        source = self.source
        source_info = self.source_info[source]
        b = source_info.buffer

        if not source_info.waiting_for_input_stream and not source.eof(
        ) and info:
            lines_below_bottom = info.ui_content.line_count - info.last_visible_line(
            )

            # Make sure to preload at least 2x the amount of lines on a page.
            if lines_below_bottom < info.window_height * 2 or self.forward_forever:
                # Lines to be loaded.
                lines = [info.window_height * 2 - lines_below_bottom
                         ]  # nonlocal

                fd = source.get_fd()

                def handle_content(tokens):
                    """ Handle tokens, update `line_tokens`, decrease
                    line count and return list of characters. """
                    data = []
                    for token_char in tokens:
                        char = token_char[1]
                        if char == '\n':
                            self.line_tokens.append([])

                            # Decrease line count.
                            lines[0] -= 1
                        else:
                            self.line_tokens[-1].append(token_char)
                        data.append(char)
                    return data

                def insert_text(list_of_fragments):
                    document = Document(b.text + ''.join(list_of_fragments),
                                        b.cursor_position)
                    b.set_document(document, bypass_readonly=True)

                    if self.forward_forever:
                        b.cursor_position = len(b.text)

                def receive_content_from_fd():
                    # Read data from the source.
                    tokens = source.read_chunk()
                    data = handle_content(tokens)

                    # Set document.
                    insert_text(data)

                    # Remove the reader when we received another whole page.
                    # or when there is nothing more to read.
                    if lines[0] <= 0 or source.eof():
                        if fd is not None:
                            self.eventloop.remove_reader(fd)
                        source_info.waiting_for_input_stream = False

                    # Redraw.
                    self.cli.invalidate()

                def receive_content_from_generator():
                    " (in executor) Read data from generator. "
                    # Call `read_chunk` as long as we need more lines.
                    while lines[0] > 0 and not source.eof():
                        tokens = source.read_chunk()
                        data = handle_content(tokens)
                        insert_text(data)

                        # Schedule redraw.
                        self.cli.invalidate()

                    source_info.waiting_for_input_stream = False

                # Set 'waiting_for_input_stream' and render.
                source_info.waiting_for_input_stream = True
                self.cli.invalidate()

                # Add reader for stdin.
                if fd is not None:
                    self.eventloop.add_reader(fd, receive_content_from_fd)
                else:
                    # Execute receive_content_from_generator in thread.
                    # (Don't use 'run_in_executor', because we need a daemon.
                    t = threading.Thread(target=receive_content_from_generator)
                    t.daemon = True
                    t.start()

    def run(self):
        """
        Create an event loop for the application and run it.
        """
        self.eventloop = create_eventloop()

        try:
            self.cli = CommandLineInterface(application=self.application,
                                            eventloop=self.eventloop,
                                            input=StdinInput(sys.stdout))

            # Hide message when a key is pressed.
            def key_pressed(_):
                self.message = None

            self.cli.input_processor.beforeKeyPress += key_pressed

            def pre_run():
                # Set search highlighting.
                if self.search_text:
                    self.cli.search_state.text = self.search_text

            with self.cli.patch_stdout_context():
                self.cli.run(reset_current_buffer=False, pre_run=pre_run)
        finally:
            # Close eventloop.
            self.eventloop.close()
            self.eventloop = None