From 4a34da82c18e6da1e46d6bf3d21082a6b6c8b947 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 6 Dec 2023 13:34:19 +0100 Subject: perf(column): keep track of number of lines that hold up the 'signcolumn' Problem: The entire marktree needs to be traversed each time a sign is removed from the sentinel line. Solution: Remove sentinel line and instead keep track of the number of lines that hold up the 'signcolumn' in "max_count". Adjust this number for added/removed signs, and set it to 0 when the maximum number of signs on a line changes. Only when "max_count" is decremented to 0 due to sign removal do we need to check the entire buffer. Also replace "invalid_top" and "invalid_bot" with a map of invalid ranges, further reducing the number of lines to be checked. Also improve tree traversal when counting the number of signs. Instead of looping over the to be checked range and counting the overlap for each row, keep track of the overlap in an array and add this to the count. --- src/nvim/buffer.c | 66 ++++--------------------------------------------------- 1 file changed, 4 insertions(+), 62 deletions(-) (limited to 'src/nvim/buffer.c') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 8a594dea92..b5fac15af6 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -750,6 +750,8 @@ void buf_clear(void) { linenr_T line_count = curbuf->b_ml.ml_line_count; extmark_free_all(curbuf); // delete any extmarks + map_destroy(int, curbuf->b_signcols.invalid); + *curbuf->b_signcols.invalid = (Map(int, SignRange)) MAP_INIT; while (!(curbuf->b_ml.ml_flags & ML_EMPTY)) { ml_delete(1, false); } @@ -920,6 +922,8 @@ static void free_buffer_stuff(buf_T *buf, int free_flags) } uc_clear(&buf->b_ucmds); // clear local user commands extmark_free_all(buf); // delete any extmarks + map_destroy(int, buf->b_signcols.invalid); + *buf->b_signcols.invalid = (Map(int, SignRange)) MAP_INIT; map_clear_mode(buf, MAP_ALL_MODES, true, false); // clear local mappings map_clear_mode(buf, MAP_ALL_MODES, true, true); // clear local abbrevs XFREE_CLEAR(buf->b_start_fenc); @@ -1844,7 +1848,6 @@ buf_T *buflist_new(char *ffname_arg, char *sfname_arg, linenr_T lnum, int flags) buf = xcalloc(1, sizeof(buf_T)); // init b: variables buf->b_vars = tv_dict_alloc(); - buf->b_signcols.sentinel = 0; init_var_dict(buf->b_vars, &buf->b_bufvar, VAR_SCOPE); buf_init_changedtick(buf); } @@ -4026,67 +4029,6 @@ char *buf_spname(buf_T *buf) return NULL; } -/// Invalidate the signcolumn if needed after deleting a sign ranging from line1 to line2. -void buf_signcols_del_check(buf_T *buf, linenr_T line1, linenr_T line2) -{ - linenr_T sent = buf->b_signcols.sentinel; - if (sent >= line1 && sent <= line2) { - // When removed sign overlaps the sentinel line, entire buffer needs to be checked. - buf->b_signcols.sentinel = buf->b_signcols.size = 0; - } -} - -/// Invalidate the signcolumn if needed after adding a sign ranging from line1 to line2. -void buf_signcols_add_check(buf_T *buf, linenr_T line1, linenr_T line2) -{ - if (!buf->b_signcols.sentinel) { - return; - } - - linenr_T sent = buf->b_signcols.sentinel; - if (sent >= line1 && sent <= line2) { - // If added sign overlaps sentinel line, increment without invalidating. - if (buf->b_signcols.size == buf->b_signcols.max) { - buf->b_signcols.max++; - } - buf->b_signcols.size++; - return; - } - - if (line1 < buf->b_signcols.invalid_top) { - buf->b_signcols.invalid_top = line1; - } - if (line2 > buf->b_signcols.invalid_bot) { - buf->b_signcols.invalid_bot = line2; - } -} - -int buf_signcols(buf_T *buf, int max) -{ - if (!buf->b_signs_with_text) { - buf->b_signcols.size = 0; - } else if (max <= 1 && buf->b_signs_with_text >= (size_t)max) { - buf->b_signcols.size = max; - } else { - linenr_T sent = buf->b_signcols.sentinel; - if (!sent || max > buf->b_signcols.max) { - // Recheck if the window scoped maximum 'signcolumn' is greater than the - // previous maximum or if there is no sentinel line yet. - buf->b_signcols.invalid_top = sent ? sent : 1; - buf->b_signcols.invalid_bot = sent ? sent : buf->b_ml.ml_line_count; - } - - if (buf->b_signcols.invalid_bot) { - decor_validate_signcols(buf, max); - } - } - - buf->b_signcols.max = max; - buf->b_signcols.invalid_top = MAXLNUM; - buf->b_signcols.invalid_bot = 0; - return buf->b_signcols.size; -} - /// Get "buf->b_fname", use "[No Name]" if it is NULL. char *buf_get_fname(const buf_T *buf) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL -- cgit From 6346987601a28b00564295ee8be0a8b00d9ff911 Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Thu, 7 Dec 2023 23:46:57 +0600 Subject: refactor(options): reduce `findoption()` usage Problem: Many places in the code use `findoption()` to access an option using its name, even if the option index is available. This is very slow because it requires looping through the options array over and over. Solution: Use option index instead of name wherever possible. Also introduce an `OptIndex` enum which contains the index for every option as enum constants, this eliminates the need to pass static option names as strings. --- src/nvim/buffer.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/nvim/buffer.c') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index b5fac15af6..0392ff6ebd 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -3281,7 +3281,7 @@ void maketitle(void) if (*p_titlestring != NUL) { if (stl_syntax & STL_IN_TITLE) { build_stl_str_hl(curwin, buf, sizeof(buf), p_titlestring, - "titlestring", 0, 0, maxlen, NULL, NULL, NULL); + kOptTitlestring, 0, 0, maxlen, NULL, NULL, NULL); title_str = buf; } else { title_str = p_titlestring; @@ -3386,7 +3386,7 @@ void maketitle(void) if (*p_iconstring != NUL) { if (stl_syntax & STL_IN_ICON) { build_stl_str_hl(curwin, icon_str, sizeof(buf), p_iconstring, - "iconstring", 0, 0, 0, NULL, NULL, NULL); + kOptIconstring, 0, 0, 0, NULL, NULL, NULL); } else { icon_str = p_iconstring; } @@ -4147,9 +4147,9 @@ int buf_open_scratch(handle_T bufnr, char *bufname) apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, false, curbuf); (void)setfname(curbuf, bufname, NULL, true); apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, false, curbuf); - set_option_value_give_err("bh", STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL); - set_option_value_give_err("bt", STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL); - set_option_value_give_err("swf", BOOLEAN_OPTVAL(false), OPT_LOCAL); + set_option_value_give_err(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL); + set_option_value_give_err(kOptBuftype, STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL); + set_option_value_give_err(kOptSwapfile, BOOLEAN_OPTVAL(false), OPT_LOCAL); RESET_BINDING(curwin); return OK; } -- cgit From 69bc519b53ebf78fd95c8256468e7d538ebcb948 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Tue, 12 Dec 2023 15:40:21 +0100 Subject: refactor: move non-symbols to defs.h headers --- src/nvim/buffer.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src/nvim/buffer.c') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 0392ff6ebd..75c6545c1d 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -39,7 +39,6 @@ #include "nvim/charset.h" #include "nvim/cmdexpand.h" #include "nvim/cursor.h" -#include "nvim/decoration.h" #include "nvim/diff.h" #include "nvim/digraph.h" #include "nvim/drawscreen.h" -- cgit From 7f6b775b45de5011ff1c44e63e57551566d80704 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Sat, 16 Dec 2023 22:14:28 +0100 Subject: refactor: use `bool` to represent boolean values --- src/nvim/buffer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/buffer.c') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 75c6545c1d..7660093fc2 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -201,13 +201,13 @@ bool buf_ensure_loaded(buf_T *buf) /// @param flags_arg extra flags for readfile() /// /// @return FAIL for failure, OK otherwise. -int open_buffer(int read_stdin, exarg_T *eap, int flags_arg) +int open_buffer(bool read_stdin, exarg_T *eap, int flags_arg) { int flags = flags_arg; int retval = OK; bufref_T old_curbuf; OptInt old_tw = curbuf->b_p_tw; - int read_fifo = false; + bool read_fifo = false; bool silent = shortmess(SHM_FILEINFO); // The 'readonly' flag is only set when BF_NEVERLOADED is being reset. -- cgit From 095bd8d0f8340475319cfa13776d5ec386984859 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Tue, 19 Dec 2023 23:39:33 +0100 Subject: fix(buffer): do not filter help buffer Problem: If a help buffer is opened without legacy syntax set (because treesitter is enabled), Vim strips (some) markup. This means the syntax engine fails to parse (some) syntax if treesitter highlighting is disabled again. Solution: Do not strip the help buffer of markup since (legacy or treesitter) highlighting is always enabled in Nvim. Similarly, remove redundant setting of filetype and give the function a more descriptive name. --- src/nvim/buffer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/buffer.c') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 7660093fc2..c9ae4c3ed3 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -302,9 +302,9 @@ int open_buffer(bool read_stdin, exarg_T *eap, int flags_arg) } #endif - // Help buffer is filtered. + // Help buffer: populate *local-additions* in help.txt if (bt_help(curbuf)) { - fix_help_buffer(); + get_local_additions(); } } else if (read_stdin) { int save_bin = curbuf->b_p_bin; -- cgit From 8533adb4844b771b84dac2141fa2fa60e0487b47 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 21 Dec 2023 16:50:05 +0800 Subject: refactor(IWYU): move decor provider types to decoration_defs.h (#26692) --- src/nvim/buffer.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src/nvim/buffer.c') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index c9ae4c3ed3..d62e73a191 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -94,7 +94,6 @@ #include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/terminal.h" -#include "nvim/types_defs.h" #include "nvim/ui.h" #include "nvim/undo.h" #include "nvim/usercmd.h" -- cgit From af93a74a0f4afa9a3a4f55ffdf28141eaf776d22 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Mon, 18 Dec 2023 10:55:23 +0100 Subject: refactor: run IWYU on entire repo Reference: https://github.com/neovim/neovim/issues/6371. --- src/nvim/buffer.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'src/nvim/buffer.c') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index d62e73a191..24eab644a1 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -43,11 +43,9 @@ #include "nvim/digraph.h" #include "nvim/drawscreen.h" #include "nvim/eval.h" -#include "nvim/eval/typval_defs.h" #include "nvim/eval/vars.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" -#include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" #include "nvim/ex_getln.h" @@ -69,7 +67,6 @@ #include "nvim/mark.h" #include "nvim/mbyte.h" #include "nvim/memfile_defs.h" -#include "nvim/memline_defs.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/move.h" -- cgit From c89292fcb7f2ebf06efb7c1d00c28f34c6f68fec Mon Sep 17 00:00:00 2001 From: dundargoc Date: Thu, 28 Dec 2023 13:42:24 +0100 Subject: refactor: follow style guide --- src/nvim/buffer.c | 89 ++++++++++++++++++++++++------------------------------- 1 file changed, 39 insertions(+), 50 deletions(-) (limited to 'src/nvim/buffer.c') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 24eab644a1..41ab7ae13d 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -128,7 +128,7 @@ int get_highest_fnum(void) /// @param read_stdin read file from stdin, otherwise fifo /// @param eap for forced 'ff' and 'fenc' or NULL /// @param flags extra flags for readfile() -static int read_buffer(int read_stdin, exarg_T *eap, int flags) +static int read_buffer(bool read_stdin, exarg_T *eap, int flags) { int retval = OK; bool silent = shortmess(SHM_FILEINFO); @@ -269,9 +269,7 @@ int open_buffer(bool read_stdin, exarg_T *eap, int flags_arg) if (curbuf->b_ffname != NULL) { #ifdef UNIX int save_bin = curbuf->b_p_bin; - int perm; - - perm = os_getperm(curbuf->b_ffname); + int perm = os_getperm(curbuf->b_ffname); if (perm >= 0 && (0 || S_ISFIFO(perm) || S_ISSOCK(perm) # ifdef OPEN_CHR_FILES @@ -327,7 +325,7 @@ int open_buffer(bool read_stdin, exarg_T *eap, int flags_arg) // if first time loading this buffer, init b_chartab[] if (curbuf->b_flags & BF_NEVERLOADED) { - (void)buf_init_chartab(curbuf, false); + buf_init_chartab(curbuf, false); parse_cino(curbuf); } @@ -937,8 +935,8 @@ void goto_buffer(exarg_T *eap, int start, int dir, int count) if (swap_exists_action == SEA_NONE) { swap_exists_action = SEA_DIALOG; } - (void)do_buffer(*eap->cmd == 's' ? DOBUF_SPLIT : DOBUF_GOTO, - start, dir, count, eap->forceit); + do_buffer(*eap->cmd == 's' ? DOBUF_SPLIT : DOBUF_GOTO, + start, dir, count, eap->forceit); if (swap_exists_action == SEA_QUIT && *eap->cmd == 's') { cleanup_T cs; @@ -1046,7 +1044,7 @@ char *do_bufdel(int command, char *arg, int addr_count, int start_bnr, int end_b int bnr; // buffer number if (addr_count == 0) { - (void)do_buffer(command, DOBUF_CURRENT, FORWARD, 0, forceit); + do_buffer(command, DOBUF_CURRENT, FORWARD, 0, forceit); } else { if (addr_count == 2) { if (*arg) { // both range and argument is not allowed @@ -1125,7 +1123,7 @@ char *do_bufdel(int command, char *arg, int addr_count, int start_bnr, int end_b /// Make the current buffer empty. /// Used when it is wiped out and it's the last buffer. -static int empty_curbuf(int close_others, int forceit, int action) +static int empty_curbuf(bool close_others, int forceit, int action) { buf_T *buf = curbuf; @@ -1197,8 +1195,8 @@ int do_buffer(int action, int start, int dir, int count, int forceit) { buf_T *buf; buf_T *bp; - int unload = (action == DOBUF_UNLOAD || action == DOBUF_DEL - || action == DOBUF_WIPE); + bool unload = (action == DOBUF_UNLOAD || action == DOBUF_DEL + || action == DOBUF_WIPE); switch (start) { case DOBUF_FIRST: @@ -1374,9 +1372,7 @@ int do_buffer(int action, int start, int dir, int count, int forceit) if (au_new_curbuf.br_buf != NULL && bufref_valid(&au_new_curbuf)) { buf = au_new_curbuf.br_buf; } else if (curwin->w_jumplistlen > 0) { - int jumpidx; - - jumpidx = curwin->w_jumplistidx - 1; + int jumpidx = curwin->w_jumplistidx - 1; if (jumpidx < 0) { jumpidx = curwin->w_jumplistlen - 1; } @@ -1665,7 +1661,7 @@ void enter_buffer(buf_T *buf) need_fileinfo = true; // display file info after redraw } // check if file changed - (void)buf_check_timestamp(curbuf); + buf_check_timestamp(curbuf); curwin->w_topline = 1; curwin->w_topfill = 0; @@ -1690,12 +1686,12 @@ void enter_buffer(buf_T *buf) do_autochdir(); if (curbuf->b_kmap_state & KEYMAP_INIT) { - (void)keymap_init(); + keymap_init(); } // May need to set the spell language. Can only do this after the buffer // has been properly setup. if (!curbuf->b_help && curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) { - (void)parse_spelllang(curwin); + parse_spelllang(curwin); } curbuf->b_last_used = time(NULL); @@ -1961,7 +1957,7 @@ bool curbuf_reusable(void) /// Free the memory for the options of a buffer. /// If "free_p_ff" is true also free 'fileformat', 'buftype' and /// 'fileencoding'. -void free_buf_options(buf_T *buf, int free_p_ff) +void free_buf_options(buf_T *buf, bool free_p_ff) { if (free_p_ff) { clear_string_option(&buf->b_p_fenc); @@ -2224,7 +2220,7 @@ int buflist_findpat(const char *pattern, const char *pattern_end, bool unlisted, return -1; } char *patend = pat + strlen(pat) - 1; - int toggledollar = (patend > pat && *patend == '$'); + bool toggledollar = (patend > pat && *patend == '$'); // First try finding a listed buffer. If not found and "unlisted" // is true, try finding an unlisted buffer. @@ -2755,8 +2751,6 @@ linenr_T buflist_findlnum(buf_T *buf) void buflist_list(exarg_T *eap) { buf_T *buf = firstbuf; - int len; - int i; garray_T buflist; buf_T **buflist_data = NULL; @@ -2821,21 +2815,21 @@ void buflist_list(exarg_T *eap) } msg_putchar('\n'); - len = vim_snprintf(IObuff, IOSIZE - 20, "%3d%c%c%c%c%c \"%s\"", - buf->b_fnum, - buf->b_p_bl ? ' ' : 'u', - buf == curbuf ? '%' : (curwin->w_alt_fnum == buf->b_fnum ? '#' : ' '), - buf->b_ml.ml_mfp == NULL ? ' ' : (buf->b_nwindows == 0 ? 'h' : 'a'), - ro_char, - changed_char, - NameBuff); + int len = vim_snprintf(IObuff, IOSIZE - 20, "%3d%c%c%c%c%c \"%s\"", + buf->b_fnum, + buf->b_p_bl ? ' ' : 'u', + buf == curbuf ? '%' : (curwin->w_alt_fnum == buf->b_fnum ? '#' : ' '), + buf->b_ml.ml_mfp == NULL ? ' ' : (buf->b_nwindows == 0 ? 'h' : 'a'), + ro_char, + changed_char, + NameBuff); if (len > IOSIZE - 20) { len = IOSIZE - 20; } // put "line 999" in column 40 or after the file name - i = 40 - vim_strsize(IObuff); + int i = 40 - vim_strsize(IObuff); do { IObuff[len++] = ' '; } while (--i > 0 && len < IOSIZE - 18); @@ -3134,7 +3128,7 @@ static bool buf_same_file_id(buf_T *buf, FileID *file_id) /// Print info about the current buffer. /// /// @param fullname when non-zero print full path -void fileinfo(int fullname, int shorthelp, int dont_truncate) +void fileinfo(int fullname, int shorthelp, bool dont_truncate) { char *name; int n; @@ -3208,7 +3202,7 @@ void fileinfo(int fullname, int shorthelp, int dont_truncate) (int)curwin->w_cursor.col + 1, (int)curwin->w_virtcol + 1); } - (void)append_arg_number(curwin, buffer, IOSIZE); + append_arg_number(curwin, buffer, IOSIZE); if (dont_truncate) { // Temporarily set msg_scroll to avoid the message being truncated. @@ -3251,7 +3245,6 @@ void maketitle(void) char *title_str = NULL; char *icon_str = NULL; int maxlen = 0; - int len; char buf[IOSIZE]; if (!redrawing()) { @@ -3374,7 +3367,7 @@ void maketitle(void) #undef SPACE_FOR_ARGNR } } - int mustset = value_change(title_str, &lasttitle); + bool mustset = value_change(title_str, &lasttitle); if (p_icon) { icon_str = buf; @@ -3394,7 +3387,7 @@ void maketitle(void) } *icon_str = NUL; // Truncate name at 100 bytes. - len = (int)strlen(buf_p); + int len = (int)strlen(buf_p); if (len > 100) { len -= 100; len += utf_cp_tail_off(buf_p, buf_p + len) + 1; @@ -3556,16 +3549,12 @@ bool bt_prompt(buf_T *buf) /// Open a window for a number of buffers. void ex_buffer_all(exarg_T *eap) { - buf_T *buf; win_T *wp, *wpnext; int split_ret = OK; - bool p_ea_save; int open_wins = 0; - int r; linenr_T count; // Maximum number of windows to open. int all; // When true also load inactive buffers. int had_tab = cmdmod.cmod_tab; - tabpage_T *tpnext; if (eap->addr_count == 0) { // make as many windows as possible count = 9999; @@ -3590,7 +3579,7 @@ void ex_buffer_all(exarg_T *eap) goto_tabpage_tp(first_tabpage, true, true); } while (true) { - tpnext = curtab->tp_next; + tabpage_T *tpnext = curtab->tp_next; // Try to close floating windows first for (wp = lastwin->w_floating ? lastwin : firstwin; wp != NULL; wp = wpnext) { wpnext = wp->w_floating @@ -3635,7 +3624,7 @@ void ex_buffer_all(exarg_T *eap) // lastwin may be aucmd_win win_enter(lastwin_nofloating(), false); autocmd_no_leave++; - for (buf = firstbuf; buf != NULL && open_wins < count; buf = buf->b_next) { + for (buf_T *buf = firstbuf; buf != NULL && open_wins < count; buf = buf->b_next) { // Check if this buffer needs a window if ((!all && buf->b_ml.ml_mfp == NULL) || !buf->b_p_bl) { continue; @@ -3665,7 +3654,7 @@ void ex_buffer_all(exarg_T *eap) bufref_T bufref; set_bufref(&bufref, buf); // Split the window and put the buffer in it. - p_ea_save = p_ea; + bool p_ea_save = p_ea; p_ea = true; // use space from all windows split_ret = win_split(0, WSP_ROOM | WSP_BELOW); open_wins++; @@ -3706,7 +3695,7 @@ void ex_buffer_all(exarg_T *eap) os_breakcheck(); if (got_int) { - (void)vgetc(); // only break the file loading, not the rest + vgetc(); // only break the file loading, not the rest break; } // Autocommands deleted the buffer or aborted script processing!!! @@ -3724,8 +3713,8 @@ void ex_buffer_all(exarg_T *eap) // Close superfluous windows. for (wp = lastwin; open_wins > count;) { - r = (buf_hide(wp->w_buffer) || !bufIsChanged(wp->w_buffer) - || autowrite(wp->w_buffer, false) == OK) && !is_aucmd_win(wp); + bool r = (buf_hide(wp->w_buffer) || !bufIsChanged(wp->w_buffer) + || autowrite(wp->w_buffer, false) == OK) && !is_aucmd_win(wp); if (!win_valid(wp)) { // BufWrite Autocommands made the window invalid, start over wp = lastwin; @@ -3798,8 +3787,8 @@ static int chk_modeline(linenr_T lnum, int flags) int prev = -1; for (s = ml_get(lnum); *s != NUL; s++) { if (prev == -1 || ascii_isspace(prev)) { - if ((prev != -1 && strncmp(s, "ex:", (size_t)3) == 0) - || strncmp(s, "vi:", (size_t)3) == 0) { + if ((prev != -1 && strncmp(s, "ex:", 3) == 0) + || strncmp(s, "vi:", 3) == 0) { break; } // Accept both "vim" and "Vim". @@ -3864,8 +3853,8 @@ static int chk_modeline(linenr_T lnum, int flags) // "vi:set opt opt opt: foo" -- foo not interpreted // "vi:opt opt opt: foo" -- foo interpreted // Accept "se" for compatibility with Elvis. - if (strncmp(s, "set ", (size_t)4) == 0 - || strncmp(s, "se ", (size_t)3) == 0) { + if (strncmp(s, "set ", 4) == 0 + || strncmp(s, "se ", 3) == 0) { if (*e != ':') { // no terminating ':'? break; } @@ -4140,7 +4129,7 @@ int buf_open_scratch(handle_T bufnr, char *bufname) return FAIL; } apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, false, curbuf); - (void)setfname(curbuf, bufname, NULL, true); + setfname(curbuf, bufname, NULL, true); apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, false, curbuf); set_option_value_give_err(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL); set_option_value_give_err(kOptBuftype, STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL); -- cgit From b49d4e18a67d16548fa72013237a87564656170e Mon Sep 17 00:00:00 2001 From: dundargoc Date: Mon, 1 Jan 2024 15:56:00 +0100 Subject: refactor: remove redundant NOLINT comments --- src/nvim/buffer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/buffer.c') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 41ab7ae13d..c22aaec3df 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -276,7 +276,7 @@ int open_buffer(bool read_stdin, exarg_T *eap, int flags_arg) || (S_ISCHR(perm) && is_dev_fd_file(curbuf->b_ffname)) # endif - )) { // NOLINT(whitespace/parens) + )) { read_fifo = true; } if (read_fifo) { @@ -2145,7 +2145,7 @@ buf_T *buflist_findname_exp(char *fname) #else false #endif - ); // NOLINT(whitespace/parens) + ); if (ffname != NULL) { buf = buflist_findname(ffname); xfree(ffname); -- cgit From 1813661a6197c76ea6621284570aca1d56597099 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Thu, 4 Jan 2024 15:38:16 +0100 Subject: refactor(IWYU): fix headers Remove `export` pramgas from defs headers as it causes IWYU to believe that the definitions from the defs headers comes from main header, which is not what we really want. --- src/nvim/buffer.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) (limited to 'src/nvim/buffer.c') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index c22aaec3df..b29e87e91b 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -32,6 +32,7 @@ #include "nvim/ascii_defs.h" #include "nvim/assert_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" #include "nvim/buffer_updates.h" #include "nvim/change.h" @@ -43,21 +44,26 @@ #include "nvim/digraph.h" #include "nvim/drawscreen.h" #include "nvim/eval.h" +#include "nvim/eval/typval.h" #include "nvim/eval/vars.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" +#include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" +#include "nvim/ex_eval_defs.h" #include "nvim/ex_getln.h" #include "nvim/extmark.h" #include "nvim/file_search.h" #include "nvim/fileio.h" #include "nvim/fold.h" #include "nvim/garray.h" +#include "nvim/garray_defs.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/hashtab.h" +#include "nvim/hashtab_defs.h" #include "nvim/help.h" #include "nvim/indent.h" #include "nvim/indent_c.h" @@ -65,25 +71,33 @@ #include "nvim/map_defs.h" #include "nvim/mapping.h" #include "nvim/mark.h" +#include "nvim/mark_defs.h" #include "nvim/mbyte.h" #include "nvim/memfile_defs.h" +#include "nvim/memline.h" +#include "nvim/memline_defs.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/move.h" #include "nvim/normal.h" #include "nvim/option.h" +#include "nvim/option_defs.h" #include "nvim/option_vars.h" #include "nvim/optionstr.h" #include "nvim/os/fs.h" +#include "nvim/os/fs_defs.h" #include "nvim/os/input.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" #include "nvim/os/time.h" #include "nvim/path.h" #include "nvim/plines.h" #include "nvim/pos_defs.h" #include "nvim/quickfix.h" #include "nvim/regexp.h" +#include "nvim/regexp_defs.h" #include "nvim/runtime.h" +#include "nvim/runtime_defs.h" #include "nvim/search.h" #include "nvim/spell.h" #include "nvim/state_defs.h" @@ -4137,3 +4151,49 @@ int buf_open_scratch(handle_T bufnr, char *bufname) RESET_BINDING(curwin); return OK; } + +bool buf_is_empty(buf_T *buf) +{ + return buf->b_ml.ml_line_count == 1 && *ml_get_buf(buf, 1) == '\0'; +} + +/// Increment b:changedtick value +/// +/// Also checks b: for consistency in case of debug build. +/// +/// @param[in,out] buf Buffer to increment value in. +void buf_inc_changedtick(buf_T *const buf) + FUNC_ATTR_NONNULL_ALL +{ + buf_set_changedtick(buf, buf_get_changedtick(buf) + 1); +} + +/// Set b:changedtick, also checking b: for consistency in debug build +/// +/// @param[out] buf Buffer to set changedtick in. +/// @param[in] changedtick New value. +void buf_set_changedtick(buf_T *const buf, const varnumber_T changedtick) + FUNC_ATTR_NONNULL_ALL +{ + typval_T old_val = buf->changedtick_di.di_tv; + +#ifndef NDEBUG + dictitem_T *const changedtick_di = tv_dict_find(buf->b_vars, S_LEN("changedtick")); + assert(changedtick_di != NULL); + assert(changedtick_di->di_tv.v_type == VAR_NUMBER); + assert(changedtick_di->di_tv.v_lock == VAR_FIXED); + // For some reason formatc does not like the below. +# ifndef UNIT_TESTING_LUA_PREPROCESSING + assert(changedtick_di->di_flags == (DI_FLAGS_RO|DI_FLAGS_FIX)); +# endif + assert(changedtick_di == (dictitem_T *)&buf->changedtick_di); +#endif + buf->changedtick_di.di_tv.vval.v_number = changedtick; + + if (tv_dict_is_watched(buf->b_vars)) { + tv_dict_watcher_notify(buf->b_vars, + (char *)buf->changedtick_di.di_key, + &buf->changedtick_di.di_tv, + &old_val); + } +} -- cgit From 967c7abde3c6fa3210a4920a5848a54dc913d851 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Thu, 11 Jan 2024 14:30:12 +0100 Subject: fix(column): keep track of number of lines with number of signs Problem: Some edge cases to the old (pre-#26406) and current "b_signcols" structure result in an incorrectly sized "auto" 'signcolumn'. Solution: * Implement a simpler 'signcolumn' validation strategy by immediately counting the number of signs in a range upon sign insertion and deletion. Decrease in performance here but there is a clear path forward to decreasing this performance hit by moving signs to a dedicated marktree, or by adding meta-data to the existing marktree which may be queried more efficiently? * Also replace "max_count" and keep track of the number of lines with a certain number of signs. This makes it so that it is no longer necessary to scan the entire buffer when the maximum number of signs decreases. This likely makes the commit a net increase in performance. * To ensure correctness we also have re-initialize the count for an edited region that spans multiple lines. Such an edit may move the signs within it. Thus we count and decrement before splicing the marktree and count and increment after. --- src/nvim/buffer.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'src/nvim/buffer.c') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index b29e87e91b..da1e27aebf 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -757,8 +757,6 @@ void buf_clear(void) { linenr_T line_count = curbuf->b_ml.ml_line_count; extmark_free_all(curbuf); // delete any extmarks - map_destroy(int, curbuf->b_signcols.invalid); - *curbuf->b_signcols.invalid = (Map(int, SignRange)) MAP_INIT; while (!(curbuf->b_ml.ml_flags & ML_EMPTY)) { ml_delete(1, false); } @@ -929,8 +927,6 @@ static void free_buffer_stuff(buf_T *buf, int free_flags) } uc_clear(&buf->b_ucmds); // clear local user commands extmark_free_all(buf); // delete any extmarks - map_destroy(int, buf->b_signcols.invalid); - *buf->b_signcols.invalid = (Map(int, SignRange)) MAP_INIT; map_clear_mode(buf, MAP_ALL_MODES, true, false); // clear local mappings map_clear_mode(buf, MAP_ALL_MODES, true, true); // clear local abbrevs XFREE_CLEAR(buf->b_start_fenc); -- cgit From d85f180f26c0570c2510c899a0bf0023ec55a692 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Tue, 15 Aug 2023 19:38:52 +0100 Subject: vim-patch:9.1.0049: Make "[Command Line]" a special buffer name Problem: E95 is possible if a buffer called "[Command Line]" already exists when opening the cmdwin. This can also happen if the cmdwin's buffer could not be deleted when closing. Solution: Un-name the cmdwin buffer, and give it a special name instead, similar to what's done for quickfix buffers and for unnamed prompt and scratch buffers. As a result, BufFilePre/Post are no longer fired when opening the cmdwin. Add a "command" key to the dictionary returned by getbufinfo() to differentiate the cmdwin buffer instead. (Sean Dewar) Cherry-pick test_normal changes from v9.0.0954. https://github.com/vim/vim/commit/1fb41032060df09ca2640dc49541f11062f6dfaa --- src/nvim/buffer.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'src/nvim/buffer.c') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index da1e27aebf..d597d82ee2 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -4012,6 +4012,9 @@ char *buf_spname(buf_T *buf) if (buf->b_fname != NULL) { return buf->b_fname; } + if (buf == cmdwin_buf) { + return _("[Command Line]"); + } if (bt_prompt(buf)) { return _("[Prompt]"); } @@ -4129,6 +4132,7 @@ void wipe_buffer(buf_T *buf, bool aucmd) /// - Always considered 'nomodified' /// /// @param bufnr Buffer to switch to, or 0 to create a new buffer. +/// @param bufname Buffer name, or NULL. /// /// @see curbufIsChanged() /// @@ -4138,9 +4142,11 @@ int buf_open_scratch(handle_T bufnr, char *bufname) if (do_ecmd((int)bufnr, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE, NULL) == FAIL) { return FAIL; } - apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, false, curbuf); - setfname(curbuf, bufname, NULL, true); - apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, false, curbuf); + if (bufname != NULL) { + apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, false, curbuf); + setfname(curbuf, bufname, NULL, true); + apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, false, curbuf); + } set_option_value_give_err(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL); set_option_value_give_err(kOptBuftype, STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL); set_option_value_give_err(kOptSwapfile, BOOLEAN_OPTVAL(false), OPT_LOCAL); -- cgit From af6537bc66e2ea506b9640c34720ef85d4704be6 Mon Sep 17 00:00:00 2001 From: vE5li Date: Mon, 29 Jan 2024 04:08:51 +0100 Subject: fix(jumplist): Ctrl+o, Ctrl+i weird behavior when deleting buffers #25461 Problem: - Navigation is not always symmetric: pressing Ctrl+o n times followed by Ctrl+i n times does not always gets me back to where I started. - Invalid buffers are not skipped by Ctrl+i/o, I have to press Ctrl+i/o multiple times to get to the next/previous buffer. Solution: - Remove all entries of a buffer from the jump list when deleting it. - Don't add a new entry to the jump list if the next buffer to be displayed is already in the jump list. Closes #25365 --- src/nvim/buffer.c | 117 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 82 insertions(+), 35 deletions(-) (limited to 'src/nvim/buffer.c') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index d597d82ee2..00cb7272c0 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -1183,6 +1183,32 @@ static int empty_curbuf(bool close_others, int forceit, int action) return retval; } +/// Remove every jump list entry referring to a given buffer. +/// This function will also adjust the current jump list index. +void buf_remove_from_jumplist(buf_T *deleted_buf) +{ + // Remove all jump list entries that match the deleted buffer. + for (int i = curwin->w_jumplistlen - 1; i >= 0; i--) { + buf_T *buf = buflist_findnr(curwin->w_jumplist[i].fmark.fnum); + + if (buf == deleted_buf) { + // Found an entry that we want to delete. + curwin->w_jumplistlen -= 1; + + // If the current jump list index behind the entry we want to + // delete, move it back by one. + if (curwin->w_jumplistidx > i && curwin->w_jumplistidx > 0) { + curwin->w_jumplistidx -= 1; + } + + // Actually remove the entry from the jump list. + for (int d = i; d < curwin->w_jumplistlen; d++) { + curwin->w_jumplist[d] = curwin->w_jumplist[d + 1]; + } + } + } +} + /// Implementation of the commands for the buffer list. /// /// action == DOBUF_GOTO go to specified buffer @@ -1205,6 +1231,7 @@ int do_buffer(int action, int start, int dir, int count, int forceit) { buf_T *buf; buf_T *bp; + bool update_jumplist = true; bool unload = (action == DOBUF_UNLOAD || action == DOBUF_DEL || action == DOBUF_WIPE); @@ -1362,7 +1389,11 @@ int do_buffer(int action, int start, int dir, int count, int forceit) // If the buffer to be deleted is not the current one, delete it here. if (buf != curbuf) { + // Remove the buffer to be deleted from the jump list. + buf_remove_from_jumplist(buf); + close_windows(buf, false); + if (buf != curbuf && bufref_valid(&bufref) && buf->b_nwindows <= 0) { close_buffer(NULL, buf, action, false, false); } @@ -1382,40 +1413,53 @@ int do_buffer(int action, int start, int dir, int count, int forceit) if (au_new_curbuf.br_buf != NULL && bufref_valid(&au_new_curbuf)) { buf = au_new_curbuf.br_buf; } else if (curwin->w_jumplistlen > 0) { - int jumpidx = curwin->w_jumplistidx - 1; - if (jumpidx < 0) { - jumpidx = curwin->w_jumplistlen - 1; - } + // Remove the current buffer from the jump list. + buf_remove_from_jumplist(curbuf); + + // It's possible that we removed all jump list entries, in that case we need to try another + // approach + if (curwin->w_jumplistlen > 0) { + // If the index is the same as the length, the current position was not yet added to the jump + // list. So we can safely go back to the last entry and search from there. + if (curwin->w_jumplistidx == curwin->w_jumplistlen) { + curwin->w_jumplistidx = curwin->w_jumplistlen - 1; + } - forward = jumpidx; - while (jumpidx != curwin->w_jumplistidx) { - buf = buflist_findnr(curwin->w_jumplist[jumpidx].fmark.fnum); - if (buf != NULL) { - // Skip current and unlisted bufs. Also skip a quickfix - // buffer, it might be deleted soon. - if (buf == curbuf || !buf->b_p_bl || bt_quickfix(buf)) { - buf = NULL; - } else if (buf->b_ml.ml_mfp == NULL) { - // skip unloaded buf, but may keep it for later - if (bp == NULL) { - bp = buf; + int jumpidx = curwin->w_jumplistidx; + + forward = jumpidx; + do { + buf = buflist_findnr(curwin->w_jumplist[jumpidx].fmark.fnum); + + if (buf != NULL) { + // Skip unlisted bufs. Also skip a quickfix + // buffer, it might be deleted soon. + if (!buf->b_p_bl || bt_quickfix(buf)) { + buf = NULL; + } else if (buf->b_ml.ml_mfp == NULL) { + // skip unloaded buf, but may keep it for later + if (bp == NULL) { + bp = buf; + } + buf = NULL; } - buf = NULL; } - } - if (buf != NULL) { // found a valid buffer: stop searching - break; - } - // advance to older entry in jump list - if (!jumpidx && curwin->w_jumplistidx == curwin->w_jumplistlen) { - break; - } - if (--jumpidx < 0) { - jumpidx = curwin->w_jumplistlen - 1; - } - if (jumpidx == forward) { // List exhausted for sure - break; - } + if (buf != NULL) { // found a valid buffer: stop searching + curwin->w_jumplistidx = jumpidx; + update_jumplist = false; + break; + } + // advance to older entry in jump list + if (!jumpidx && curwin->w_jumplistidx == curwin->w_jumplistlen) { + break; + } + if (--jumpidx < 0) { + jumpidx = curwin->w_jumplistlen - 1; + } + if (jumpidx == forward) { // List exhausted for sure + break; + } + } while (jumpidx != curwin->w_jumplistidx); } } @@ -1511,7 +1555,7 @@ int do_buffer(int action, int start, int dir, int count, int forceit) } // Go to the other buffer. - set_curbuf(buf, action); + set_curbuf(buf, action, update_jumplist); if (action == DOBUF_SPLIT) { RESET_BINDING(curwin); // reset 'scrollbind' and 'cursorbind' @@ -1533,14 +1577,17 @@ int do_buffer(int action, int start, int dir, int count, int forceit) /// DOBUF_UNLOAD unload it /// DOBUF_DEL delete it /// DOBUF_WIPE wipe it out -void set_curbuf(buf_T *buf, int action) +void set_curbuf(buf_T *buf, int action, bool update_jumplist) { buf_T *prevbuf; int unload = (action == DOBUF_UNLOAD || action == DOBUF_DEL || action == DOBUF_WIPE); OptInt old_tw = curbuf->b_p_tw; - setpcmark(); + if (update_jumplist) { + setpcmark(); + } + if ((cmdmod.cmod_flags & CMOD_KEEPALT) == 0) { curwin->w_alt_fnum = curbuf->b_fnum; // remember alternate file } @@ -3675,7 +3722,7 @@ void ex_buffer_all(exarg_T *eap) // Open the buffer in this window. swap_exists_action = SEA_DIALOG; - set_curbuf(buf, DOBUF_GOTO); + set_curbuf(buf, DOBUF_GOTO, false); if (!bufref_valid(&bufref)) { // Autocommands deleted the buffer. swap_exists_action = SEA_NONE; -- cgit From af5beac1bd7a68ff0a4e1a944853bacd6a6c0745 Mon Sep 17 00:00:00 2001 From: bfredl Date: Thu, 8 Feb 2024 13:40:35 +0100 Subject: refactor(api): refactor more api functions to use arena return Currently having two separate memory strategies for API return values is a bit unnecessary, and mostly a consequence of converting the hot spot cases which needed it first. But there is really no downside to using arena everywhere (which implies also directly using strings which are allocated earlier or even statically, without copy). There only restriction is we need to know the size of arrays in advance, but this info can often be passed on from some earlier stage if it is missing. This collects some "small" cases. The more complex stuff will get a PR each. --- src/nvim/buffer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/buffer.c') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 00cb7272c0..7c0d415099 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -3326,7 +3326,7 @@ void maketitle(void) if (*p_titlestring != NUL) { if (stl_syntax & STL_IN_TITLE) { build_stl_str_hl(curwin, buf, sizeof(buf), p_titlestring, - kOptTitlestring, 0, 0, maxlen, NULL, NULL, NULL); + kOptTitlestring, 0, 0, maxlen, NULL, NULL, NULL, NULL); title_str = buf; } else { title_str = p_titlestring; @@ -3431,7 +3431,7 @@ void maketitle(void) if (*p_iconstring != NUL) { if (stl_syntax & STL_IN_ICON) { build_stl_str_hl(curwin, icon_str, sizeof(buf), p_iconstring, - kOptIconstring, 0, 0, 0, NULL, NULL, NULL); + kOptIconstring, 0, 0, 0, NULL, NULL, NULL, NULL); } else { icon_str = p_iconstring; } -- cgit From f1f8fa850f741b7c35b8ff9c02ac2810a75e25b1 Mon Sep 17 00:00:00 2001 From: Will Hopkins Date: Sat, 10 Feb 2024 13:06:01 -0800 Subject: refactor: rename w_float_config to w_config #27419 Follows up on rename of `FloatConfig` to `WinConfig` in #27397. --- src/nvim/buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/buffer.c') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 7c0d415099..38c3ee13aa 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -2773,7 +2773,7 @@ void get_winopts(buf_T *buf) curwin->w_changelistidx = wip->wi_changelistidx; } - if (curwin->w_float_config.style == kWinStyleMinimal) { + if (curwin->w_config.style == kWinStyleMinimal) { didset_window_options(curwin, false); win_set_minimal_style(curwin); } -- cgit From 163add40b8b98b91dfb8eff589f49dc75f1032ea Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 16 Feb 2024 06:56:12 +0800 Subject: vim-patch:9.1.0113: duplicate code when cleaning undo stack Problem: duplicate code when cleaning undo stack Solution: refactor undo cleanup into a single public function related: vim/vim#13928 https://github.com/vim/vim/commit/9071ed8107244e0c56a16b77d1c28e975cb21dd2 Co-authored-by: Christian Brabandt --- src/nvim/buffer.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/nvim/buffer.c') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 38c3ee13aa..bf1d2ac6dd 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -843,8 +843,9 @@ void buf_freeall(buf_T *buf, int flags) ml_close(buf, true); // close and delete the memline/memfile buf->b_ml.ml_line_count = 0; // no lines in buffer if ((flags & BFA_KEEP_UNDO) == 0) { - u_blockfree(buf); // free the memory allocated for undo - u_clearall(buf); // reset all undo information + // free the memory allocated for undo + // and reset all undo information + u_clearallandblockfree(buf); } syntax_clear(&buf->b_s); // reset syntax info buf->b_flags &= ~BF_READERR; // a read error is no longer relevant -- cgit From 04f723f1a5780196a5356d3bbca17438c572ffa1 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 24 Feb 2024 22:59:28 +0800 Subject: vim-patch:9.1.0131: buffer-completion may not always find all matches (#27610) Problem: buffer-completion code too complicated and does not always find all matches (irisjae) Solution: do not try to anchor pattern to beginning of line or directory-separator, always return all matches Note: we are considering the non-fuzzy buffer-matching here. Currently, the buffer-completion code makes 2 attempts to match a pattern against the list of available patterns. First try is to match the pattern and anchor it to either the beginning of the file name or at a directory-separator (// or \\). When a match is found, Vim returns the matching buffers and does not try to find a match anywhere within a buffer name. So if you have opened two buffers like /tmp/Foobar.c and /tmp/MyFoobar.c using `:b Foo` will only complete to the first filename, but not the second (the same happens with `getcompletion('Foo', 'buffer')`). It may make sense, that completion priorities buffer names at directory boundaries, but it inconsistent, may cause confusion why a certain buffer name is not completed when typing `:b Foo` which returns only a single file name and then pressing Enter (to switch to that buffer), Vim will error with 'E93: More than one match for Foo'). Similar things may happen when wiping the /tmp/Foobar.c pattern and afterwards the completion starts completing other buffers. So let's simplify the code and always match the pattern anywhere in the buffer name, do not try to favor matches at directory boundaries. This is also simplifies the code a bit, we do not need to run over the list of buffers several times, but only twice. fixes vim/vim#13894 closes: vim/vim#14082 https://github.com/vim/vim/commit/0dc0bff000fd804c6b0778ccc4554a4e4c82c8c9 Cherry-pick test_cmdline.vim from patch 9.1.0019 as it already passes. Co-authored-by: Christian Brabandt --- src/nvim/buffer.c | 176 +++++++++++++++++++++++++----------------------------- 1 file changed, 82 insertions(+), 94 deletions(-) (limited to 'src/nvim/buffer.c') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index bf1d2ac6dd..ac6ccf223c 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -2377,10 +2377,8 @@ static int buf_time_compare(const void *s1, const void *s2) /// @return OK if matches found, FAIL otherwise. int ExpandBufnames(char *pat, int *num_file, char ***file, int options) { - int count = 0; - int round; - char *p; bufmatch_T *matches = NULL; + bool to_free = false; *num_file = 0; // return values in case of FAIL *file = NULL; @@ -2392,125 +2390,115 @@ int ExpandBufnames(char *pat, int *num_file, char ***file, int options) const bool fuzzy = cmdline_fuzzy_complete(pat); char *patc = NULL; + fuzmatch_str_T *fuzmatch = NULL; + regmatch_T regmatch; + // Make a copy of "pat" and change "^" to "\(^\|[\/]\)" (if doing regular // expression matching) if (!fuzzy) { - if (*pat == '^') { - patc = xmalloc(strlen(pat) + 11); - STRCPY(patc, "\\(^\\|[\\/]\\)"); - STRCPY(patc + 11, pat + 1); + if (*pat == '^' && pat[1] != NUL) { + patc = xstrdup(pat + 1); + to_free = true; + } else if (*pat == '^') { + patc = ""; } else { patc = pat; } + regmatch.regprog = vim_regcomp(patc, RE_MAGIC); } - fuzmatch_str_T *fuzmatch = NULL; - // attempt == 0: try match with '\<', match at start of word - // attempt == 1: try match without '\<', match anywhere - for (int attempt = 0; attempt <= (fuzzy ? 0 : 1); attempt++) { - regmatch_T regmatch; - if (!fuzzy) { - if (attempt > 0 && patc == pat) { - break; // there was no anchor, no need to try again + int count = 0; + int score = 0; + // round == 1: Count the matches. + // round == 2: Build the array to keep the matches. + for (int round = 1; round <= 2; round++) { + count = 0; + FOR_ALL_BUFFERS(buf) { + if (!buf->b_p_bl) { // skip unlisted buffers + continue; } - regmatch.regprog = vim_regcomp(patc + attempt * 11, RE_MAGIC); - } - - int score = 0; - // round == 1: Count the matches. - // round == 2: Build the array to keep the matches. - for (round = 1; round <= 2; round++) { - count = 0; - FOR_ALL_BUFFERS(buf) { - if (!buf->b_p_bl) { // skip unlisted buffers + if (options & BUF_DIFF_FILTER) { + // Skip buffers not suitable for + // :diffget or :diffput completion. + if (buf == curbuf || !diff_mode_buf(buf)) { continue; } - if (options & BUF_DIFF_FILTER) { - // Skip buffers not suitable for - // :diffget or :diffput completion. - if (buf == curbuf || !diff_mode_buf(buf)) { - continue; - } - } + } - if (!fuzzy) { - if (regmatch.regprog == NULL) { - // invalid pattern, possibly after recompiling - if (patc != pat) { - xfree(patc); - } - return FAIL; - } - p = buflist_match(®match, buf, p_wic); - } else { - p = NULL; - // first try matching with the short file name - if ((score = fuzzy_match_str(buf->b_sfname, pat)) != 0) { - p = buf->b_sfname; + char *p = NULL; + if (!fuzzy) { + if (regmatch.regprog == NULL) { + // invalid pattern, possibly after recompiling + if (to_free) { + xfree(patc); } - if (p == NULL) { - // next try matching with the full path file name - if ((score = fuzzy_match_str(buf->b_ffname, pat)) != 0) { - p = buf->b_ffname; - } - } - } - - if (p == NULL) { - continue; - } - - if (round == 1) { - count++; - continue; + return FAIL; } - - if (options & WILD_HOME_REPLACE) { - p = home_replace_save(buf, p); - } else { - p = xstrdup(p); + p = buflist_match(®match, buf, p_wic); + } else { + p = NULL; + // first try matching with the short file name + if ((score = fuzzy_match_str(buf->b_sfname, pat)) != 0) { + p = buf->b_sfname; } - - if (!fuzzy) { - if (matches != NULL) { - matches[count].buf = buf; - matches[count].match = p; - count++; - } else { - (*file)[count++] = p; + if (p == NULL) { + // next try matching with the full path file name + if ((score = fuzzy_match_str(buf->b_ffname, pat)) != 0) { + p = buf->b_ffname; } - } else { - fuzmatch[count].idx = count; - fuzmatch[count].str = p; - fuzmatch[count].score = score; - count++; } } - if (count == 0) { // no match found, break here - break; + + if (p == NULL) { + continue; } + if (round == 1) { - if (!fuzzy) { - *file = xmalloc((size_t)count * sizeof(**file)); - if (options & WILD_BUFLASTUSED) { - matches = xmalloc((size_t)count * sizeof(*matches)); - } + count++; + continue; + } + + if (options & WILD_HOME_REPLACE) { + p = home_replace_save(buf, p); + } else { + p = xstrdup(p); + } + + if (!fuzzy) { + if (matches != NULL) { + matches[count].buf = buf; + matches[count].match = p; + count++; } else { - fuzmatch = xmalloc((size_t)count * sizeof(fuzmatch_str_T)); + (*file)[count++] = p; } + } else { + fuzmatch[count].idx = count; + fuzmatch[count].str = p; + fuzmatch[count].score = score; + count++; } } - - if (!fuzzy) { - vim_regfree(regmatch.regprog); - if (count) { // match(es) found, break here - break; + if (count == 0) { // no match found, break here + break; + } + if (round == 1) { + if (!fuzzy) { + *file = xmalloc((size_t)count * sizeof(**file)); + if (options & WILD_BUFLASTUSED) { + matches = xmalloc((size_t)count * sizeof(*matches)); + } + } else { + fuzmatch = xmalloc((size_t)count * sizeof(fuzmatch_str_T)); } } } - if (!fuzzy && patc != pat) { - xfree(patc); + if (!fuzzy) { + vim_regfree(regmatch.regprog); + if (to_free) { + xfree(patc); + } } if (!fuzzy) { -- cgit From ad5a155b1f4b387d3aaa54c91d0146cb0287bb9f Mon Sep 17 00:00:00 2001 From: VanaIgr Date: Mon, 26 Feb 2024 04:12:55 -0600 Subject: fix(mbyte): fix bugs in utf_cp_*_off() functions Problems: - Illegal bytes after valid UTF-8 char cause utf_cp_*_off() to fail. - When stream isn't NUL-terminated, utf_cp_*_off() may go over the end. Solution: Don't go over end of the char of end of the string. --- src/nvim/buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/buffer.c') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index ac6ccf223c..2ceed20768 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -3436,7 +3436,7 @@ void maketitle(void) int len = (int)strlen(buf_p); if (len > 100) { len -= 100; - len += utf_cp_tail_off(buf_p, buf_p + len) + 1; + len += utf_cp_bounds(buf_p, buf_p + len).end_off; buf_p += len; } STRCPY(icon_str, buf_p); -- cgit From 0eaae1bc057a2a4672d3e485498137a0d2282ad3 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 29 Feb 2024 08:33:02 +0800 Subject: vim-patch:9.1.0143: [security]: autocmd causes use-after-free in set_curbuf() (#27664) Problem: [security]: autocmd cause use-after-free in set_curbuf() (kawarimidoll) Solution: check side-effect of BufLeave autocommand, when the number of windows changed, close windows containing buffers that will be wiped, if curbuf changed unexpectedly make sure b_nwindows is decremented otherwise it cannot be wiped set_curbuf() already makes some efforts to ensure the BufLeave autocommands do not cause issues. However there are still 2 issues that are not taken care of: 1) If a BufLeave autocommand opens a new window containing the same buffer as that is going got be closed in close_buffer() a bit later, we suddenly have another window open, containing a free'd buffer. So we must check if the number of windows changed and if it does (and the current buffer is going to be wiped (according to the 'bufhidden' setting), let's immediately close all windows containing the current buffer using close_windows() 2) If a BufLeave autocommand changes our current buffer (displays it in the current window), buf->b_nwindow will be incremented. As part of set_curbuf() we will however enter another buffer soon, which means, the newly created curbuf will have b_nwindows still have set, even so the buffer is no longer displayed in a window. This causes later problems, because it will no longer be possible to wipe such a buffer. So just before entering the final buffer, check if the curbuf changed when calling the BufLeave autocommand and if it does (and curbuf is still valid), decrement curbuf->b_nwindows. Both issues can be verified using the provided test (however the second issue only because such an impacted buffer won't be wiped, causing futher issues in later tests). fixes: vim/vim#13839 closes: vim/vim#14104 https://github.com/vim/vim/commit/55f8bba73be5f9c3a5a4d0d6c5f56e65f2c7d3fc Co-authored-by: Christian Brabandt --- src/nvim/buffer.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'src/nvim/buffer.c') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 2ceed20768..f6c7229485 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -1584,6 +1584,7 @@ void set_curbuf(buf_T *buf, int action, bool update_jumplist) int unload = (action == DOBUF_UNLOAD || action == DOBUF_DEL || action == DOBUF_WIPE); OptInt old_tw = curbuf->b_p_tw; + const int last_winid = get_last_winid(); if (update_jumplist) { setpcmark(); @@ -1612,7 +1613,11 @@ void set_curbuf(buf_T *buf, int action, bool update_jumplist) if (prevbuf == curwin->w_buffer) { reset_synblock(curwin); } - if (unload) { + // autocommands may have opened a new window + // with prevbuf, grr + if (unload + || (last_winid != get_last_winid() + && strchr("wdu", prevbuf->b_p_bh[0]) != NULL)) { close_windows(prevbuf, false); } if (bufref_valid(&prevbufref) && !aborting()) { @@ -1642,6 +1647,11 @@ void set_curbuf(buf_T *buf, int action, bool update_jumplist) // If curwin->w_buffer is null, enter_buffer() will make it valid again bool valid = buf_valid(buf); if ((valid && buf != curbuf && !aborting()) || curwin->w_buffer == NULL) { + // autocommands changed curbuf and we will move to another + // buffer soon, so decrement curbuf->b_nwindows + if (curbuf != NULL && prevbuf != curbuf) { + curbuf->b_nwindows--; + } // If the buffer is not valid but curwin->w_buffer is NULL we must // enter some buffer. Using the last one is hopefully OK. if (!valid) { -- cgit