예제 #1
0
    """
    show_status_bar = True
    current_path = None


def get_statusbar_text():
    return ' Press Ctrl-C to open menu. '


def get_statusbar_right_text():
    return ' {}:{}  '.format(
        text_field.document.cursor_position_row + 1,
        text_field.document.cursor_position_col + 1)


search_toolbar = SearchToolbar()
text_field = TextArea(
    lexer=DynamicLexer(
        lambda: PygmentsLexer.from_filename(
            ApplicationState.current_path or '.txt',
            sync_from_start=False)),
    scrollbar=True,
    line_numbers=True,
    search_field=search_toolbar,
)


class TextInputDialog(object):
    def __init__(self, title='', label_text='', completer=None):
        self.future = Future()
예제 #2
0
def create_application():
    def get_statusbar_text():
        return "Ctrl-C: Close. Ctrl-E: Execute. Ctrl-S: Stop music. F2: Open Menu. "

    def get_statusbar_right_text():
        return " {}:{}  ".format(
            text_field.document.cursor_position_row + 1,
            text_field.document.cursor_position_col + 1,
        )

    search_toolbar = SearchToolbar()
    text_field = TextArea(
        lexer=PygmentsLexer.from_filename('.mkl', sync_from_start=False),
        completer=VariableCompleter(ApplicationState.context),
        scrollbar=True,
        line_numbers=True,
        search_field=search_toolbar,
    )

    class TextInputDialog:
        def __init__(self, title="", label_text="", completer=None):
            self.future = Future()

            def accept_text(buf):
                get_app().layout.focus(ok_button)
                buf.complete_state = None
                return True

            def accept():
                self.future.set_result(self.text_area.text)

            def cancel():
                self.future.set_result(None)

            self.text_area = TextArea(
                completer=completer,
                multiline=False,
                width=D(preferred=40),
                accept_handler=accept_text,
            )

            ok_button = Button(text="OK", handler=accept)
            cancel_button = Button(text="Cancel", handler=cancel)

            self.dialog = Dialog(
                title=title,
                body=HSplit([Label(text=label_text), self.text_area]),
                buttons=[ok_button, cancel_button],
                width=D(preferred=80),
                modal=True,
            )

        def __pt_container__(self):
            return self.dialog

    class MessageDialog:
        def __init__(self, title, text):
            self.future = Future()

            def set_done():
                self.future.set_result(None)

            ok_button = Button(text="OK", handler=(lambda: set_done()))

            self.dialog = Dialog(
                title=title,
                body=HSplit([
                    Label(text=text),
                ]),
                buttons=[ok_button],
                width=D(preferred=80),
                modal=True,
            )

        def __pt_container__(self):
            return self.dialog

    body = HSplit([
        text_field,
        search_toolbar,
        ConditionalContainer(
            content=VSplit(
                [
                    Window(FormattedTextControl(get_statusbar_text),
                           style="class:status"),
                    Window(
                        FormattedTextControl(get_statusbar_right_text),
                        style="class:status.right",
                        width=9,
                        align=WindowAlign.RIGHT,
                    ),
                ],
                height=1,
            ),
            filter=Condition(lambda: ApplicationState.show_status_bar),
        ),
    ])

    # Global key bindings.
    bindings = KeyBindings()

    @bindings.add("f2")
    def _(event):
        " Focus menu. "
        event.app.layout.focus(root_container.window)

    #
    # Handlers for menu items.
    #

    def do_open_file():
        async def coroutine():
            open_dialog = TextInputDialog(
                title="Open file",
                label_text="Enter the path of a file:",
                completer=PathCompleter(),
            )

            path = await show_dialog_as_float(open_dialog)
            ApplicationState.current_path = path

            if path is not None:
                try:
                    with open(path, "rb") as f:
                        text_field.text = f.read().decode("utf-8",
                                                          errors="ignore")
                except IOError as e:
                    show_message("Error", "{}".format(e))

        ensure_future(coroutine())

    def do_about():
        show_message("About",
                     "Text editor demo.\nCreated by Jonathan Slenders.")

    def show_message(title, text):
        async def coroutine():
            dialog = MessageDialog(title, text)

            await show_dialog_as_float(dialog)

        ensure_future(coroutine())

    async def show_dialog_as_float(dialog):
        " Coroutine. "
        float_ = Float(content=dialog)
        root_container.floats.insert(0, float_)

        app = get_app()

        focused_before = app.layout.current_window
        app.layout.focus(dialog)
        result = await dialog.future
        app.layout.focus(focused_before)

        if float_ in root_container.floats:
            root_container.floats.remove(float_)

        return result

    @bindings.add('c-n')
    def do_new_file(event=None):
        text_field.text = ""

    @bindings.add('c-c')
    def do_exit(event=None):
        get_app().exit()

    def do_time_date():
        text = datetime.datetime.now().isoformat()
        text_field.buffer.insert_text(text)

    def do_go_to():
        async def coroutine():
            dialog = TextInputDialog(title="Go to line",
                                     label_text="Line number:")

            line_number = await show_dialog_as_float(dialog)

            try:
                line_number = int(line_number)
            except ValueError:
                show_message("Invalid line number")
            else:
                text_field.buffer.cursor_position = text_field.buffer.document.translate_row_col_to_index(
                    line_number - 1, 0)

        ensure_future(coroutine())

    def do_undo():
        text_field.buffer.undo()

    def do_cut():
        data = text_field.buffer.cut_selection()
        get_app().clipboard.set_data(data)

    def do_copy():
        data = text_field.buffer.copy_selection()
        get_app().clipboard.set_data(data)

    def do_delete():
        text_field.buffer.cut_selection()

    def do_find():
        start_search(text_field.control)

    def do_find_next():
        search_state = get_app().current_search_state

        cursor_position = text_field.buffer.get_search_position(
            search_state, include_current_position=False)
        text_field.buffer.cursor_position = cursor_position

    def do_paste():
        text_field.buffer.paste_clipboard_data(get_app().clipboard.get_data())

    def do_select_all():
        text_field.buffer.cursor_position = 0
        text_field.buffer.start_selection()
        text_field.buffer.cursor_position = len(text_field.buffer.text)

    @bindings.add("c-e")
    def do_eval(event=None):
        if ApplicationState.context is None or ApplicationState.player is None:
            show_message("Error",
                         "Cannot evaluate because no context is defined")

            return

        code = text_field.buffer.text

        try:
            script = ApplicationState.context.script

            now = ApplicationState.player.get_time()

            ctx = ApplicationState.context.fork(cursor=now)

            val = script.eval(code, context=ctx)

            if val is not None and isinstance(val, Music):
                pl = InteractivePlayer(lambda: val.expand(ctx),
                                       ApplicationState.player,
                                       realtime=True)

                ApplicationState.interactive_players.append(pl)

                create_task(pl.start())
        except BaseException as e:
            if hasattr(e, 'message'):
                show_message("Eval Error", str(cast(Any, e).message))
            else:
                show_message("Eval Error", str(e))

    @bindings.add("c-s")
    def do_mute_players(event=None):
        for p in ApplicationState.interactive_players:
            create_task(p.stop())

        ApplicationState.interactive_players = []

    def do_status_bar():
        ApplicationState.show_status_bar = not ApplicationState.show_status_bar

    #
    # The menu container.
    #

    root_container = MenuContainer(
        body=body,
        menu_items=[
            MenuItem(
                "File",
                children=[
                    MenuItem("New...", handler=do_new_file),
                    MenuItem("Open...", handler=do_open_file),
                    MenuItem("Save"),
                    MenuItem("Save as..."),
                    MenuItem("-", disabled=True),
                    MenuItem("Exit", handler=do_exit),
                ],
            ),
            MenuItem(
                "Edit",
                children=[
                    MenuItem("Undo", handler=do_undo),
                    MenuItem("Cut", handler=do_cut),
                    MenuItem("Copy", handler=do_copy),
                    MenuItem("Paste", handler=do_paste),
                    MenuItem("Delete", handler=do_delete),
                    MenuItem("-", disabled=True),
                    MenuItem("Find", handler=do_find),
                    MenuItem("Find next", handler=do_find_next),
                    MenuItem("Replace"),
                    MenuItem("Go To", handler=do_go_to),
                    MenuItem("Select All", handler=do_select_all),
                    MenuItem("Time/Date", handler=do_time_date),
                ],
            ),
            MenuItem("View",
                     children=[
                         MenuItem("Status Bar", handler=do_status_bar),
                     ]),
            MenuItem("Info", children=[
                MenuItem("About", handler=do_about),
            ]),
        ],
        floats=[
            Float(
                xcursor=True,
                ycursor=True,
                content=CompletionsMenu(max_height=16, scroll_offset=1),
            ),
        ],
        key_bindings=bindings,
    )

    style = Style.from_dict({
        "status": "reverse",
        "shadow": "bg:#440044",
    })

    layout = Layout(root_container, focused_element=text_field)

    application = Application(
        layout=layout,
        enable_page_navigation_bindings=True,
        style=style,
        mouse_support=True,
        full_screen=True,
    )

    return application, text_field
예제 #3
0
def main():
    # The layout.
    search_field = SearchToolbar()  # For reverse search.

    output_field = TextArea(style="class:output-field", text=help_text)
    input_field = TextArea(
        height=1,
        prompt=">>> ",
        style="class:input-field",
        multiline=False,
        wrap_lines=False,
        search_field=search_field,
    )

    container = HSplit(
        [
            output_field,
            Window(height=1, char="-", style="class:line"),
            input_field,
            search_field,
        ]
    )

    # Attach accept handler to the input field. We do this by assigning the
    # handler to the `TextArea` that we created earlier. it is also possible to
    # pass it to the constructor of `TextArea`.
    # NOTE: It's better to assign an `accept_handler`, rather then adding a
    #       custom ENTER key binding. This will automatically reset the input
    #       field and add the strings to the history.
    def accept(buff):
        # Evaluate "calculator" expression.
        try:
            output = "\n\nIn:  {}\nOut: {}".format(
                input_field.text, eval(input_field.text)
            )  # Don't do 'eval' in real code!
        except BaseException as e:
            output = "\n\n{}".format(e)
        new_text = output_field.text + output

        # Add text to output buffer.
        output_field.buffer.document = Document(
            text=new_text, cursor_position=len(new_text)
        )

    input_field.accept_handler = accept

    # The key bindings.
    kb = KeyBindings()

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

    # Style.
    style = Style(
        [
            ("output-field", "bg:#000044 #ffffff"),
            ("input-field", "bg:#000000 #ffffff"),
            ("line", "#004400"),
        ]
    )

    # Run application.
    application = Application(
        layout=Layout(container, focused_element=input_field),
        key_bindings=kb,
        style=style,
        mouse_support=True,
        full_screen=True,
    )

    application.run()
예제 #4
0
            "class:status.position",
            "{}:{}".format(
                text_area.document.cursor_position_row + 1,
                text_area.document.cursor_position_col + 1,
            ),
        ),
        ("class:status", " - Press "),
        ("class:status.key", "Ctrl-C"),
        ("class:status", " to exit, "),
        ("class:status.key", "/"),
        ("class:status", " for searching."),
    ]


search_field = SearchToolbar(
    text_if_not_searching=[("class:not-searching",
                            "Press '/' to start searching.")])

text_area = TextArea(
    text=text,
    read_only=True,
    scrollbar=True,
    line_numbers=True,
    search_field=search_field,
    lexer=PygmentsLexer(PythonLexer),
)

root_container = HSplit([
    # The top toolbar.
    Window(
        content=FormattedTextControl(get_statusbar_text),
예제 #5
0
    def __init__(self, my_app: "sqlApp") -> None:

        self.my_app = my_app
        self.search_field = SearchToolbar()
        history_file = config_location() + 'history'
        ensure_dir_exists(history_file)
        hist = ThreadedHistory(FileHistory(expanduser(history_file)))
        self.input_buffer = Buffer(
            name="defaultbuffer",
            tempfile_suffix=".py",
            multiline=MultilineFilter(self.my_app),
            history=hist,
            completer=ThreadedCompleter(self.my_app.completer),
            auto_suggest=ThreadedAutoSuggest(AutoSuggestFromHistory()),
            complete_while_typing=Condition(
                lambda: self.my_app.active_conn is not None))
        main_win_control = BufferControl(
            buffer=self.input_buffer,
            lexer=PygmentsLexer(SqlLexer),
            search_buffer_control=self.search_field.control,
            include_default_input_processors=False,
            input_processors=[AppendAutoSuggestion()],
            preview_search=True)

        self.main_win = Window(
            main_win_control,
            height=(
                lambda:
                (None if get_app().is_done else
                 (Dimension(min=self.my_app.min_num_menu_lines)
                  if not self.my_app.show_preview else Dimension(
                      min=self.my_app.min_num_menu_lines, preferred=180)))),
            get_line_prefix=partial(sql_line_prefix, my_app=self.my_app),
            scroll_offsets=ScrollOffsets(bottom=1, left=4, right=4))

        preview_element = PreviewElement(self.my_app)
        self.lprompt = login_prompt(self.my_app)
        self.preview = preview_element.create_container()
        self.disconnect_dialog = disconnect_dialog(self.my_app)
        container = HSplit([
            VSplit([
                FloatContainer(
                    content=HSplit([
                        self.main_win,
                        self.search_field,
                    ]),
                    floats=[
                        Float(
                            bottom=1,
                            left=1,
                            right=0,
                            content=sql_sidebar_help(self.my_app),
                        ),
                        Float(content=self.lprompt),
                        Float(content=self.preview, ),
                        preview_element.create_completion_float(),
                        Float(content=self.disconnect_dialog, ),
                        Float(left=2,
                              bottom=1,
                              content=exit_confirmation(self.my_app)),
                        Float(xcursor=True,
                              ycursor=True,
                              transparent=True,
                              content=CompletionsMenu(scroll_offset=1,
                                                      max_height=16,
                                                      extra_filter=has_focus(
                                                          self.input_buffer)))
                    ]),
                ConditionalContainer(
                    content=sql_sidebar(self.my_app),
                    filter=ShowSidebar(self.my_app) & ~is_done,
                )
            ]),
            VSplit([
                status_bar(self.my_app),
                show_sidebar_button_info(self.my_app)
            ])
        ])

        def accept(buff):
            app = get_app()
            app.exit(result=["non-preview", buff.text])
            app.pre_run_callables.append(buff.reset)
            return True

        self.input_buffer.accept_handler = accept
        self.layout = Layout(container, focused_element=self.main_win)
예제 #6
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),
        )
예제 #7
0
def _(event):
    'Help'
    do_help()

@BINDINGS.add('f11')
def _(event):
    'Help'
    do_about()




# Handlers
# --------

SEARCH_TOOLBAR = SearchToolbar()
TEXT_FIELD = TextArea(
    lexer=DynamicLexer(
        lambda: PygmentsLexer.from_filename(
            ApplicationState.current_path or '.txt', sync_from_start=False
        )
    ),
    scrollbar=False,
    line_numbers=True,
    wrap_lines=True,
    search_field=SEARCH_TOOLBAR,
)
TITLE = Window(
    height=1,
    content=FormattedTextControl('cli-pto'),
    align=WindowAlign.CENTER,