From ae4ca4edf89ece433b61e8bf92c412298b58d9ea Mon Sep 17 00:00:00 2001 From: glepnir Date: Fri, 13 Oct 2023 14:49:01 +0800 Subject: [PATCH] feat(complete): support f flag for complete buffer part --- runtime/doc/news.txt | 2 ++ runtime/doc/options.txt | 1 + runtime/lua/vim/_meta/options.lua | 1 + src/nvim/insexpand.c | 35 ++++++++++++++++++++++ src/nvim/options.lua | 1 + src/nvim/optionstr.c | 4 +-- test/functional/editor/completion_spec.lua | 24 +++++++++++++++ 7 files changed, 66 insertions(+), 2 deletions(-) diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 6f2c6efe54..c090cfe166 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -195,6 +195,8 @@ The following new APIs and features were added. • Added |vim.snippet| for snippet expansion support. +• 'complete' option supports "f" flag for completing buffer names. + ============================================================================== CHANGED FEATURES *news-changed* diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index c8ea5ce67f..9dc382948c 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1458,6 +1458,7 @@ A jump table for the options with a short description can be found at |Q_op|. |i_CTRL-X_CTRL-D| ] tag completion t same as "]" + f scan the buffer names (as opposed to buffer contents) Unloaded buffers are not loaded, thus their autocmds |:autocmd| are not executed, this may lead to unexpected completions from some files diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index cc013112b3..83b30838ab 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -998,6 +998,7 @@ vim.bo.cms = vim.bo.commentstring --- `i_CTRL-X_CTRL-D` --- ] tag completion --- t same as "]" +--- f scan the buffer names (as opposed to buffer contents) --- --- Unloaded buffers are not loaded, thus their autocmds `:autocmd` are --- not executed, this may lead to unexpected completions from some files diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c index 09860a6a40..427088e414 100644 --- a/src/nvim/insexpand.c +++ b/src/nvim/insexpand.c @@ -91,6 +91,7 @@ enum { CTRL_X_LOCAL_MSG = 15, ///< only used in "ctrl_x_msgs" CTRL_X_EVAL = 16, ///< for builtin function complete() CTRL_X_CMDLINE_CTRL_X = 17, ///< CTRL-X typed in CTRL_X_CMDLINE + CTRL_X_BUFNAMES = 18, }; #define CTRL_X_MSG(i) ctrl_x_msgs[(i) & ~CTRL_X_WANT_IDENT] @@ -536,6 +537,8 @@ bool vim_is_ctrl_x_key(int c) return c == Ctrl_S || c == Ctrl_P || c == Ctrl_N; case CTRL_X_EVAL: return (c == Ctrl_P || c == Ctrl_N); + case CTRL_X_BUFNAMES: + return (c == Ctrl_P || c == Ctrl_N); } internal_error("vim_is_ctrl_x_key()"); return false; @@ -2917,6 +2920,8 @@ static int process_next_cpt_value(ins_compl_next_state_T *st, int *compl_type_ar compl_type = CTRL_X_PATH_PATTERNS; } else if (*st->e_cpt == 'd') { compl_type = CTRL_X_PATH_DEFINES; + } else if (*st->e_cpt == 'f') { + compl_type = CTRL_X_BUFNAMES; } else if (*st->e_cpt == ']' || *st->e_cpt == 't') { compl_type = CTRL_X_TAGS; if (!shortmess(SHM_COMPLETIONSCAN)) { @@ -3274,6 +3279,9 @@ static bool get_next_completion_match(int type, ins_compl_next_state_T *st, pos_ case CTRL_X_SPELL: get_next_spell_completion(st->first_match_pos.lnum); break; + case CTRL_X_BUFNAMES: + get_next_bufname_token(); + break; default: // normal ^P/^N and ^X^L found_new_match = get_next_default_completion(st, ini); @@ -3291,6 +3299,33 @@ static bool get_next_completion_match(int type, ins_compl_next_state_T *st, pos_ return found_new_match; } +static void get_next_bufname_token(void) +{ + FOR_ALL_BUFFERS(b) { + if (b->b_p_bl) { + char *start = get_past_head(b->b_sfname); + char *current = start; + char *p = (char *)path_next_component(start); + while (true) { + int len = (int)(p - current) - (*p == NUL ? 0 : 1); + // treat . as a separator, unless it is the first char in a filename + char *dot = strchr(current, '.'); + if (dot && *p == NUL && *current != '.') { + len = (int)(dot - current); + p = dot + 1; + } + ins_compl_add(current, len, NULL, NULL, false, NULL, 0, + p_ic ? CP_ICASE : 0, false); + if (*p == NUL) { + break; + } + current = p; + p = (char *)path_next_component(p); + } + } + } +} + /// Get the next expansion(s), using "compl_pattern". /// The search starts at position "ini" in curbuf and in the direction /// compl_direction. diff --git a/src/nvim/options.lua b/src/nvim/options.lua index d416b0070f..00377cac1e 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -1370,6 +1370,7 @@ return { |i_CTRL-X_CTRL-D| ] tag completion t same as "]" + f scan the buffer names (as opposed to buffer contents) Unloaded buffers are not loaded, thus their autocmds |:autocmd| are not executed, this may lead to unexpected completions from some files diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c index 2e90b6b4ae..63ceb07b48 100644 --- a/src/nvim/optionstr.c +++ b/src/nvim/optionstr.c @@ -1171,7 +1171,7 @@ const char *did_set_complete(optset_T *args) if (!*s) { break; } - if (vim_strchr(".wbuksid]tU", (uint8_t)(*s)) == NULL) { + if (vim_strchr(".wbuksid]tUf", (uint8_t)(*s)) == NULL) { return illegal_char(args->os_errbuf, args->os_errbuflen, (uint8_t)(*s)); } if (*++s != NUL && *s != ',' && *s != ' ') { @@ -1200,7 +1200,7 @@ const char *did_set_complete(optset_T *args) int expand_set_complete(optexpand_T *args, int *numMatches, char ***matches) { static char *(p_cpt_values[]) = { - ".", "w", "b", "u", "k", "kspell", "s", "i", "d", "]", "t", "U", NULL + ".", "w", "b", "u", "k", "kspell", "s", "i", "d", "]", "t", "U", "f", NULL }; return expand_set_opt_string(args, p_cpt_values, diff --git a/test/functional/editor/completion_spec.lua b/test/functional/editor/completion_spec.lua index ea3397d50d..cb5e0b0b14 100644 --- a/test/functional/editor/completion_spec.lua +++ b/test/functional/editor/completion_spec.lua @@ -1228,4 +1228,28 @@ describe('completion', function() expect('colorscheme NOSUCHCOLORSCHEME') assert_alive() end) + + it('complete with f flag #25598', function() + screen:try_resize(20, 9) + local bufname = 'foo/bar.txt' + local hidden = 'fooA/.hidden' + if helpers.is_os('win') then + bufname = 'C:\\foo\\bar.txt' + hidden = 'C:\\fooA\\.hidden' + end + command('set complete+=f | edit '.. bufname ..' | edit '..hidden) + feed('i') + + screen:expect{grid=[[ + foo^ | + {2:foo }{0: }| + {1:bar }{0: }| + {1:txt }{0: }| + {1:fooA }{0: }| + {1:.hidden }{0: }| + {0:~ }| + {0:~ }| + {3:-- }{4:match 1 of 5} | + ]]} + end) end)