diff options
Diffstat (limited to 'src/nvim/api')
-rw-r--r-- | src/nvim/api/buffer.c | 217 | ||||
-rw-r--r-- | src/nvim/api/private/helpers.c | 117 | ||||
-rw-r--r-- | src/nvim/api/vim.c | 1 |
3 files changed, 334 insertions, 1 deletions
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 10250be044..a77c33e891 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" @@ -544,7 +547,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); @@ -999,6 +1003,213 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err) return rv; } +/// Returns position info for a given extmark id +/// +/// @param buffer The buffer handle +/// @param namespace a identifier returned previously with nvim_create_namespace +/// @param id the extmark id +/// @param[out] err Details of an error that may have occurred +/// @return (row, col) tuple or empty list () if extmark id was absent +ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer namespace, + Integer id, Error *err) + FUNC_API_SINCE(6) +{ + Array rv = ARRAY_DICT_INIT; + + buf_T *buf = find_buffer_by_handle(buffer, err); + + if (!buf) { + return rv; + } + + if (!ns_initialized((uint64_t)namespace)) { + api_set_error(err, kErrorTypeValidation, _("Invalid mark namespace")); + return rv; + } + + ExtendedMark *extmark = extmark_from_id(buf, + (uint64_t)namespace, + (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; +} + +/// List extmarks in a range (inclusive) +/// +/// range ends can be specified as (row, col) tuples, as well as extmark +/// ids in the same namespace. In addition, 0 and -1 works as shorthands +/// for (0,0) and (-1,-1) respectively, so that all marks in the buffer can be +/// quieried as: +/// +/// all_marks = nvim_buf_get_extmarks(0, my_ns, 0, -1, -1) +/// +/// If end is a lower position than start, then the range will be traversed +/// backwards. This is mostly used with limited amount, to be able to get the +/// first marks prior to a given position. +/// +/// @param buffer The buffer handle +/// @param namespace An id returned previously from nvim_create_namespace +/// @param lower One of: extmark id, (row, col) or 0, -1 for buffer ends +/// @param upper One of: extmark id, (row, col) or 0, -1 for buffer ends +/// @param amount Maximum number of marks to return or -1 for all marks found +/// /// @param[out] err Details of an error that may have occurred +/// @return [[nsmark_id, row, col], ...] +Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, + Object start, Object end, Integer amount, + Error *err) + FUNC_API_SINCE(6) +{ + 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 mark namespace")); + return rv; + } + + if (amount == 0) { + return rv; + } + + + bool reverse = false; + + linenr_T l_lnum; + colnr_T l_col; + if (!set_extmark_index_from_obj(buf, ns_id, start, &l_lnum, &l_col, err)) { + return rv; + } + + linenr_T u_lnum; + colnr_T u_col; + if (!set_extmark_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)amount, + reverse); + + for (size_t i = 0; i < kv_size(marks); i++) { + Array mark = ARRAY_DICT_INIT; + ExtendedMark *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; +} + +/// Create or update a namespaced mark at a position +/// +/// If an invalid namespace is given, an error will be raised. +/// +/// @param buffer The buffer handle +/// @param ns_id a identifier returned previously with nvim_create_namespace +/// @param id The extmark's id or 0 for next free id +/// @param row The row to set the extmark to. +/// @param col The column to set the extmark to. +/// @param[out] err Details of an error that may have occurred +/// @return the nsmark_id for a new mark, or 0 for an update +Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer id, + Integer line, Integer col, Error *err) + FUNC_API_SINCE(6) +{ + 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 mark namespace")); + 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(curbuf, (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; + } + + bool new = extmark_set(buf, (uint64_t)ns_id, id_num, + (linenr_T)line+1, + (colnr_T)col+1, + kExtmarkUndo); + + if (new) { + return (Integer)id_num; + } else { + return 0; + } +} + +/// Remove an extmark +/// +/// @param buffer The buffer handle +/// @param ns_id a identifier returned previously with nvim_create_namespace +/// @param id The extmarks's id +/// @param[out] err Details of an error that may have occurred +/// @return true on success, false if no extmarks found +Boolean nvim_buf_del_extmark(Buffer buffer, + Integer ns_id, + Integer id, + Error *err) + FUNC_API_SINCE(6) +{ + 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 mark namespace")); + 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 @@ -1097,6 +1308,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 diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index b54fc9207e..5e9a572a78 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -24,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" @@ -1507,6 +1508,63 @@ 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 +ExtendedMark *extmark_from_id_or_pos(Buffer buffer, + Integer namespace, + Object id, + Error *err, + bool throw) +{ + buf_T *buf = find_buffer_by_handle(buffer, err); + + if (!buf) { + return NULL; + } + + ExtendedMark *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)namespace, 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)namespace, + (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) @@ -1516,3 +1574,62 @@ bool ns_initialized(uint64_t ns) } return ns < (uint64_t)next_namespace_id; } + +// Extmarks may be queried from position or name or even special names +// in the future such as "cursor". This macro sets the line and col +// to make the extmark functions recognize what's required +// +// *lnum: linenr_T, lnum to be set +// *col: colnr_T, col to be set +bool set_extmark_index_from_obj(buf_T *buf, Integer namespace, + 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; + } + + ExtendedMark *extmark = extmark_from_id(buf, (uint64_t)namespace, + (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/vim.c b/src/nvim/api/vim.c index e587df5384..10f7dd1a7b 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -39,6 +39,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" |