Пример #1
0
 def _create_application(self):
     """
     Create an `Application` instance.
     """
     return Application(
         input=self.input,
         output=self.output,
         layout=self.ptpython_layout.layout,
         key_bindings=merge_key_bindings([
             load_python_bindings(self),
             load_auto_suggest_bindings(),
             load_sidebar_bindings(self),
             load_confirm_exit_bindings(self),
             ConditionalKeyBindings(
                 load_open_in_editor_bindings(),
                 Condition(lambda: self.enable_open_in_editor)),
             # Extra key bindings should not be active when the sidebar is visible.
             ConditionalKeyBindings(
                 self.extra_key_bindings,
                 Condition(lambda: not self.show_sidebar))
         ]),
         color_depth=lambda: self.color_depth,
         paste_mode=Condition(lambda: self.paste_mode),
         mouse_support=Condition(lambda: self.enable_mouse_support),
         style=DynamicStyle(lambda: self._current_style),
         style_transformation=self.style_transformation,
         include_default_pygments_style=False,
         reverse_vi_search_direction=True)
Пример #2
0
 def _create_application(self):
     """
     Create an `Application` instance.
     """
     return Application(
         input=self.input,
         output=self.output,
         layout=self.ptpython_layout.layout,
         key_bindings=merge_key_bindings([
             load_python_bindings(self),
             load_auto_suggest_bindings(),
             load_sidebar_bindings(self),
             load_confirm_exit_bindings(self),
             # Extra key bindings should not be active when the sidebar is visible.
             ConditionalKeyBindings(
                 self.extra_key_bindings,
                 Condition(lambda: not self.show_sidebar))
         ]),
         color_depth=lambda: self.color_depth,
         paste_mode=Condition(lambda: self.paste_mode),
         mouse_support=Condition(lambda: self.enable_mouse_support),
         style=DynamicStyle(lambda: self._current_style),
         style_transformation=self.style_transformation,
         include_default_pygments_style=False,
         reverse_vi_search_direction=True)
Пример #3
0
    def _create_application(self, editing_mode, erase_when_done):
        """
        Create the `Application` object.
        """
        dyncond = self._dyncond

        # Default key bindings.
        auto_suggest_bindings = load_auto_suggest_bindings()
        open_in_editor_bindings = load_open_in_editor_bindings()
        prompt_bindings = self._create_prompt_bindings()

        # Create application
        application = Application(
            layout=self.layout,
            style=DynamicStyle(lambda: self.style),
            include_default_pygments_style=dyncond(
                'include_default_pygments_style'),
            clipboard=DynamicClipboard(lambda: self.clipboard),
            key_bindings=merge_key_bindings([
                merge_key_bindings([
                    auto_suggest_bindings,
                    ConditionalKeyBindings(
                        open_in_editor_bindings,
                        dyncond('enable_open_in_editor')
                        & has_focus(DEFAULT_BUFFER)), prompt_bindings
                ]),
                DynamicKeyBindings(lambda: self.key_bindings),
            ]),
            mouse_support=dyncond('mouse_support'),
            editing_mode=editing_mode,
            erase_when_done=erase_when_done,
            reverse_vi_search_direction=True,
            color_depth=lambda: self.color_depth,

            # I/O.
            input=self.input,
            output=self.output)

        # During render time, make sure that we focus the right search control
        # (if we are searching). - This could be useful if people make the
        # 'multiline' property dynamic.
        '''
        def on_render(app):
            multiline = _true(self.multiline)
            current_control = app.layout.current_control

            if multiline:
                if current_control == search_buffer_control:
                    app.layout.current_control = search_toolbar.control
                    app.invalidate()
            else:
                if current_control == search_toolbar.control:
                    app.layout.current_control = search_buffer_control
                    app.invalidate()

        app.on_render += on_render
        '''

        return application
Пример #4
0
    def _create_application(self) -> Application:
        self.sql_layout = sqlAppLayout(my_app=self)
        kb = KeyBindings()

        confirmation_visible = Condition(lambda: self.show_exit_confirmation)

        @kb.add("c-q")
        def _(event):
            " Pressing Ctrl-Q or Ctrl-C will exit the user interface. "
            self.show_exit_confirmation = True

        @kb.add("y", filter=confirmation_visible)
        @kb.add("Y", filter=confirmation_visible)
        @kb.add("enter", filter=confirmation_visible)
        @kb.add("c-q", filter=confirmation_visible)
        def _(event):
            """
            Really quit.
            """
            event.app.exit(exception=ExitEX(), style="class:exiting")

        @kb.add(Keys.Any, filter=confirmation_visible)
        def _(event):
            """
            Cancel exit.
            """
            self.show_exit_confirmation = False

        # Global key bindings.
        @kb.add("tab",
                filter=Condition(
                    lambda: self.show_preview or self.show_login_prompt))
        def _(event):
            event.app.layout.focus_next()

        @kb.add("f4")
        def _(event):
            " Toggle between Emacs and Vi mode. "
            self.vi_mode = not self.vi_mode

        # apparently ctrls does this
        @kb.add("c-t", filter=Condition(lambda: not self.show_preview))
        def _(event):
            """
            Show/hide sidebar.
            """
            self.show_sidebar = not self.show_sidebar
            if self.show_sidebar:
                event.app.layout.focus("sidebarbuffer")
            else:
                event.app.layout.focus_previous()

        sidebar_visible = Condition(lambda: self.show_sidebar and not self.show_expanding_object and not self.show_login_prompt and not self.show_preview) \
                        & ~has_focus("sidebarsearchbuffer")

        @kb.add("up", filter=sidebar_visible)
        @kb.add("c-p", filter=sidebar_visible)
        @kb.add("k", filter=sidebar_visible)
        def _(event):
            " Go to previous option. "
            obj = self._selected_object
            self.select_previous()
            inc = len(self.selected_object.name) + 1  # newline character
            if obj is self.obj_list[0]:
                idx = 0
                cursor = 0
                while obj is not self._selected_object:
                    if not obj.next_object:
                        raise IndexError
                    cursor += len(obj.name) + 1
                    idx += 1
                    obj = obj.next_object
                self._selected_obj_idx = [idx, cursor]
            else:
                self._selected_obj_idx[0] -= 1
                self._selected_obj_idx[1] -= inc

        @kb.add("down", filter=sidebar_visible)
        @kb.add("c-n", filter=sidebar_visible)
        @kb.add("j", filter=sidebar_visible)
        def _(event):
            " Go to next option. "
            inc = len(self.selected_object.name) + 1  # newline character
            self.select_next()
            if self.selected_object is self.obj_list[0]:
                self._selected_obj_idx = [0, 0]
            else:
                self._selected_obj_idx[0] += 1
                self._selected_obj_idx[1] += inc

        @kb.add("enter", filter=sidebar_visible)
        def _(event):
            " If connection, connect.  If table preview"
            obj = self.selected_object
            if type(obj).__name__ == "myDBConn" and not obj.conn.connected():
                self.show_login_prompt = True
                event.app.layout.focus(self.sql_layout.lprompt)
            if type(obj).__name__ == "myDBConn" and obj.conn.connected():
                # OG: some thread locking may be needed here
                self._active_conn = obj.conn
            elif obj.otype in ["table", "view", "function"]:
                self.show_preview = True
                self.show_sidebar = False
                event.app.layout.focus(self.sql_layout.preview)

        @kb.add("right", filter=sidebar_visible)
        @kb.add("l", filter=sidebar_visible)
        @kb.add(" ", filter=sidebar_visible)
        def _(event):
            " Select next value for current option. "
            obj = self.selected_object
            obj.expand()
            if type(obj).__name__ == "myDBConn" and not obj.conn.connected():
                self.show_login_prompt = True
                event.app.layout.focus(self.sql_layout.lprompt)

        @kb.add("left", filter=sidebar_visible)
        @kb.add("h", filter=sidebar_visible)
        def _(event):
            " Select next value for current option. "
            obj = self.selected_object
            if type(obj).__name__ == "myDBConn" and obj.conn.connected(
            ) and obj.children is None:
                self.show_disconnect_dialog = True
                event.app.layout.focus(self.sql_layout.disconnect_dialog)
            else:
                obj.collapse()

        auto_suggest_bindings = load_auto_suggest_bindings()

        return Application(
            layout=self.sql_layout.layout,
            key_bindings=merge_key_bindings([kb, auto_suggest_bindings]),
            enable_page_navigation_bindings=True,
            style=style_factory(self.syntax_style, self.cli_style),
            include_default_pygments_style=False,
            mouse_support=self.mouse_support,
            full_screen=False,
            editing_mode=EditingMode.VI
            if self.config["main"].as_bool("vi") else EditingMode.EMACS)
Пример #5
0
            click.secho(f'{e}', fg='red')
            raise click.UsageError(e)


def prompt_message_callback(repo):
    def ppc():
        if repo.active_project:
            return f'tsar:{repo.active_project}:{repo.active_task}:{repo.active_roi}>'
        return u'tsar>'

    return ppc


text_type = str

kb = load_auto_suggest_bindings()
try:
    _b = kb.get_bindings_for_keys(('escape', 'f'))[0]
    kb.add('c-right')(_b)
    _e = kb.get_bindings_for_keys(('right',))[0]
    kb.add('end')(_e)
except IndexError:
    warning('Internal: could not redeclare key bindings')
except StopIteration:
    pass

prompt_kwargs = {
    'complete_style': CompleteStyle.READLINE_LIKE,
    'complete_while_typing': False,
    'key_bindings': kb,
    'history': FileHistory(os.path.join(os.getenv('HOME', '~'), '.ocli-history')),
Пример #6
0
    def _create_application(self, editing_mode, erase_when_done):
        def dyncond(attr_name):
            """
            Dynamically take this setting from this 'Prompt' class.
            `attr_name` represents an attribute name of this class. Its value
            can either be a boolean or a `Filter`.

            This returns something that can be used as either a `Filter`
            or `Filter`.
            """
            @Condition
            def dynamic():
                value = getattr(self, attr_name)
                return to_filter(value)()

            return dynamic

        # Create functions that will dynamically split the prompt. (If we have
        # a multiline prompt.)
        has_before_fragments, get_prompt_text_1, get_prompt_text_2 = \
            _split_multiline_prompt(self._get_prompt)

        # Create buffers list.
        def accept(buff):
            """ Accept the content of the default buffer. This is called when
            the validation succeeds. """
            self.app.set_result(buff.document.text)

            # Reset content before running again.
            self.app.pre_run_callables.append(buff.reset)

        default_buffer = Buffer(
            name=DEFAULT_BUFFER,
            # Make sure that complete_while_typing is disabled when
            # enable_history_search is enabled. (First convert to Filter,
            # to avoid doing bitwise operations on bool objects.)
            complete_while_typing=Condition(
                lambda: _true(self.complete_while_typing) and not _true(
                    self.enable_history_search) and not self.complete_style ==
                CompleteStyle.READLINE_LIKE),
            validate_while_typing=dyncond('validate_while_typing'),
            enable_history_search=dyncond('enable_history_search'),
            validator=DynamicValidator(lambda: self.validator),
            completer=ThreadedCompleter(
                completer=DynamicCompleter(lambda: self.completer),
                in_thread=dyncond('complete_in_thread'),
            ),
            history=DynamicHistory(lambda: self.history),
            auto_suggest=DynamicAutoSuggest(lambda: self.auto_suggest),
            accept_handler=accept,
            get_tempfile_suffix=lambda: self.tempfile_suffix)

        search_buffer = Buffer(name=SEARCH_BUFFER)

        # Create processors list.
        input_processor = merge_processors([
            ConditionalProcessor(
                # By default, only highlight search when the search
                # input has the focus. (Note that this doesn't mean
                # there is no search: the Vi 'n' binding for instance
                # still allows to jump to the next match in
                # navigation mode.)
                HighlightSearchProcessor(preview_search=True),
                has_focus(search_buffer)),
            HighlightSelectionProcessor(),
            ConditionalProcessor(AppendAutoSuggestion(),
                                 has_focus(default_buffer) & ~is_done),
            ConditionalProcessor(PasswordProcessor(), dyncond('is_password')),
            DisplayMultipleCursors(),

            # Users can insert processors here.
            DynamicProcessor(lambda: self.extra_input_processor),

            # For single line mode, show the prompt before the input.
            ConditionalProcessor(
                merge_processors([
                    BeforeInput(get_prompt_text_2),
                    ShowArg(),
                ]), ~dyncond('multiline'))
        ])

        # Create bottom toolbars.
        bottom_toolbar = ConditionalContainer(
            Window(FormattedTextControl(lambda: self.bottom_toolbar,
                                        style='class:bottom-toolbar.text'),
                   style='class:bottom-toolbar',
                   dont_extend_height=True,
                   height=Dimension(min=1)),
            filter=~is_done & renderer_height_is_known
            & Condition(lambda: self.bottom_toolbar is not None))

        search_toolbar = SearchToolbar(
            search_buffer,
            get_search_state=lambda: default_buffer_control.get_search_state())
        search_buffer_control = BufferControl(buffer=search_buffer,
                                              input_processor=merge_processors(
                                                  [
                                                      ReverseSearchProcessor(),
                                                      ShowArg(),
                                                  ]))

        system_toolbar = SystemToolbar()

        def get_search_buffer_control():
            " Return the UIControl to be focused when searching start. "
            if _true(self.multiline):
                return search_toolbar.control
            else:
                return search_buffer_control

        default_buffer_control = BufferControl(
            buffer=default_buffer,
            get_search_buffer_control=get_search_buffer_control,
            input_processor=input_processor,
            lexer=DynamicLexer(lambda: self.lexer),
            preview_search=True)

        default_buffer_window = Window(
            default_buffer_control,
            height=self._get_default_buffer_control_height,
            left_margins=[
                # In multiline mode, use the window margin to display
                # the prompt and continuation fragments.
                ConditionalMargin(
                    PromptMargin(get_prompt_text_2, self._get_continuation),
                    filter=dyncond('multiline'),
                )
            ],
            wrap_lines=dyncond('wrap_lines'))

        @Condition
        def multi_column_complete_style():
            return self.complete_style == CompleteStyle.MULTI_COLUMN

        # Build the layout.
        layout = HSplit([
            # The main input, with completion menus floating on top of it.
            FloatContainer(
                HSplit([
                    ConditionalContainer(
                        Window(FormattedTextControl(get_prompt_text_1),
                               dont_extend_height=True),
                        Condition(has_before_fragments)),
                    ConditionalContainer(
                        default_buffer_window,
                        Condition(lambda: get_app().layout.current_control !=
                                  search_buffer_control),
                    ),
                    ConditionalContainer(
                        Window(search_buffer_control),
                        Condition(lambda: get_app().layout.current_control ==
                                  search_buffer_control),
                    ),
                ]),
                [
                    # Completion menus.
                    Float(xcursor=True,
                          ycursor=True,
                          content=CompletionsMenu(
                              max_height=16,
                              scroll_offset=1,
                              extra_filter=has_focus(default_buffer)
                              & ~multi_column_complete_style)),
                    Float(xcursor=True,
                          ycursor=True,
                          content=MultiColumnCompletionsMenu(
                              show_meta=True,
                              extra_filter=has_focus(default_buffer)
                              & multi_column_complete_style)),
                    # The right prompt.
                    Float(right=0,
                          top=0,
                          hide_when_covering_content=True,
                          content=_RPrompt(lambda: self.rprompt)),
                ]),
            ConditionalContainer(ValidationToolbar(), filter=~is_done),
            ConditionalContainer(system_toolbar,
                                 dyncond('enable_system_prompt') & ~is_done),

            # In multiline mode, we use two toolbars for 'arg' and 'search'.
            ConditionalContainer(
                Window(FormattedTextControl(self._get_arg_text), height=1),
                dyncond('multiline') & has_arg),
            ConditionalContainer(search_toolbar,
                                 dyncond('multiline') & ~is_done),
            bottom_toolbar,
        ])

        # Default key bindings.
        auto_suggest_bindings = load_auto_suggest_bindings()
        open_in_editor_bindings = load_open_in_editor_bindings()
        prompt_bindings = self._create_prompt_bindings()

        # Create application
        application = Application(
            layout=Layout(layout, default_buffer_window),
            style=DynamicStyle(lambda: self.style),
            include_default_pygments_style=dyncond(
                'include_default_pygments_style'),
            clipboard=DynamicClipboard(lambda: self.clipboard),
            key_bindings=merge_key_bindings([
                merge_key_bindings([
                    auto_suggest_bindings,
                    ConditionalKeyBindings(
                        open_in_editor_bindings,
                        dyncond('enable_open_in_editor')
                        & has_focus(DEFAULT_BUFFER)), prompt_bindings
                ]),
                ConditionalKeyBindings(
                    system_toolbar.get_global_key_bindings(),
                    dyncond('enable_system_prompt')),
                DynamicKeyBindings(lambda: self.extra_key_bindings),
            ]),
            mouse_support=dyncond('mouse_support'),
            editing_mode=editing_mode,
            erase_when_done=erase_when_done,
            reverse_vi_search_direction=True,

            # I/O.
            input=self.input,
            output=self.output)

        # During render time, make sure that we focus the right search control
        # (if we are searching). - This could be useful if people make the
        # 'multiline' property dynamic.
        '''
        def on_render(app):
            multiline = _true(self.multiline)
            current_control = app.layout.current_control

            if multiline:
                if current_control == search_buffer_control:
                    app.layout.current_control = search_toolbar.control
                    app.invalidate()
            else:
                if current_control == search_toolbar.control:
                    app.layout.current_control = search_buffer_control
                    app.invalidate()

        app.on_render += on_render
        '''

        return application, default_buffer, default_buffer_control
Пример #7
0
def browse():
    """
    A browser for the bibmanager database.
    """
    # Content of the text buffer:
    bibs = bm.load()
    keys = [bib.key for bib in bibs]
    all_compact_text = "\n".join(keys)
    all_expanded_text = "\n\n".join(bib.meta() + bib.content for bib in bibs)
    # A list object, since I want this to be a global variable
    selected_content = [None]

    lex_style = style_from_pygments_cls(
        pygments.styles.get_style_by_name(cm.get('style')))
    custom_style = Style.from_dict({
        "status": "reverse",
        "status.position": "#aaaa00",
        "status.key": "#ffaa00",
        "shadow": "bg:#440044",
        "not-searching": "#888888",
    })
    style = merge_styles([lex_style, custom_style])

    def get_menubar_text():
        return [
            ("class:status", " ("),
            ("class:status.key", "enter"),
            ("class:status", ")select entry  ("),
            ("class:status.key", "e"),
            ("class:status", ")xpand entry  ("),
            ("class:status.key", "f"),
            ("class:status", ")ind  ("),
            ("class:status.key", "s"),
            ("class:status", ")ave  ("),
            ("class:status.key", "h"),
            ("class:status", ")elp  ("),
            ("class:status.key", "q"),
            ("class:status", ")uit"),
        ]

    def get_menubar_right_text():
        """Get index of entry under cursor."""
        key = get_current_key(text_field.buffer.document, keys)
        return f" {keys.index(key) + 1} "

    def get_infobar_text():
        """Get author-year-title of entry under cursor."""
        key = get_current_key(text_field.buffer.document, keys)
        bib = bibs[keys.index(key)]
        year = '' if bib.year is None else bib.year
        title = 'NO_TITLE' if bib.title is None else bib.title
        return f"{bib.get_authors('ushort')}{year}: {title}"

    search_buffer = Buffer(completer=WordCompleter(keys),
                           complete_while_typing=False,
                           multiline=False)
    literal_search_field = SearchToolbar(
        search_buffer=search_buffer,
        forward_search_prompt="Text search: ",
        backward_search_prompt="Text search backward: ",
        ignore_case=False)

    # Entry search bar:
    authors_list = [bib.authors for bib in bibs]
    firsts = sorted(
        set([
            u.get_authors([authors[0]], format='ushort')
            for authors in authors_list if authors is not None
        ]))
    firsts = [
        '^{' + first + '}' if ' ' in first else '^' + first for first in firsts
    ]

    lasts = sorted(
        set([
            u.get_authors([author], format='ushort')
            for authors in authors_list if authors is not None
            for author in authors
        ]))
    lasts = ['{' + last + '}' if ' ' in last else last for last in lasts]

    bibkeys = [bib.key for bib in bibs]
    bibcodes = [bib.bibcode for bib in bibs if bib.bibcode is not None]
    bibyears = sorted(
        set([str(bib.year) for bib in bibs if bib.year is not None]))
    titles = [bib.title for bib in bibs]
    tags = sorted(
        set(
            itertools.chain(
                *[bib.tags for bib in bibs if bib.tags is not None])))

    key_words = {
        'author:"^"': firsts,
        'author:""': lasts,
        'year:': bibyears,
        'title:""': titles,
        'key:': bibkeys,
        'bibcode:': bibcodes,
        'tags:': tags,
    }
    completer = u.DynamicKeywordCompleter(key_words)
    suggester = u.DynamicKeywordSuggester()
    auto_suggest_bindings = load_auto_suggest_bindings()

    # Searcher:
    entry_search_buffer = Buffer(
        completer=completer,
        complete_while_typing=False,
        auto_suggest=suggester,
    )

    def get_line_prefix(lineno, wrap_count):
        return FormattedText([
            ('bold', 'Entry search: '),
        ])

    entry_search_field = Window(BufferControl(
        buffer=entry_search_buffer,
        input_processors=[AppendAutoSuggestion()],
    ),
                                get_line_prefix=get_line_prefix,
                                height=1)
    # Wrap in conditional container to display it only when focused:
    entry_search_focus = Condition(
        lambda: get_app().layout.current_window == entry_search_field)
    entry_search_container = ConditionalContainer(
        content=entry_search_field,
        filter=entry_search_focus,
    )

    text_field = TextArea(
        text=all_compact_text,
        lexer=PygmentsLexer(BibTeXLexer),
        scrollbar=True,
        line_numbers=False,
        read_only=True,
        search_field=literal_search_field,
        input_processors=[HighlightEntryProcessor()],
    )
    text_field.buffer.name = 'text_area_buffer'
    text_field.is_expanded = False
    text_field.compact_text = all_compact_text
    text_field.expanded_text = all_expanded_text
    # Shortcut to HighlightEntryProcessor:
    for processor in text_field.control.input_processors:
        if processor.__class__.__name__ == 'HighlightEntryProcessor':
            text_field.bm_processor = processor
    # Do not highlight searched text:
    sp = text_field.control.default_input_processors[0]
    sp._classname = ' '
    sp._classname_current = ' '

    menu_bar = VSplit(
        [
            Window(FormattedTextControl(get_menubar_text),
                   style="class:status"),
            Window(FormattedTextControl(get_menubar_right_text),
                   style="class:status.right",
                   width=9,
                   align=WindowAlign.RIGHT),
        ],
        height=1,
    )

    info_bar = ConditionalContainer(
        content=Window(
            content=FormattedTextControl(get_infobar_text),
            height=D.exact(1),
            style="class:status",
        ),
        filter=~entry_search_focus,
    )

    body = HSplit([
        menu_bar,
        text_field,
        literal_search_field,
        entry_search_container,
        info_bar,
    ])

    root_container = FloatContainer(
        content=body,
        floats=[
            Float(
                xcursor=True,
                ycursor=True,
                content=CompletionsMenu(max_height=16, scroll_offset=1),
            ),
        ],
    )

    # Key bindings:
    bindings = KeyBindings()

    text_focus = Condition(
        lambda: get_app().layout.current_window == text_field.window)
    dialog_focus = Condition(
        lambda: hasattr(get_app().layout.current_window, 'dialog'))

    @bindings.add("q", filter=text_focus)
    def _quit(event):
        event.app.exit()

    # Navigation:
    @bindings.add("g", filter=text_focus)
    def _go_to_first_line(event):
        event.current_buffer.cursor_position = 0

    @bindings.add("G", filter=text_focus)
    def _go_to_last_line(event) -> None:
        event.current_buffer.cursor_position = len(event.current_buffer.text)

    @bindings.add("d", filter=text_focus)
    def _scroll_down(event):
        scroll_half_page_down(event)

    @bindings.add("u", filter=text_focus)
    def _scroll_up(event):
        scroll_half_page_up(event)

    @bindings.add("n", filter=text_focus)
    def _find_next(event):
        search_state = event.app.current_search_state
        event.current_buffer.apply_search(search_state,
                                          include_current_position=False,
                                          count=event.arg)

    @bindings.add("N", filter=text_focus)
    def _find_previous(event):
        search_state = event.app.current_search_state
        event.current_buffer.apply_search(~search_state,
                                          include_current_position=False,
                                          count=event.arg)

    @bindings.add("h", filter=text_focus)
    def _show_help(event):
        show_message("Shortcuts", help_message)

    @bindings.add("f", filter=text_focus)
    def _start_literal_search(event):
        search.start_search(direction=search.SearchDirection.FORWARD)

    # TBD: Remove 't' binding no before 17/12/2022
    @bindings.add("t", filter=text_focus)
    @bindings.add("k", filter=text_focus)
    def _start_entry_search(event):
        text_field.current_key = get_current_key(event.current_buffer.document,
                                                 keys)
        event.app.layout.focus(entry_search_field)

    @bindings.add("b", filter=text_focus)
    def _open_in_browser(event):
        key = get_current_key(event.current_buffer.document, keys)
        bib = bm.find(key=key, bibs=bibs)
        if bib.adsurl is not None:
            webbrowser.open(bib.adsurl, new=2)
        else:
            show_message("Message", f"Entry '{key}' does not have an ADS url.")

    @bindings.add("c-c", filter=dialog_focus)
    def _close_dialog(event):
        get_app().layout.current_window.dialog.future.set_result(None)

    @bindings.add("s", filter=text_focus)
    def _save_selected_to_file(event):
        selected = text_field.bm_processor.selected_entries
        if len(selected) == 0:
            show_message("Message", "Nothing to save.")
            return

        async def coroutine():
            dialog = TextInputDialog(
                title="Save to File",
                label_text="\nEnter a file path or leave blank to quit "
                "and print to screen:\n(press Control-c to cancel)\n",
                completer=PathCompleter(),
            )
            path = await show_dialog_as_float(dialog)
            content = '\n\n'.join(bibs[keys.index(key)].content
                                  for key in selected)
            if path == "":
                selected_content[0] = content
                # The program termination is in TextInputDialog() since I
                # need to close this coroutine first.
                return
            if path is not None:
                try:
                    with open(path, "w") as f:
                        f.write(content)
                except IOError as e:
                    show_message("Error", str(e))

        ensure_future(coroutine())

    @bindings.add("enter", filter=text_focus)
    def _toggle_selected_entry(event):
        "Select/deselect entry pointed by the cursor."
        key = get_current_key(event.current_buffer.document, keys)
        text_field.bm_processor.toggle_selected_entry(key)

    @bindings.add("enter", filter=entry_search_focus)
    def _select_entries(event):
        "Parse the input tag text and send focus back to main text."
        # Reset tag text to '':
        doc = event.current_buffer.document
        start_pos = doc.cursor_position + doc.get_start_of_line_position()
        event.current_buffer.cursor_position = start_pos
        event.current_buffer.delete(doc.get_end_of_line_position() -
                                    doc.get_start_of_line_position())

        # Catch text and parse search text:
        matches = u.parse_search(doc.current_line)
        if len(matches) == 0:
            text_field.compact_text = all_compact_text[:]
            text_field.expanded_text = all_expanded_text[:]
            search_buffer.completer.words = keys
        else:
            text_field.compact_text = "\n".join([bib.key for bib in matches])
            text_field.expanded_text = "\n\n".join(bib.meta() + bib.content
                                                   for bib in matches)
            search_buffer.completer.words = [bib.key for bib in matches]

        # Return focus to main text:
        event.app.layout.focus(text_field.window)

        # Update main text with selected tag:
        buffer = event.current_buffer
        text_field.text = text_field.compact_text
        if text_field.current_key in search_buffer.completer.words:
            buffer_position = text_field.text.index(text_field.current_key)
        else:
            buffer_position = 0
        buffer.cursor_position = buffer_position
        text_field.is_expanded = False

    # TBD: Remove 'T' binding no before 17/12/2022
    @bindings.add("T", filter=text_focus)
    @bindings.add("K", filter=text_focus)
    def _deselect_tags(event):
        buffer = event.current_buffer
        key = get_current_key(buffer.document, keys)
        text_field.compact_text = all_compact_text[:]
        text_field.expanded_text = all_expanded_text[:]
        search_buffer.completer.words = keys
        # Update main text:
        text_field.text = text_field.compact_text
        buffer.cursor_position = buffer.text.index(key)
        text_field.is_expanded = False

    @bindings.add("e", filter=text_focus)
    def _expand_collapse_entry(event):
        "Expand/collapse current entry."
        doc = event.current_buffer.document
        key, start_end, is_expanded = get_current_key(doc,
                                                      keys,
                                                      get_start_end=True,
                                                      get_expanded=True)
        bib = bm.find(key=key, bibs=bibs)
        if is_expanded:
            # Remove blank lines around if surrounded by keys:
            start_row, _ = doc._find_line_start_index(start_end[0])
            if start_row > 0 and doc.lines[start_row - 2] in keys:
                start_end[0] -= 1
            end_row, _ = doc._find_line_start_index(start_end[1])
            if end_row < doc.line_count - 1 and doc.lines[end_row + 2] in keys:
                start_end[1] += 1
            event.app.clipboard.set_text(bib.key)
        else:
            expanded_content = bib.meta() + bib.content
            row = doc.cursor_position_row
            # Add blank lines around if surrounded by keys:
            if row > 0 and doc.lines[row - 1] != '':
                expanded_content = '\n' + expanded_content
            if row < doc.line_count - 1 and doc.lines[row + 1] != '':
                expanded_content = expanded_content + '\n'
            event.app.clipboard.set_text(expanded_content)

        text_field.read_only = False
        event.current_buffer.cursor_position = start_end[0]
        event.current_buffer.delete(count=start_end[1] - start_end[0])
        event.current_buffer.paste_clipboard_data(
            event.app.clipboard.get_data(),
            count=event.arg,
            paste_mode=PasteMode.VI_BEFORE)
        text_field.read_only = True
        if is_expanded:
            event.current_buffer.cursor_position = start_end[0]

    @bindings.add("E", filter=text_focus)
    def _expand_collapse_all(event):
        "Expand/collapse all entries."
        buffer = event.current_buffer
        key = get_current_key(buffer.document, keys)
        if text_field.is_expanded:
            text_field.text = text_field.compact_text
        else:
            text_field.text = text_field.expanded_text

        buffer.cursor_position = buffer.text.index(key)
        text_field.is_expanded = not text_field.is_expanded

    @bindings.add("o", filter=text_focus)
    def _open_pdf(event):
        buffer = event.current_buffer
        key = get_current_key(buffer.document, keys)
        bib = bm.find(key=key, bibs=bibs)

        has_pdf = bib.pdf is not None
        has_bibcode = bib.bibcode is not None
        is_missing = has_pdf and not os.path.exists(f'{u.BM_PDF()}{bib.pdf}')

        if not has_pdf and not has_bibcode:
            show_message("Message",
                         f"BibTeX entry '{key}' does not have a PDF.")
            return

        if has_pdf and not is_missing:
            pm.open(key=key)
            return

        if has_pdf and is_missing and not has_bibcode:
            show_message(
                "Message",
                f"BibTeX entry has a PDF file: {bib.pdf}, but the file "
                "could not be found.")
            return

        # Need to fetch before opening:
        async def coroutine():
            dialog = MessageDialog(
                "PDF file not found",
                "Fetch from ADS?\n(might take a few seconds ...)",
                asking=True)
            fetch = await show_dialog_as_float(dialog)
            if fetch:
                with io.StringIO() as buf, redirect_stdout(buf):
                    fetched = pm.fetch(bib.bibcode, replace=True)
                    fetch_output = buf.getvalue()

                if fetched is None:
                    show_message("PDF fetch failed", fetch_output)
                else:
                    show_message("PDF fetch succeeded.", fetch_output)
                    pm.open(key=key)

        ensure_future(coroutine())

    key_bindings = merge_key_bindings([
        auto_suggest_bindings,
        bindings,
    ])
    application = Application(
        layout=Layout(root_container, focused_element=text_field),
        key_bindings=key_bindings,
        enable_page_navigation_bindings=True,
        style=style,
        full_screen=True,
    )

    application.run()
    if selected_content[0] is not None:
        tokens = list(pygments.lex(selected_content[0], lexer=BibTeXLexer()))

        print_formatted_text(
            PygmentsTokens(tokens),
            end="",
            style=lex_style,
            #output=create_output(sys.stdout),
        )
    def _create_application(self, editing_mode, erase_when_done):
        """
        Create the `Application` object.
        """
        dyncond = self._dyncond

        # Default key bindings.
        auto_suggest_bindings = load_auto_suggest_bindings()
        open_in_editor_bindings = load_open_in_editor_bindings()
        prompt_bindings = self._create_prompt_bindings()

        # Create application
        application = Application(
            layout=self.layout,
            style=DynamicStyle(lambda: self.style),
            style_transformation=merge_style_transformations([
                DynamicStyleTransformation(lambda: self.style_transformation),
                ConditionalStyleTransformation(
                    SwapLightAndDarkStyleTransformation(),
                    dyncond('swap_light_and_dark_colors'),
                ),
            ]),
            include_default_pygments_style=dyncond('include_default_pygments_style'),
            clipboard=DynamicClipboard(lambda: self.clipboard),
            key_bindings=merge_key_bindings([
                merge_key_bindings([
                    auto_suggest_bindings,
                    ConditionalKeyBindings(open_in_editor_bindings,
                        dyncond('enable_open_in_editor') &
                        has_focus(DEFAULT_BUFFER)),
                    prompt_bindings
                ]),
                DynamicKeyBindings(lambda: self.key_bindings),
            ]),
            mouse_support=dyncond('mouse_support'),
            editing_mode=editing_mode,
            erase_when_done=erase_when_done,
            reverse_vi_search_direction=True,
            color_depth=lambda: self.color_depth,

            # I/O.
            input=self.input,
            output=self.output)

        # During render time, make sure that we focus the right search control
        # (if we are searching). - This could be useful if people make the
        # 'multiline' property dynamic.
        '''
        def on_render(app):
            multiline = is_true(self.multiline)
            current_control = app.layout.current_control

            if multiline:
                if current_control == search_buffer_control:
                    app.layout.current_control = search_toolbar.control
                    app.invalidate()
            else:
                if current_control == search_toolbar.control:
                    app.layout.current_control = search_buffer_control
                    app.invalidate()

        app.on_render += on_render
        '''

        return application