diff options
author | James McCoy <jamessan@jamessan.com> | 2020-11-20 12:08:31 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-11-20 12:08:31 -0500 |
commit | 97ffa158aa9ae6c80a48b3ceea91270f0a179743 (patch) | |
tree | 3096aa9010f797d2e4322cb1af5bc58dc41e7819 /src | |
parent | 978857e0c25384489c08309ec8128d3a1c8b5e3e (diff) | |
parent | a539a12d723dd9c9ac63df815d35cd0204199b96 (diff) | |
download | rneovim-97ffa158aa9ae6c80a48b3ceea91270f0a179743.tar.gz rneovim-97ffa158aa9ae6c80a48b3ceea91270f0a179743.tar.bz2 rneovim-97ffa158aa9ae6c80a48b3ceea91270f0a179743.zip |
Merge pull request #12004 from erw7/fix-paste
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/tui/input.c | 131 | ||||
-rw-r--r-- | src/nvim/tui/input.h | 8 |
2 files changed, 105 insertions, 34 deletions
diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 94c326a5eb..124f96e039 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -22,6 +22,14 @@ #define KEY_BUFFER_SIZE 0xfff +#ifndef UNIT_TESTING +typedef enum { + kIncomplete = -1, + kNotApplicable = 0, + kComplete = 1, +} HandleState; +#endif + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "tui/input.c.generated.h" #endif @@ -339,8 +347,14 @@ static void tk_getkeys(TermInput *input, bool force) static void tinput_timer_cb(TimeWatcher *watcher, void *data) { - tk_getkeys(data, true); - tinput_flush(data, true); + TermInput *input = (TermInput *)data; + // If the raw buffer is not empty, process the raw buffer first because it is + // processing an incomplete bracketed paster sequence. + if (rbuffer_size(input->read_stream.buffer)) { + handle_raw_buffer(input, true); + } + tk_getkeys(input, true); + tinput_flush(input, true); } /// Handle focus events. @@ -365,19 +379,22 @@ static bool handle_focus_event(TermInput *input) return false; } -static bool handle_bracketed_paste(TermInput *input) +#define START_PASTE "\x1b[200~" +#define END_PASTE "\x1b[201~" +static HandleState handle_bracketed_paste(TermInput *input) { - if (rbuffer_size(input->read_stream.buffer) > 5 - && (!rbuffer_cmp(input->read_stream.buffer, "\x1b[200~", 6) - || !rbuffer_cmp(input->read_stream.buffer, "\x1b[201~", 6))) { + size_t buf_size = rbuffer_size(input->read_stream.buffer); + if (buf_size > 5 + && (!rbuffer_cmp(input->read_stream.buffer, START_PASTE, 6) + || !rbuffer_cmp(input->read_stream.buffer, END_PASTE, 6))) { bool enable = *rbuffer_get(input->read_stream.buffer, 4) == '0'; if (input->paste && enable) { - return false; // Pasting "start paste" code literally. + return kNotApplicable; // Pasting "start paste" code literally. } // Advance past the sequence rbuffer_consumed(input->read_stream.buffer, 6); if (!!input->paste == enable) { - return true; // Spurious "disable paste" code. + return kComplete; // Spurious "disable paste" code. } if (enable) { @@ -392,9 +409,15 @@ static bool handle_bracketed_paste(TermInput *input) // Paste phase: "disabled". input->paste = 0; } - return true; + return kComplete; + } else if (buf_size < 6 + && (!rbuffer_cmp(input->read_stream.buffer, START_PASTE, buf_size) + || !rbuffer_cmp(input->read_stream.buffer, + END_PASTE, buf_size))) { + // Wait for further input, as the sequence may be split. + return kIncomplete; } - return false; + return kNotApplicable; } // ESC NUL => <Esc> @@ -441,39 +464,47 @@ static void set_bg_deferred(void **argv) // ignored in the calculations. // // [1] https://en.wikipedia.org/wiki/Luma_%28video%29 -static bool handle_background_color(TermInput *input) +static HandleState handle_background_color(TermInput *input) { if (input->waiting_for_bg_response <= 0) { - return false; + return kNotApplicable; } size_t count = 0; size_t component = 0; size_t header_size = 0; size_t num_components = 0; + size_t buf_size = rbuffer_size(input->read_stream.buffer); uint16_t rgb[] = { 0, 0, 0 }; uint16_t rgb_max[] = { 0, 0, 0 }; bool eat_backslash = false; bool done = false; bool bad = false; - if (rbuffer_size(input->read_stream.buffer) >= 9 + if (buf_size >= 9 && !rbuffer_cmp(input->read_stream.buffer, "\x1b]11;rgb:", 9)) { header_size = 9; num_components = 3; - } else if (rbuffer_size(input->read_stream.buffer) >= 10 + } else if (buf_size >= 10 && !rbuffer_cmp(input->read_stream.buffer, "\x1b]11;rgba:", 10)) { header_size = 10; num_components = 4; + } else if (buf_size < 10 + && !rbuffer_cmp(input->read_stream.buffer, + "\x1b]11;rgba", buf_size)) { + // An incomplete sequence was found, waiting for the next input. + return kIncomplete; } else { input->waiting_for_bg_response--; if (input->waiting_for_bg_response == 0) { DLOG("did not get a response for terminal background query"); } - return false; + return kNotApplicable; } - input->waiting_for_bg_response = 0; - rbuffer_consumed(input->read_stream.buffer, header_size); RBUFFER_EACH(input->read_stream.buffer, c, i) { count = i + 1; + // Skip the header. + if (i < header_size) { + continue; + } if (eat_backslash) { done = true; break; @@ -495,8 +526,8 @@ static bool handle_background_color(TermInput *input) bad = true; } } - rbuffer_consumed(input->read_stream.buffer, count); if (done && !bad && rgb_max[0] && rgb_max[1] && rgb_max[2]) { + rbuffer_consumed(input->read_stream.buffer, count); double r = (double)rgb[0] / (double)rgb_max[0]; double g = (double)rgb[1] / (double)rgb_max[1]; double b = (double)rgb[2] / (double)rgb_max[2]; @@ -505,34 +536,41 @@ static bool handle_background_color(TermInput *input) DLOG("bg response: %s", bgvalue); loop_schedule_deferred(&main_loop, event_create(set_bg_deferred, 1, bgvalue)); + input->waiting_for_bg_response = 0; + } else if (!done && !bad) { + // An incomplete sequence was found, waiting for the next input. + return kIncomplete; } else { + input->waiting_for_bg_response = 0; + rbuffer_consumed(input->read_stream.buffer, count); DLOG("failed to parse bg response"); - return false; + return kNotApplicable; } - return true; + return kComplete; } #ifdef UNIT_TESTING -bool ut_handle_background_color(TermInput *input) +HandleState ut_handle_background_color(TermInput *input) { return handle_background_color(input); } #endif -static void tinput_read_cb(Stream *stream, RBuffer *buf, size_t count_, - void *data, bool eof) +static void handle_raw_buffer(TermInput *input, bool force) { - TermInput *input = data; - - if (eof) { - loop_schedule_fast(&main_loop, event_create(tinput_done_event, 0)); - return; - } + HandleState is_paste; + HandleState is_bc; do { - if (handle_focus_event(input) - || handle_bracketed_paste(input) - || handle_forced_escape(input) - || handle_background_color(input)) { + if (!force + && (handle_focus_event(input) + || (is_paste = handle_bracketed_paste(input)) != kNotApplicable + || handle_forced_escape(input) + || (is_bc = handle_background_color(input)) != kNotApplicable)) { + if (is_paste == kIncomplete || is_bc == kIncomplete) { + // Wait for the next input, leaving it in the raw buffer due to an + // incomplete sequence. + return; + } continue; } @@ -578,7 +616,34 @@ static void tinput_read_cb(Stream *stream, RBuffer *buf, size_t count_, } } } while (rbuffer_size(input->read_stream.buffer)); +} + +static void tinput_read_cb(Stream *stream, RBuffer *buf, size_t count_, + void *data, bool eof) +{ + TermInput *input = data; + + if (eof) { + loop_schedule_fast(&main_loop, event_create(tinput_done_event, 0)); + return; + } + + handle_raw_buffer(input, false); tinput_flush(input, true); + + // An incomplete sequence was found. Leave it in the raw buffer and wait for + // the next input. + if (rbuffer_size(input->read_stream.buffer)) { + // If 'ttimeout' is not set, start the timer with a timeout of 0 to process + // the next input. + long ms = input->ttimeout ? + (input->ttimeoutlen >= 0 ? input->ttimeoutlen : 0) : 0; + // Stop the current timer if already running + time_watcher_stop(&input->timer_handle); + time_watcher_start(&input->timer_handle, tinput_timer_cb, (uint32_t)ms, 0); + return; + } + // Make sure the next input escape sequence fits into the ring buffer without // wraparound, else it could be misinterpreted (because rbuffer_read_ptr() // exposes the underlying buffer to callers unaware of the wraparound). diff --git a/src/nvim/tui/input.h b/src/nvim/tui/input.h index b30546c815..ed76455189 100644 --- a/src/nvim/tui/input.h +++ b/src/nvim/tui/input.h @@ -32,7 +32,13 @@ typedef struct term_input { #endif #ifdef UNIT_TESTING -bool ut_handle_background_color(TermInput *input); +typedef enum { + kIncomplete = -1, + kNotApplicable = 0, + kComplete = 1, +} HandleState; + +HandleState ut_handle_background_color(TermInput *input); #endif #endif // NVIM_TUI_INPUT_H |