fix(lua): find length of completion prefix earlier (#29384)

Do the expansion right after setting the expand context, so that the
length of the completion prefix can be set, but don't do that directly
in set_one_cmd_context(), as that's also called by getcmdcompltype().
This commit is contained in:
zeertzjq 2024-06-18 09:47:10 +08:00 committed by GitHub
parent 9d200c78a5
commit 948f2beed4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 89 additions and 26 deletions

View File

@ -240,6 +240,9 @@ int nextwild(expand_T *xp, int type, int options, bool escape)
if (xp->xp_numfiles == -1) { if (xp->xp_numfiles == -1) {
set_expand_context(xp); set_expand_context(xp);
if (xp->xp_context == EXPAND_LUA) {
nlua_expand_pat(xp, xp->xp_pattern);
}
cmd_showtail = expand_showtail(xp); cmd_showtail = expand_showtail(xp);
} }
@ -287,11 +290,6 @@ int nextwild(expand_T *xp, int type, int options, bool escape)
use_options, type); use_options, type);
xfree(p1); xfree(p1);
// xp->xp_pattern might have been modified by ExpandOne (for example,
// in lua completion), so recompute the pattern index and length
i = (int)(xp->xp_pattern - ccline->cmdbuff);
xp->xp_pattern_len = (size_t)ccline->cmdpos - (size_t)i;
// Longest match: make sure it is not shorter, happens with :help. // Longest match: make sure it is not shorter, happens with :help.
if (p2 != NULL && type == WILD_LONGEST) { if (p2 != NULL && type == WILD_LONGEST) {
int j; int j;
@ -1061,6 +1059,9 @@ int showmatches(expand_T *xp, bool wildmenu)
if (xp->xp_numfiles == -1) { if (xp->xp_numfiles == -1) {
set_expand_context(xp); set_expand_context(xp);
if (xp->xp_context == EXPAND_LUA) {
nlua_expand_pat(xp, xp->xp_pattern);
}
int i = expand_cmdline(xp, ccline->cmdbuff, ccline->cmdpos, int i = expand_cmdline(xp, ccline->cmdbuff, ccline->cmdpos,
&numMatches, &matches); &numMatches, &matches);
showtail = expand_showtail(xp); showtail = expand_showtail(xp);
@ -2797,8 +2798,7 @@ static int ExpandFromContext(expand_T *xp, char *pat, char ***matches, int *numM
} }
if (xp->xp_context == EXPAND_LUA) { if (xp->xp_context == EXPAND_LUA) {
ILOG("PAT %s", pat); return nlua_expand_get_matches(numMatches, matches);
return nlua_expand_pat(xp, pat, numMatches, matches);
} }
if (!fuzzy) { if (!fuzzy) {
@ -3610,7 +3610,10 @@ void f_getcompletion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
} }
theend: theend:
; if (xpc.xp_context == EXPAND_LUA) {
nlua_expand_pat(&xpc, xpc.xp_pattern);
xpc.xp_pattern_len = strlen(xpc.xp_pattern);
}
char *pat; char *pat;
if (cmdline_fuzzy_completion_supported(&xpc)) { if (cmdline_fuzzy_completion_supported(&xpc)) {
// when fuzzy matching, don't modify the search string // when fuzzy matching, don't modify the search string

View File

@ -43,6 +43,7 @@
#include "nvim/indent_c.h" #include "nvim/indent_c.h"
#include "nvim/insexpand.h" #include "nvim/insexpand.h"
#include "nvim/keycodes.h" #include "nvim/keycodes.h"
#include "nvim/lua/executor.h"
#include "nvim/macros_defs.h" #include "nvim/macros_defs.h"
#include "nvim/mark_defs.h" #include "nvim/mark_defs.h"
#include "nvim/mbyte.h" #include "nvim/mbyte.h"
@ -4144,6 +4145,9 @@ static int get_cmdline_compl_info(char *line, colnr_T curs_col)
compl_pattern = xstrnsave(line, (size_t)curs_col); compl_pattern = xstrnsave(line, (size_t)curs_col);
compl_patternlen = (size_t)curs_col; compl_patternlen = (size_t)curs_col;
set_cmd_context(&compl_xp, compl_pattern, (int)compl_patternlen, curs_col, false); set_cmd_context(&compl_xp, compl_pattern, (int)compl_patternlen, curs_col, false);
if (compl_xp.xp_context == EXPAND_LUA) {
nlua_expand_pat(&compl_xp, compl_xp.xp_pattern);
}
if (compl_xp.xp_context == EXPAND_UNSUCCESSFUL if (compl_xp.xp_context == EXPAND_UNSUCCESSFUL
|| compl_xp.xp_context == EXPAND_NOTHING) { || compl_xp.xp_context == EXPAND_NOTHING) {
// No completion possible, use an empty pattern to get a // No completion possible, use an empty pattern to get a

View File

@ -1924,10 +1924,14 @@ static void nlua_add_treesitter(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
lua_setfield(lstate, -2, "_ts_get_minimum_language_version"); lua_setfield(lstate, -2, "_ts_get_minimum_language_version");
} }
int nlua_expand_pat(expand_T *xp, char *pat, int *num_results, char ***results) static garray_T expand_result_array = GA_EMPTY_INIT_VALUE;
/// Finds matches for Lua cmdline completion and advances xp->xp_pattern after prefix.
/// This should be called before xp->xp_pattern is first used.
void nlua_expand_pat(expand_T *xp, const char *pat)
{ {
lua_State *const lstate = global_lstate; lua_State *const lstate = global_lstate;
int ret = OK; int status = OK;
// [ vim ] // [ vim ]
lua_getglobal(lstate, "vim"); lua_getglobal(lstate, "vim");
@ -1942,54 +1946,54 @@ int nlua_expand_pat(expand_T *xp, char *pat, int *num_results, char ***results)
if (nlua_pcall(lstate, 1, 2) != 0) { if (nlua_pcall(lstate, 1, 2) != 0) {
nlua_error(lstate, nlua_error(lstate,
_("Error executing vim._expand_pat: %.*s")); _("Error executing vim._expand_pat: %.*s"));
return FAIL; return;
} }
Error err = ERROR_INIT; Error err = ERROR_INIT;
*num_results = 0;
*results = NULL;
Arena arena = ARENA_EMPTY; Arena arena = ARENA_EMPTY;
int prefix_len = (int)nlua_pop_Integer(lstate, &arena, &err); int prefix_len = (int)nlua_pop_Integer(lstate, &arena, &err);
if (ERROR_SET(&err)) { if (ERROR_SET(&err)) {
ret = FAIL; status = FAIL;
goto cleanup; goto cleanup;
} }
Array completions = nlua_pop_Array(lstate, &arena, &err); Array completions = nlua_pop_Array(lstate, &arena, &err);
if (ERROR_SET(&err)) { if (ERROR_SET(&err)) {
ret = FAIL; status = FAIL;
goto cleanup_array; goto cleanup_array;
} }
garray_T result_array; ga_clear(&expand_result_array);
ga_init(&result_array, (int)sizeof(char *), 80); ga_init(&expand_result_array, (int)sizeof(char *), 80);
for (size_t i = 0; i < completions.size; i++) { for (size_t i = 0; i < completions.size; i++) {
Object v = completions.items[i]; Object v = completions.items[i];
if (v.type != kObjectTypeString) { if (v.type != kObjectTypeString) {
ret = FAIL; status = FAIL;
goto cleanup_array; goto cleanup_array;
} }
GA_APPEND(char *, &result_array, string_to_cstr(v.data.string)); GA_APPEND(char *, &expand_result_array, string_to_cstr(v.data.string));
} }
xp->xp_pattern += prefix_len; xp->xp_pattern += prefix_len;
*results = result_array.ga_data;
*num_results = result_array.ga_len;
cleanup_array: cleanup_array:
arena_mem_free(arena_finish(&arena)); arena_mem_free(arena_finish(&arena));
cleanup: cleanup:
if (status == FAIL) {
if (ret == FAIL) { ga_clear(&expand_result_array);
ga_clear(&result_array); }
} }
return ret; int nlua_expand_get_matches(int *num_results, char ***results)
{
*results = expand_result_array.ga_data;
*num_results = expand_result_array.ga_len;
expand_result_array = (garray_T)GA_EMPTY_INIT_VALUE;
return *num_results > 0;
} }
static int nlua_is_thread(lua_State *lstate) static int nlua_is_thread(lua_State *lstate)

View File

@ -812,11 +812,63 @@ describe('completion', function()
} }
end) end)
it('prefix is not included in completion for cmdline mode', function()
feed(':lua math.a<Tab>')
screen:expect([[
|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{100:abs}{3: acos asin atan atan2 }|
:lua math.abs^ |
]])
feed('<Tab>')
screen:expect([[
|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{3:abs }{100:acos}{3: asin atan atan2 }|
:lua math.acos^ |
]])
end)
it('prefix is not included in completion for i_CTRL-X_CTRL-V #19623', function()
feed('ilua math.a<C-X><C-V>')
screen:expect([[
lua math.abs^ |
{1:~ }{12: abs }{1: }|
{1:~ }{4: acos }{1: }|
{1:~ }{4: asin }{1: }|
{1:~ }{4: atan }{1: }|
{1:~ }{4: atan2 }{1: }|
{1:~ }|
{5:-- Command-line completion (^V^N^P) }{6:match 1 of 5} |
]])
feed('<C-V>')
screen:expect([[
lua math.acos^ |
{1:~ }{4: abs }{1: }|
{1:~ }{12: acos }{1: }|
{1:~ }{4: asin }{1: }|
{1:~ }{4: atan }{1: }|
{1:~ }{4: atan2 }{1: }|
{1:~ }|
{5:-- Command-line completion (^V^N^P) }{6:match 2 of 5} |
]])
end)
it('provides completion from `getcompletion()`', function() it('provides completion from `getcompletion()`', function()
eq({ 'vim' }, fn.getcompletion('vi', 'lua')) eq({ 'vim' }, fn.getcompletion('vi', 'lua'))
eq({ 'api' }, fn.getcompletion('vim.ap', 'lua')) eq({ 'api' }, fn.getcompletion('vim.ap', 'lua'))
eq({ 'tbl_filter' }, fn.getcompletion('vim.tbl_fil', 'lua')) eq({ 'tbl_filter' }, fn.getcompletion('vim.tbl_fil', 'lua'))
eq({ 'vim' }, fn.getcompletion('print(vi', 'lua')) eq({ 'vim' }, fn.getcompletion('print(vi', 'lua'))
eq({ 'abs', 'acos', 'asin', 'atan', 'atan2' }, fn.getcompletion('math.a', 'lua'))
eq({ 'abs', 'acos', 'asin', 'atan', 'atan2' }, fn.getcompletion('lua math.a', 'cmdline'))
-- fuzzy completion is not supported, so the result should be the same -- fuzzy completion is not supported, so the result should be the same
command('set wildoptions+=fuzzy') command('set wildoptions+=fuzzy')
eq({ 'vim' }, fn.getcompletion('vi', 'lua')) eq({ 'vim' }, fn.getcompletion('vi', 'lua'))