From 72ac27e5a030b7c9207ecd00b8c2d092d28da644 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 8 Sep 2015 00:40:25 +0200 Subject: [PATCH 01/16] Implement the bracketed paste mode As an alternative method of paste detection, more reliable but might not be supported by all the VTs. --- src/fe-text/gui-readline.c | 42 +++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/fe-text/gui-readline.c b/src/fe-text/gui-readline.c index ec61e317..b8f3c1c7 100644 --- a/src/fe-text/gui-readline.c +++ b/src/fe-text/gui-readline.c @@ -37,6 +37,7 @@ #include "gui-windows.h" #include "utf8.h" +#include #include typedef void (*ENTRY_REDIRECT_KEY_FUNC) (int key, void *data, SERVER_REC *server, WI_ITEM_REC *item); @@ -65,6 +66,13 @@ static char *paste_old_prompt; static int paste_prompt, paste_line_count; static int paste_join_multiline; static int paste_timeout_id; +static int paste_bracketed_mode; + +/* Terminal sequences that surround the input when the terminal has the + * bracketed paste mode active. Fror more details see + * https://cirw.in/blog/bracketed-paste */ +static const unichar bp_start[] = { 0x1b, '[', '2', '0', '0', '~' }; +static const unichar bp_end[] = { 0x1b, '[', '2', '0', '1', '~' }; static void sig_input(void); @@ -647,12 +655,43 @@ static void sig_input(void) unichar key; term_gets(buffer, &line_count); key = g_array_index(buffer, unichar, 0); + /* Either Ctrl-k or Ctrl-c is pressed */ if (key == 11 || key == 3) paste_flush(key == 11); g_array_free(buffer, TRUE); } else { term_gets(paste_buffer, &paste_line_count); - if (paste_detect_time > 0 && paste_buffer->len >= 3) { + + /* use the bracketed paste mode to detect when the user has + * pasted some text into the field. */ + if (paste_buffer->len > 12) { + /* try to find the start/end sequence */ + int seq_start = memmem(paste_buffer->data, + paste_buffer->len * g_array_get_element_size(paste_buffer), + bp_start, sizeof(bp_start)) != NULL, + seq_end = memmem(paste_buffer->data, + paste_buffer->len * g_array_get_element_size(paste_buffer), + bp_end, sizeof(bp_end)) != NULL; + + g_warning("found sequences : start %d end %d", seq_start, seq_end); + + if (seq_start) { + paste_bracketed_mode = TRUE; + /* remove the leading sequence chars */ + memmove(paste_buffer->data, paste_buffer->data + sizeof(bp_start), + paste_buffer->len * g_array_get_element_size(paste_buffer) - sizeof(bp_start)); + g_array_set_size(paste_buffer, paste_buffer->len - 6); + } + + if (seq_end) { + paste_bracketed_mode = FALSE; + /* remove the trailing sequence chars */ + g_array_set_size(paste_buffer, paste_buffer->len - 6); + /* decide what to do with the buffer */ + paste_timeout(NULL); + } + } + else if (paste_detect_time > 0 && paste_buffer->len >= 3) { if (paste_timeout_id != -1) g_source_remove(paste_timeout_id); paste_timeout_id = g_timeout_add(paste_detect_time, paste_timeout, NULL); @@ -945,6 +984,7 @@ void gui_readline_init(void) paste_buffer = g_array_new(FALSE, FALSE, sizeof(unichar)); paste_old_prompt = NULL; paste_timeout_id = -1; + paste_bracketed_mode = FALSE; g_get_current_time(&last_keypress); input_listen_init(STDIN_FILENO); From f1eead7b4a7cdcae6d71dba6e97d38e9d34fcf95 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 8 Sep 2015 00:48:13 +0200 Subject: [PATCH 02/16] Toggles --- src/fe-text/gui-readline.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/fe-text/gui-readline.c b/src/fe-text/gui-readline.c index b8f3c1c7..90399711 100644 --- a/src/fe-text/gui-readline.c +++ b/src/fe-text/gui-readline.c @@ -66,6 +66,7 @@ static char *paste_old_prompt; static int paste_prompt, paste_line_count; static int paste_join_multiline; static int paste_timeout_id; +static int paste_use_bracketed_mode; static int paste_bracketed_mode; /* Terminal sequences that surround the input when the terminal has the @@ -662,11 +663,11 @@ static void sig_input(void) } else { term_gets(paste_buffer, &paste_line_count); - /* use the bracketed paste mode to detect when the user has - * pasted some text into the field. */ - if (paste_buffer->len > 12) { + /* use the bracketed paste mode to detect when the user pastes + * some text into the entry */ + if (paste_use_bracketed_mode != FALSE && paste_buffer->len > 12) { /* try to find the start/end sequence */ - int seq_start = memmem(paste_buffer->data, + int seq_start = memmem(paste_buffer->data, paste_buffer->len * g_array_get_element_size(paste_buffer), bp_start, sizeof(bp_start)) != NULL, seq_end = memmem(paste_buffer->data, @@ -678,7 +679,7 @@ static void sig_input(void) if (seq_start) { paste_bracketed_mode = TRUE; /* remove the leading sequence chars */ - memmove(paste_buffer->data, paste_buffer->data + sizeof(bp_start), + memmove(paste_buffer->data, paste_buffer->data + sizeof(bp_start), paste_buffer->len * g_array_get_element_size(paste_buffer) - sizeof(bp_start)); g_array_set_size(paste_buffer, paste_buffer->len - 6); } @@ -969,6 +970,7 @@ static void setup_changed(void) paste_verify_line_count = settings_get_int("paste_verify_line_count"); paste_join_multiline = settings_get_bool("paste_join_multiline"); + paste_use_bracketed_mode = settings_get_bool("paste_use_bracketed_mode"); } void gui_readline_init(void) @@ -990,6 +992,7 @@ void gui_readline_init(void) settings_add_str("history", "scroll_page_count", "/2"); settings_add_time("misc", "paste_detect_time", "5msecs"); + settings_add_bool("misc", "paste_use_bracketed_mode", FALSE); /* NOTE: function keys can generate at least 5 characters long keycodes. this must be larger to allow them to work. */ settings_add_int("misc", "paste_verify_line_count", 5); From 15dad291c7829c4ae7855880654b94d6edb4f17f Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 8 Sep 2015 00:55:34 +0200 Subject: [PATCH 03/16] Replace some hairy logic with g_array_remove_range In the hope it'll do the same under the hood. --- src/fe-text/gui-readline.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/fe-text/gui-readline.c b/src/fe-text/gui-readline.c index 90399711..c4c0064e 100644 --- a/src/fe-text/gui-readline.c +++ b/src/fe-text/gui-readline.c @@ -679,9 +679,7 @@ static void sig_input(void) if (seq_start) { paste_bracketed_mode = TRUE; /* remove the leading sequence chars */ - memmove(paste_buffer->data, paste_buffer->data + sizeof(bp_start), - paste_buffer->len * g_array_get_element_size(paste_buffer) - sizeof(bp_start)); - g_array_set_size(paste_buffer, paste_buffer->len - 6); + g_array_remove_range(paste_buffer, 0, 6); } if (seq_end) { From 4764b102ff274f4a8695ced2ff2ebb39bf8d7bc2 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Wed, 9 Sep 2015 22:35:11 +0200 Subject: [PATCH 04/16] Enable the bracketed paste mode on demand --- src/fe-text/gui-readline.c | 3 +++ src/fe-text/term-terminfo.c | 8 ++++++++ src/fe-text/term.h | 2 ++ 3 files changed, 13 insertions(+) diff --git a/src/fe-text/gui-readline.c b/src/fe-text/gui-readline.c index c4c0064e..91dec4ea 100644 --- a/src/fe-text/gui-readline.c +++ b/src/fe-text/gui-readline.c @@ -969,6 +969,9 @@ static void setup_changed(void) paste_verify_line_count = settings_get_int("paste_verify_line_count"); paste_join_multiline = settings_get_bool("paste_join_multiline"); paste_use_bracketed_mode = settings_get_bool("paste_use_bracketed_mode"); + + /* Enable the bracketed paste mode on demand */ + term_set_bracketed_paste_mode(paste_use_bracketed_mode); } void gui_readline_init(void) diff --git a/src/fe-text/term-terminfo.c b/src/fe-text/term-terminfo.c index ded79c28..9376bda8 100644 --- a/src/fe-text/term-terminfo.c +++ b/src/fe-text/term-terminfo.c @@ -689,3 +689,11 @@ void term_gets(GArray *buffer, int *line_count) } } } + +void term_set_bracketed_paste_mode(int enable) +{ + if (enable) + tputs("\e[?2004h", 0, term_putchar); + else + tputs("\e[?2004l", 0, term_putchar); +} diff --git a/src/fe-text/term.h b/src/fe-text/term.h index cdcc787a..692ce9c5 100644 --- a/src/fe-text/term.h +++ b/src/fe-text/term.h @@ -94,6 +94,8 @@ void term_refresh(TERM_WINDOW *window); void term_stop(void); +void term_set_bracketed_paste_mode(int enable); + /* keyboard input handling */ void term_set_input_type(int type); void term_gets(GArray *buffer, int *line_count); From 6888fc5fc74936af74fd30042e45652951648ea4 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Wed, 9 Sep 2015 22:41:17 +0200 Subject: [PATCH 05/16] Get rid of the non-portable memmem The sequences we're after are found at the beginning or at the end of the buffer, there's no need to scan the whole thing. --- src/fe-text/gui-readline.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/fe-text/gui-readline.c b/src/fe-text/gui-readline.c index 91dec4ea..87c824cf 100644 --- a/src/fe-text/gui-readline.c +++ b/src/fe-text/gui-readline.c @@ -666,15 +666,11 @@ static void sig_input(void) /* use the bracketed paste mode to detect when the user pastes * some text into the entry */ if (paste_use_bracketed_mode != FALSE && paste_buffer->len > 12) { - /* try to find the start/end sequence */ - int seq_start = memmem(paste_buffer->data, - paste_buffer->len * g_array_get_element_size(paste_buffer), - bp_start, sizeof(bp_start)) != NULL, - seq_end = memmem(paste_buffer->data, - paste_buffer->len * g_array_get_element_size(paste_buffer), - bp_end, sizeof(bp_end)) != NULL; - - g_warning("found sequences : start %d end %d", seq_start, seq_end); + /* try to find the start/end sequence, we know that we + * either find those at the start/end of the buffer or + * we don't find those at all. */ + int seq_start = !memcmp(paste_buffer->data, bp_start, sizeof(bp_start)), + seq_end = !memcmp(paste_buffer->data + paste_buffer->len * g_array_get_element_size(paste_buffer) - sizeof(bp_end), bp_end, sizeof(bp_end)); if (seq_start) { paste_bracketed_mode = TRUE; From 9a6b2dedcce165211892b39cf7455314dfde57fb Mon Sep 17 00:00:00 2001 From: dequis Date: Thu, 17 Sep 2015 00:52:55 -0300 Subject: [PATCH 06/16] Improve bracketed paste start/end detection - Use a keybinding to detect the start of a bracketed paste - Iterate over the paste buffer looking for the end marker --- src/fe-text/gui-readline.c | 50 +++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/src/fe-text/gui-readline.c b/src/fe-text/gui-readline.c index 87c824cf..51b9f758 100644 --- a/src/fe-text/gui-readline.c +++ b/src/fe-text/gui-readline.c @@ -665,37 +665,44 @@ static void sig_input(void) /* use the bracketed paste mode to detect when the user pastes * some text into the entry */ - if (paste_use_bracketed_mode != FALSE && paste_buffer->len > 12) { - /* try to find the start/end sequence, we know that we - * either find those at the start/end of the buffer or - * we don't find those at all. */ - int seq_start = !memcmp(paste_buffer->data, bp_start, sizeof(bp_start)), - seq_end = !memcmp(paste_buffer->data + paste_buffer->len * g_array_get_element_size(paste_buffer) - sizeof(bp_end), bp_end, sizeof(bp_end)); + if (paste_bracketed_mode) { + int i; + int len = paste_buffer->len - G_N_ELEMENTS(bp_end); + unichar *ptr = (unichar *) paste_buffer->data; - if (seq_start) { - paste_bracketed_mode = TRUE; - /* remove the leading sequence chars */ - g_array_remove_range(paste_buffer, 0, 6); + if (len <= 0) { + return; } - if (seq_end) { - paste_bracketed_mode = FALSE; - /* remove the trailing sequence chars */ - g_array_set_size(paste_buffer, paste_buffer->len - 6); - /* decide what to do with the buffer */ - paste_timeout(NULL); + for (i = 0; i <= len; i++, ptr++) { + if (ptr[0] == bp_end[0] && !memcmp(ptr, bp_end, sizeof(bp_end))) { + /* remove the trailing sequence chars */ + g_array_set_size(paste_buffer, i); + + /* decide what to do with the buffer */ + paste_timeout(NULL); + + paste_bracketed_mode = FALSE; + break; + } } } else if (paste_detect_time > 0 && paste_buffer->len >= 3) { if (paste_timeout_id != -1) g_source_remove(paste_timeout_id); paste_timeout_id = g_timeout_add(paste_detect_time, paste_timeout, NULL); - } else { + } else if (!paste_bracketed_mode) { int i; for (i = 0; i < paste_buffer->len; i++) { unichar key = g_array_index(paste_buffer, unichar, i); signal_emit("gui key pressed", 1, GINT_TO_POINTER(key)); + + if (paste_bracketed_mode) { + /* just enabled by the signal, remove what was processed so far */ + g_array_remove_range(paste_buffer, 0, i + 1); + return; + } } g_array_set_size(paste_buffer, 0); paste_line_count = 0; @@ -703,6 +710,11 @@ static void sig_input(void) } } +static void key_paste_start(void) +{ + paste_bracketed_mode = TRUE; +} + time_t get_idle_time(void) { return last_keypress.tv_sec; @@ -1060,6 +1072,8 @@ void gui_readline_init(void) key_bind("key", NULL, "meta2-5F", "cend", (SIGNAL_FUNC) key_combo); key_bind("key", NULL, "meta2-1;5F", "cend", (SIGNAL_FUNC) key_combo); + key_bind("paste_start", "Bracketed paste start", "meta2-200~", "paste_start", (SIGNAL_FUNC) key_paste_start); + /* cursor movement */ key_bind("backward_character", "Move the cursor a character backward", "left", NULL, (SIGNAL_FUNC) key_backward_character); key_bind("forward_character", "Move the cursor a character forward", "right", NULL, (SIGNAL_FUNC) key_forward_character); @@ -1155,6 +1169,8 @@ void gui_readline_deinit(void) key_configure_freeze(); + key_unbind("paste_start", (SIGNAL_FUNC) key_paste_start); + key_unbind("backward_character", (SIGNAL_FUNC) key_backward_character); key_unbind("forward_character", (SIGNAL_FUNC) key_forward_character); key_unbind("backward_word", (SIGNAL_FUNC) key_backward_word); From 52729ca3da6ca594d710f58f252ac6cd6952fab0 Mon Sep 17 00:00:00 2001 From: dequis Date: Thu, 17 Sep 2015 00:54:13 -0300 Subject: [PATCH 07/16] Save the part of the paste buffer after the bp_end marker for later Also move relevant code to a paste_bracketed_end() function --- src/fe-text/gui-readline.c | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/src/fe-text/gui-readline.c b/src/fe-text/gui-readline.c index 51b9f758..61cdad1a 100644 --- a/src/fe-text/gui-readline.c +++ b/src/fe-text/gui-readline.c @@ -61,6 +61,7 @@ static int paste_detect_time, paste_verify_line_count; static char *paste_entry; static int paste_entry_pos; static GArray *paste_buffer; +static GArray *paste_buffer_rest; static char *paste_old_prompt; static int paste_prompt, paste_line_count; @@ -331,6 +332,12 @@ static void paste_flush(int send) paste_send(); g_array_set_size(paste_buffer, 0); + /* re-add anything that may have been after the bracketed paste end */ + if (paste_buffer_rest->len) { + g_array_append_vals(paste_buffer, paste_buffer_rest->data, paste_buffer_rest->len); + g_array_set_size(paste_buffer_rest, 0); + } + gui_entry_set_prompt(active_entry, paste_old_prompt == NULL ? "" : paste_old_prompt); g_free(paste_old_prompt); paste_old_prompt = NULL; @@ -643,6 +650,26 @@ static gboolean paste_timeout(gpointer data) return FALSE; } +static void paste_bracketed_end(int i, gboolean rest) +{ + /* if there's stuff after the end bracket, save it for later */ + if (rest) { + unichar *start = ((unichar *) paste_buffer->data) + i + G_N_ELEMENTS(bp_end); + int len = paste_buffer->len - G_N_ELEMENTS(bp_end); + + g_array_set_size(paste_buffer_rest, 0); + g_array_append_vals(paste_buffer_rest, start, len); + } + + /* remove the rest, including the trailing sequence chars */ + g_array_set_size(paste_buffer, i); + + /* decide what to do with the buffer */ + paste_timeout(NULL); + + paste_bracketed_mode = FALSE; +} + static void sig_input(void) { if (!active_entry) { @@ -676,13 +703,7 @@ static void sig_input(void) for (i = 0; i <= len; i++, ptr++) { if (ptr[0] == bp_end[0] && !memcmp(ptr, bp_end, sizeof(bp_end))) { - /* remove the trailing sequence chars */ - g_array_set_size(paste_buffer, i); - - /* decide what to do with the buffer */ - paste_timeout(NULL); - - paste_bracketed_mode = FALSE; + paste_bracketed_end(i, i != len); break; } } @@ -993,6 +1014,7 @@ void gui_readline_init(void) paste_entry = NULL; paste_entry_pos = 0; paste_buffer = g_array_new(FALSE, FALSE, sizeof(unichar)); + paste_buffer_rest = g_array_new(FALSE, FALSE, sizeof(unichar)); paste_old_prompt = NULL; paste_timeout_id = -1; paste_bracketed_mode = FALSE; @@ -1228,6 +1250,7 @@ void gui_readline_deinit(void) key_unbind("stop_irc", (SIGNAL_FUNC) key_sig_stop); keyboard_destroy(keyboard); g_array_free(paste_buffer, TRUE); + g_array_free(paste_buffer_rest, TRUE); key_configure_thaw(); From 3b01198f0306676425f2bf90db2ca9c7179b014e Mon Sep 17 00:00:00 2001 From: dequis Date: Fri, 25 Sep 2015 01:37:06 -0300 Subject: [PATCH 08/16] paste_bracketed_end: Fix rest length calculation --- src/fe-text/gui-readline.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fe-text/gui-readline.c b/src/fe-text/gui-readline.c index 61cdad1a..8d571041 100644 --- a/src/fe-text/gui-readline.c +++ b/src/fe-text/gui-readline.c @@ -655,7 +655,7 @@ static void paste_bracketed_end(int i, gboolean rest) /* if there's stuff after the end bracket, save it for later */ if (rest) { unichar *start = ((unichar *) paste_buffer->data) + i + G_N_ELEMENTS(bp_end); - int len = paste_buffer->len - G_N_ELEMENTS(bp_end); + int len = paste_buffer->len - i - G_N_ELEMENTS(bp_end); g_array_set_size(paste_buffer_rest, 0); g_array_append_vals(paste_buffer_rest, start, len); From 79987d87f37f37ce07171ad4c6b94ffbab77b36d Mon Sep 17 00:00:00 2001 From: dequis Date: Fri, 25 Sep 2015 02:21:27 -0300 Subject: [PATCH 09/16] Send last line of bracketed paste together with the rest --- src/fe-text/gui-readline.c | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/fe-text/gui-readline.c b/src/fe-text/gui-readline.c index 8d571041..7f1ed9ca 100644 --- a/src/fe-text/gui-readline.c +++ b/src/fe-text/gui-readline.c @@ -69,6 +69,7 @@ static int paste_join_multiline; static int paste_timeout_id; static int paste_use_bracketed_mode; static int paste_bracketed_mode; +static int paste_was_bracketed_mode; /* Terminal sequences that surround the input when the terminal has the * bracketed paste mode active. Fror more details see @@ -258,9 +259,16 @@ static void paste_buffer_join_lines(GArray *buf) g_array_set_size(buf, dest - arr); } +static void paste_send_line(char *text) +{ + /* we need to get the current history every time because it might change between calls */ + command_history_add(command_history_current(active_win), text); + + signal_emit("send command", 3, text, active_win->active_server, active_win->active); +} + static void paste_send(void) { - HISTORY_REC *history; unichar *arr; GString *str; char out[10], *text; @@ -285,11 +293,7 @@ static void paste_send(void) } text = gui_entry_get_text(active_entry); - history = command_history_current(active_win); - command_history_add(history, text); - - signal_emit("send command", 3, text, - active_win->active_server, active_win->active); + paste_send_line(text); g_free(text); } @@ -297,12 +301,7 @@ static void paste_send(void) str = g_string_new(NULL); for (; i < paste_buffer->len; i++) { if (arr[i] == '\r' || arr[i] == '\n') { - history = command_history_current(active_win); - command_history_add(history, str->str); - - signal_emit("send command", 3, str->str, - active_win->active_server, - active_win->active); + paste_send_line(str->str); g_string_truncate(str, 0); } else if (active_entry->utf8) { out[g_unichar_to_utf8(arr[i], out)] = '\0'; @@ -316,7 +315,14 @@ static void paste_send(void) } } - gui_entry_set_text(active_entry, str->str); + if (paste_was_bracketed_mode) { + /* the text before the bracket end should be sent along with the rest */ + paste_send_line(str->str); + gui_entry_set_text(active_entry, ""); + } else { + gui_entry_set_text(active_entry, str->str); + } + g_string_free(str, TRUE); } @@ -632,6 +638,8 @@ static void key_delete_to_next_space(void) static gboolean paste_timeout(gpointer data) { + paste_was_bracketed_mode = paste_bracketed_mode; + if (paste_line_count == 0) { int i; From 83f9772e213b6b1e181338738c2997150162c871 Mon Sep 17 00:00:00 2001 From: dequis Date: Fri, 25 Sep 2015 02:23:38 -0300 Subject: [PATCH 10/16] Fix some minor style issues --- src/fe-text/gui-readline.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/fe-text/gui-readline.c b/src/fe-text/gui-readline.c index 7f1ed9ca..9b635139 100644 --- a/src/fe-text/gui-readline.c +++ b/src/fe-text/gui-readline.c @@ -710,13 +710,12 @@ static void sig_input(void) } for (i = 0; i <= len; i++, ptr++) { - if (ptr[0] == bp_end[0] && !memcmp(ptr, bp_end, sizeof(bp_end))) { + if (ptr[0] == bp_end[0] && memcmp(ptr, bp_end, sizeof(bp_end)) == 0) { paste_bracketed_end(i, i != len); break; } } - } - else if (paste_detect_time > 0 && paste_buffer->len >= 3) { + } else if (paste_detect_time > 0 && paste_buffer->len >= 3) { if (paste_timeout_id != -1) g_source_remove(paste_timeout_id); paste_timeout_id = g_timeout_add(paste_detect_time, paste_timeout, NULL); From 7d062a313add81584608f2b0abf086f2b73e8098 Mon Sep 17 00:00:00 2001 From: dequis Date: Fri, 25 Sep 2015 02:27:59 -0300 Subject: [PATCH 11/16] Create paste_bracketed_middle() function to handle small pastes "Small" as in ending in the same sig_input() call where they started --- src/fe-text/gui-readline.c | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/src/fe-text/gui-readline.c b/src/fe-text/gui-readline.c index 9b635139..5d859ede 100644 --- a/src/fe-text/gui-readline.c +++ b/src/fe-text/gui-readline.c @@ -678,6 +678,24 @@ static void paste_bracketed_end(int i, gboolean rest) paste_bracketed_mode = FALSE; } +static void paste_bracketed_middle() +{ + int i; + int len = paste_buffer->len - G_N_ELEMENTS(bp_end); + unichar *ptr = (unichar *) paste_buffer->data; + + if (len <= 0) { + return; + } + + for (i = 0; i <= len; i++, ptr++) { + if (ptr[0] == bp_end[0] && memcmp(ptr, bp_end, sizeof(bp_end)) == 0) { + paste_bracketed_end(i, i != len); + break; + } + } +} + static void sig_input(void) { if (!active_entry) { @@ -701,20 +719,8 @@ static void sig_input(void) /* use the bracketed paste mode to detect when the user pastes * some text into the entry */ if (paste_bracketed_mode) { - int i; - int len = paste_buffer->len - G_N_ELEMENTS(bp_end); - unichar *ptr = (unichar *) paste_buffer->data; + paste_bracketed_middle(); - if (len <= 0) { - return; - } - - for (i = 0; i <= len; i++, ptr++) { - if (ptr[0] == bp_end[0] && memcmp(ptr, bp_end, sizeof(bp_end)) == 0) { - paste_bracketed_end(i, i != len); - break; - } - } } else if (paste_detect_time > 0 && paste_buffer->len >= 3) { if (paste_timeout_id != -1) g_source_remove(paste_timeout_id); @@ -729,6 +735,9 @@ static void sig_input(void) if (paste_bracketed_mode) { /* just enabled by the signal, remove what was processed so far */ g_array_remove_range(paste_buffer, 0, i + 1); + + /* handle single-line / small pastes here */ + paste_bracketed_middle(); return; } } From c721d57688e8f5413df7c179eac54c315343fbb0 Mon Sep 17 00:00:00 2001 From: dequis Date: Fri, 25 Sep 2015 03:09:14 -0300 Subject: [PATCH 12/16] Handle a paste start marker right after an end one (ignore both) This actually workarounds a bug with the "st" terminal, for which i've already submitted a patch, but irssi needs to be able to handle it decently too. --- src/fe-text/gui-readline.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/fe-text/gui-readline.c b/src/fe-text/gui-readline.c index 5d859ede..4616e919 100644 --- a/src/fe-text/gui-readline.c +++ b/src/fe-text/gui-readline.c @@ -681,7 +681,8 @@ static void paste_bracketed_end(int i, gboolean rest) static void paste_bracketed_middle() { int i; - int len = paste_buffer->len - G_N_ELEMENTS(bp_end); + int marklen = G_N_ELEMENTS(bp_end); + int len = paste_buffer->len - marklen; unichar *ptr = (unichar *) paste_buffer->data; if (len <= 0) { @@ -690,6 +691,23 @@ static void paste_bracketed_middle() for (i = 0; i <= len; i++, ptr++) { if (ptr[0] == bp_end[0] && memcmp(ptr, bp_end, sizeof(bp_end)) == 0) { + + /* if there are at least 6 bytes after the end, + * check for another start marker right afterwards */ + if (i <= (len - marklen) && + memcmp(ptr + marklen, bp_start, sizeof(bp_start)) == 0) { + + /* remove both markers*/ + g_array_remove_range(paste_buffer, i, marklen * 2); + len -= marklen * 2; + + /* go one step back */ + if (i > 0) { + i--; + ptr--; + } + continue; + } paste_bracketed_end(i, i != len); break; } From 7866d2bcd6797b8279238ffb2f710bd9ea599cc8 Mon Sep 17 00:00:00 2001 From: dequis Date: Fri, 25 Sep 2015 03:22:02 -0300 Subject: [PATCH 13/16] Handle empty bracketed pastes (or sequences of those) Both cases were off-by-one mistakes erring on the side of being too conservative. This fixes these two harmless issues: - For a single empty paste, it required another keystroke before processing it - For a sequence of themcase, a single '~' was left in the input --- src/fe-text/gui-readline.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/fe-text/gui-readline.c b/src/fe-text/gui-readline.c index 4616e919..6e169b93 100644 --- a/src/fe-text/gui-readline.c +++ b/src/fe-text/gui-readline.c @@ -685,7 +685,7 @@ static void paste_bracketed_middle() int len = paste_buffer->len - marklen; unichar *ptr = (unichar *) paste_buffer->data; - if (len <= 0) { + if (len < 0) { return; } @@ -702,10 +702,8 @@ static void paste_bracketed_middle() len -= marklen * 2; /* go one step back */ - if (i > 0) { - i--; - ptr--; - } + i--; + ptr--; continue; } paste_bracketed_end(i, i != len); From 38d372eccb3745a7734b8f8963ae572d1343c5b1 Mon Sep 17 00:00:00 2001 From: dequis Date: Sat, 12 Dec 2015 01:17:48 -0300 Subject: [PATCH 14/16] Disable timeout-based paste detection if paste_use_bracketed_mode is on --- src/fe-text/gui-readline.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fe-text/gui-readline.c b/src/fe-text/gui-readline.c index 96b8b386..a9755318 100644 --- a/src/fe-text/gui-readline.c +++ b/src/fe-text/gui-readline.c @@ -737,7 +737,7 @@ static void sig_input(void) if (paste_bracketed_mode) { paste_bracketed_middle(); - } else if (paste_detect_time > 0 && paste_buffer->len >= 3) { + } else if (!paste_use_bracketed_mode && paste_detect_time > 0 && paste_buffer->len >= 3) { if (paste_timeout_id != -1) g_source_remove(paste_timeout_id); paste_timeout_id = g_timeout_add(paste_detect_time, paste_timeout, NULL); From e6fa311590da78d05e9eea42d3664cec01c9b1ae Mon Sep 17 00:00:00 2001 From: dequis Date: Sat, 12 Dec 2015 01:44:05 -0300 Subject: [PATCH 15/16] Bracketed paste: Adjust paste line count if there's text after newlines With bracketed paste, "a\nb" will result in two lines being pasted, because it's a single thing, with an end marker which the timeout based pastes don't have. Due to the way term_gets() counts lines, that input will have paste_line_count == 1. This can be misleading. This code adjusts it by looking at the last character, and increasing the count if it finds anything that isn't a newline. --- src/fe-text/gui-readline.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/fe-text/gui-readline.c b/src/fe-text/gui-readline.c index a9755318..851cbbe6 100644 --- a/src/fe-text/gui-readline.c +++ b/src/fe-text/gui-readline.c @@ -660,6 +660,8 @@ static gboolean paste_timeout(gpointer data) static void paste_bracketed_end(int i, gboolean rest) { + unichar last_char; + /* if there's stuff after the end bracket, save it for later */ if (rest) { unichar *start = ((unichar *) paste_buffer->data) + i + G_N_ELEMENTS(bp_end); @@ -672,6 +674,14 @@ static void paste_bracketed_end(int i, gboolean rest) /* remove the rest, including the trailing sequence chars */ g_array_set_size(paste_buffer, i); + last_char = g_array_index(paste_buffer, unichar, i - 1); + + if (paste_line_count > 0 && last_char != '\n' && last_char != '\r') { + /* there are newlines, but there's also stuff after the newline + * adjust line count to reflect this */ + paste_line_count++; + } + /* decide what to do with the buffer */ paste_timeout(NULL); From ce77842a985edc82aa1ca30c83c4edfe904872aa Mon Sep 17 00:00:00 2001 From: dequis Date: Sun, 13 Dec 2015 13:56:09 -0300 Subject: [PATCH 16/16] Bracketed paste: fix nitpick from ahf's review Thanks ahf --- src/fe-text/gui-readline.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fe-text/gui-readline.c b/src/fe-text/gui-readline.c index 851cbbe6..f531d282 100644 --- a/src/fe-text/gui-readline.c +++ b/src/fe-text/gui-readline.c @@ -339,7 +339,7 @@ static void paste_flush(int send) g_array_set_size(paste_buffer, 0); /* re-add anything that may have been after the bracketed paste end */ - if (paste_buffer_rest->len) { + if (paste_buffer_rest->len > 0) { g_array_append_vals(paste_buffer, paste_buffer_rest->data, paste_buffer_rest->len); g_array_set_size(paste_buffer_rest, 0); }