From 4cc56905cb886327bcf2f0454a2164910dc5df3a Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sat, 27 May 2017 10:08:42 +0200 Subject: API: nvim_put #6819 --- src/nvim/api/vim.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) (limited to 'src/nvim/api/vim.c') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index d027eca59a..4c1f8dcc39 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -36,6 +36,7 @@ #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/fileio.h" +#include "nvim/ops.h" #include "nvim/option.h" #include "nvim/state.h" #include "nvim/syntax.h" @@ -1204,6 +1205,53 @@ Dictionary nvim_get_namespaces(void) return retval; } +/// @param lines contents. One empty line for no-op, zero lines to emulate error +/// @param type type ("c", "l", "b") or empty to guess from contents +/// @param name if emulates put from a register, otherwise empty +/// @param prev True to emulate "P" otherwise "p" +/// @param count repeat count +/// @param[out] err details of an error that have occurred, if any. +void nvim_put(ArrayOf(String) lines, String type, String regname, Boolean prev, Integer count, Error *err) + FUNC_API_SINCE(6) +{ + if (regname.size > 1) { + api_set_error(err, + kErrorTypeValidation, + "regname must be a single ASCII char or the empty string"); + return; + } + yankreg_T *reg = xcalloc(sizeof(yankreg_T), 1); + if (!prepare_yankreg_from_object(reg, type, lines.size)) { + api_set_error(err, + kErrorTypeValidation, + "Invalid regtype %s", + type.data); + return; + } + + for (size_t i = 0; i < lines.size; i++) { + if (lines.items[i].type != kObjectTypeString) { + api_set_error(err, + kErrorTypeValidation, + "All items in the lines array must be strings"); + goto cleanup; + } + String line = lines.items[i].data.string; + reg->y_array[i] = (char_u *)xmemdupz(line.data, line.size); + memchrsub(reg->y_array[i], NUL, NL, line.size); + } + + finish_yankreg_from_object(reg, false); + + int name = regname.size ? regname.data[0] : NUL; + do_put(name, reg, prev ? BACKWARD : FORWARD, (long)count, 0); + +cleanup: + free_register(reg); + xfree(reg); + +} + /// Subscribes to event broadcasts. /// /// @param channel_id Channel id (passed automatically by the dispatcher) -- cgit From 9e25a36467228bbcb8c6db88d2acb69cf7145af3 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sun, 28 May 2017 16:11:13 +0200 Subject: API: nvim_put #6819: try to fix Insert, Visual --- src/nvim/api/vim.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'src/nvim/api/vim.c') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 4c1f8dcc39..31ddfa57f1 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -29,6 +29,7 @@ #include "nvim/ex_docmd.h" #include "nvim/screen.h" #include "nvim/memline.h" +#include "nvim/mark.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/popupmnu.h" @@ -1244,7 +1245,16 @@ void nvim_put(ArrayOf(String) lines, String type, String regname, Boolean prev, finish_yankreg_from_object(reg, false); int name = regname.size ? regname.data[0] : NUL; - do_put(name, reg, prev ? BACKWARD : FORWARD, (long)count, 0); + bool VIsual_was_active = VIsual_active; + int flags = 0; + if (State & INSERT) { + flags |= PUT_CURSEND; + } else if (VIsual_active) { + // TODO: fix VIsual when cursor is before, or emulate the delete as well + flags |= lt(VIsual, curwin->w_cursor) ? PUT_CURSEND : 0; + } + do_put(name, reg, prev ? BACKWARD : FORWARD, (long)count, flags); + VIsual_active = VIsual_was_active; cleanup: free_register(reg); -- cgit From e1177be363f84f5f4f34c21b760bc47f70d5fa48 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 19 Aug 2019 23:43:19 +0200 Subject: API: nvim_put #6819 --- src/nvim/api/vim.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) (limited to 'src/nvim/api/vim.c') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 31ddfa57f1..bafb21bd4e 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1206,21 +1206,18 @@ Dictionary nvim_get_namespaces(void) return retval; } -/// @param lines contents. One empty line for no-op, zero lines to emulate error +/// Inserts text at cursor. +/// +/// Compare |:put| and |p| which are always linewise. +/// +/// @param lines contents /// @param type type ("c", "l", "b") or empty to guess from contents -/// @param name if emulates put from a register, otherwise empty -/// @param prev True to emulate "P" otherwise "p" -/// @param count repeat count -/// @param[out] err details of an error that have occurred, if any. -void nvim_put(ArrayOf(String) lines, String type, String regname, Boolean prev, Integer count, Error *err) +/// @param direction behave like |P| instead of |p| +/// @param[out] err Error details, if any +void nvim_put(ArrayOf(String) lines, String type, Boolean direction, + Error *err) FUNC_API_SINCE(6) { - if (regname.size > 1) { - api_set_error(err, - kErrorTypeValidation, - "regname must be a single ASCII char or the empty string"); - return; - } yankreg_T *reg = xcalloc(sizeof(yankreg_T), 1); if (!prepare_yankreg_from_object(reg, type, lines.size)) { api_set_error(err, @@ -1229,6 +1226,9 @@ void nvim_put(ArrayOf(String) lines, String type, String regname, Boolean prev, type.data); return; } + if (lines.size == 0) { + goto cleanup; // Nothing to do. + } for (size_t i = 0; i < lines.size; i++) { if (lines.items[i].type != kObjectTypeString) { @@ -1244,7 +1244,6 @@ void nvim_put(ArrayOf(String) lines, String type, String regname, Boolean prev, finish_yankreg_from_object(reg, false); - int name = regname.size ? regname.data[0] : NUL; bool VIsual_was_active = VIsual_active; int flags = 0; if (State & INSERT) { @@ -1253,13 +1252,12 @@ void nvim_put(ArrayOf(String) lines, String type, String regname, Boolean prev, // TODO: fix VIsual when cursor is before, or emulate the delete as well flags |= lt(VIsual, curwin->w_cursor) ? PUT_CURSEND : 0; } - do_put(name, reg, prev ? BACKWARD : FORWARD, (long)count, flags); + do_put(0, reg, direction ? BACKWARD : FORWARD, 1, flags); VIsual_active = VIsual_was_active; cleanup: free_register(reg); xfree(reg); - } /// Subscribes to event broadcasts. -- cgit From 0221a9220a2ec0691a7139c8362aba80d1f3b8ee Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 20 Aug 2019 19:41:45 +0200 Subject: paste: edge-case: handle EOL at end-of-buffer This is "readfile()-style", see also ":help channel-lines". --- src/nvim/api/vim.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'src/nvim/api/vim.c') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index bafb21bd4e..9ba855b61f 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1210,9 +1210,13 @@ Dictionary nvim_get_namespaces(void) /// /// Compare |:put| and |p| which are always linewise. /// -/// @param lines contents -/// @param type type ("c", "l", "b") or empty to guess from contents -/// @param direction behave like |P| instead of |p| +/// @param lines |readfile()|-style list of lines. |channel-lines| +/// @param type Edit behavior: +/// - "b" |blockwise-visual| mode +/// - "c" |characterwise| mode +/// - "l" |linewise| mode +/// - "" guess by contents +/// @param direction Behave like |P| instead of |p| /// @param[out] err Error details, if any void nvim_put(ArrayOf(String) lines, String type, Boolean direction, Error *err) -- cgit From 517bf99ddb79ca27b13491572a9439e982409abc Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 20 Aug 2019 20:03:21 +0200 Subject: API: nvim_put: Avoid "N more lines" message --- src/nvim/api/vim.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/nvim/api/vim.c') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 9ba855b61f..900c3bab58 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1256,7 +1256,9 @@ void nvim_put(ArrayOf(String) lines, String type, Boolean direction, // TODO: fix VIsual when cursor is before, or emulate the delete as well flags |= lt(VIsual, curwin->w_cursor) ? PUT_CURSEND : 0; } + msg_silent++; // Avoid "N more lines" message. do_put(0, reg, direction ? BACKWARD : FORWARD, 1, flags); + msg_silent--; VIsual_active = VIsual_was_active; cleanup: -- cgit From 613296936ba30ae73f3391c2e3c36096f3703c06 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 20 Aug 2019 22:41:21 +0200 Subject: API: nvim_put: always PUT_CURSEND Fixes strange behavior where sometimes the buffer contents of a series of paste chunks (vim._paste) would be out-of-order. Now the tui_spec.lua screen-tests are much more reliable. But they still sometimes fail because of off-by-one cursor (caused by "typeahead race" resulting in wrong mode; fixed later in this patch-series). --- src/nvim/api/vim.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'src/nvim/api/vim.c') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 900c3bab58..d3e368d01b 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1249,13 +1249,7 @@ void nvim_put(ArrayOf(String) lines, String type, Boolean direction, finish_yankreg_from_object(reg, false); bool VIsual_was_active = VIsual_active; - int flags = 0; - if (State & INSERT) { - flags |= PUT_CURSEND; - } else if (VIsual_active) { - // TODO: fix VIsual when cursor is before, or emulate the delete as well - flags |= lt(VIsual, curwin->w_cursor) ? PUT_CURSEND : 0; - } + int flags = PUT_CURSEND; msg_silent++; // Avoid "N more lines" message. do_put(0, reg, direction ? BACKWARD : FORWARD, 1, flags); msg_silent--; -- cgit From 93e5f0235b8e85423d0284231661ba4b0d7caa07 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 20 Aug 2019 23:53:13 +0200 Subject: API: nvim_put: "follow" parameter --- src/nvim/api/vim.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'src/nvim/api/vim.c') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index d3e368d01b..02000907f9 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1206,7 +1206,7 @@ Dictionary nvim_get_namespaces(void) return retval; } -/// Inserts text at cursor. +/// Puts text at cursor. /// /// Compare |:put| and |p| which are always linewise. /// @@ -1216,10 +1216,11 @@ Dictionary nvim_get_namespaces(void) /// - "c" |characterwise| mode /// - "l" |linewise| mode /// - "" guess by contents -/// @param direction Behave like |P| instead of |p| +/// @param after Insert after cursor (like |p|), or before (like |P|). +/// @param follow Place cursor at end of inserted text. /// @param[out] err Error details, if any -void nvim_put(ArrayOf(String) lines, String type, Boolean direction, - Error *err) +void nvim_put(ArrayOf(String) lines, String type, Boolean after, + Boolean follow, Error *err) FUNC_API_SINCE(6) { yankreg_T *reg = xcalloc(sizeof(yankreg_T), 1); @@ -1249,9 +1250,10 @@ void nvim_put(ArrayOf(String) lines, String type, Boolean direction, finish_yankreg_from_object(reg, false); bool VIsual_was_active = VIsual_active; - int flags = PUT_CURSEND; msg_silent++; // Avoid "N more lines" message. - do_put(0, reg, direction ? BACKWARD : FORWARD, 1, flags); + do_put(0, reg, + after ? FORWARD : BACKWARD, 1, + follow ? PUT_CURSEND : 0); msg_silent--; VIsual_active = VIsual_was_active; -- cgit From eacc70fb3ebae6d76112ab10647a42339f5f223f Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 24 Aug 2019 13:54:27 +0200 Subject: API: nvim_paste --- src/nvim/api/vim.c | 48 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 8 deletions(-) (limited to 'src/nvim/api/vim.c') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 02000907f9..b355491dcc 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1206,6 +1206,42 @@ Dictionary nvim_get_namespaces(void) return retval; } +/// Paste +/// +/// Invokes the `vim.paste` handler, which handles each mode appropriately. +/// Sets redo/undo. Faster than |nvim_input()|. +/// +/// @param data Multiline input. May be binary (containing NUL bytes). +/// @param phase Pass -1 to paste as one big buffer (i.e. without streaming). +/// To "stream" a paste, call `nvim_paste` sequentially with +/// these `phase` values: +/// - 1: starts the paste (exactly once) +/// - 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 +Boolean nvim_paste(String data, Integer phase, Error *err) + FUNC_API_SINCE(6) +{ + if (phase < -1 || phase > 3) { + api_set_error(err, kErrorTypeValidation, "Invalid phase: %"PRId64, phase); + return false; + } + Array args = ARRAY_DICT_INIT; + ADD(args, ARRAY_OBJ(string_to_array(data))); + 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); + api_free_object(rv); + api_free_array(args); + + return ok; +} + /// Puts text at cursor. /// /// Compare |:put| and |p| which are always linewise. @@ -1225,11 +1261,8 @@ void nvim_put(ArrayOf(String) lines, String type, Boolean after, { yankreg_T *reg = xcalloc(sizeof(yankreg_T), 1); if (!prepare_yankreg_from_object(reg, type, lines.size)) { - api_set_error(err, - kErrorTypeValidation, - "Invalid regtype %s", - type.data); - return; + api_set_error(err, kErrorTypeValidation, "Invalid type: '%s'", type.data); + goto cleanup; } if (lines.size == 0) { goto cleanup; // Nothing to do. @@ -1237,9 +1270,8 @@ void nvim_put(ArrayOf(String) lines, String type, Boolean after, for (size_t i = 0; i < lines.size; i++) { if (lines.items[i].type != kObjectTypeString) { - api_set_error(err, - kErrorTypeValidation, - "All items in the lines array must be strings"); + api_set_error(err, kErrorTypeValidation, + "Invalid lines (expected array of strings)"); goto cleanup; } String line = lines.items[i].data.string; -- cgit From 5b41070c639f979023178042bea8e5fcc8a898fe Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 25 Aug 2019 10:20:38 +0200 Subject: paste: implement redo (AKA dot-repeat) - Normal-mode redo idiom(?): prepend "i" and append ESC. - Insert-mode only needs AppendToRedobuffLit(). - Cmdline-mode: only paste the first line. --- src/nvim/api/vim.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) (limited to 'src/nvim/api/vim.c') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index b355491dcc..6d6fd85266 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1212,7 +1212,7 @@ Dictionary nvim_get_namespaces(void) /// Sets redo/undo. Faster than |nvim_input()|. /// /// @param data Multiline input. May be binary (containing NUL bytes). -/// @param phase Pass -1 to paste as one big buffer (i.e. without streaming). +/// @param phase -1: paste in a single call (i.e. without streaming). /// To "stream" a paste, call `nvim_paste` sequentially with /// these `phase` values: /// - 1: starts the paste (exactly once) @@ -1227,8 +1227,13 @@ Boolean nvim_paste(String data, Integer phase, Error *err) 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 lines = string_to_array(data); Array args = ARRAY_DICT_INIT; - ADD(args, ARRAY_OBJ(string_to_array(data))); + ADD(args, ARRAY_OBJ(lines)); ADD(args, INTEGER_OBJ(phase)); Object rv = nvim_execute_lua(STATIC_CSTR_AS_STRING("return vim._paste(...)"), @@ -1236,6 +1241,20 @@ Boolean nvim_paste(String data, Integer phase, Error *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. + for (size_t i = 0; i < lines.size; i++) { + String s = lines.items[i].data.string; + assert(data.size <= INT_MAX); + AppendToRedobuffLit((char_u *)s.data, (int)s.size); + // readfile()-style: "\n" is indicated by presence of N+1 item. + if (i + 1 < lines.size) { + AppendCharToRedobuff(NL); + } + } + } + if (!(State & CMDLINE) && !(State & INSERT) && (phase == -1 || phase == 3)) { + AppendCharToRedobuff(ESC); // Dot-repeat. + } api_free_object(rv); api_free_array(args); -- cgit From 4344ac11119abd20ba911d72cf540321277dd150 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 26 Aug 2019 03:15:09 +0200 Subject: paste: tickle cursor HACK: The cursor does not get repositioned after the paste completes. Scheduling a dummy event seems to fix it. Test case: 0. Revert this commit. 1. Paste some text in Normal-mode. 2. Notice the cursor is still in the cmdline area. --- src/nvim/api/vim.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src/nvim/api/vim.c') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 6d6fd85266..3631fbff66 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1252,11 +1252,15 @@ 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. } - api_free_object(rv); - api_free_array(args); + if (phase == -1 || phase == 3) { + // XXX: Tickle main loop to ensure cursor is updated. + loop_schedule_deferred(&main_loop, event_create(loop_dummy_event, 0)); + } return ok; } -- cgit From ed60015266356b3c0c42aa34698d9287f22fcba1 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 26 Aug 2019 20:57:57 +0200 Subject: 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). --- src/nvim/api/vim.c | 52 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 18 deletions(-) (limited to 'src/nvim/api/vim.c') 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. /// -- cgit From 87389c6a57cf9fa91746503c479cdbea348030b9 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 27 Aug 2019 05:19:25 +0200 Subject: paste: make vim.paste() "public" --- src/nvim/api/vim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/api/vim.c') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 910b76d02d..4f132ddbae 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1243,7 +1243,7 @@ Boolean nvim_paste(String data, Integer phase, Error *err) Array lines = string_to_array(data); ADD(args, ARRAY_OBJ(lines)); ADD(args, INTEGER_OBJ(phase)); - rv = nvim_execute_lua(STATIC_CSTR_AS_STRING("return vim._paste(...)"), args, + rv = nvim_execute_lua(STATIC_CSTR_AS_STRING("return vim.paste(...)"), args, err); if (ERROR_SET(err)) { draining = true; -- cgit From 46aa254bf30d567bd2da4fbfab33bbdcbb111a37 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 27 Aug 2019 05:19:32 +0200 Subject: paste: handle 'nomodifiable' - nvim_paste(): Marshal through luaeval() instead of nvim_execute_lua() because the latter seems to hide some errors. - Handle 'nomodifiable' in `nvim_put()` explicitly. - Require explicit `false` from `vim.paste()` in order to "cancel", otherwise assume true ("continue"). --- src/nvim/api/vim.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'src/nvim/api/vim.c') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 4f132ddbae..bf73a721fb 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1211,6 +1211,11 @@ Dictionary nvim_get_namespaces(void) /// Invokes the `vim.paste` handler, which handles each mode appropriately. /// Sets redo/undo. Faster than |nvim_input()|. /// +/// Errors ('nomodifiable', `vim.paste()` failure, …) are reflected in `err` +/// but do not affect the return value (which is strictly decided by +/// `vim.paste()`). On error, subsequent calls are ignored ("drained") until +/// the next paste is initiated (phase 1 or -1). +/// /// @param data Multiline input. May be binary (containing NUL bytes). /// @param phase -1: paste in a single call (i.e. without streaming). /// To "stream" a paste, call `nvim_paste` sequentially with @@ -1233,6 +1238,7 @@ Boolean nvim_paste(String data, Integer phase, Error *err) return false; } Array args = ARRAY_DICT_INIT; + Array args2 = ARRAY_DICT_INIT; Object rv = OBJECT_INIT; if (phase == -1 || phase == 1) { // Start of paste-stream. draining = false; @@ -1243,8 +1249,9 @@ Boolean nvim_paste(String data, Integer phase, Error *err) Array lines = string_to_array(data); ADD(args, ARRAY_OBJ(lines)); ADD(args, INTEGER_OBJ(phase)); - rv = nvim_execute_lua(STATIC_CSTR_AS_STRING("return vim.paste(...)"), args, - err); + ADD(args2, STRING_OBJ(cstr_to_string("vim.paste(_A[1], _A[2])"))); + ADD(args2, ARRAY_OBJ(args2)); + rv = nvim_call_function(STATIC_CSTR_AS_STRING("luaeval"), args2, err); if (ERROR_SET(err)) { draining = true; goto theend; @@ -1255,7 +1262,7 @@ Boolean nvim_paste(String data, Integer phase, Error *err) } // 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); + 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; @@ -1271,9 +1278,9 @@ Boolean nvim_paste(String data, Integer phase, Error *err) AppendCharToRedobuff(ESC); // Dot-repeat. } theend: - api_free_object(rv); - api_free_array(args); + api_free_array(args2); if (cancel || phase == -1 || phase == 3) { // End of paste-stream. + draining = false; // XXX: Tickle main loop to ensure cursor is updated. loop_schedule_deferred(&main_loop, event_create(loop_dummy_event, 0)); } @@ -1303,6 +1310,10 @@ void nvim_put(ArrayOf(String) lines, String type, Boolean after, api_set_error(err, kErrorTypeValidation, "Invalid type: '%s'", type.data); goto cleanup; } + if (!MODIFIABLE(curbuf)) { + api_set_error(err, kErrorTypeException, "Buffer is not 'modifiable'"); + goto cleanup; + } if (lines.size == 0) { goto cleanup; // Nothing to do. } -- cgit From 3157baed83b7e94f2ff92e6fd97e85dab41a1c94 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 27 Aug 2019 05:19:36 +0200 Subject: API: TRY_WRAP() for "abort-causing non-exception errors" - Introduce TRY_WRAP() until we have an *architectural* solution. - TODO: bfredl idea: prepare error-handling at "top level" (nv_event). - nvim_paste(): Revert luaeval() hack (see parent commit). - With TRY_WRAP() in nvim_put(), 'nomodifiable' error now correctly "bubbles up". --- src/nvim/api/vim.c | 63 +++++++++++++++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 32 deletions(-) (limited to 'src/nvim/api/vim.c') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index bf73a721fb..1ca0d8789d 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -54,6 +54,20 @@ # include "api/vim.c.generated.h" #endif +// `msg_list` controls the collection of abort-causing non-exception errors, +// which would otherwise be ignored. This pattern is from do_cmdline(). +// +// TODO(bfredl): prepare error-handling at "top level" (nv_event). +#define TRY_WRAP(code) \ + do { \ + struct msglist **saved_msg_list = msg_list; \ + struct msglist *private_msg_list; \ + msg_list = &private_msg_list; \ + private_msg_list = NULL; \ + code \ + msg_list = saved_msg_list; /* Restore the exception context. */ \ + } while (0) + void api_vim_init(void) FUNC_API_NOEXPORT { @@ -392,13 +406,7 @@ Object nvim_eval(String expr, Error *err) static int recursive = 0; // recursion depth Object rv = OBJECT_INIT; - // `msg_list` controls the collection of abort-causing non-exception errors, - // which would otherwise be ignored. This pattern is from do_cmdline(). - struct msglist **saved_msg_list = msg_list; - struct msglist *private_msg_list; - msg_list = &private_msg_list; - private_msg_list = NULL; - + TRY_WRAP({ // Initialize `force_abort` and `suppress_errthrow` at the top level. if (!recursive) { force_abort = false; @@ -423,8 +431,8 @@ Object nvim_eval(String expr, Error *err) } tv_clear(&rettv); - msg_list = saved_msg_list; // Restore the exception context. recursive--; + }); return rv; } @@ -474,13 +482,7 @@ static Object _call_function(String fn, Array args, dict_T *self, Error *err) } } - // `msg_list` controls the collection of abort-causing non-exception errors, - // which would otherwise be ignored. This pattern is from do_cmdline(). - struct msglist **saved_msg_list = msg_list; - struct msglist *private_msg_list; - msg_list = &private_msg_list; - private_msg_list = NULL; - + TRY_WRAP({ // Initialize `force_abort` and `suppress_errthrow` at the top level. if (!recursive) { force_abort = false; @@ -502,8 +504,8 @@ static Object _call_function(String fn, Array args, dict_T *self, Error *err) rv = vim_to_object(&rettv); } tv_clear(&rettv); - msg_list = saved_msg_list; // Restore the exception context. recursive--; + }); free_vim_args: while (i > 0) { @@ -1238,7 +1240,6 @@ Boolean nvim_paste(String data, Integer phase, Error *err) return false; } Array args = ARRAY_DICT_INIT; - Array args2 = ARRAY_DICT_INIT; Object rv = OBJECT_INIT; if (phase == -1 || phase == 1) { // Start of paste-stream. draining = false; @@ -1249,9 +1250,8 @@ Boolean nvim_paste(String data, Integer phase, Error *err) Array lines = string_to_array(data); ADD(args, ARRAY_OBJ(lines)); ADD(args, INTEGER_OBJ(phase)); - ADD(args2, STRING_OBJ(cstr_to_string("vim.paste(_A[1], _A[2])"))); - ADD(args2, ARRAY_OBJ(args2)); - rv = nvim_call_function(STATIC_CSTR_AS_STRING("luaeval"), args2, err); + rv = nvim_execute_lua(STATIC_CSTR_AS_STRING("return vim.paste(...)"), args, + err); if (ERROR_SET(err)) { draining = true; goto theend; @@ -1278,7 +1278,8 @@ Boolean nvim_paste(String data, Integer phase, Error *err) AppendCharToRedobuff(ESC); // Dot-repeat. } theend: - api_free_array(args2); + api_free_object(rv); + api_free_array(args); if (cancel || phase == -1 || phase == 3) { // End of paste-stream. draining = false; // XXX: Tickle main loop to ensure cursor is updated. @@ -1310,10 +1311,6 @@ void nvim_put(ArrayOf(String) lines, String type, Boolean after, api_set_error(err, kErrorTypeValidation, "Invalid type: '%s'", type.data); goto cleanup; } - if (!MODIFIABLE(curbuf)) { - api_set_error(err, kErrorTypeException, "Buffer is not 'modifiable'"); - goto cleanup; - } if (lines.size == 0) { goto cleanup; // Nothing to do. } @@ -1331,13 +1328,15 @@ void nvim_put(ArrayOf(String) lines, String type, Boolean after, finish_yankreg_from_object(reg, false); - bool VIsual_was_active = VIsual_active; - msg_silent++; // Avoid "N more lines" message. - do_put(0, reg, - after ? FORWARD : BACKWARD, 1, - follow ? PUT_CURSEND : 0); - msg_silent--; - VIsual_active = VIsual_was_active; + TRY_WRAP({ + try_start(); + bool VIsual_was_active = VIsual_active; + msg_silent++; // Avoid "N more lines" message. + do_put(0, reg, after ? FORWARD : BACKWARD, 1, follow ? PUT_CURSEND : 0); + msg_silent--; + VIsual_active = VIsual_was_active; + try_end(err); + }); cleanup: free_register(reg); -- cgit