From 46a87a5d2bac598fed0870f0d3c926087f95d30f Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 14 Feb 2023 05:19:04 -0500 Subject: refactor(api): VALIDATE macros #22187 Problem: - API validation involves too much boilerplate. - API validation errors are not consistently worded. Solution: Introduce some macros. Currently these are clumsy, but they at least help with consistency and avoid some nesting. --- src/nvim/api/extmark.c | 268 ++++++++++++++++++++++++------------------------- 1 file changed, 131 insertions(+), 137 deletions(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index ab3b3485e4..71ba9cfd4c 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -11,6 +11,7 @@ #include "nvim/api/extmark.h" #include "nvim/api/private/defs.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" @@ -218,10 +219,9 @@ 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; for (size_t i = 0; i < opts.size; i++) { @@ -233,12 +233,14 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, } else if (v->type == kObjectTypeInteger) { details = v->data.integer; } else { - api_set_error(err, kErrorTypeValidation, "details is not an boolean"); - return rv; + VALIDATE_EXP(false, "details", "Boolean or Integer", api_typename(v->type), { + return rv; + }); } } else { - api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data); - return rv; + VALIDATE_S(false, "key", k.data, { + return rv; + }); } } @@ -301,10 +303,9 @@ 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_initialized((uint32_t)ns_id), "ns_id", ns_id, { return rv; - } + }); Integer limit = -1; bool details = false; @@ -313,10 +314,9 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e 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"); + VALIDATE_T("limit", kObjectTypeInteger, v->type, { return rv; - } + }); limit = v->data.integer; } else if (strequal("details", k.data)) { if (v->type == kObjectTypeBoolean) { @@ -324,12 +324,14 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e } else if (v->type == kObjectTypeInteger) { details = v->data.integer; } else { - api_set_error(err, kErrorTypeValidation, "details is not an boolean"); - return rv; + VALIDATE_EXP(false, "details", "Boolean or Integer", api_typename(v->type), { + return rv; + }); } } else { - api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data); - return rv; + VALIDATE_S(false, "key", k.data, { + return rv; + }); } } @@ -501,27 +503,26 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer 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; + VALIDATE_S(false, "id (must be positive integer)", "", { + goto error; + }); } int line2 = -1; // 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"); + VALIDATE(!HAS_KEY(opts->end_row), "cannot use both end_row and end_line", { goto error; - } + }); opts->end_row = opts->end_line; } @@ -536,29 +537,28 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer 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"); + VALIDATE((val >= 0 && !(val > buf->b_ml.ml_line_count && strict)), + "end_row value outside range", { goto error; - } else { - line2 = (int)val; - } + }); + line2 = (int)val; } else if (HAS_KEY(opts->end_row)) { - api_set_error(err, kErrorTypeValidation, "end_row is not an integer"); - goto error; + VALIDATE_T("end_row", kObjectTypeInteger, opts->end_row.type, { + goto error; + }); } 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"); + VALIDATE((val >= 0 && val <= MAXCOL), "end_col value outside range", { goto error; - } else { - col2 = (int)val; - } + }); + col2 = (int)val; } else if (HAS_KEY(opts->end_col)) { - api_set_error(err, kErrorTypeValidation, "end_col is not an integer"); - goto error; + VALIDATE_T("end_col", kObjectTypeInteger, opts->end_col.type, { + goto error; + }); } // uncrustify:off @@ -596,8 +596,9 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } has_decor = true; } else if (HAS_KEY(opts->conceal)) { - api_set_error(err, kErrorTypeValidation, "conceal is not a String"); - goto error; + VALIDATE_T("conceal", kObjectTypeString, opts->conceal.type, { + goto error; + }); } if (opts->virt_text.type == kObjectTypeArray) { @@ -608,8 +609,9 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer goto error; } } else if (HAS_KEY(opts->virt_text)) { - api_set_error(err, kErrorTypeValidation, "virt_text is not an Array"); - goto error; + VALIDATE_T("virt_text", kObjectTypeArray, opts->virt_text.type, { + goto error; + }); } if (opts->virt_text_pos.type == kObjectTypeString) { @@ -621,21 +623,23 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } else if (strequal("right_align", str.data)) { decor.virt_text_pos = kVTRightAlign; } else { - api_set_error(err, kErrorTypeValidation, "virt_text_pos: invalid value"); - goto error; + VALIDATE_S(false, "virt_text_pos", "", { + goto error; + }); } } else if (HAS_KEY(opts->virt_text_pos)) { - api_set_error(err, kErrorTypeValidation, "virt_text_pos is not a String"); - goto error; + VALIDATE_T("virt_text_pos", kObjectTypeString, opts->virt_text_pos.type, { + 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; + VALIDATE_T("virt_text_win_col", kObjectTypeInteger, opts->virt_text_win_col.type, { + goto error; + }); } OPTION_TO_BOOL(decor.virt_text_hide, virt_text_hide, false); @@ -650,13 +654,14 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } else if (strequal("blend", str.data)) { decor.hl_mode = kHlModeBlend; } else { - api_set_error(err, kErrorTypeValidation, - "virt_text_pos: invalid value"); - goto error; + VALIDATE_S(false, "virt_text_pos", "", { + goto error; + }); } } else if (HAS_KEY(opts->hl_mode)) { - api_set_error(err, kErrorTypeValidation, "hl_mode is not a String"); - goto error; + VALIDATE_T("hl_mode", kObjectTypeString, opts->hl_mode.type, { + goto error; + }); } bool virt_lines_leftcol = false; @@ -665,10 +670,9 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer if (opts->virt_lines.type == kObjectTypeArray) { Array a = opts->virt_lines.data.array; 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 })); @@ -678,8 +682,9 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer has_decor = true; } } else if (HAS_KEY(opts->virt_lines)) { - api_set_error(err, kErrorTypeValidation, "virt_lines is not an Array"); - goto error; + VALIDATE_T("virt_lines", kObjectTypeArray, opts->virt_lines.type, { + goto error; + }); } OPTION_TO_BOOL(decor.virt_lines_above, virt_lines_above, false); @@ -687,26 +692,26 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer if (opts->priority.type == kObjectTypeInteger) { Integer val = opts->priority.data.integer; - if (val < 0 || val > UINT16_MAX) { - api_set_error(err, kErrorTypeValidation, "priority is not a valid value"); + VALIDATE_S((val >= 0 && val <= UINT16_MAX), "priority", "(out of range)", { 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; + VALIDATE_T("priority", kObjectTypeInteger, opts->priority.type, { + goto error; + }); } 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"); + VALIDATE_S(init_sign_text(&decor.sign_text, opts->sign_text.data.string.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; + VALIDATE_T("sign_text", kObjectTypeString, opts->sign_text.type, { + goto error; + }); } bool right_gravity = true; @@ -714,11 +719,10 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer // 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->end_right_gravity)), + "cannot set end_right_gravity without setting end_row or end_col", { goto error; - } + }); bool end_right_gravity = false; OPTION_TO_BOOL(end_right_gravity, end_right_gravity, false); @@ -742,16 +746,15 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer has_decor = true; } - if (line < 0) { - api_set_error(err, kErrorTypeValidation, "line value outside range"); + VALIDATE_S((line >= 0), "line", "(out of range)", { 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_S(!strict, "line", "(out of range)", { 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)); } @@ -759,15 +762,14 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer if (col == -1) { col = (Integer)len; } else if (col > (Integer)len) { - if (strict) { - api_set_error(err, kErrorTypeValidation, "col value outside range"); + VALIDATE_S(!strict, "col", "(out of range)", { 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_S(false, "col", "(out of range)", { + goto error; + }); } if (col2 >= 0) { @@ -781,12 +783,10 @@ 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_S(!strict, "end_col", "(out of range)", { goto error; - } else { - col2 = (int)len; - } + }); + col2 = (int)len; } } else if (line2 >= 0) { col2 = 0; @@ -829,10 +829,9 @@ 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); } @@ -887,14 +886,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_S((line >= 0 && line < MAXLNUM), "line number", "(out of range)", { return 0; - } - if (col_start < 0 || col_start > MAXCOL) { - api_set_error(err, kErrorTypeValidation, "Column value outside range"); + }); + VALIDATE_S((col_start >= 0 && col_start <= MAXCOL), "column", "(out of range)", { return 0; - } + }); + if (col_end < 0 || col_end > MAXCOL) { col_end = MAXCOL; } @@ -950,10 +948,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_S((line_start >= 0 && line_start < MAXLNUM), "line number", "(out of range)", { return; - } + }); + if (line_end < 0 || line_end > MAXLNUM) { line_end = MAXLNUM; } @@ -1034,11 +1032,10 @@ void nvim_set_decoration_provider(Integer ns_id, Dict(set_decoration_provider) * continue; } - if (v->type != kObjectTypeLuaRef) { - api_set_error(err, kErrorTypeValidation, - "%s is not a function", cbs[i].name); + VALIDATE_T(cbs[i].name, kObjectTypeLuaRef, v->type, { goto error; - } + }); + *(cbs[i].dest) = v->data.luaref; v->data.luaref = LUA_NOREF; } @@ -1075,39 +1072,39 @@ 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"); + + VALIDATE_INT((extmark.row >= 0), "mark id (not found)", id, { return false; - } + }); + *row = extmark.row; + *col = extmark.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((pos.size == 2 + && pos.items[0].type == kObjectTypeInteger + && pos.items[1].type == kObjectTypeInteger), + "Invalid position: expected 2 Integer items", { 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); *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; + VALIDATE(false, "Invalid position: expected mark id Integer or 2-item Array", { + return false; + }); } } // adapted from sign.c:sign_define_init_text. @@ -1151,17 +1148,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), + "Invalid chunk: expected Array with 1 or 2 Strings", { goto free_exit; - } + }); String str = chunk.items[0].data.string; -- cgit From ff3d04b75b4a9314815c37d53ebc4d035a043335 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 14 Feb 2023 08:07:38 -0500 Subject: refactor(api): VALIDATE macros #22256 - VALIDATE() takes a format string - deduplicate check_string_array - VALIDATE_RANGE - validate UI args --- src/nvim/api/extmark.c | 117 ++++++++++++++++++++++++------------------------- 1 file changed, 58 insertions(+), 59 deletions(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 71ba9cfd4c..15f759eddf 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -520,7 +520,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer // For backward compatibility we support "end_line" as an alias for "end_row" if (HAS_KEY(opts->end_line)) { - VALIDATE(!HAS_KEY(opts->end_row), "cannot use both end_row and end_line", { + VALIDATE(!HAS_KEY(opts->end_row), "%s", "cannot use both 'end_row' and 'end_line'", { goto error; }); opts->end_row = opts->end_line; @@ -535,30 +535,29 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer bool strict = true; OPTION_TO_BOOL(strict, strict, true); - if (opts->end_row.type == kObjectTypeInteger) { - Integer val = opts->end_row.data.integer; - VALIDATE((val >= 0 && !(val > buf->b_ml.ml_line_count && strict)), - "end_row value outside range", { + if (HAS_KEY(opts->end_row)) { + VALIDATE_T("end_row", kObjectTypeInteger, opts->end_row.type, { goto error; }); - line2 = (int)val; - } else if (HAS_KEY(opts->end_row)) { - VALIDATE_T("end_row", kObjectTypeInteger, opts->end_row.type, { + + Integer val = opts->end_row.data.integer; + VALIDATE_RANGE((val >= 0 && !(val > buf->b_ml.ml_line_count && strict)), "end_row", { goto error; }); + line2 = (int)val; } colnr_T col2 = -1; - if (opts->end_col.type == kObjectTypeInteger) { - Integer val = opts->end_col.data.integer; - VALIDATE((val >= 0 && val <= MAXCOL), "end_col value outside range", { + if (HAS_KEY(opts->end_col)) { + VALIDATE_T("end_col", kObjectTypeInteger, opts->end_col.type, { goto error; }); - col2 = (int)val; - } else if (HAS_KEY(opts->end_col)) { - VALIDATE_T("end_col", kObjectTypeInteger, opts->end_col.type, { + + Integer val = opts->end_col.data.integer; + VALIDATE_RANGE((val >= 0 && val <= MAXCOL), "end_col", { goto error; }); + col2 = (int)val; } // uncrustify:off @@ -588,33 +587,37 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } } - if (opts->conceal.type == kObjectTypeString) { + if (HAS_KEY(opts->conceal)) { + VALIDATE_T("conceal", kObjectTypeString, opts->conceal.type, { + goto error; + }); + String c = opts->conceal.data.string; decor.conceal = true; if (c.size) { decor.conceal_char = utf_ptr2char(c.data); } has_decor = true; - } else if (HAS_KEY(opts->conceal)) { - VALIDATE_T("conceal", kObjectTypeString, opts->conceal.type, { + } + + if (HAS_KEY(opts->virt_text)) { + VALIDATE_T("virt_text", kObjectTypeArray, opts->virt_text.type, { 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 (ERROR_SET(err)) { goto error; } - } else if (HAS_KEY(opts->virt_text)) { - VALIDATE_T("virt_text", kObjectTypeArray, opts->virt_text.type, { + } + + if (HAS_KEY(opts->virt_text_pos)) { + VALIDATE_T("virt_text_pos", kObjectTypeString, opts->virt_text_pos.type, { goto error; }); - } - if (opts->virt_text_pos.type == kObjectTypeString) { String str = opts->virt_text_pos.data.string; if (strequal("eol", str.data)) { decor.virt_text_pos = kVTEndOfLine; @@ -627,19 +630,15 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer goto error; }); } - } else if (HAS_KEY(opts->virt_text_pos)) { - VALIDATE_T("virt_text_pos", kObjectTypeString, opts->virt_text_pos.type, { - 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)) { + if (HAS_KEY(opts->virt_text_win_col)) { VALIDATE_T("virt_text_win_col", kObjectTypeInteger, opts->virt_text_win_col.type, { goto error; }); + + decor.col = (int)opts->virt_text_win_col.data.integer; + decor.virt_text_pos = kVTWinCol; } OPTION_TO_BOOL(decor.virt_text_hide, virt_text_hide, false); @@ -667,7 +666,11 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer bool virt_lines_leftcol = false; OPTION_TO_BOOL(virt_lines_leftcol, virt_lines_leftcol, false); - if (opts->virt_lines.type == kObjectTypeArray) { + if (HAS_KEY(opts->virt_lines)) { + VALIDATE_T("virt_lines", kObjectTypeArray, opts->virt_lines.type, { + goto error; + }); + Array a = opts->virt_lines.data.array; for (size_t j = 0; j < a.size; j++) { VALIDATE_T("virt_text_line", kObjectTypeArray, a.items[j].type, { @@ -681,37 +684,33 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } has_decor = true; } - } else if (HAS_KEY(opts->virt_lines)) { - VALIDATE_T("virt_lines", kObjectTypeArray, opts->virt_lines.type, { - goto error; - }); } OPTION_TO_BOOL(decor.virt_lines_above, virt_lines_above, false); - if (opts->priority.type == kObjectTypeInteger) { + if (HAS_KEY(opts->priority)) { + VALIDATE_T("priority", kObjectTypeInteger, opts->priority.type, { + goto error; + }); + Integer val = opts->priority.data.integer; - VALIDATE_S((val >= 0 && val <= UINT16_MAX), "priority", "(out of range)", { + VALIDATE_RANGE((val >= 0 && val <= UINT16_MAX), "priority", { goto error; }); decor.priority = (DecorPriority)val; - } else if (HAS_KEY(opts->priority)) { - VALIDATE_T("priority", kObjectTypeInteger, opts->priority.type, { + } + + if (HAS_KEY(opts->sign_text)) { + VALIDATE_T("sign_text", kObjectTypeString, opts->sign_text.type, { goto error; }); - } - if (opts->sign_text.type == kObjectTypeString) { VALIDATE_S(init_sign_text(&decor.sign_text, opts->sign_text.data.string.data), "sign_text", "", { goto error; }); has_decor = true; - } else if (HAS_KEY(opts->sign_text)) { - VALIDATE_T("sign_text", kObjectTypeString, opts->sign_text.type, { - goto error; - }); } bool right_gravity = true; @@ -720,7 +719,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer // Only error out if they try to set end_right_gravity without // setting end_col or end_row VALIDATE(!(line2 == -1 && col2 == -1 && HAS_KEY(opts->end_right_gravity)), - "cannot set end_right_gravity without setting end_row or end_col", { + "%s", "cannot set end_right_gravity without end_row or end_col", { goto error; }); @@ -732,7 +731,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer bool ephemeral = false; OPTION_TO_BOOL(ephemeral, ephemeral, false); - if (opts->spell.type == kObjectTypeNil) { + if (!HAS_KEY(opts->spell)) { decor.spell = kNone; } else { bool spell = false; @@ -746,12 +745,12 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer has_decor = true; } - VALIDATE_S((line >= 0), "line", "(out of range)", { + VALIDATE_RANGE((line >= 0), "line", { goto error; }); if (line > buf->b_ml.ml_line_count) { - VALIDATE_S(!strict, "line", "(out of range)", { + VALIDATE_RANGE(!strict, "line", { goto error; }); line = buf->b_ml.ml_line_count; @@ -762,12 +761,12 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer if (col == -1) { col = (Integer)len; } else if (col > (Integer)len) { - VALIDATE_S(!strict, "col", "(out of range)", { + VALIDATE_RANGE(!strict, "col", { goto error; }); col = (Integer)len; } else if (col < -1) { - VALIDATE_S(false, "col", "(out of range)", { + VALIDATE_RANGE(false, "col", { goto error; }); } @@ -783,7 +782,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer line2 = (int)line; } if (col2 > (Integer)len) { - VALIDATE_S(!strict, "end_col", "(out of range)", { + VALIDATE_RANGE(!strict, "end_col", { goto error; }); col2 = (int)len; @@ -886,10 +885,10 @@ Integer nvim_buf_add_highlight(Buffer buffer, Integer ns_id, String hl_group, In return 0; } - VALIDATE_S((line >= 0 && line < MAXLNUM), "line number", "(out of range)", { + VALIDATE_RANGE((line >= 0 && line < MAXLNUM), "line number", { return 0; }); - VALIDATE_S((col_start >= 0 && col_start <= MAXCOL), "column", "(out of range)", { + VALIDATE_RANGE((col_start >= 0 && col_start <= MAXCOL), "column", { return 0; }); @@ -948,7 +947,7 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start, return; } - VALIDATE_S((line_start >= 0 && line_start < MAXLNUM), "line number", "(out of range)", { + VALIDATE_RANGE((line_start >= 0 && line_start < MAXLNUM), "line number", { return; }); @@ -1092,7 +1091,7 @@ static bool extmark_get_index_from_obj(buf_T *buf, Integer ns_id, Object obj, in VALIDATE((pos.size == 2 && pos.items[0].type == kObjectTypeInteger && pos.items[1].type == kObjectTypeInteger), - "Invalid position: expected 2 Integer items", { + "%s", "Invalid position: expected 2 Integer items", { return false; }); @@ -1102,7 +1101,7 @@ static bool extmark_get_index_from_obj(buf_T *buf, Integer ns_id, Object obj, in *col = (colnr_T)(pos_col >= 0 ? pos_col : MAXCOL); return true; } else { - VALIDATE(false, "Invalid position: expected mark id Integer or 2-item Array", { + VALIDATE(false, "%s", "Invalid position: expected mark id Integer or 2-item Array", { return false; }); } @@ -1153,7 +1152,7 @@ VirtText parse_virt_text(Array chunks, Error *err, int *width) }); Array chunk = chunks.items[i].data.array; VALIDATE((chunk.size > 0 && chunk.size <= 2 && chunk.items[0].type == kObjectTypeString), - "Invalid chunk: expected Array with 1 or 2 Strings", { + "%s", "Invalid chunk: expected Array with 1 or 2 Strings", { goto free_exit; }); -- cgit From 556f8646c01d1751cf39fe4df9c622899dceab9d Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 14 Feb 2023 14:19:28 -0500 Subject: refactor(api): consistent VALIDATE messages #22262 Problem: Validation messages are not consistently formatted. - Parameter names sometimes are NOT quoted. - Descriptive names (non-parameters) sometimes ARE quoted. Solution: Always quote the `name` value passed to a VALIDATE macro _unless_ the value has whitespace. --- src/nvim/api/extmark.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 15f759eddf..91486abf38 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -238,7 +238,7 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, }); } } else { - VALIDATE_S(false, "key", k.data, { + VALIDATE_S(false, "'opts' key", k.data, { return rv; }); } @@ -329,7 +329,7 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e }); } } else { - VALIDATE_S(false, "key", k.data, { + VALIDATE_S(false, "'opts' key", k.data, { return rv; }); } @@ -508,12 +508,13 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer }); 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)) { - VALIDATE_S(false, "id (must be positive integer)", "", { + if (HAS_KEY(opts->id)) { + VALIDATE_EXP((opts->id.type == kObjectTypeInteger && opts->id.data.integer > 0), + "id", "positive Integer", NULL, { goto error; }); + + id = (uint32_t)opts->id.data.integer; } int line2 = -1; @@ -1088,10 +1089,10 @@ static bool extmark_get_index_from_obj(buf_T *buf, Integer ns_id, Object obj, in // Check if it is a position } else if (obj.type == kObjectTypeArray) { Array pos = obj.data.array; - VALIDATE((pos.size == 2 - && pos.items[0].type == kObjectTypeInteger - && pos.items[1].type == kObjectTypeInteger), - "%s", "Invalid position: expected 2 Integer items", { + VALIDATE_EXP((pos.size == 2 + && pos.items[0].type == kObjectTypeInteger + && pos.items[1].type == kObjectTypeInteger), + "mark position", "2 Integer items", NULL, { return false; }); @@ -1101,7 +1102,7 @@ static bool extmark_get_index_from_obj(buf_T *buf, Integer ns_id, Object obj, in *col = (colnr_T)(pos_col >= 0 ? pos_col : MAXCOL); return true; } else { - VALIDATE(false, "%s", "Invalid position: expected mark id Integer or 2-item Array", { + VALIDATE_EXP(false, "mark position", "mark id Integer or 2-item Array", NULL, { return false; }); } -- cgit From 344a1ee8e6b7d78120f8393d1babfd285e866334 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 22 Feb 2023 00:07:26 +0800 Subject: docs: fix typos (#22353) --- src/nvim/api/extmark.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 91486abf38..845a4c7dbe 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -445,7 +445,7 @@ 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 -- cgit From 39842be8cd8808c7da2638a6cc84d7c3fe40b996 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 5 Mar 2023 07:09:28 +0800 Subject: fix(extmarks): don't leak memory on error (#22507) --- src/nvim/api/extmark.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 845a4c7dbe..9e03cc8676 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -809,8 +809,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer return (Integer)id; error: - clear_virttext(&decor.virt_text); - xfree(decor.sign_text); + decor_clear(&decor); return 0; } -- cgit From 8021300806e2ccf04b3ec33970b682ee3c7a9cc3 Mon Sep 17 00:00:00 2001 From: bfredl Date: Thu, 16 Mar 2023 13:56:05 +0100 Subject: refactor(extmarks): some minor internal API changes extranges and a bunch of other improvements are coming for 0.10 This gets in some minor surrounding API changes to avoid rebase conflicts until then. - decorations will be able to be specific to windows - adjust deletion API to fit with extranges --- src/nvim/api/extmark.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 9e03cc8676..adc2925b7e 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -793,7 +793,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } // TODO(bfredl): synergize these two branches even more - if (ephemeral && decor_state.buf == buf) { + if (ephemeral && decor_state.win && decor_state.win->w_buffer == buf) { decor_add_ephemeral((int)line, (int)col, line2, col2, &decor, (uint64_t)ns_id, id); } else { if (ephemeral) { -- cgit From 2a10f64e254375e77e1c5a6aeae3cd65cd122afb Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Sat, 25 Mar 2023 02:24:24 +0100 Subject: feat(extmarks): extend nvim_buf_get_extmarks() Problem: Can not get all extmarks in a buffer. Properties are missing from the details array. Solution: Allow getting all extmarks in a buffer by supplying a -1 "ns_id". Add missing properties to the details array. --- src/nvim/api/extmark.c | 131 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 101 insertions(+), 30 deletions(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index adc2925b7e..4afbdfa487 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -105,7 +105,16 @@ 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) +static Object hl_group_name(int hl_id, bool hl_name) +{ + if (hl_name) { + return STRING_OBJ(cstr_to_string(syn_id2name(hl_id))); + } else { + return INTEGER_OBJ(hl_id); + } +} + +static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict, bool hl_name) { Array rv = ARRAY_DICT_INIT; if (id) { @@ -117,6 +126,8 @@ static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict if (add_dict) { Dictionary dict = ARRAY_DICT_INIT; + PUT(dict, "ns_id", INTEGER_OBJ((Integer)extmark->ns_id)); + PUT(dict, "right_gravity", BOOLEAN_OBJ(extmark->right_gravity)); if (extmark->end_row >= 0) { @@ -127,8 +138,7 @@ static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict 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_group", hl_group_name(decor->hl_id, hl_name)); PUT(dict, "hl_eol", BOOLEAN_OBJ(decor->hl_eol)); } if (decor->hl_mode) { @@ -142,8 +152,7 @@ static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict 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(chunk, hl_group_name(vtc->hl_id, hl_name)); } ADD(chunks, ARRAY_OBJ(chunk)); } @@ -172,8 +181,7 @@ static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict 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(chunk, hl_group_name(vtc->hl_id, hl_name)); } ADD(chunks, ARRAY_OBJ(chunk)); } @@ -184,10 +192,44 @@ static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict PUT(dict, "virt_lines_leftcol", BOOLEAN_OBJ(virt_lines_leftcol)); } - if (decor->hl_id || kv_size(decor->virt_text) || decor->ui_watched) { + if (decor->sign_text) { + PUT(dict, "sign_text", STRING_OBJ(cstr_to_string(decor->sign_text))); + } + + // uncrustify:off + + struct { char *name; const int val; } hls[] = { + { "sign_hl_group" , decor->sign_hl_id }, + { "number_hl_group" , decor->number_hl_id }, + { "line_hl_group" , decor->line_hl_id }, + { "cursorline_hl_group", decor->cursorline_hl_id }, + { NULL, 0 }, + }; + + // uncrustify:on + + for (int j = 0; hls[j].name && hls[j].val; j++) { + if (hls[j].val) { + PUT(dict, hls[j].name, hl_group_name(hls[j].val, hl_name)); + } + } + + if (decor->sign_text + || decor->hl_id + || kv_size(decor->virt_text) + || decor->ui_watched) { PUT(dict, "priority", INTEGER_OBJ(decor->priority)); } + if (decor->conceal) { + String name = cstr_to_string((char *)&decor->conceal_char); + PUT(dict, "conceal", STRING_OBJ(name)); + } + + if (decor->spell != kNone) { + PUT(dict, "spell", BOOLEAN_OBJ(decor->spell == kTrue)); + } + if (dict.size) { ADD(rv, DICTIONARY_OBJ(dict)); } @@ -203,6 +245,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 @@ -224,18 +267,19 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, }); 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 { - VALIDATE_EXP(false, "details", "Boolean or Integer", api_typename(v->type), { - return rv; - }); + 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 { VALIDATE_S(false, "'opts' key", k.data, { @@ -248,7 +292,7 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, if (extmark.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 @@ -282,14 +326,16 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, /// /// /// @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 +/// - 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, @@ -303,12 +349,20 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e return rv; } - VALIDATE_INT(ns_initialized((uint32_t)ns_id), "ns_id", ns_id, { - return rv; - }); + bool all_ns; + if (ns_id == -1) { + all_ns = true; + } else { + VALIDATE_INT(ns_initialized((uint32_t)ns_id), "ns_id", ns_id, { + return rv; + }); + all_ns = false; + } Integer limit = -1; bool details = false; + bool hl_name = true; + ExtmarkType type = kExtmarkNone; for (size_t i = 0; i < opts.size; i++) { String k = opts.items[i].key; @@ -319,12 +373,29 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e }); 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; + 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 if (strequal("type", k.data)) { + VALIDATE_EXP(v->type == kObjectTypeString, "type", "String", api_typename(v->type), { + return rv; + }); + if (strequal(v->data.string.data, "sign")) { + type = kExtmarkSign; + } else if (strequal(v->data.string.data, "virt_text")) { + type = kExtmarkVirtText; + } else if (strequal(v->data.string.data, "virt_lines")) { + type = kExtmarkVirtLines; + } else if (strequal(v->data.string.data, "highlight")) { + type = kExtmarkHighlight; } else { - VALIDATE_EXP(false, "details", "Boolean or Integer", api_typename(v->type), { + VALIDATE_EXP(false, "type", "sign, virt_text, virt_lines or highlight", v->data.string.data, { return rv; }); } @@ -359,11 +430,11 @@ 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); + ExtmarkInfoArray marks = extmark_get(buf, (uint32_t)ns_id, l_row, l_col, u_row, + u_col, (int64_t)limit, reverse, all_ns, type); 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, (bool)details, hl_name))); } kv_destroy(marks); -- cgit From 34ac75b32927328a0c691c5bda987c0fdb5ce9eb Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 5 Apr 2023 17:19:53 +0100 Subject: refactor: rename local API alias from a to api Problem: Codebase inconsistently binds vim.api onto a or api. Solution: Use api everywhere. a as an identifier is too short to have at the module level. --- src/nvim/api/extmark.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 4afbdfa487..fc17b9d0cc 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -311,17 +311,17 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, /// /// Example: ///
lua
-///   local a   = vim.api
-///   local pos = a.nvim_win_get_cursor(0)
-///   local ns  = a.nvim_create_namespace('my-plugin')
+///   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  = a.nvim_buf_set_extmark(0, ns, 0, 0, {})
+///   local m1  = api.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, {})
+///   local m2  = api.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}, {})
+///   local ms  = api.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, {})
+///   local all = api.nvim_buf_get_extmarks(0, ns, 0, -1, {})
 ///   print(vim.inspect(ms))
 /// 
/// -- cgit From 59fed8bb6457eb6c5204dc39a49d7ea0e1781482 Mon Sep 17 00:00:00 2001 From: luukvbaal Date: Tue, 18 Apr 2023 15:07:37 +0200 Subject: fix(api): extmark highlight groups not always included in details (#23179) Problem: Erroneous for loop condition. Solution: Remove for loop condition. --- src/nvim/api/extmark.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index fc17b9d0cc..eb49587d03 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -208,7 +208,7 @@ static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict // uncrustify:on - for (int j = 0; hls[j].name && hls[j].val; j++) { + for (int j = 0; hls[j].name; j++) { if (hls[j].val) { PUT(dict, hls[j].name, hl_group_name(hls[j].val, hl_name)); } -- cgit From 3b0df1780e2c8526bda5dead18ee7cc45925caba Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Wed, 26 Apr 2023 23:23:44 +0200 Subject: refactor: uncrustify Notable changes: replace all infinite loops to `while(true)` and remove `int` from `unsigned int`. --- src/nvim/api/extmark.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index eb49587d03..b781da1dc3 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -1168,7 +1168,7 @@ static bool extmark_get_index_from_obj(buf_T *buf, Integer ns_id, Object obj, in 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 { -- cgit From eb4676c67f5dd54bcda473783315901a3444b40b Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 27 Apr 2023 17:30:22 +0100 Subject: fix: disallow removing extmarks in on_lines callbacks (#23219) fix(extmarks): disallow removing extmarks in on_lines callbacks decor_redraw_start (which runs before decor_providers_invoke_lines) gets references for the extmarks on a specific line. If these extmarks are deleted in on_lines callbacks then this results in a heap-use-after-free error. Fixes #22801 --- src/nvim/api/extmark.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index b781da1dc3..87232a8a93 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -874,7 +874,10 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer 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); + kExtmarkNoUndo, err); + if (ERROR_SET(err)) { + goto error; + } } return (Integer)id; @@ -903,6 +906,11 @@ Boolean nvim_buf_del_extmark(Buffer buffer, Integer ns_id, Integer id, Error *er return false; }); + if (decor_state.running_on_lines) { + api_set_error(err, kErrorTypeValidation, "Cannot remove extmarks during on_line callbacks"); + return false; + } + return extmark_del(buf, (uint32_t)ns_id, (uint32_t)id); } @@ -993,7 +1001,7 @@ Integer nvim_buf_add_highlight(Buffer buffer, Integer ns_id, String hl_group, In extmark_set(buf, ns, NULL, (int)line, (colnr_T)col_start, end_line, (colnr_T)col_end, - &decor, true, false, kExtmarkNoUndo); + &decor, true, false, kExtmarkNoUndo, NULL); return ns_id; } @@ -1022,6 +1030,11 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start, return; }); + if (decor_state.running_on_lines) { + api_set_error(err, kErrorTypeValidation, "Cannot remove extmarks during on_line callbacks"); + return; + } + if (line_end < 0 || line_end > MAXLNUM) { line_end = MAXLNUM; } @@ -1051,11 +1064,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 -- cgit From e2fdd53d8c015913e8be4ff708fc3488558c8906 Mon Sep 17 00:00:00 2001 From: bfredl Date: Sun, 14 May 2023 18:45:56 +0200 Subject: refactor(map): avoid duplicated khash_t types for values This reduces the total number of khash_t instantiations from 22 to 8. Make the khash internal functions take the size of values as a runtime parameter. This is abstracted with typesafe Map containers which are still specialized for both key, value type. Introduce `Set(key)` type for when there is no value. Refactor shada.c to use Map/Set instead of khash directly. This requires `map_ref` operation to be more flexible. Return pointers to both key and value, plus an indicator for new_item. As a bonus, `map_key` is now redundant. Instead of Map(cstr_t, FileMarks), use a pointer map as the FileMarks struct is humongous. Make `event_strings` actually work like an intern pool instead of wtf it was doing before. --- src/nvim/api/extmark.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 87232a8a93..d6f0288f94 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -33,12 +33,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\* @@ -77,7 +75,7 @@ Dictionary nvim_get_namespaces(void) String name; handle_T id; - map_foreach(&namespace_ids, name, id, { + map_foreach(handle_T, &namespace_ids, name, id, { PUT(retval, name.data, INTEGER_OBJ(id)); }) @@ -88,7 +86,7 @@ const char *describe_ns(NS ns_id) { String name; handle_T id; - map_foreach(&namespace_ids, name, id, { + map_foreach(handle_T, &namespace_ids, name, id, { if ((NS)id == ns_id && name.size) { return name.data; } -- cgit From efa9b299a7cb68909e9bcd290e4d12bcb6d0bb03 Mon Sep 17 00:00:00 2001 From: Ibby <33922797+SleepySwords@users.noreply.github.com> Date: Sun, 19 Mar 2023 16:31:08 +1100 Subject: feat(ui): inline virtual text vim-patch:9.0.0067: cannot show virtual text Problem: Cannot show virtual text. Solution: Initial changes for virtual text support, using text properties. https://github.com/vim/vim/commit/7f9969c559b51446632ac7e8f76cde07e7d0078d vim-patch:9.0.0116: virtual text not displayed if 'signcolumn' is "yes" Problem: Virtual text not displayed if 'signcolumn' is "yes". Solution: Set c_extra and c_final to NUL. https://github.com/vim/vim/commit/711483cd1381a4ed848d783ae0a6792d5b04447b Co-authored-by: bfredl --- src/nvim/api/extmark.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index d6f0288f94..dd256a54f0 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -477,6 +477,8 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// - "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) @@ -695,6 +697,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer decor.virt_text_pos = kVTOverlay; } else if (strequal("right_align", str.data)) { decor.virt_text_pos = kVTRightAlign; + } else if (strequal("inline", str.data)) { + decor.virt_text_pos = kVTInline; } else { VALIDATE_S(false, "virt_text_pos", "", { goto error; -- cgit From cfd4fdfea4d0e68ea50ad412b88b5289ded6fd6f Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Tue, 23 May 2023 14:25:10 +0600 Subject: refactor(api): new helper macros Adds new API helper macros `CSTR_AS_OBJ()`, `STATIC_CSTR_AS_OBJ()`, and `STATIC_CSTR_TO_OBJ()`, which cleans up a lot of the current code. These macros will also be used extensively in the upcoming option refactor PRs because then API Objects will be used to get/set options. This PR also modifies pre-existing code to use old API helper macros like `CSTR_TO_OBJ()` to make them cleaner. --- src/nvim/api/extmark.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index d6f0288f94..21b0b811b6 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -106,7 +106,7 @@ bool ns_initialized(uint32_t ns) static Object hl_group_name(int hl_id, bool hl_name) { if (hl_name) { - return STRING_OBJ(cstr_to_string(syn_id2name(hl_id))); + return CSTR_TO_OBJ(syn_id2name(hl_id)); } else { return INTEGER_OBJ(hl_id); } @@ -140,7 +140,7 @@ static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict 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]))); + PUT(dict, "hl_mode", CSTR_TO_OBJ(hl_mode_str[decor->hl_mode])); } if (kv_size(decor->virt_text)) { @@ -148,7 +148,7 @@ static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict 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))); + ADD(chunk, CSTR_TO_OBJ(vtc->text)); if (vtc->hl_id > 0) { ADD(chunk, hl_group_name(vtc->hl_id, hl_name)); } @@ -160,7 +160,7 @@ static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict 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]))); + CSTR_TO_OBJ(virt_text_pos_str[decor->virt_text_pos])); } if (decor->ui_watched) { @@ -177,7 +177,7 @@ static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict 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))); + ADD(chunk, CSTR_TO_OBJ(vtc->text)); if (vtc->hl_id > 0) { ADD(chunk, hl_group_name(vtc->hl_id, hl_name)); } @@ -191,7 +191,7 @@ static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict } if (decor->sign_text) { - PUT(dict, "sign_text", STRING_OBJ(cstr_to_string(decor->sign_text))); + PUT(dict, "sign_text", CSTR_TO_OBJ(decor->sign_text)); } // uncrustify:off -- cgit From e41b2e34b46a806b0fc59914263f8f4af9ac62f1 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 28 May 2023 11:09:25 +0800 Subject: test(extmarks): add test for virt_text_hide with 'smoothscroll' (#23791) --- src/nvim/api/extmark.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 299413e510..aca290494b 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -483,8 +483,9 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// window column (starting from the first /// text column) /// - 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` -- cgit From a9cd8467cbd54035e7814b862054c828467c2ce2 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 22 Jun 2023 13:59:57 +0800 Subject: fix(api): wrong nvim_buf_set_extmark error for invalid hl_mode --- src/nvim/api/extmark.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index aca290494b..1946d77846 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -701,7 +701,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } else if (strequal("inline", str.data)) { decor.virt_text_pos = kVTInline; } else { - VALIDATE_S(false, "virt_text_pos", "", { + VALIDATE_S(false, "virt_text_pos", str.data, { goto error; }); } @@ -719,7 +719,11 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer OPTION_TO_BOOL(decor.virt_text_hide, virt_text_hide, false); OPTION_TO_BOOL(decor.hl_eol, hl_eol, false); - if (opts->hl_mode.type == kObjectTypeString) { + if (HAS_KEY(opts->hl_mode)) { + VALIDATE_T("hl_mode", kObjectTypeString, opts->hl_mode.type, { + goto error; + }); + String str = opts->hl_mode.data.string; if (strequal("replace", str.data)) { decor.hl_mode = kHlModeReplace; @@ -728,14 +732,10 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } else if (strequal("blend", str.data)) { decor.hl_mode = kHlModeBlend; } else { - VALIDATE_S(false, "virt_text_pos", "", { + VALIDATE_S(false, "hl_mode", str.data, { goto error; }); } - } else if (HAS_KEY(opts->hl_mode)) { - VALIDATE_T("hl_mode", kObjectTypeString, opts->hl_mode.type, { - goto error; - }); } bool virt_lines_leftcol = false; -- cgit From 4e6356559c8cd44dbcaa765d1f39e176064526ec Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 22 Jun 2023 03:44:51 -0700 Subject: test: spellcheck :help (vimdoc) files #24109 Enforce consistent terminology (defined in `gen_help_html.lua:spell_dict`) for common misspellings. This does not spellcheck English in general (perhaps a future TODO, though it may be noisy). --- src/nvim/api/extmark.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 1946d77846..d79cbf7508 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -1048,7 +1048,7 @@ 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 -- cgit From f0884f21fa0cccc576f00bc18895cc80ba906031 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 22 Jun 2023 20:39:35 +0800 Subject: feat(extmarks): support hl_mode "combine" for inline virt_text (#24099) --- src/nvim/api/extmark.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index d79cbf7508..a101e1bbf1 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -478,7 +478,7 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// 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 +/// 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) @@ -490,10 +490,10 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// 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 @@ -730,6 +730,11 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } else if (strequal("combine", str.data)) { decor.hl_mode = kHlModeCombine; } else if (strequal("blend", str.data)) { + if (decor.virt_text_pos == kVTInline) { + VALIDATE(false, "%s", "cannot use 'blend' hl_mode with inline virtual text", { + goto error; + }); + } decor.hl_mode = kHlModeBlend; } else { VALIDATE_S(false, "hl_mode", str.data, { -- cgit From 49a7585981cdf7403e76a614558e602a98e64301 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Fri, 23 Jun 2023 12:16:55 +0200 Subject: docs: autocmds, misc --- src/nvim/api/extmark.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index a101e1bbf1..0608a8961d 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -320,7 +320,7 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, /// 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, {}) -/// print(vim.inspect(ms)) +/// vim.print(ms) /// /// /// @param buffer Buffer handle, or 0 for current buffer -- cgit From 9359701eae7bd8a59e4a916e085cc686f609d693 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 13 Jul 2023 07:02:06 +0800 Subject: test(extmarks): add test for virt_text_win_col with cpo+=n (#24328) --- src/nvim/api/extmark.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 0608a8961d..d254373eb0 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -473,15 +473,16 @@ 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 +/// 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 because of /// scrolling with 'nowrap' or 'smoothscroll'. -- cgit From 7bc93e0e2f246dd78026a3472d929a0fe450f70d Mon Sep 17 00:00:00 2001 From: bfredl Date: Tue, 1 Aug 2023 14:01:19 +0200 Subject: refactor(api): use typed keysets Initially this is just for geting rid of boilerplate, but eventually the types could get exposed as metadata --- src/nvim/api/extmark.c | 166 ++++++++++++++++--------------------------------- 1 file changed, 53 insertions(+), 113 deletions(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index d254373eb0..8a60d81649 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -10,6 +10,7 @@ #include "lauxlib.h" #include "nvim/api/extmark.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" @@ -581,40 +582,32 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer }); uint32_t id = 0; - if (HAS_KEY(opts->id)) { - VALIDATE_EXP((opts->id.type == kObjectTypeInteger && opts->id.data.integer > 0), - "id", "positive Integer", NULL, { + if (HAS_KEY(opts, set_extmark, id)) { + VALIDATE_EXP((opts->id > 0), "id", "positive Integer", NULL, { goto error; }); - id = (uint32_t)opts->id.data.integer; + 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)) { - VALIDATE(!HAS_KEY(opts->end_row), "%s", "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); - - if (HAS_KEY(opts->end_row)) { - VALIDATE_T("end_row", kObjectTypeInteger, opts->end_row.type, { - goto error; - }); + bool strict = GET_BOOL_OR_TRUE(opts, set_extmark, strict); - Integer val = opts->end_row.data.integer; + 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; }); @@ -622,12 +615,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } colnr_T col2 = -1; - if (HAS_KEY(opts->end_col)) { - VALIDATE_T("end_col", kObjectTypeInteger, opts->end_col.type, { - goto error; - }); - - Integer val = opts->end_col.data.integer; + if (HAS_KEY(opts, set_extmark, end_col)) { + Integer val = opts->end_col; VALIDATE_RANGE((val >= 0 && val <= MAXCOL), "end_col", { goto error; }); @@ -636,6 +625,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer // uncrustify:off + // TODO(bfredl): keyset type alias for hl_group? (nil|int|string) struct { const char *name; Object *opt; @@ -652,7 +642,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer // 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) { *hls[j].dest = object_to_hl_id(*hls[j].opt, hls[j].name, err); if (ERROR_SET(err)) { goto error; @@ -661,12 +651,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } } - if (HAS_KEY(opts->conceal)) { - VALIDATE_T("conceal", kObjectTypeString, opts->conceal.type, { - goto error; - }); - - String c = opts->conceal.data.string; + if (HAS_KEY(opts, set_extmark, conceal)) { + String c = opts->conceal; decor.conceal = true; if (c.size) { decor.conceal_char = utf_ptr2char(c.data); @@ -674,25 +660,16 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer has_decor = true; } - if (HAS_KEY(opts->virt_text)) { - VALIDATE_T("virt_text", kObjectTypeArray, opts->virt_text.type, { - goto error; - }); - - decor.virt_text = parse_virt_text(opts->virt_text.data.array, err, - &decor.virt_text_width); + if (HAS_KEY(opts, set_extmark, virt_text)) { + decor.virt_text = parse_virt_text(opts->virt_text, err, &decor.virt_text_width); has_decor = true; if (ERROR_SET(err)) { goto error; } } - if (HAS_KEY(opts->virt_text_pos)) { - VALIDATE_T("virt_text_pos", kObjectTypeString, opts->virt_text_pos.type, { - goto error; - }); - - 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; } else if (strequal("overlay", str.data)) { @@ -708,24 +685,16 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } } - if (HAS_KEY(opts->virt_text_win_col)) { - VALIDATE_T("virt_text_win_col", kObjectTypeInteger, opts->virt_text_win_col.type, { - goto error; - }); - - decor.col = (int)opts->virt_text_win_col.data.integer; + if (HAS_KEY(opts, set_extmark, virt_text_win_col)) { + decor.col = (int)opts->virt_text_win_col; decor.virt_text_pos = kVTWinCol; } - OPTION_TO_BOOL(decor.virt_text_hide, virt_text_hide, false); - OPTION_TO_BOOL(decor.hl_eol, hl_eol, false); + decor.hl_eol = opts->hl_eol; + decor.virt_text_hide = opts->virt_text_hide; - if (HAS_KEY(opts->hl_mode)) { - VALIDATE_T("hl_mode", kObjectTypeString, opts->hl_mode.type, { - goto error; - }); - - 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; } else if (strequal("combine", str.data)) { @@ -744,15 +713,10 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } } - bool virt_lines_leftcol = false; - OPTION_TO_BOOL(virt_lines_leftcol, virt_lines_leftcol, false); - - if (HAS_KEY(opts->virt_lines)) { - VALIDATE_T("virt_lines", kObjectTypeArray, opts->virt_lines.type, { - goto error; - }); + bool virt_lines_leftcol = opts->virt_lines_leftcol; - 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++) { VALIDATE_T("virt_text_line", kObjectTypeArray, a.items[j].type, { goto error; @@ -767,61 +731,44 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } } - OPTION_TO_BOOL(decor.virt_lines_above, virt_lines_above, false); - - if (HAS_KEY(opts->priority)) { - VALIDATE_T("priority", kObjectTypeInteger, opts->priority.type, { - goto error; - }); - - Integer val = opts->priority.data.integer; + decor.virt_lines_above = opts->virt_lines_above; - VALIDATE_RANGE((val >= 0 && val <= UINT16_MAX), "priority", { + if (HAS_KEY(opts, set_extmark, priority)) { + VALIDATE_RANGE((opts->priority >= 0 && opts->priority <= UINT16_MAX), "priority", { goto error; }); - decor.priority = (DecorPriority)val; + decor.priority = (DecorPriority)opts->priority; } - if (HAS_KEY(opts->sign_text)) { - VALIDATE_T("sign_text", kObjectTypeString, opts->sign_text.type, { - goto error; - }); - - VALIDATE_S(init_sign_text(&decor.sign_text, opts->sign_text.data.string.data), + if (HAS_KEY(opts, set_extmark, sign_text)) { + VALIDATE_S(init_sign_text(&decor.sign_text, opts->sign_text.data), "sign_text", "", { goto error; }); has_decor = true; } - 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 - VALIDATE(!(line2 == -1 && col2 == -1 && HAS_KEY(opts->end_right_gravity)), + 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); + bool end_right_gravity = opts->end_right_gravity; size_t len = 0; - bool ephemeral = false; - OPTION_TO_BOOL(ephemeral, ephemeral, false); - - if (!HAS_KEY(opts->spell)) { + if (!HAS_KEY(opts, set_extmark, spell)) { decor.spell = kNone; } else { - bool spell = false; - OPTION_TO_BOOL(spell, spell, false); - decor.spell = spell ? kTrue : kFalse; + decor.spell = opts->spell ? kTrue : kFalse; has_decor = true; } - OPTION_TO_BOOL(decor.ui_watched, ui_watched, false); + decor.ui_watched = opts->ui_watched; if (decor.ui_watched) { has_decor = true; } @@ -836,7 +783,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer }); 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, false)); } if (col == -1) { @@ -854,7 +801,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer 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, false)); } else if (line2 == buf->b_ml.ml_line_count) { // We are trying to add an extmark past final newline len = 0; @@ -873,10 +820,10 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } // TODO(bfredl): synergize these two branches even more - if (ephemeral && decor_state.win && decor_state.win->w_buffer == buf) { + if (opts->ephemeral && decor_state.win && decor_state.win->w_buffer == buf) { decor_add_ephemeral((int)line, (int)col, line2, col2, &decor, (uint64_t)ns_id, id); } else { - if (ephemeral) { + if (opts->ephemeral) { api_set_error(err, kErrorTypeException, "not yet implemented"); goto error; } @@ -1107,7 +1054,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 }, @@ -1121,25 +1068,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; } - VALIDATE_T(cbs[i].name, kObjectTypeLuaRef, v->type, { - 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|. -- cgit From cefd774fac76b91f5368833555818c80c992c3b1 Mon Sep 17 00:00:00 2001 From: bfredl Date: Thu, 24 Aug 2023 15:14:23 +0200 Subject: refactor(memline): distinguish mutating uses of ml_get_buf() ml_get_buf() takes a third parameters to indicate whether the caller wants to mutate the memline data in place. However the vast majority of the call sites is using this function just to specify a buffer but without any mutation. This makes it harder to grep for the places which actually perform mutation. Solution: Remove the bool param from ml_get_buf(). it now works like ml_get() except for a non-current buffer. Add a new ml_get_buf_mut() function for the mutating use-case, which can be grepped along with the other ml_replace() etc functions which can modify the memline. --- src/nvim/api/extmark.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 8a60d81649..30e39cd7aa 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -783,7 +783,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer }); line = buf->b_ml.ml_line_count; } else if (line < buf->b_ml.ml_line_count) { - len = opts->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) { @@ -801,7 +801,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer if (col2 >= 0) { if (line2 >= 0 && line2 < buf->b_ml.ml_line_count) { - len = opts->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; -- cgit From 5970157e1d22fd5e05ae5d3bd949f807fb7a744c Mon Sep 17 00:00:00 2001 From: bfredl Date: Wed, 17 May 2023 16:08:06 +0200 Subject: refactor(map): enhanced implementation, Clean Codeâ„¢, etc etc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This involves two redesigns of the map.c implementations: 1. Change of macro style and code organization The old khash.h and map.c implementation used huge #define blocks with a lot of backslash line continuations. This instead uses the "implementation file" .c.h pattern. Such a file is meant to be included multiple times, with different macros set prior to inclusion as parameters. we already use this pattern e.g. for eval/typval_encode.c.h to implement different typval encoders reusing a similar structure. We can structure this code into two parts. one that only depends on key type and is enough to implement sets, and one which depends on both key and value to implement maps (as a wrapper around sets, with an added value[] array) 2. Separate the main hash buckets from the key / value arrays Change the hack buckets to only contain an index into separate key / value arrays This is a common pattern in modern, state of the art hashmap implementations. Even though this leads to one more allocated array, it is this often is a net reduction of memory consumption. Consider key+value consuming at least 12 bytes per pair. On average, we will have twice as many buckets per item. Thus old implementation: 2*12 = 24 bytes per item New implementation 1*12 + 2*4 = 20 bytes per item And the difference gets bigger with larger items. One might think we have pulled a fast one here, as wouldn't the average size of the new key/value arrays be 1.5 slots per items due to amortized grows? But remember, these arrays are fully dense, and thus the accessed memory, measured in _cache lines_, the unit which actually matters, will be the fully used memory but just rounded up to the nearest cache line boundary. This has some other interesting properties, such as an insert-only set/map will be fully ordered by insert only. Preserving this ordering in face of deletions is more tricky tho. As we currently don't use ordered maps, the "delete" operation maintains compactness of the item arrays in the simplest way by breaking the ordering. It would be possible to implement an order-preserving delete although at some cost, like allowing the items array to become non-dense until the next rehash. Finally, in face of these two major changes, all code used in khash.h has been integrated into map.c and friends. Given the heavy edits it makes no sense to "layer" the code into a vendored and a wrapper part. Rather, the layered cake follows the specialization depth: code shared for all maps, code specialized to a key type (and its equivalence relation), and finally code specialized to value+key type. --- src/nvim/api/extmark.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 30e39cd7aa..6ec1fc4ee0 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -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; } @@ -76,7 +76,7 @@ Dictionary nvim_get_namespaces(void) String name; handle_T id; - map_foreach(handle_T, &namespace_ids, name, id, { + map_foreach(&namespace_ids, name, id, { PUT(retval, name.data, INTEGER_OBJ(id)); }) @@ -87,7 +87,7 @@ const char *describe_ns(NS ns_id) { String name; handle_T id; - map_foreach(handle_T, &namespace_ids, name, id, { + map_foreach(&namespace_ids, name, id, { if ((NS)id == ns_id && name.size) { return name.data; } -- cgit From 65738202f8be3ca63b75197d48f2c7a9324c035b Mon Sep 17 00:00:00 2001 From: Jaehwang Jung Date: Tue, 12 Sep 2023 04:29:39 +0900 Subject: fix(decorations): better approximation of botline #24794 Problem: * The guessed botline might be smaller than the actual botline e.g. when there are folds and the user is typing in insert mode. This may result in incorrect treesitter highlights for injections. * botline can be larger than the last line number of the buffer, which results in errors when placing extmarks. Solution: * Take a more conservative approximation. I am not sure if it is sufficient to guarantee correctness, but it seems to be good enough for the case mentioned above. * Clamp it to the last line number. Co-authored-by: Lewis Russell --- src/nvim/api/extmark.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 6ec1fc4ee0..268fdded9a 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -1035,7 +1035,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) -- cgit From b04286a187d57c50f01cd36cd4668b7a69026579 Mon Sep 17 00:00:00 2001 From: bfredl Date: Sun, 22 Nov 2020 10:10:37 +0100 Subject: feat(extmark): support proper multiline ranges The removes the previous restriction that nvim_buf_set_extmark() could not be used to highlight arbitrary multi-line regions The problem can be summarized as follows: let's assume an extmark with a hl_group is placed covering the region (5,0) to (50,0) Now, consider what happens if nvim needs to redraw a window covering the lines 20-30. It needs to be able to ask the marktree what extmarks cover this region, even if they don't begin or end here. Therefore the marktree needs to be augmented with the information covers a point, not just what marks begin or end there. To do this, we augment each node with a field "intersect" which is a set the ids of the marks which overlap this node, but only if it is not part of the set of any parent. This ensures the number of nodes that need to be explicitly marked grows only logarithmically with the total number of explicitly nodes (and thus the number of of overlapping marks). Thus we can quickly iterate all marks which overlaps any query position by looking up what leaf node contains that position. Then we only need to consider all "start" marks within that leaf node, and the "intersect" set of that node and all its parents. Now, and the major source of complexity is that the tree restructuring operations (to ensure that each node has T-1 <= size <= 2*T-1) also need to update these sets. If a full inner node is split in two, one of the new parents might start to completely overlap some ranges and its ids will need to be moved from its children's sets to its own set. Similarly, if two undersized nodes gets joined into one, it might no longer completely overlap some ranges, and now the children which do needs to have the have the ids in its set instead. And then there are the pivots! Yes the pivot operations when a child gets moved from one parent to another. --- src/nvim/api/extmark.c | 98 +++++++++++++++++++++++--------------------------- 1 file changed, 45 insertions(+), 53 deletions(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 268fdded9a..b76a275c0d 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -308,6 +308,10 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, /// 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: ///
lua
 ///   local api = vim.api
@@ -334,11 +338,13 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id,
 ///          - limit:  Maximum number of marks to return
 ///          - 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;
@@ -348,63 +354,32 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
     return rv;
   }
 
-  bool all_ns;
-  if (ns_id == -1) {
-    all_ns = true;
-  } else {
-    VALIDATE_INT(ns_initialized((uint32_t)ns_id), "ns_id", ns_id, {
-      return rv;
-    });
-    all_ns = false;
-  }
+  VALIDATE_INT(ns_id == -1 || ns_initialized((uint32_t)ns_id), "ns_id", ns_id, {
+    return rv;
+  });
 
-  Integer limit = -1;
-  bool details = false;
-  bool hl_name = true;
-  ExtmarkType type = kExtmarkNone;
+  bool details = opts->details;
+  bool hl_name = GET_BOOL_OR_TRUE(opts, get_extmarks, hl_name);
 
-  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)) {
-      VALIDATE_T("limit", kObjectTypeInteger, v->type, {
-        return rv;
-      });
-      limit = v->data.integer;
-    } else if (strequal("details", k.data)) {
-      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 if (strequal("type", k.data)) {
-      VALIDATE_EXP(v->type == kObjectTypeString, "type", "String", api_typename(v->type), {
-        return rv;
-      });
-      if (strequal(v->data.string.data, "sign")) {
-        type = kExtmarkSign;
-      } else if (strequal(v->data.string.data, "virt_text")) {
-        type = kExtmarkVirtText;
-      } else if (strequal(v->data.string.data, "virt_lines")) {
-        type = kExtmarkVirtLines;
-      } else if (strequal(v->data.string.data, "highlight")) {
-        type = kExtmarkHighlight;
-      } else {
-        VALIDATE_EXP(false, "type", "sign, virt_text, virt_lines or highlight", v->data.string.data, {
-          return rv;
-        });
-      }
+  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 {
-      VALIDATE_S(false, "'opts' key", k.data, {
+      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) {
@@ -429,11 +404,12 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
     reverse = true;
   }
 
+  // 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, all_ns, type);
+                                       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, hl_name)));
+    ADD(rv, ARRAY_OBJ(extmark_to_array(&kv_A(marks, i), true, details, hl_name)));
   }
 
   kv_destroy(marks);
@@ -451,6 +427,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|
@@ -1230,3 +1211,14 @@ 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);
+}
-- 
cgit 


From 2e92065686f62851318150a315591c30b8306a4b Mon Sep 17 00:00:00 2001
From: Gregory Anders <8965202+gpanders@users.noreply.github.com>
Date: Thu, 14 Sep 2023 08:23:01 -0500
Subject: docs: replace 
 with ``` (#25136)

---
 src/nvim/api/extmark.c | 38 ++++++++++++++++++++------------------
 1 file changed, 20 insertions(+), 18 deletions(-)

(limited to 'src/nvim/api/extmark.c')

diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c
index b76a275c0d..05f62f6c7c 100644
--- a/src/nvim/api/extmark.c
+++ b/src/nvim/api/extmark.c
@@ -300,10 +300,11 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id,
 /// 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:
-/// 
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}, {})
-/// 
+/// +/// ```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.) @@ -313,20 +314,21 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, /// of an extmark will be considered. /// /// Example: -///
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)
-/// 
+/// +/// ```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()| or -1 for all namespaces -- cgit From 71530cc972576e6656431b6d000aec9b69a0997e Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 17 Sep 2023 20:29:18 +0800 Subject: feat(folds): support virtual text format for 'foldtext' (#25209) Co-authored-by: Lewis Russell --- src/nvim/api/extmark.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 05f62f6c7c..faab6e593c 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -1206,7 +1206,9 @@ 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: -- cgit From 64e8a3c4d19eab40888fbac36b96e97bd9d68c42 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 22 Sep 2023 15:36:24 +0800 Subject: fix(ui): handle virtual text with multiple hl in more cases (#25304) --- src/nvim/api/extmark.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index faab6e593c..1e44c66974 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -1188,8 +1188,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 { -- cgit From b7763d7f6b7fdcabe06658c664457df8bc147563 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 22 Sep 2023 17:56:05 +0800 Subject: fix(api): get virtual text with multiple hl properly (#25307) --- src/nvim/api/extmark.c | 57 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 23 deletions(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 1e44c66974..91e197bea7 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -113,6 +113,36 @@ static Object hl_group_name(int hl_id, bool hl_name) } } +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(const ExtmarkInfo *extmark, bool id, bool add_dict, bool hl_name) { Array rv = ARRAY_DICT_INIT; @@ -145,16 +175,7 @@ static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict } 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, CSTR_TO_OBJ(vtc->text)); - if (vtc->hl_id > 0) { - ADD(chunk, hl_group_name(vtc->hl_id, hl_name)); - } - ADD(chunks, ARRAY_OBJ(chunk)); - } + Array chunks = virt_text_to_array(decor->virt_text, hl_name); PUT(dict, "virt_text", ARRAY_OBJ(chunks)); PUT(dict, "virt_text_hide", BOOLEAN_OBJ(decor->virt_text_hide)); if (decor->virt_text_pos == kVTWinCol) { @@ -171,19 +192,9 @@ static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict 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, CSTR_TO_OBJ(vtc->text)); - if (vtc->hl_id > 0) { - ADD(chunk, hl_group_name(vtc->hl_id, hl_name)); - } - ADD(chunks, ARRAY_OBJ(chunk)); - } + for (size_t i = 0; i < kv_size(decor->virt_lines); i++) { + virt_lines_leftcol = kv_A(decor->virt_lines, i).left_col; + Array chunks = virt_text_to_array(kv_A(decor->virt_lines, i).line, hl_name); ADD(all_chunks, ARRAY_OBJ(chunks)); } PUT(dict, "virt_lines", ARRAY_OBJ(all_chunks)); -- cgit From cf8b2c0e74fd5e723b0c15c2ce84e6900fd322d3 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 30 Sep 2023 12:05:28 +0800 Subject: build(iwyu): add a few more _defs.h mappings (#25435) --- src/nvim/api/extmark.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 91e197bea7..14454b626e 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -9,8 +9,8 @@ #include "klib/kvec.h" #include "lauxlib.h" #include "nvim/api/extmark.h" +#include "nvim/api/keysets.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" @@ -20,6 +20,7 @@ #include "nvim/drawscreen.h" #include "nvim/extmark.h" #include "nvim/highlight_group.h" +#include "nvim/marktree.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" -- cgit From 14c7bf391625d5f24096d3c06b48d955d78e9e5d Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Tue, 31 Oct 2023 23:53:52 +0100 Subject: refactor(extmarks): extmark_del() with MarkTreeIter --- src/nvim/api/extmark.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 14454b626e..0b372c57e5 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -862,7 +862,7 @@ Boolean nvim_buf_del_extmark(Buffer buffer, Integer ns_id, Integer id, Error *er 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) -- cgit From 68cb4a7405ea9f8841d1f25ee8997c49e77fa679 Mon Sep 17 00:00:00 2001 From: bfredl Date: Fri, 3 Nov 2023 12:26:38 +0100 Subject: feat(extmarks): add "undo_restore" flag to opt out of undo-restoring It is a design goal of extmarks that they allow precise tracking of changes across undo/redo, including restore the exact positions after a do/undo or undo/redo cycle. However this behavior is not useful for all usecases. Many plugins won't keep marks around for long after text changes, but uses them more like a cache until some external source (like LSP semantic highlights) has fully updated to changed text and then will explicitly readjust/replace extmarks as needed. Add a "undo_restore" flag which is true by default (matches existing behavior) but can be set to false to opt-out of this behavior. Delete dead u_extmark_set() code. --- src/nvim/api/extmark.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 0b372c57e5..3f1745df40 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -166,6 +166,10 @@ static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict PUT(dict, "end_right_gravity", BOOLEAN_OBJ(extmark->end_right_gravity)); } + if (extmark->no_undo) { + PUT(dict, "undo_restore", BOOLEAN_OBJ(false)); + } + const Decoration *decor = &extmark->decor; if (decor->hl_id) { PUT(dict, "hl_group", hl_group_name(decor->hl_id, hl_name)); @@ -519,6 +523,9 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// 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. /// - priority: a priority value for the highlight group or sign /// attribute. For example treesitter highlighting uses a /// value of 100. @@ -825,7 +832,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer 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, err); + !GET_BOOL_OR_TRUE(opts, set_extmark, undo_restore), err); if (ERROR_SET(err)) { goto error; } @@ -952,7 +959,7 @@ Integer nvim_buf_add_highlight(Buffer buffer, Integer ns_id, String hl_group, In extmark_set(buf, ns, NULL, (int)line, (colnr_T)col_start, end_line, (colnr_T)col_end, - &decor, true, false, kExtmarkNoUndo, NULL); + &decor, true, false, false, NULL); return ns_id; } -- cgit From 4e6f559b8c5f77924fdbe2e5abd9c6aa8efad13f Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Tue, 24 Oct 2023 13:32:00 +0200 Subject: feat(extmarks): add 'invalidate' property to extmarks Problem: No way to have extmarks automatically removed when the range it is attached to is deleted. Solution: Add new 'invalidate' property that will hide a mark when the entirety of its range is deleted. When "undo_restore" is set to false, delete the mark from the buffer instead. --- src/nvim/api/extmark.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 3f1745df40..aeecab6bd0 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -170,6 +170,13 @@ static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict PUT(dict, "undo_restore", BOOLEAN_OBJ(false)); } + if (extmark->invalidate) { + PUT(dict, "invalidate", BOOLEAN_OBJ(true)); + } + if (extmark->invalid) { + PUT(dict, "invalid", BOOLEAN_OBJ(true)); + } + const Decoration *decor = &extmark->decor; if (decor->hl_id) { PUT(dict, "hl_group", hl_group_name(decor->hl_id, hl_name)); @@ -526,6 +533,9 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// - 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. @@ -759,8 +769,6 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer goto error; }); - bool end_right_gravity = opts->end_right_gravity; - size_t len = 0; if (!HAS_KEY(opts, set_extmark, spell)) { @@ -823,7 +831,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer // TODO(bfredl): synergize these two branches even more if (opts->ephemeral && decor_state.win && decor_state.win->w_buffer == buf) { - decor_add_ephemeral((int)line, (int)col, line2, col2, &decor, (uint64_t)ns_id, id); + decor_push_ephemeral((int)line, (int)col, line2, col2, &decor, (uint64_t)ns_id, id); } else { if (opts->ephemeral) { api_set_error(err, kErrorTypeException, "not yet implemented"); @@ -831,8 +839,9 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } extmark_set(buf, (uint32_t)ns_id, &id, (int)line, (colnr_T)col, line2, col2, - has_decor ? &decor : NULL, right_gravity, end_right_gravity, - !GET_BOOL_OR_TRUE(opts, set_extmark, undo_restore), err); + has_decor ? &decor : NULL, right_gravity, opts->end_right_gravity, + !GET_BOOL_OR_TRUE(opts, set_extmark, undo_restore), + opts->invalidate, err); if (ERROR_SET(err)) { goto error; } @@ -959,7 +968,7 @@ Integer nvim_buf_add_highlight(Buffer buffer, Integer ns_id, String hl_group, In extmark_set(buf, ns, NULL, (int)line, (colnr_T)col_start, end_line, (colnr_T)col_end, - &decor, true, false, false, NULL); + &decor, true, false, false, false, NULL); return ns_id; } @@ -1010,7 +1019,7 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start, /// 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` -- cgit From 353a4be7e84fdc101318215bdcc8a7e780d737fe Mon Sep 17 00:00:00 2001 From: dundargoc Date: Sun, 12 Nov 2023 13:13:58 +0100 Subject: build: remove PVS We already have an extensive suite of static analysis tools we use, which causes a fair bit of redundancy as we get duplicate warnings. PVS is also prone to give false warnings which creates a lot of work to identify and disable. --- src/nvim/api/extmark.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index aeecab6bd0..84b89a7428 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -1,6 +1,3 @@ -// 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 #include #include -- cgit From c4afb9788c4f139eb2e3b7aa4d6a6a20b67ba156 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Sat, 11 Nov 2023 00:52:50 +0100 Subject: refactor(sign): move legacy signs to extmarks Problem: The legacy signlist data structures and associated functions are redundant since the introduction of extmark signs. Solution: Store signs defined through the legacy commands in a hashmap, placed signs in the extmark tree. Replace signlist associated functions. Usage of the legacy sign commands should yield no change in behavior with the exception of: - "orphaned signs" are now always removed when the line it is placed on is deleted. This used to depend on the value of 'signcolumn'. - It is no longer possible to place multiple signs with the same identifier in a single group on multiple lines. This will now move the sign instead. Moreover, both signs placed through the legacy sign commands and through |nvim_buf_set_extmark()|: - Will show up in both |sign-place| and |nvim_buf_get_extmarks()|. - Are displayed by increasing sign identifier, left to right. Extmark signs used to be ordered decreasingly as opposed to legacy signs. --- src/nvim/api/extmark.c | 46 ++++++---------------------------------------- 1 file changed, 6 insertions(+), 40 deletions(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 84b89a7428..efcdd0e7f7 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -22,6 +22,7 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/pos.h" +#include "nvim/sign.h" #include "nvim/strings.h" #include "nvim/vim.h" @@ -81,7 +82,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; @@ -90,7 +91,7 @@ const char *describe_ns(NS ns_id) return name.data; } }) - return "(UNKNOWN PLUGIN)"; + return unknown; } // Is the Namespace in use? @@ -314,8 +315,8 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, 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) @@ -750,7 +751,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } if (HAS_KEY(opts, set_extmark, sign_text)) { - VALIDATE_S(init_sign_text(&decor.sign_text, opts->sign_text.data), + VALIDATE_S(init_sign_text(NULL, &decor.sign_text, opts->sign_text.data), "sign_text", "", { goto error; }); @@ -1150,41 +1151,6 @@ static bool extmark_get_index_from_obj(buf_T *buf, Integer ns_id, Object obj, in }); } } -// 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, " "); - } - - return OK; -} VirtText parse_virt_text(Array chunks, Error *err, int *width) { -- cgit From ec283e6b4ba85dcb61e97e089605e006e85cc273 Mon Sep 17 00:00:00 2001 From: bfredl Date: Sat, 18 Nov 2023 20:35:12 +0100 Subject: refactor(extmark): redundant ExtmarkInfo delenda est, use MTPair instead --- src/nvim/api/extmark.c | 46 ++++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index efcdd0e7f7..31fc01403a 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -142,40 +142,42 @@ Array virt_text_to_array(VirtText vt, bool hl_name) return chunks; } -static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict, bool hl_name) +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, "ns_id", INTEGER_OBJ((Integer)extmark->ns_id)); + PUT(dict, "ns_id", INTEGER_OBJ((Integer)start.ns)); - PUT(dict, "right_gravity", BOOLEAN_OBJ(extmark->right_gravity)); + PUT(dict, "right_gravity", BOOLEAN_OBJ(mt_right(start))); - 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)); + 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 (extmark->no_undo) { + if (mt_no_undo(start)) { PUT(dict, "undo_restore", BOOLEAN_OBJ(false)); } - if (extmark->invalidate) { + if (mt_invalidate(start)) { PUT(dict, "invalidate", BOOLEAN_OBJ(true)); } - if (extmark->invalid) { + if (mt_invalid(start)) { PUT(dict, "invalid", BOOLEAN_OBJ(true)); } - const Decoration *decor = &extmark->decor; + // pretend this is a pointer for a short while, Decoration will be factored away very soon + const Decoration decor[1] = { get_decor(start) }; if (decor->hl_id) { PUT(dict, "hl_group", hl_group_name(decor->hl_id, hl_name)); PUT(dict, "hl_eol", BOOLEAN_OBJ(decor->hl_eol)); @@ -308,11 +310,11 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, } } - 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, hl_name); + return extmark_to_array(extmark, false, details, hl_name); } /// Gets |extmarks| (including |signs|) in "traversal order" from a |charwise| @@ -432,7 +434,7 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e 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, details, hl_name))); + ADD(rv, ARRAY_OBJ(extmark_to_array(kv_A(marks, i), true, details, hl_name))); } kv_destroy(marks); @@ -1121,13 +1123,13 @@ static bool extmark_get_index_from_obj(buf_T *buf, Integer ns_id, Object obj, in }); } - ExtmarkInfo extmark = extmark_from_id(buf, (uint32_t)ns_id, (uint32_t)id); + MTPair extmark = extmark_from_id(buf, (uint32_t)ns_id, (uint32_t)id); - VALIDATE_INT((extmark.row >= 0), "mark id (not found)", id, { + VALIDATE_INT((extmark.start.pos.row >= 0), "mark id (not found)", id, { return false; }); - *row = extmark.row; - *col = extmark.col; + *row = extmark.start.pos.row; + *col = extmark.start.pos.col; return true; // Check if it is a position -- cgit From 0b38fe4dbb77c15ae6f5779174855acab25fc86c Mon Sep 17 00:00:00 2001 From: bfredl Date: Wed, 8 Mar 2023 15:18:02 +0100 Subject: refactor(decorations): break up Decoration struct into smaller pieces Remove the monolithic Decoration struct. Before this change, each extmark could either represent just a hl_id + priority value as a inline decoration, or it would take a pointer to this monolitic 112 byte struct which has to be allocated. This change separates the decorations into two pieces: DecorSignHighlight for signs, highlights and simple set-flag decorations (like spell, ui-watched), and DecorVirtText for virtual text and lines. The main separation here is whether they are expected to allocate more memory. Currently this is not really true as sign text has to be an allocated string, but the plan is to get rid of this eventually (it can just be an array of two schar_T:s). Further refactors are expected to improve the representation of each decoration kind individually. The goal of this particular PR is to get things started by cutting the Gordian knot which was the monolithic struct Decoration. Now, each extmark can either contain chained indicies/pointers to these kinds of objects, or it can fit a subset of DecorSignHighlight inline. The point of this change is not only to make decorations smaller in memory. In fact, the main motivation is to later allow them to grow _larger_, but on a dynamic, on demand fashion. As a simple example, it would be possible to augment highlights to take a list of multiple `hl_group`:s, which then would trivially map to a chain of multiple DecorSignHighlight entries. One small feature improvement included with this refactor itself, is that the restriction that extmarks cannot be removed inside a decoration provider has been lifted. These are instead safely lifetime extended on a "to free" list until the current iteration of screen drawing is done. NB: flags is a mess. but DecorLevel is useless, this slightly less so --- src/nvim/api/extmark.c | 279 +++++++++++++++++++++++-------------------------- 1 file changed, 131 insertions(+), 148 deletions(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 31fc01403a..8a2cde8372 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -103,15 +103,6 @@ bool ns_initialized(uint32_t ns) return ns < (uint32_t)next_namespace_id; } -static Object hl_group_name(int hl_id, bool hl_name) -{ - if (hl_name) { - return CSTR_TO_OBJ(syn_id2name(hl_id)); - } else { - return INTEGER_OBJ(hl_id); - } -} - Array virt_text_to_array(VirtText vt, bool hl_name) { Array chunks = ARRAY_DICT_INIT; @@ -176,85 +167,9 @@ static Array extmark_to_array(MTPair extmark, bool id, bool add_dict, bool hl_na PUT(dict, "invalid", BOOLEAN_OBJ(true)); } - // pretend this is a pointer for a short while, Decoration will be factored away very soon - const Decoration decor[1] = { get_decor(start) }; - if (decor->hl_id) { - PUT(dict, "hl_group", hl_group_name(decor->hl_id, hl_name)); - PUT(dict, "hl_eol", BOOLEAN_OBJ(decor->hl_eol)); - } - if (decor->hl_mode) { - PUT(dict, "hl_mode", CSTR_TO_OBJ(hl_mode_str[decor->hl_mode])); - } - - if (kv_size(decor->virt_text)) { - Array chunks = virt_text_to_array(decor->virt_text, hl_name); - 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", - CSTR_TO_OBJ(virt_text_pos_str[decor->virt_text_pos])); - } - - if (decor->ui_watched) { - PUT(dict, "ui_watched", 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 < kv_size(decor->virt_lines); i++) { - virt_lines_leftcol = kv_A(decor->virt_lines, i).left_col; - Array chunks = virt_text_to_array(kv_A(decor->virt_lines, i).line, hl_name); - 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 (decor->sign_text) { - PUT(dict, "sign_text", CSTR_TO_OBJ(decor->sign_text)); - } - - // uncrustify:off - - struct { char *name; const int val; } hls[] = { - { "sign_hl_group" , decor->sign_hl_id }, - { "number_hl_group" , decor->number_hl_id }, - { "line_hl_group" , decor->line_hl_id }, - { "cursorline_hl_group", decor->cursorline_hl_id }, - { NULL, 0 }, - }; - - // uncrustify:on - - for (int j = 0; hls[j].name; j++) { - if (hls[j].val) { - PUT(dict, hls[j].name, hl_group_name(hls[j].val, hl_name)); - } - } - - if (decor->sign_text - || decor->hl_id - || kv_size(decor->virt_text) - || decor->ui_watched) { - PUT(dict, "priority", INTEGER_OBJ(decor->priority)); - } - - if (decor->conceal) { - String name = cstr_to_string((char *)&decor->conceal_char); - PUT(dict, "conceal", STRING_OBJ(name)); - } - - if (decor->spell != kNone) { - PUT(dict, "spell", BOOLEAN_OBJ(decor->spell == kTrue)); - } + 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; @@ -581,8 +496,14 @@ 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; + String conceal_char_large = STRING_INIT; buf_T *buf = find_buffer_by_handle(buffer, err); if (!buf) { @@ -643,11 +564,11 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer 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 }, }; @@ -655,26 +576,33 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer for (int j = 0; hls[j].name && hls[j].dest; j++) { 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 (HAS_KEY(opts, set_extmark, conceal)) { + hl.flags |= kSHConceal; + has_hl = true; String c = opts->conceal; - decor.conceal = true; - if (c.size) { - decor.conceal_char = utf_ptr2char(c.data); + if (c.size > 0) { + if (c.size <= 4) { + memcpy(hl.conceal_char, c.data, c.size + (c.size < 4 ? 1 : 0)); + } else { + conceal_char_large = c; + } } - has_decor = true; } if (HAS_KEY(opts, set_extmark, virt_text)) { - decor.virt_text = parse_virt_text(opts->virt_text, err, &decor.virt_text_width); - has_decor = true; + virt_text.data.virt_text = parse_virt_text(opts->virt_text, err, &virt_text.width); if (ERROR_SET(err)) { goto error; } @@ -683,13 +611,13 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer 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)) { - decor.virt_text_pos = kVTInline; + virt_text.pos = kVPosInline; } else { VALIDATE_S(false, "virt_text_pos", str.data, { goto error; @@ -698,26 +626,26 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } if (HAS_KEY(opts, set_extmark, virt_text_win_col)) { - decor.col = (int)opts->virt_text_win_col; - decor.virt_text_pos = kVTWinCol; + virt_text.col = (int)opts->virt_text_win_col; + virt_text.pos = kVPosWinCol; } - decor.hl_eol = opts->hl_eol; - decor.virt_text_hide = opts->virt_text_hide; + hl.flags |= opts->hl_eol ? kSHHlEol : 0; + virt_text.flags |= opts->virt_text_hide ? kVTHide : 0; 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)) { - if (decor.virt_text_pos == kVTInline) { + if (virt_text.pos == kVPosInline) { VALIDATE(false, "%s", "cannot use 'blend' hl_mode with inline virtual text", { goto error; }); } - decor.hl_mode = kHlModeBlend; + virt_text.hl_mode = kHlModeBlend; } else { VALIDATE_S(false, "hl_mode", str.data, { goto error; @@ -735,29 +663,32 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer }); 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; } } - decor.virt_lines_above = opts->virt_lines_above; + virt_lines.flags |= opts->virt_lines_above ? kVTLinesAbove : 0; if (HAS_KEY(opts, set_extmark, priority)) { VALIDATE_RANGE((opts->priority >= 0 && opts->priority <= UINT16_MAX), "priority", { goto error; }); - decor.priority = (DecorPriority)opts->priority; + hl.priority = (DecorPriority)opts->priority; + sign.priority = (DecorPriority)opts->priority; + virt_text.priority = (DecorPriority)opts->priority; + virt_lines.priority = (DecorPriority)opts->priority; } if (HAS_KEY(opts, set_extmark, sign_text)) { - VALIDATE_S(init_sign_text(NULL, &decor.sign_text, opts->sign_text.data), + sign.text.ptr = NULL; + VALIDATE_S(init_sign_text(NULL, &sign.text.ptr, opts->sign_text.data), "sign_text", "", { goto error; }); - has_decor = true; + sign.flags |= kSHIsSign; } bool right_gravity = GET_BOOL_OR_TRUE(opts, set_extmark, right_gravity); @@ -771,16 +702,18 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer size_t len = 0; - if (!HAS_KEY(opts, set_extmark, spell)) { - decor.spell = kNone; - } else { - decor.spell = opts->spell ? kTrue : kFalse; - has_decor = true; + if (HAS_KEY(opts, set_extmark, spell)) { + hl.flags |= (opts->spell) ? kSHSpellOn : kSHSpellOff; + has_hl = true; } - decor.ui_watched = opts->ui_watched; - 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; } VALIDATE_RANGE((line >= 0), "line", { @@ -829,28 +762,90 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer col2 = 0; } - // TODO(bfredl): synergize these two branches even more if (opts->ephemeral && decor_state.win && decor_state.win->w_buffer == buf) { - decor_push_ephemeral((int)line, (int)col, line2, col2, &decor, (uint64_t)ns_id, id); + 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, conceal_char_large); + decor_range_add_sh(&decor_state, r, c, line2, col2, &sh, true, (uint32_t)ns_id, id); + } + if (sign.flags & kSHIsSign) { + decor_range_add_sh(&decor_state, r, c, line2, col2, &sign, true, (uint32_t)ns_id, id); + } } else { 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 || conceal_char_large.size) { + if (has_hl) { + DecorSignHighlight sh = decor_sh_from_inline(hl, conceal_char_large); + 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, opts->end_right_gravity, + 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)) { - goto error; + decor_free(decor); + return 0; } } return (Integer)id; error: - decor_clear(&decor); + clear_virttext(&virt_text.data.virt_text); + clear_virtlines(&virt_lines.data.virt_lines); return 0; } @@ -873,11 +868,6 @@ Boolean nvim_buf_del_extmark(Buffer buffer, Integer ns_id, Integer id, Error *er return false; }); - if (decor_state.running_on_lines) { - api_set_error(err, kErrorTypeValidation, "Cannot remove extmarks during on_line callbacks"); - return false; - } - return extmark_del_id(buf, (uint32_t)ns_id, (uint32_t)id); } @@ -962,13 +952,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, false, false, NULL); + 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; } @@ -997,11 +985,6 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start, return; }); - if (decor_state.running_on_lines) { - api_set_error(err, kErrorTypeValidation, "Cannot remove extmarks during on_line callbacks"); - return; - } - if (line_end < 0 || line_end > MAXLNUM) { line_end = MAXLNUM; } -- cgit From a827003e3052c6d9ee7bdb71518182e9bd76317d Mon Sep 17 00:00:00 2001 From: dundargoc Date: Sat, 25 Nov 2023 11:32:32 +0100 Subject: build: rework IWYU mapping files Create mapping to most of the C spec and some POSIX specific functions. This is more robust than relying files shipped with IWYU. --- src/nvim/api/extmark.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 8a2cde8372..d2e387f478 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -13,6 +13,7 @@ #include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/decoration.h" +#include "nvim/decoration_defs.h" #include "nvim/decoration_provider.h" #include "nvim/drawscreen.h" #include "nvim/extmark.h" @@ -23,8 +24,6 @@ #include "nvim/memory.h" #include "nvim/pos.h" #include "nvim/sign.h" -#include "nvim/strings.h" -#include "nvim/vim.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/extmark.c.generated.h" -- cgit From 6361806aa28edca55ad3316a58bc3e936df9c0eb Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 26 Nov 2023 22:58:52 +0800 Subject: refactor: move garray_T to garray_defs.h (#26227) --- src/nvim/api/extmark.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index d2e387f478..5112d2474d 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -13,7 +13,6 @@ #include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/decoration.h" -#include "nvim/decoration_defs.h" #include "nvim/decoration_provider.h" #include "nvim/drawscreen.h" #include "nvim/extmark.h" -- cgit From 574d25642fc9ca65b396633aeab6e2d32778b642 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 27 Nov 2023 17:21:58 +0800 Subject: refactor: move Arena and ArenaMem to memory_defs.h (#26240) --- src/nvim/api/extmark.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 5112d2474d..28fcf7dd0f 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -1,10 +1,10 @@ #include +#include #include #include #include #include "klib/kvec.h" -#include "lauxlib.h" #include "nvim/api/extmark.h" #include "nvim/api/keysets.h" #include "nvim/api/private/defs.h" -- cgit From 8b428ca8b79ebb7b36c3e403ff3bcb6924a635a6 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Mon, 27 Nov 2023 16:00:21 +0100 Subject: build(IWYU): fix includes for func_attr.h --- src/nvim/api/extmark.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 28fcf7dd0f..b7bc57e52c 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -16,6 +16,7 @@ #include "nvim/decoration_provider.h" #include "nvim/drawscreen.h" #include "nvim/extmark.h" +#include "nvim/func_attr.h" #include "nvim/highlight_group.h" #include "nvim/marktree.h" #include "nvim/mbyte.h" -- cgit From f4aedbae4cb1f206f5b7c6142697b71dd473059b Mon Sep 17 00:00:00 2001 From: dundargoc Date: Mon, 27 Nov 2023 18:39:38 +0100 Subject: build(IWYU): fix includes for undo_defs.h --- src/nvim/api/extmark.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index b7bc57e52c..98074da139 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -22,7 +22,7 @@ #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" -#include "nvim/pos.h" +#include "nvim/pos_defs.h" #include "nvim/sign.h" #ifdef INCLUDE_GENERATED_DECLARATIONS -- cgit From e38a05369293293b5b510b1b0014fcc2e7cb87f4 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Mon, 27 Nov 2023 18:46:03 +0100 Subject: build(IWYU): export generated headers --- src/nvim/api/extmark.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 98074da139..a05d75e3e6 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -8,6 +8,7 @@ #include "nvim/api/extmark.h" #include "nvim/api/keysets.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" -- cgit From e3f735ef101d670555f44226614a5c3557053b1f Mon Sep 17 00:00:00 2001 From: dundargoc Date: Mon, 27 Nov 2023 20:13:32 +0100 Subject: refactor: fix includes for api/autocmd.h --- src/nvim/api/extmark.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index a05d75e3e6..9a4dfe6788 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -6,7 +6,7 @@ #include "klib/kvec.h" #include "nvim/api/extmark.h" -#include "nvim/api/keysets.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" -- cgit From ae3685798deaf51f14422c568998998c03f91f2c Mon Sep 17 00:00:00 2001 From: bfredl Date: Sun, 26 Nov 2023 21:07:29 +0100 Subject: feat(decoration): allow conceal_char to be a composing char decor->text.str pointer must go. This removes it for conceal char, in preparation for a larger PR which will also handle the sign case. By actually allowing composing chars for a conceal chars, this becomes a feature and not just a refactor, as a bonus. --- src/nvim/api/extmark.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 9a4dfe6788..d9e41f2448 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -18,6 +18,7 @@ #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" @@ -503,7 +504,6 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer DecorVirtText virt_text = DECOR_VIRT_TEXT_INIT; DecorVirtText virt_lines = DECOR_VIRT_LINES_INIT; bool has_hl = false; - String conceal_char_large = STRING_INIT; buf_T *buf = find_buffer_by_handle(buffer, err); if (!buf) { @@ -593,10 +593,11 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer has_hl = true; String c = opts->conceal; if (c.size > 0) { - if (c.size <= 4) { - memcpy(hl.conceal_char, c.data, c.size + (c.size < 4 ? 1 : 0)); - } else { - conceal_char_large = c; + 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; } } } @@ -777,7 +778,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer 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, conceal_char_large); + DecorSignHighlight sh = decor_sh_from_inline(hl); decor_range_add_sh(&decor_state, r, c, line2, col2, &sh, true, (uint32_t)ns_id, id); } if (sign.flags & kSHIsSign) { @@ -815,9 +816,9 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } DecorInline decor = DECOR_INLINE_INIT; - if (decor_alloc || decor_indexed != DECOR_ID_INVALID || conceal_char_large.size) { + if (decor_alloc || decor_indexed != DECOR_ID_INVALID || schar_high(hl.conceal_char)) { if (has_hl) { - DecorSignHighlight sh = decor_sh_from_inline(hl, conceal_char_large); + DecorSignHighlight sh = decor_sh_from_inline(hl); sh.next = decor_indexed; decor_indexed = decor_put_sh(sh); } -- cgit From a0e9ef09d7af8274c754ca6c368ef4a6f7411510 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 29 Nov 2023 02:17:16 +0100 Subject: fix(decorations): do not apply sign highlight id as range attr id --- src/nvim/api/extmark.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'src/nvim/api/extmark.c') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index d9e41f2448..d71498d6ed 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -781,9 +781,6 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer DecorSignHighlight sh = decor_sh_from_inline(hl); decor_range_add_sh(&decor_state, r, c, line2, col2, &sh, true, (uint32_t)ns_id, id); } - if (sign.flags & kSHIsSign) { - decor_range_add_sh(&decor_state, r, c, line2, col2, &sign, true, (uint32_t)ns_id, id); - } } else { if (opts->ephemeral) { api_set_error(err, kErrorTypeException, "not yet implemented"); -- cgit