diff options
79 files changed, 1555 insertions, 390 deletions
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index e7cda59906..f399f1ed25 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -2608,6 +2608,8 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {*opts}) • "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) diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 9f6ab2ddd0..5690dbf3bc 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -56,6 +56,8 @@ The following new APIs or features were added. • |Query:iter_matches()| now has the ability to set the maximum start depth for matches. +• Added inline virtual text support to |nvim_buf_set_extmark()|. + ============================================================================== CHANGED FEATURES *news-changed* diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 6ecbff2606..14937cfd8f 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -293,17 +293,17 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) break; case kCallbackFuncref: case kCallbackPartial: - PUT(autocmd_info, "callback", STRING_OBJ(cstr_as_string(callback_to_string(cb)))); + PUT(autocmd_info, "callback", CSTR_AS_OBJ(callback_to_string(cb))); break; default: abort(); } } else { - PUT(autocmd_info, "command", STRING_OBJ(cstr_as_string(xstrdup(ac->exec.callable.cmd)))); + PUT(autocmd_info, "command", CSTR_TO_OBJ(ac->exec.callable.cmd)); } - PUT(autocmd_info, "pattern", STRING_OBJ(cstr_to_string(ap->pat))); - PUT(autocmd_info, "event", STRING_OBJ(cstr_to_string(event_nr2name(event)))); + PUT(autocmd_info, "pattern", CSTR_TO_OBJ(ap->pat)); + PUT(autocmd_info, "event", CSTR_TO_OBJ(event_nr2name(event))); PUT(autocmd_info, "once", BOOLEAN_OBJ(ac->once)); if (ap->buflocal_nr) { @@ -475,7 +475,7 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc } if (patterns.size == 0) { - ADD(patterns, STRING_OBJ(STATIC_CSTR_TO_STRING("*"))); + ADD(patterns, STATIC_CSTR_TO_OBJ("*")); } VALIDATE_R((event_array.size > 0), "event", { @@ -587,7 +587,7 @@ void nvim_clear_autocmds(Dict(clear_autocmds) *opts, Error *err) // When we create the autocmds, we want to say that they are all matched, so that's * // but when we clear them, we want to say that we didn't pass a pattern, so that's NUL if (patterns.size == 0) { - ADD(patterns, STRING_OBJ(STATIC_CSTR_TO_STRING(""))); + ADD(patterns, STATIC_CSTR_TO_OBJ("")); } // If we didn't pass any events, that means clear all events. @@ -763,7 +763,7 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err) } if (patterns.size == 0) { - ADD(patterns, STRING_OBJ(STATIC_CSTR_TO_STRING(""))); + ADD(patterns, STATIC_CSTR_TO_OBJ("")); } if (HAS_KEY(opts->data)) { @@ -894,7 +894,7 @@ static bool get_patterns_from_pattern_or_buf(Array *patterns, Object pattern, Ob } snprintf((char *)pattern_buflocal, BUFLOCAL_PAT_LEN, "<buffer=%d>", (int)buf->handle); - ADD(*patterns, STRING_OBJ(cstr_to_string(pattern_buflocal))); + ADD(*patterns, CSTR_TO_OBJ(pattern_buflocal)); } return true; diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 10c684941c..82a62c3192 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -518,7 +518,7 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In { MAXSIZE_TEMP_ARRAY(scratch, 1); if (replacement.size == 0) { - ADD_C(scratch, STRING_OBJ(STATIC_CSTR_AS_STRING(""))); + ADD_C(scratch, STATIC_CSTR_AS_OBJ("")); replacement = scratch; } diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c index b1603c5d6e..6d715bcf46 100644 --- a/src/nvim/api/command.c +++ b/src/nvim/api/command.c @@ -245,7 +245,7 @@ Dictionary nvim_parse_cmd(String str, Dictionary opts, Error *err) Dictionary filter = ARRAY_DICT_INIT; PUT(filter, "pattern", cmdinfo.cmdmod.cmod_filter_pat ? CSTR_TO_OBJ(cmdinfo.cmdmod.cmod_filter_pat) - : STRING_OBJ(STATIC_CSTR_TO_STRING(""))); + : STATIC_CSTR_TO_OBJ("")); PUT(filter, "force", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_filter_force)); PUT(mods, "filter", DICTIONARY_OBJ(filter)); @@ -438,7 +438,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error break; } - ADD(args, STRING_OBJ(cstr_as_string(data_str))); + ADD(args, CSTR_AS_OBJ(data_str)); } bool argc_valid; diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index d6f0288f94..299413e510 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 @@ -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; diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c index 6193a5c75b..e18312a6dc 100644 --- a/src/nvim/api/options.c +++ b/src/nvim/api/options.c @@ -188,7 +188,7 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) switch (result) { case gov_string: - rv = STRING_OBJ(cstr_as_string(stringval)); + rv = CSTR_AS_OBJ(stringval); break; case gov_number: rv = INTEGER_OBJ(numval); diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 02060a8950..2544809553 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -660,10 +660,10 @@ static void init_ui_event_metadata(Dictionary *metadata) msgpack_unpacked_destroy(&unpacked); PUT(*metadata, "ui_events", ui_events); Array ui_options = ARRAY_DICT_INIT; - ADD(ui_options, STRING_OBJ(cstr_to_string("rgb"))); + ADD(ui_options, CSTR_TO_OBJ("rgb")); for (UIExtension i = 0; i < kUIExtCount; i++) { if (ui_ext_names[i][0] != '_') { - ADD(ui_options, STRING_OBJ(cstr_to_string(ui_ext_names[i]))); + ADD(ui_options, CSTR_TO_OBJ(ui_ext_names[i])); } } PUT(*metadata, "ui_options", ARRAY_OBJ(ui_options)); @@ -692,17 +692,17 @@ static void init_type_metadata(Dictionary *metadata) Dictionary buffer_metadata = ARRAY_DICT_INIT; PUT(buffer_metadata, "id", INTEGER_OBJ(kObjectTypeBuffer - EXT_OBJECT_TYPE_SHIFT)); - PUT(buffer_metadata, "prefix", STRING_OBJ(cstr_to_string("nvim_buf_"))); + PUT(buffer_metadata, "prefix", CSTR_TO_OBJ("nvim_buf_")); Dictionary window_metadata = ARRAY_DICT_INIT; PUT(window_metadata, "id", INTEGER_OBJ(kObjectTypeWindow - EXT_OBJECT_TYPE_SHIFT)); - PUT(window_metadata, "prefix", STRING_OBJ(cstr_to_string("nvim_win_"))); + PUT(window_metadata, "prefix", CSTR_TO_OBJ("nvim_win_")); Dictionary tabpage_metadata = ARRAY_DICT_INIT; PUT(tabpage_metadata, "id", INTEGER_OBJ(kObjectTypeTabpage - EXT_OBJECT_TYPE_SHIFT)); - PUT(tabpage_metadata, "prefix", STRING_OBJ(cstr_to_string("nvim_tabpage_"))); + PUT(tabpage_metadata, "prefix", CSTR_TO_OBJ("nvim_tabpage_")); PUT(types, "Buffer", DICTIONARY_OBJ(buffer_metadata)); PUT(types, "Window", DICTIONARY_OBJ(window_metadata)); diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index bac501ed62..a9cfaeae22 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -34,6 +34,7 @@ .type = kObjectTypeString, \ .data.string = s }) +#define CSTR_AS_OBJ(s) STRING_OBJ(cstr_as_string(s)) #define CSTR_TO_OBJ(s) STRING_OBJ(cstr_to_string(s)) #define BUFFER_OBJ(s) ((Object) { \ @@ -103,6 +104,9 @@ .data = xmemdupz(s, sizeof(s) - 1), \ .size = sizeof(s) - 1 }) +#define STATIC_CSTR_AS_OBJ(s) STRING_OBJ(STATIC_CSTR_AS_STRING(s)) +#define STATIC_CSTR_TO_OBJ(s) STRING_OBJ(STATIC_CSTR_TO_STRING(s)) + // Helpers used by the generated msgpack-rpc api wrappers #define api_init_boolean #define api_init_integer diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 532c3054ab..e98c589189 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -795,7 +795,7 @@ void remote_ui_put(UI *ui, const char *cell) UIData *data = ui->data; data->client_col++; Array args = data->call_buf; - ADD_C(args, STRING_OBJ(cstr_as_string((char *)cell))); + ADD_C(args, CSTR_AS_OBJ((char *)cell)); push_call(ui, "put", args); } diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index d47f47e638..36163859eb 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -531,7 +531,7 @@ static void find_runtime_cb(char *fname, void *cookie) { Array *rv = (Array *)cookie; if (fname != NULL) { - ADD(*rv, STRING_OBJ(cstr_to_string(fname))); + ADD(*rv, CSTR_TO_OBJ(fname)); } } @@ -1383,7 +1383,7 @@ Dictionary nvim_get_mode(void) get_mode(modestr); bool blocked = input_blocking(); - PUT(rv, "mode", STRING_OBJ(cstr_to_string(modestr))); + PUT(rv, "mode", CSTR_TO_OBJ(modestr)); PUT(rv, "blocking", BOOLEAN_OBJ(blocked)); return rv; @@ -1926,7 +1926,7 @@ Array nvim__inspect_cell(Integer grid, Integer row, Integer col, Arena *arena, E } ret = arena_array(arena, 3); size_t off = g->line_offset[(size_t)row] + (size_t)col; - ADD_C(ret, STRING_OBJ(cstr_as_string((char *)g->chars[off]))); + ADD_C(ret, CSTR_AS_OBJ((char *)g->chars[off])); int attr = g->attrs[off]; ADD_C(ret, DICTIONARY_OBJ(hl_get_attr_by_id(attr, true, arena, err))); // will not work first time @@ -2035,7 +2035,7 @@ Array nvim_get_mark(String name, Dictionary opts, Error *err) ADD(rv, INTEGER_OBJ(row)); ADD(rv, INTEGER_OBJ(col)); ADD(rv, INTEGER_OBJ(bufnr)); - ADD(rv, STRING_OBJ(cstr_to_string(filename))); + ADD(rv, CSTR_TO_OBJ(filename)); if (allocated) { xfree(filename); diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c index 208aa165c9..1a67be8860 100644 --- a/src/nvim/api/vimscript.c +++ b/src/nvim/api/vimscript.c @@ -502,7 +502,7 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, E }; err_dict.items[0] = (KeyValuePair) { .key = STATIC_CSTR_TO_STRING("message"), - .value = STRING_OBJ(cstr_to_string(east.err.msg)), + .value = CSTR_TO_OBJ(east.err.msg), }; if (east.err.arg == NULL) { err_dict.items[1] = (KeyValuePair) { @@ -539,7 +539,7 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, E chunk_arr.items[0] = INTEGER_OBJ((Integer)chunk.start.line); chunk_arr.items[1] = INTEGER_OBJ((Integer)chunk.start.col); chunk_arr.items[2] = INTEGER_OBJ((Integer)chunk.end_col); - chunk_arr.items[3] = STRING_OBJ(cstr_to_string(chunk.group)); + chunk_arr.items[3] = CSTR_TO_OBJ(chunk.group); hl.items[i] = ARRAY_OBJ(chunk_arr); } ret.items[ret.size++] = (KeyValuePair) { @@ -616,7 +616,7 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, E kv_drop(ast_conv_stack, 1); ret_node->items[ret_node->size++] = (KeyValuePair) { .key = STATIC_CSTR_TO_STRING("type"), - .value = STRING_OBJ(cstr_to_string(east_node_type_tab[node->type])), + .value = CSTR_TO_OBJ(east_node_type_tab[node->type]), }; Array start_array = { .items = xmalloc(2 * sizeof(start_array.items[0])), @@ -701,11 +701,11 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, E case kExprNodeComparison: ret_node->items[ret_node->size++] = (KeyValuePair) { .key = STATIC_CSTR_TO_STRING("cmp_type"), - .value = STRING_OBJ(cstr_to_string(eltkn_cmp_type_tab[node->data.cmp.type])), + .value = CSTR_TO_OBJ(eltkn_cmp_type_tab[node->data.cmp.type]), }; ret_node->items[ret_node->size++] = (KeyValuePair) { .key = STATIC_CSTR_TO_STRING("ccs_strategy"), - .value = STRING_OBJ(cstr_to_string(ccs_tab[node->data.cmp.ccs])), + .value = CSTR_TO_OBJ(ccs_tab[node->data.cmp.ccs]), }; ret_node->items[ret_node->size++] = (KeyValuePair) { .key = STATIC_CSTR_TO_STRING("invert"), diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index c267fee39a..8e4fbb6779 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -267,7 +267,7 @@ Dictionary nvim_win_get_config(Window window, Error *err) PUT(rv, "bufpos", ARRAY_OBJ(pos)); } } - PUT(rv, "anchor", STRING_OBJ(cstr_to_string(float_anchor_str[config->anchor]))); + PUT(rv, "anchor", CSTR_TO_OBJ(float_anchor_str[config->anchor])); PUT(rv, "row", FLOAT_OBJ(config->row)); PUT(rv, "col", FLOAT_OBJ(config->col)); PUT(rv, "zindex", INTEGER_OBJ(config->zindex)); @@ -283,7 +283,7 @@ Dictionary nvim_win_get_config(Window window, Error *err) char *hi_name = syn_id2name(hi_id); if (hi_name[0]) { ADD(tuple, STRING_OBJ(s)); - ADD(tuple, STRING_OBJ(cstr_to_string(hi_name))); + ADD(tuple, CSTR_TO_OBJ(hi_name)); ADD(border, ARRAY_OBJ(tuple)); } else { ADD(border, STRING_OBJ(s)); @@ -297,7 +297,7 @@ Dictionary nvim_win_get_config(Window window, Error *err) Array tuple = ARRAY_DICT_INIT; ADD(tuple, CSTR_TO_OBJ(title_datas.items[i].text)); if (title_datas.items[i].hl_id > 0) { - ADD(tuple, STRING_OBJ(cstr_to_string(syn_id2name(title_datas.items[i].hl_id)))); + ADD(tuple, CSTR_TO_OBJ(syn_id2name(title_datas.items[i].hl_id))); } ADD(titles, ARRAY_OBJ(tuple)); } @@ -317,7 +317,7 @@ Dictionary nvim_win_get_config(Window window, Error *err) const char *rel = (wp->w_floating && !config->external ? float_relative_str[config->relative] : ""); - PUT(rv, "relative", STRING_OBJ(cstr_to_string(rel))); + PUT(rv, "relative", CSTR_TO_OBJ(rel)); return rv; } diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index f8b26d9d16..f3f98bbd17 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -808,6 +808,7 @@ struct file_buffer { MarkTree b_marktree[1]; Map(uint32_t, uint32_t) b_extmark_ns[1]; // extmark namespaces + size_t b_virt_text_inline; // number of inline virtual texts size_t b_virt_line_blocks; // number of virt_line blocks size_t b_signs; // number of sign extmarks size_t b_signs_with_text; // number of sign extmarks with text diff --git a/src/nvim/channel.c b/src/nvim/channel.c index 154b8206b8..569d3f5887 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -884,14 +884,14 @@ Dictionary channel_info(uint64_t id) stream_desc = "job"; if (chan->stream.proc.type == kProcessTypePty) { const char *name = pty_process_tty_name(&chan->stream.pty); - PUT(info, "pty", STRING_OBJ(cstr_to_string(name))); + PUT(info, "pty", CSTR_TO_OBJ(name)); } char **p = chan->stream.proc.argv; Array argv = ARRAY_DICT_INIT; if (p != NULL) { while (*p != NULL) { - ADD(argv, STRING_OBJ(cstr_to_string(*p))); + ADD(argv, CSTR_TO_OBJ(*p)); p++; } } @@ -918,7 +918,7 @@ Dictionary channel_info(uint64_t id) default: abort(); } - PUT(info, "stream", STRING_OBJ(cstr_to_string(stream_desc))); + PUT(info, "stream", CSTR_TO_OBJ(stream_desc)); if (chan->is_rpc) { mode_desc = "rpc"; @@ -929,7 +929,7 @@ Dictionary channel_info(uint64_t id) } else { mode_desc = "bytes"; } - PUT(info, "mode", STRING_OBJ(cstr_to_string(mode_desc))); + PUT(info, "mode", CSTR_TO_OBJ(mode_desc)); return info; } diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 8cae831881..49890a460a 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -989,6 +989,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en } chartabsize_T cts; + bool on_NUL = false; init_chartabsize_arg(&cts, wp, pos->lnum, 0, line, line); // This function is used very often, do some speed optimizations. @@ -1052,8 +1053,9 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en // make sure we don't go past the end of the line if (*cts.cts_ptr == NUL) { - // NUL at end of line only takes one column - incr = 1; + // NUL at end of line only takes one column, unless there is virtual text + incr = MAX(1, cts.cts_cur_text_width_left + cts.cts_cur_text_width_right); + on_NUL = true; break; } @@ -1079,8 +1081,6 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en } if (cursor != NULL) { - // cursor is after inserted text - vcol += cts.cts_cur_text_width; if ((*ptr == TAB) && (State & MODE_NORMAL) && !wp->w_p_list @@ -1089,6 +1089,13 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en // cursor at end *cursor = vcol + incr - 1; } else { + if (!on_NUL) { + // cursor is after inserted text, unless on the NUL + vcol += cts.cts_cur_text_width_left; + if ((State & MODE_INSERT) == 0) { + vcol += cts.cts_cur_text_width_right; + } + } // cursor at start *cursor = vcol + head; } diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c index 428f9f28e4..4e5886406a 100644 --- a/src/nvim/cursor_shape.c +++ b/src/nvim/cursor_shape.c @@ -62,8 +62,8 @@ Array mode_style_array(Arena *arena) for (int i = 0; i < SHAPE_IDX_COUNT; i++) { cursorentry_T *cur = &shape_table[i]; Dictionary dic = arena_dict(arena, 3 + ((cur->used_for & SHAPE_CURSOR) ? 9 : 0)); - PUT_C(dic, "name", STRING_OBJ(cstr_as_string(cur->full_name))); - PUT_C(dic, "short_name", STRING_OBJ(cstr_as_string(cur->name))); + PUT_C(dic, "name", CSTR_AS_OBJ(cur->full_name)); + PUT_C(dic, "short_name", CSTR_AS_OBJ(cur->name)); if (cur->used_for & SHAPE_MOUSE) { PUT_C(dic, "mouse_shape", INTEGER_OBJ(cur->mshape)); } diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index 87e4441f32..ce1af290c1 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -83,6 +83,9 @@ void decor_redraw(buf_T *buf, int row1, int row2, Decoration *decor) if (decor && decor_virt_pos(*decor)) { redraw_buf_line_later(buf, row1 + 1, false); + if (decor->virt_text_pos == kVTInline) { + changed_line_display_buf(buf); + } } if (decor && kv_size(decor->virt_lines)) { @@ -95,6 +98,10 @@ void decor_remove(buf_T *buf, int row, int row2, Decoration *decor) { decor_redraw(buf, row, row2, decor); if (decor) { + if (kv_size(decor->virt_text) && decor->virt_text_pos == kVTInline) { + assert(buf->b_virt_text_inline > 0); + buf->b_virt_text_inline--; + } if (kv_size(decor->virt_lines)) { assert(buf->b_virt_line_blocks > 0); buf->b_virt_line_blocks--; diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h index 92001d496d..95c9655742 100644 --- a/src/nvim/decoration.h +++ b/src/nvim/decoration.h @@ -23,9 +23,11 @@ typedef enum { kVTOverlay, kVTWinCol, kVTRightAlign, + kVTInline, } VirtTextPos; -EXTERN const char *const virt_text_pos_str[] INIT(= { "eol", "overlay", "win_col", "right_align" }); +EXTERN const char *const virt_text_pos_str[] INIT(= { "eol", "overlay", "win_col", "right_align", + "inline" }); typedef enum { kHlModeUnknown, diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index dc90be36be..510731a438 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -98,6 +98,7 @@ typedef struct { int char_attr; ///< attributes for next character int n_extra; ///< number of extra bytes + int n_attr; ///< chars with special attr char *p_extra; ///< string of extra chars, plus NUL, only used ///< when c_extra and c_final are NUL char *p_extra_free; ///< p_extra buffer that needs to be freed @@ -105,9 +106,12 @@ typedef struct { int c_extra; ///< extra chars, all the same int c_final; ///< final char, mandatory if set + bool extra_for_extmark; + // saved "extra" items for when draw_state becomes WL_LINE (again) int saved_n_extra; char *saved_p_extra; + bool saved_extra_for_extmark; int saved_c_extra; int saved_c_final; int saved_char_attr; @@ -120,6 +124,14 @@ typedef struct { int filler_lines; ///< nr of filler lines to be drawn int filler_todo; ///< nr of filler lines still to do + 1 SignTextAttrs sattrs[SIGN_SHOW_MAX]; ///< sign attributes for the sign column + + VirtText virt_inline; + size_t virt_inline_i; + + bool reset_extra_attr; + + int skip_cells; // nr of cells to skip for virtual text + int skipped_cells; // nr of skipped virtual text cells } winlinevars_T; /// for line_putchar. Contains the state that needs to be remembered from @@ -342,12 +354,20 @@ static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode, schar_T dummy[2]; int cells = line_putchar(buf, &s, through ? dummy : &linebuf_char[col], max_col - col, false, vcol); - // if we failed to emit a char, we still need to advance - cells = MAX(cells, 1); - + // If we failed to emit a char, we still need to put a space and advance. + if (cells < 1) { + schar_from_ascii(linebuf_char[col], ' '); + cells = 1; + } for (int c = 0; c < cells; c++) { linebuf_attr[col++] = attr; } + if (col < max_col && linebuf_char[col][0] == 0) { + // If the left half of a double-width char is overwritten, + // change the right half to a space so that grid redraws properly, + // but don't advance the current column. + schar_from_ascii(linebuf_char[col], ' '); + } vcol += cells; } return col; @@ -843,6 +863,73 @@ static void apply_cursorline_highlight(win_T *wp, winlinevars_T *wlv) } } +static void handle_inline_virtual_text(win_T *wp, winlinevars_T *wlv, ptrdiff_t v, bool *do_save) +{ + while (true) { + // we could already be inside an existing inline text with multiple chunks + if (!(wlv->virt_inline_i < kv_size(wlv->virt_inline))) { + DecorState *state = &decor_state; + for (size_t i = 0; i < kv_size(state->active); i++) { + DecorRange *item = &kv_A(state->active, i); + if (item->start_row != state->row + || !kv_size(item->decor.virt_text) + || item->decor.virt_text_pos != kVTInline) { + continue; + } + if (item->win_col >= -1 && item->start_col == v) { + wlv->virt_inline = item->decor.virt_text; + wlv->virt_inline_i = 0; + item->win_col = -2; + break; + } + } + } + + if (wlv->n_extra == 0 || !wlv->extra_for_extmark) { + wlv->reset_extra_attr = false; + } + + if (wlv->n_extra <= 0 && wlv->virt_inline_i < kv_size(wlv->virt_inline)) { + VirtTextChunk vtc = kv_A(wlv->virt_inline, wlv->virt_inline_i); + wlv->p_extra = vtc.text; + wlv->n_extra = (int)strlen(wlv->p_extra); + wlv->extra_for_extmark = true; + wlv->c_extra = NUL; + wlv->c_final = NUL; + wlv->extra_attr = vtc.hl_id ? syn_id2attr(vtc.hl_id) : 0; + wlv->n_attr = mb_charlen(vtc.text); + wlv->virt_inline_i++; + *do_save = true; + // If the text didn't reach until the first window + // column we need to skip cells. + if (wlv->skip_cells > 0) { + int virt_text_len = wlv->n_attr; + if (virt_text_len > wlv->skip_cells) { + int len = mb_charlen2bytelen(wlv->p_extra, wlv->skip_cells); + wlv->n_extra -= len; + wlv->p_extra += len; + wlv->n_attr -= wlv->skip_cells; + // Skipped cells needed to be accounted for in vcol. + wlv->skipped_cells += wlv->skip_cells; + wlv->skip_cells = 0; + } else { + // the whole text is left of the window, drop + // it and advance to the next one + wlv->skip_cells -= virt_text_len; + // Skipped cells needed to be accounted for in vcol. + wlv->skipped_cells += virt_text_len; + wlv->n_attr = 0; + wlv->n_extra = 0; + + // go to the start so the next virtual text chunk can be selected. + continue; + } + } + } + break; + } +} + static bool check_mb_utf8(int *c, int *u8cc) { if (utf_char2len(*c) > 1) { @@ -909,6 +996,7 @@ static void win_line_start(win_T *wp, winlinevars_T *wlv, bool save_extra) wlv->draw_state = WL_START; wlv->saved_n_extra = wlv->n_extra; wlv->saved_p_extra = wlv->p_extra; + wlv->saved_extra_for_extmark = wlv->extra_for_extmark; wlv->saved_c_extra = wlv->c_extra; wlv->saved_c_final = wlv->c_final; wlv->saved_char_attr = wlv->char_attr; @@ -923,6 +1011,8 @@ static void win_line_continue(winlinevars_T *wlv) if (wlv->saved_n_extra > 0) { // Continue item from end of wrapped line. wlv->n_extra = wlv->saved_n_extra; + wlv->saved_n_extra = 0; + wlv->extra_for_extmark = wlv->saved_extra_for_extmark; wlv->c_extra = wlv->saved_c_extra; wlv->c_final = wlv->saved_c_final; wlv->p_extra = wlv->saved_p_extra; @@ -961,12 +1051,12 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, // at end-of-line bool has_fold = foldinfo.fi_level != 0 && foldinfo.fi_lines > 0; - int n_attr = 0; // chars with special attr int saved_attr2 = 0; // char_attr saved for n_attr int n_attr3 = 0; // chars with overruling special attr int saved_attr3 = 0; // char_attr saved for n_attr3 - int n_skip = 0; // nr of chars to skip for 'nowrap' + int n_skip = 0; // nr of chars to skip for 'nowrap' or + // concealing int fromcol_prev = -2; // start of inverting after cursor bool noinvcur = false; // don't invert the cursor @@ -978,7 +1068,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, bool area_highlighting = false; // Visual or incsearch highlighting in this line int vi_attr = 0; // attributes for Visual and incsearch highlighting int area_attr = 0; // attributes desired by highlighting + int saved_area_attr = 0; // idem for area_attr int search_attr = 0; // attributes desired by 'hlsearch' + int saved_search_attr = 0; // search_attr to be used when n_extra + // goes to zero int vcol_save_attr = 0; // saved attr for 'cursorcolumn' int syntax_attr = 0; // attributes desired by syntax bool has_syntax = false; // this buffer has syntax highl. @@ -1016,6 +1109,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, int prev_c1 = 0; // first composing char for prev_c bool search_attr_from_match = false; // if search_attr is from :match + bool saved_search_attr_from_match = false; // if search_attr is from :match bool has_decor = false; // this buffer has decoration int win_col_offset = 0; // offset for window columns @@ -1428,6 +1522,12 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, } } + // If there the text doesn't reach to the desired column, need to skip + // "skip_cells" cells when virtual text follows. + if (!wp->w_p_wrap && v > wlv.vcol) { + wlv.skip_cells = (int)(v - wlv.vcol); + } + // Adjust for when the inverted text is before the screen, // and when the start of the inverted text is before the screen. if (wlv.tocol <= wlv.vcol) { @@ -1706,7 +1806,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.n_extra = 0; } - if (wlv.draw_state == WL_LINE && (area_highlighting || has_spell)) { + int extmark_attr = 0; + if (wlv.draw_state == WL_LINE + && (area_highlighting || has_spell || (extra_check && !has_fold))) { // handle Visual or match highlighting in this line if (wlv.vcol == wlv.fromcol || (wlv.vcol + 1 == wlv.fromcol && wlv.n_extra == 0 @@ -1724,7 +1826,29 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, area_active = false; } - if (!wlv.n_extra) { + if (has_decor && v >= 0) { + bool selected = (area_active || (area_highlighting && noinvcur + && wlv.vcol == wp->w_virtcol)); + extmark_attr = decor_redraw_col(wp, (colnr_T)v, wlv.off, + selected, &decor_state); + + bool do_save = false; + handle_inline_virtual_text(wp, &wlv, v, &do_save); + if (do_save) { + // restore search_attr and area_attr when n_extra is down to zero + // TODO(bfredl): this is ugly as fuck. look if we can do this some other way. + saved_search_attr = search_attr; + saved_area_attr = area_attr; + saved_search_attr_from_match = search_attr_from_match; + search_attr_from_match = false; + search_attr = 0; + area_attr = 0; + extmark_attr = 0; + n_skip = 0; + } + } + + if (wlv.n_extra == 0) { // Check for start/end of 'hlsearch' and other matches. // After end, check for start/end of next match. // When another match, have to check for start again. @@ -1742,12 +1866,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, } if (wlv.diff_hlf != (hlf_T)0) { + // When there is extra text (eg: virtual text) it gets the + // diff highlighting for the line, but not for changed text. if (wlv.diff_hlf == HLF_CHD && ptr - line >= change_start && wlv.n_extra == 0) { wlv.diff_hlf = HLF_TXD; // changed text } - if (wlv.diff_hlf == HLF_TXD && ptr - line > change_end - && wlv.n_extra == 0) { + if (wlv.diff_hlf == HLF_TXD && ((ptr - line > change_end && wlv.n_extra == 0) + || (wlv.n_extra > 0 && wlv.extra_for_extmark))) { wlv.diff_hlf = HLF_CHD; // changed line } wlv.line_attr = win_hl_attr(wp, (int)wlv.diff_hlf); @@ -1848,6 +1974,26 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.p_extra++; } wlv.n_extra--; + + // Only restore search_attr and area_attr after "n_extra" in + // the next screen line is also done. + if (wlv.n_extra <= 0) { + if (wlv.saved_n_extra <= 0) { + if (search_attr == 0) { + search_attr = saved_search_attr; + } + if (area_attr == 0 && *ptr != NUL) { + area_attr = saved_area_attr; + } + + if (wlv.extra_for_extmark) { + // wlv.extra_attr should be used at this position but not + // any further. + wlv.reset_extra_attr = true; + } + } + wlv.extra_for_extmark = false; + } } else if (foldinfo.fi_lines > 0) { // skip writing the buffer line itself c = NUL; @@ -1899,7 +2045,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.c_extra = NUL; wlv.c_final = NUL; if (area_attr == 0 && search_attr == 0) { - n_attr = wlv.n_extra + 1; + wlv.n_attr = wlv.n_extra + 1; wlv.extra_attr = win_hl_attr(wp, HLF_8); saved_attr2 = wlv.char_attr; // save current attr } @@ -1954,7 +2100,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.c_final = NUL; c = ' '; if (area_attr == 0 && search_attr == 0) { - n_attr = wlv.n_extra + 1; + wlv.n_attr = wlv.n_extra + 1; wlv.extra_attr = win_hl_attr(wp, HLF_AT); saved_attr2 = wlv.char_attr; // save current attr } @@ -2019,10 +2165,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, } if (has_decor && v > 0) { - bool selected = (area_active || (area_highlighting && noinvcur - && wlv.vcol == wp->w_virtcol)); - int extmark_attr = decor_redraw_col(wp, (colnr_T)v - 1, wlv.off, - selected, &decor_state); if (extmark_attr != 0) { if (!attr_pri) { wlv.char_attr = hl_combine_attr(wlv.char_attr, extmark_attr); @@ -2046,9 +2188,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, v = (ptr - line); if (has_spell && v >= word_end && v > cur_checked_col) { spell_attr = 0; - if (!attr_pri) { - wlv.char_attr = hl_combine_attr(wlv.char_attr, syntax_attr); - } if (c != 0 && ((!has_syntax && !no_plain_buffer) || can_spell)) { char *prev_ptr; char *p; @@ -2130,7 +2269,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, chartabsize_T cts; init_chartabsize_arg(&cts, wp, lnum, wlv.vcol, line, p); + // do not want virtual text to be counted here + cts.cts_has_virt_text = false; wlv.n_extra = win_lbr_chartabsize(&cts, NULL) - 1; + clear_chartabsize_arg(&cts); // We have just drawn the showbreak value, no need to add // space for it again. @@ -2162,7 +2304,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, c = ' '; } } - clear_chartabsize_arg(&cts); } in_multispace = c == ' ' && ((ptr > line + 1 && ptr[-2] == ' ') || *ptr == ' '); @@ -2193,7 +2334,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, } else { c = (c == ' ') ? wp->w_p_lcs_chars.space : wp->w_p_lcs_chars.nbsp; } - n_attr = 1; + wlv.n_attr = 1; wlv.extra_attr = win_hl_attr(wp, HLF_0); saved_attr2 = wlv.char_attr; // save current attr mb_c = c; @@ -2216,7 +2357,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, c = wp->w_p_lcs_chars.space; } - n_attr = 1; + wlv.n_attr = 1; wlv.extra_attr = win_hl_attr(wp, HLF_0); saved_attr2 = wlv.char_attr; // save current attr mb_c = c; @@ -2332,7 +2473,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.c_extra = wp->w_p_lcs_chars.tab2; } wlv.c_final = wp->w_p_lcs_chars.tab3; - n_attr = tab_len + 1; + wlv.n_attr = tab_len + 1; wlv.extra_attr = win_hl_attr(wp, HLF_0); saved_attr2 = wlv.char_attr; // save current attr mb_c = c; @@ -2359,15 +2500,11 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, && wlv.line_attr == 0 && wlv.line_attr_lowprio == 0) { // In virtualedit, visual selections may extend beyond end of line - if (area_highlighting && virtual_active() - && wlv.tocol != MAXCOL && wlv.vcol < wlv.tocol) { - wlv.n_extra = 0; - } else { + if (!(area_highlighting && virtual_active() + && wlv.tocol != MAXCOL && wlv.vcol < wlv.tocol)) { wlv.p_extra = at_end_str; - wlv.n_extra = 1; - wlv.c_extra = NUL; - wlv.c_final = NUL; } + wlv.n_extra = 0; } if (wp->w_p_list && wp->w_p_lcs_chars.eol > 0) { c = wp->w_p_lcs_chars.eol; @@ -2377,7 +2514,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, lcs_eol_one = -1; ptr--; // put it back at the NUL wlv.extra_attr = win_hl_attr(wp, HLF_AT); - n_attr = 1; + wlv.n_attr = 1; mb_c = c; mb_utf8 = check_mb_utf8(&c, u8cc); } else if (c != NUL) { @@ -2406,7 +2543,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.n_extra = byte2cells(c) - 1; c = (uint8_t)(*wlv.p_extra++); } - n_attr = wlv.n_extra + 1; + wlv.n_attr = wlv.n_extra + 1; wlv.extra_attr = win_hl_attr(wp, HLF_8); saved_attr2 = wlv.char_attr; // save current attr mb_utf8 = false; // don't draw as UTF-8 @@ -2466,7 +2603,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, } } wlv.n_extra = 0; - n_attr = 0; + wlv.n_attr = 0; } else if (n_skip == 0) { is_concealing = true; n_skip = 1; @@ -2501,8 +2638,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, } // Don't override visual selection highlighting. - if (n_attr > 0 && wlv.draw_state == WL_LINE && !search_attr_from_match) { + if (wlv.n_attr > 0 && wlv.draw_state == WL_LINE && !search_attr_from_match) { wlv.char_attr = hl_combine_attr(wlv.char_attr, wlv.extra_attr); + if (wlv.reset_extra_attr) { + wlv.reset_extra_attr = false; + wlv.extra_attr = 0; + // search_attr_from_match can be restored now that the extra_attr has been applied + search_attr_from_match = saved_search_attr_from_match; + } } // Handle the case where we are in column 0 but not on the first @@ -2522,7 +2665,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.c_extra = MB_FILLER_CHAR; wlv.c_final = NUL; wlv.n_extra = 1; - n_attr = 2; + wlv.n_attr = 2; wlv.extra_attr = win_hl_attr(wp, HLF_AT); } mb_c = c; @@ -2738,7 +2881,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, && !has_fold && (*ptr != NUL || lcs_eol_one > 0 - || (wlv.n_extra && (wlv.c_extra != NUL || *wlv.p_extra != NUL)))) { + || (wlv.n_extra > 0 && (wlv.c_extra != NUL || *wlv.p_extra != NUL)))) { c = wp->w_p_lcs_chars.ext; wlv.char_attr = win_hl_attr(wp, HLF_AT); mb_c = c; @@ -2809,6 +2952,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.col++; // UTF-8: Put a 0 in the second screen char. linebuf_char[wlv.off][0] = 0; + linebuf_attr[wlv.off] = linebuf_attr[wlv.off - 1]; if (wlv.draw_state > WL_STC && wlv.filler_todo <= 0) { wlv.vcol++; } @@ -2858,7 +3002,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.boguscols += wlv.n_extra; } wlv.n_extra = 0; - n_attr = 0; + wlv.n_attr = 0; } if (utf_char2cells(mb_c) > 1) { @@ -2883,13 +3027,19 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, if (wlv.n_extra > 0) { wlv.vcol += wlv.n_extra; wlv.n_extra = 0; - n_attr = 0; + wlv.n_attr = 0; } } } else { n_skip--; } + // The skipped cells need to be accounted for in vcol. + if (wlv.draw_state > WL_STC && wlv.skipped_cells > 0) { + wlv.vcol += wlv.skipped_cells; + wlv.skipped_cells = 0; + } + // Only advance the "wlv.vcol" when after the 'number' or // 'relativenumber' column. if (wlv.draw_state > WL_STC @@ -2907,7 +3057,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, } // restore attributes after last 'listchars' or 'number' char - if (n_attr > 0 && wlv.draw_state == WL_LINE && --n_attr == 0) { + if (wlv.n_attr > 0 && wlv.draw_state == WL_LINE && --wlv.n_attr == 0) { wlv.char_attr = saved_attr2; } diff --git a/src/nvim/edit.c b/src/nvim/edit.c index e3321a8b99..a25387f5a6 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -232,7 +232,7 @@ static void insert_enter(InsertState *s) stop_insert_mode = false; // need to position cursor again when on a TAB - if (gchar_cursor() == TAB) { + if (gchar_cursor() == TAB || curbuf->b_virt_text_inline > 0) { curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL); } @@ -3471,7 +3471,7 @@ static bool ins_esc(long *count, int cmdchar, bool nomove) State = MODE_NORMAL; may_trigger_modechanged(); // need to position cursor again when on a TAB - if (gchar_cursor() == TAB) { + if (gchar_cursor() == TAB || curbuf->b_virt_text_inline > 0) { curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL); } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 1b21f107a8..52924bf9a5 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -8712,7 +8712,7 @@ void ex_checkhealth(exarg_T *eap) { Error err = ERROR_INIT; MAXSIZE_TEMP_ARRAY(args, 1); - ADD_C(args, STRING_OBJ(cstr_as_string(eap->arg))); + ADD_C(args, CSTR_AS_OBJ(eap->arg)); NLUA_EXEC_STATIC("return vim.health._check(...)", args, &err); if (!ERROR_SET(&err)) { return; diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 9a8dc9899c..1d8c3c0cf4 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -54,7 +54,6 @@ #include "nvim/highlight_group.h" #include "nvim/indent.h" #include "nvim/input.h" -#include "nvim/lua/executor.h" #include "nvim/macros.h" #include "nvim/main.h" #include "nvim/mark.h" @@ -4804,29 +4803,3 @@ void ex_oldfiles(exarg_T *eap) } } } - -void ex_trust(exarg_T *eap) -{ - const char *const p = skiptowhite(eap->arg); - char *arg1 = xmemdupz(eap->arg, (size_t)(p - eap->arg)); - const char *action = "allow"; - const char *path = skipwhite(p); - - if (strcmp(arg1, "++deny") == 0) { - action = "deny"; - } else if (strcmp(arg1, "++remove") == 0) { - action = "remove"; - } else if (*arg1 != '\0') { - semsg(e_invarg2, arg1); - goto theend; - } - - if (path[0] == '\0') { - path = NULL; - } - - nlua_trust(action, path); - -theend: - xfree(arg1); -} diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index a4c1863576..dd23f6ece9 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -3387,7 +3387,7 @@ static void ui_ext_cmdline_show(CmdlineInfo *line) } else { Array item = arena_array(&arena, 2); ADD_C(item, INTEGER_OBJ(0)); - ADD_C(item, STRING_OBJ(cstr_as_string(line->cmdbuff))); + ADD_C(item, CSTR_AS_OBJ(line->cmdbuff)); content = arena_array(&arena, 1); ADD_C(content, ARRAY_OBJ(item)); } @@ -3414,7 +3414,7 @@ void ui_ext_cmdline_block_append(size_t indent, const char *line) Array item = ARRAY_DICT_INIT; ADD(item, INTEGER_OBJ(0)); - ADD(item, STRING_OBJ(cstr_as_string(buf))); + ADD(item, CSTR_AS_OBJ(buf)); Array content = ARRAY_DICT_INIT; ADD(content, ARRAY_OBJ(item)); ADD(cmdline_block, ARRAY_OBJ(content)); diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c index acdc36f9c7..f4a6a02682 100644 --- a/src/nvim/extmark.c +++ b/src/nvim/extmark.c @@ -148,6 +148,9 @@ revised: } if (decor) { + if (kv_size(decor->virt_text) && decor->virt_text_pos == kVTInline) { + buf->b_virt_text_inline++; + } if (kv_size(decor->virt_lines)) { buf->b_virt_line_blocks++; } diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 57aa063504..e60007bf88 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -3481,8 +3481,7 @@ void vim_deltempdir(void) char *vim_gettempdir(void) { static int notfound = 0; - bool exists = false; - if (vim_tempdir == NULL || !(exists = os_isdir(vim_tempdir))) { + if (vim_tempdir == NULL || !os_isdir(vim_tempdir)) { if (vim_tempdir != NULL) { notfound++; if (notfound == 1) { diff --git a/src/nvim/generators/gen_ex_cmds.lua b/src/nvim/generators/gen_ex_cmds.lua index 76b372eac2..b9fae7d0fe 100644 --- a/src/nvim/generators/gen_ex_cmds.lua +++ b/src/nvim/generators/gen_ex_cmds.lua @@ -55,6 +55,7 @@ defsfile:write(string.format([[ #include "nvim/help.h" #include "nvim/indent.h" #include "nvim/lua/executor.h" +#include "nvim/lua/secure.h" #include "nvim/mapping.h" #include "nvim/mark.h" #include "nvim/match.h" diff --git a/src/nvim/grid.c b/src/nvim/grid.c index 037606c38f..76dd2a073a 100644 --- a/src/nvim/grid.c +++ b/src/nvim/grid.c @@ -316,8 +316,7 @@ int grid_puts_len(ScreenGrid *grid, const char *text, int textlen, int row, int // When at the start of the text and overwriting the right half of a // two-cell character in the same grid, truncate that into a '>'. if (ptr == text && col > 0 && grid->chars[off][0] == 0) { - grid->chars[off - 1][0] = '>'; - grid->chars[off - 1][1] = 0; + schar_from_ascii(grid->chars[off - 1], '>'); } schar_copy(grid->chars[off], buf); diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index a53da95fba..fad113adc5 100644 --- a/src/nvim/highlight.c +++ b/src/nvim/highlight.c @@ -207,7 +207,7 @@ int ns_get_hl(NS *ns_hl, int hl_id, bool link, bool nodefault) if (!valid_item && p->hl_def != LUA_NOREF && !recursive) { MAXSIZE_TEMP_ARRAY(args, 3); ADD_C(args, INTEGER_OBJ((Integer)ns_id)); - ADD_C(args, STRING_OBJ(cstr_to_string(syn_id2name(hl_id)))); + ADD_C(args, CSTR_TO_OBJ(syn_id2name(hl_id))); ADD_C(args, BOOLEAN_OBJ(link)); // TODO(bfredl): preload the "global" attr dict? @@ -1141,21 +1141,21 @@ static void hl_inspect_impl(Array *arr, int attr) HlEntry e = kv_A(attr_entries, attr); switch (e.kind) { case kHlSyntax: - PUT(item, "kind", STRING_OBJ(cstr_to_string("syntax"))); + PUT(item, "kind", CSTR_TO_OBJ("syntax")); PUT(item, "hi_name", - STRING_OBJ(cstr_to_string(syn_id2name(e.id1)))); + CSTR_TO_OBJ(syn_id2name(e.id1))); break; case kHlUI: - PUT(item, "kind", STRING_OBJ(cstr_to_string("ui"))); + PUT(item, "kind", CSTR_TO_OBJ("ui")); const char *ui_name = (e.id1 == -1) ? "Normal" : hlf_names[e.id1]; - PUT(item, "ui_name", STRING_OBJ(cstr_to_string(ui_name))); + PUT(item, "ui_name", CSTR_TO_OBJ(ui_name)); PUT(item, "hi_name", - STRING_OBJ(cstr_to_string(syn_id2name(e.id2)))); + CSTR_TO_OBJ(syn_id2name(e.id2))); break; case kHlTerminal: - PUT(item, "kind", STRING_OBJ(cstr_to_string("term"))); + PUT(item, "kind", CSTR_TO_OBJ("term")); break; case kHlCombine: diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c index 41e7bdb7ac..09e7d04e02 100644 --- a/src/nvim/highlight_group.c +++ b/src/nvim/highlight_group.c @@ -697,10 +697,18 @@ int load_colors(char *name) char *buf = xmalloc(buflen); apply_autocmds(EVENT_COLORSCHEMEPRE, name, curbuf->b_fname, false, curbuf); snprintf(buf, buflen, "colors/%s.vim", name); - int retval = source_runtime(buf, DIP_START + DIP_OPT); + int retval = source_runtime(buf, 0); if (retval == FAIL) { snprintf(buf, buflen, "colors/%s.lua", name); - retval = source_runtime(buf, DIP_START + DIP_OPT); + retval = source_runtime(buf, 0); + } + if (retval == FAIL) { + snprintf(buf, buflen, "colors/%s.vim", name); + retval = source_runtime(buf, DIP_NORTP + DIP_START + DIP_OPT); + } + if (retval == FAIL) { + snprintf(buf, buflen, "colors/%s.lua", name); + retval = source_runtime(buf, DIP_NORTP + DIP_START + DIP_OPT); } xfree(buf); if (retval == OK) { @@ -1548,8 +1556,11 @@ static bool hlgroup2dict(Dictionary *hl, NS ns_id, int hl_id, Arena *arena) HlAttrs attr = syn_attr2entry(ns_id == 0 ? sgp->sg_attr : ns_get_hl(&ns_id, hl_id, false, sgp->sg_set)); *hl = arena_dict(arena, HLATTRS_DICT_SIZE + 1); + if (attr.rgb_ae_attr & HL_DEFAULT) { + PUT_C(*hl, "default", BOOLEAN_OBJ(true)); + } if (link > 0) { - PUT_C(*hl, "link", STRING_OBJ(cstr_as_string(hl_table[link - 1].sg_name))); + PUT_C(*hl, "link", CSTR_AS_OBJ(hl_table[link - 1].sg_name)); } Dictionary hl_cterm = arena_dict(arena, HLATTRS_DICT_SIZE); hlattrs2dict(hl, NULL, attr, true, true); diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 1d11379956..8c1d8addcd 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -109,11 +109,16 @@ typedef enum luv_err_type { kThreadCallback, } luv_err_t; +lua_State *get_global_lstate(void) +{ + return global_lstate; +} + /// Convert lua error into a Vim error message /// /// @param lstate Lua interpreter state. /// @param[in] msg Message base, must contain one `%s`. -static void nlua_error(lua_State *const lstate, const char *const msg) +void nlua_error(lua_State *const lstate, const char *const msg) FUNC_ATTR_NONNULL_ALL { size_t len; @@ -150,7 +155,7 @@ static void nlua_error(lua_State *const lstate, const char *const msg) /// @param lstate Lua interpreter state /// @param[in] nargs Number of arguments expected by the function being called. /// @param[in] nresults Number of results the function returns. -static int nlua_pcall(lua_State *lstate, int nargs, int nresults) +int nlua_pcall(lua_State *lstate, int nargs, int nresults) { lua_getglobal(lstate, "debug"); lua_getfield(lstate, -1, "traceback"); @@ -336,7 +341,7 @@ static int nlua_init_argv(lua_State *const L, char **argv, int argc, int lua_arg lua_pushstring(L, argv[lua_arg0 - 1]); lua_rawseti(L, -2, 0); // _G.arg[0] = "foo.lua" - for (; lua_arg0 >= 0 && i + lua_arg0 < argc; i++) { + for (; i + lua_arg0 < argc; i++) { lua_pushstring(L, argv[i + lua_arg0]); lua_rawseti(L, -2, i + 1); // _G.arg[i+1] = "--foo" } @@ -836,7 +841,7 @@ void nlua_run_script(char **argv, int argc, int lua_arg0) exit(lua_ok ? 0 : 1); } -lua_State *nlua_init_state(bool thread) +static lua_State *nlua_init_state(bool thread) { // If it is called from the main thread, it will attempt to rebuild the cache. const uv_thread_t self = uv_thread_self(); @@ -916,6 +921,7 @@ static void nlua_common_free_all_mem(lua_State *lstate) lua_close(lstate); } + static void nlua_print_event(void **argv) { char *str = argv[0]; @@ -2275,80 +2281,3 @@ plain: kv_printf(str, "<Lua %d>", ref); return str.items; } - -char *nlua_read_secure(const char *path) -{ - lua_State *const lstate = global_lstate; - const int top = lua_gettop(lstate); - - lua_getglobal(lstate, "vim"); - lua_getfield(lstate, -1, "secure"); - lua_getfield(lstate, -1, "read"); - lua_pushstring(lstate, path); - if (nlua_pcall(lstate, 1, 1)) { - nlua_error(lstate, _("Error executing vim.secure.read: %.*s")); - lua_settop(lstate, top); - return NULL; - } - - size_t len = 0; - const char *contents = lua_tolstring(lstate, -1, &len); - char *buf = NULL; - if (contents != NULL) { - // Add one to include trailing null byte - buf = xcalloc(len + 1, sizeof(char)); - memcpy(buf, contents, len + 1); - } - - lua_settop(lstate, top); - return buf; -} - -bool nlua_trust(const char *action, const char *path) -{ - lua_State *const lstate = global_lstate; - const int top = lua_gettop(lstate); - - lua_getglobal(lstate, "vim"); - lua_getfield(lstate, -1, "secure"); - lua_getfield(lstate, -1, "trust"); - - lua_newtable(lstate); - lua_pushstring(lstate, "action"); - lua_pushstring(lstate, action); - lua_settable(lstate, -3); - if (path == NULL) { - lua_pushstring(lstate, "bufnr"); - lua_pushnumber(lstate, 0); - lua_settable(lstate, -3); - } else { - lua_pushstring(lstate, "path"); - lua_pushstring(lstate, path); - lua_settable(lstate, -3); - } - - if (nlua_pcall(lstate, 1, 2)) { - nlua_error(lstate, _("Error executing vim.secure.trust: %.*s")); - lua_settop(lstate, top); - return false; - } - - bool success = lua_toboolean(lstate, -2); - const char *msg = lua_tostring(lstate, -1); - if (msg != NULL) { - if (success) { - if (strcmp(action, "allow") == 0) { - smsg("Allowed \"%s\" in trust database.", msg); - } else if (strcmp(action, "deny") == 0) { - smsg("Denied \"%s\" in trust database.", msg); - } else if (strcmp(action, "remove") == 0) { - smsg("Removed \"%s\" from trust database.", msg); - } - } else { - semsg(e_trustfile, msg); - } - } - - lua_settop(lstate, top); - return success; -} diff --git a/src/nvim/lua/secure.c b/src/nvim/lua/secure.c new file mode 100644 index 0000000000..30d5a95fc0 --- /dev/null +++ b/src/nvim/lua/secure.c @@ -0,0 +1,118 @@ +// 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 <lauxlib.h> +#include <lua.h> +#include <lualib.h> + +#include "nvim/charset.h" +#include "nvim/lua/executor.h" +#include "nvim/lua/secure.h" +#include "nvim/message.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "lua/secure.c.generated.h" +#endif + +char *nlua_read_secure(const char *path) +{ + lua_State *const lstate = get_global_lstate(); + const int top = lua_gettop(lstate); + + lua_getglobal(lstate, "vim"); + lua_getfield(lstate, -1, "secure"); + lua_getfield(lstate, -1, "read"); + lua_pushstring(lstate, path); + if (nlua_pcall(lstate, 1, 1)) { + nlua_error(lstate, _("Error executing vim.secure.read: %.*s")); + lua_settop(lstate, top); + return NULL; + } + + size_t len = 0; + const char *contents = lua_tolstring(lstate, -1, &len); + char *buf = NULL; + if (contents != NULL) { + // Add one to include trailing null byte + buf = xcalloc(len + 1, sizeof(char)); + memcpy(buf, contents, len + 1); + } + + lua_settop(lstate, top); + return buf; +} + +static bool nlua_trust(const char *action, const char *path) +{ + lua_State *const lstate = get_global_lstate(); + const int top = lua_gettop(lstate); + + lua_getglobal(lstate, "vim"); + lua_getfield(lstate, -1, "secure"); + lua_getfield(lstate, -1, "trust"); + + lua_newtable(lstate); + lua_pushstring(lstate, "action"); + lua_pushstring(lstate, action); + lua_settable(lstate, -3); + if (path == NULL) { + lua_pushstring(lstate, "bufnr"); + lua_pushnumber(lstate, 0); + lua_settable(lstate, -3); + } else { + lua_pushstring(lstate, "path"); + lua_pushstring(lstate, path); + lua_settable(lstate, -3); + } + + if (nlua_pcall(lstate, 1, 2)) { + nlua_error(lstate, _("Error executing vim.secure.trust: %.*s")); + lua_settop(lstate, top); + return false; + } + + bool success = lua_toboolean(lstate, -2); + const char *msg = lua_tostring(lstate, -1); + if (msg != NULL) { + if (success) { + if (strcmp(action, "allow") == 0) { + smsg("Allowed \"%s\" in trust database.", msg); + } else if (strcmp(action, "deny") == 0) { + smsg("Denied \"%s\" in trust database.", msg); + } else if (strcmp(action, "remove") == 0) { + smsg("Removed \"%s\" from trust database.", msg); + } + } else { + semsg(e_trustfile, msg); + } + } + + lua_settop(lstate, top); + return success; +} + +void ex_trust(exarg_T *eap) +{ + const char *const p = skiptowhite(eap->arg); + char *arg1 = xmemdupz(eap->arg, (size_t)(p - eap->arg)); + const char *action = "allow"; + const char *path = skipwhite(p); + + if (strcmp(arg1, "++deny") == 0) { + action = "deny"; + } else if (strcmp(arg1, "++remove") == 0) { + action = "remove"; + } else if (*arg1 != '\0') { + semsg(e_invarg2, arg1); + goto theend; + } + + if (path[0] == '\0') { + path = NULL; + } + + nlua_trust(action, path); + +theend: + xfree(arg1); +} diff --git a/src/nvim/lua/secure.h b/src/nvim/lua/secure.h new file mode 100644 index 0000000000..87c468538e --- /dev/null +++ b/src/nvim/lua/secure.h @@ -0,0 +1,12 @@ +#ifndef NVIM_LUA_SECURE_H +#define NVIM_LUA_SECURE_H + +#include <lua.h> + +#include "nvim/ex_cmds_defs.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "lua/secure.h.generated.h" +#endif + +#endif // NVIM_LUA_SECURE_H diff --git a/src/nvim/main.c b/src/nvim/main.c index 4999d9dd2a..6015bbd06e 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -49,6 +49,7 @@ #include "nvim/keycodes.h" #include "nvim/log.h" #include "nvim/lua/executor.h" +#include "nvim/lua/secure.h" #include "nvim/macros.h" #include "nvim/main.h" #include "nvim/mark.h" diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c index c5449bd66e..e40e67cead 100644 --- a/src/nvim/mapping.c +++ b/src/nvim/mapping.c @@ -2096,13 +2096,13 @@ static Dictionary mapblock_fill_dict(const mapblock_T *const mp, const char *lhs : cstr_as_string(str2special_save(mp->m_str, false, true)))); } if (mp->m_desc != NULL) { - PUT(dict, "desc", STRING_OBJ(cstr_to_string(mp->m_desc))); + PUT(dict, "desc", CSTR_TO_OBJ(mp->m_desc)); } - PUT(dict, "lhs", STRING_OBJ(cstr_as_string(lhs))); - PUT(dict, "lhsraw", STRING_OBJ(cstr_to_string(mp->m_keys))); + PUT(dict, "lhs", CSTR_AS_OBJ(lhs)); + PUT(dict, "lhsraw", CSTR_TO_OBJ(mp->m_keys)); if (lhsrawalt != NULL) { // Also add the value for the simplified entry. - PUT(dict, "lhsrawalt", STRING_OBJ(cstr_to_string(lhsrawalt))); + PUT(dict, "lhsrawalt", CSTR_TO_OBJ(lhsrawalt)); } PUT(dict, "noremap", INTEGER_OBJ(noremap_value)); PUT(dict, "script", INTEGER_OBJ(mp->m_noremap == REMAP_SCRIPT ? 1 : 0)); @@ -2115,7 +2115,7 @@ static Dictionary mapblock_fill_dict(const mapblock_T *const mp, const char *lhs if (mp->m_replace_keycodes) { PUT(dict, "replace_keycodes", INTEGER_OBJ(1)); } - PUT(dict, "mode", STRING_OBJ(cstr_as_string(mapmode))); + PUT(dict, "mode", CSTR_AS_OBJ(mapmode)); return dict; } diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 7d61b918d2..66c26275f1 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -2024,6 +2024,24 @@ int mb_charlen(const char *str) return count; } +int mb_charlen2bytelen(const char *str, int charlen) +{ + const char *p = str; + int count = 0; + + if (p == NULL) { + return 0; + } + + for (int i = 0; *p != NUL && i < charlen; i++) { + int b = utfc_ptr2len(p); + p += b; + count += b; + } + + return count; +} + /// Like mb_charlen() but for a string with specified length. int mb_charlen_len(const char *str, int len) { diff --git a/src/nvim/message.c b/src/nvim/message.c index aa97ea15e5..1cb4a3cbf4 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1069,7 +1069,7 @@ void ex_messages(void *const eap_p) for (; p != NULL; p = p->next) { if (kv_size(p->multiattr) || (p->msg && p->msg[0])) { Array entry = ARRAY_DICT_INIT; - ADD(entry, STRING_OBJ(cstr_to_string(p->kind))); + ADD(entry, CSTR_TO_OBJ(p->kind)); Array content = ARRAY_DICT_INIT; if (kv_size(p->multiattr)) { for (uint32_t i = 0; i < kv_size(p->multiattr); i++) { @@ -1082,7 +1082,7 @@ void ex_messages(void *const eap_p) } else if (p->msg && p->msg[0]) { Array content_entry = ARRAY_DICT_INIT; ADD(content_entry, INTEGER_OBJ(p->attr)); - ADD(content_entry, STRING_OBJ(cstr_to_string(p->msg))); + ADD(content_entry, CSTR_TO_OBJ(p->msg)); ADD(content, ARRAY_OBJ(content_entry)); } ADD(entry, ARRAY_OBJ(content)); diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index cd5daee915..1772b0497b 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -628,7 +628,7 @@ static void chan_close_with_error(Channel *channel, char *msg, int loglevel) ChannelCallFrame *frame = kv_A(channel->rpc.call_stack, i); frame->returned = true; frame->errored = true; - frame->result = STRING_OBJ(cstr_to_string(msg)); + frame->result = CSTR_TO_OBJ(msg); } channel_close(channel->id, kChannelPartRpc, NULL); @@ -665,7 +665,7 @@ static WBuffer *serialize_response(uint64_t channel_id, MsgpackRpcRequestHandler } else { Array args = ARRAY_DICT_INIT; ADD(args, INTEGER_OBJ(err->type)); - ADD(args, STRING_OBJ(cstr_to_string(err->msg))); + ADD(args, CSTR_TO_OBJ(err->msg)); msgpack_rpc_serialize_request(0, cstr_as_string("nvim_error_event"), args, &pac); api_free_array(args); diff --git a/src/nvim/normal.c b/src/nvim/normal.c index c3fdb304a3..60fff45323 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -2031,7 +2031,7 @@ static void display_showcmd(void) if (len > 0) { // placeholder for future highlight support ADD_C(chunk, INTEGER_OBJ(0)); - ADD_C(chunk, STRING_OBJ(cstr_as_string(showcmd_buf))); + ADD_C(chunk, CSTR_AS_OBJ(showcmd_buf)); ADD_C(content, ARRAY_OBJ(chunk)); } ui_call_msg_showcmd(content); diff --git a/src/nvim/option.c b/src/nvim/option.c index 327c5fc089..722afa64d8 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -1252,7 +1252,7 @@ static void do_set_option_string(int opt_idx, int opt_flags, char **argp, int ne } if (options[opt_idx].flags & P_UI_OPTION) { ui_call_option_set(cstr_as_string(options[opt_idx].fullname), - STRING_OBJ(cstr_as_string(saved_newval))); + CSTR_AS_OBJ(saved_newval)); } } xfree(saved_origval); @@ -3803,7 +3803,7 @@ void ui_refresh_options(void) value = INTEGER_OBJ(*(long *)varp); } else if (flags & P_STRING) { // cstr_as_string handles NULL string - value = STRING_OBJ(cstr_as_string(*(char **)varp)); + value = CSTR_AS_OBJ(*(char **)varp); } ui_call_option_set(name, value); } diff --git a/src/nvim/os/process.c b/src/nvim/os/process.c index 2a248a1e32..98ae251e2b 100644 --- a/src/nvim/os/process.c +++ b/src/nvim/os/process.c @@ -254,7 +254,7 @@ Dictionary os_proc_info(int pid) if (pe.th32ProcessID == (DWORD)pid) { PUT(pinfo, "pid", INTEGER_OBJ(pid)); PUT(pinfo, "ppid", INTEGER_OBJ((int)pe.th32ParentProcessID)); - PUT(pinfo, "name", STRING_OBJ(cstr_to_string(pe.szExeFile))); + PUT(pinfo, "name", CSTR_TO_OBJ(pe.szExeFile)); } return pinfo; diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 3e69e547cb..25c745ae97 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -102,12 +102,16 @@ int plines_win_nofold(win_T *wp, linenr_T lnum) char *s; unsigned col; int width; + chartabsize_T cts; s = ml_get_buf(wp->w_buffer, lnum, false); - if (*s == NUL) { // empty line - return 1; + init_chartabsize_arg(&cts, wp, lnum, 0, s, s); + if (*s == NUL && !cts.cts_has_virt_text) { + return 1; // be quick for an empty line } - col = win_linetabsize(wp, lnum, s, MAXCOL); + win_linetabsize_cts(&cts, (colnr_T)MAXCOL); + clear_chartabsize_arg(&cts); + col = (unsigned)cts.cts_vcol; // If list mode is on, then the '$' at the end of the line may take up one // extra column. @@ -262,6 +266,11 @@ int linetabsize_col(int startcol, char *s) while (*cts.cts_ptr != NUL) { cts.cts_vcol += lbr_chartabsize_adv(&cts); } + if (cts.cts_has_virt_text && cts.cts_ptr == cts.cts_line) { + // check for virtual text in an empty line + (void)lbr_chartabsize_adv(&cts); + cts.cts_vcol += cts.cts_cur_text_width_left + cts.cts_cur_text_width_right; + } clear_chartabsize_arg(&cts); return cts.cts_vcol; } @@ -277,10 +286,7 @@ unsigned win_linetabsize(win_T *wp, linenr_T lnum, char *line, colnr_T len) { chartabsize_T cts; init_chartabsize_arg(&cts, wp, lnum, 0, line, line); - for (; *cts.cts_ptr != NUL && (len == MAXCOL || cts.cts_ptr < line + len); - MB_PTR_ADV(cts.cts_ptr)) { - cts.cts_vcol += win_lbr_chartabsize(&cts, NULL); - } + win_linetabsize_cts(&cts, len); clear_chartabsize_arg(&cts); return (unsigned)cts.cts_vcol; } @@ -292,20 +298,43 @@ unsigned linetabsize(win_T *wp, linenr_T lnum) return win_linetabsize(wp, lnum, ml_get_buf(wp->w_buffer, lnum, false), (colnr_T)MAXCOL); } +void win_linetabsize_cts(chartabsize_T *cts, colnr_T len) +{ + for (; *cts->cts_ptr != NUL && (len == MAXCOL || cts->cts_ptr < cts->cts_line + len); + MB_PTR_ADV(cts->cts_ptr)) { + cts->cts_vcol += win_lbr_chartabsize(cts, NULL); + } + // check for a virtual text on an empty line + if (cts->cts_has_virt_text && *cts->cts_ptr == NUL + && cts->cts_ptr == cts->cts_line) { + (void)win_lbr_chartabsize(cts, NULL); + cts->cts_vcol += cts->cts_cur_text_width_left + cts->cts_cur_text_width_right; + } +} + /// Prepare the structure passed to chartabsize functions. /// /// "line" is the start of the line, "ptr" is the first relevant character. -/// When "lnum" is zero do not use text properties that insert text. -void init_chartabsize_arg(chartabsize_T *cts, win_T *wp, linenr_T lnum FUNC_ATTR_UNUSED, - colnr_T col, char *line, char *ptr) +/// When "lnum" is zero do not use inline virtual text. +void init_chartabsize_arg(chartabsize_T *cts, win_T *wp, linenr_T lnum, colnr_T col, char *line, + char *ptr) { cts->cts_win = wp; cts->cts_vcol = col; cts->cts_line = line; cts->cts_ptr = ptr; - cts->cts_cur_text_width = 0; - // TODO(bfredl): actually lookup inline virtual text here + cts->cts_cur_text_width_left = 0; + cts->cts_cur_text_width_right = 0; cts->cts_has_virt_text = false; + cts->cts_row = lnum - 1; + + if (cts->cts_row >= 0) { + marktree_itr_get(wp->w_buffer->b_marktree, cts->cts_row, 0, cts->cts_iter); + mtkey_t mark = marktree_itr_current(cts->cts_iter); + if (mark.pos.row == cts->cts_row) { + cts->cts_has_virt_text = true; + } + } } /// Free any allocated item in "cts". @@ -369,7 +398,8 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) int mb_added = 0; int numberextra; - cts->cts_cur_text_width = 0; + cts->cts_cur_text_width_left = 0; + cts->cts_cur_text_width_right = 0; // No 'linebreak', 'showbreak' and 'breakindent': return quickly. if (!wp->w_p_lbr && !wp->w_p_bri && *get_showbreak_value(wp) == NUL @@ -383,7 +413,34 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) // First get normal size, without 'linebreak' or virtual text int size = win_chartabsize(wp, s, vcol); if (cts->cts_has_virt_text) { - // TODO(bfredl): inline virtual text + int tab_size = size; + int charlen = *s == NUL ? 1 : utf_ptr2len(s); + int col = (int)(s - line); + while (true) { + mtkey_t mark = marktree_itr_current(cts->cts_iter); + if (mark.pos.row != cts->cts_row || mark.pos.col > col) { + break; + } else if (mark.pos.col >= col && mark.pos.col < col + charlen) { + if (!mt_end(mark)) { + Decoration decor = get_decor(mark); + if (decor.virt_text_pos == kVTInline) { + if (mt_right(mark)) { + cts->cts_cur_text_width_right += decor.virt_text_width; + } else { + cts->cts_cur_text_width_left += decor.virt_text_width; + } + size += decor.virt_text_width; + if (*s == TAB) { + // tab size changes because of the inserted text + size -= tab_size; + tab_size = win_chartabsize(wp, s, vcol + size); + size += tab_size; + } + } + } + } + marktree_itr_next(wp->w_buffer->b_marktree, cts->cts_iter); + } } int c = (uint8_t)(*s); diff --git a/src/nvim/plines.h b/src/nvim/plines.h index 808f6d284e..2ce7133705 100644 --- a/src/nvim/plines.h +++ b/src/nvim/plines.h @@ -11,10 +11,12 @@ typedef struct { win_T *cts_win; char *cts_line; // start of the line char *cts_ptr; // current position in line + int cts_row; bool cts_has_virt_text; // true if if a property inserts text - int cts_cur_text_width; // width of current inserted text - // TODO(bfredl): iterator in to the marktree for scanning virt text + int cts_cur_text_width_left; // width of virtual text left of cursor + int cts_cur_text_width_right; // width of virtual text right of cursor + MarkTreeIter cts_iter[1]; int cts_vcol; // virtual column at current position } chartabsize_T; diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c index 6d5d063a75..15d0372c6f 100644 --- a/src/nvim/popupmenu.c +++ b/src/nvim/popupmenu.c @@ -164,10 +164,10 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i Array arr = arena_array(&arena, (size_t)size); for (int i = 0; i < size; i++) { Array item = arena_array(&arena, 4); - ADD_C(item, STRING_OBJ(cstr_as_string(array[i].pum_text))); - ADD_C(item, STRING_OBJ(cstr_as_string(array[i].pum_kind))); - ADD_C(item, STRING_OBJ(cstr_as_string(array[i].pum_extra))); - ADD_C(item, STRING_OBJ(cstr_as_string(array[i].pum_info))); + ADD_C(item, CSTR_AS_OBJ(array[i].pum_text)); + ADD_C(item, CSTR_AS_OBJ(array[i].pum_kind)); + ADD_C(item, CSTR_AS_OBJ(array[i].pum_extra)); + ADD_C(item, CSTR_AS_OBJ(array[i].pum_info)); ADD_C(arr, ARRAY_OBJ(item)); } ui_call_popupmenu_show(arr, selected, pum_win_row, cursor_col, diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c index a9068fabc8..4b27067fb8 100644 --- a/src/nvim/runtime.c +++ b/src/nvim/runtime.c @@ -512,7 +512,7 @@ Array runtime_inspect(void) for (size_t i = 0; i < kv_size(path); i++) { SearchPathItem *item = &kv_A(path, i); Array entry = ARRAY_DICT_INIT; - ADD(entry, STRING_OBJ(cstr_to_string(item->path))); + ADD(entry, CSTR_TO_OBJ(item->path)); ADD(entry, BOOLEAN_OBJ(item->after)); if (item->has_lua != kNone) { ADD(entry, BOOLEAN_OBJ(item->has_lua == kTrue)); @@ -568,7 +568,7 @@ ArrayOf(String) runtime_get_named_common(bool lua, Array pat, bool all, item->path, pat_item.data.string.data); if (size < buf_len) { if (os_file_is_readable(buf)) { - ADD(rv, STRING_OBJ(cstr_to_string(buf))); + ADD(rv, CSTR_TO_OBJ(buf)); if (!all) { goto done; } diff --git a/src/nvim/shada.c b/src/nvim/shada.c index d3a0b12e5e..db911f4bfd 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -2529,15 +2529,15 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, ShaDaReadDef .capacity = 5, .items = ((KeyValuePair[]) { { STATIC_CSTR_AS_STRING("generator"), - STRING_OBJ(STATIC_CSTR_AS_STRING("nvim")) }, + STATIC_CSTR_AS_OBJ("nvim") }, { STATIC_CSTR_AS_STRING("version"), - STRING_OBJ(cstr_as_string(longVersion)) }, + CSTR_AS_OBJ(longVersion) }, { STATIC_CSTR_AS_STRING("max_kbyte"), INTEGER_OBJ((Integer)max_kbyte) }, { STATIC_CSTR_AS_STRING("pid"), INTEGER_OBJ((Integer)os_get_pid()) }, { STATIC_CSTR_AS_STRING("encoding"), - STRING_OBJ(cstr_as_string(p_enc)) }, + CSTR_AS_OBJ(p_enc) }, }), } } diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index 5a14d57538..015c578396 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -596,7 +596,7 @@ void win_redr_ruler(win_T *wp) MAXSIZE_TEMP_ARRAY(content, 1); MAXSIZE_TEMP_ARRAY(chunk, 2); ADD_C(chunk, INTEGER_OBJ(attr)); - ADD_C(chunk, STRING_OBJ(cstr_as_string(buffer))); + ADD_C(chunk, CSTR_AS_OBJ(buffer)); ADD_C(content, ARRAY_OBJ(chunk)); ui_call_msg_ruler(content); did_show_ext_ruler = true; diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 81a68d5b07..30a1af68ad 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -547,8 +547,8 @@ static void set_bg(char *bgvalue) { if (ui_client_attached) { MAXSIZE_TEMP_ARRAY(args, 2); - ADD_C(args, STRING_OBJ(cstr_as_string("term_background"))); - ADD_C(args, STRING_OBJ(cstr_as_string(bgvalue))); + ADD_C(args, CSTR_AS_OBJ("term_background")); + ADD_C(args, CSTR_AS_OBJ(bgvalue)); rpc_send_event(ui_client_channel_id, "nvim_ui_set_option", args); } } diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 6a9769e351..c9d9b08b79 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -1302,16 +1302,16 @@ static void show_verbose_terminfo(TUIData *tui) Array chunks = ARRAY_DICT_INIT; Array title = ARRAY_DICT_INIT; - ADD(title, STRING_OBJ(cstr_to_string("\n\n--- Terminal info --- {{{\n"))); - ADD(title, STRING_OBJ(cstr_to_string("Title"))); + ADD(title, CSTR_TO_OBJ("\n\n--- Terminal info --- {{{\n")); + ADD(title, CSTR_TO_OBJ("Title")); ADD(chunks, ARRAY_OBJ(title)); Array info = ARRAY_DICT_INIT; String str = terminfo_info_msg(ut, tui->term); ADD(info, STRING_OBJ(str)); ADD(chunks, ARRAY_OBJ(info)); Array end_fold = ARRAY_DICT_INIT; - ADD(end_fold, STRING_OBJ(cstr_to_string("}}}\n"))); - ADD(end_fold, STRING_OBJ(cstr_to_string("Title"))); + ADD(end_fold, CSTR_TO_OBJ("}}}\n")); + ADD(end_fold, CSTR_TO_OBJ("Title")); ADD(chunks, ARRAY_OBJ(end_fold)); Array args = ARRAY_DICT_INIT; @@ -1425,7 +1425,7 @@ void tui_option_set(TUIData *tui, String name, Object value) if (ui_client_channel_id) { MAXSIZE_TEMP_ARRAY(args, 2); - ADD_C(args, STRING_OBJ(cstr_as_string("rgb"))); + ADD_C(args, CSTR_AS_OBJ("rgb")); ADD_C(args, BOOLEAN_OBJ(value.data.boolean)); rpc_send_event(ui_client_channel_id, "nvim_ui_set_option", args); } diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 24f20af2f3..8c31032492 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -613,8 +613,8 @@ Array ui_array(void) PUT(info, "override", BOOLEAN_OBJ(ui->override)); // TUI fields. (`stdin_fd` is intentionally omitted.) - PUT(info, "term_name", STRING_OBJ(cstr_to_string(ui->term_name))); - PUT(info, "term_background", STRING_OBJ(cstr_to_string(ui->term_background))); + PUT(info, "term_name", CSTR_TO_OBJ(ui->term_name)); + PUT(info, "term_background", CSTR_TO_OBJ(ui->term_background)); PUT(info, "term_colors", INTEGER_OBJ(ui->term_colors)); PUT(info, "stdin_tty", BOOLEAN_OBJ(ui->stdin_tty)); PUT(info, "stdout_tty", BOOLEAN_OBJ(ui->stdout_tty)); diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c index b93b31f7dc..1918b0b800 100644 --- a/src/nvim/ui_client.c +++ b/src/nvim/ui_client.c @@ -83,11 +83,11 @@ void ui_client_attach(int width, int height, char *term) PUT_C(opts, "ext_linegrid", BOOLEAN_OBJ(true)); PUT_C(opts, "ext_termcolors", BOOLEAN_OBJ(true)); if (term) { - PUT_C(opts, "term_name", STRING_OBJ(cstr_as_string(term))); + PUT_C(opts, "term_name", CSTR_AS_OBJ(term)); } if (ui_client_bg_response != kNone) { bool is_dark = (ui_client_bg_response == kTrue); - PUT_C(opts, "term_background", STRING_OBJ(cstr_as_string(is_dark ? "dark" : "light"))); + PUT_C(opts, "term_background", CSTR_AS_OBJ(is_dark ? "dark" : "light")); } PUT_C(opts, "term_colors", INTEGER_OBJ(t_colors)); if (!ui_client_is_remote) { diff --git a/src/nvim/usercmd.c b/src/nvim/usercmd.c index 11f2620aaa..c56b241988 100644 --- a/src/nvim/usercmd.c +++ b/src/nvim/usercmd.c @@ -1754,8 +1754,8 @@ Dictionary commands_array(buf_T *buf) Dictionary d = ARRAY_DICT_INIT; ucmd_T *cmd = USER_CMD_GA(gap, i); - PUT(d, "name", STRING_OBJ(cstr_to_string(cmd->uc_name))); - PUT(d, "definition", STRING_OBJ(cstr_to_string(cmd->uc_rep))); + PUT(d, "name", CSTR_TO_OBJ(cmd->uc_name)); + PUT(d, "definition", CSTR_TO_OBJ(cmd->uc_rep)); PUT(d, "script_id", INTEGER_OBJ(cmd->uc_script_ctx.sc_sid)); PUT(d, "bang", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_BANG))); PUT(d, "bar", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_TRLBAR))); @@ -1775,21 +1775,21 @@ Dictionary commands_array(buf_T *buf) case (EX_EXTRA | EX_NOSPC | EX_NEEDARG): arg[0] = '1'; break; } - PUT(d, "nargs", STRING_OBJ(cstr_to_string(arg))); + PUT(d, "nargs", CSTR_TO_OBJ(arg)); char *cmd_compl = get_command_complete(cmd->uc_compl); PUT(d, "complete", (cmd_compl == NULL - ? NIL : STRING_OBJ(cstr_to_string(cmd_compl)))); + ? NIL : CSTR_TO_OBJ(cmd_compl))); PUT(d, "complete_arg", cmd->uc_compl_arg == NULL - ? NIL : STRING_OBJ(cstr_to_string(cmd->uc_compl_arg))); + ? NIL : CSTR_TO_OBJ(cmd->uc_compl_arg)); Object obj = NIL; if (cmd->uc_argt & EX_COUNT) { if (cmd->uc_def >= 0) { snprintf(str, sizeof(str), "%" PRId64, cmd->uc_def); - obj = STRING_OBJ(cstr_to_string(str)); // -count=N + obj = CSTR_TO_OBJ(str); // -count=N } else { - obj = STRING_OBJ(cstr_to_string("0")); // -count + obj = CSTR_TO_OBJ("0"); // -count } } PUT(d, "count", obj); @@ -1797,12 +1797,12 @@ Dictionary commands_array(buf_T *buf) obj = NIL; if (cmd->uc_argt & EX_RANGE) { if (cmd->uc_argt & EX_DFLALL) { - obj = STRING_OBJ(cstr_to_string("%")); // -range=% + obj = CSTR_TO_OBJ("%"); // -range=% } else if (cmd->uc_def >= 0) { snprintf(str, sizeof(str), "%" PRId64, cmd->uc_def); - obj = STRING_OBJ(cstr_to_string(str)); // -range=N + obj = CSTR_TO_OBJ(str); // -range=N } else { - obj = STRING_OBJ(cstr_to_string(".")); // -range + obj = CSTR_TO_OBJ("."); // -range } } PUT(d, "range", obj); @@ -1811,7 +1811,7 @@ Dictionary commands_array(buf_T *buf) for (int j = 0; addr_type_complete[j].expand != ADDR_NONE; j++) { if (addr_type_complete[j].expand != ADDR_LINES && addr_type_complete[j].expand == cmd->uc_addr_type) { - obj = STRING_OBJ(cstr_to_string(addr_type_complete[j].name)); + obj = CSTR_TO_OBJ(addr_type_complete[j].name); break; } } diff --git a/test/README.md b/test/README.md index 65a5ae7912..42d0ec0323 100644 --- a/test/README.md +++ b/test/README.md @@ -285,7 +285,7 @@ Number; !must be defined to function properly): - `VALGRIND_LOG` (F) (S): overrides valgrind log file name used for `VALGRIND`. -- `TEST_COLORS` (F) (U) (D): enable pretty colors in test runner. +- `TEST_COLORS` (F) (U) (D): enable pretty colors in test runner. Set to true by default. - `TEST_SKIP_FRAGILE` (F) (D): makes test suite skip some fragile tests. diff --git a/test/busted/outputHandlers/nvim.lua b/test/busted/outputHandlers/nvim.lua index e17536e0f8..4bfa21fe49 100644 --- a/test/busted/outputHandlers/nvim.lua +++ b/test/busted/outputHandlers/nvim.lua @@ -1,9 +1,15 @@ local pretty = require 'pl.pretty' local global_helpers = require('test.helpers') --- Colors are disabled by default. #15610 local colors = setmetatable({}, {__index = function() return function(s) return s == nil and '' or tostring(s) end end}) + +local enable_colors = true if os.getenv "TEST_COLORS" then + local test_colors = os.getenv("TEST_COLORS"):lower() + local disable_colors = test_colors == 'false' or test_colors == '0' or test_colors == 'no' or test_colors == 'off' + enable_colors = not disable_colors +end +if enable_colors then colors = require 'term.colors' end diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index df9092fa14..bf9e952319 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -630,19 +630,19 @@ describe('api/buf', function() eq('Index out of bounds', pcall_err(get_offset, 6)) eq('Index out of bounds', pcall_err(get_offset, -1)) - meths.set_option_value('eol', false, {buf=0}) - meths.set_option_value('fixeol', false, {buf=0}) + meths.set_option_value('eol', false, {}) + meths.set_option_value('fixeol', false, {}) eq(28, get_offset(5)) -- fileformat is ignored - meths.set_option_value('fileformat', 'dos', {buf=0}) + meths.set_option_value('fileformat', 'dos', {}) eq(0, get_offset(0)) eq(6, get_offset(1)) eq(15, get_offset(2)) eq(16, get_offset(3)) eq(24, get_offset(4)) eq(28, get_offset(5)) - meths.set_option_value('eol', true, {buf=0}) + meths.set_option_value('eol', true, {}) eq(29, get_offset(5)) command("set hidden") @@ -699,9 +699,9 @@ describe('api/buf', function() describe('nvim_get_option_value, nvim_set_option_value', function() it('works', function() - eq(8, nvim('get_option_value', 'shiftwidth', {buf = 0})) - nvim('set_option_value', 'shiftwidth', 4, {buf=0}) - eq(4, nvim('get_option_value', 'shiftwidth', {buf = 0})) + eq(8, nvim('get_option_value', 'shiftwidth', {})) + nvim('set_option_value', 'shiftwidth', 4, {}) + eq(4, nvim('get_option_value', 'shiftwidth', {})) -- global-local option nvim('set_option_value', 'define', 'test', {buf = 0}) eq('test', nvim('get_option_value', 'define', {buf = 0})) diff --git a/test/functional/api/extmark_spec.lua b/test/functional/api/extmark_spec.lua index 0960e910f1..675c8332de 100644 --- a/test/functional/api/extmark_spec.lua +++ b/test/functional/api/extmark_spec.lua @@ -1401,7 +1401,7 @@ describe('API/extmarks', function() it('in read-only buffer', function() command("view! runtime/doc/help.txt") - eq(true, meths.get_option_value('ro', {buf=0})) + eq(true, meths.get_option_value('ro', {})) local id = set_extmark(ns, 0, 0, 2) eq({{id, 0, 2}}, get_extmarks(ns,0, -1)) end) @@ -1474,7 +1474,7 @@ describe('API/extmarks', function() it('in prompt buffer', function() feed('dd') local id = set_extmark(ns, marks[1], 0, 0, {}) - meths.set_option_value('buftype', 'prompt', {buf = 0}) + meths.set_option_value('buftype', 'prompt', {}) feed('i<esc>') eq({{id, 0, 2}}, get_extmarks(ns, 0, -1)) end) diff --git a/test/functional/api/highlight_spec.lua b/test/functional/api/highlight_spec.lua index 1601184e1b..d3a79327ae 100644 --- a/test/functional/api/highlight_spec.lua +++ b/test/functional/api/highlight_spec.lua @@ -596,4 +596,9 @@ describe('API: get highlight', function() eq({}, data["@foobar.hubbabubba"]) eq(nil, data["@foobar"]) end) + + it('should return default flag', function() + meths.set_hl(0, 'Tried', { fg = "#00ff00", default = true }) + eq({ fg = tonumber('00ff00', 16), default = true }, meths.get_hl(0, { name = 'Tried' })) + end) end) diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 5b8b52a559..040c26e058 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -1397,7 +1397,7 @@ describe('API', function() it('works to set global value of local options', function() nvim('set_option_value', 'lisp', true, {scope='global'}) eq(true, nvim('get_option_value', 'lisp', {scope='global'})) - eq(false, nvim('get_option_value', 'lisp', {buf=0})) + eq(false, nvim('get_option_value', 'lisp', {})) eq(nil, nvim('command_output', 'setglobal lisp?'):match('nolisp')) eq('nolisp', nvim('command_output', 'setlocal lisp?'):match('nolisp')) nvim('set_option_value', 'shiftwidth', 20, {scope='global'}) @@ -1493,7 +1493,7 @@ describe('API', function() end) it('set window options', function() - nvim('set_option_value', 'colorcolumn', '4,3', {win=0}) + nvim('set_option_value', 'colorcolumn', '4,3', {}) eq('4,3', nvim('get_option_value', 'colorcolumn', {scope = 'local'})) command("set modified hidden") command("enew") -- edit new buffer, window option is preserved diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua index 660fa4731e..a6d1807961 100644 --- a/test/functional/api/window_spec.lua +++ b/test/functional/api/window_spec.lua @@ -365,11 +365,11 @@ describe('API/win', function() describe('nvim_get_option_value, nvim_set_option_value', function() it('works', function() - nvim('set_option_value', 'colorcolumn', '4,3', {win=0}) - eq('4,3', nvim('get_option_value', 'colorcolumn', {win = 0})) + nvim('set_option_value', 'colorcolumn', '4,3', {}) + eq('4,3', nvim('get_option_value', 'colorcolumn', {})) command("set modified hidden") command("enew") -- edit new buffer, window option is preserved - eq('4,3', nvim('get_option_value', 'colorcolumn', {win = 0})) + eq('4,3', nvim('get_option_value', 'colorcolumn', {})) -- global-local option nvim('set_option_value', 'statusline', 'window-status', {win=0}) diff --git a/test/functional/ex_cmds/append_spec.lua b/test/functional/ex_cmds/append_spec.lua index e3a39384a6..4134eed87e 100644 --- a/test/functional/ex_cmds/append_spec.lua +++ b/test/functional/ex_cmds/append_spec.lua @@ -43,7 +43,7 @@ local cmdtest = function(cmd, prep, ret1) eq(hisline, funcs.histget(':', -2)) eq(cmd, funcs.histget(':')) -- Test that command-line window was launched - eq('nofile', meths.get_option_value('buftype', {buf=0})) + eq('nofile', meths.get_option_value('buftype', {})) eq('n', funcs.mode(1)) feed('<CR>') eq('c', funcs.mode(1)) diff --git a/test/functional/ex_cmds/mksession_spec.lua b/test/functional/ex_cmds/mksession_spec.lua index f51e7c62cb..7522d4a99c 100644 --- a/test/functional/ex_cmds/mksession_spec.lua +++ b/test/functional/ex_cmds/mksession_spec.lua @@ -81,7 +81,7 @@ describe(':mksession', function() local buf_count = #meths.list_bufs() eq(2, buf_count) - eq('terminal', meths.get_option_value('buftype', { buf = 0 })) + eq('terminal', meths.get_option_value('buftype', {})) test_terminal_session_disabled(2) @@ -112,7 +112,7 @@ describe(':mksession', function() it('do not restore :terminal if not set in sessionoptions, only buffer #13078', function() command('terminal') - eq('terminal', meths.get_option_value('buftype', { buf = 0 })) + eq('terminal', meths.get_option_value('buftype', {})) local buf_count = #meths.list_bufs() eq(1, buf_count) @@ -120,7 +120,7 @@ describe(':mksession', function() test_terminal_session_disabled(1) -- no terminal should be set - eq('', meths.get_option_value('buftype', { buf = 0 })) + eq('', meths.get_option_value('buftype', {})) end) it('restores tab-local working directories', function() diff --git a/test/functional/legacy/012_directory_spec.lua b/test/functional/legacy/012_directory_spec.lua index f44ba10f7f..25d0dcb81e 100644 --- a/test/functional/legacy/012_directory_spec.lua +++ b/test/functional/legacy/012_directory_spec.lua @@ -58,7 +58,7 @@ describe("'directory' option", function() end of testfile]]) meths.set_option_value('swapfile', true, {}) - meths.set_option_value('swapfile', true, {buf=0}) + meths.set_option_value('swapfile', true, {}) meths.set_option_value('directory', '.', {}) -- sanity check: files should not exist yet. @@ -83,7 +83,7 @@ describe("'directory' option", function() meths.set_option_value('directory', 'Xtest.je', {}) command('bdelete') command('edit Xtest2/Xtest3') - eq(true, meths.get_option_value('swapfile', {buf=0})) + eq(true, meths.get_option_value('swapfile', {})) poke_eventloop() eq({ "Xtest3" }, ls_dir_sorted("Xtest2")) diff --git a/test/functional/lua/buffer_updates_spec.lua b/test/functional/lua/buffer_updates_spec.lua index 64cb524e99..d415b708be 100644 --- a/test/functional/lua/buffer_updates_spec.lua +++ b/test/functional/lua/buffer_updates_spec.lua @@ -415,7 +415,7 @@ describe('lua: nvim_buf_attach on_bytes', function() it('opening lines', function() local check_events = setup_eventcheck(verify, origlines) - -- meths.set_option_value('autoindent', true, { buf = 0 }) + -- meths.set_option_value('autoindent', true, {}) feed 'Go' check_events { { "test1", "bytes", 1, 4, 7, 0, 114, 0, 0, 0, 1, 0, 1 }; @@ -428,7 +428,7 @@ describe('lua: nvim_buf_attach on_bytes', function() it('opening lines with autoindent', function() local check_events = setup_eventcheck(verify, origlines) - meths.set_option_value('autoindent', true, { buf = 0 }) + meths.set_option_value('autoindent', true, {}) feed 'Go' check_events { { "test1", "bytes", 1, 4, 7, 0, 114, 0, 0, 0, 1, 0, 5 }; @@ -462,8 +462,8 @@ describe('lua: nvim_buf_attach on_bytes', function() it('continuing comments with fo=or', function() local check_events = setup_eventcheck(verify, {'// Comment'}) - meths.set_option_value('formatoptions', 'ro', { buf = 0 }) - meths.set_option_value('filetype', 'c', { buf = 0 }) + meths.set_option_value('formatoptions', 'ro', {}) + meths.set_option_value('filetype', 'c', {}) feed 'A<CR>' check_events { { "test1", "bytes", 1, 4, 0, 10, 10, 0, 0, 0, 1, 3, 4 }; diff --git a/test/functional/lua/filetype_spec.lua b/test/functional/lua/filetype_spec.lua index 4ea94796bc..b3d95e1c7f 100644 --- a/test/functional/lua/filetype_spec.lua +++ b/test/functional/lua/filetype_spec.lua @@ -134,6 +134,6 @@ end) describe('filetype.lua', function() it('does not override user autocommands that set filetype #20333', function() clear({args={'--clean', '--cmd', 'autocmd BufRead *.md set filetype=notmarkdown', 'README.md'}}) - eq('notmarkdown', meths.get_option_value('filetype', { buf = 0 })) + eq('notmarkdown', meths.get_option_value('filetype', {})) end) end) diff --git a/test/functional/lua/luaeval_spec.lua b/test/functional/lua/luaeval_spec.lua index 31da5db1df..dfbd2fb18b 100644 --- a/test/functional/lua/luaeval_spec.lua +++ b/test/functional/lua/luaeval_spec.lua @@ -514,7 +514,7 @@ describe('v:lua', function() [5] = {bold = true, foreground = Screen.colors.SeaGreen4}, }) screen:attach() - meths.set_option_value('omnifunc', 'v:lua.mymod.omni', { buf = 0 }) + meths.set_option_value('omnifunc', 'v:lua.mymod.omni', {}) feed('isome st<c-x><c-o>') screen:expect{grid=[[ some stuff^ | diff --git a/test/functional/lua/runtime_spec.lua b/test/functional/lua/runtime_spec.lua index 72c99ac1f3..cd19ea4e49 100644 --- a/test/functional/lua/runtime_spec.lua +++ b/test/functional/lua/runtime_spec.lua @@ -61,6 +61,38 @@ describe('runtime:', function() eq('vim', eval('g:colorscheme')) end) + + it("loads lua colorscheme in 'rtp' if vim version only exists in 'pp' #23724", function() + local pack_dir = 'Test_Pack' + mkdir_p(pack_dir) + finally(function() + rmdir(pack_dir) + end) + exec('set pp+=' .. pack_dir) + + local pack_opt_dir = pack_dir .. sep .. 'pack' .. sep .. 'some_name' .. sep .. 'opt' + local colors_opt_dir = pack_opt_dir .. sep .. 'some_pack' .. sep .. 'colors' + mkdir_p(colors_opt_dir) + + local rtp_colorscheme_file = colorscheme_folder .. sep .. 'new_colorscheme' + local pp_opt_colorscheme_file = colors_opt_dir .. sep .. 'new_colorscheme' + + write_file(pp_opt_colorscheme_file .. '.lua', [[vim.g.colorscheme = 'lua_pp']]) + exec('colorscheme new_colorscheme') + eq('lua_pp', eval('g:colorscheme')) + + write_file(pp_opt_colorscheme_file .. '.vim', [[let g:colorscheme = 'vim_pp']]) + exec('colorscheme new_colorscheme') + eq('vim_pp', eval('g:colorscheme')) + + write_file(rtp_colorscheme_file .. '.lua', [[vim.g.colorscheme = 'lua_rtp']]) + exec('colorscheme new_colorscheme') + eq('lua_rtp', eval('g:colorscheme')) + + write_file(rtp_colorscheme_file .. '.vim', [[let g:colorscheme = 'vim_rtp']]) + exec('colorscheme new_colorscheme') + eq('vim_rtp', eval('g:colorscheme')) + end) end) describe('compiler', function() diff --git a/test/functional/lua/secure_spec.lua b/test/functional/lua/secure_spec.lua index 9a6292a6c6..fc20a06390 100644 --- a/test/functional/lua/secure_spec.lua +++ b/test/functional/lua/secure_spec.lua @@ -160,7 +160,7 @@ describe('vim.secure', function() -- Cannot write file pcall_err(command, 'write') - eq(true, meths.get_option_value('readonly', {buf=0})) + eq(true, meths.get_option_value('readonly', {})) end) end) diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index 9a7654d610..978a4fe0b6 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -1496,7 +1496,7 @@ describe('lua stdlib', function() it('vim.bo', function() eq('', funcs.luaeval "vim.bo.filetype") exec_lua [[ - vim.api.nvim_set_option_value("filetype", "markdown", {buf = 0}) + vim.api.nvim_set_option_value("filetype", "markdown", {}) BUF = vim.api.nvim_create_buf(false, true) vim.api.nvim_set_option_value("modifiable", false, {buf = BUF}) ]] @@ -1519,9 +1519,9 @@ describe('lua stdlib', function() it('vim.wo', function() exec_lua [[ - vim.api.nvim_set_option_value("cole", 2, {win=0}) + vim.api.nvim_set_option_value("cole", 2, {}) vim.cmd "split" - vim.api.nvim_set_option_value("cole", 2, {win=0}) + vim.api.nvim_set_option_value("cole", 2, {}) ]] eq(2, funcs.luaeval "vim.wo.cole") exec_lua [[ diff --git a/test/functional/plugin/lsp/incremental_sync_spec.lua b/test/functional/plugin/lsp/incremental_sync_spec.lua index 1dca464d74..724b3efb97 100644 --- a/test/functional/plugin/lsp/incremental_sync_spec.lua +++ b/test/functional/plugin/lsp/incremental_sync_spec.lua @@ -21,7 +21,7 @@ before_each(function () -- ["mac"] = '\r', -- } - -- local line_ending = format_line_ending[vim.api.nvim_get_option_value('fileformat', {buf=0})] + -- local line_ending = format_line_ending[vim.api.nvim_get_option_value('fileformat', {})] function test_register(bufnr, id, offset_encoding, line_ending) diff --git a/test/functional/plugin/shada_spec.lua b/test/functional/plugin/shada_spec.lua index 43222f1904..8d37100607 100644 --- a/test/functional/plugin/shada_spec.lua +++ b/test/functional/plugin/shada_spec.lua @@ -2181,8 +2181,8 @@ describe('plugin/shada.vim', function() ' - contents "ab"', ' - "a"', }, nvim_eval('getline(1, "$")')) - eq(false, nvim('get_option_value', 'modified', {buf=0})) - eq('shada', nvim('get_option_value', 'filetype', {buf=0})) + eq(false, nvim('get_option_value', 'modified', {})) + eq('shada', nvim('get_option_value', 'filetype', {})) nvim_command('edit ' .. fname_tmp) eq({ 'History entry with timestamp ' .. epoch .. ':', @@ -2191,8 +2191,8 @@ describe('plugin/shada.vim', function() ' - contents "ab"', ' - "b"', }, nvim_eval('getline(1, "$")')) - eq(false, nvim('get_option_value', 'modified', {buf=0})) - eq('shada', nvim('get_option_value', 'filetype', {buf=0})) + eq(false, nvim('get_option_value', 'modified', {})) + eq('shada', nvim('get_option_value', 'filetype', {})) eq('++opt not supported', exc_exec('edit ++enc=latin1 ' .. fname)) neq({ 'History entry with timestamp ' .. epoch .. ':', @@ -2201,7 +2201,7 @@ describe('plugin/shada.vim', function() ' - contents "ab"', ' - "a"', }, nvim_eval('getline(1, "$")')) - neq(true, nvim('get_option_value', 'modified', {buf=0})) + neq(true, nvim('get_option_value', 'modified', {})) end) it('event FileReadCmd', function() @@ -2217,8 +2217,8 @@ describe('plugin/shada.vim', function() ' - contents "ab"', ' - "a"', }, nvim_eval('getline(1, "$")')) - eq(true, nvim('get_option_value', 'modified', {buf=0})) - neq('shada', nvim('get_option_value', 'filetype', {buf=0})) + eq(true, nvim('get_option_value', 'modified', {})) + neq('shada', nvim('get_option_value', 'filetype', {})) nvim_command('1,$read ' .. fname_tmp) eq({ '', @@ -2233,9 +2233,9 @@ describe('plugin/shada.vim', function() ' - contents "ab"', ' - "b"', }, nvim_eval('getline(1, "$")')) - eq(true, nvim('get_option_value', 'modified', {buf=0})) - neq('shada', nvim('get_option_value', 'filetype', {buf=0})) - nvim('set_option_value', 'modified', false, {buf=0}) + eq(true, nvim('get_option_value', 'modified', {})) + neq('shada', nvim('get_option_value', 'filetype', {})) + nvim('set_option_value', 'modified', false, {}) eq('++opt not supported', exc_exec('$read ++enc=latin1 ' .. fname)) eq({ '', @@ -2250,7 +2250,7 @@ describe('plugin/shada.vim', function() ' - contents "ab"', ' - "b"', }, nvim_eval('getline(1, "$")')) - neq(true, nvim('get_option_value', 'modified', {buf=0})) + neq(true, nvim('get_option_value', 'modified', {})) end) it('event BufWriteCmd', function() @@ -2517,10 +2517,10 @@ describe('ftplugin/shada.vim', function() it('sets options correctly', function() nvim_command('filetype plugin indent on') nvim_command('setlocal filetype=shada') - eq(true, nvim('get_option_value', 'expandtab', {buf=0})) - eq(2, nvim('get_option_value', 'tabstop', {buf=0})) - eq(2, nvim('get_option_value', 'softtabstop', {buf=0})) - eq(2, nvim('get_option_value', 'shiftwidth', {buf=0})) + eq(true, nvim('get_option_value', 'expandtab', {})) + eq(2, nvim('get_option_value', 'tabstop', {})) + eq(2, nvim('get_option_value', 'softtabstop', {})) + eq(2, nvim('get_option_value', 'shiftwidth', {})) end) it('sets indentkeys correctly', function() diff --git a/test/functional/provider/perl_spec.lua b/test/functional/provider/perl_spec.lua index bc7261895d..8049f0f3e2 100644 --- a/test/functional/provider/perl_spec.lua +++ b/test/functional/provider/perl_spec.lua @@ -45,7 +45,7 @@ describe('legacy perl provider', function() -- :perldo 1; doesn't change $_, -- the buffer should not be changed command('normal :perldo 1;') - eq(false, meths.get_option_value('modified', {buf=0})) + eq(false, meths.get_option_value('modified', {})) -- insert some text insert('abc\ndef\nghi') expect([[ diff --git a/test/functional/provider/ruby_spec.lua b/test/functional/provider/ruby_spec.lua index 40ace28c4e..d3b967dfbe 100644 --- a/test/functional/provider/ruby_spec.lua +++ b/test/functional/provider/ruby_spec.lua @@ -97,7 +97,7 @@ describe(':rubydo command', function() it('does not modify the buffer if no changes are made', function() command('normal :rubydo 42') - eq(false, meths.get_option_value('modified', {buf=0})) + eq(false, meths.get_option_value('modified', {})) end) end) diff --git a/test/functional/shada/buffers_spec.lua b/test/functional/shada/buffers_spec.lua index 2740eeeca6..b1c4ded541 100644 --- a/test/functional/shada/buffers_spec.lua +++ b/test/functional/shada/buffers_spec.lua @@ -48,7 +48,7 @@ describe('shada support code', function() reset('set shada+=%') nvim_command('edit ' .. testfilename) nvim_command('edit ' .. testfilename_2) - meths.set_option_value('buflisted', false, {buf=0}) + meths.set_option_value('buflisted', false, {}) expect_exit(nvim_command, 'qall') reset('set shada+=%') eq(2, funcs.bufnr('$')) @@ -60,7 +60,7 @@ describe('shada support code', function() reset('set shada+=%') nvim_command('edit ' .. testfilename) nvim_command('edit ' .. testfilename_2) - meths.set_option_value('buftype', 'quickfix', {buf=0}) + meths.set_option_value('buftype', 'quickfix', {}) expect_exit(nvim_command, 'qall') reset('set shada+=%') eq(2, funcs.bufnr('$')) diff --git a/test/functional/terminal/scrollback_spec.lua b/test/functional/terminal/scrollback_spec.lua index 48a9fa7171..5d3e55d898 100644 --- a/test/functional/terminal/scrollback_spec.lua +++ b/test/functional/terminal/scrollback_spec.lua @@ -401,7 +401,7 @@ describe("'scrollback' option", function() screen = thelpers.screen_setup(nil, "['sh']", 30) end - meths.set_option_value('scrollback', 0, {buf = 0}) + meths.set_option_value('scrollback', 0, {}) feed_data(('%s REP 31 line%s'):format(testprg('shell-test'), is_os('win') and '\r' or '\n')) screen:expect{any='30: line '} retry(nil, nil, function() expect_lines(7) end) @@ -417,7 +417,7 @@ describe("'scrollback' option", function() screen = thelpers.screen_setup(nil, "['sh']", 30) end - meths.set_option_value('scrollback', 200, {buf=0}) + meths.set_option_value('scrollback', 200, {}) -- Wait for prompt. screen:expect{any='%$'} @@ -426,10 +426,10 @@ describe("'scrollback' option", function() screen:expect{any='30: line '} retry(nil, nil, function() expect_lines(33, 2) end) - meths.set_option_value('scrollback', 10, {buf=0}) + meths.set_option_value('scrollback', 10, {}) poke_eventloop() retry(nil, nil, function() expect_lines(16) end) - meths.set_option_value('scrollback', 10000, {buf=0}) + meths.set_option_value('scrollback', 10000, {}) retry(nil, nil, function() expect_lines(16) end) -- Terminal job data is received asynchronously, may happen before the -- 'scrollback' option is synchronized with the internal sb_buffer. @@ -484,18 +484,18 @@ describe("'scrollback' option", function() ]]) local term_height = 6 -- Actual terminal screen height, not the scrollback -- Initial - local scrollback = meths.get_option_value('scrollback', {buf=0}) + local scrollback = meths.get_option_value('scrollback', {}) eq(scrollback + term_height, eval('line("$")')) -- Reduction scrollback = scrollback - 2 - meths.set_option_value('scrollback', scrollback, {buf=0}) + meths.set_option_value('scrollback', scrollback, {}) eq(scrollback + term_height, eval('line("$")')) end) it('defaults to 10000 in :terminal buffers', function() set_fake_shell() command('terminal') - eq(10000, meths.get_option_value('scrollback', {buf=0})) + eq(10000, meths.get_option_value('scrollback', {})) end) it('error if set to invalid value', function() @@ -507,7 +507,7 @@ describe("'scrollback' option", function() it('defaults to -1 on normal buffers', function() command('new') - eq(-1, meths.get_option_value('scrollback', {buf=0})) + eq(-1, meths.get_option_value('scrollback', {})) end) it(':setlocal in a :terminal buffer', function() @@ -516,45 +516,45 @@ describe("'scrollback' option", function() -- _Global_ scrollback=-1 defaults :terminal to 10_000. command('setglobal scrollback=-1') command('terminal') - eq(10000, meths.get_option_value('scrollback', {buf=0})) + eq(10000, meths.get_option_value('scrollback', {})) -- _Local_ scrollback=-1 in :terminal forces the _maximum_. command('setlocal scrollback=-1') retry(nil, nil, function() -- Fixup happens on refresh, not immediately. - eq(100000, meths.get_option_value('scrollback', {buf=0})) + eq(100000, meths.get_option_value('scrollback', {})) end) -- _Local_ scrollback=-1 during TermOpen forces the maximum. #9605 command('setglobal scrollback=-1') command('autocmd TermOpen * setlocal scrollback=-1') command('terminal') - eq(100000, meths.get_option_value('scrollback', {buf=0})) + eq(100000, meths.get_option_value('scrollback', {})) end) it(':setlocal in a normal buffer', function() command('new') -- :setlocal to -1. command('setlocal scrollback=-1') - eq(-1, meths.get_option_value('scrollback', {buf=0})) + eq(-1, meths.get_option_value('scrollback', {})) -- :setlocal to anything except -1. Currently, this just has no effect. command('setlocal scrollback=42') - eq(42, meths.get_option_value('scrollback', {buf=0})) + eq(42, meths.get_option_value('scrollback', {})) end) it(':set updates local value and global default', function() set_fake_shell() command('set scrollback=42') -- set global value - eq(42, meths.get_option_value('scrollback', {buf=0})) + eq(42, meths.get_option_value('scrollback', {})) command('terminal') - eq(42, meths.get_option_value('scrollback', {buf=0})) -- inherits global default + eq(42, meths.get_option_value('scrollback', {})) -- inherits global default command('setlocal scrollback=99') - eq(99, meths.get_option_value('scrollback', {buf=0})) + eq(99, meths.get_option_value('scrollback', {})) command('set scrollback<') -- reset to global default - eq(42, meths.get_option_value('scrollback', {buf=0})) + eq(42, meths.get_option_value('scrollback', {})) command('setglobal scrollback=734') -- new global default - eq(42, meths.get_option_value('scrollback', {buf=0})) -- local value did not change + eq(42, meths.get_option_value('scrollback', {})) -- local value did not change command('terminal') - eq(734, meths.get_option_value('scrollback', {buf=0})) + eq(734, meths.get_option_value('scrollback', {})) end) end) diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 3cfd222336..1b65c1cddc 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -1458,8 +1458,8 @@ describe('TUI', function() it('allows grid to assume wider ambiguous-width characters than host terminal #19686', function() child_session:request('nvim_buf_set_lines', 0, 0, -1, true, { ('℃'):rep(60), ('℃'):rep(60) }) - child_session:request('nvim_set_option_value', 'cursorline', true, {win=0}) - child_session:request('nvim_set_option_value', 'list', true, {win=0}) + child_session:request('nvim_set_option_value', 'cursorline', true, {}) + child_session:request('nvim_set_option_value', 'list', true, {}) child_session:request('nvim_set_option_value', 'listchars', 'eol:$', {win=0}) feed_data('gg') local singlewidth_screen = [[ diff --git a/test/functional/terminal/window_split_tab_spec.lua b/test/functional/terminal/window_split_tab_spec.lua index 3e1520e5fd..da14531fa2 100644 --- a/test/functional/terminal/window_split_tab_spec.lua +++ b/test/functional/terminal/window_split_tab_spec.lua @@ -71,7 +71,7 @@ describe(':terminal', function() end) it('does not change size if updated when not visible in any window #19665', function() - local channel = meths.get_option_value('channel', { buf = 0 }) + local channel = meths.get_option_value('channel', {}) command('enew') sleep(100) meths.chan_send(channel, 'foo') diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 324362a865..e430865df6 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -611,6 +611,20 @@ describe('decorations providers', function() end) end) +local example_text = [[ +for _,item in ipairs(items) do + local text, hl_id_cell, count = unpack(item) + if hl_id_cell ~= nil then + hl_id = hl_id_cell + end + for _ = 1, (count or 1) do + local cell = line[colpos] + cell.text = text + cell.hl_id = hl_id + colpos = colpos+1 + end +end]] + describe('extmark decorations', function() local screen, ns before_each( function() @@ -650,20 +664,6 @@ describe('extmark decorations', function() ns = meths.create_namespace 'test' end) - local example_text = [[ -for _,item in ipairs(items) do - local text, hl_id_cell, count = unpack(item) - if hl_id_cell ~= nil then - hl_id = hl_id_cell - end - for _ = 1, (count or 1) do - local cell = line[colpos] - cell.text = text - cell.hl_id = hl_id - colpos = colpos+1 - end -end]] - it('empty virtual text at eol should not break colorcolumn #17860', function() insert(example_text) feed('gg') @@ -704,14 +704,14 @@ end]] -- can "float" beyond end of line meths.buf_set_extmark(0, ns, 5, 28, { virt_text={{'loopy', 'ErrorMsg'}}, virt_text_pos='overlay'}) -- bound check: right edge of window - meths.buf_set_extmark(0, ns, 2, 26, { virt_text={{'bork bork bork '}, {'bork bork bork', 'ErrorMsg'}}, virt_text_pos='overlay'}) + meths.buf_set_extmark(0, ns, 2, 26, { virt_text={{'bork bork bork'}, {(' bork'):rep(10), 'ErrorMsg'}}, virt_text_pos='overlay'}) -- empty virt_text should not change anything meths.buf_set_extmark(0, ns, 6, 16, { virt_text={{''}}, virt_text_pos='overlay'}) screen:expect{grid=[[ ^for _,item in ipairs(items) do | {2:|} local text, hl_id_cell, count = unpack(item) | - {2:|} if hl_id_cell ~= nil tbork bork bork {4:bork bork}| + {2:|} if hl_id_cell ~= nil tbork bork bork{4: bork bork}| {2:|} {1:|} hl_id = hl_id_cell | {2:|} end | {2:|} for _ = 1, (count or 1) {4:loopy} | @@ -726,7 +726,6 @@ end]] | ]]} - -- handles broken lines screen:try_resize(22, 25) screen:expect{grid=[[ @@ -736,7 +735,7 @@ end]] cell, count = unpack(i| tem) | {2:|} if hl_id_cell ~= n| - il tbork bork bork {4:bor}| + il tbork bork bork{4: bor}| {2:|} {1:|} hl_id = hl_id_| cell | {2:|} end | @@ -756,6 +755,75 @@ end]] {1:~ }| | ]]} + + -- truncating in the middle of a char leaves a space + meths.buf_set_lines(0, 0, 1, true, {'for _,item in ipairs(items) do -- 古古古'}) + meths.buf_set_lines(0, 10, 12, true, {' end -- ??????????', 'end -- ?古古古古?古古'}) + meths.buf_set_extmark(0, ns, 0, 35, { virt_text={{'A', 'ErrorMsg'}, {'AA'}}, virt_text_pos='overlay'}) + meths.buf_set_extmark(0, ns, 10, 19, { virt_text={{'口口口', 'ErrorMsg'}}, virt_text_pos='overlay'}) + meths.buf_set_extmark(0, ns, 11, 21, { virt_text={{'口口口', 'ErrorMsg'}}, virt_text_pos='overlay'}) + meths.buf_set_extmark(0, ns, 11, 8, { virt_text={{'口口', 'ErrorMsg'}}, virt_text_pos='overlay'}) + screen:expect{grid=[[ + ^for _,item in ipairs(i| + tems) do -- {4:A}AA 古 | + {2:|} local text, hl_id_| + cell, count = unpack(i| + tem) | + {2:|} if hl_id_cell ~= n| + il tbork bork bork{4: bor}| + {2:|} {1:|} hl_id = hl_id_| + cell | + {2:|} end | + {2:|} for _ = 1, (count | + or 1) {4:loopy} | + {2:|} {1:|} local cell = l| + ine[colpos] | + {2:|} {1:|} cell.text = te| + xt | + {2:|} {1:|} cell.hl_id = h| + l_id | + {2:|} {1:|} cofoo{3:bar}{4:!!}olpo| + s+1 | + end -- ???????{4:口 }| + end -- {4:口口} 古古{4:口口 }| + {1:~ }| + {1:~ }| + | + ]]} + + screen:try_resize(82, 13) + screen:expect{grid=[[ + ^for _,item in ipairs(items) do -- {4:A}AA 古 | + {2:|} local text, hl_id_cell, count = unpack(item) | + {2:|} if hl_id_cell ~= nil tbork bork bork{4: bork bork bork bork bork bork bork bork b}| + {2:|} {1:|} hl_id = hl_id_cell | + {2:|} end | + {2:|} for _ = 1, (count or 1) {4:loopy} | + {2:|} {1:|} local cell = line[colpos] | + {2:|} {1:|} cell.text = text | + {2:|} {1:|} cell.hl_id = hl_id | + {2:|} {1:|} cofoo{3:bar}{4:!!}olpos+1 | + end -- ???????{4:口口口} | + end -- {4:口口} 古古{4:口口口} | + | + ]]} + + meths.buf_clear_namespace(0, ns, 0, -1) + screen:expect{grid=[[ + ^for _,item in ipairs(items) do -- 古古古 | + local text, hl_id_cell, count = unpack(item) | + if hl_id_cell ~= nil then | + hl_id = hl_id_cell | + end | + for _ = 1, (count or 1) do | + local cell = line[colpos] | + cell.text = text | + cell.hl_id = hl_id | + colpos = colpos+1 | + end -- ?????????? | + end -- ?古古古古?古古 | + | + ]]} end) it('can have virtual text of overlay position and styling', function() @@ -1165,7 +1233,744 @@ end]] meths.buf_set_extmark(0, ns, 0, 3, { end_col = 6, hl_group = 'TestBold', priority = 20 }) screen:expect_unchanged(true) end) +end) + +describe('decorations: inline virtual text', function() + local screen, ns + before_each( function() + clear() + screen = Screen.new(50, 10) + screen:attach() + screen:set_default_attr_ids { + [1] = {bold=true, foreground=Screen.colors.Blue}; + [2] = {foreground = Screen.colors.Brown}; + [3] = {bold = true, foreground = Screen.colors.SeaGreen}; + [4] = {background = Screen.colors.Red1, foreground = Screen.colors.Gray100}; + [5] = {background = Screen.colors.Red1, bold = true}; + [6] = {foreground = Screen.colors.DarkCyan}; + [7] = {background = Screen.colors.LightGrey}; + [8] = {bold = true}; + [9] = {background = Screen.colors.Plum1}; + [10] = {foreground = Screen.colors.SlateBlue}; + [11] = {blend = 30, background = Screen.colors.Red1}; + [12] = {background = Screen.colors.Yellow1}; + [13] = {reverse = true}; + [14] = {foreground = Screen.colors.SlateBlue, background = Screen.colors.LightMagenta}; + [15] = {bold = true, reverse = true}; + } + + ns = meths.create_namespace 'test' + end) + + + it('works', function() + insert(example_text) + feed 'gg' + screen:expect{grid=[[ + ^for _,item in ipairs(items) do | + local text, hl_id_cell, count = unpack(item) | + if hl_id_cell ~= nil then | + hl_id = hl_id_cell | + end | + for _ = 1, (count or 1) do | + local cell = line[colpos] | + cell.text = text | + cell.hl_id = hl_id | + | + ]]} + + meths.buf_set_extmark(0, ns, 1, 14, {virt_text={{': ', 'Special'}, {'string', 'Type'}}, virt_text_pos='inline'}) + screen:expect{grid=[[ + ^for _,item in ipairs(items) do | + local text{10:: }{3:string}, hl_id_cell, count = unpack| + (item) | + if hl_id_cell ~= nil then | + hl_id = hl_id_cell | + end | + for _ = 1, (count or 1) do | + local cell = line[colpos] | + cell.text = text | + | + ]]} + + screen:try_resize(55, 10) + screen:expect{grid=[[ + ^for _,item in ipairs(items) do | + local text{10:: }{3:string}, hl_id_cell, count = unpack(item| + ) | + if hl_id_cell ~= nil then | + hl_id = hl_id_cell | + end | + for _ = 1, (count or 1) do | + local cell = line[colpos] | + cell.text = text | + | + ]]} + + screen:try_resize(56, 10) + screen:expect{grid=[[ + ^for _,item in ipairs(items) do | + local text{10:: }{3:string}, hl_id_cell, count = unpack(item)| + if hl_id_cell ~= nil then | + hl_id = hl_id_cell | + end | + for _ = 1, (count or 1) do | + local cell = line[colpos] | + cell.text = text | + cell.hl_id = hl_id | + | + ]]} + end) + + it('cursor positions are correct with multiple inline virtual text', function() + insert('12345678') + meths.buf_set_extmark(0, ns, 0, 4, + { virt_text = { { ' virtual text ', 'Special' } }, virt_text_pos = 'inline' }) + meths.buf_set_extmark(0, ns, 0, 4, + { virt_text = { { ' virtual text ', 'Special' } }, virt_text_pos = 'inline' }) + feed '^' + feed '4l' + screen:expect { grid = [[ + 1234{10: virtual text virtual text }^5678 | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) + + it('adjusts cursor location correctly when inserting around inline virtual text', function() + insert('12345678') + feed '$' + meths.buf_set_extmark(0, ns, 0, 4, + { virt_text = { { ' virtual text ', 'Special' } }, virt_text_pos = 'inline' }) + + screen:expect { grid = [[ + 1234{10: virtual text }567^8 | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) + + it('has correct highlighting with multi-byte characters', function() + insert('12345678') + meths.buf_set_extmark(0, ns, 0, 4, + { virt_text = { { 'múlti-byté chñröcters 修补', 'Special' } }, virt_text_pos = 'inline' }) + + screen:expect { grid = [[ + 1234{10:múlti-byté chñröcters 修补}567^8 | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) + + it('has correct cursor position when inserting around virtual text', function() + insert('12345678') + meths.buf_set_extmark(0, ns, 0, 4, + { virt_text = { { 'virtual text', 'Special' } }, virt_text_pos = 'inline' }) + feed '^' + feed '3l' + feed 'a' + screen:expect { grid = [[ + 1234{10:^virtual text}5678 | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {8:-- INSERT --} | + ]]} + feed '<ESC>' + screen:expect{grid=[[ + 123^4{10:virtual text}5678 | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + feed '^' + feed '4l' + feed 'i' + screen:expect { grid = [[ + 1234{10:^virtual text}5678 | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {8:-- INSERT --} | + ]]} + end) + + it('has correct cursor position with virtual text on an empty line', function() + meths.buf_set_extmark(0, ns, 0, 0, + { virt_text = { { 'virtual text', 'Special' } }, virt_text_pos = 'inline' }) + screen:expect { grid = [[ + {10:^virtual text} | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) + + it('text is drawn correctly when inserting a wrapping virtual text on an empty line', function() + feed('o<esc>') + insert([[aaaaaaa + +bbbbbbb]]) + meths.buf_set_extmark(0, ns, 0, 0, + { virt_text = { { string.rep('X', 51), 'Special' } }, virt_text_pos = 'inline' }) + meths.buf_set_extmark(0, ns, 2, 0, + { virt_text = { { string.rep('X', 50), 'Special' } }, virt_text_pos = 'inline' }) + feed('gg0') + screen:expect { grid = [[ + {10:^XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + {10:X} | + aaaaaaa | + {10:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + bbbbbbb | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + feed('j') + screen:expect { grid = [[ + {10:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + {10:X} | + ^aaaaaaa | + {10:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + bbbbbbb | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + feed('j') + screen:expect { grid = [[ + {10:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + {10:X} | + aaaaaaa | + {10:^XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + bbbbbbb | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + feed('j') + screen:expect { grid = [[ + {10:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + {10:X} | + aaaaaaa | + {10:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + ^bbbbbbb | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) + + it('cursor position is correct with virtual text attached to hard tabs', function() + command('set noexpandtab') + feed('i') + feed('<TAB>') + feed('<TAB>') + feed('test') + feed('<ESC>') + meths.buf_set_extmark(0, ns, 0, 1, + { virt_text = { { 'virtual text', 'Special' } }, virt_text_pos = 'inline' }) + feed('0') + screen:expect { grid = [[ + ^ {10:virtual text} test | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + feed('l') + screen:expect { grid = [[ + {10:virtual text} ^ test | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + feed('l') + screen:expect { grid = [[ + {10:virtual text} ^test | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + feed('l') + screen:expect { grid = [[ + {10:virtual text} t^est | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + feed('l') + screen:expect { grid = [[ + {10:virtual text} te^st | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) + + it('cursor position is correct with virtual text on an empty line', function() + command('set linebreak') + insert('one twoword') + feed('0') + meths.buf_set_extmark(0, ns, 0, 3, + { virt_text = { { ': virtual text', 'Special' } }, virt_text_pos = 'inline' }) + screen:expect { grid = [[ + ^one{10:: virtual text} twoword | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) + + it('search highlight is correct', function() + insert('foo foo foo foo') + feed('0') + meths.buf_set_extmark(0, ns, 0, 8, + { virt_text = { { 'virtual text', 'Special' } }, virt_text_pos = 'inline' }) + screen:expect { grid = [[ + ^foo foo {10:virtual text}foo foo | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + feed('/foo') + screen:expect { grid = [[ + {12:foo} {13:foo} {10:virtual text}{12:foo} {12:foo} | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + /foo^ | + ]]} + end) + + it('visual select highlight is correct', function() + insert('foo foo foo foo') + feed('0') + meths.buf_set_extmark(0, ns, 0, 8, + { virt_text = { { 'virtual text', 'Special' } }, virt_text_pos = 'inline' }) + feed('8l') + screen:expect { grid = [[ + foo foo {10:virtual text}^foo foo | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + feed('v') + feed('2h') + screen:expect { grid = [[ + foo fo^o{7: }{10:virtual text}{7:f}oo foo | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {8:-- VISUAL --} | + ]]} + end) + + it('cursor position is correct when inserting around a virtual text with right gravity set to false', function() + insert('foo foo foo foo') + meths.buf_set_extmark(0, ns, 0, 8, + { virt_text = { { 'virtual text', 'Special' } }, virt_text_pos = 'inline', right_gravity = false }) + feed('0') + feed('8l') + screen:expect { grid = [[ + foo foo {10:virtual text}^foo foo | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + feed('i') + screen:expect { grid = [[ + foo foo {10:virtual text}^foo foo | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {8:-- INSERT --} | + ]]} + end) + + it('cursor position is correct when inserting around virtual texts with both left and right gravity ', function() + insert('foo foo foo foo') + meths.buf_set_extmark(0, ns, 0, 8, { virt_text = {{ '>>', 'Special' }}, virt_text_pos = 'inline', right_gravity = false }) + meths.buf_set_extmark(0, ns, 0, 8, { virt_text = {{ '<<', 'Special' }}, virt_text_pos = 'inline', right_gravity = true }) + feed('08l') + screen:expect{ grid = [[ + foo foo {10:>><<}^foo foo | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + feed('i') + screen:expect { grid = [[ + foo foo {10:>>^<<}foo foo | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {8:-- INSERT --} | + ]]} + end) + + it('draws correctly with no wrap multiple virtual text, where one is hidden', function() + insert('abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz') + command("set nowrap") + meths.buf_set_extmark(0, ns, 0, 50, + { virt_text = { { 'virtual text', 'Special' } }, virt_text_pos = 'inline' }) + meths.buf_set_extmark(0, ns, 0, 2, + { virt_text = { { 'virtual text', 'Special' } }, virt_text_pos = 'inline' }) + feed('$') + screen:expect { grid = [[ + opqrstuvwxyzabcdefghijklmnopqrstuvwx{10:virtual text}y^z| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) + + it('draws correctly with no wrap and a long virtual text', function() + insert('abcdefghi') + command("set nowrap") + meths.buf_set_extmark(0, ns, 0, 2, + { virt_text = { { string.rep('X', 55), 'Special' } }, virt_text_pos = 'inline' }) + feed('$') + screen:expect { grid = [[ + {10:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}cdefgh^i| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) + + it('tabs are the correct length with no wrap following virtual text', function() + command('set nowrap') + feed('itest<TAB>a<ESC>') + meths.buf_set_extmark(0, ns, 0, 0, + { virt_text = { { string.rep('a', 55), 'Special' } }, virt_text_pos = 'inline' }) + feed('gg$') + screen:expect { grid = [[ + {10:aaaaaaaaaaaaaaaaaaaaaaaaa}test ^a | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) + + it('highlighting does not extend when no wrap is enabled with a long virtual text', function() + insert('abcdef') + command("set nowrap") + meths.buf_set_extmark(0, ns, 0, 3, + { virt_text = { { string.rep('X', 50), 'Special' } }, virt_text_pos = 'inline' }) + feed('$') + screen:expect { grid = [[ + {10:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}de^f| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) + + it('highlighting is correct when virtual text wraps with number', function() + insert([[ + test + test]]) + command('set number') + meths.buf_set_extmark(0, ns, 0, 1, + { virt_text = { { string.rep('X', 55), 'Special' } }, virt_text_pos = 'inline' }) + feed('gg0') + screen:expect { grid = [[ + {2: 1 }^t{10:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + {2: }{10:XXXXXXXXXX}est | + {2: 2 }test | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) + + it('highlighting is correct when virtual text is proceeded with a match', function() + insert([[test]]) + meths.buf_set_extmark(0, ns, 0, 2, + { virt_text = { { 'virtual text', 'Special' } }, virt_text_pos = 'inline' }) + feed('gg0') + command('match ErrorMsg /e/') + screen:expect { grid = [[ + ^t{4:e}{10:virtual text}st | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + command('match ErrorMsg /s/') + screen:expect { grid = [[ + ^te{10:virtual text}{4:s}t | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) + + it('in diff mode is highlighted correct', function() + insert([[ + 9000 + 0009 + 0009 + 9000 + 0009 + ]]) + command("set diff") + meths.buf_set_extmark(0, ns, 0, 1, + { virt_text = { { 'test', 'Special' } }, virt_text_pos = 'inline', right_gravity = false }) + command("vnew") + insert([[ + 000 + 000 + 000 + 000 + 000 + ]]) + command("set diff") + feed('gg0') + screen:expect { grid = [[ + {9:^000 }│{5:9}{14:test}{9:000 }| + {9:000 }│{9:000}{5:9}{9: }| + {9:000 }│{9:000}{5:9}{9: }| + {9:000 }│{5:9}{9:000 }| + {9:000 }│{9:000}{5:9}{9: }| + │ | + {1:~ }│{1:~ }| + {1:~ }│{1:~ }| + {15:[No Name] [+] }{13:[No Name] [+] }| + | + ]]} + end) + + it('correctly draws when there are multiple overlapping virtual texts on the same line with nowrap', function() + command('set nowrap') + insert('a') + meths.buf_set_extmark(0, ns, 0, 0, + { virt_text = { { string.rep('a', 55), 'Special' } }, virt_text_pos = 'inline' }) + meths.buf_set_extmark(0, ns, 0, 0, + { virt_text = { { string.rep('b', 55), 'Special' } }, virt_text_pos = 'inline' }) + feed('$') + screen:expect { grid = [[ + {10:bbbbbbbbbbbbbbbbbbbbbbbbb}^a | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) + + it('correctly draws when overflowing virtual text is followed by tab with no wrap', function() + command('set nowrap') + feed('i<TAB>test<ESC>') + meths.buf_set_extmark( + 0, + ns, + 0, + 0, + { virt_text = { { string.rep('a', 60), 'Special' } }, virt_text_pos = 'inline' } + ) + feed('0') + screen:expect({ + grid = [[ + {10:aaaaaaaaaaaaaaaaaaaaaa} ^ test | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]], + }) + end) end) describe('decorations: virtual lines', function() @@ -1189,7 +1994,7 @@ describe('decorations: virtual lines', function() ns = meths.create_namespace 'test' end) - local example_text = [[ + local example_text2 = [[ if (h->n_buckets < new_n_buckets) { // expand khkey_t *new_keys = (khkey_t *)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); h->keys = new_keys; @@ -1200,7 +2005,7 @@ if (h->n_buckets < new_n_buckets) { // expand }]] it('works with one line', function() - insert(example_text) + insert(example_text2) feed 'gg' meths.buf_set_extmark(0, ns, 1, 33, { virt_lines={ {{">> ", "NonText"}, {"krealloc", "Identifier"}, {": change the size of an allocation"}}}; @@ -1309,7 +2114,7 @@ if (h->n_buckets < new_n_buckets) { // expand end) it('works with text at the beginning of the buffer', function() - insert(example_text) + insert(example_text2) feed 'gg' screen:expect{grid=[[ @@ -1370,7 +2175,7 @@ if (h->n_buckets < new_n_buckets) { // expand end) it('works with text at the end of the buffer', function() - insert(example_text) + insert(example_text2) feed 'G' screen:expect{grid=[[ @@ -1489,7 +2294,7 @@ if (h->n_buckets < new_n_buckets) { // expand end) it('works beyond end of the buffer with virt_lines_above', function() - insert(example_text) + insert(example_text2) feed 'G' screen:expect{grid=[[ @@ -1760,7 +2565,7 @@ if (h->n_buckets < new_n_buckets) { // expand end) it('works with sign and numbercolumns', function() - insert(example_text) + insert(example_text2) feed 'gg' command 'set number signcolumn=yes' screen:expect{grid=[[ @@ -1826,7 +2631,7 @@ if (h->n_buckets < new_n_buckets) { // expand it('works with hard tabs', function() - insert(example_text) + insert(example_text2) feed 'gg' meths.buf_set_extmark(0, ns, 1, 0, { virt_lines={ {{">>", "NonText"}, {"\tvery\ttabby", "Identifier"}, {"text\twith\ttabs"}}}; @@ -1910,10 +2715,10 @@ describe('decorations: signs', function() } ns = meths.create_namespace 'test' - meths.set_option_value('signcolumn', 'auto:9', {win = 0}) + meths.set_option_value('signcolumn', 'auto:9', {}) end) - local example_text = [[ + local example_test3 = [[ l1 l2 l3 @@ -1922,7 +2727,7 @@ l5 ]] it('can add a single sign (no end row)', function() - insert(example_text) + insert(example_test3) feed 'gg' meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S'}) @@ -1943,7 +2748,7 @@ l5 end) it('can add a single sign (with end row)', function() - insert(example_text) + insert(example_test3) feed 'gg' meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S', end_row=1}) @@ -1965,7 +2770,7 @@ l5 it('can add multiple signs (single extmark)', function() pending('TODO(lewis6991): Support ranged signs') - insert(example_text) + insert(example_test3) feed 'gg' meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S', end_row = 2}) @@ -1987,7 +2792,7 @@ l5 it('can add multiple signs (multiple extmarks)', function() pending('TODO(lewis6991): Support ranged signs') - insert(example_text) + insert(example_test3) feed'gg' meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S1'}) @@ -2009,7 +2814,7 @@ l5 end) it('can add multiple signs (multiple extmarks) 2', function() - insert(example_text) + insert(example_test3) feed 'gg' meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S1'}) @@ -2049,7 +2854,7 @@ l5 it('can add multiple signs (multiple extmarks) 3', function() pending('TODO(lewis6991): Support ranged signs') - insert(example_text) + insert(example_test3) feed 'gg' meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S1', end_row=2}) @@ -2070,7 +2875,7 @@ l5 end) it('can add multiple signs (multiple extmarks) 4', function() - insert(example_text) + insert(example_test3) feed 'gg' meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S1', end_row=0}) @@ -2091,7 +2896,7 @@ l5 end) it('works with old signs', function() - insert(example_text) + insert(example_test3) feed 'gg' helpers.command('sign define Oldsign text=x') @@ -2118,7 +2923,7 @@ l5 it('works with old signs (with range)', function() pending('TODO(lewis6991): Support ranged signs') - insert(example_text) + insert(example_test3) feed 'gg' helpers.command('sign define Oldsign text=x') @@ -2147,7 +2952,7 @@ l5 it('can add a ranged sign (with start out of view)', function() pending('TODO(lewis6991): Support ranged signs') - insert(example_text) + insert(example_test3) command 'set signcolumn=yes:2' feed 'gg' feed '2<C-e>' @@ -2204,7 +3009,7 @@ l5 it('works with priority #19716', function() screen:try_resize(20, 3) - insert(example_text) + insert(example_test3) feed 'gg' helpers.command('sign define Oldsign text=O3') @@ -2222,7 +3027,7 @@ l5 ]]} -- Check truncation works too - meths.set_option_value('signcolumn', 'auto', {win = 0}) + meths.set_option_value('signcolumn', 'auto', {}) screen:expect{grid=[[ S5^l1 | @@ -2233,8 +3038,8 @@ l5 it('does not set signcolumn for signs without text', function() screen:try_resize(20, 3) - meths.set_option_value('signcolumn', 'auto', {win = 0}) - insert(example_text) + meths.set_option_value('signcolumn', 'auto', {}) + insert(example_test3) feed 'gg' meths.buf_set_extmark(0, ns, 0, -1, {number_hl_group='Error'}) screen:expect{grid=[[ diff --git a/test/functional/ui/spell_spec.lua b/test/functional/ui/spell_spec.lua index 15819aef40..0f553d4a9b 100644 --- a/test/functional/ui/spell_spec.lua +++ b/test/functional/ui/spell_spec.lua @@ -254,4 +254,19 @@ describe("'spell'", function() ]]) end) + it('and syntax does not clear extmark highlighting at the start of a word', function() + screen:try_resize(43, 3) + command([[ + set spell + syntax match Constant "^.*$" + call setline(1, "This is some text without any spell errors.") + ]]) + local ns = meths.create_namespace("spell") + curbufmeths.set_extmark(ns, 0, 0, { hl_group = 'WarningMsg', end_col = 43 }) + screen:expect([[ + {6:^This is some text without any spell errors.}| + {0:~ }| + | + ]]) + end) end) diff --git a/test/functional/vimscript/buf_functions_spec.lua b/test/functional/vimscript/buf_functions_spec.lua index 2d6d4b8e04..2a5720fbd7 100644 --- a/test/functional/vimscript/buf_functions_spec.lua +++ b/test/functional/vimscript/buf_functions_spec.lua @@ -268,7 +268,7 @@ describe('setbufvar() function', function() end) it('may set options, including window-local and global values', function() local buf1 = meths.get_current_buf() - eq(false, meths.get_option_value('number', {win=0})) + eq(false, meths.get_option_value('number', {})) command('split') command('new') eq(2, bufmeths.get_number(curwinmeths.get_buf())) |