Example #1
0
class PythonCommandLineInterface(object):
    def __init__(
            self,
            get_globals=None,
            get_locals=None,
            stdin=None,
            stdout=None,
            vi_mode=False,
            history_filename=None,
            style=PythonStyle,

            # For internal use.
            _completer=None,
            _validator=None,
            _python_prompt_control=None,
            _extra_buffers=None,
            _extra_sidebars=None):

        self.settings = PythonCLISettings()

        self.get_globals = get_globals or (lambda: {})
        self.get_locals = get_locals or self.get_globals

        self.completer = _completer or PythonCompleter(self.get_globals,
                                                       self.get_locals)
        self.validator = _validator or PythonValidator()
        self.history = FileHistory(
            history_filename) if history_filename else History()
        self.python_prompt_control = _python_prompt_control or PythonPrompt(
            self.settings)
        self._extra_sidebars = _extra_sidebars or []

        # Use a KeyBindingManager for loading the key bindings.
        self.key_bindings_manager = KeyBindingManager(
            enable_vi_mode=vi_mode, enable_system_prompt=True)
        load_python_bindings(
            self.key_bindings_manager,
            self.settings,
            add_buffer=self.add_new_python_buffer,
            close_current_buffer=self.close_current_python_buffer)

        self.get_signatures_thread_running = False

        buffers = {
            'default':
            Buffer(focussable=AlwaysOff()
                   ),  # Never use or focus the default buffer.
            'docstring':
            Buffer(focussable=HasSignature(self.settings)
                   & ShowDocstring(self.settings)),
            # XXX: also make docstring read only.
        }
        buffers.update(_extra_buffers or {})

        self.cli = CommandLineInterface(
            style=style,
            key_bindings_registry=self.key_bindings_manager.registry,
            buffers=buffers,
            create_async_autocompleters=True)

        def on_input_timeout():
            """
            When there is no input activity,
            in another thread, get the signature of the current code.
            """
            if not self.cli.focus_stack.current.startswith('python-'):
                return

            # Never run multiple get-signature threads.
            if self.get_signatures_thread_running:
                return
            self.get_signatures_thread_running = True

            buffer = self.cli.current_buffer
            document = buffer.document

            def run():
                script = get_jedi_script_from_document(document,
                                                       self.get_locals(),
                                                       self.get_globals())

                # Show signatures in help text.
                if script:
                    try:
                        signatures = script.call_signatures()
                    except ValueError:
                        # e.g. in case of an invalid \\x escape.
                        signatures = []
                    except Exception:
                        # Sometimes we still get an exception (TypeError), because
                        # of probably bugs in jedi. We can silence them.
                        # See: https://github.com/davidhalter/jedi/issues/492
                        signatures = []
                else:
                    signatures = []

                self.get_signatures_thread_running = False

                # Set signatures and redraw if the text didn't change in the
                # meantime. Otherwise request new signatures.
                if buffer.text == document.text:
                    buffer.signatures = signatures

                    # Set docstring in docstring buffer.
                    if signatures:
                        string = signatures[0].docstring()
                        if not isinstance(string, six.text_type):
                            string = string.decode('utf-8')
                        self.cli.buffers['docstring'].reset(
                            initial_document=Document(string,
                                                      cursor_position=0))
                    else:
                        self.cli.buffers['docstring'].reset()

                    self.cli.request_redraw()
                else:
                    on_input_timeout()

            self.cli.run_in_executor(run)

        self.cli.onInputTimeout += on_input_timeout
        self.cli.onReset += self.key_bindings_manager.reset

        self.add_new_python_buffer()

    def _update_layout(self):
        """
        Generate new layout.
        (To be done when we add/remove buffers.)
        """
        self.cli.layout = create_layout(self.cli.buffers,
                                        self.settings,
                                        self.key_bindings_manager,
                                        self.python_prompt_control,
                                        extra_sidebars=self._extra_sidebars)

    def add_new_python_buffer(self):
        # Create a new buffer.
        buffer = self._create_buffer()
        self.settings.buffer_index += 1
        name = 'python-%i' % self.settings.buffer_index

        # Insert and update layout.
        self.cli.add_buffer(name, buffer, focus=True)
        self._update_layout()

    def close_current_python_buffer(self):
        name, _ = current_python_buffer(self.cli, self.settings)

        if name:
            python_buffers_left = len(
                [b for b in self.cli.buffers if b.startswith('python-')])

            if python_buffers_left > 1:
                focus_next_buffer(
                    self.cli,
                    name_filter=lambda name: name.startswith('python-'))
                del self.cli.buffers[name]
                self._update_layout()
            else:
                self.cli.set_exit()

    def _create_buffer(self):
        def is_buffer_multiline(document):
            return (self.settings.paste_mode
                    or self.settings.currently_multiline
                    or document_is_multiline_python(document))

        return PythonBuffer(is_multiline=is_buffer_multiline,
                            tempfile_suffix='.py',
                            history=self.history,
                            completer=self.completer,
                            validator=self.validator)
Example #2
0
class PythonCommandLineInterface(object):
    def __init__(self,
                 get_globals=None, get_locals=None,
                 stdin=None, stdout=None,
                 vi_mode=False, history_filename=None,
                 style=PythonStyle,

                 # For internal use.
                 _completer=None,
                 _validator=None,
                 _lexer=None,
                 _python_prompt_control=None,
                 _extra_buffers=None,
                 _extra_buffer_processors=None,
                 _extra_sidebars=None):

        self.settings = PythonCLISettings()

        self.get_globals = get_globals or (lambda: {})
        self.get_locals = get_locals or self.get_globals

        self.completer = _completer or PythonCompleter(self.get_globals, self.get_locals)
        self.validator = _validator or PythonValidator()
        self.history = FileHistory(history_filename) if history_filename else History()
        self.python_prompt_control = _python_prompt_control or PythonPrompt(self.settings)
        self._extra_sidebars = _extra_sidebars or []
        self._extra_buffer_processors = _extra_buffer_processors or []
        self._lexer = _lexer or PythonLexer

        # Use a KeyBindingManager for loading the key bindings.
        self.key_bindings_manager = KeyBindingManager(enable_vi_mode=vi_mode,
                                                      enable_open_in_editor=True,
                                                      enable_system_prompt=True)
        load_python_bindings(self.key_bindings_manager, self.settings,
                             add_buffer=self.add_new_python_buffer,
                             close_current_buffer=self.close_current_python_buffer)

        self.get_signatures_thread_running = False

        buffers = {
            'default': Buffer(focussable=Never()),  # Never use or focus the default buffer.
            'docstring': Buffer(focussable=HasSignature(self.settings) & ShowDocstring(self.settings)),
                                # XXX: also make docstring read only.
        }
        buffers.update(_extra_buffers or {})

        self.cli = CommandLineInterface(
            style=style,
            key_bindings_registry=self.key_bindings_manager.registry,
            buffers=buffers,
            complete_while_typing=Always(),
            on_abort=AbortAction.RETRY,
            on_exit=AbortAction.RAISE_EXCEPTION)

        def on_input_timeout():
            """
            When there is no input activity,
            in another thread, get the signature of the current code.
            """
            if not self.cli.focus_stack.current.startswith('python-'):
                return

            # Never run multiple get-signature threads.
            if self.get_signatures_thread_running:
                return
            self.get_signatures_thread_running = True

            buffer = self.cli.current_buffer
            document = buffer.document

            def run():
                script = get_jedi_script_from_document(document, self.get_locals(), self.get_globals())

                # Show signatures in help text.
                if script:
                    try:
                        signatures = script.call_signatures()
                    except ValueError:
                        # e.g. in case of an invalid \\x escape.
                        signatures = []
                    except Exception:
                        # Sometimes we still get an exception (TypeError), because
                        # of probably bugs in jedi. We can silence them.
                        # See: https://github.com/davidhalter/jedi/issues/492
                        signatures = []
                else:
                    signatures = []

                self.get_signatures_thread_running = False

                # Set signatures and redraw if the text didn't change in the
                # meantime. Otherwise request new signatures.
                if buffer.text == document.text:
                    buffer.signatures = signatures

                    # Set docstring in docstring buffer.
                    if signatures:
                        string = signatures[0].docstring()
                        if not isinstance(string, six.text_type):
                            string = string.decode('utf-8')
                        self.cli.buffers['docstring'].reset(
                            initial_document=Document(string, cursor_position=0))
                    else:
                        self.cli.buffers['docstring'].reset()

                    self.cli.request_redraw()
                else:
                    on_input_timeout()

            self.cli.eventloop.run_in_executor(run)

        self.cli.onInputTimeout += on_input_timeout
        self.cli.onReset += self.key_bindings_manager.reset

        self.add_new_python_buffer()

    def _update_layout(self):
        """
        Generate new layout.
        (To be done when we add/remove buffers.)
        """
        self.cli.layout = create_layout(
            self.cli.buffers, self.settings, self.key_bindings_manager, self.python_prompt_control,
            lexer=self._lexer,
            extra_buffer_processors=self._extra_buffer_processors,
            extra_sidebars=self._extra_sidebars)

    def add_new_python_buffer(self):
        # Create a new buffer.
        buffer = self._create_buffer()
        self.settings.buffer_index += 1
        name = 'python-%i' % self.settings.buffer_index

        # Insert and update layout.
        self.cli.add_buffer(name, buffer, focus=True)
        self._update_layout()

    def close_current_python_buffer(self):
        name, _ = current_python_buffer(self.cli, self.settings)

        if name:
            python_buffers_left = len([b for b in self.cli.buffers if b.startswith('python-')])

            if python_buffers_left > 1:
                focus_next_buffer(self.cli, name_filter=lambda name: name.startswith('python-'))
                del self.cli.buffers[name]
                self._update_layout()
            else:
                self.cli.set_exit()

    def _create_buffer(self):
        def is_buffer_multiline(document):
            return (self.settings.paste_mode or
                    self.settings.currently_multiline or
                    document_is_multiline_python(document))

        return PythonBuffer(
            is_multiline=is_buffer_multiline,
            tempfile_suffix='.py',
            history=self.history,
            completer=self.completer,
            validator=self.validator)