diff options
-rw-r--r-- | runtime/lua/vim/lsp.lua | 39 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/util.lua | 7 | ||||
-rw-r--r-- | src/nvim/api/buffer.c | 38 | ||||
-rw-r--r-- | src/nvim/buffer.c | 1 | ||||
-rw-r--r-- | src/nvim/buffer_defs.h | 7 | ||||
-rw-r--r-- | src/nvim/decoration.c | 90 | ||||
-rw-r--r-- | src/nvim/decoration.h | 18 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 2 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 1 | ||||
-rw-r--r-- | src/nvim/extmark.c | 40 | ||||
-rw-r--r-- | src/nvim/extmark_defs.h | 10 | ||||
-rw-r--r-- | src/nvim/lib/kvec.h | 12 | ||||
-rw-r--r-- | src/nvim/marktree.c | 8 | ||||
-rw-r--r-- | src/nvim/marktree.h | 9 | ||||
-rw-r--r-- | src/nvim/plines.c | 4 | ||||
-rw-r--r-- | src/nvim/screen.c | 20 | ||||
-rw-r--r-- | src/nvim/testdir/test_cmdline.vim | 15 | ||||
-rw-r--r-- | test/functional/ui/decorations_spec.lua | 7 | ||||
-rw-r--r-- | test/unit/marktree_spec.lua | 4 |
19 files changed, 210 insertions, 122 deletions
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 9a008ac965..56ac1cbc66 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -644,7 +644,9 @@ end --- - debounce_text_changes (number, default nil): Debounce didChange --- notifications to the server by the given number in milliseconds. No debounce --- occurs if nil ---- +--- - exit_timeout (number, default 500): Milliseconds to wait for server to +-- exit cleanly after sending the 'shutdown' request before sending kill -15. +-- If set to false, nvim exits immediately after sending the 'shutdown' request to the server. ---@returns Client id. |vim.lsp.get_client_by_id()| Note: client may not be --- fully initialized. Use `on_init` to do any actions once --- the client has been initialized. @@ -1226,9 +1228,38 @@ function lsp._vim_exit_handler() client.stop() end - if not vim.wait(500, function() return tbl_isempty(active_clients) end, 50) then - for _, client in pairs(active_clients) do - client.stop(true) + local timeouts = {} + local max_timeout = 0 + local send_kill = false + + for client_id, client in pairs(active_clients) do + local timeout = if_nil(client.config.flags.exit_timeout, 500) + if timeout then + send_kill = true + timeouts[client_id] = timeout + max_timeout = math.max(timeout, max_timeout) + else + active_clients[client_id] = nil + end + end + + local poll_time = 50 + + local function check_clients_closed() + for client_id, _ in pairs(active_clients) do + timeouts[client_id] = timeouts[client_id] - poll_time + if timeouts[client_id] < 0 then + active_clients[client_id] = nil + end + end + return tbl_isempty(active_clients) + end + + if send_kill then + if not vim.wait(max_timeout, check_clients_closed, poll_time) then + for _, client in pairs(active_clients) do + client.stop(true) + end end end end diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 952926b67e..7a0ac458f3 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -226,9 +226,10 @@ function M.get_progress_messages() table.remove(client.messages, item.idx) end - for _, item in ipairs(progress_remove) do - client.messages.progress[item.token] = nil - end + end + + for _, item in ipairs(progress_remove) do + item.client.messages.progress[item.token] = nil end return new_messages diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 31d44c68bf..92fcb59b34 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -247,7 +247,6 @@ Boolean nvim_buf_detach(uint64_t channel_id, Buffer buffer, Error *err) } void nvim__buf_redraw_range(Buffer buffer, Integer first, Integer last, Error *err) - FUNC_API_LUA_ONLY { buf_T *buf = find_buffer_by_handle(buffer, err); if (!buf) { @@ -1531,12 +1530,6 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// option is still used for hard tabs. By default lines are /// placed below the buffer line containing the mark. /// -/// Note: currently virtual lines are limited to one block -/// per buffer. Thus setting a new mark disables any previous -/// `virt_lines` decoration. However plugins should not rely -/// on this behaviour, as this limitation is planned to be -/// removed. -/// /// - virt_lines_above: place virtual lines above instead. /// - virt_lines_leftcol: Place extmarks in the leftmost /// column of the window, bypassing @@ -1680,9 +1673,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer goto error; } - VirtLines virt_lines = KV_INITIAL_VALUE; - bool virt_lines_above = false; bool virt_lines_leftcol = false; + OPTION_TO_BOOL(virt_lines_leftcol, virt_lines_leftcol, false); if (opts->virt_lines.type == kObjectTypeArray) { Array a = opts->virt_lines.data.array; @@ -1693,7 +1685,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } int dummig; VirtText jtem = parse_virt_text(a.items[j].data.array, err, &dummig); - kv_push(virt_lines, jtem); + kv_push(decor.virt_lines, ((struct virt_line){ jtem, virt_lines_leftcol })); if (ERROR_SET(err)) { goto error; } @@ -1703,8 +1695,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer goto error; } - OPTION_TO_BOOL(virt_lines_above, virt_lines_above, false); - OPTION_TO_BOOL(virt_lines_leftcol, virt_lines_leftcol, false); + + OPTION_TO_BOOL(decor.virt_lines_above, virt_lines_above, false); if (opts->priority.type == kObjectTypeInteger) { Integer val = opts->priority.data.integer; @@ -1774,7 +1766,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer if (ephemeral) { d = &decor; - } else if (kv_size(decor.virt_text) + } else if (kv_size(decor.virt_text) || kv_size(decor.virt_lines) || decor.priority != DECOR_PRIORITY_BASE || decor.hl_eol) { // TODO(bfredl): this is a bit sketchy. eventually we should @@ -1794,22 +1786,11 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer goto error; } - if (kv_size(virt_lines) && buf->b_virt_line_mark) { - mtpos_t pos = marktree_lookup(buf->b_marktree, buf->b_virt_line_mark, NULL); - clear_virt_lines(buf, pos.row); // handles pos.row == -1 - } + extmark_set(buf, (uint64_t)ns_id, &id, (int)line, (colnr_T)col, line2, col2, + d, right_gravity, end_right_gravity, kExtmarkNoUndo); - uint64_t mark = extmark_set(buf, (uint64_t)ns_id, &id, (int)line, (colnr_T)col, - line2, col2, d, right_gravity, - end_right_gravity, kExtmarkNoUndo); - - if (kv_size(virt_lines)) { - buf->b_virt_lines = virt_lines; - buf->b_virt_line_mark = mark; - buf->b_virt_line_pos = -1; - buf->b_virt_line_above = virt_lines_above; - buf->b_virt_line_leftcol = virt_lines_leftcol; - redraw_buf_line_later(buf, MIN(buf->b_ml.ml_line_count, line+1+(virt_lines_above?0:1))); + if (kv_size(decor.virt_lines)) { + redraw_buf_line_later(buf, MIN(buf->b_ml.ml_line_count, line+1+(decor.virt_lines_above?0:1))); } } @@ -2013,6 +1994,7 @@ Dictionary nvim__buf_stats(Buffer buffer, Error *err) // this exists to debug issues PUT(rv, "dirty_bytes", INTEGER_OBJ((Integer)buf->deleted_bytes)); PUT(rv, "dirty_bytes2", INTEGER_OBJ((Integer)buf->deleted_bytes2)); + PUT(rv, "virt_blocks", INTEGER_OBJ((Integer)buf->b_virt_line_blocks)); u_header_T *uhp = NULL; if (buf->b_u_curhead != NULL) { diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 569ad626ad..3ad93c1c2c 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -818,7 +818,6 @@ static void free_buffer_stuff(buf_T *buf, int free_flags) uc_clear(&buf->b_ucmds); // clear local user commands buf_delete_signs(buf, (char_u *)"*"); // delete any signs extmark_free_all(buf); // delete any extmarks - clear_virt_lines(buf, -1); map_clear_int(buf, MAP_ALL_MODES, true, false); // clear local mappings map_clear_int(buf, MAP_ALL_MODES, true, true); // clear local abbrevs XFREE_CLEAR(buf->b_start_fenc); diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 19b0a3c5c6..c0a0376088 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -866,12 +866,7 @@ struct file_buffer { MarkTree b_marktree[1]; Map(uint64_t, ExtmarkItem) b_extmark_index[1]; Map(uint64_t, ExtmarkNs) b_extmark_ns[1]; // extmark namespaces - - VirtLines b_virt_lines; - uint64_t b_virt_line_mark; - int b_virt_line_pos; - bool b_virt_line_above; - bool b_virt_line_leftcol; + size_t b_virt_line_blocks; // number of virt_line blocks // array of channel_id:s which have asked to receive updates for this // buffer. diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index 4e80528c74..c0f3c32f93 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -91,12 +91,31 @@ void decor_redraw(buf_T *buf, int row1, int row2, Decoration *decor) if (kv_size(decor->virt_text)) { redraw_buf_line_later(buf, row1+1); } + + if (kv_size(decor->virt_lines)) { + redraw_buf_line_later(buf, MIN(buf->b_ml.ml_line_count, + row1+1+(decor->virt_lines_above?0:1))); + } +} + +void decor_remove(buf_T *buf, int row, int row2, Decoration *decor) +{ + if (kv_size(decor->virt_lines)) { + assert(buf->b_virt_line_blocks > 0); + buf->b_virt_line_blocks--; + } + decor_redraw(buf, row, row2, decor); + decor_free(decor); } void decor_free(Decoration *decor) { if (decor && !decor->shared) { clear_virttext(&decor->virt_text); + for (size_t i = 0; i < kv_size(decor->virt_lines); i++) { + clear_virttext(&kv_A(decor->virt_lines, i).line); + } + kv_destroy(decor->virt_lines); xfree(decor); } } @@ -118,6 +137,8 @@ Decoration *decor_find_virttext(buf_T *buf, int row, uint64_t ns_id) mtmark_t mark = marktree_itr_current(itr); if (mark.row < 0 || mark.row > row) { break; + } else if (marktree_decor_level(mark.id) < kDecorLevelVisible) { + goto next_mark; } ExtmarkItem *item = map_ref(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark.id, false); @@ -125,6 +146,7 @@ Decoration *decor_find_virttext(buf_T *buf, int row, uint64_t ns_id) && item->decor && kv_size(item->decor->virt_text)) { return item->decor; } +next_mark: marktree_itr_next(buf->b_marktree, itr); } return NULL; @@ -158,21 +180,22 @@ bool decor_redraw_start(buf_T *buf, int top_row, DecorState *state) if (mark.row < 0) { // || mark.row > end_row break; } - if ((mark.row < top_row && mark.id&MARKTREE_END_FLAG)) { + if ((mark.row < top_row && mark.id&MARKTREE_END_FLAG) + || marktree_decor_level(mark.id) < kDecorLevelVisible) { goto next_mark; } - mtpos_t altpos = marktree_lookup(buf->b_marktree, - mark.id^MARKTREE_END_FLAG, NULL); uint64_t start_id = mark.id & ~MARKTREE_END_FLAG; ExtmarkItem *item = map_ref(uint64_t, ExtmarkItem)(buf->b_extmark_index, start_id, false); if (!item || !item->decor) { - // TODO(bfredl): dedicated flag for being a decoration? goto next_mark; } Decoration *decor = item->decor; + mtpos_t altpos = marktree_lookup(buf->b_marktree, + mark.id^MARKTREE_END_FLAG, NULL); + if ((!(mark.id&MARKTREE_END_FLAG) && altpos.row < top_row && !kv_size(decor->virt_text)) || ((mark.id&MARKTREE_END_FLAG) && altpos.row >= top_row)) { @@ -251,21 +274,21 @@ int decor_redraw_col(buf_T *buf, int col, int win_col, bool hidden, DecorState * break; } - if ((mark.id&MARKTREE_END_FLAG)) { - // TODO(bfredl): check decoration flag + if ((mark.id&MARKTREE_END_FLAG) + || marktree_decor_level(mark.id) < kDecorLevelVisible) { goto next_mark; } - mtpos_t endpos = marktree_lookup(buf->b_marktree, - mark.id|MARKTREE_END_FLAG, NULL); ExtmarkItem *item = map_ref(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark.id, false); if (!item || !item->decor) { - // TODO(bfredl): dedicated flag for being a decoration? goto next_mark; } Decoration *decor = item->decor; + mtpos_t endpos = marktree_lookup(buf->b_marktree, + mark.id|MARKTREE_END_FLAG, NULL); + if (endpos.row == -1) { endpos.row = mark.row; endpos.col = mark.col; @@ -414,33 +437,38 @@ void decor_free_all_mem(void) } -int decor_virtual_lines(win_T *wp, linenr_T lnum) +int decor_virt_lines(win_T *wp, linenr_T lnum, VirtLines *lines) { buf_T *buf = wp->w_buffer; - if (!buf->b_virt_line_mark) { + if (!buf->b_virt_line_blocks) { + // Only pay for what you use: in case virt_lines feature is not active + // in a buffer, plines do not need to access the marktree at all return 0; } - if (buf->b_virt_line_pos < 0) { - mtpos_t pos = marktree_lookup(buf->b_marktree, buf->b_virt_line_mark, NULL); - if (pos.row < 0) { - buf->b_virt_line_mark = 0; + + int virt_lines = 0; + int row = (int)MAX(lnum - 2, 0); + int end_row = (int)lnum; + MarkTreeIter itr[1] = { 0 }; + marktree_itr_get(buf->b_marktree, row, 0, itr); + while (true) { + mtmark_t mark = marktree_itr_current(itr); + if (mark.row < 0 || mark.row >= end_row) { + break; + } else if (marktree_decor_level(mark.id) < kDecorLevelVirtLine) { + goto next_mark; + } + bool above = mark.row > (int)(lnum - 2); + ExtmarkItem *item = map_ref(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark.id, false); + if (item && item->decor && item->decor->virt_lines_above == above) { + virt_lines += (int)kv_size(item->decor->virt_lines); + if (lines) { + kv_splice(*lines, item->decor->virt_lines); + } } - buf->b_virt_line_pos = pos.row + (buf->b_virt_line_above ? 0 : 1); +next_mark: + marktree_itr_next(buf->b_marktree, itr); } - return (lnum-1 == buf->b_virt_line_pos) ? (int)kv_size(buf->b_virt_lines) : 0; -} - -void clear_virt_lines(buf_T *buf, int row) -{ - if (row > -1) { - redraw_buf_line_later(buf, MIN(buf->b_ml.ml_line_count, - row+1+(buf->b_virt_line_above?0:1))); - } - for (size_t i = 0; i < kv_size(buf->b_virt_lines); i++) { - clear_virttext(&kv_A(buf->b_virt_lines, i)); - } - kv_destroy(buf->b_virt_lines); // re-initializes - buf->b_virt_line_pos = -1; - buf->b_virt_line_mark = 0; + return virt_lines; } diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h index e44fbab0a5..97ae9ed5d8 100644 --- a/src/nvim/decoration.h +++ b/src/nvim/decoration.h @@ -24,22 +24,34 @@ typedef enum { kHlModeBlend, } HlMode; +typedef kvec_t(VirtTextChunk) VirtText; +#define VIRTTEXT_EMPTY ((VirtText)KV_INITIAL_VALUE) + + +typedef kvec_t(struct virt_line { VirtText line; bool left_col; }) VirtLines; + + struct Decoration { VirtText virt_text; + VirtLines virt_lines; + int hl_id; // highlight group VirtTextPos virt_text_pos; HlMode hl_mode; + + // TODO(bfredl): at some point turn this into FLAGS bool virt_text_hide; bool hl_eol; bool shared; // shared decoration, don't free + bool virt_lines_above; // TODO(bfredl): style, signs, etc DecorPriority priority; int col; // fixed col value, like win_col int virt_text_width; // width of virt_text }; -#define DECORATION_INIT { KV_INITIAL_VALUE, 0, kVTEndOfLine, kHlModeUnknown, \ - false, false, false, DECOR_PRIORITY_BASE, 0, 0 } +#define DECORATION_INIT { KV_INITIAL_VALUE, KV_INITIAL_VALUE, 0, kVTEndOfLine, kHlModeUnknown, \ + false, false, false, false, DECOR_PRIORITY_BASE, 0, 0 } typedef struct { int start_row; @@ -60,9 +72,7 @@ typedef struct { int row; int col_until; int current; - int eol_col; - VirtText *virt_text; } DecorState; typedef struct { diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 946bde060d..1c78e25639 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -3359,7 +3359,7 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) expand_T xpc; bool filtered = false; int options = WILD_SILENT | WILD_USE_NL | WILD_ADD_SLASH - | WILD_NO_BEEP; + | WILD_NO_BEEP | WILD_HOME_REPLACE; if (argvars[1].v_type != VAR_STRING) { EMSG2(_(e_invarg2), "type must be a string"); diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 6914402ada..532f8aa9b3 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -2915,6 +2915,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) ExpandInit(xp); xp->xp_pattern = (char_u *)buff; + xp->xp_line = (char_u *)buff; xp->xp_context = EXPAND_COMMANDS; // Default until we get past command ea.argt = 0; diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c index dc73e34111..05d1d69f9d 100644 --- a/src/nvim/extmark.c +++ b/src/nvim/extmark.c @@ -83,8 +83,7 @@ uint64_t extmark_set(buf_T *buf, uint64_t ns_id, uint64_t *idp, int row, colnr_T ExtmarkItem it = map_del(uint64_t, ExtmarkItem)(buf->b_extmark_index, old_mark); if (it.decor) { - decor_redraw(buf, row, row, it.decor); - decor_free(it.decor); + decor_remove(buf, row, row, it.decor); } mark = marktree_revise(buf->b_marktree, itr); goto revised; @@ -96,12 +95,20 @@ uint64_t extmark_set(buf_T *buf, uint64_t ns_id, uint64_t *idp, int row, colnr_T } } + uint8_t decor_level = kDecorLevelNone; // no decor + if (decor) { + decor_level = kDecorLevelVisible; // decor affects redraw + if (kv_size(decor->virt_lines)) { + decor_level = kDecorLevelVirtLine; // decor affects horizontal size + } + } + if (end_row > -1) { mark = marktree_put_pair(buf->b_marktree, row, col, right_gravity, - end_row, end_col, end_right_gravity); + end_row, end_col, end_right_gravity, decor_level); } else { - mark = marktree_put(buf->b_marktree, row, col, right_gravity); + mark = marktree_put(buf->b_marktree, row, col, right_gravity, decor_level); } revised: @@ -117,6 +124,9 @@ revised: } if (decor) { + if (kv_size(decor->virt_lines)) { + buf->b_virt_line_blocks++; + } decor_redraw(buf, row, end_row > -1 ? end_row : row, decor); } @@ -170,12 +180,7 @@ bool extmark_del(buf_T *buf, uint64_t ns_id, uint64_t id) } if (item.decor) { - decor_redraw(buf, pos.row, pos2.row, item.decor); - decor_free(item.decor); - } - - if (mark == buf->b_virt_line_mark) { - clear_virt_lines(buf, pos.row); + decor_remove(buf, pos.row, pos2.row, item.decor); } map_del(uint64_t, uint64_t)(ns->map, id); @@ -228,17 +233,13 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, int l_row, colnr_T l_col, int u_r marktree_del_itr(buf->b_marktree, itr, false); if (*del_status >= 0) { // we had a decor_id DecorItem it = kv_A(decors, *del_status); - decor_redraw(buf, it.row1, mark.row, it.decor); - decor_free(it.decor); + decor_remove(buf, it.row1, mark.row, it.decor); } map_del(uint64_t, ssize_t)(&delete_set, mark.id); continue; } uint64_t start_id = mark.id & ~MARKTREE_END_FLAG; - if (start_id == buf->b_virt_line_mark) { - clear_virt_lines(buf, mark.row); - } ExtmarkItem item = map_get(uint64_t, ExtmarkItem)(buf->b_extmark_index, start_id); @@ -257,8 +258,7 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, int l_row, colnr_T l_col, int u_r } map_put(uint64_t, ssize_t)(&delete_set, other, decor_id); } else if (item.decor) { - decor_redraw(buf, mark.row, mark.row, item.decor); - decor_free(item.decor); + decor_remove(buf, mark.row, mark.row, item.decor); } ExtmarkNs *my_ns = all_ns ? buf_ns_ref(buf, item.ns_id, false) : ns; map_del(uint64_t, uint64_t)(my_ns->map, item.mark_id); @@ -276,8 +276,7 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, int l_row, colnr_T l_col, int u_r marktree_del_itr(buf->b_marktree, itr, false); if (decor_id >= 0) { DecorItem it = kv_A(decors, decor_id); - decor_redraw(buf, it.row1, pos.row, it.decor); - decor_free(it.decor); + decor_remove(buf, it.row1, pos.row, it.decor); } }); map_clear(uint64_t, ssize_t)(&delete_set); @@ -508,7 +507,6 @@ void extmark_apply_undo(ExtmarkUndoObject undo_info, bool undo) kExtmarkNoUndo); } } - curbuf->b_virt_line_pos = -1; } @@ -588,7 +586,6 @@ void extmark_splice_impl(buf_T *buf, int start_row, colnr_T start_col, bcount_t colnr_T new_col, bcount_t new_byte, ExtmarkOp undo) { buf->deleted_bytes2 = 0; - buf->b_virt_line_pos = -1; buf_updates_send_splice(buf, start_row, start_col, start_byte, old_row, old_col, old_byte, new_row, new_col, new_byte); @@ -680,7 +677,6 @@ void extmark_move_region(buf_T *buf, int start_row, colnr_T start_col, bcount_t colnr_T new_col, bcount_t new_byte, ExtmarkOp undo) { buf->deleted_bytes2 = 0; - buf->b_virt_line_pos = -1; // TODO(bfredl): this is not synced to the buffer state inside the callback. // But unless we make the undo implementation smarter, this is not ensured // anyway. diff --git a/src/nvim/extmark_defs.h b/src/nvim/extmark_defs.h index c0a4f4014f..4d4d9aefb5 100644 --- a/src/nvim/extmark_defs.h +++ b/src/nvim/extmark_defs.h @@ -11,10 +11,6 @@ typedef struct { int hl_id; } VirtTextChunk; -typedef kvec_t(VirtTextChunk) VirtText; -#define VIRTTEXT_EMPTY ((VirtText)KV_INITIAL_VALUE) -typedef kvec_t(VirtText) VirtLines; - typedef struct { @@ -38,4 +34,10 @@ typedef enum { kExtmarkUndoNoRedo, // Operation should be undoable, but not redoable } ExtmarkOp; +typedef enum { + kDecorLevelNone = 0, + kDecorLevelVisible = 1, + kDecorLevelVirtLine = 2, +} DecorLevel; + #endif // NVIM_EXTMARK_DEFS_H diff --git a/src/nvim/lib/kvec.h b/src/nvim/lib/kvec.h index eb2d9bbb77..ff0a8bcb7c 100644 --- a/src/nvim/lib/kvec.h +++ b/src/nvim/lib/kvec.h @@ -94,6 +94,17 @@ memcpy((v1).items, (v0).items, sizeof((v1).items[0]) * (v0).size); \ } while (0) +#define kv_splice(v1, v0) \ + do { \ + if ((v1).capacity < (v1).size + (v0).size) { \ + (v1).capacity = (v1).size + (v0).size; \ + kv_roundup32((v1).capacity); \ + kv_resize((v1), (v1).capacity); \ + } \ + memcpy((v1).items + (v1).size, (v0).items, sizeof((v1).items[0]) * (v0).size); \ + (v1).size = (v1).size + (v0).size; \ + } while (0) + #define kv_pushp(v) \ ((((v).size == (v).capacity) ? (kv_resize_full(v), 0) : 0), \ ((v).items + ((v).size++))) @@ -101,6 +112,7 @@ #define kv_push(v, x) \ (*kv_pushp(v) = (x)) + #define kv_a(v, i) \ (*(((v).capacity <= (size_t)(i) \ ? ((v).capacity = (v).size = (i) + 1, \ diff --git a/src/nvim/marktree.c b/src/nvim/marktree.c index 39c1e36147..a7f540c748 100644 --- a/src/nvim/marktree.c +++ b/src/nvim/marktree.c @@ -221,9 +221,11 @@ static inline void marktree_putp_aux(MarkTree *b, mtnode_t *x, mtkey_t k) } } -uint64_t marktree_put(MarkTree *b, int row, int col, bool right_gravity) +uint64_t marktree_put(MarkTree *b, int row, int col, bool right_gravity, uint8_t decor_level) { uint64_t id = (b->next_id+=ID_INCR); + assert(decor_level < DECOR_LEVELS); + id = id | ((uint64_t)decor_level << DECOR_OFFSET); uint64_t keyid = id; if (right_gravity) { // order all right gravity keys after the left ones, for effortless @@ -235,9 +237,11 @@ uint64_t marktree_put(MarkTree *b, int row, int col, bool right_gravity) } uint64_t marktree_put_pair(MarkTree *b, int start_row, int start_col, bool start_right, int end_row, - int end_col, bool end_right) + int end_col, bool end_right, uint8_t decor_level) { uint64_t id = (b->next_id+=ID_INCR)|PAIRED; + assert(decor_level < DECOR_LEVELS); + id = id | ((uint64_t)decor_level << DECOR_OFFSET); uint64_t start_id = id|(start_right?RIGHT_GRAVITY:0); uint64_t end_id = id|END_FLAG|(end_right?RIGHT_GRAVITY:0); marktree_put_key(b, start_row, start_col, start_id); diff --git a/src/nvim/marktree.h b/src/nvim/marktree.h index 02f73b8052..a1dcdf5164 100644 --- a/src/nvim/marktree.h +++ b/src/nvim/marktree.h @@ -75,4 +75,13 @@ typedef struct { #define MARKTREE_PAIRED_FLAG (((uint64_t)1) << 1) #define MARKTREE_END_FLAG (((uint64_t)1) << 0) +#define DECOR_LEVELS 4 +#define DECOR_OFFSET 61 +#define DECOR_MASK (((uint64_t)(DECOR_LEVELS-1)) << DECOR_OFFSET) + +static inline uint8_t marktree_decor_level(uint64_t id) +{ + return (uint8_t)((id&DECOR_MASK) >> DECOR_OFFSET); +} + #endif // NVIM_MARKTREE_H diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 5b0418ed92..ed28521d80 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -54,7 +54,7 @@ int plines_win(win_T *wp, linenr_T lnum, bool winheight) /// @return Number of filler lines above lnum int win_get_fill(win_T *wp, linenr_T lnum) { - int virt_lines = decor_virtual_lines(wp, lnum); + int virt_lines = decor_virt_lines(wp, lnum, NULL); // be quick when there are no filler lines if (diffopt_filler()) { @@ -69,7 +69,7 @@ int win_get_fill(win_T *wp, linenr_T lnum) bool win_may_fill(win_T *wp) { - return (wp->w_p_diff && diffopt_filler()) || wp->w_buffer->b_virt_line_mark; + return (wp->w_p_diff && diffopt_filler()) || wp->w_buffer->b_virt_line_blocks; } /// @param winheight when true limit to window height diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 04542b0e47..fcd428ba1b 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -2372,11 +2372,12 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc filler_lines = 0; area_highlighting = true; } - int virtual_lines = decor_virtual_lines(wp, lnum); - filler_lines += virtual_lines; + VirtLines virt_lines = KV_INITIAL_VALUE; + int n_virt_lines = decor_virt_lines(wp, lnum, &virt_lines); + filler_lines += n_virt_lines; if (lnum == wp->w_topline) { filler_lines = wp->w_topfill; - virtual_lines = MIN(virtual_lines, filler_lines); + n_virt_lines = MIN(n_virt_lines, filler_lines); } filler_todo = filler_lines; @@ -2904,7 +2905,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc if (draw_state == WL_SBR - 1 && n_extra == 0) { draw_state = WL_SBR; - if (filler_todo > filler_lines - virtual_lines) { + if (filler_todo > filler_lines - n_virt_lines) { // TODO(bfredl): check this doesn't inhibit TUI-style // clear-to-end-of-line. c_extra = ' '; @@ -4423,12 +4424,12 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc int draw_col = col - boguscols; if (filler_todo > 0) { - int index = filler_todo - (filler_lines - virtual_lines); + int index = filler_todo - (filler_lines - n_virt_lines); if (index > 0) { - int fpos = kv_size(buf->b_virt_lines) - index; - assert(fpos >= 0); - int offset = buf->b_virt_line_leftcol ? 0 : win_col_offset; - draw_virt_text_item(buf, offset, kv_A(buf->b_virt_lines, fpos), + int i = kv_size(virt_lines) - index; + assert(i >= 0); + int offset = kv_A(virt_lines, i).left_col ? 0 : win_col_offset; + draw_virt_text_item(buf, offset, kv_A(virt_lines, i).line, kHlModeReplace, grid->Columns, offset); } } else { @@ -4506,6 +4507,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc cap_col = 0; } + kv_destroy(virt_lines); xfree(p_extra_free); return row; } diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index ffca415282..98340d0ac6 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -309,6 +309,11 @@ func Test_getcompletion() let l = getcompletion('NoMatch', 'dir') call assert_equal([], l) + if glob('~/*') !=# '' + let l = getcompletion('~/', 'dir') + call assert_true(l[0][0] ==# '~') + endif + let l = getcompletion('exe', 'expression') call assert_true(index(l, 'executable(') >= 0) let l = getcompletion('kill', 'expression') @@ -422,6 +427,16 @@ func Test_getcompletion() let l = getcompletion('call paint', 'cmdline') call assert_equal([], l) + func T(a, c, p) + return "oneA\noneB\noneC" + endfunc + command -nargs=1 -complete=custom,T MyCmd + let l = getcompletion('MyCmd ', 'cmdline') + call assert_equal(['oneA', 'oneB', 'oneC'], l) + + delcommand MyCmd + delfunc T + " For others test if the name is recognized. let names = ['buffer', 'environment', 'file_in_path', 'mapping', 'tag', 'tag_listfiles', 'user'] if has('cmdline_hist') diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 8074f91215..40c33d9a4d 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -901,15 +901,15 @@ if (h->n_buckets < new_n_buckets) { // expand screen:expect{grid=[[ if (h->n_buckets < new_n_buckets) { // expand | khkey_t *new_keys = (khkey_t *) | + {1:>> }{2:krealloc}: change the size of an allocation | {3:krealloc}((void *)h->keys, new_n_buckets * sizeof(k| hkey_t)); | h->keys = new_keys; | if (kh_is_map && val_size) { | - char *new_vals = {3:krealloc}( h->vals_buf, new_n_| - buck^ets * val_size); | + ^char *new_vals = {3:krealloc}( h->vals_buf, new_n_| + buckets * val_size); | {5:^^ REVIEW:}{6: new_vals variable seems unneccesary?} | h->vals_buf = new_vals; | - } | | ]]} @@ -1235,6 +1235,7 @@ if (h->n_buckets < new_n_buckets) { // expand | ]]} + meths.buf_clear_namespace(0, ns, 0, -1) meths.buf_set_extmark(0, ns, 2, 0, { virt_lines={ {{"Some special", "Special"}}; diff --git a/test/unit/marktree_spec.lua b/test/unit/marktree_spec.lua index cd9c7bef13..10d02d2eb4 100644 --- a/test/unit/marktree_spec.lua +++ b/test/unit/marktree_spec.lua @@ -97,7 +97,7 @@ describe('marktree', function() for i = 1,100 do for j = 1,100 do local gravitate = (i%2) > 0 - local id = tonumber(lib.marktree_put(tree, j, i, gravitate)) + local id = tonumber(lib.marktree_put(tree, j, i, gravitate, 0)) ok(id > 0) eq(nil, shadow[id]) shadow[id] = {j,i,gravitate} @@ -191,7 +191,7 @@ describe('marktree', function() -- https://github.com/neovim/neovim/pull/14719 lib.marktree_clear(tree) for i = 1,20 do - lib.marktree_put(tree, i, i, false) + lib.marktree_put(tree, i, i, false, 0) end lib.marktree_itr_get(tree, 10, 10, iter) |