diff options
-rw-r--r-- | src/nvim/api/vim.c | 48 | ||||
-rw-r--r-- | src/nvim/ops.c | 69 |
2 files changed, 115 insertions, 2 deletions
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) diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 4f1709bb1f..ebf5c7a7bc 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -2732,7 +2732,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) * Using inserted text works differently, because the register includes * special characters (newlines, etc.). */ - if (regname == '.') { + if (regname == '.' && !reg) { bool non_linewise_vis = (VIsual_active && VIsual_mode != 'V'); // PUT_LINE has special handling below which means we use 'i' to start. @@ -2815,7 +2815,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) * For special registers '%' (file name), '#' (alternate file name) and * ':' (last command line), etc. we have to create a fake yank register. */ - if (get_spec_reg(regname, &insert_string, &allocated, true)) { + if (!reg && get_spec_reg(regname, &insert_string, &allocated, true)) { if (insert_string == NULL) { return; } @@ -5675,6 +5675,71 @@ end: return target; } +/// @param[out] reg Expected to be empty +bool prepare_yankreg_from_object(yankreg_T *reg, String regtype, size_t lines) +{ + if (regtype.size > 1) { + return false; + } + char type = regtype.data ? regtype.data[0] : NUL; + + switch (type) { + case 0: + reg->y_type = kMTUnknown; + break; + case 'v': case 'c': + reg->y_type = kMTCharWise; + break; + case 'V': case 'l': + reg->y_type = kMTLineWise; + break; + case 'b': case Ctrl_V: + reg->y_type = kMTBlockWise; + break; + default: + return false; + } + + reg->y_array = xcalloc(lines, sizeof(uint8_t *)); + reg->y_size = lines; + reg->additional_data = NULL; + reg->timestamp = 0; + return true; +} + +void finish_yankreg_from_object(yankreg_T *reg, bool clipboard_adjust) +{ + if (reg->y_size > 0 && strlen((char *)reg->y_array[reg->y_size-1]) == 0) { + // a known-to-be charwise yank might have a final linebreak + // but otherwise there is no line after the final newline + if (reg->y_type != kMTCharWise) { + if (reg->y_type == kMTUnknown || clipboard_adjust) { + xfree(reg->y_array[reg->y_size-1]); + reg->y_size--; + } + if (reg->y_type == kMTUnknown) { + reg->y_type = kMTLineWise; + } + } + } else { + if (reg->y_type == kMTUnknown) { + reg->y_type = kMTCharWise; + } + } + + if (reg->y_type == kMTBlockWise) { + size_t maxlen = 0; + for (size_t i = 0; i < reg->y_size; i++) { + size_t rowlen = STRLEN(reg->y_array[i]); + if (rowlen > maxlen) { + maxlen = rowlen; + } + } + assert(maxlen <= INT_MAX); + reg->y_width = (int)maxlen - 1; + } +} + static bool get_clipboard(int name, yankreg_T **target, bool quiet) { // show message on error |