aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/api
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/api')
-rw-r--r--src/nvim/api/buffer.c368
-rw-r--r--src/nvim/api/private/helpers.c126
-rw-r--r--src/nvim/api/private/helpers.h14
-rw-r--r--src/nvim/api/vim.c167
4 files changed, 585 insertions, 90 deletions
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index 3e1209d1b1..8f5718d97e 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -23,7 +23,10 @@
#include "nvim/memory.h"
#include "nvim/misc1.h"
#include "nvim/ex_cmds.h"
+#include "nvim/map_defs.h"
+#include "nvim/map.h"
#include "nvim/mark.h"
+#include "nvim/mark_extended.h"
#include "nvim/fileio.h"
#include "nvim/move.h"
#include "nvim/syntax.h"
@@ -101,25 +104,47 @@ String buffer_get_line(Buffer buffer, Integer index, Error *err)
return rv;
}
-/// Activates buffer-update events on a channel, or as lua callbacks.
+/// Activates buffer-update events on a channel, or as Lua callbacks.
+///
+/// Example (Lua): capture buffer updates in a global `events` variable
+/// (use "print(vim.inspect(events))" to see its contents):
+/// <pre>
+/// events = {}
+/// vim.api.nvim_buf_attach(0, false, {
+/// on_lines=function(...) table.insert(events, {...}) end})
+/// </pre>
+///
+/// @see |nvim_buf_detach()|
+/// @see |api-buffer-updates-lua|
///
/// @param channel_id
/// @param buffer Buffer handle, or 0 for current buffer
-/// @param send_buffer Set to true if the initial notification should contain
-/// the whole buffer. If so, the first notification will be a
-/// `nvim_buf_lines_event`. Otherwise, the first notification will be
-/// a `nvim_buf_changedtick_event`. Not used for lua callbacks.
+/// @param send_buffer True if the initial notification should contain the
+/// whole buffer: first notification will be `nvim_buf_lines_event`.
+/// Else the first notification will be `nvim_buf_changedtick_event`.
+/// Not for Lua callbacks.
/// @param opts Optional parameters.
-/// - `on_lines`: lua callback received on change.
-/// - `on_changedtick`: lua callback received on changedtick
-/// increment without text change.
-/// - `utf_sizes`: include UTF-32 and UTF-16 size of
-/// the replaced region.
-/// See |api-buffer-updates-lua| for more information
+/// - on_lines: Lua callback invoked on change.
+/// Return `true` to detach. Args:
+/// - buffer handle
+/// - b:changedtick
+/// - first line that changed (zero-indexed)
+/// - last line that was changed
+/// - last line in the updated range
+/// - byte count of previous contents
+/// - deleted_codepoints (if `utf_sizes` is true)
+/// - deleted_codeunits (if `utf_sizes` is true)
+/// - on_changedtick: Lua callback invoked on changedtick
+/// increment without text change. Args:
+/// - buffer handle
+/// - b:changedtick
+/// - on_detach: Lua callback invoked on detach. Args:
+/// - buffer handle
+/// - utf_sizes: include UTF-32 and UTF-16 size of the replaced
+/// region, as args to `on_lines`.
/// @param[out] err Error details, if any
-/// @return False when updates couldn't be enabled because the buffer isn't
-/// loaded or `opts` contained an invalid key; otherwise True.
-/// TODO: LUA_API_NO_EVAL
+/// @return False if attach failed (invalid parameter, or buffer isn't loaded);
+/// otherwise True. TODO: LUA_API_NO_EVAL
Boolean nvim_buf_attach(uint64_t channel_id,
Buffer buffer,
Boolean send_buffer,
@@ -183,13 +208,14 @@ error:
/// Deactivates buffer-update events on the channel.
///
-/// For Lua callbacks see |api-lua-detach|.
+/// @see |nvim_buf_attach()|
+/// @see |api-lua-detach| for detaching Lua callbacks
///
/// @param channel_id
/// @param buffer Buffer handle, or 0 for current buffer
/// @param[out] err Error details, if any
-/// @return False when updates couldn't be disabled because the buffer
-/// isn't loaded; otherwise True.
+/// @return False if detach failed (because the buffer isn't loaded);
+/// otherwise True.
Boolean nvim_buf_detach(uint64_t channel_id,
Buffer buffer,
Error *err)
@@ -529,7 +555,8 @@ void nvim_buf_set_lines(uint64_t channel_id,
(linenr_T)(end - 1),
MAXLNUM,
(long)extra,
- false);
+ false,
+ kExtmarkUndo);
changed_lines((linenr_T)start, 0, (linenr_T)end, (long)extra, true);
fix_cursor((linenr_T)start, (linenr_T)end, (linenr_T)extra);
@@ -984,6 +1011,254 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err)
return rv;
}
+/// Returns position for a given extmark id
+///
+/// @param buffer Buffer handle, or 0 for current buffer
+/// @param ns_id Namespace id from |nvim_create_namespace()|
+/// @param id Extmark id
+/// @param[out] err Error details, if any
+/// @return (row, col) tuple or empty list () if extmark id was absent
+ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id,
+ Integer id, Error *err)
+ FUNC_API_SINCE(7)
+{
+ Array rv = ARRAY_DICT_INIT;
+
+ buf_T *buf = find_buffer_by_handle(buffer, err);
+
+ if (!buf) {
+ return rv;
+ }
+
+ if (!ns_initialized((uint64_t)ns_id)) {
+ api_set_error(err, kErrorTypeValidation, _("Invalid ns_id"));
+ return rv;
+ }
+
+ Extmark *extmark = extmark_from_id(buf, (uint64_t)ns_id, (uint64_t)id);
+ if (!extmark) {
+ return rv;
+ }
+ ADD(rv, INTEGER_OBJ((Integer)extmark->line->lnum-1));
+ ADD(rv, INTEGER_OBJ((Integer)extmark->col-1));
+ return rv;
+}
+
+/// Gets extmarks in "traversal order" from a |charwise| region defined by
+/// buffer positions (inclusive, 0-indexed |api-indexing|).
+///
+/// Region can be given as (row,col) tuples, or valid extmark ids (whose
+/// positions define the bounds). 0 and -1 are understood as (0,0) and (-1,-1)
+/// respectively, thus the following are equivalent:
+///
+/// <pre>
+/// nvim_buf_get_extmarks(0, my_ns, 0, -1, {})
+/// nvim_buf_get_extmarks(0, my_ns, [0,0], [-1,-1], {})
+/// </pre>
+///
+/// If `end` is less than `start`, traversal works backwards. (Useful
+/// with `limit`, to get the first marks prior to a given position.)
+///
+/// Example:
+///
+/// <pre>
+/// local a = vim.api
+/// local pos = a.nvim_win_get_cursor(0)
+/// local ns = a.nvim_create_namespace('my-plugin')
+/// -- Create new extmark at line 1, column 1.
+/// local m1 = a.nvim_buf_set_extmark(0, ns, 0, 0, 0, {})
+/// -- Create new extmark at line 3, column 1.
+/// local m2 = a.nvim_buf_set_extmark(0, ns, 0, 2, 0, {})
+/// -- Get extmarks only from line 3.
+/// local ms = a.nvim_buf_get_extmarks(0, ns, {2,0}, {2,0}, {})
+/// -- Get all marks in this buffer + namespace.
+/// local all = a.nvim_buf_get_extmarks(0, ns, 0, -1, {})
+/// print(vim.inspect(ms))
+/// </pre>
+///
+/// @param buffer Buffer handle, or 0 for current buffer
+/// @param ns_id Namespace id from |nvim_create_namespace()|
+/// @param start Start of range, given as (row, col) or valid extmark id
+/// (whose position defines the bound)
+/// @param end End of range, given as (row, col) or valid extmark id
+/// (whose position defines the bound)
+/// @param opts Optional parameters. Keys:
+/// - limit: Maximum number of marks to return
+/// @param[out] err Error details, if any
+/// @return List of [extmark_id, row, col] tuples in "traversal order".
+Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start,
+ Object end, Dictionary opts, Error *err)
+ FUNC_API_SINCE(7)
+{
+ Array rv = ARRAY_DICT_INIT;
+
+ buf_T *buf = find_buffer_by_handle(buffer, err);
+ if (!buf) {
+ return rv;
+ }
+
+ if (!ns_initialized((uint64_t)ns_id)) {
+ api_set_error(err, kErrorTypeValidation, _("Invalid ns_id"));
+ return rv;
+ }
+ Integer limit = -1;
+
+ for (size_t i = 0; i < opts.size; i++) {
+ String k = opts.items[i].key;
+ Object *v = &opts.items[i].value;
+ if (strequal("limit", k.data)) {
+ if (v->type != kObjectTypeInteger) {
+ api_set_error(err, kErrorTypeValidation, "limit is not an integer");
+ return rv;
+ }
+ limit = v->data.integer;
+ v->data.integer = LUA_NOREF;
+ } else {
+ api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data);
+ return rv;
+ }
+ }
+
+ if (limit == 0) {
+ return rv;
+ }
+
+
+ bool reverse = false;
+
+ linenr_T l_lnum;
+ colnr_T l_col;
+ if (!extmark_get_index_from_obj(buf, ns_id, start, &l_lnum, &l_col, err)) {
+ return rv;
+ }
+
+ linenr_T u_lnum;
+ colnr_T u_col;
+ if (!extmark_get_index_from_obj(buf, ns_id, end, &u_lnum, &u_col, err)) {
+ return rv;
+ }
+
+ if (l_lnum > u_lnum || (l_lnum == u_lnum && l_col > u_col)) {
+ reverse = true;
+ linenr_T tmp_lnum = l_lnum;
+ l_lnum = u_lnum;
+ u_lnum = tmp_lnum;
+ colnr_T tmp_col = l_col;
+ l_col = u_col;
+ u_col = tmp_col;
+ }
+
+
+ ExtmarkArray marks = extmark_get(buf, (uint64_t)ns_id, l_lnum, l_col, u_lnum,
+ u_col, (int64_t)limit, reverse);
+
+ for (size_t i = 0; i < kv_size(marks); i++) {
+ Array mark = ARRAY_DICT_INIT;
+ Extmark *extmark = kv_A(marks, i);
+ ADD(mark, INTEGER_OBJ((Integer)extmark->mark_id));
+ ADD(mark, INTEGER_OBJ(extmark->line->lnum-1));
+ ADD(mark, INTEGER_OBJ(extmark->col-1));
+ ADD(rv, ARRAY_OBJ(mark));
+ }
+
+ kv_destroy(marks);
+ return rv;
+}
+
+/// Creates or updates an extmark.
+///
+/// To create a new extmark, pass id=0. The extmark id will be returned.
+// To move an existing mark, pass its id.
+///
+/// It is also allowed to create a new mark by passing in a previously unused
+/// id, but the caller must then keep track of existing and unused ids itself.
+/// (Useful over RPC, to avoid waiting for the return value.)
+///
+/// @param buffer Buffer handle, or 0 for current buffer
+/// @param ns_id Namespace id from |nvim_create_namespace()|
+/// @param id Extmark id, or 0 to create new
+/// @param line Line number where to place the mark
+/// @param col Column where to place the mark
+/// @param opts Optional parameters. Currently not used.
+/// @param[out] err Error details, if any
+/// @return Id of the created/updated extmark
+Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer id,
+ Integer line, Integer col,
+ Dictionary opts, Error *err)
+ FUNC_API_SINCE(7)
+{
+ buf_T *buf = find_buffer_by_handle(buffer, err);
+ if (!buf) {
+ return 0;
+ }
+
+ if (!ns_initialized((uint64_t)ns_id)) {
+ api_set_error(err, kErrorTypeValidation, _("Invalid ns_id"));
+ return 0;
+ }
+
+ if (opts.size > 0) {
+ api_set_error(err, kErrorTypeValidation, "opts dict isn't empty");
+ return 0;
+ }
+
+ size_t len = 0;
+ if (line < 0 || line > buf->b_ml.ml_line_count) {
+ api_set_error(err, kErrorTypeValidation, "line value outside range");
+ return 0;
+ } else if (line < buf->b_ml.ml_line_count) {
+ len = STRLEN(ml_get_buf(buf, (linenr_T)line+1, false));
+ }
+
+ if (col == -1) {
+ col = (Integer)len;
+ } else if (col < -1 || col > (Integer)len) {
+ api_set_error(err, kErrorTypeValidation, "col value outside range");
+ return 0;
+ }
+
+ uint64_t id_num;
+ if (id == 0) {
+ id_num = extmark_free_id_get(buf, (uint64_t)ns_id);
+ } else if (id > 0) {
+ id_num = (uint64_t)id;
+ } else {
+ api_set_error(err, kErrorTypeValidation, _("Invalid mark id"));
+ return 0;
+ }
+
+ extmark_set(buf, (uint64_t)ns_id, id_num,
+ (linenr_T)line+1, (colnr_T)col+1, kExtmarkUndo);
+
+ return (Integer)id_num;
+}
+
+/// Removes an extmark.
+///
+/// @param buffer Buffer handle, or 0 for current buffer
+/// @param ns_id Namespace id from |nvim_create_namespace()|
+/// @param id Extmark id
+/// @param[out] err Error details, if any
+/// @return true if the extmark was found, else false
+Boolean nvim_buf_del_extmark(Buffer buffer,
+ Integer ns_id,
+ Integer id,
+ Error *err)
+ FUNC_API_SINCE(7)
+{
+ buf_T *buf = find_buffer_by_handle(buffer, err);
+
+ if (!buf) {
+ return false;
+ }
+ if (!ns_initialized((uint64_t)ns_id)) {
+ api_set_error(err, kErrorTypeValidation, _("Invalid ns_id"));
+ return false;
+ }
+
+ return extmark_del(buf, (uint64_t)ns_id, (uint64_t)id, kExtmarkUndo);
+}
+
/// Adds a highlight to buffer.
///
/// Useful for plugins that dynamically generate highlights to a buffer
@@ -1050,7 +1325,8 @@ Integer nvim_buf_add_highlight(Buffer buffer,
return ns_id;
}
-/// Clears namespaced objects, highlights and virtual text, from a line range
+/// Clears namespaced objects (highlights, extmarks, virtual text) from
+/// a region.
///
/// Lines are 0-indexed. |api-indexing| To clear the namespace in the entire
/// buffer, specify line_start=0 and line_end=-1.
@@ -1082,6 +1358,10 @@ void nvim_buf_clear_namespace(Buffer buffer,
}
bufhl_clear_line_range(buf, (int)ns_id, (int)line_start+1, (int)line_end);
+ extmark_clear(buf, ns_id == -1 ? 0 : (uint64_t)ns_id,
+ (linenr_T)line_start+1,
+ (linenr_T)line_end,
+ kExtmarkUndo);
}
/// Clears highlights and virtual text from namespace and range of lines
@@ -1192,6 +1472,56 @@ free_exit:
return 0;
}
+/// Get the virtual text (annotation) for a buffer line.
+///
+/// The virtual text is returned as list of lists, whereas the inner lists have
+/// either one or two elements. The first element is the actual text, the
+/// optional second element is the highlight group.
+///
+/// The format is exactly the same as given to nvim_buf_set_virtual_text().
+///
+/// If there is no virtual text associated with the given line, an empty list
+/// is returned.
+///
+/// @param buffer Buffer handle, or 0 for current buffer
+/// @param line Line to get the virtual text from (zero-indexed)
+/// @param[out] err Error details, if any
+/// @return List of virtual text chunks
+Array nvim_buf_get_virtual_text(Buffer buffer, Integer lnum, Error *err)
+ FUNC_API_SINCE(7)
+{
+ Array chunks = ARRAY_DICT_INIT;
+
+ buf_T *buf = find_buffer_by_handle(buffer, err);
+ if (!buf) {
+ return chunks;
+ }
+
+ if (lnum < 0 || lnum >= MAXLNUM) {
+ api_set_error(err, kErrorTypeValidation, "Line number outside range");
+ return chunks;
+ }
+
+ BufhlLine *lineinfo = bufhl_tree_ref(&buf->b_bufhl_info, (linenr_T)(lnum + 1),
+ false);
+ if (!lineinfo) {
+ return chunks;
+ }
+
+ for (size_t i = 0; i < lineinfo->virt_text.size; i++) {
+ Array chunk = ARRAY_DICT_INIT;
+ VirtTextChunk *vtc = &lineinfo->virt_text.items[i];
+ ADD(chunk, STRING_OBJ(cstr_to_string(vtc->text)));
+ if (vtc->hl_id > 0) {
+ ADD(chunk, STRING_OBJ(cstr_to_string(
+ (const char *)syn_id2name(vtc->hl_id))));
+ }
+ ADD(chunks, ARRAY_OBJ(chunk));
+ }
+
+ return chunks;
+}
+
Dictionary nvim__buf_stats(Buffer buffer, Error *err)
{
Dictionary rv = ARRAY_DICT_INIT;
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index 2056cb07e3..b8d62e42a1 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -10,6 +10,7 @@
#include "nvim/api/private/helpers.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/handle.h"
+#include "nvim/api/vim.h"
#include "nvim/msgpack_rpc/helpers.h"
#include "nvim/lua/executor.h"
#include "nvim/ascii.h"
@@ -23,6 +24,7 @@
#include "nvim/eval/typval.h"
#include "nvim/map_defs.h"
#include "nvim/map.h"
+#include "nvim/mark_extended.h"
#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/version.h"
@@ -1505,3 +1507,127 @@ ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf)
return mappings;
}
+
+// Returns an extmark given an id or a positional index
+// If throw == true then an error will be raised if nothing
+// was found
+// Returns NULL if something went wrong
+Extmark *extmark_from_id_or_pos(Buffer buffer, Integer ns, Object id,
+ Error *err, bool throw)
+{
+ buf_T *buf = find_buffer_by_handle(buffer, err);
+
+ if (!buf) {
+ return NULL;
+ }
+
+ Extmark *extmark = NULL;
+ if (id.type == kObjectTypeArray) {
+ if (id.data.array.size != 2) {
+ api_set_error(err, kErrorTypeValidation,
+ _("Position must have 2 elements"));
+ return NULL;
+ }
+ linenr_T row = (linenr_T)id.data.array.items[0].data.integer;
+ colnr_T col = (colnr_T)id.data.array.items[1].data.integer;
+ if (row < 1 || col < 1) {
+ if (throw) {
+ api_set_error(err, kErrorTypeValidation, _("Row and column MUST be > 0"));
+ }
+ return NULL;
+ }
+ extmark = extmark_from_pos(buf, (uint64_t)ns, row, col);
+ } else if (id.type != kObjectTypeInteger) {
+ if (throw) {
+ api_set_error(err, kErrorTypeValidation,
+ _("Mark id must be an int or [row, col]"));
+ }
+ return NULL;
+ } else if (id.data.integer < 0) {
+ if (throw) {
+ api_set_error(err, kErrorTypeValidation, _("Mark id must be positive"));
+ }
+ return NULL;
+ } else {
+ extmark = extmark_from_id(buf,
+ (uint64_t)ns,
+ (uint64_t)id.data.integer);
+ }
+
+ if (!extmark) {
+ if (throw) {
+ api_set_error(err, kErrorTypeValidation, _("Mark doesn't exist"));
+ }
+ return NULL;
+ }
+ return extmark;
+}
+
+// Is the Namespace in use?
+bool ns_initialized(uint64_t ns)
+{
+ if (ns < 1) {
+ return false;
+ }
+ return ns < (uint64_t)next_namespace_id;
+}
+
+/// Gets the line and column of an extmark.
+///
+/// Extmarks may be queried by position, name or even special names
+/// in the future such as "cursor".
+///
+/// @param[out] lnum extmark line
+/// @param[out] colnr extmark column
+///
+/// @return true if the extmark was found, else false
+bool extmark_get_index_from_obj(buf_T *buf, Integer ns, Object obj, linenr_T
+ *lnum, colnr_T *colnr, Error *err)
+{
+ // Check if it is mark id
+ if (obj.type == kObjectTypeInteger) {
+ Integer id = obj.data.integer;
+ if (id == 0) {
+ *lnum = 1;
+ *colnr = 1;
+ return true;
+ } else if (id == -1) {
+ *lnum = MAXLNUM;
+ *colnr = MAXCOL;
+ return true;
+ } else if (id < 0) {
+ api_set_error(err, kErrorTypeValidation, _("Mark id must be positive"));
+ return false;
+ }
+
+ Extmark *extmark = extmark_from_id(buf, (uint64_t)ns, (uint64_t)id);
+ if (extmark) {
+ *lnum = extmark->line->lnum;
+ *colnr = extmark->col;
+ return true;
+ } else {
+ api_set_error(err, kErrorTypeValidation, _("No mark with requested id"));
+ return false;
+ }
+
+ // Check if it is a position
+ } else if (obj.type == kObjectTypeArray) {
+ Array pos = obj.data.array;
+ if (pos.size != 2
+ || pos.items[0].type != kObjectTypeInteger
+ || pos.items[1].type != kObjectTypeInteger) {
+ api_set_error(err, kErrorTypeValidation,
+ _("Position must have 2 integer elements"));
+ return false;
+ }
+ Integer line = pos.items[0].data.integer;
+ Integer col = pos.items[1].data.integer;
+ *lnum = (linenr_T)(line >= 0 ? line + 1 : MAXLNUM);
+ *colnr = (colnr_T)(col >= 0 ? col + 1 : MAXCOL);
+ return true;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ _("Position must be a mark id Integer or position Array"));
+ return false;
+ }
+}
diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h
index 0ea7667428..8930f252f6 100644
--- a/src/nvim/api/private/helpers.h
+++ b/src/nvim/api/private/helpers.h
@@ -102,6 +102,20 @@ typedef struct {
int did_emsg;
} TryState;
+// `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)
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/private/helpers.h.generated.h"
#endif
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 602733fd31..19601b6539 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -25,6 +25,7 @@
#include "nvim/highlight.h"
#include "nvim/window.h"
#include "nvim/types.h"
+#include "nvim/ex_cmds2.h"
#include "nvim/ex_docmd.h"
#include "nvim/screen.h"
#include "nvim/memline.h"
@@ -39,6 +40,7 @@
#include "nvim/ops.h"
#include "nvim/option.h"
#include "nvim/state.h"
+#include "nvim/mark_extended.h"
#include "nvim/syntax.h"
#include "nvim/getchar.h"
#include "nvim/os/input.h"
@@ -53,20 +55,6 @@
# 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
{
@@ -85,10 +73,70 @@ void api_vim_free_all_mem(void)
map_free(String, handle_T)(namespace_ids);
}
+/// Executes Vimscript (multiline block of Ex-commands), like anonymous
+/// |:source|.
+///
+/// Unlike |nvim_command()| this function supports heredocs, script-scope (s:),
+/// etc.
+///
+/// On execution error: fails with VimL error, does not update v:errmsg.
+///
+/// @see |execute()|
+/// @see |nvim_command()|
+///
+/// @param src Vimscript code
+/// @param output Capture and return all (non-error, non-shell |:!|) output
+/// @param[out] err Error details (Vim error), if any
+/// @return Output (non-error, non-shell |:!|) if `output` is true,
+/// else empty string.
+String nvim_exec(String src, Boolean output, Error *err)
+ FUNC_API_SINCE(7)
+{
+ const int save_msg_silent = msg_silent;
+ garray_T *const save_capture_ga = capture_ga;
+ garray_T capture_local;
+ if (output) {
+ ga_init(&capture_local, 1, 80);
+ capture_ga = &capture_local;
+ }
+
+ try_start();
+ msg_silent++;
+ do_source_str(src.data, "nvim_exec()");
+ capture_ga = save_capture_ga;
+ msg_silent = save_msg_silent;
+ try_end(err);
+
+ if (ERROR_SET(err)) {
+ goto theend;
+ }
+
+ if (output && capture_local.ga_len > 1) {
+ String s = (String){
+ .data = capture_local.ga_data,
+ .size = (size_t)capture_local.ga_len,
+ };
+ // redir usually (except :echon) prepends a newline.
+ if (s.data[0] == '\n') {
+ memmove(s.data, s.data + 1, s.size - 1);
+ s.data[s.size - 1] = '\0';
+ s.size = s.size - 1;
+ }
+ return s; // Caller will free the memory.
+ }
+theend:
+ if (output) {
+ ga_clear(&capture_local);
+ }
+ return (String)STRING_INIT;
+}
+
/// Executes an ex-command.
///
/// On execution error: fails with VimL error, does not update v:errmsg.
///
+/// @see |nvim_exec()|
+///
/// @param command Ex-command string
/// @param[out] err Error details (Vim error), if any
void nvim_command(String command, Error *err)
@@ -345,53 +393,16 @@ String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt,
return cstr_as_string(ptr);
}
-/// Executes an ex-command and returns its (non-error) output.
-/// Shell |:!| output is not captured.
-///
-/// On execution error: fails with VimL error, does not update v:errmsg.
-///
-/// @param command Ex-command string
-/// @param[out] err Error details (Vim error), if any
+/// @deprecated
+/// @see nvim_exec
String nvim_command_output(String command, Error *err)
FUNC_API_SINCE(1)
+ FUNC_API_DEPRECATED_SINCE(7)
{
- const int save_msg_silent = msg_silent;
- garray_T *const save_capture_ga = capture_ga;
- garray_T capture_local;
- ga_init(&capture_local, 1, 80);
-
- try_start();
- msg_silent++;
- capture_ga = &capture_local;
- do_cmdline_cmd(command.data);
- capture_ga = save_capture_ga;
- msg_silent = save_msg_silent;
- try_end(err);
-
- if (ERROR_SET(err)) {
- goto theend;
- }
-
- if (capture_local.ga_len > 1) {
- String s = (String){
- .data = capture_local.ga_data,
- .size = (size_t)capture_local.ga_len,
- };
- // redir usually (except :echon) prepends a newline.
- if (s.data[0] == '\n') {
- memmove(s.data, s.data + 1, s.size - 1);
- s.data[s.size - 1] = '\0';
- s.size = s.size - 1;
- }
- return s; // Caller will free the memory.
- }
-
-theend:
- ga_clear(&capture_local);
- return (String)STRING_INIT;
+ return nvim_exec(command, true, err);
}
-/// Evaluates a VimL expression (:help expression).
+/// Evaluates a VimL |expression|.
/// Dictionaries and Lists are recursively expanded.
///
/// On execution error: fails with VimL error, does not update v:errmsg.
@@ -436,6 +447,15 @@ Object nvim_eval(String expr, Error *err)
return rv;
}
+/// @deprecated Use nvim_exec_lua() instead.
+Object nvim_execute_lua(String code, Array args, Error *err)
+ FUNC_API_SINCE(3)
+ FUNC_API_DEPRECATED_SINCE(7)
+ FUNC_API_REMOTE_ONLY
+{
+ return executor_exec_lua_api(code, args, err);
+}
+
/// Execute Lua code. Parameters (if any) are available as `...` inside the
/// chunk. The chunk can return a value.
///
@@ -448,8 +468,9 @@ Object nvim_eval(String expr, Error *err)
/// or executing the Lua code.
///
/// @return Return value of Lua code if present or NIL.
-Object nvim_execute_lua(String code, Array args, Error *err)
- FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY
+Object nvim_exec_lua(String code, Array args, Error *err)
+ FUNC_API_SINCE(7)
+ FUNC_API_REMOTE_ONLY
{
return executor_exec_lua_api(code, args, err);
}
@@ -1054,10 +1075,9 @@ fail:
/// @param enter Enter the window (make it the current window)
/// @param config Map defining the window configuration. Keys:
/// - `relative`: Sets the window layout to "floating", placed at (row,col)
-/// coordinates relative to one of:
+/// coordinates relative to:
/// - "editor" The global editor grid
-/// - "win" Window given by the `win` field, or current window by
-/// default.
+/// - "win" Window given by the `win` field, or current window.
/// - "cursor" Cursor position in current window.
/// - `win`: |window-ID| for relative="win".
/// - `anchor`: Decides which corner of the float to place at (row,col):
@@ -1088,9 +1108,10 @@ fail:
/// float where the text should not be edited. Disables
/// 'number', 'relativenumber', 'cursorline', 'cursorcolumn',
/// 'foldcolumn', 'spell' and 'list' options. 'signcolumn'
-/// is changed to `auto`. The end-of-buffer region is hidden
-/// by setting `eob` flag of 'fillchars' to a space char,
-/// and clearing the |EndOfBuffer| region in 'winhighlight'.
+/// is changed to `auto` and 'colorcolumn' is cleared. The
+/// end-of-buffer region is hidden by setting `eob` flag of
+/// 'fillchars' to a space char, and clearing the
+/// |EndOfBuffer| region in 'winhighlight'.
/// @param[out] err Error details, if any
///
/// @return Window handle, or 0 on error
@@ -1109,6 +1130,10 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dictionary config,
if (enter) {
win_enter(wp, false);
}
+ if (!win_valid(wp)) {
+ api_set_error(err, kErrorTypeException, "Window was closed immediately");
+ return 0;
+ }
if (buffer > 0) {
nvim_win_set_buf(wp->handle, buffer, err);
}
@@ -1260,13 +1285,13 @@ Boolean nvim_paste(String data, Boolean crlf, Integer phase, Error *err)
Array lines = string_to_array(data, crlf);
ADD(args, ARRAY_OBJ(lines));
ADD(args, INTEGER_OBJ(phase));
- rv = nvim_execute_lua(STATIC_CSTR_AS_STRING("return vim.paste(...)"), args,
- err);
+ rv = nvim_exec_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)) {
+ if (!(State & (CMDLINE | INSERT)) && (phase == -1 || phase == 1)) {
ResetRedobuff();
AppendCharToRedobuff('a'); // Dot-repeat.
}
@@ -1284,7 +1309,7 @@ Boolean nvim_paste(String data, Boolean crlf, Integer phase, Error *err)
}
}
}
- if (!(State & CMDLINE) && !(State & INSERT) && (phase == -1 || phase == 3)) {
+ if (!(State & (CMDLINE | INSERT)) && (phase == -1 || phase == 3)) {
AppendCharToRedobuff(ESC); // Dot-repeat.
}
theend:
@@ -1304,7 +1329,7 @@ theend:
/// @param lines |readfile()|-style list of lines. |channel-lines|
/// @param type Edit behavior: any |getregtype()| result, or:
/// - "b" |blockwise-visual| mode (may include width, e.g. "b3")
-/// - "c" |characterwise| mode
+/// - "c" |charwise| mode
/// - "l" |linewise| mode
/// - "" guess by contents, see |setreg()|
/// @param after Insert after cursor (like |p|), or before (like |P|).
@@ -2395,7 +2420,7 @@ Array nvim_get_proc_children(Integer pid, Error *err)
Array a = ARRAY_DICT_INIT;
ADD(a, INTEGER_OBJ(pid));
String s = cstr_to_string("return vim._os_proc_children(select(1, ...))");
- Object o = nvim_execute_lua(s, a, err);
+ Object o = nvim_exec_lua(s, a, err);
api_free_string(s);
api_free_array(a);
if (o.type == kObjectTypeArray) {
@@ -2441,7 +2466,7 @@ Object nvim_get_proc(Integer pid, Error *err)
Array a = ARRAY_DICT_INIT;
ADD(a, INTEGER_OBJ(pid));
String s = cstr_to_string("return vim._os_proc_info(select(1, ...))");
- Object o = nvim_execute_lua(s, a, err);
+ Object o = nvim_exec_lua(s, a, err);
api_free_string(s);
api_free_array(a);
if (o.type == kObjectTypeArray && o.data.array.size == 0) {