Пример #1
0
def edit_multiline(default_text=""):
    kb = KeyBindings()

    @kb.add('c-q')
    @kb.add('escape', 'enter')
    def exit_(event):
        """
        Pressing Ctrl-Q, Alt+Enter or Esc + Enter will exit the editor.
        """
        event.app.exit(textf.text)

    @kb.add('c-c')
    def do_copy(event):
        data = textf.buffer.copy_selection()
        get_app().clipboard.set_data(data)

    @kb.add('c-x', eager=True)
    def do_cut(event):
        data = textf.buffer.cut_selection()
        get_app().clipboard.set_data(data)

    @kb.add('c-z')
    def do_undo(event):
        textf.buffer.undo()

    @kb.add('c-y')
    def do_redo(event):
        textf.buffer.redo()

    @kb.add('c-a')
    def do_select_all(event):
        textf.buffer.cursor_position = 0
        textf.buffer.start_selection()
        textf.buffer.cursor_position = len(textf.buffer.text)
        update_stored_pos(None)

    @kb.add('c-v')
    def do_paste(event):
        textf.buffer.paste_clipboard_data(get_app().clipboard.get_data())

    @kb.add('left')
    def kb_left(event):
        textf.buffer.selection_state = None
        if textf.buffer.cursor_position != 0 and textf.text[
                textf.buffer.cursor_position - 1] == '\n':
            textf.buffer.cursor_up()
            textf.buffer.cursor_right(len(textf.text))
        else:
            textf.buffer.cursor_left()
        update_stored_pos(None)

    @kb.add('right')
    def kb_right(event):
        textf.buffer.selection_state = None
        if textf.buffer.cursor_position < len(textf.text) and textf.text[
                textf.buffer.cursor_position] == '\n':
            textf.buffer.cursor_down()
            textf.buffer.cursor_left(len(textf.text))

        else:
            textf.buffer.cursor_right()
        update_stored_pos(None)

    @kb.add('home')
    def kb_home(event):
        textf.buffer.selection_state = None
        width = getTermWidth()
        doc = textf.document
        if textf.buffer.cursor_position == doc._line_start_indexes[
                cursor_row()] + int(cursor_col() / width) * width:
            textf.buffer.cursor_position = doc._line_start_indexes[
                cursor_row()]
        else:
            textf.buffer.cursor_position = doc._line_start_indexes[
                cursor_row()] + int(cursor_col() / width) * width
        update_stored_pos(None)

    @kb.add('end')
    def kb_end(event):
        textf.buffer.selection_state = None
        width = getTermWidth()
        doc = textf.document
        row = cursor_row()
        if textf.buffer.cursor_position == doc._line_start_indexes[row] + (
                int(cursor_col() / width) + 1) * width - 1:
            textf.buffer.cursor_position = doc._line_start_indexes[row] + len(
                doc.current_line)
        else:
            textf.buffer.cursor_position = min(
                doc._line_start_indexes[row] +
                (int(cursor_col() / width) + 1) * width - 1,
                doc._line_start_indexes[row] + len(doc.current_line))
        update_stored_pos(None)

    @kb.add('up')
    def kb_up(event):
        textf.freezestore = True
        width = getTermWidth()
        doc = textf.document
        textf.buffer.selection_state = None
        col = cursor_col()
        row = cursor_row()
        if width > 9000:  # A failsafe in case the terminal size is incorrectly detected
            textf.buffer.cursor_up()
            return

        if col >= width:  # Move one row up staying on the same line
            textf.buffer.cursor_position = doc._line_start_indexes[row] + int(
                col / width - 1) * width + textf.stored_cursor_pos
        elif row >= 1:  # Moving up to a different line
            prevlinelen = len(doc.lines[row - 1])

            textf.buffer.cursor_position = min(
                doc._line_start_indexes[row] - 1,
                doc._line_start_indexes[row - 1] +
                int(prevlinelen / width) * width + textf.stored_cursor_pos)
        else:  # Cursor is on the first row of first line
            textf.buffer.cursor_position = 0
            textf.freezestore = False
            update_stored_pos(None)

    @kb.add('down')
    def kb_down(event):
        textf.freezestore = True
        width = getTermWidth()
        doc = textf.document
        textf.buffer.selection_state = None
        col = cursor_col()
        row = cursor_row()
        nextlinelen = len(doc.lines[row +
                                    1]) if row < len(doc.lines) - 1 else -1
        if width > 9000:  # A failsafe in case the terminal size is incorrectly detected
            textf.buffer.cursor_down()
            return

        if col <= len(doc.current_line
                      ) - width:  # Move one row down staying on the same line
            textf.buffer.cursor_position = doc._line_start_indexes[row] + int(
                col / width + 1) * width + textf.stored_cursor_pos
        elif nextlinelen < 0:  # Move to the very end
            textf.buffer.cursor_position = len(textf.text)
            textf.freezestore = False
            update_stored_pos(None)
        # Move to the end of the same line the cursor is on
        elif col != len(doc.lines[row]) and textf.stored_cursor_pos >= len(
                doc.lines[row]) - int(len(doc.lines[row]) / width) * width:
            textf.buffer.cursor_position = doc._line_start_indexes[row + 1] - 1
        else:  # Move to a different line
            textf.buffer.cursor_position = min(
                doc._line_start_indexes[row + 1] + nextlinelen,
                doc._line_start_indexes[row + 1] + textf.stored_cursor_pos)

    textf = TextArea()
    bottom_bar_text = FormattedTextControl(
        text=
        '\nCurrently editing. Press Ctrl+Q, Alt+Enter or Esc + Enter to exit.')
    bottom_bar = Window(content=bottom_bar_text)

    root_container = HSplit([
        textf,
        bottom_bar,
    ])

    layout = Layout(root_container)

    app = Application(key_bindings=kb,
                      layout=layout,
                      enable_page_navigation_bindings=True,
                      full_screen=False)
    textf.freezestore = False
    textf.text = default_text
    textf.buffer.cursor_position = len(textf.buffer.text)

    # Find the row the cursor is at
    # My own function, in fear of race conditions
    def cursor_row():
        i = 0
        while i < len(
                textf.document._line_start_indexes
        ) and textf.buffer.cursor_position >= textf.document._line_start_indexes[
                i]:
            i += 1
        return i - 1

    # Find the column the cursor is at
    # There is a built-in function, but I think there's some kind of a race condition if it's used
    def cursor_col():
        i = textf.buffer.cursor_position - 1
        while i >= 0 and textf.text[i] != '\n':
            i -= 1
        return textf.buffer.cursor_position - i - 1

    def update_stored_pos(event):
        if not event:
            textf.freezestore = False
        if textf.freezestore:
            textf.freezestore = False
            return
        width = getTermWidth()
        col = cursor_col()
        textf.stored_cursor_pos = col - int(col / width) * width

    textf.buffer.on_cursor_position_changed += update_stored_pos
    update_stored_pos(None)

    text = app.run()

    clear_lines(1)

    return text
                              password=True)

conf_prompt_base_url = Window(FormattedTextControl('SlashNext Base URL'),
                              height=1,
                              align=WindowAlign.LEFT,
                              style="class:conf_prompt")

conf_input_base_url = TextArea(height=1,
                               prompt='',
                               multiline=False,
                               wrap_lines=False,
                               style="class:conf_input",
                               focusable=True,
                               focus_on_click=True)

conf_input_base_url.text = 'https://oti.slashnext.cloud/api'

conf_status = TextArea(height=5,
                       multiline=True,
                       wrap_lines=True,
                       style="class:conf_notice")

separator = Window(height=1,
                   width=1,
                   align=WindowAlign.CENTER,
                   style="class:conf_prompt")

terminator = Window(align=WindowAlign.CENTER, style="class:conf_prompt")

line = Window(height=1, char="─", style="class:line")