mirror of
https://github.com/weechat/weechat.git
synced 2026-07-03 16:23:14 +02:00
core: add support of terminal "bracketed paste mode" (task #11316)
This commit is contained in:
+10
-2
@@ -2393,6 +2393,12 @@ COMMAND_CALLBACK(input)
|
||||
gui_input_undo (buffer);
|
||||
else if (string_strcasecmp (argv[1], "redo") == 0)
|
||||
gui_input_redo (buffer);
|
||||
else if (string_strcasecmp (argv[1], "paste_start") == 0)
|
||||
gui_key_paste_bracketed_start ();
|
||||
else if (string_strcasecmp (argv[1], "paste_stop") == 0)
|
||||
{
|
||||
/* do nothing here */
|
||||
}
|
||||
}
|
||||
|
||||
return WEECHAT_RC_OK;
|
||||
@@ -5762,7 +5768,9 @@ command_init ()
|
||||
" switch_active_buffer: switch to next merged buffer\n"
|
||||
" switch_active_buffer_previous: switch to previous "
|
||||
"merged buffer\n"
|
||||
" insert: insert text in command line\n\n"
|
||||
" insert: insert text in command line\n"
|
||||
" paste_start: start paste (bracketed paste mode)\n"
|
||||
" paste_stop: stop paste (bracketed paste mode)\n\n"
|
||||
"This command is used by key bindings or plugins."),
|
||||
"return|complete_next|complete_previous|search_text|"
|
||||
"search_switch_case|search_previous|search_next|search_stop|"
|
||||
@@ -5778,7 +5786,7 @@ command_init ()
|
||||
"jump_next_visited_buffer|hotlist_clear|grab_key|"
|
||||
"grab_key_command|grab_mouse|grab_mouse_area|set_unread|"
|
||||
"set_unread_current_buffer|switch_active_buffer|"
|
||||
"switch_active_buffer_previous|insert",
|
||||
"switch_active_buffer_previous|insert|paste_start|paste_stop",
|
||||
&command_input, NULL);
|
||||
hook_command (NULL, "key",
|
||||
N_("bind/unbind keys"),
|
||||
|
||||
+28
-2
@@ -83,6 +83,7 @@ struct t_config_option *config_look_bar_more_left;
|
||||
struct t_config_option *config_look_bar_more_right;
|
||||
struct t_config_option *config_look_bar_more_up;
|
||||
struct t_config_option *config_look_bar_more_down;
|
||||
struct t_config_option *config_look_bracketed_paste_mode;
|
||||
struct t_config_option *config_look_buffer_notify_default;
|
||||
struct t_config_option *config_look_buffer_time_format;
|
||||
struct t_config_option *config_look_color_basic_force_bold;
|
||||
@@ -311,6 +312,22 @@ config_change_buffer_content (void *data, struct t_config_option *option)
|
||||
gui_current_window->refresh_needed = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* config_change_bracketed_paste_mode: called when bracketed paste mode is
|
||||
* changed
|
||||
*/
|
||||
|
||||
void
|
||||
config_change_bracketed_paste_mode (void *data, struct t_config_option *option)
|
||||
{
|
||||
/* make C compiler happy */
|
||||
(void) data;
|
||||
(void) option;
|
||||
|
||||
if (gui_ok)
|
||||
gui_window_set_bracketed_paste_mode (CONFIG_BOOLEAN(config_look_bracketed_paste_mode));
|
||||
}
|
||||
|
||||
/*
|
||||
* config_change_mouse: called when mouse state is changed
|
||||
*/
|
||||
@@ -1682,6 +1699,15 @@ config_weechat_init_options ()
|
||||
N_("string displayed when bar can be scrolled down "
|
||||
"(for bars with filling different from \"horizontal\")"),
|
||||
NULL, 0, 0, "++", NULL, 0, NULL, NULL, &config_change_buffer_content, NULL, NULL, NULL);
|
||||
config_look_bracketed_paste_mode = config_file_new_option (
|
||||
weechat_config_file, ptr_section,
|
||||
"bracketed_paste_mode", "boolean",
|
||||
N_("enable terminal \"bracketed paste mode\" (not supported in all "
|
||||
"terminals/multiplexers): in this mode, pasted text is bracketed "
|
||||
"with control sequences so that WeeChat can differentiate pasted "
|
||||
"text from typed-in text \"(ESC[200~\", followed by the pasted text, "
|
||||
"followed by \"ESC[201~\")"),
|
||||
NULL, 0, 0, "off", NULL, 0, NULL, NULL, &config_change_bracketed_paste_mode, NULL, NULL, NULL);
|
||||
config_look_buffer_notify_default = config_file_new_option (
|
||||
weechat_config_file, ptr_section,
|
||||
"buffer_notify_default", "integer",
|
||||
@@ -1960,8 +1986,8 @@ config_weechat_init_options ()
|
||||
weechat_config_file, ptr_section,
|
||||
"paste_max_lines", "integer",
|
||||
N_("max number of lines for paste without asking user "
|
||||
"(0 = disable this feature)"),
|
||||
NULL, 0, INT_MAX, "3", NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
"(-1 = disable this feature)"),
|
||||
NULL, -1, INT_MAX, "3", NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
config_look_prefix[GUI_CHAT_PREFIX_ERROR] = config_file_new_option (
|
||||
weechat_config_file, ptr_section,
|
||||
"prefix_error", "string",
|
||||
|
||||
@@ -108,6 +108,7 @@ extern struct t_config_option *config_look_bar_more_left;
|
||||
extern struct t_config_option *config_look_bar_more_right;
|
||||
extern struct t_config_option *config_look_bar_more_up;
|
||||
extern struct t_config_option *config_look_bar_more_down;
|
||||
extern struct t_config_option *config_look_bracketed_paste_mode;
|
||||
extern struct t_config_option *config_look_buffer_notify_default;
|
||||
extern struct t_config_option *config_look_buffer_time_format;
|
||||
extern struct t_config_option *config_look_command_chars;
|
||||
|
||||
@@ -208,6 +208,8 @@ gui_key_default_bindings (int context)
|
||||
BIND(/* m-> */ "meta->", "/input jump_next_visited_buffer");
|
||||
BIND(/* m-/ */ "meta-/", "/input jump_last_buffer_displayed");
|
||||
BIND(/* m-m */ "meta-m", "/mute mouse toggle");
|
||||
BIND(/* start paste */ "meta2-200~", "/input paste_start");
|
||||
BIND(/* end paste */ "meta2-201~", "/input paste_stop");
|
||||
|
||||
/* bind meta-j + {01..99} to switch to buffers # > 10 */
|
||||
for (i = 1; i < 100; i++)
|
||||
@@ -288,9 +290,9 @@ gui_key_default_bindings (int context)
|
||||
*/
|
||||
|
||||
void
|
||||
gui_key_flush ()
|
||||
gui_key_flush (int ignore_bracketed)
|
||||
{
|
||||
int i, key, insert_ok;
|
||||
int i, j, key, insert_ok;
|
||||
static char key_str[64] = { '\0' };
|
||||
static int length_key_str = 0;
|
||||
char key_temp[2], *key_utf, *input_old, *ptr_char, *next_char, *ptr_error;
|
||||
@@ -450,6 +452,36 @@ gui_key_flush ()
|
||||
else
|
||||
key_str[0] = '\0';
|
||||
length_key_str = strlen (key_str);
|
||||
|
||||
/*
|
||||
* bracketed paste detected (ESC[200~ + text + ESC[201~):
|
||||
* the ESC[200~ has been found and will be removed immediately,
|
||||
* the ESC[201~ should be found/removed later)
|
||||
*/
|
||||
if (gui_key_paste_bracketed)
|
||||
{
|
||||
gui_key_paste_bracketed = 0;
|
||||
|
||||
if (!ignore_bracketed)
|
||||
{
|
||||
/* check for large paste */
|
||||
if (gui_key_paste_check (1))
|
||||
{
|
||||
/*
|
||||
* paste mode has been enabled (ask to user what do to),
|
||||
* then remove the ESC[200~ from beginning of buffer,
|
||||
* stop reading buffer immediately and return
|
||||
*/
|
||||
i++;
|
||||
for (j = 0; j < gui_key_buffer_size - i; j++)
|
||||
{
|
||||
gui_key_buffer[j] = gui_key_buffer[j + i];
|
||||
}
|
||||
gui_key_buffer_size -= i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gui_key_buffer_reset ();
|
||||
@@ -463,7 +495,8 @@ gui_key_flush ()
|
||||
int
|
||||
gui_key_read_cb (void *data, int fd)
|
||||
{
|
||||
int ret, i, accept_paste, cancel_paste, text_added_to_buffer, paste_lines;
|
||||
int ret, i, accept_paste, cancel_paste, text_added_to_buffer;
|
||||
int ignore_bracketed_paste;
|
||||
unsigned char buffer[4096];
|
||||
|
||||
/* make C compiler happy */
|
||||
@@ -473,6 +506,7 @@ gui_key_read_cb (void *data, int fd)
|
||||
accept_paste = 0;
|
||||
cancel_paste = 0;
|
||||
text_added_to_buffer = 0;
|
||||
ignore_bracketed_paste = 0;
|
||||
|
||||
if (gui_key_paste_pending)
|
||||
{
|
||||
@@ -512,10 +546,13 @@ gui_key_read_cb (void *data, int fd)
|
||||
|
||||
for (i = 0; i < ret; i++)
|
||||
{
|
||||
/* add all chars (ignore a '\n' after a '\r') */
|
||||
/*
|
||||
* add all chars, but ignore a newline ('\r' or '\n') after
|
||||
* another one)
|
||||
*/
|
||||
if ((i == 0)
|
||||
|| (buffer[i] != '\n')
|
||||
|| (buffer[i - 1] != '\r'))
|
||||
|| ((buffer[i] != '\r') && (buffer[i] != '\n'))
|
||||
|| ((buffer[i - 1] != '\r') && (buffer[i - 1] != '\n')))
|
||||
{
|
||||
gui_key_buffer_add (buffer[i]);
|
||||
}
|
||||
@@ -528,7 +565,10 @@ gui_key_read_cb (void *data, int fd)
|
||||
{
|
||||
/* user is ok for pasting text, let's paste! */
|
||||
if (accept_paste)
|
||||
{
|
||||
gui_key_paste_accept ();
|
||||
ignore_bracketed_paste = 1;
|
||||
}
|
||||
/* user doesn't want to paste text: clear whole buffer! */
|
||||
else if (cancel_paste)
|
||||
gui_key_paste_cancel ();
|
||||
@@ -536,23 +576,9 @@ gui_key_read_cb (void *data, int fd)
|
||||
gui_input_text_changed_modifier_and_signal (gui_current_window->buffer, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* detect user paste or large amount of text
|
||||
* if so, ask user what to do
|
||||
*/
|
||||
if (CONFIG_INTEGER(config_look_paste_max_lines) > 0)
|
||||
{
|
||||
paste_lines = gui_key_get_paste_lines ();
|
||||
if (paste_lines > CONFIG_INTEGER(config_look_paste_max_lines))
|
||||
{
|
||||
gui_key_paste_pending = 1;
|
||||
gui_input_paste_pending_signal ();
|
||||
}
|
||||
}
|
||||
}
|
||||
gui_key_paste_check (0);
|
||||
|
||||
gui_key_flush ();
|
||||
gui_key_flush (ignore_bracketed_paste);
|
||||
|
||||
return WEECHAT_RC_OK;
|
||||
}
|
||||
|
||||
@@ -177,6 +177,8 @@ gui_main_init ()
|
||||
gui_mouse_enable ();
|
||||
else
|
||||
gui_mouse_disable ();
|
||||
|
||||
gui_window_set_bracketed_paste_mode (CONFIG_BOOLEAN(config_look_bracketed_paste_mode));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -401,6 +403,9 @@ gui_main_end (int clean_exit)
|
||||
gui_main_refreshs ();
|
||||
}
|
||||
|
||||
/* disable bracketed paste mode */
|
||||
gui_window_set_bracketed_paste_mode (0);
|
||||
|
||||
/* disable mouse */
|
||||
gui_mouse_disable ();
|
||||
|
||||
|
||||
@@ -2273,6 +2273,28 @@ gui_window_send_clipboard (const char *storage_unit, const char *text)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* gui_window_set_bracketed_paste_mode: enable/disable bracketed paste mode
|
||||
*/
|
||||
|
||||
void
|
||||
gui_window_set_bracketed_paste_mode (int enable)
|
||||
{
|
||||
char *envterm, *envtmux;
|
||||
int tmux, screen;
|
||||
|
||||
envtmux = getenv ("TMUX");
|
||||
tmux = (envtmux && envtmux[0]);
|
||||
|
||||
envterm = getenv ("TERM");
|
||||
screen = (envterm && (strncmp (envterm, "screen", 6) == 0) && !tmux);
|
||||
|
||||
fprintf (stderr, "%s\033[?2004%s%s",
|
||||
(screen) ? "\033P" : "",
|
||||
(enable) ? "h" : "l",
|
||||
(screen) ? "\033\\" : "");
|
||||
}
|
||||
|
||||
/*
|
||||
* gui_window_move_cursor: move cursor on screen (for cursor mode)
|
||||
*/
|
||||
|
||||
@@ -866,6 +866,18 @@ gui_window_send_clipboard (const char *storage_unit, const char *text)
|
||||
/* TODO: write this function for Gtk */
|
||||
}
|
||||
|
||||
/*
|
||||
* gui_window_set_bracketed_paste_mode: enable/disable bracketed paste mode
|
||||
*/
|
||||
|
||||
void
|
||||
gui_window_set_bracketed_paste_mode (int enable)
|
||||
{
|
||||
(void) enable;
|
||||
|
||||
/* TODO: write this function for Gtk */
|
||||
}
|
||||
|
||||
/*
|
||||
* gui_window_move_cursor: move cursor on screen (for cursor mode)
|
||||
*/
|
||||
|
||||
+10
-12
@@ -586,9 +586,8 @@ char *
|
||||
gui_bar_item_default_input_paste (void *data, struct t_gui_bar_item *item,
|
||||
struct t_gui_window *window)
|
||||
{
|
||||
char *text_paste_pending = N_("%sPaste %d lines ? [ctrl-Y] Yes [ctrl-N] No");
|
||||
char *ptr_message, *buf;
|
||||
int length;
|
||||
char buf[1024];
|
||||
int lines;
|
||||
|
||||
/* make C compiler happy */
|
||||
(void) data;
|
||||
@@ -600,15 +599,14 @@ gui_bar_item_default_input_paste (void *data, struct t_gui_bar_item *item,
|
||||
if (!gui_key_paste_pending)
|
||||
return NULL;
|
||||
|
||||
ptr_message = _(text_paste_pending);
|
||||
length = strlen (ptr_message) + 16 + 1;
|
||||
buf = malloc (length);
|
||||
if (buf)
|
||||
snprintf (buf, length, ptr_message,
|
||||
gui_color_get_custom (gui_color_get_name (CONFIG_COLOR(config_color_input_actions))),
|
||||
gui_key_get_paste_lines ());
|
||||
|
||||
return buf;
|
||||
lines = gui_key_get_paste_lines ();
|
||||
snprintf (buf, sizeof (buf),
|
||||
NG_("%sPaste %d line ? [ctrl-Y] Yes [ctrl-N] No",
|
||||
"%sPaste %d lines ? [ctrl-Y] Yes [ctrl-N] No",
|
||||
lines),
|
||||
gui_color_get_custom (gui_color_get_name (CONFIG_COLOR(config_color_input_actions))),
|
||||
lines);
|
||||
return strdup (buf);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+100
-6
@@ -32,6 +32,7 @@
|
||||
#include <time.h>
|
||||
|
||||
#include "../core/weechat.h"
|
||||
#include "../core/wee-config.h"
|
||||
#include "../core/wee-hashtable.h"
|
||||
#include "../core/wee-hdata.h"
|
||||
#include "../core/wee-hook.h"
|
||||
@@ -83,6 +84,7 @@ int gui_key_buffer_size = 0; /* input buffer size in bytes */
|
||||
|
||||
int gui_key_paste_pending = 0; /* 1 is big paste was detected and */
|
||||
/* WeeChat is asking user what to do */
|
||||
int gui_key_paste_bracketed = 0; /* bracketed paste mode detected */
|
||||
int gui_key_paste_lines = 0; /* number of lines for pending paste */
|
||||
|
||||
time_t gui_key_last_activity_time = 0; /* last activity time (key) */
|
||||
@@ -1396,6 +1398,50 @@ gui_key_buffer_add (unsigned char key)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* gui_key_paste_start: start paste of text
|
||||
*/
|
||||
|
||||
void
|
||||
gui_key_paste_start ()
|
||||
{
|
||||
/* remove the "ESC[201~" at the end of buffer (end of bracketed paste) */
|
||||
if ((gui_key_buffer_size >= 6)
|
||||
&& (gui_key_buffer[gui_key_buffer_size - 6] == '\x1B')
|
||||
&& (gui_key_buffer[gui_key_buffer_size- 5] == '[')
|
||||
&& (gui_key_buffer[gui_key_buffer_size - 4] == '2')
|
||||
&& (gui_key_buffer[gui_key_buffer_size - 3] == '0')
|
||||
&& (gui_key_buffer[gui_key_buffer_size - 2] == '1')
|
||||
&& (gui_key_buffer[gui_key_buffer_size - 1] == '~'))
|
||||
{
|
||||
gui_key_buffer_size -= 6;
|
||||
}
|
||||
|
||||
/* remove final newline if there is only one line to paste */
|
||||
if ((gui_key_paste_lines <= 1)
|
||||
&& (gui_key_buffer_size > 0)
|
||||
&& ((gui_key_buffer[gui_key_buffer_size - 1] == '\r')
|
||||
|| (gui_key_buffer[gui_key_buffer_size - 1] == '\n')))
|
||||
{
|
||||
gui_key_buffer_size--;
|
||||
gui_key_paste_lines = 0;
|
||||
}
|
||||
|
||||
gui_key_paste_pending = 1;
|
||||
gui_input_paste_pending_signal ();
|
||||
}
|
||||
|
||||
/*
|
||||
* gui_key_paste_bracket_start: start bracketed paste of text
|
||||
* (ESC[200~ detected)
|
||||
*/
|
||||
|
||||
void
|
||||
gui_key_paste_bracketed_start ()
|
||||
{
|
||||
gui_key_paste_bracketed = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* gui_key_get_paste_lines: return real number of lines in buffer
|
||||
* if last key is not Return, then this is lines + 1
|
||||
@@ -1405,14 +1451,58 @@ gui_key_buffer_add (unsigned char key)
|
||||
int
|
||||
gui_key_get_paste_lines ()
|
||||
{
|
||||
if ((gui_key_buffer_size > 0)
|
||||
&& (gui_key_buffer[gui_key_buffer_size - 1] != '\r')
|
||||
&& (gui_key_buffer[gui_key_buffer_size - 1] != '\n'))
|
||||
int length;
|
||||
|
||||
length = gui_key_buffer_size;
|
||||
|
||||
if ((length >= 6)
|
||||
&& (gui_key_buffer[length - 6] == '\x1B')
|
||||
&& (gui_key_buffer[length- 5] == '[')
|
||||
&& (gui_key_buffer[length - 4] == '2')
|
||||
&& (gui_key_buffer[length - 3] == '0')
|
||||
&& (gui_key_buffer[length - 2] == '1')
|
||||
&& (gui_key_buffer[length - 1] == '~'))
|
||||
{
|
||||
length -= 6;
|
||||
}
|
||||
|
||||
if ((length > 0)
|
||||
&& (gui_key_buffer[length - 1] != '\r')
|
||||
&& (gui_key_buffer[length - 1] != '\n'))
|
||||
{
|
||||
return gui_key_paste_lines + 1;
|
||||
}
|
||||
|
||||
return gui_key_paste_lines;
|
||||
return (gui_key_paste_lines > 0) ? gui_key_paste_lines : 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* gui_key_paste_check: check pasted lines: if more than N lines, then enable
|
||||
* paste mode and ask confirmation to user
|
||||
* (ctrl-Y=paste, ctrl-N=cancel)
|
||||
* (N is option weechat.look.paste_max_lines)
|
||||
* return 1 if paste mode has been enabled, 0 otherwise
|
||||
*/
|
||||
|
||||
int
|
||||
gui_key_paste_check (int bracketed_paste)
|
||||
{
|
||||
int max_lines;
|
||||
|
||||
max_lines = CONFIG_INTEGER(config_look_paste_max_lines);
|
||||
if (max_lines >= 0)
|
||||
{
|
||||
if (!bracketed_paste && (max_lines == 0))
|
||||
max_lines = 1;
|
||||
if (gui_key_get_paste_lines () > max_lines)
|
||||
{
|
||||
/* ask user what to do */
|
||||
gui_key_paste_start ();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1422,8 +1512,12 @@ gui_key_get_paste_lines ()
|
||||
void
|
||||
gui_key_paste_accept ()
|
||||
{
|
||||
/* add final '\n' if there is not in pasted text */
|
||||
if ((gui_key_buffer_size > 0)
|
||||
/*
|
||||
* add final newline if there is not in pasted text
|
||||
* (for at least 2 lines pasted)
|
||||
*/
|
||||
if ((gui_key_get_paste_lines () > 1)
|
||||
&& (gui_key_buffer_size > 0)
|
||||
&& (gui_key_buffer[gui_key_buffer_size - 1] != '\r')
|
||||
&& (gui_key_buffer[gui_key_buffer_size - 1] != '\n'))
|
||||
{
|
||||
|
||||
@@ -75,6 +75,7 @@ extern int gui_key_grab_count;
|
||||
extern int *gui_key_buffer;
|
||||
extern int gui_key_buffer_size;
|
||||
extern int gui_key_paste_pending;
|
||||
extern int gui_key_paste_bracketed;
|
||||
extern time_t gui_key_last_activity_time;
|
||||
|
||||
/* key functions */
|
||||
@@ -109,7 +110,10 @@ extern void gui_key_free_all (struct t_gui_key **keys,
|
||||
int *keys_count);
|
||||
extern void gui_key_buffer_reset ();
|
||||
extern void gui_key_buffer_add (unsigned char key);
|
||||
extern void gui_key_paste_start ();
|
||||
extern void gui_key_paste_bracketed_start ();
|
||||
extern int gui_key_get_paste_lines ();
|
||||
extern int gui_key_paste_check (int bracketed_paste);
|
||||
extern void gui_key_paste_accept ();
|
||||
extern void gui_key_paste_cancel ();
|
||||
extern void gui_key_end ();
|
||||
|
||||
@@ -240,6 +240,7 @@ extern void gui_window_refresh_screen (int full_refresh);
|
||||
extern void gui_window_set_title (const char *title);
|
||||
extern void gui_window_send_clipboard (const char *storage_unit,
|
||||
const char *text);
|
||||
extern void gui_window_set_bracketed_paste_mode (int enable);
|
||||
extern void gui_window_move_cursor ();
|
||||
extern void gui_window_term_display_infos ();
|
||||
extern void gui_window_objects_print_log (struct t_gui_window *window);
|
||||
|
||||
Reference in New Issue
Block a user