aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJustin M. Keyes <justinkz@gmail.com>2019-08-26 20:57:57 +0200
committerJustin M. Keyes <justinkz@gmail.com>2019-08-27 22:13:45 +0200
commited60015266356b3c0c42aa34698d9287f22fcba1 (patch)
treec1ae9ec1da22f94bce1157cd42a74112c2849746 /src
parent4344ac11119abd20ba911d72cf540321277dd150 (diff)
downloadrneovim-ed60015266356b3c0c42aa34698d9287f22fcba1.tar.gz
rneovim-ed60015266356b3c0c42aa34698d9287f22fcba1.tar.bz2
rneovim-ed60015266356b3c0c42aa34698d9287f22fcba1.zip
paste: handle vim.paste() failure
- Show error only once per "paste stream". - Drain remaining chunks until phase=3. - Lay groundwork for "cancel". - Constrain semantics of "cancel" to mean "client must stop"; it is unrelated to presence of error(s).
Diffstat (limited to 'src')
-rw-r--r--src/nvim/api/vim.c52
-rw-r--r--src/nvim/tui/input.c9
2 files changed, 38 insertions, 23 deletions
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 3631fbff66..910b76d02d 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -1206,7 +1206,7 @@ Dictionary nvim_get_namespaces(void)
return retval;
}
-/// Paste
+/// Pastes at cursor, in any mode.
///
/// Invokes the `vim.paste` handler, which handles each mode appropriately.
/// Sets redo/undo. Faster than |nvim_input()|.
@@ -1219,29 +1219,44 @@ Dictionary nvim_get_namespaces(void)
/// - 2: continues the paste (zero or more times)
/// - 3: ends the paste (exactly once)
/// @param[out] err Error details, if any
-/// @return true if paste should continue, false if paste was canceled
+/// @return
+/// - true: Client may continue pasting.
+/// - false: Client must cancel the paste.
Boolean nvim_paste(String data, Integer phase, Error *err)
FUNC_API_SINCE(6)
{
+ static bool draining = false;
+ bool cancel = false;
+
if (phase < -1 || phase > 3) {
api_set_error(err, kErrorTypeValidation, "Invalid phase: %"PRId64, phase);
return false;
}
- if (!(State & CMDLINE) && !(State & INSERT) && (phase == -1 || phase == 1)) {
- ResetRedobuff();
- AppendCharToRedobuff('a'); // Dot-repeat.
+ Array args = ARRAY_DICT_INIT;
+ Object rv = OBJECT_INIT;
+ if (phase == -1 || phase == 1) { // Start of paste-stream.
+ draining = false;
+ } else if (draining) {
+ // Skip remaining chunks. Report error only once per "stream".
+ goto theend;
}
Array lines = string_to_array(data);
- Array args = ARRAY_DICT_INIT;
ADD(args, ARRAY_OBJ(lines));
ADD(args, INTEGER_OBJ(phase));
- Object rv
- = nvim_execute_lua(STATIC_CSTR_AS_STRING("return vim._paste(...)"),
- args, err);
- // Abort paste if handler does not return true.
- bool ok = !ERROR_SET(err)
- && (rv.type == kObjectTypeBoolean && rv.data.boolean);
- if (ok && !(State & CMDLINE)) { // Dot-repeat.
+ rv = nvim_execute_lua(STATIC_CSTR_AS_STRING("return vim._paste(...)"), args,
+ err);
+ if (ERROR_SET(err)) {
+ draining = true;
+ goto theend;
+ }
+ if (!(State & CMDLINE) && !(State & INSERT) && (phase == -1 || phase == 1)) {
+ ResetRedobuff();
+ AppendCharToRedobuff('a'); // Dot-repeat.
+ }
+ // vim.paste() decides if client should cancel. Errors do NOT cancel: we
+ // want to drain remaining chunks (rather than divert them to main input).
+ cancel = (rv.type != kObjectTypeBoolean || !rv.data.boolean);
+ if (!cancel && !(State & CMDLINE)) { // Dot-repeat.
for (size_t i = 0; i < lines.size; i++) {
String s = lines.items[i].data.string;
assert(data.size <= INT_MAX);
@@ -1252,20 +1267,21 @@ Boolean nvim_paste(String data, Integer phase, Error *err)
}
}
}
- api_free_object(rv);
- api_free_array(args);
if (!(State & CMDLINE) && !(State & INSERT) && (phase == -1 || phase == 3)) {
AppendCharToRedobuff(ESC); // Dot-repeat.
}
- if (phase == -1 || phase == 3) {
+theend:
+ api_free_object(rv);
+ api_free_array(args);
+ if (cancel || phase == -1 || phase == 3) { // End of paste-stream.
// XXX: Tickle main loop to ensure cursor is updated.
loop_schedule_deferred(&main_loop, event_create(loop_dummy_event, 0));
}
- return ok;
+ return !cancel;
}
-/// Puts text at cursor.
+/// Puts text at cursor, in any mode.
///
/// Compare |:put| and |p| which are always linewise.
///
diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c
index f9f39c36ff..c74ef58ba1 100644
--- a/src/nvim/tui/input.c
+++ b/src/nvim/tui/input.c
@@ -107,13 +107,12 @@ static void tinput_wait_enqueue(void **argv)
const String keys = { .data = buf, .size = len };
if (input->paste) {
Error err = ERROR_INIT;
- Boolean rv = nvim_paste(keys, input->paste, &err);
- // Paste phase: "continue" (unless handler failed).
- input->paste = rv && !ERROR_SET(&err) ? 2 : 0;
+ // Paste phase: "continue" (unless handler canceled).
+ input->paste = !nvim_paste(keys, input->paste, &err)
+ ? 0 : (1 == input->paste ? 2 : input->paste);
rbuffer_consumed(input->key_buffer, len);
rbuffer_reset(input->key_buffer);
if (ERROR_SET(&err)) {
- msg_putchar('\n');
// TODO(justinmk): emsgf() does not display, why?
msg_printf_attr(HL_ATTR(HLF_E)|MSG_HIST, "paste: %s", err.msg);
api_clear_error(&err);
@@ -373,7 +372,7 @@ static bool handle_bracketed_paste(TermInput *input)
tinput_flush(input, true);
// Paste phase: "first-chunk".
input->paste = 1;
- } else if (input->paste != 0) {
+ } else if (input->paste) {
// Paste phase: "last-chunk".
input->paste = input->paste == 2 ? 3 : -1;
tinput_flush(input, true);