degesch: enable and use bracketed paste mode

urxvt, xterm and maybe others support quoting text pasted by the user
from clipboard, which prevents leading tabs from changing into
highlights.

The handling isn't perfect so far, just wrong in a different way, as
we mishandle newlines.
This commit is contained in:
Přemysl Eric Janouch 2015-11-15 01:03:29 +01:00
parent 465c2e4082
commit d8299a1231
1 changed files with 85 additions and 7 deletions

View File

@ -1367,9 +1367,13 @@ struct app_context
size_t nick_palette_len; ///< Number of entries in nick_palette
bool awaiting_mirc_escape; ///< Awaiting a mIRC attribute escape
// TODO: try to get rid of this in favor of "paste_buffer" -> "input_buffer"
char char_buf[MB_LEN_MAX + 1]; ///< Buffered multibyte char
size_t char_buf_len; ///< How much of an MB char is buffered
bool in_bracketed_paste; ///< User is pasting some content
struct str paste_buffer; ///< Buffered pasted content
bool running_backlog_helper; ///< Running a backlog helper
}
*g_ctx;
@ -1433,6 +1437,7 @@ app_context_init (struct app_context *self)
free (encoding);
input_init (&self->input);
str_init (&self->paste_buffer);
self->nick_palette =
filter_color_cube_for_acceptable_nick_colors (&self->nick_palette_len);
@ -1466,6 +1471,7 @@ app_context_free (struct app_context *self)
iconv_close (self->term_to_utf8);
input_free (&self->input);
str_free (&self->paste_buffer);
}
static void refresh_prompt (struct app_context *ctx);
@ -8602,6 +8608,7 @@ process_input (struct app_context *ctx, char *user_input)
if (!(input = iconv_xstrdup (ctx->term_to_utf8, user_input, -1, NULL)))
print_error ("character conversion failed for `%s'", "user input");
else
// TODO: split at newlines?
(void) process_input_utf8 (ctx, ctx->current_buffer, input, 0);
free (input);
}
@ -8973,6 +8980,13 @@ make_completions (struct app_context *ctx, char *line, int start, int end)
// --- Common code for user actions --------------------------------------------
static void
toggle_bracketed_paste (bool enable)
{
fprintf (stdout, "\x1b[?2004%c", "lh"[enable]);
fflush (stdout);
}
static void
suspend_terminal (struct app_context *ctx)
{
@ -8982,6 +8996,7 @@ suspend_terminal (struct app_context *ctx)
el_set (ctx->input.editline, EL_PREP_TERM, 0);
#endif
toggle_bracketed_paste (false);
input_hide (&ctx->input);
poller_fd_reset (&ctx->tty_event);
// TODO: also disable the date change timer
@ -8996,6 +9011,7 @@ resume_terminal (struct app_context *ctx)
el_set (ctx->input.editline, EL_PREP_TERM, 1);
#endif
toggle_bracketed_paste (true);
// In theory we could just print all unseen messages but this is safer
buffer_print_backlog (ctx, ctx->current_buffer);
// Now it's safe to process any user input
@ -9154,6 +9170,8 @@ bind_common_keys (struct app_context *ctx)
if (clear_screen)
input_bind_control (self, 'l', "redraw-screen");
input_bind (self, "\x1b[200~", "start-paste-mode");
}
// --- GNU Readline user actions -----------------------------------------------
@ -9236,6 +9254,17 @@ on_readline_insert_attribute (int count, int key)
return 0;
}
static int
on_readline_start_paste_mode (int count, int key)
{
(void) count;
(void) key;
struct app_context *ctx = g_ctx;
ctx->in_bracketed_paste = true;
return 0;
}
static int
on_readline_return (int count, int key)
{
@ -9309,6 +9338,7 @@ app_readline_init (void)
rl_add_defun ("display-full-log", on_readline_display_full_log, -1);
rl_add_defun ("redraw-screen", on_readline_redraw_screen, -1);
rl_add_defun ("insert-attribute", on_readline_insert_attribute, -1);
rl_add_defun ("start-paste-mode", on_readline_start_paste_mode, -1);
rl_add_defun ("send-line", on_readline_return, -1);
bind_common_keys (ctx);
@ -9407,6 +9437,16 @@ on_editline_insert_attribute (EditLine *editline, int key)
return CC_NORM;
}
static unsigned char
on_editline_start_paste_mode (EditLine *editline, int key)
{
(void) editline;
(void) key;
g_ctx->in_bracketed_paste = true;
return CC_NORM;
}
static unsigned char
on_editline_complete (EditLine *editline, int key)
{
@ -9506,6 +9546,7 @@ app_editline_init (struct input *self)
{ "display-full-log", "Show full log", on_editline_display_full_log },
{ "redraw-screen", "Redraw screen", on_editline_redraw_screen },
{ "insert-attribute", "mIRC formatting", on_editline_insert_attribute },
{ "start-paste-mode", "Bracketed paste", on_editline_start_paste_mode },
{ "send-line", "Send line", on_editline_return },
{ "complete", "Complete word", on_editline_complete },
};
@ -9886,6 +9927,43 @@ done:
ctx->awaiting_mirc_escape = false;
}
#define BRACKETED_PASTE_LIMIT 102400 ///< How much text can be pasted
static void
process_bracketed_paste (const struct pollfd *fd, struct app_context *ctx)
{
struct str *buf = &ctx->paste_buffer;
str_ensure_space (buf, 1);
if (read (fd->fd, buf->str + buf->len, 1) != 1)
goto error;
buf->str[++buf->len] = '\0';
static const char stop_mark[] = "\x1b[201~";
static const size_t stop_mark_len = sizeof stop_mark - 1;
if (buf->len < stop_mark_len)
return;
size_t text_len = buf->len - stop_mark_len;
if (memcmp (buf->str + text_len, stop_mark, stop_mark_len))
return;
// Avoid endless flooding of the buffer
if (text_len > BRACKETED_PASTE_LIMIT)
log_global_error (ctx, "Paste trimmed to %zu bytes",
(text_len = BRACKETED_PASTE_LIMIT));
buf->str[text_len] = '\0';
if (input_insert (&ctx->input, buf->str))
goto done;
error:
input_ding (&ctx->input);
log_global_error (ctx, "Paste failed");
done:
str_reset (buf);
ctx->in_bracketed_paste = false;
}
static void
on_tty_readable (const struct pollfd *fd, struct app_context *ctx)
{
@ -9895,14 +9973,12 @@ on_tty_readable (const struct pollfd *fd, struct app_context *ctx)
print_debug ("fd %d: unexpected revents: %d", fd->fd, fd->revents);
if (ctx->awaiting_mirc_escape)
{
process_mirc_escape (fd, ctx);
return;
}
// XXX: this may loop for a bit: stop the event or eat the input?
// (This prevents a segfault when the input has been stopped.)
if (ctx->input.active)
else if (ctx->in_bracketed_paste)
process_bracketed_paste (fd, ctx);
else if (ctx->input.active)
// XXX: this may loop for a bit: stop the event or eat the input?
// (This prevents a segfault when the input has been stopped.)
input_on_readable (&ctx->input);
}
@ -10053,6 +10129,7 @@ main (int argc, char *argv[])
// Initialize input so that we can switch to new buffers
refresh_prompt (&ctx);
input_start (&ctx.input, argv[0]);
toggle_bracketed_paste (true);
// Finally, we juice the configuration for some servers to create
load_servers (&ctx);
@ -10065,6 +10142,7 @@ main (int argc, char *argv[])
save_configuration (&ctx);
app_context_free (&ctx);
toggle_bracketed_paste (false);
free_terminal ();
return EXIT_SUCCESS;
}