aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/api/extmark.c
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2023-11-30 20:35:25 +0000
committerJosh Rahm <joshuarahm@gmail.com>2023-11-30 20:35:25 +0000
commit1b7b916b7631ddf73c38e3a0070d64e4636cb2f3 (patch)
treecd08258054db80bb9a11b1061bb091c70b76926a /src/nvim/api/extmark.c
parenteaa89c11d0f8aefbb512de769c6c82f61a8baca3 (diff)
parent4a8bf24ac690004aedf5540fa440e788459e5e34 (diff)
downloadrneovim-aucmd_textputpost.tar.gz
rneovim-aucmd_textputpost.tar.bz2
rneovim-aucmd_textputpost.zip
Merge remote-tracking branch 'upstream/master' into aucmd_textputpostaucmd_textputpost
Diffstat (limited to 'src/nvim/api/extmark.c')
-rw-r--r--src/nvim/api/extmark.c878
1 files changed, 438 insertions, 440 deletions
diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c
index ab3b3485e4..d71498d6ed 100644
--- a/src/nvim/api/extmark.c
+++ b/src/nvim/api/extmark.c
@@ -1,29 +1,31 @@
-// This is an open source non-commercial project. Dear PVS-Studio, please check
-// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
-
#include <assert.h>
+#include <lauxlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "klib/kvec.h"
-#include "lauxlib.h"
#include "nvim/api/extmark.h"
+#include "nvim/api/keysets_defs.h"
#include "nvim/api/private/defs.h"
+#include "nvim/api/private/dispatch.h"
#include "nvim/api/private/helpers.h"
+#include "nvim/api/private/validate.h"
#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/decoration.h"
#include "nvim/decoration_provider.h"
#include "nvim/drawscreen.h"
#include "nvim/extmark.h"
+#include "nvim/func_attr.h"
+#include "nvim/grid.h"
#include "nvim/highlight_group.h"
+#include "nvim/marktree.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
-#include "nvim/pos.h"
-#include "nvim/strings.h"
-#include "nvim/vim.h"
+#include "nvim/pos_defs.h"
+#include "nvim/sign.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/extmark.c.generated.h"
@@ -32,12 +34,10 @@
void api_extmark_free_all_mem(void)
{
String name;
- handle_T id;
- map_foreach(&namespace_ids, name, id, {
- (void)id;
+ map_foreach_key(&namespace_ids, name, {
xfree(name.data);
})
- map_destroy(String, handle_T)(&namespace_ids);
+ map_destroy(String, &namespace_ids);
}
/// Creates a new namespace or gets an existing one. \*namespace\*
@@ -54,14 +54,14 @@ void api_extmark_free_all_mem(void)
Integer nvim_create_namespace(String name)
FUNC_API_SINCE(5)
{
- handle_T id = map_get(String, handle_T)(&namespace_ids, name);
+ handle_T id = map_get(String, int)(&namespace_ids, name);
if (id > 0) {
return id;
}
id = next_namespace_id++;
if (name.size > 0) {
String name_alloc = copy_string(name, NULL);
- map_put(String, handle_T)(&namespace_ids, name_alloc, id);
+ map_put(String, int)(&namespace_ids, name_alloc, id);
}
return (Integer)id;
}
@@ -83,7 +83,7 @@ Dictionary nvim_get_namespaces(void)
return retval;
}
-const char *describe_ns(NS ns_id)
+const char *describe_ns(NS ns_id, const char *unknown)
{
String name;
handle_T id;
@@ -92,7 +92,7 @@ const char *describe_ns(NS ns_id)
return name.data;
}
})
- return "(UNKNOWN PLUGIN)";
+ return unknown;
}
// Is the Namespace in use?
@@ -104,92 +104,73 @@ bool ns_initialized(uint32_t ns)
return ns < (uint32_t)next_namespace_id;
}
-static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict)
+Array virt_text_to_array(VirtText vt, bool hl_name)
{
+ Array chunks = ARRAY_DICT_INIT;
+ Array hl_array = ARRAY_DICT_INIT;
+ for (size_t i = 0; i < kv_size(vt); i++) {
+ char *text = kv_A(vt, i).text;
+ int hl_id = kv_A(vt, i).hl_id;
+ if (text == NULL) {
+ if (hl_id > 0) {
+ ADD(hl_array, hl_group_name(hl_id, hl_name));
+ }
+ continue;
+ }
+ Array chunk = ARRAY_DICT_INIT;
+ ADD(chunk, CSTR_TO_OBJ(text));
+ if (hl_array.size > 0) {
+ if (hl_id > 0) {
+ ADD(hl_array, hl_group_name(hl_id, hl_name));
+ }
+ ADD(chunk, ARRAY_OBJ(hl_array));
+ hl_array = (Array)ARRAY_DICT_INIT;
+ } else if (hl_id > 0) {
+ ADD(chunk, hl_group_name(hl_id, hl_name));
+ }
+ ADD(chunks, ARRAY_OBJ(chunk));
+ }
+ assert(hl_array.size == 0);
+ return chunks;
+}
+
+static Array extmark_to_array(MTPair extmark, bool id, bool add_dict, bool hl_name)
+{
+ MTKey start = extmark.start;
Array rv = ARRAY_DICT_INIT;
if (id) {
- ADD(rv, INTEGER_OBJ((Integer)extmark->mark_id));
+ ADD(rv, INTEGER_OBJ((Integer)start.id));
}
- ADD(rv, INTEGER_OBJ(extmark->row));
- ADD(rv, INTEGER_OBJ(extmark->col));
+ ADD(rv, INTEGER_OBJ(start.pos.row));
+ ADD(rv, INTEGER_OBJ(start.pos.col));
if (add_dict) {
Dictionary dict = ARRAY_DICT_INIT;
- PUT(dict, "right_gravity", BOOLEAN_OBJ(extmark->right_gravity));
+ PUT(dict, "ns_id", INTEGER_OBJ((Integer)start.ns));
- if (extmark->end_row >= 0) {
- PUT(dict, "end_row", INTEGER_OBJ(extmark->end_row));
- PUT(dict, "end_col", INTEGER_OBJ(extmark->end_col));
- PUT(dict, "end_right_gravity", BOOLEAN_OBJ(extmark->end_right_gravity));
- }
+ PUT(dict, "right_gravity", BOOLEAN_OBJ(mt_right(start)));
- const Decoration *decor = &extmark->decor;
- if (decor->hl_id) {
- String name = cstr_to_string((const char *)syn_id2name(decor->hl_id));
- PUT(dict, "hl_group", STRING_OBJ(name));
- PUT(dict, "hl_eol", BOOLEAN_OBJ(decor->hl_eol));
- }
- if (decor->hl_mode) {
- PUT(dict, "hl_mode", STRING_OBJ(cstr_to_string(hl_mode_str[decor->hl_mode])));
+ if (extmark.end_pos.row >= 0) {
+ PUT(dict, "end_row", INTEGER_OBJ(extmark.end_pos.row));
+ PUT(dict, "end_col", INTEGER_OBJ(extmark.end_pos.col));
+ PUT(dict, "end_right_gravity", BOOLEAN_OBJ(extmark.end_right_gravity));
}
- if (kv_size(decor->virt_text)) {
- Array chunks = ARRAY_DICT_INIT;
- for (size_t i = 0; i < decor->virt_text.size; i++) {
- Array chunk = ARRAY_DICT_INIT;
- VirtTextChunk *vtc = &decor->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));
- }
- PUT(dict, "virt_text", ARRAY_OBJ(chunks));
- PUT(dict, "virt_text_hide", BOOLEAN_OBJ(decor->virt_text_hide));
- if (decor->virt_text_pos == kVTWinCol) {
- PUT(dict, "virt_text_win_col", INTEGER_OBJ(decor->col));
- }
- PUT(dict, "virt_text_pos",
- STRING_OBJ(cstr_to_string(virt_text_pos_str[decor->virt_text_pos])));
+ if (mt_no_undo(start)) {
+ PUT(dict, "undo_restore", BOOLEAN_OBJ(false));
}
- if (decor->ui_watched) {
- PUT(dict, "ui_watched", BOOLEAN_OBJ(true));
+ if (mt_invalidate(start)) {
+ PUT(dict, "invalidate", BOOLEAN_OBJ(true));
}
-
- if (kv_size(decor->virt_lines)) {
- Array all_chunks = ARRAY_DICT_INIT;
- bool virt_lines_leftcol = false;
- for (size_t i = 0; i < decor->virt_lines.size; i++) {
- Array chunks = ARRAY_DICT_INIT;
- VirtText *vt = &decor->virt_lines.items[i].line;
- virt_lines_leftcol = decor->virt_lines.items[i].left_col;
- for (size_t j = 0; j < vt->size; j++) {
- Array chunk = ARRAY_DICT_INIT;
- VirtTextChunk *vtc = &vt->items[j];
- 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));
- }
- ADD(all_chunks, ARRAY_OBJ(chunks));
- }
- PUT(dict, "virt_lines", ARRAY_OBJ(all_chunks));
- PUT(dict, "virt_lines_above", BOOLEAN_OBJ(decor->virt_lines_above));
- PUT(dict, "virt_lines_leftcol", BOOLEAN_OBJ(virt_lines_leftcol));
+ if (mt_invalid(start)) {
+ PUT(dict, "invalid", BOOLEAN_OBJ(true));
}
- if (decor->hl_id || kv_size(decor->virt_text) || decor->ui_watched) {
- PUT(dict, "priority", INTEGER_OBJ(decor->priority));
- }
+ decor_to_dict_legacy(&dict, mt_decor(start), hl_name);
- if (dict.size) {
- ADD(rv, DICTIONARY_OBJ(dict));
- }
+ ADD(rv, DICTIONARY_OBJ(dict));
}
return rv;
@@ -202,6 +183,7 @@ static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict
/// @param id Extmark id
/// @param opts Optional parameters. Keys:
/// - details: Whether to include the details dict
+/// - hl_name: Whether to include highlight group name instead of id, true if omitted
/// @param[out] err Error details, if any
/// @return 0-indexed (row, col) tuple or empty list () if extmark id was
/// absent
@@ -218,80 +200,92 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id,
return rv;
}
- if (!ns_initialized((uint32_t)ns_id)) {
- api_set_error(err, kErrorTypeValidation, "Invalid ns_id");
+ VALIDATE_INT(ns_initialized((uint32_t)ns_id), "ns_id", ns_id, {
return rv;
- }
+ });
bool details = false;
+ bool hl_name = true;
for (size_t i = 0; i < opts.size; i++) {
String k = opts.items[i].key;
Object *v = &opts.items[i].value;
if (strequal("details", k.data)) {
- if (v->type == kObjectTypeBoolean) {
- details = v->data.boolean;
- } else if (v->type == kObjectTypeInteger) {
- details = v->data.integer;
- } else {
- api_set_error(err, kErrorTypeValidation, "details is not an boolean");
+ details = api_object_to_bool(*v, "details", false, err);
+ if (ERROR_SET(err)) {
+ return rv;
+ }
+ } else if (strequal("hl_name", k.data)) {
+ hl_name = api_object_to_bool(*v, "hl_name", false, err);
+ if (ERROR_SET(err)) {
return rv;
}
} else {
- api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data);
- return rv;
+ VALIDATE_S(false, "'opts' key", k.data, {
+ return rv;
+ });
}
}
- ExtmarkInfo extmark = extmark_from_id(buf, (uint32_t)ns_id, (uint32_t)id);
- if (extmark.row < 0) {
+ MTPair extmark = extmark_from_id(buf, (uint32_t)ns_id, (uint32_t)id);
+ if (extmark.start.pos.row < 0) {
return rv;
}
- return extmark_to_array(&extmark, false, details);
+ return extmark_to_array(extmark, false, details, hl_name);
}
-/// Gets |extmarks| in "traversal order" from a |charwise| region defined by
-/// buffer positions (inclusive, 0-indexed |api-indexing|).
+/// Gets |extmarks| (including |signs|) 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>lua
-/// vim.api.nvim_buf_get_extmarks(0, my_ns, 0, -1, {})
-/// vim.api.nvim_buf_get_extmarks(0, my_ns, {0,0}, {-1,-1}, {})
-/// </pre>
+///
+/// ```lua
+/// vim.api.nvim_buf_get_extmarks(0, my_ns, 0, -1, {})
+/// vim.api.nvim_buf_get_extmarks(0, my_ns, {0,0}, {-1,-1}, {})
+/// ```
///
/// If `end` is less than `start`, traversal works backwards. (Useful
/// with `limit`, to get the first marks prior to a given position.)
///
+/// Note: when using extmark ranges (marks with a end_row/end_col position)
+/// the `overlap` option might be useful. Otherwise only the start position
+/// of an extmark will be considered.
+///
/// Example:
-/// <pre>lua
-/// 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, {})
-/// -- Create new extmark at line 3, column 1.
-/// local m2 = a.nvim_buf_set_extmark(0, ns, 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>
+///
+/// ```lua
+/// local api = vim.api
+/// local pos = api.nvim_win_get_cursor(0)
+/// local ns = api.nvim_create_namespace('my-plugin')
+/// -- Create new extmark at line 1, column 1.
+/// local m1 = api.nvim_buf_set_extmark(0, ns, 0, 0, {})
+/// -- Create new extmark at line 3, column 1.
+/// local m2 = api.nvim_buf_set_extmark(0, ns, 2, 0, {})
+/// -- Get extmarks only from line 3.
+/// local ms = api.nvim_buf_get_extmarks(0, ns, {2,0}, {2,0}, {})
+/// -- Get all marks in this buffer + namespace.
+/// local all = api.nvim_buf_get_extmarks(0, ns, 0, -1, {})
+/// vim.print(ms)
+/// ```
///
/// @param buffer Buffer handle, or 0 for current buffer
-/// @param ns_id Namespace id from |nvim_create_namespace()|
+/// @param ns_id Namespace id from |nvim_create_namespace()| or -1 for all namespaces
/// @param start Start of range: a 0-indexed (row, col) or valid extmark id
/// (whose position defines the bound). |api-indexing|
/// @param end End of range (inclusive): a 0-indexed (row, col) or valid
/// extmark id (whose position defines the bound). |api-indexing|
/// @param opts Optional parameters. Keys:
/// - limit: Maximum number of marks to return
-/// - details Whether to include the details dict
+/// - details: Whether to include the details dict
+/// - hl_name: Whether to include highlight group name instead of id, true if omitted
+/// - overlap: Also include marks which overlap the range, even if
+/// their start position is less than `start`
+/// - type: Filter marks by type: "highlight", "sign", "virt_text" and "virt_lines"
/// @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)
+Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object end,
+ Dict(get_extmarks) *opts, Error *err)
FUNC_API_SINCE(7)
{
Array rv = ARRAY_DICT_INIT;
@@ -301,38 +295,32 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
return rv;
}
- if (!ns_initialized((uint32_t)ns_id)) {
- api_set_error(err, kErrorTypeValidation, "Invalid ns_id");
+ VALIDATE_INT(ns_id == -1 || ns_initialized((uint32_t)ns_id), "ns_id", ns_id, {
return rv;
- }
-
- Integer limit = -1;
- bool details = false;
-
- 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;
- } else if (strequal("details", k.data)) {
- if (v->type == kObjectTypeBoolean) {
- details = v->data.boolean;
- } else if (v->type == kObjectTypeInteger) {
- details = v->data.integer;
- } else {
- api_set_error(err, kErrorTypeValidation, "details is not an boolean");
- return rv;
- }
+ });
+
+ bool details = opts->details;
+ bool hl_name = GET_BOOL_OR_TRUE(opts, get_extmarks, hl_name);
+
+ ExtmarkType type = kExtmarkNone;
+ if (HAS_KEY(opts, get_extmarks, type)) {
+ if (strequal(opts->type.data, "sign")) {
+ type = kExtmarkSign;
+ } else if (strequal(opts->type.data, "virt_text")) {
+ type = kExtmarkVirtText;
+ } else if (strequal(opts->type.data, "virt_lines")) {
+ type = kExtmarkVirtLines;
+ } else if (strequal(opts->type.data, "highlight")) {
+ type = kExtmarkHighlight;
} else {
- api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data);
- return rv;
+ VALIDATE_EXP(false, "type", "sign, virt_text, virt_lines or highlight", opts->type.data, {
+ return rv;
+ });
}
}
+ Integer limit = HAS_KEY(opts, get_extmarks, limit) ? opts->limit : -1;
+
if (limit == 0) {
return rv;
} else if (limit < 0) {
@@ -357,11 +345,12 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
reverse = true;
}
- ExtmarkInfoArray marks = extmark_get(buf, (uint32_t)ns_id, l_row, l_col,
- u_row, u_col, (int64_t)limit, reverse);
+ // note: ns_id=-1 allowed, represented as UINT32_MAX
+ ExtmarkInfoArray marks = extmark_get(buf, (uint32_t)ns_id, l_row, l_col, u_row,
+ u_col, (int64_t)limit, reverse, type, opts->overlap);
for (size_t i = 0; i < kv_size(marks); i++) {
- ADD(rv, ARRAY_OBJ(extmark_to_array(&kv_A(marks, i), true, (bool)details)));
+ ADD(rv, ARRAY_OBJ(extmark_to_array(kv_A(marks, i), true, details, hl_name)));
}
kv_destroy(marks);
@@ -379,6 +368,11 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
/// Using the optional arguments, it is possible to use this to highlight
/// a range of text, and also to associate virtual text to the mark.
///
+/// If present, the position defined by `end_col` and `end_row` should be after
+/// the start position in order for the extmark to cover a range.
+/// An earlier end position is not an error, but then it behaves like an empty
+/// range (no highlighting).
+///
/// @param buffer Buffer handle, or 0 for current buffer
/// @param ns_id Namespace id from |nvim_create_namespace()|
/// @param line Line where to place the mark, 0-based. |api-indexing|
@@ -402,24 +396,28 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
/// either as a string or as an integer, the latter which
/// can be obtained using |nvim_get_hl_id_by_name()|.
/// - virt_text_pos : position of virtual text. Possible values:
-/// - "eol": right after eol character (default)
+/// - "eol": right after eol character (default).
/// - "overlay": display over the specified column, without
/// shifting the underlying text.
/// - "right_align": display right aligned in the window.
+/// - "inline": display at the specified column, and
+/// shift the buffer text to the right as needed.
/// - virt_text_win_col : position the virtual text at a fixed
/// window column (starting from the first
-/// text column)
+/// text column of the screen line) instead
+/// of "virt_text_pos".
/// - virt_text_hide : hide the virtual text when the background
-/// text is selected or hidden due to
-/// horizontal scroll 'nowrap'
+/// text is selected or hidden because of
+/// scrolling with 'nowrap' or 'smoothscroll'.
+/// Currently only affects "overlay" virt_text.
/// - hl_mode : control how highlights are combined with the
/// highlights of the text. Currently only affects
/// virt_text highlights, but might affect `hl_group`
/// in later versions.
-/// - "replace": only show the virt_text color. This is the
-/// default
-/// - "combine": combine with background text color
+/// - "replace": only show the virt_text color. This is the default.
+/// - "combine": combine with background text color.
/// - "blend": blend with background text color.
+/// Not supported for "inline" virt_text.
///
/// - virt_lines : virtual lines to add next to this mark
/// This should be an array over lines, where each line in
@@ -443,11 +441,17 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
/// buffer.
/// - right_gravity : boolean that indicates the direction
/// the extmark will be shifted in when new text is inserted
-/// (true for right, false for left). defaults to true.
+/// (true for right, false for left). Defaults to true.
/// - end_right_gravity : boolean that indicates the direction
/// the extmark end position (if it exists) will be shifted
/// in when new text is inserted (true for right, false
/// for left). Defaults to false.
+/// - undo_restore : Restore the exact position of the mark
+/// if text around the mark was deleted and then restored by undo.
+/// Defaults to true.
+/// - invalidate : boolean that indicates whether to hide the
+/// extmark if the entirety of its range is deleted. If
+/// "undo_restore" is false, the extmark is deleted instead.
/// - priority: a priority value for the highlight group or sign
/// attribute. For example treesitter highlighting uses a
/// value of 100.
@@ -493,286 +497,255 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
Dict(set_extmark) *opts, Error *err)
FUNC_API_SINCE(7)
{
- Decoration decor = DECORATION_INIT;
- bool has_decor = false;
+ DecorHighlightInline hl = DECOR_HIGHLIGHT_INLINE_INIT;
+ // TODO(bfredl): in principle signs with max one (1) hl group and max 4 bytes of text.
+ // should be a candidate for inlining as well.
+ DecorSignHighlight sign = DECOR_SIGN_HIGHLIGHT_INIT;
+ DecorVirtText virt_text = DECOR_VIRT_TEXT_INIT;
+ DecorVirtText virt_lines = DECOR_VIRT_LINES_INIT;
+ bool has_hl = false;
buf_T *buf = find_buffer_by_handle(buffer, err);
if (!buf) {
goto error;
}
- if (!ns_initialized((uint32_t)ns_id)) {
- api_set_error(err, kErrorTypeValidation, "Invalid ns_id");
+ VALIDATE_INT(ns_initialized((uint32_t)ns_id), "ns_id", ns_id, {
goto error;
- }
+ });
uint32_t id = 0;
- if (opts->id.type == kObjectTypeInteger && opts->id.data.integer > 0) {
- id = (uint32_t)opts->id.data.integer;
- } else if (HAS_KEY(opts->id)) {
- api_set_error(err, kErrorTypeValidation, "id is not a positive integer");
- goto error;
+ if (HAS_KEY(opts, set_extmark, id)) {
+ VALIDATE_EXP((opts->id > 0), "id", "positive Integer", NULL, {
+ goto error;
+ });
+
+ id = (uint32_t)opts->id;
}
int line2 = -1;
+ bool did_end_line = false;
// For backward compatibility we support "end_line" as an alias for "end_row"
- if (HAS_KEY(opts->end_line)) {
- if (HAS_KEY(opts->end_row)) {
- api_set_error(err, kErrorTypeValidation, "cannot use both end_row and end_line");
+ if (HAS_KEY(opts, set_extmark, end_line)) {
+ VALIDATE(!HAS_KEY(opts, set_extmark, end_row),
+ "%s", "cannot use both 'end_row' and 'end_line'", {
goto error;
- }
- opts->end_row = opts->end_line;
- }
+ });
-#define OPTION_TO_BOOL(target, name, val) \
- target = api_object_to_bool(opts->name, #name, val, err); \
- if (ERROR_SET(err)) { \
- goto error; \
+ opts->end_row = opts->end_line;
+ did_end_line = true;
}
- bool strict = true;
- OPTION_TO_BOOL(strict, strict, true);
+ bool strict = GET_BOOL_OR_TRUE(opts, set_extmark, strict);
- if (opts->end_row.type == kObjectTypeInteger) {
- Integer val = opts->end_row.data.integer;
- if (val < 0 || (val > buf->b_ml.ml_line_count && strict)) {
- api_set_error(err, kErrorTypeValidation, "end_row value outside range");
+ if (HAS_KEY(opts, set_extmark, end_row) || did_end_line) {
+ Integer val = opts->end_row;
+ VALIDATE_RANGE((val >= 0 && !(val > buf->b_ml.ml_line_count && strict)), "end_row", {
goto error;
- } else {
- line2 = (int)val;
- }
- } else if (HAS_KEY(opts->end_row)) {
- api_set_error(err, kErrorTypeValidation, "end_row is not an integer");
- goto error;
+ });
+ line2 = (int)val;
}
colnr_T col2 = -1;
- if (opts->end_col.type == kObjectTypeInteger) {
- Integer val = opts->end_col.data.integer;
- if (val < 0 || val > MAXCOL) {
- api_set_error(err, kErrorTypeValidation, "end_col value outside range");
+ if (HAS_KEY(opts, set_extmark, end_col)) {
+ Integer val = opts->end_col;
+ VALIDATE_RANGE((val >= 0 && val <= MAXCOL), "end_col", {
goto error;
- } else {
- col2 = (int)val;
- }
- } else if (HAS_KEY(opts->end_col)) {
- api_set_error(err, kErrorTypeValidation, "end_col is not an integer");
- goto error;
+ });
+ col2 = (int)val;
}
// uncrustify:off
+ // TODO(bfredl): keyset type alias for hl_group? (nil|int|string)
struct {
const char *name;
Object *opt;
int *dest;
} hls[] = {
- { "hl_group" , &opts->hl_group , &decor.hl_id },
- { "sign_hl_group" , &opts->sign_hl_group , &decor.sign_hl_id },
- { "number_hl_group" , &opts->number_hl_group , &decor.number_hl_id },
- { "line_hl_group" , &opts->line_hl_group , &decor.line_hl_id },
- { "cursorline_hl_group", &opts->cursorline_hl_group, &decor.cursorline_hl_id },
+ { "hl_group" , &opts->hl_group , &hl.hl_id },
+ { "sign_hl_group" , &opts->sign_hl_group , &sign.hl_id },
+ { "number_hl_group" , &opts->number_hl_group , &sign.number_hl_id },
+ { "line_hl_group" , &opts->line_hl_group , &sign.line_hl_id },
+ { "cursorline_hl_group", &opts->cursorline_hl_group, &sign.cursorline_hl_id },
{ NULL, NULL, NULL },
};
// uncrustify:on
for (int j = 0; hls[j].name && hls[j].dest; j++) {
- if (HAS_KEY(*hls[j].opt)) {
+ if (hls[j].opt->type != kObjectTypeNil) {
+ if (j > 0) {
+ sign.flags |= kSHIsSign;
+ } else {
+ has_hl = true;
+ }
*hls[j].dest = object_to_hl_id(*hls[j].opt, hls[j].name, err);
if (ERROR_SET(err)) {
goto error;
}
- has_decor = true;
}
}
- if (opts->conceal.type == kObjectTypeString) {
- String c = opts->conceal.data.string;
- decor.conceal = true;
- if (c.size) {
- decor.conceal_char = utf_ptr2char(c.data);
+ if (HAS_KEY(opts, set_extmark, conceal)) {
+ hl.flags |= kSHConceal;
+ has_hl = true;
+ String c = opts->conceal;
+ if (c.size > 0) {
+ int ch;
+ hl.conceal_char = utfc_ptr2schar_len(c.data, (int)c.size, &ch);
+ if (!hl.conceal_char || !vim_isprintc(ch)) {
+ api_set_error(err, kErrorTypeValidation, "conceal char has to be printable");
+ goto error;
+ }
}
- has_decor = true;
- } else if (HAS_KEY(opts->conceal)) {
- api_set_error(err, kErrorTypeValidation, "conceal is not a String");
- goto error;
}
- if (opts->virt_text.type == kObjectTypeArray) {
- decor.virt_text = parse_virt_text(opts->virt_text.data.array, err,
- &decor.virt_text_width);
- has_decor = true;
+ if (HAS_KEY(opts, set_extmark, virt_text)) {
+ virt_text.data.virt_text = parse_virt_text(opts->virt_text, err, &virt_text.width);
if (ERROR_SET(err)) {
goto error;
}
- } else if (HAS_KEY(opts->virt_text)) {
- api_set_error(err, kErrorTypeValidation, "virt_text is not an Array");
- goto error;
}
- if (opts->virt_text_pos.type == kObjectTypeString) {
- String str = opts->virt_text_pos.data.string;
+ if (HAS_KEY(opts, set_extmark, virt_text_pos)) {
+ String str = opts->virt_text_pos;
if (strequal("eol", str.data)) {
- decor.virt_text_pos = kVTEndOfLine;
+ virt_text.pos = kVPosEndOfLine;
} else if (strequal("overlay", str.data)) {
- decor.virt_text_pos = kVTOverlay;
+ virt_text.pos = kVPosOverlay;
} else if (strequal("right_align", str.data)) {
- decor.virt_text_pos = kVTRightAlign;
+ virt_text.pos = kVPosRightAlign;
+ } else if (strequal("inline", str.data)) {
+ virt_text.pos = kVPosInline;
} else {
- api_set_error(err, kErrorTypeValidation, "virt_text_pos: invalid value");
- goto error;
+ VALIDATE_S(false, "virt_text_pos", str.data, {
+ goto error;
+ });
}
- } else if (HAS_KEY(opts->virt_text_pos)) {
- api_set_error(err, kErrorTypeValidation, "virt_text_pos is not a String");
- goto error;
}
- if (opts->virt_text_win_col.type == kObjectTypeInteger) {
- decor.col = (int)opts->virt_text_win_col.data.integer;
- decor.virt_text_pos = kVTWinCol;
- } else if (HAS_KEY(opts->virt_text_win_col)) {
- api_set_error(err, kErrorTypeValidation,
- "virt_text_win_col is not a Number of the correct size");
- goto error;
+ if (HAS_KEY(opts, set_extmark, virt_text_win_col)) {
+ virt_text.col = (int)opts->virt_text_win_col;
+ virt_text.pos = kVPosWinCol;
}
- OPTION_TO_BOOL(decor.virt_text_hide, virt_text_hide, false);
- OPTION_TO_BOOL(decor.hl_eol, hl_eol, false);
+ hl.flags |= opts->hl_eol ? kSHHlEol : 0;
+ virt_text.flags |= opts->virt_text_hide ? kVTHide : 0;
- if (opts->hl_mode.type == kObjectTypeString) {
- String str = opts->hl_mode.data.string;
+ if (HAS_KEY(opts, set_extmark, hl_mode)) {
+ String str = opts->hl_mode;
if (strequal("replace", str.data)) {
- decor.hl_mode = kHlModeReplace;
+ virt_text.hl_mode = kHlModeReplace;
} else if (strequal("combine", str.data)) {
- decor.hl_mode = kHlModeCombine;
+ virt_text.hl_mode = kHlModeCombine;
} else if (strequal("blend", str.data)) {
- decor.hl_mode = kHlModeBlend;
+ if (virt_text.pos == kVPosInline) {
+ VALIDATE(false, "%s", "cannot use 'blend' hl_mode with inline virtual text", {
+ goto error;
+ });
+ }
+ virt_text.hl_mode = kHlModeBlend;
} else {
- api_set_error(err, kErrorTypeValidation,
- "virt_text_pos: invalid value");
- goto error;
+ VALIDATE_S(false, "hl_mode", str.data, {
+ goto error;
+ });
}
- } else if (HAS_KEY(opts->hl_mode)) {
- api_set_error(err, kErrorTypeValidation, "hl_mode is not a String");
- goto error;
}
- bool virt_lines_leftcol = false;
- OPTION_TO_BOOL(virt_lines_leftcol, virt_lines_leftcol, false);
+ bool virt_lines_leftcol = opts->virt_lines_leftcol;
- if (opts->virt_lines.type == kObjectTypeArray) {
- Array a = opts->virt_lines.data.array;
+ if (HAS_KEY(opts, set_extmark, virt_lines)) {
+ Array a = opts->virt_lines;
for (size_t j = 0; j < a.size; j++) {
- if (a.items[j].type != kObjectTypeArray) {
- api_set_error(err, kErrorTypeValidation, "virt_text_line item is not an Array");
+ VALIDATE_T("virt_text_line", kObjectTypeArray, a.items[j].type, {
goto error;
- }
+ });
int dummig;
VirtText jtem = parse_virt_text(a.items[j].data.array, err, &dummig);
- kv_push(decor.virt_lines, ((struct virt_line){ jtem, virt_lines_leftcol }));
+ kv_push(virt_lines.data.virt_lines, ((struct virt_line){ jtem, virt_lines_leftcol }));
if (ERROR_SET(err)) {
goto error;
}
- has_decor = true;
}
- } else if (HAS_KEY(opts->virt_lines)) {
- api_set_error(err, kErrorTypeValidation, "virt_lines is not an Array");
- goto error;
}
- OPTION_TO_BOOL(decor.virt_lines_above, virt_lines_above, false);
-
- if (opts->priority.type == kObjectTypeInteger) {
- Integer val = opts->priority.data.integer;
+ virt_lines.flags |= opts->virt_lines_above ? kVTLinesAbove : 0;
- if (val < 0 || val > UINT16_MAX) {
- api_set_error(err, kErrorTypeValidation, "priority is not a valid value");
+ if (HAS_KEY(opts, set_extmark, priority)) {
+ VALIDATE_RANGE((opts->priority >= 0 && opts->priority <= UINT16_MAX), "priority", {
goto error;
- }
- decor.priority = (DecorPriority)val;
- } else if (HAS_KEY(opts->priority)) {
- api_set_error(err, kErrorTypeValidation, "priority is not a Number of the correct size");
- goto error;
+ });
+ hl.priority = (DecorPriority)opts->priority;
+ sign.priority = (DecorPriority)opts->priority;
+ virt_text.priority = (DecorPriority)opts->priority;
+ virt_lines.priority = (DecorPriority)opts->priority;
}
- if (opts->sign_text.type == kObjectTypeString) {
- if (!init_sign_text(&decor.sign_text,
- opts->sign_text.data.string.data)) {
- api_set_error(err, kErrorTypeValidation, "sign_text is not a valid value");
+ if (HAS_KEY(opts, set_extmark, sign_text)) {
+ sign.text.ptr = NULL;
+ VALIDATE_S(init_sign_text(NULL, &sign.text.ptr, opts->sign_text.data),
+ "sign_text", "", {
goto error;
- }
- has_decor = true;
- } else if (HAS_KEY(opts->sign_text)) {
- api_set_error(err, kErrorTypeValidation, "sign_text is not a String");
- goto error;
+ });
+ sign.flags |= kSHIsSign;
}
- bool right_gravity = true;
- OPTION_TO_BOOL(right_gravity, right_gravity, true);
+ bool right_gravity = GET_BOOL_OR_TRUE(opts, set_extmark, right_gravity);
// Only error out if they try to set end_right_gravity without
// setting end_col or end_row
- if (line2 == -1 && col2 == -1 && HAS_KEY(opts->end_right_gravity)) {
- api_set_error(err, kErrorTypeValidation,
- "cannot set end_right_gravity without setting end_row or end_col");
+ VALIDATE(!(line2 == -1 && col2 == -1 && HAS_KEY(opts, set_extmark, end_right_gravity)),
+ "%s", "cannot set end_right_gravity without end_row or end_col", {
goto error;
- }
-
- bool end_right_gravity = false;
- OPTION_TO_BOOL(end_right_gravity, end_right_gravity, false);
+ });
size_t len = 0;
- bool ephemeral = false;
- OPTION_TO_BOOL(ephemeral, ephemeral, false);
-
- if (opts->spell.type == kObjectTypeNil) {
- decor.spell = kNone;
- } else {
- bool spell = false;
- OPTION_TO_BOOL(spell, spell, false);
- decor.spell = spell ? kTrue : kFalse;
- has_decor = true;
+ if (HAS_KEY(opts, set_extmark, spell)) {
+ hl.flags |= (opts->spell) ? kSHSpellOn : kSHSpellOff;
+ has_hl = true;
}
- OPTION_TO_BOOL(decor.ui_watched, ui_watched, false);
- if (decor.ui_watched) {
- has_decor = true;
+ if (opts->ui_watched) {
+ hl.flags |= kSHUIWatched;
+ if (virt_text.pos == kVPosOverlay) {
+ // TODO(bfredl): in a revised interface this should be the default.
+ hl.flags |= kSHUIWatchedOverlay;
+ }
+ has_hl = true;
}
- if (line < 0) {
- api_set_error(err, kErrorTypeValidation, "line value outside range");
+ VALIDATE_RANGE((line >= 0), "line", {
goto error;
- } else if (line > buf->b_ml.ml_line_count) {
- if (strict) {
- api_set_error(err, kErrorTypeValidation, "line value outside range");
+ });
+
+ if (line > buf->b_ml.ml_line_count) {
+ VALIDATE_RANGE(!strict, "line", {
goto error;
- } else {
- line = buf->b_ml.ml_line_count;
- }
+ });
+ line = buf->b_ml.ml_line_count;
} else if (line < buf->b_ml.ml_line_count) {
- len = ephemeral ? MAXCOL : strlen(ml_get_buf(buf, (linenr_T)line + 1, false));
+ len = opts->ephemeral ? MAXCOL : strlen(ml_get_buf(buf, (linenr_T)line + 1));
}
if (col == -1) {
col = (Integer)len;
} else if (col > (Integer)len) {
- if (strict) {
- api_set_error(err, kErrorTypeValidation, "col value outside range");
+ VALIDATE_RANGE(!strict, "col", {
goto error;
- } else {
- col = (Integer)len;
- }
+ });
+ col = (Integer)len;
} else if (col < -1) {
- api_set_error(err, kErrorTypeValidation, "col value outside range");
- goto error;
+ VALIDATE_RANGE(false, "col", {
+ goto error;
+ });
}
if (col2 >= 0) {
if (line2 >= 0 && line2 < buf->b_ml.ml_line_count) {
- len = ephemeral ? MAXCOL : strlen(ml_get_buf(buf, (linenr_T)line2 + 1, false));
+ len = opts->ephemeral ? MAXCOL : strlen(ml_get_buf(buf, (linenr_T)line2 + 1));
} else if (line2 == buf->b_ml.ml_line_count) {
// We are trying to add an extmark past final newline
len = 0;
@@ -781,36 +754,96 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
line2 = (int)line;
}
if (col2 > (Integer)len) {
- if (strict) {
- api_set_error(err, kErrorTypeValidation, "end_col value outside range");
+ VALIDATE_RANGE(!strict, "end_col", {
goto error;
- } else {
- col2 = (int)len;
- }
+ });
+ col2 = (int)len;
}
} else if (line2 >= 0) {
col2 = 0;
}
- // TODO(bfredl): synergize these two branches even more
- if (ephemeral && decor_state.buf == buf) {
- decor_add_ephemeral((int)line, (int)col, line2, col2, &decor, (uint64_t)ns_id, id);
+ if (opts->ephemeral && decor_state.win && decor_state.win->w_buffer == buf) {
+ int r = (int)line;
+ int c = (int)col;
+ if (line2 == -1) {
+ line2 = r;
+ col2 = c;
+ }
+
+ if (kv_size(virt_text.data.virt_text)) {
+ decor_range_add_virt(&decor_state, r, c, line2, col2, decor_put_vt(virt_text, NULL), true);
+ }
+ if (kv_size(virt_lines.data.virt_lines)) {
+ decor_range_add_virt(&decor_state, r, c, line2, col2, decor_put_vt(virt_lines, NULL), true);
+ }
+ if (has_hl) {
+ DecorSignHighlight sh = decor_sh_from_inline(hl);
+ decor_range_add_sh(&decor_state, r, c, line2, col2, &sh, true, (uint32_t)ns_id, id);
+ }
} else {
- if (ephemeral) {
+ if (opts->ephemeral) {
api_set_error(err, kErrorTypeException, "not yet implemented");
goto error;
}
+ uint16_t decor_flags = 0;
+
+ DecorVirtText *decor_alloc = NULL;
+ if (kv_size(virt_text.data.virt_text)) {
+ decor_alloc = decor_put_vt(virt_text, decor_alloc);
+ if (virt_text.pos == kVPosInline) {
+ decor_flags |= MT_FLAG_DECOR_VIRT_TEXT_INLINE;
+ }
+ }
+ if (kv_size(virt_lines.data.virt_lines)) {
+ decor_alloc = decor_put_vt(virt_lines, decor_alloc);
+ decor_flags |= MT_FLAG_DECOR_VIRT_LINES;
+ }
+
+ uint32_t decor_indexed = DECOR_ID_INVALID;
+ if (sign.flags & kSHIsSign) {
+ decor_indexed = decor_put_sh(sign);
+ if (sign.text.ptr != NULL) {
+ decor_flags |= MT_FLAG_DECOR_SIGNTEXT;
+ }
+ if (sign.number_hl_id || sign.line_hl_id || sign.cursorline_hl_id) {
+ decor_flags |= MT_FLAG_DECOR_SIGNHL;
+ }
+ }
+
+ DecorInline decor = DECOR_INLINE_INIT;
+ if (decor_alloc || decor_indexed != DECOR_ID_INVALID || schar_high(hl.conceal_char)) {
+ if (has_hl) {
+ DecorSignHighlight sh = decor_sh_from_inline(hl);
+ sh.next = decor_indexed;
+ decor_indexed = decor_put_sh(sh);
+ }
+ decor.ext = true;
+ decor.data.ext = (DecorExt){ .sh_idx = decor_indexed, .vt = decor_alloc };
+ } else {
+ decor.data.hl = hl;
+ }
+
+ if (has_hl) {
+ decor_flags |= MT_FLAG_DECOR_HL;
+ }
+
extmark_set(buf, (uint32_t)ns_id, &id, (int)line, (colnr_T)col, line2, col2,
- has_decor ? &decor : NULL, right_gravity, end_right_gravity,
- kExtmarkNoUndo);
+ decor, decor_flags, right_gravity, opts->end_right_gravity,
+ !GET_BOOL_OR_TRUE(opts, set_extmark, undo_restore),
+ opts->invalidate, err);
+ if (ERROR_SET(err)) {
+ decor_free(decor);
+ return 0;
+ }
}
return (Integer)id;
error:
- clear_virttext(&decor.virt_text);
- xfree(decor.sign_text);
+ clear_virttext(&virt_text.data.virt_text);
+ clear_virtlines(&virt_lines.data.virt_lines);
return 0;
}
@@ -829,12 +862,11 @@ Boolean nvim_buf_del_extmark(Buffer buffer, Integer ns_id, Integer id, Error *er
if (!buf) {
return false;
}
- if (!ns_initialized((uint32_t)ns_id)) {
- api_set_error(err, kErrorTypeValidation, "Invalid ns_id");
+ VALIDATE_INT(ns_initialized((uint32_t)ns_id), "ns_id", ns_id, {
return false;
- }
+ });
- return extmark_del(buf, (uint32_t)ns_id, (uint32_t)id);
+ return extmark_del_id(buf, (uint32_t)ns_id, (uint32_t)id);
}
uint32_t src2ns(Integer *src_id)
@@ -887,14 +919,13 @@ Integer nvim_buf_add_highlight(Buffer buffer, Integer ns_id, String hl_group, In
return 0;
}
- if (line < 0 || line >= MAXLNUM) {
- api_set_error(err, kErrorTypeValidation, "Line number outside range");
+ VALIDATE_RANGE((line >= 0 && line < MAXLNUM), "line number", {
return 0;
- }
- if (col_start < 0 || col_start > MAXCOL) {
- api_set_error(err, kErrorTypeValidation, "Column value outside range");
+ });
+ VALIDATE_RANGE((col_start >= 0 && col_start <= MAXCOL), "column", {
return 0;
- }
+ });
+
if (col_end < 0 || col_end > MAXCOL) {
col_end = MAXCOL;
}
@@ -919,13 +950,11 @@ Integer nvim_buf_add_highlight(Buffer buffer, Integer ns_id, String hl_group, In
end_line++;
}
- Decoration decor = DECORATION_INIT;
- decor.hl_id = hl_id;
+ DecorInline decor = DECOR_INLINE_INIT;
+ decor.data.hl.hl_id = hl_id;
- extmark_set(buf, ns, NULL,
- (int)line, (colnr_T)col_start,
- end_line, (colnr_T)col_end,
- &decor, true, false, kExtmarkNoUndo);
+ extmark_set(buf, ns, NULL, (int)line, (colnr_T)col_start, end_line, (colnr_T)col_end,
+ decor, MT_FLAG_DECOR_HL, true, false, false, false, NULL);
return ns_id;
}
@@ -950,10 +979,10 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start,
return;
}
- if (line_start < 0 || line_start >= MAXLNUM) {
- api_set_error(err, kErrorTypeValidation, "Line number outside range");
+ VALIDATE_RANGE((line_start >= 0 && line_start < MAXLNUM), "line number", {
return;
- }
+ });
+
if (line_end < 0 || line_end > MAXLNUM) {
line_end = MAXLNUM;
}
@@ -964,14 +993,14 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start,
/// Set or change decoration provider for a |namespace|
///
-/// This is a very general purpose interface for having lua callbacks
+/// This is a very general purpose interface for having Lua callbacks
/// being triggered during the redraw code.
///
/// The expected usage is to set |extmarks| for the currently
/// redrawn buffer. |nvim_buf_set_extmark()| can be called to add marks
/// on a per-window or per-lines basis. Use the `ephemeral` key to only
/// use the mark for the current screen redraw (the callback will be called
-/// again for the next redraw ).
+/// again for the next redraw).
///
/// Note: this function should not be called often. Rather, the callbacks
/// themselves can be used to throttle unneeded callbacks. the `on_start`
@@ -983,11 +1012,13 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start,
/// for the extmarks set/modified inside the callback anyway.
///
/// Note: doing anything other than setting extmarks is considered experimental.
-/// Doing things like changing options are not expliticly forbidden, but is
+/// Doing things like changing options are not explicitly forbidden, but is
/// likely to have unexpected consequences (such as 100% CPU consumption).
/// doing `vim.rpcnotify` should be OK, but `vim.rpcrequest` is quite dubious
/// for the moment.
///
+/// Note: It is not allowed to remove or update extmarks in 'on_line' callbacks.
+///
/// @param ns_id Namespace id from |nvim_create_namespace()|
/// @param opts Table of callbacks:
/// - on_start: called first on each screen redraw
@@ -996,7 +1027,8 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start,
/// window callbacks)
/// ["buf", bufnr, tick]
/// - on_win: called when starting to redraw a
-/// specific window.
+/// specific window. botline_guess is an approximation
+/// that does not exceed the last line number.
/// ["win", winid, bufnr, topline, botline_guess]
/// - on_line: called for each buffer line being redrawn.
/// (The interaction with fold lines is subject to change)
@@ -1015,7 +1047,7 @@ void nvim_set_decoration_provider(Integer ns_id, Dict(set_decoration_provider) *
struct {
const char *name;
- Object *source;
+ LuaRef *source;
LuaRef *dest;
} cbs[] = {
{ "on_start", &opts->on_start, &p->redraw_start },
@@ -1029,26 +1061,18 @@ void nvim_set_decoration_provider(Integer ns_id, Dict(set_decoration_provider) *
};
for (size_t i = 0; cbs[i].source && cbs[i].dest && cbs[i].name; i++) {
- Object *v = cbs[i].source;
- if (v->type == kObjectTypeNil) {
+ LuaRef *v = cbs[i].source;
+ if (*v <= 0) {
continue;
}
- if (v->type != kObjectTypeLuaRef) {
- api_set_error(err, kErrorTypeValidation,
- "%s is not a function", cbs[i].name);
- goto error;
- }
- *(cbs[i].dest) = v->data.luaref;
- v->data.luaref = LUA_NOREF;
+ *(cbs[i].dest) = *v;
+ *v = LUA_NOREF;
}
p->active = true;
p->hl_valid++;
p->hl_cached = false;
- return;
-error:
- decor_provider_clear(p);
}
/// Gets the line and column of an |extmark|.
@@ -1075,75 +1099,40 @@ static bool extmark_get_index_from_obj(buf_T *buf, Integer ns_id, Object obj, in
*col = MAXCOL;
return true;
} else if (id < 0) {
- api_set_error(err, kErrorTypeValidation, "Mark id must be positive");
- return false;
+ VALIDATE_INT(false, "mark id", id, {
+ return false;
+ });
}
- ExtmarkInfo extmark = extmark_from_id(buf, (uint32_t)ns_id, (uint32_t)id);
- if (extmark.row >= 0) {
- *row = extmark.row;
- *col = extmark.col;
- return true;
- } else {
- api_set_error(err, kErrorTypeValidation, "No mark with requested id");
+ MTPair extmark = extmark_from_id(buf, (uint32_t)ns_id, (uint32_t)id);
+
+ VALIDATE_INT((extmark.start.pos.row >= 0), "mark id (not found)", id, {
return false;
- }
+ });
+ *row = extmark.start.pos.row;
+ *col = extmark.start.pos.col;
+ return true;
// 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");
+ VALIDATE_EXP((pos.size == 2
+ && pos.items[0].type == kObjectTypeInteger
+ && pos.items[1].type == kObjectTypeInteger),
+ "mark position", "2 Integer items", NULL, {
return false;
- }
+ });
+
Integer pos_row = pos.items[0].data.integer;
Integer pos_col = pos.items[1].data.integer;
- *row = (int)(pos_row >= 0 ? pos_row : MAXLNUM);
+ *row = (int)(pos_row >= 0 ? pos_row : MAXLNUM);
*col = (colnr_T)(pos_col >= 0 ? pos_col : MAXCOL);
return true;
} else {
- api_set_error(err, kErrorTypeValidation,
- "Position must be a mark id Integer or position Array");
- return false;
- }
-}
-// adapted from sign.c:sign_define_init_text.
-// TODO(lewis6991): Consider merging
-static int init_sign_text(char **sign_text, char *text)
-{
- char *s;
-
- char *endp = text + (int)strlen(text);
-
- // Count cells and check for non-printable chars
- int cells = 0;
- for (s = text; s < endp; s += utfc_ptr2len(s)) {
- if (!vim_isprintc(utf_ptr2char(s))) {
- break;
- }
- cells += utf_ptr2cells(s);
- }
- // Currently must be empty, one or two display cells
- if (s != endp || cells > 2) {
- return FAIL;
- }
- if (cells < 1) {
- return OK;
- }
-
- // Allocate one byte more if we need to pad up
- // with a space.
- size_t len = (size_t)(endp - text + ((cells == 1) ? 1 : 0));
- *sign_text = xstrnsave(text, len);
-
- if (cells == 1) {
- STRCPY(*sign_text + len - 1, " ");
+ VALIDATE_EXP(false, "mark position", "mark id Integer or 2-item Array", NULL, {
+ return false;
+ });
}
-
- return OK;
}
VirtText parse_virt_text(Array chunks, Error *err, int *width)
@@ -1151,17 +1140,14 @@ VirtText parse_virt_text(Array chunks, Error *err, int *width)
VirtText virt_text = KV_INITIAL_VALUE;
int w = 0;
for (size_t i = 0; i < chunks.size; i++) {
- if (chunks.items[i].type != kObjectTypeArray) {
- api_set_error(err, kErrorTypeValidation, "Chunk is not an array");
+ VALIDATE_T("chunk", kObjectTypeArray, chunks.items[i].type, {
goto free_exit;
- }
+ });
Array chunk = chunks.items[i].data.array;
- if (chunk.size == 0 || chunk.size > 2
- || chunk.items[0].type != kObjectTypeString) {
- api_set_error(err, kErrorTypeValidation,
- "Chunk is not an array with one or two strings");
+ VALIDATE((chunk.size > 0 && chunk.size <= 2 && chunk.items[0].type == kObjectTypeString),
+ "%s", "Invalid chunk: expected Array with 1 or 2 Strings", {
goto free_exit;
- }
+ });
String str = chunk.items[0].data.string;
@@ -1176,8 +1162,7 @@ VirtText parse_virt_text(Array chunks, Error *err, int *width)
goto free_exit;
}
if (j < arr.size - 1) {
- kv_push(virt_text, ((VirtTextChunk){ .text = NULL,
- .hl_id = hl_id }));
+ kv_push(virt_text, ((VirtTextChunk){ .text = NULL, .hl_id = hl_id }));
}
}
} else {
@@ -1194,10 +1179,23 @@ VirtText parse_virt_text(Array chunks, Error *err, int *width)
kv_push(virt_text, ((VirtTextChunk){ .text = text, .hl_id = hl_id }));
}
- *width = w;
+ if (width != NULL) {
+ *width = w;
+ }
return virt_text;
free_exit:
clear_virttext(&virt_text);
return virt_text;
}
+
+String nvim__buf_debug_extmarks(Buffer buffer, Boolean keys, Boolean dot, Error *err)
+ FUNC_API_SINCE(7)
+{
+ buf_T *buf = find_buffer_by_handle(buffer, err);
+ if (!buf) {
+ return NULL_STRING;
+ }
+
+ return mt_inspect(buf->b_marktree, keys, dot);
+}