<commit_before>"""
The main `CommandLineInterface` class and logic.
"""
from __future__ import unicode_literals

import os
import signal
import six
import sys
import weakref

from .buffer import Buffer, AcceptAction
from .clipboard import Clipboard
from .completion import CompleteEvent
from .completion import get_common_complete_suffix
from .eventloop.base import EventLoop
from .eventloop.callbacks import EventLoopCallbacks
from .filters import Filter, Always, Never
from .focus_stack import FocusStack
from .history import History
from .key_binding.bindings.emacs import load_emacs_bindings
from .key_binding.input_processor import InputProcessor
from .key_binding.registry import Registry
from .layout import Window
from .layout.controls import BufferControl
from .renderer import Renderer, Output
from .search_state import SearchState
from .styles import DefaultStyle
from .utils import EventHook
from .enums import DEFAULT_BUFFER, SEARCH_BUFFER, SYSTEM_BUFFER

from types import GeneratorType

if sys.platform == 'win32':
    from .terminal.win32_input import raw_mode, cooked_mode
    from .terminal.win32_output import Win32Output
else:
    from .terminal.vt100_input import raw_mode, cooked_mode
    from .terminal.vt100_output import Vt100_Output


__all__ = (
    'AbortAction',
    'CommandLineInterface',
)


class AbortAction:
    """
    Actions to take on an Exit or Abort exception.
    """
    IGNORE = 'ignore'
    RETRY = 'retry'
    RAISE_EXCEPTION = 'raise-exception'


class CommandLineInterface(object):
    """
    Wrapper around all the other classes, tying everything together.

    Typical usage::

        cli = CommandLineInterface(eventloop)
        while True:
            result = cli.read_input()
            print(result)

    :param eventloop: The `EventLoop` to be used when `read_input` is called.
                      (Further, this allows callbacks to know where to find the
                      `run_in_executor`.) It can be `None` as well, when no
                      eventloop is used/exposed.
    :param stdin: Input stream, by default sys.stdin
    :param stdout: Output stream, by default sys.stdout
    :param layout: :class:`Layout` instance.
    :param style: :class:`Layout` instance.
    :param on_abort: :class:`AbortAction` value. What to do when Ctrl-C has
                     been pressed.
    :param complete_while_typing: Filter instance. Decide whether or not to do
                                  asynchronous autocompleting while typing.
    :attr paste_mode: Filter to indicate that we are in "paste mode". When
                      enabled, inserting newlines will never insert a margin.
    """
    def __init__(self, eventloop, stdin=None, stdout=None,
                 layout=None,
                 buffer=None,
                 buffers=None,
                 style=None,
                 key_bindings_registry=None,
                 clipboard=None,
                 renderer=None,
                 output=None,
                 initial_focussed_buffer=DEFAULT_BUFFER,
                 on_abort=AbortAction.RETRY, on_exit=AbortAction.IGNORE,
                 complete_while_typing=Always(),
                 paste_mode=Never(),
                 use_alternate_screen=False):

        assert buffer is None or isinstance(buffer, Buffer)
        assert buffers is None or isinstance(buffers, dict)
        assert key_bindings_registry is None or isinstance(key_bindings_registry, Registry)
        assert output is None or isinstance(output, Output)
        assert isinstance(complete_while_typing, Filter)
        assert isinstance(paste_mode, Filter)
        assert eventloop is None or isinstance(eventloop, EventLoop)

        assert renderer is None or output is None  # Never expect both.

        self.stdin = stdin or sys.__stdin__
        self.stdout = stdout or sys.__stdout__
        self.style = style or DefaultStyle
        self.complete_while_typing = complete_while_typing
        self._paste_mode = paste_mode

        self.on_abort = on_abort
        self.on_exit = on_exit

        # Remember eventloop.
        self.eventloop = eventloop

        # Events

        #: Called when there is no input for x seconds.
        #: (Fired when any eventloop.onInputTimeout is fired.)
        self.onInputTimeout = EventHook()

        self.onReadInputStart = EventHook()
        self.onReadInputEnd = EventHook()
        self.onReset = EventHook()
        self.onBufferChanged = EventHook()  # Called when any buffer changes.

        # Focus stack.
        self.focus_stack = FocusStack(initial=initial_focussed_buffer)

        #: The input buffers.
        self.buffers = {
            # For the 'search' and 'system' buffers, 'returnable' is False, in
            # order to block normal Enter/ControlC behaviour.
            DEFAULT_BUFFER: (buffer or Buffer(accept_action=AcceptAction.RETURN_DOCUMENT)),
            SEARCH_BUFFER: Buffer(history=History(), accept_action=AcceptAction.IGNORE),
            SYSTEM_BUFFER: Buffer(history=History(), accept_action=AcceptAction.IGNORE),
        }
        if buffers:
            self.buffers.update(buffers)

        # Search state. (Does also remember what has to be highlighted.)
        self.search_state = SearchState()

        #: The `Layout` instance.
        self.layout = layout or Window(BufferControl())

        #: The clipboard instance
        self.clipboard = clipboard or Clipboard()

        #: The `Renderer` instance.
        # Make sure that the same stdout is used, when a custom renderer has been passed.
        def default_output():
            if sys.platform == 'win32':
                return Win32Output(self.stdout)
            else:
                return Vt100_Output.from_pty(self.stdout)

        self.renderer = renderer or Renderer(output or default_output(),
                                             use_alternate_screen=use_alternate_screen)

        if key_bindings_registry is None:
            key_bindings_registry = Registry()
            load_emacs_bindings(key_bindings_registry)

        #: The `InputProcessor` instance.
        self.input_processor = InputProcessor(key_bindings_registry, weakref.ref(self))

        self._async_completers = {}  # Map buffer name to completer function.

        # Call `add_buffer` for each buffer.
        for name, b in self.buffers.items():
            self.add_buffer(name, b)

        self.reset()

    def add_buffer(self, name, buffer, focus=False):
        """
        Insert a new buffer.
        """
        assert isinstance(buffer, Buffer)
        self.buffers[name] = buffer

        def create_while_typing_completer(completer):
            """
            Wrapper around the asynchronous completer, that ensures that it's
            only called while typing if the `complete_while_typing` filter is
            enabled.
            """
            def complete_while_typing():
                # Only complete when "complete_while_typing" is enabled.
                if self.complete_while_typing(self):
                    completer()
            return complete_while_typing

        # Create asynchronous completer.
        if buffer.completer:
            completer_function = self._create_async_completer(buffer)
            self._async_completers[name] = completer_function
            buffer.onTextInsert += create_while_typing_completer(completer_function)

        # Throw onTextChanged when text in this buffer changes.
        buffer.onTextChanged += lambda: self.onBufferChanged.fire()

        if focus:
            self.focus_stack.replace(name)

    def start_completion(self, buffer_name=None, select_first=False,
                         select_last=False, insert_common_part=False):
        """
        Start asynchronous autocompletion of this buffer.
        (This will do nothing if a previous completion was still in progress.)
        """
        buffer_name = buffer_name or self.current_buffer_name
        completer = self._async_completers.get(buffer_name)

        if completer:
            completer(select_first=select_first,
                      select_last=select_last,
                      insert_common_part=insert_common_part)

    @property
    def current_buffer_name(self):
        """
        The name of the current  :class:`Buffer`.
        """
        return self.focus_stack.current

    @property
    def current_buffer(self):
        """
        The current focussed :class:`Buffer`.
        """
        return self.buffers[self.focus_stack.current]

    @property
    def is_searching(self):
        """
        True when we are searching.
        """
        return self.current_buffer_name == SEARCH_BUFFER

    @property
    def search_text(self):
        """
        The text we are searching for.
        """
        # When the search buffer has focus, take that text.
        if self.is_searching and self.current_buffer.text:
            return self.current_buffer.text
        # Otherwise, take the text of the last active search.
        else:
            return self.search_state.text

    def reset(self, reset_current_buffer=False):
        """
        Reset everything, for reading the next input.

        :param reset_current_buffer: If True, also reset the focussed buffer.
        """
        # Notice that we don't reset the buffers. (This happens just before
        # returning, and when we have multiple buffers, we clearly want the
        # content in the other buffers to remain unchanged between several
        # calls of `read_input`. (And the same is true for the `focus_stack`.)

        self._exit_flag = False
        self._abort_flag = False

        self._return_value = None

        self.renderer.reset()
        self.input_processor.reset()
        self.layout.reset()

        if reset_current_buffer:
            self.current_buffer.reset()

        # Trigger reset event.
        self.onReset.fire()

    @property<commit_msg>Bugfix: search_state should be reset for every new read.<commit_after>"""
The main `CommandLineInterface` class and logic.
"""
from __future__ import unicode_literals

import os
import signal
import six
import sys
import weakref

from .buffer import Buffer, AcceptAction
from .clipboard import Clipboard
from .completion import CompleteEvent
from .completion import get_common_complete_suffix
from .eventloop.base import EventLoop
from .eventloop.callbacks import EventLoopCallbacks
from .filters import Filter, Always, Never
from .focus_stack import FocusStack
from .history import History
from .key_binding.bindings.emacs import load_emacs_bindings
from .key_binding.input_processor import InputProcessor
from .key_binding.registry import Registry
from .layout import Window
from .layout.controls import BufferControl
from .renderer import Renderer, Output
from .search_state import SearchState
from .styles import DefaultStyle
from .utils import EventHook
from .enums import DEFAULT_BUFFER, SEARCH_BUFFER, SYSTEM_BUFFER

from types import GeneratorType

if sys.platform == 'win32':
    from .terminal.win32_input import raw_mode, cooked_mode
    from .terminal.win32_output import Win32Output
else:
    from .terminal.vt100_input import raw_mode, cooked_mode
    from .terminal.vt100_output import Vt100_Output


__all__ = (
    'AbortAction',
    'CommandLineInterface',
)


class AbortAction:
    """
    Actions to take on an Exit or Abort exception.
    """
    IGNORE = 'ignore'
    RETRY = 'retry'
    RAISE_EXCEPTION = 'raise-exception'


class CommandLineInterface(object):
    """
    Wrapper around all the other classes, tying everything together.

    Typical usage::

        cli = CommandLineInterface(eventloop)
        while True:
            result = cli.read_input()
            print(result)

    :param eventloop: The `EventLoop` to be used when `read_input` is called.
                      (Further, this allows callbacks to know where to find the
                      `run_in_executor`.) It can be `None` as well, when no
                      eventloop is used/exposed.
    :param stdin: Input stream, by default sys.stdin
    :param stdout: Output stream, by default sys.stdout
    :param layout: :class:`Layout` instance.
    :param style: :class:`Layout` instance.
    :param on_abort: :class:`AbortAction` value. What to do when Ctrl-C has
                     been pressed.
    :param complete_while_typing: Filter instance. Decide whether or not to do
                                  asynchronous autocompleting while typing.
    :attr paste_mode: Filter to indicate that we are in "paste mode". When
                      enabled, inserting newlines will never insert a margin.
    """
    def __init__(self, eventloop, stdin=None, stdout=None,
                 layout=None,
                 buffer=None,
                 buffers=None,
                 style=None,
                 key_bindings_registry=None,
                 clipboard=None,
                 renderer=None,
                 output=None,
                 initial_focussed_buffer=DEFAULT_BUFFER,
                 on_abort=AbortAction.RETRY, on_exit=AbortAction.IGNORE,
                 complete_while_typing=Always(),
                 paste_mode=Never(),
                 use_alternate_screen=False):

        assert buffer is None or isinstance(buffer, Buffer)
        assert buffers is None or isinstance(buffers, dict)
        assert key_bindings_registry is None or isinstance(key_bindings_registry, Registry)
        assert output is None or isinstance(output, Output)
        assert isinstance(complete_while_typing, Filter)
        assert isinstance(paste_mode, Filter)
        assert eventloop is None or isinstance(eventloop, EventLoop)

        assert renderer is None or output is None  # Never expect both.

        self.stdin = stdin or sys.__stdin__
        self.stdout = stdout or sys.__stdout__
        self.style = style or DefaultStyle
        self.complete_while_typing = complete_while_typing
        self._paste_mode = paste_mode

        self.on_abort = on_abort
        self.on_exit = on_exit

        # Remember eventloop.
        self.eventloop = eventloop

        # Events

        #: Called when there is no input for x seconds.
        #: (Fired when any eventloop.onInputTimeout is fired.)
        self.onInputTimeout = EventHook()

        self.onReadInputStart = EventHook()
        self.onReadInputEnd = EventHook()
        self.onReset = EventHook()
        self.onBufferChanged = EventHook()  # Called when any buffer changes.

        # Focus stack.
        self.focus_stack = FocusStack(initial=initial_focussed_buffer)

        #: The input buffers.
        self.buffers = {
            # For the 'search' and 'system' buffers, 'returnable' is False, in
            # order to block normal Enter/ControlC behaviour.
            DEFAULT_BUFFER: (buffer or Buffer(accept_action=AcceptAction.RETURN_DOCUMENT)),
            SEARCH_BUFFER: Buffer(history=History(), accept_action=AcceptAction.IGNORE),
            SYSTEM_BUFFER: Buffer(history=History(), accept_action=AcceptAction.IGNORE),
        }
        if buffers:
            self.buffers.update(buffers)

        #: The `Layout` instance.
        self.layout = layout or Window(BufferControl())

        #: The clipboard instance
        self.clipboard = clipboard or Clipboard()

        #: The `Renderer` instance.
        # Make sure that the same stdout is used, when a custom renderer has been passed.
        def default_output():
            if sys.platform == 'win32':
                return Win32Output(self.stdout)
            else:
                return Vt100_Output.from_pty(self.stdout)

        self.renderer = renderer or Renderer(output or default_output(),
                                             use_alternate_screen=use_alternate_screen)

        if key_bindings_registry is None:
            key_bindings_registry = Registry()
            load_emacs_bindings(key_bindings_registry)

        #: The `InputProcessor` instance.
        self.input_processor = InputProcessor(key_bindings_registry, weakref.ref(self))

        self._async_completers = {}  # Map buffer name to completer function.

        # Call `add_buffer` for each buffer.
        for name, b in self.buffers.items():
            self.add_buffer(name, b)

        self.reset()

    def add_buffer(self, name, buffer, focus=False):
        """
        Insert a new buffer.
        """
        assert isinstance(buffer, Buffer)
        self.buffers[name] = buffer

        def create_while_typing_completer(completer):
            """
            Wrapper around the asynchronous completer, that ensures that it's
            only called while typing if the `complete_while_typing` filter is
            enabled.
            """
            def complete_while_typing():
                # Only complete when "complete_while_typing" is enabled.
                if self.complete_while_typing(self):
                    completer()
            return complete_while_typing

        # Create asynchronous completer.
        if buffer.completer:
            completer_function = self._create_async_completer(buffer)
            self._async_completers[name] = completer_function
            buffer.onTextInsert += create_while_typing_completer(completer_function)

        # Throw onTextChanged when text in this buffer changes.
        buffer.onTextChanged += lambda: self.onBufferChanged.fire()

        if focus:
            self.focus_stack.replace(name)

    def start_completion(self, buffer_name=None, select_first=False,
                         select_last=False, insert_common_part=False):
        """
        Start asynchronous autocompletion of this buffer.
        (This will do nothing if a previous completion was still in progress.)
        """
        buffer_name = buffer_name or self.current_buffer_name
        completer = self._async_completers.get(buffer_name)

        if completer:
            completer(select_first=select_first,
                      select_last=select_last,
                      insert_common_part=insert_common_part)

    @property
    def current_buffer_name(self):
        """
        The name of the current  :class:`Buffer`.
        """
        return self.focus_stack.current

    @property
    def current_buffer(self):
        """
        The current focussed :class:`Buffer`.
        """
        return self.buffers[self.focus_stack.current]

    @property
    def is_searching(self):
        """
        True when we are searching.
        """
        return self.current_buffer_name == SEARCH_BUFFER

    @property
    def search_text(self):
        """
        The text we are searching for.
        """
        # When the search buffer has focus, take that text.
        if self.is_searching and self.current_buffer.text:
            return self.current_buffer.text
        # Otherwise, take the text of the last active search.
        else:
            return self.search_state.text

    def reset(self, reset_current_buffer=False):
        """
        Reset everything, for reading the next input.

        :param reset_current_buffer: If True, also reset the focussed buffer.
        """
        # Notice that we don't reset the buffers. (This happens just before
        # returning, and when we have multiple buffers, we clearly want the
        # content in the other buffers to remain unchanged between several
        # calls of `read_input`. (And the same is true for the `focus_stack`.)

        self._exit_flag = False
        self._abort_flag = False

        self._return_value = None

        self.renderer.reset()
        self.input_processor.reset()
        self.layout.reset()

        if reset_current_buffer:
            self.current_buffer.reset()

        # Search new search state. (Does also remember what has to be
        # highlighted.)
        self.search_state = SearchState()

        # Trigger reset event.
        self.onReset.fire()

    @property
{'fraction_non_alphanumeric': 0.07079158316633266, 'fraction_numerical': 0.002404809619238477, 'mean_word_length': 2.3374017722788833, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4710209', 'n_tokens_mistral': 5395, 'n_tokens_neox': 5121, 'n_words': 1571}