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:
parent
465c2e4082
commit
d8299a1231
92
degesch.c
92
degesch.c
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue