diff options
| -rw-r--r-- | src/nvim/tui/term_input.inl | 62 | ||||
| -rw-r--r-- | src/nvim/tui/tui.c | 17 | 
2 files changed, 69 insertions, 10 deletions
| diff --git a/src/nvim/tui/term_input.inl b/src/nvim/tui/term_input.inl index 0c0e6c07c9..dba970e8f2 100644 --- a/src/nvim/tui/term_input.inl +++ b/src/nvim/tui/term_input.inl @@ -1,10 +1,12 @@  #include <termkey.h>  #include "nvim/ascii.h" +#include "nvim/misc2.h"  #include "nvim/os/os.h"  #include "nvim/os/input.h"  #include "nvim/os/rstream.h" +#define PASTETOGGLE_KEY "<f37>"  struct term_input {    int in_fd; @@ -113,7 +115,7 @@ static int get_key_code_timeout(void)    bool timeout = false;    // Check 'timeout' and 'ttimeout' to determine if we should send ESC    // after 'ttimeoutlen'. See :help 'ttimeout' for more information -  Error err; +  Error err = ERROR_INIT;    timeout = vim_get_option(cstr_as_string("timeout"), &err).data.boolean;    if (!timeout) {      timeout = vim_get_option(cstr_as_string("ttimeout"), &err).data.boolean; @@ -164,6 +166,49 @@ static void timer_cb(uv_timer_t *handle)    tk_getkeys(handle->data, true);  } +static bool handle_bracketed_paste(TermInput *input) +{ +  char *ptr = rbuffer_read_ptr(input->read_buffer); +  size_t len = rbuffer_pending(input->read_buffer); +  if (len > 5 && (!strncmp(ptr, "\x1b[200~", 6) +        || !strncmp(ptr, "\x1b[201~", 6))) { +    bool enable = ptr[4] == '0'; +    // Advance past the sequence +    rbuffer_consumed(input->read_buffer, 6); +    if (enable) { +      // Get the current mode +      int state = get_real_state(); +      if (state & NORMAL) { +        // Enter insert mode +        input_enqueue(cstr_as_string("i")); +      } else if (state & VISUAL) { +        // Remove the selected text and enter insert mode +        input_enqueue(cstr_as_string("c")); +      } else if (!(state & INSERT)) { +        // Don't mess with the paste option +        return true; +      } +    } +    input_enqueue(cstr_as_string(PASTETOGGLE_KEY)); +    return true; +  } +  return false; +} + +static bool handle_forced_escape(TermInput *input) +{ +  char *ptr = rbuffer_read_ptr(input->read_buffer); +  size_t len = rbuffer_pending(input->read_buffer); +  if (len > 1 && ptr[0] == ESC && ptr[1] == NUL) { +    // skip the ESC and NUL and push one <esc> to the input buffer +    termkey_push_bytes(input->tk, ptr, 1); +    rbuffer_consumed(input->read_buffer, 2); +    tk_getkeys(input, true); +    return true; +  } +  return false; +} +  static void read_cb(RStream *rstream, void *rstream_data, bool eof)  {    if (eof) { @@ -174,15 +219,11 @@ static void read_cb(RStream *rstream, void *rstream_data, bool eof)    TermInput *input = rstream_data;    do { -    char *ptr = rbuffer_read_ptr(input->read_buffer); -    size_t len = rbuffer_pending(input->read_buffer); -    if (len > 1 && ptr[0] == ESC && ptr[1] == NUL) { -      // skip the ESC and NUL and push one <esc> to the input buffer -      termkey_push_bytes(input->tk, ptr, 1); -      rbuffer_consumed(input->read_buffer, 2); -      tk_getkeys(input, true); +    if (handle_bracketed_paste(input) || handle_forced_escape(input)) {        continue;      } +    char *ptr = rbuffer_read_ptr(input->read_buffer); +    size_t len = rbuffer_pending(input->read_buffer);      // Find the next 'esc' and push everything up to it(excluding)      size_t i;      for (i = ptr[0] == ESC ? 1 : 0; i < len; i++) { @@ -228,6 +269,11 @@ static TermInput *term_input_new(void)    // initialize a timer handle for handling ESC with libtermkey    uv_timer_init(uv_default_loop(), &rv->timer_handle);    rv->timer_handle.data = rv; +  // Set the pastetoggle option to a special key that will be sent when +  // \e[20{0,1}~/ are received +  Error err = ERROR_INIT; +  vim_set_option(cstr_as_string("pastetoggle"), +      STRING_OBJ(cstr_as_string(PASTETOGGLE_KEY)), &err);    return rv;  } diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 575a75adec..44296cf446 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -50,6 +50,7 @@ typedef struct {    Cell **screen;    struct {      size_t enable_mouse, disable_mouse; +    size_t enable_bracketed_paste, disable_bracketed_paste;    } unibi_ext;  } TUIData; @@ -103,6 +104,8 @@ void tui_start(void)    // Enter alternate screen and clear    unibi_out(ui, unibi_enter_ca_mode, NULL);    unibi_out(ui, unibi_clear_screen, NULL); +  // Enable bracketed paste +  unibi_out(ui, (int)data->unibi_ext.enable_bracketed_paste, NULL);    // setup output handle in a separate event loop(we wanna do synchronous    // write to the tty) @@ -162,6 +165,8 @@ static void tui_stop(UI *ui)    unibi_out(ui, unibi_exit_attribute_mode, NULL);    unibi_out(ui, unibi_cursor_normal, NULL);    unibi_out(ui, unibi_exit_ca_mode, NULL); +  // Disable bracketed paste +  unibi_out(ui, (int)data->unibi_ext.disable_bracketed_paste, NULL);    flush_buf(ui);    uv_close((uv_handle_t *)&data->output_handle, NULL);    uv_run(data->write_loop, UV_RUN_DEFAULT); @@ -665,6 +670,8 @@ static void fix_terminfo(TUIData *data)      goto end;    } +  bool inside_tmux = os_getenv("TMUX") != NULL; +  #define STARTS_WITH(str, prefix) (!memcmp(str, prefix, sizeof(prefix) - 1))    if (STARTS_WITH(term, "rxvt")) { @@ -689,13 +696,19 @@ static void fix_terminfo(TUIData *data)      unibi_set_if_empty(ut, unibi_from_status_line, "\x07");    } +  if (STARTS_WITH(term, "xterm") || STARTS_WITH(term, "rxvt") || inside_tmux) { +    data->unibi_ext.enable_bracketed_paste = unibi_add_ext_str(ut, NULL, +        "\x1b[?2004h"); +    data->unibi_ext.disable_bracketed_paste = unibi_add_ext_str(ut, NULL, +        "\x1b[?2004l"); +  } +  end:    // Fill some empty slots with common terminal strings    data->unibi_ext.enable_mouse = unibi_add_ext_str(ut, NULL,        "\x1b[?1002h\x1b[?1006h");    data->unibi_ext.disable_mouse = unibi_add_ext_str(ut, NULL,        "\x1b[?1002l\x1b[?1006l"); -    unibi_set_if_empty(ut, unibi_cursor_address, "\x1b[%i%p1%d;%p2%dH");    unibi_set_if_empty(ut, unibi_exit_attribute_mode, "\x1b[0;10m");    unibi_set_if_empty(ut, unibi_set_a_foreground, @@ -735,7 +748,7 @@ static char *get_term_option(UI *ui, char *option)    char *rv = pmap_get(cstr_t)(data->option_cache, option);    if (!rv) { -    Error err; +    Error err = ERROR_INIT;      Object val = vim_get_option(cstr_as_string(option), &err);      if (val.type == kObjectTypeString) {        rv = val.data.string.data; | 
