diff options
33 files changed, 723 insertions, 133 deletions
diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim index 2fb9d74d8d..2b06ee8c48 100644 --- a/runtime/autoload/provider/clipboard.vim +++ b/runtime/autoload/provider/clipboard.vim @@ -73,9 +73,9 @@ function! provider#clipboard#Executable() abort let s:cache_enabled = 0 return 'pbcopy' elseif exists('$WAYLAND_DISPLAY') && executable('wl-copy') && executable('wl-paste') - let s:copy['+'] = 'wl-copy --foreground' + let s:copy['+'] = 'wl-copy --foreground --type text/plain' let s:paste['+'] = 'wl-paste --no-newline' - let s:copy['*'] = 'wl-copy --foreground --primary' + let s:copy['*'] = 'wl-copy --foreground --primary --type text/plain' let s:paste['*'] = 'wl-paste --no-newline --primary' return 'wl-copy' elseif exists('$DISPLAY') && executable('xclip') diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 505c0c5ea0..7bb9ae68bc 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -6599,12 +6599,13 @@ A jump table for the options with a short description can be found at |Q_op|. 'wildoptions' 'wop' string (default "") global A list of words that change how command line completion is done. - Currently only one word is allowed: tagfile When using CTRL-D to list matching tags, the kind of tag and the file of the tag is listed. Only one match is displayed per line. Often used tag kinds are: d #define f function + pum Display the completion matches using the popupmenu + in the same style as the |ins-completion-menu|. Also see |cmdline-completion|. *'winaltkeys'* *'wak'* diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt index 270c4fb556..6976efe60a 100644 --- a/runtime/doc/ui.txt +++ b/runtime/doc/ui.txt @@ -31,7 +31,7 @@ a dictionary with these (optional) keys: `ext_popupmenu` Externalize the popupmenu. |ui-popupmenu| `ext_tabline` Externalize the tabline. |ui-tabline| `ext_cmdline` Externalize the cmdline. |ui-cmdline| - `ext_wildmenu` Externalize the wildmenu. |ui-wildmenu| + `ext_wildmenu` Externalize the wildmenu (deprecated). |ui-wildmenu| `ext_messages` Externalize messages. |ui-messages| `ext_linegrid` Use new revision of the grid events. |ui-linegrid| `ext_multigrid` Use per-window grid based events. |ui-multigrid| @@ -554,7 +554,7 @@ See |nvim_input_mouse| for sending mouse events to Nvim. ============================================================================== Popupmenu Events *ui-popupmenu* -Only sent if `ext_popupmenu` option is set in |ui-options| +Only sent if `ext_popupmenu` option is set in |ui-options|. ["popupmenu_show", items, selected, row, col, grid] Show |popupmenu-completion|. `items` is an array of completion items @@ -564,7 +564,9 @@ Only sent if `ext_popupmenu` option is set in |ui-options| index into the array of items (-1 if no item is selected). `row` and `col` give the anchor position, where the first character of the completed word will be. When |ui-multigrid| is used, `grid` is the - grid for the anchor position. + grid for the anchor position. When `ext_cmdline` is active, `grid` is + set to -1 to indicate the popupmenu should be anchored to the external + cmdline. Then `col` will be a byte position in the cmdline text. ["popupmenu_select", selected] Select an item in the current popupmenu. `selected` is a zero-based @@ -588,7 +590,10 @@ Only sent if `ext_tabline` option is set in |ui-options| ============================================================================== Cmdline Events *ui-cmdline* -Only sent if `ext_cmdline` option is set in |ui-options|. +Only sent if `ext_cmdline` option is set in |ui-options|. To handle +command-line completion (wildmenu), use |ui-popupmenu| events activated by +|ext_popupmenu| option. (The `ext_wildmenu` option only exists for backwards +compatibility). ["cmdline_show", content, pos, firstc, prompt, indent, level] content: List of [attrs, string] @@ -648,6 +653,11 @@ Wildmenu Events *ui-wildmenu* Only sent if `ext_wildmenu` option is set in |ui-options| +Deprecated. When `ext_cmdline` and `ext_popupmenu` are both set, +|ui-popupmenu| events will be used for command-line completion. But if +`ext_wildmenu` is also set, these events are still used for backwards +compatibility. New clients should use `ext_popupmenu` instead. + ["wildmenu_show", items] Activate the wildmenu (command-line completion). `items` is an array with the completion items. diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 55c3be9a57..d3ac0ba613 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -195,6 +195,7 @@ Options: 'scrollback' 'statusline' supports unlimited alignment sections 'tabline' %@Func@foo%X can call any function on mouse-click + 'wildoptions' `pum` flag to use popupmenu for wildmode completion 'winhighlight' window-local highlights Variables: diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index d3cbb46dad..93502d7306 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -34,6 +34,7 @@ typedef struct { // Position of legacy cursor, used both for drawing and visible user cursor. Integer client_row, client_col; + bool wildmenu_active; } UIData; static PMap(uint64_t) *connected_uis = NULL; @@ -146,6 +147,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, data->buffer = (Array)ARRAY_DICT_INIT; data->hl_id = 0; data->client_col = -1; + data->wildmenu_active = false; ui->data = data; pmap_put(uint64_t)(connected_uis, channel_id, ui); @@ -586,6 +588,7 @@ static Array translate_firstarg(UI *ui, Array args) static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed) { + UIData *data = ui->data; if (!ui->ui_ext[kUILinegrid]) { // the representation of highlights in cmdline changed, translate back // never consumes args @@ -611,6 +614,39 @@ static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed) } } + // Back-compat: translate popupmenu_xx to legacy wildmenu_xx. + if (ui->ui_ext[kUIWildmenu]) { + if (strequal(name, "popupmenu_show")) { + data->wildmenu_active = (args.items[4].data.integer == -1) + || !ui->ui_ext[kUIPopupmenu]; + if (data->wildmenu_active) { + Array new_args = ARRAY_DICT_INIT; + Array items = args.items[0].data.array; + Array new_items = ARRAY_DICT_INIT; + for (size_t i = 0; i < items.size; i++) { + ADD(new_items, copy_object(items.items[i].data.array.items[0])); + } + ADD(new_args, ARRAY_OBJ(new_items)); + push_call(ui, "wildmenu_show", new_args); + if (args.items[1].data.integer != -1) { + Array new_args2 = ARRAY_DICT_INIT; + ADD(new_args2, args.items[1]); + push_call(ui, "wildmenu_select", new_args); + } + return; + } + } else if (strequal(name, "popupmenu_select")) { + if (data->wildmenu_active) { + name = "wildmenu_select"; + } + } else if (strequal(name, "popupmenu_hide")) { + if (data->wildmenu_active) { + name = "wildmenu_hide"; + } + } + } + + Array my_args = ARRAY_DICT_INIT; // Objects are currently single-reference // make a copy, but only if necessary diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h index b89c5b6014..a1d25766fe 100644 --- a/src/nvim/api/ui_events.in.h +++ b/src/nvim/api/ui_events.in.h @@ -143,11 +143,11 @@ void cmdline_block_hide(void) FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; void wildmenu_show(Array items) - FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; + FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL FUNC_API_BRIDGE_IMPL; void wildmenu_select(Integer selected) - FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; + FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL FUNC_API_BRIDGE_IMPL; void wildmenu_hide(void) - FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; + FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL FUNC_API_BRIDGE_IMPL; void msg_show(String kind, Array content, Boolean replace_last) FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 614185a463..2f2546b360 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -996,18 +996,16 @@ typedef struct { .relative = 0, .external = false, \ .focusable = true }) -/* - * Structure which contains all information that belongs to a window - * - * All row numbers are relative to the start of the window, except w_winrow. - */ +/// Structure which contains all information that belongs to a window. +/// +/// All row numbers are relative to the start of the window, except w_winrow. struct window_S { handle_T handle; ///< unique identifier for the window buf_T *w_buffer; ///< buffer we are a window into (used ///< often, keep it the first item!) - synblock_T *w_s; /* for :ownsyntax */ + synblock_T *w_s; ///< for :ownsyntax int w_hl_id_normal; ///< 'winhighlight' normal id int w_hl_attr_normal; ///< 'winhighlight' normal final attrs @@ -1017,18 +1015,18 @@ struct window_S { int w_hl_needs_update; ///< attrs need to be recalculated - win_T *w_prev; /* link to previous window */ - win_T *w_next; /* link to next window */ - bool w_closing; /* window is being closed, don't let - autocommands close it too. */ + win_T *w_prev; ///< link to previous window + win_T *w_next; ///< link to next window + bool w_closing; ///< window is being closed, don't let + /// autocommands close it too. - frame_T *w_frame; /* frame containing this window */ + frame_T *w_frame; ///< frame containing this window - pos_T w_cursor; /* cursor position in buffer */ + pos_T w_cursor; ///< cursor position in buffer - colnr_T w_curswant; /* The column we'd like to be at. This is - used to try to stay in the same column - for up/down cursor motions. */ + colnr_T w_curswant; ///< Column we want to be at. This is + /// used to try to stay in the same column + /// for up/down cursor motions. int w_set_curswant; // If set, then update w_curswant the next // time through cursupdate() to the diff --git a/src/nvim/edit.c b/src/nvim/edit.c index f6b5a01915..c8d98bce3b 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -2656,7 +2656,7 @@ void ins_compl_show_pum(void) col = curwin->w_cursor.col; curwin->w_cursor.col = compl_col; pum_selected_item = cur; - pum_display(compl_match_array, compl_match_arraysize, cur, array_changed); + pum_display(compl_match_array, compl_match_arraysize, cur, array_changed, 0); curwin->w_cursor.col = col; if (!has_event(EVENT_MENUPOPUPCHANGED)) { diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 5ef2a8772e..df677a3a13 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6328,8 +6328,12 @@ call_func( } - /* execute the function if no errors detected and executing */ - if (evaluate && error == ERROR_NONE) { + // Execute the function if executing and no errors were detected. + if (!evaluate) { + // Not evaluating, which means the return value is unknown. This + // matters for giving error messages. + rettv->v_type = VAR_UNKNOWN; + } else if (error == ERROR_NONE) { char_u *rfname = fname; /* Ignore "g:" before a function name. */ diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 5b9b4fed12..3467bc8b92 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -3235,7 +3235,7 @@ const char * set_one_cmd_context( case CMD_tjump: case CMD_stjump: case CMD_ptjump: - if (*p_wop != NUL) { + if (wop_flags & WOP_TAGFILE) { xp->xp_context = EXPAND_TAGS_LISTFILES; } else { xp->xp_context = EXPAND_TAGS; diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 8e6fc5ad4f..2dbd6375f1 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -214,6 +214,15 @@ static int hislen = 0; /* actual length of history tables */ /// user interrupting highlight function to not interrupt command-line. static bool getln_interrupted_highlight = false; +// "compl_match_array" points the currently displayed list of entries in the +// popup menu. It is NULL when there is no popup menu. +static pumitem_T *compl_match_array = NULL; +static int compl_match_arraysize; +// First column in cmdline of the matched item for completion. +static int compl_startcol; +static int compl_selected; + + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ex_getln.c.generated.h" @@ -600,6 +609,13 @@ static int command_line_execute(VimState *state, int key) } else if (s->c == K_RIGHT) { s->c = Ctrl_N; } + if (compl_match_array) { + if (s->c == K_UP) { + s->c = Ctrl_P; + } else if (s->c == K_DOWN) { + s->c = Ctrl_N; + } + } } // Hitting CR after "emenu Name.": complete submenu @@ -615,8 +631,10 @@ static int command_line_execute(VimState *state, int key) if (!(s->c == p_wc && KeyTyped) && s->c != p_wcm && s->c != Ctrl_N && s->c != Ctrl_P && s->c != Ctrl_A && s->c != Ctrl_L) { - if (ui_has(kUIWildmenu)) { - ui_call_wildmenu_hide(); + if (compl_match_array) { + pum_undisplay(true); + xfree(compl_match_array); + compl_match_array = NULL; } if (s->xpc.xp_numfiles != -1) { (void)ExpandOne(&s->xpc, NULL, NULL, 0, WILD_FREE); @@ -3746,13 +3764,12 @@ ExpandOne ( else findex = -1; } - if (p_wmnu) { - if (ui_has(kUIWildmenu)) { - ui_call_wildmenu_select(findex); - } else { - win_redr_status_matches(xp, xp->xp_numfiles, xp->xp_files, - findex, cmd_showtail); - } + if (compl_match_array) { + compl_selected = findex; + cmdline_pum_display(false); + } else if (p_wmnu) { + win_redr_status_matches(xp, xp->xp_numfiles, xp->xp_files, + findex, cmd_showtail); } if (findex == -1) { return vim_strsave(orig_save); @@ -4069,6 +4086,12 @@ void tilde_replace(char_u *orig_pat, int num_files, char_u **files) } } +void cmdline_pum_display(bool changed_array) +{ + pum_display(compl_match_array, compl_match_arraysize, compl_selected, + changed_array, compl_startcol); +} + /* * Show all matches for completion on the command line. * Returns EXPAND_NOTHING when the character that triggered expansion should @@ -4102,12 +4125,28 @@ static int showmatches(expand_T *xp, int wildmenu) showtail = cmd_showtail; } - if (ui_has(kUIWildmenu)) { - Array args = ARRAY_DICT_INIT; + bool compl_use_pum = (ui_has(kUICmdline) + ? ui_has(kUIPopupmenu) + : wildmenu && (wop_flags & WOP_PUM)) + || ui_has(kUIWildmenu); + + if (compl_use_pum) { + compl_match_arraysize = num_files; + compl_match_array = xcalloc(compl_match_arraysize, sizeof(pumitem_T)); for (i = 0; i < num_files; i++) { - ADD(args, STRING_OBJ(cstr_to_string((char *)files_found[i]))); + compl_match_array[i].pum_text = L_SHOWFILE(i); + } + ssize_t offset = showtail ? sm_gettail(xp->xp_pattern)-xp->xp_pattern : 0; + if (ui_has(kUICmdline)) { + compl_startcol = ccline.cmdpos - strnlen((char *)xp->xp_pattern+offset, + xp->xp_pattern_len-offset); + } else { + compl_startcol = ccline.cmdspos + - mb_string2cells_len(xp->xp_pattern+offset, + xp->xp_pattern_len-offset); } - ui_call_wildmenu_show(args); + compl_selected = -1; + cmdline_pum_display(true); return EXPAND_OK; } diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 7be4107c94..53b945d983 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -6527,6 +6527,7 @@ aucmd_prepbuf ( win = curwin; aco->save_curwin = curwin; + aco->save_prevwin = prevwin; aco->save_curbuf = curbuf; if (win != NULL) { /* There is a window for "buf" in the current tab page, make it the @@ -6624,6 +6625,8 @@ win_found: // Hmm, original window disappeared. Just use the first one. curwin = firstwin; } + prevwin = win_valid(aco->save_prevwin) ? aco->save_prevwin + : firstwin; // window disappeared? vars_clear(&aucmd_win->w_vars->dv_hashtab); // free all w: variables hash_init(&aucmd_win->w_vars->dv_hashtab); // re-use the hashtab curbuf = curwin->w_buffer; @@ -6656,6 +6659,8 @@ win_found: } curwin = aco->save_curwin; + prevwin = win_valid(aco->save_prevwin) ? aco->save_prevwin + : firstwin; // window disappeared? curbuf = curwin->w_buffer; // In case the autocommand moves the cursor to a position that does not // exist in curbuf diff --git a/src/nvim/fileio.h b/src/nvim/fileio.h index 8db4b89806..a6011ec414 100644 --- a/src/nvim/fileio.h +++ b/src/nvim/fileio.h @@ -23,6 +23,7 @@ typedef struct { buf_T *save_curbuf; ///< saved curbuf int use_aucmd_win; ///< using aucmd_win win_T *save_curwin; ///< saved curwin + win_T *save_prevwin; ///< saved prevwin win_T *new_curwin; ///< new curwin bufref_T new_curbuf; ///< new curbuf char_u *globaldir; ///< saved value of globaldir diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 93069893cf..72b97736fc 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -24,6 +24,10 @@ #include "nvim/undo.h" #include "nvim/ascii.h" +#ifdef WIN32 +#include "nvim/os/os.h" +#endif + #include "nvim/lua/executor.h" #include "nvim/lua/converter.h" @@ -118,6 +122,14 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_setfield(lstate, -2, "debug"); lua_pop(lstate, 1); +#ifdef WIN32 + // os.getenv + lua_getglobal(lstate, "os"); + lua_pushcfunction(lstate, &nlua_getenv); + lua_setfield(lstate, -2, "getenv"); + lua_pop(lstate, 1); +#endif + // vim if (luaL_dostring(lstate, (char *)&vim_module[0])) { nlua_error(lstate, _("E5106: Error while creating vim module: %.*s")); @@ -297,7 +309,7 @@ nlua_print_error: return 0; } -/// debug.debug implementation: interaction with user while debugging +/// debug.debug: interaction with user while debugging. /// /// @param lstate Lua interpreter state. int nlua_debug(lua_State *lstate) @@ -337,6 +349,19 @@ int nlua_debug(lua_State *lstate) return 0; } +#ifdef WIN32 +/// os.getenv: override os.getenv to maintain coherency. #9681 +/// +/// uv_os_setenv uses SetEnvironmentVariableW which does not update _environ. +/// +/// @param lstate Lua interpreter state. +static int nlua_getenv(lua_State *lstate) +{ + lua_pushstring(lstate, os_getenv(luaL_checkstring(lstate, 1))); + return 1; +} +#endif + /// Evaluate lua string /// /// Used for luaeval(). diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 5ed2b4c564..6c34cacb8d 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -555,6 +555,24 @@ size_t mb_string2cells(const char_u *str) return clen; } +/// Get the number of cells occupied by string `str` with maximum length `size` +/// +/// @param str The source string, may not be NULL, must be a NUL-terminated +/// string. +/// @param size maximum length of string. It will terminate on earlier NUL. +/// @return The number of cells occupied by string `str` +size_t mb_string2cells_len(const char_u *str, size_t size) +{ + size_t clen = 0; + + for (const char_u *p = str; *p != NUL && p < str+size; + p += utf_ptr2len_len(p, size+(p-str))) { + clen += utf_ptr2cells(p); + } + + return clen; +} + /// Convert a UTF-8 byte sequence to a wide character /// /// If the sequence is illegal or truncated by a NUL then the first byte is diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index 64a4b8b0b4..d06fa8f762 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -710,9 +710,6 @@ open_line ( less_cols_off++; } } - if (*p_extra != NUL) { - did_ai = false; // append some text, don't truncate now - } /* columns for marks adjusted for removed columns */ less_cols = (int)(p_extra - saved_line); diff --git a/src/nvim/option.c b/src/nvim/option.c index 2346c84b54..ad0ccd9f15 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -284,7 +284,6 @@ static char *(p_ambw_values[]) = { "single", "double", NULL }; static char *(p_bg_values[]) = { "light", "dark", NULL }; static char *(p_nf_values[]) = { "bin", "octal", "hex", "alpha", NULL }; static char *(p_ff_values[]) = { FF_UNIX, FF_DOS, FF_MAC, NULL }; -static char *(p_wop_values[]) = { "tagfile", NULL }; static char *(p_wak_values[]) = { "yes", "menu", "no", NULL }; static char *(p_mousem_values[]) = { "extend", "popup", "popup_setpos", "mac", NULL }; @@ -2608,11 +2607,11 @@ ambw_end: else if (varp == &p_wim) { if (check_opt_wim() == FAIL) errmsg = e_invarg; - } - /* 'wildoptions' */ - else if (varp == &p_wop) { - if (check_opt_strings(p_wop, p_wop_values, TRUE) != OK) + // 'wildoptions' + } else if (varp == &p_wop) { + if (opt_strings_flags(p_wop, p_wop_values, &wop_flags, true) != OK) { errmsg = e_invarg; + } } /* 'winaltkeys' */ else if (varp == &p_wak) { diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 74047e7cef..c71ce9175b 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -659,6 +659,12 @@ extern char_u *p_vfile; /* 'verbosefile' */ #endif EXTERN int p_warn; // 'warn' EXTERN char_u *p_wop; // 'wildoptions' +EXTERN unsigned wop_flags; +# ifdef IN_OPTION_C +static char *(p_wop_values[]) = { "tagfile", "pum", NULL }; +#endif +#define WOP_TAGFILE 0x01 +#define WOP_PUM 0x02 EXTERN long p_window; // 'window' EXTERN char_u *p_wak; // 'winaltkeys' EXTERN char_u *p_wig; // 'wildignore' diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 2398f9d61c..81133ae15c 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -2699,7 +2699,7 @@ return { }, { full_name='wildoptions', abbreviation='wop', - type='string', scope={'global'}, + type='string', list='onecomma', scope={'global'}, vi_def=true, varname='p_wop', defaults={if_true={vi=""}} diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index e7bfbc8240..7d1021962c 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -151,25 +151,36 @@ int os_unsetenv(const char *name) char *os_getenvname_at_index(size_t index) { #ifdef _WIN32 - // Check if index is inside the environ array and is not the last element. - for (size_t i = 0; i <= index; i++) { - if (_wenviron[i] == NULL) { - return NULL; - } - } - wchar_t *utf16_str = _wenviron[index]; - char *utf8_str; - int conversion_result = utf16_to_utf8(utf16_str, &utf8_str); - if (conversion_result != 0) { - EMSG2("utf16_to_utf8 failed: %d", conversion_result); + wchar_t *env = GetEnvironmentStringsW(); + if (!env) { return NULL; } - size_t namesize = 0; - while (utf8_str[namesize] != '=' && utf8_str[namesize] != NUL) { - namesize++; + char *name = NULL; + size_t current_index = 0; + // GetEnvironmentStringsW() result has this format: + // var1=value1\0var2=value2\0...varN=valueN\0\0 + for (wchar_t *it = env; *it != L'\0' || *(it + 1) != L'\0'; it++) { + if (index == current_index) { + char *utf8_str; + int conversion_result = utf16_to_utf8(it, &utf8_str); + if (conversion_result != 0) { + EMSG2("utf16_to_utf8 failed: %d", conversion_result); + break; + } + size_t namesize = 0; + while (utf8_str[namesize] != '=' && utf8_str[namesize] != NUL) { + namesize++; + } + name = (char *)vim_strnsave((char_u *)utf8_str, namesize); + xfree(utf8_str); + break; + } + if (*it == L'\0') { + current_index++; + } } - char *name = (char *)vim_strnsave((char_u *)utf8_str, namesize); - xfree(utf8_str); + + FreeEnvironmentStringsW(env); return name; #else # if defined(HAVE__NSGETENVIRON) diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index e9b3f04454..8cf09b14d7 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -66,7 +66,9 @@ static bool pum_invalid = false; // the screen was just cleared /// @param array_changed if true, array contains different items since last call /// if false, a new item is selected, but the array /// is the same -void pum_display(pumitem_T *array, int size, int selected, bool array_changed) +/// @param cmd_startcol only for cmdline mode: column of completed match +void pum_display(pumitem_T *array, int size, int selected, bool array_changed, + int cmd_startcol) { int w; int def_width; @@ -84,7 +86,8 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed) if (!pum_is_visible) { // To keep the code simple, we only allow changing the // draw mode when the popup menu is not being displayed - pum_external = ui_has(kUIPopupmenu); + pum_external = ui_has(kUIPopupmenu) + || (State == CMDLINE && ui_has(kUIWildmenu)); } do { @@ -96,19 +99,26 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed) above_row = 0; below_row = cmdline_row; - // anchor position: the start of the completed word - row = curwin->w_wrow; - if (curwin->w_p_rl) { - col = curwin->w_width - curwin->w_wcol - 1; + // wildoptions=pum + if (State == CMDLINE) { + row = ui_has(kUICmdline) ? 0 : cmdline_row; + col = cmd_startcol; + pum_anchor_grid = ui_has(kUICmdline) ? -1 : DEFAULT_GRID_HANDLE; } else { - col = curwin->w_wcol; - } + // anchor position: the start of the completed word + row = curwin->w_wrow; + if (curwin->w_p_rl) { + col = curwin->w_width - curwin->w_wcol - 1; + } else { + col = curwin->w_wcol; + } - pum_anchor_grid = (int)curwin->w_grid.handle; - if (!ui_has(kUIMultigrid)) { - pum_anchor_grid = (int)default_grid.handle; - row += curwin->w_winrow; - col += curwin->w_wincol; + pum_anchor_grid = (int)curwin->w_grid.handle; + if (!ui_has(kUIMultigrid)) { + pum_anchor_grid = (int)default_grid.handle; + row += curwin->w_winrow; + col += curwin->w_wincol; + } } if (pum_external) { diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 5255fd2a51..32371b8aba 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -7151,8 +7151,12 @@ void screen_resize(int width, int height) if (curwin->w_p_scb) do_check_scrollbind(TRUE); if (State & CMDLINE) { + redraw_popupmenu = false; update_screen(NOT_VALID); redrawcmdline(); + if (pum_drawn()) { + cmdline_pum_display(false); + } } else { update_topline(); if (pum_drawn()) { diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim index f8946c6929..fa7cfaa40c 100644 --- a/src/nvim/testdir/test_edit.vim +++ b/src/nvim/testdir/test_edit.vim @@ -402,8 +402,19 @@ func! Test_edit_13() call feedkeys("A {\<cr>more\<cr>}\<esc>", 'tnix') call assert_equal(["\tabc {", "\t\tmore", "\t}"], getline(1, '$')) set smartindent& autoindent& - bw! + bwipe! endif + + " Test autoindent removing indent of blank line. + new + call setline(1, ' foo bar baz') + set autoindent + exe "normal 0eea\<CR>\<CR>\<Esc>" + call assert_equal(" foo bar", getline(1)) + call assert_equal("", getline(2)) + call assert_equal(" baz", getline(3)) + set autoindent& + bwipe! endfunc func! Test_edit_CR() diff --git a/src/nvim/testdir/test_lambda.vim b/src/nvim/testdir/test_lambda.vim index ada25da4a8..bc7817cef8 100644 --- a/src/nvim/testdir/test_lambda.vim +++ b/src/nvim/testdir/test_lambda.vim @@ -291,3 +291,9 @@ func Test_named_function_closure() call garbagecollect() call assert_equal(14, s:Abar()) endfunc + +func Test_lambda_with_index() + let List = {x -> [x]} + let Extract = {-> function(List, ['foobar'])()[0]} + call assert_equal('foobar', Extract()) +endfunc diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim index 62ddad5dce..5a0939a6a1 100644 --- a/src/nvim/testdir/test_timers.vim +++ b/src/nvim/testdir/test_timers.vim @@ -47,6 +47,9 @@ func Test_repeat_many() call timer_stopall() let g:val = 0 let timer = timer_start(50, 'MyHandler', {'repeat': -1}) + if has('mac') + sleep 200m + endif sleep 200m call timer_stop(timer) call assert_inrange((has('mac') ? 1 : 2), 4, g:val) diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index 93599c04f1..d26cb0dbb3 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -24,8 +24,8 @@ describe('api/buf', function() end - describe('line_count, insert and del_line', function() - it('works', function() + describe('nvim_buf_set_lines, nvim_buf_line_count', function() + it('deprecated forms', function() eq(1, curbuf_depr('line_count')) curbuf_depr('insert', -1, {'line'}) eq(2, curbuf_depr('line_count')) @@ -70,7 +70,7 @@ describe('api/buf', function() end) end) - describe('{get,set,del}_line', function() + describe('deprecated: {get,set,del}_line', function() it('works', function() eq('', curbuf_depr('get_line', 0)) curbuf_depr('set_line', 0, 'line1') @@ -102,7 +102,7 @@ describe('api/buf', function() end) end) - describe('{get,set}_line_slice', function() + describe('deprecated: {get,set}_line_slice', function() it('get_line_slice: out-of-bounds returns empty array', function() curbuf_depr('set_line_slice', 0, 0, true, true, {'a', 'b', 'c'}) eq({'a', 'b', 'c'}, curbuf_depr('get_line_slice', 0, 2, true, true)) --sanity @@ -149,7 +149,7 @@ describe('api/buf', function() end) end) - describe('{get,set}_lines', function() + describe('nvim_buf_get_lines, nvim_buf_set_lines', function() local get_lines, set_lines = curbufmeths.get_lines, curbufmeths.set_lines local line_count = curbufmeths.line_count @@ -272,7 +272,7 @@ describe('api/buf', function() eq({}, get_lines(-3, -4, true)) end) - it('set_line_slice: out-of-bounds can extend past end', function() + it('set_lines: out-of-bounds can extend past end', function() set_lines(0, -1, true, {'a', 'b', 'c'}) eq({'a', 'b', 'c'}, get_lines(0, -1, true)) --sanity @@ -286,7 +286,7 @@ describe('api/buf', function() eq({'e', 'a', 'b', 'c', 'd'}, get_lines(0, -1, true)) end) - it("set_line on alternate buffer does not access invalid line (E315)", function() + it("set_lines on alternate buffer does not access invalid line (E315)", function() feed_command('set hidden') insert('Initial file') command('enew') @@ -334,9 +334,27 @@ describe('api/buf', function() {2:-- INSERT --} | ]]) end) + + it('set_lines on hidden buffer preserves "previous window" #9741', function() + insert([[ + visible buffer line 1 + line 2 + ]]) + local hiddenbuf = meths.create_buf(false,true) + command('vsplit') + command('vsplit') + feed('<c-w>l<c-w>l<c-w>l') + eq(3, funcs.winnr()) + feed('<c-w>h') + eq(2, funcs.winnr()) + meths.buf_set_lines(hiddenbuf, 0, -1, true, + {'hidden buffer line 1', 'line 2'}) + feed('<c-w>p') + eq(3, funcs.winnr()) + end) end) - describe('get_offset', function() + describe('nvim_buf_get_offset', function() local get_offset = curbufmeths.get_offset it('works', function() curbufmeths.set_lines(0,-1,true,{'Some\r','exa\000mple', '', 'buf\rfer', 'text'}) @@ -373,7 +391,7 @@ describe('api/buf', function() end) end) - describe('{get,set,del}_var', function() + describe('nvim_buf_get_var, nvim_buf_set_var, nvim_buf_del_var', function() it('works', function() curbuf('set_var', 'lua', {1, 2, {['3'] = 1}}) eq({1, 2, {['3'] = 1}}, curbuf('get_var', 'lua')) @@ -393,7 +411,7 @@ describe('api/buf', function() end) end) - describe('get_changedtick', function() + describe('nvim_buf_get_changedtick', function() it('works', function() eq(2, curbufmeths.get_changedtick()) curbufmeths.set_lines(0, 1, false, {'abc\0', '\0def', 'ghi'}) @@ -417,7 +435,7 @@ describe('api/buf', function() end) end) - describe('{get,set}_option', function() + describe('nvim_buf_get_option, nvim_buf_set_option', function() it('works', function() eq(8, curbuf('get_option', 'shiftwidth')) curbuf('set_option', 'shiftwidth', 4) @@ -430,7 +448,7 @@ describe('api/buf', function() end) end) - describe('{get,set}_name', function() + describe('nvim_buf_get_name, nvim_buf_set_name', function() it('works', function() nvim('command', 'new') eq('', curbuf('get_name')) @@ -438,14 +456,12 @@ describe('api/buf', function() curbuf('set_name', new_name) eq(new_name, curbuf('get_name')) nvim('command', 'w!') - local f = io.open(new_name) - ok(f ~= nil) - f:close() + eq(1, funcs.filereadable(new_name)) os.remove(new_name) end) end) - describe('is_loaded', function() + describe('nvim_buf_is_loaded', function() it('works', function() -- record our buffer number for when we unload it local bufnr = curbuf('get_number') @@ -470,7 +486,7 @@ describe('api/buf', function() end) end) - describe('is_valid', function() + describe('nvim_buf_is_valid', function() it('works', function() nvim('command', 'new') local b = nvim('get_current_buf') @@ -480,12 +496,12 @@ describe('api/buf', function() end) end) - describe('get_mark', function() + describe('nvim_buf_get_mark', function() it('works', function() curbuf('set_lines', -1, -1, true, {'a', 'bit of', 'text'}) curwin('set_cursor', {3, 4}) - nvim('command', 'mark V') - eq({3, 0}, curbuf('get_mark', 'V')) + nvim('command', 'mark v') + eq({3, 0}, curbuf('get_mark', 'v')) end) end) end) diff --git a/test/functional/lua/overrides_spec.lua b/test/functional/lua/overrides_spec.lua index 007d40874f..8f318e3503 100644 --- a/test/functional/lua/overrides_spec.lua +++ b/test/functional/lua/overrides_spec.lua @@ -300,3 +300,19 @@ describe('package.path/package.cpath', function() eq(new_paths_str, eval_lua('package.path'):sub(1, #new_paths_str)) end) end) + +describe('os.getenv', function() + it('returns nothing for undefined env var', function() + eq(NIL, funcs.luaeval('os.getenv("XTEST_1")')) + end) + it('returns env var set by the parent process', function() + local value = 'foo' + clear({env = {['XTEST_1']=value}}) + eq(value, funcs.luaeval('os.getenv("XTEST_1")')) + end) + it('returns env var set by let', function() + local value = 'foo' + meths.command('let $XTEST_1 = "'..value..'"') + eq(value, funcs.luaeval('os.getenv("XTEST_1")')) + end) +end) diff --git a/test/functional/ui/cmdline_spec.lua b/test/functional/ui/cmdline_spec.lua index 5d112d7f35..16be846647 100644 --- a/test/functional/ui/cmdline_spec.lua +++ b/test/functional/ui/cmdline_spec.lua @@ -600,6 +600,137 @@ local function test_cmdline(linegrid) pos = 12, }}} end) + + it('works together with ext_popupmenu', function() + local expected = { + {'define', '', '', ''}, + {'jump', '', '', ''}, + {'list', '', '', ''}, + {'place', '', '', ''}, + {'undefine', '', '', ''}, + {'unplace', '', '', ''}, + } + + command('set wildmode=full') + command('set wildmenu') + screen:set_option('ext_popupmenu', true) + feed(':sign <tab>') + + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]], cmdline={{ + firstc = ":", + content = {{"sign define"}}, + pos = 11, + }}, popupmenu={items=expected, pos=0, anchor={-1, 0, 5}}} + + feed('<tab>') + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]], cmdline={{ + firstc = ":", + content = {{"sign jump"}}, + pos = 9, + }}, popupmenu={items=expected, pos=1, anchor={-1, 0, 5}}} + + feed('<left><left>') + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]], cmdline={{ + firstc = ":", + content = {{"sign "}}, + pos = 5, + }}, popupmenu={items=expected, pos=-1, anchor={-1, 0, 5}}} + + feed('<right>') + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]], cmdline={{ + firstc = ":", + content = {{"sign define"}}, + pos = 11, + }}, popupmenu={items=expected, pos=0, anchor={-1, 0, 5}}} + + feed('a') + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]], cmdline={{ + firstc = ":", + content = {{"sign definea"}}, + pos = 12, + }}} + feed('<esc>') + + -- check positioning with multibyte char in pattern + command("e långfile1") + command("sp långfile2") + feed(':b lå<tab>') + screen:expect{grid=[[ + ^ | + {3:långfile2 }| + | + {2:långfile1 }| + | + ]], popupmenu={ + anchor = { -1, 0, 2 }, + items = {{ "långfile1", "", "", "" }, { "långfile2", "", "", "" }}, + pos = 0 + }, cmdline={{ + content = {{ "b långfile1" }}, + firstc = ":", + pos = 12 + }}} + end) + + it('ext_wildmenu takes precedence over ext_popupmenu', function() + local expected = { + 'define', + 'jump', + 'list', + 'place', + 'undefine', + 'unplace', + } + + command('set wildmode=full') + command('set wildmenu') + screen:set_option('ext_wildmenu', true) + screen:set_option('ext_popupmenu', true) + feed(':sign <tab>') + + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]], cmdline={{ + firstc = ":", + content = {{"sign define"}}, + pos = 11, + }}, wildmenu_items=expected, wildmenu_pos=0} + end) + end -- the representation of cmdline and cmdline_block contents changed with ext_linegrid diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua index 06d3b6bdda..d18d91de56 100644 --- a/test/functional/ui/float_spec.lua +++ b/test/functional/ui/float_spec.lua @@ -1352,7 +1352,7 @@ describe('floating windows', function() ]], float_pos={ [3] = {{ id = 1001 }, "NW", 1, 2, 5, true}, }, popupmenu={ - anchor = {0, 2, 3}, items = items, pos = 0 + anchor = {3, 0, 2}, items = items, pos = 0 }} else screen:expect{grid=[[ @@ -1364,7 +1364,7 @@ describe('floating windows', function() {0:~ }{12:~ }{0: }| {3:-- INSERT --} | ]], popupmenu={ - anchor = {2, 7}, items = items, pos = 0 + anchor = {1, 2, 7}, items = items, pos = 0 }} end @@ -1434,7 +1434,7 @@ describe('floating windows', function() ]], float_pos={ [3] = {{ id = 1001 }, "NW", 1, 2, 5, true}, }, popupmenu={ - anchor = {0, 0, 2}, items = items, pos = 0 + anchor = {2, 0, 0}, items = items, pos = 0 }} else screen:expect{grid=[[ @@ -1446,7 +1446,7 @@ describe('floating windows', function() {0:~ }{12:~ }{0: }| {3:-- INSERT --} | ]], popupmenu={ - anchor = {0, 0}, items = items, pos = 0 + anchor = {1, 0, 0}, items = items, pos = 0 }} end diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua index 388c6b3e95..efa776762b 100644 --- a/test/functional/ui/messages_spec.lua +++ b/test/functional/ui/messages_spec.lua @@ -179,7 +179,7 @@ describe('ui/ext_messages', function() {1:~ }| {1:~ }| ]], popupmenu={ - anchor = { 2, 0 }, + anchor = { 1, 2, 0 }, items = { { "alphpabet", "", "", "" }, { "alphanum", "", "", "" } }, pos = 1 }, showmode={ { "-- Keyword Local completion (^N^P) ", 3 }, { "match 1 of 2", 4 } }} @@ -194,7 +194,7 @@ describe('ui/ext_messages', function() {1:~ }| {1:~ }| ]], popupmenu={ - anchor = { 2, 0 }, + anchor = { 1, 2, 0 }, items = { { "alphpabet", "", "", "" }, { "alphanum", "", "", "" } }, pos = 1 }, messages={ { @@ -210,7 +210,7 @@ describe('ui/ext_messages', function() {1:~ }| {1:~ }| ]], popupmenu={ - anchor = { 2, 0 }, + anchor = { 1, 2, 0 }, items = { { "alphpabet", "", "", "" }, { "alphanum", "", "", "" } }, pos = 0 }, messages={ { diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua index 1e6ebb87f5..b457ebebab 100644 --- a/test/functional/ui/popupmenu_spec.lua +++ b/test/functional/ui/popupmenu_spec.lua @@ -50,7 +50,7 @@ describe('ui/ext_popupmenu', function() ]], popupmenu={ items=expected, pos=0, - anchor={1,0}, + anchor={1,1,0}, }} feed('<c-p>') @@ -66,7 +66,7 @@ describe('ui/ext_popupmenu', function() ]], popupmenu={ items=expected, pos=-1, - anchor={1,0}, + anchor={1,1,0}, }} -- down moves the selection in the menu, but does not insert anything @@ -83,7 +83,7 @@ describe('ui/ext_popupmenu', function() ]], popupmenu={ items=expected, pos=1, - anchor={1,0}, + anchor={1,1,0}, }} feed('<cr>') @@ -113,7 +113,7 @@ describe('ui/ext_popupmenu', function() ]], popupmenu={ items=expected, pos=0, - anchor={1,0}, + anchor={1,1,0}, }} meths.select_popupmenu_item(1,false,false,{}) @@ -129,7 +129,7 @@ describe('ui/ext_popupmenu', function() ]], popupmenu={ items=expected, pos=1, - anchor={1,0}, + anchor={1,1,0}, }} meths.select_popupmenu_item(2,true,false,{}) @@ -145,7 +145,7 @@ describe('ui/ext_popupmenu', function() ]], popupmenu={ items=expected, pos=2, - anchor={1,0}, + anchor={1,1,0}, }} meths.select_popupmenu_item(0,true,true,{}) @@ -174,7 +174,7 @@ describe('ui/ext_popupmenu', function() ]], popupmenu={ items=expected, pos=0, - anchor={1,0}, + anchor={1,1,0}, }} meths.select_popupmenu_item(-1,false,false,{}) @@ -190,7 +190,7 @@ describe('ui/ext_popupmenu', function() ]], popupmenu={ items=expected, pos=-1, - anchor={1,0}, + anchor={1,1,0}, }} meths.select_popupmenu_item(1,true,false,{}) @@ -206,7 +206,7 @@ describe('ui/ext_popupmenu', function() ]], popupmenu={ items=expected, pos=1, - anchor={1,0}, + anchor={1,1,0}, }} meths.select_popupmenu_item(-1,true,false,{}) @@ -222,7 +222,7 @@ describe('ui/ext_popupmenu', function() ]], popupmenu={ items=expected, pos=-1, - anchor={1,0}, + anchor={1,1,0}, }} meths.select_popupmenu_item(0,true,false,{}) @@ -238,7 +238,7 @@ describe('ui/ext_popupmenu', function() ]], popupmenu={ items=expected, pos=0, - anchor={1,0}, + anchor={1,1,0}, }} meths.select_popupmenu_item(-1,true,true,{}) @@ -269,7 +269,7 @@ describe('ui/ext_popupmenu', function() ]], popupmenu={ items=expected, pos=0, - anchor={1,0}, + anchor={1,1,0}, }} feed('<f1>') @@ -285,7 +285,7 @@ describe('ui/ext_popupmenu', function() ]], popupmenu={ items=expected, pos=2, - anchor={1,0}, + anchor={1,1,0}, }} feed('<f2>') @@ -301,7 +301,7 @@ describe('ui/ext_popupmenu', function() ]], popupmenu={ items=expected, pos=-1, - anchor={1,0}, + anchor={1,1,0}, }} feed('<f3>') @@ -366,6 +366,113 @@ describe('ui/ext_popupmenu', function() {2:-- INSERT --} | ]]) end) + + it('works with wildoptions=pum', function() + screen:try_resize(32,10) + command('set wildmenu') + command('set wildoptions=pum') + + local wild_expected = { + {'define', '', '', ''}, + {'jump', '', '', ''}, + {'list', '', '', ''}, + {'place', '', '', ''}, + {'undefine', '', '', ''}, + {'unplace', '', '', ''}, + } + + feed(':sign ') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + :sign ^ | + ]]) + + feed('<tab>') + screen:expect{grid=[[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + :sign define^ | + ]], popupmenu={items=wild_expected, pos=0, anchor={1, 9, 6}}} + + feed('<left>') + screen:expect{grid=[[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + :sign ^ | + ]], popupmenu={items=wild_expected, pos=-1, anchor={1, 9, 6}}} + + feed('<left>') + screen:expect{grid=[[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + :sign unplace^ | + ]], popupmenu={items=wild_expected, pos=5, anchor={1, 9, 6}}} + + feed('x') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + :sign unplacex^ | + ]]) + feed('<esc>') + + -- check positioning with multibyte char in pattern + command("e långfile1") + command("sp långfile2") + feed(':b lå<tab>') + screen:expect{grid=[[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {4:långfile2 }| + | + {1:~ }| + {1:~ }| + {3:långfile1 }| + :b långfile1^ | + ]], popupmenu={ + anchor = {1, 9, 3}, + items = {{"långfile1", "", "", "" }, {"långfile2", "", "", ""}}, + pos = 0, + }} + end) end) @@ -1209,7 +1316,7 @@ describe('builtin popupmenu', function() ]]) meths.input_mouse('wheel', 'down', '', 0, 6, 15) - screen:expect([[ + screen:expect{grid=[[ choice^ | {1:~ }| {n:word }{1: }| @@ -1218,7 +1325,7 @@ describe('builtin popupmenu', function() {n:thing }{1: }| {3:[No Name] [+] }| {2:-- INSERT --} | - ]]) + ]], unchanged=true} end) it('works with kind, menu and abbr attributes', function() @@ -1273,6 +1380,131 @@ describe('builtin popupmenu', function() ]]) end) + it('works with wildoptions=pum', function() + screen:try_resize(32,10) + command('set wildmenu') + command('set wildoptions=pum') + + feed(':sign ') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + :sign ^ | + ]]) + + feed('<tab>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }{s: define }{1: }| + {1:~ }{n: jump }{1: }| + {1:~ }{n: list }{1: }| + {1:~ }{n: place }{1: }| + {1:~ }{n: undefine }{1: }| + {1:~ }{n: unplace }{1: }| + :sign define^ | + ]]) + + feed('<left>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }{n: define }{1: }| + {1:~ }{n: jump }{1: }| + {1:~ }{n: list }{1: }| + {1:~ }{n: place }{1: }| + {1:~ }{n: undefine }{1: }| + {1:~ }{n: unplace }{1: }| + :sign ^ | + ]]) + + feed('<left>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }{n: define }{1: }| + {1:~ }{n: jump }{1: }| + {1:~ }{n: list }{1: }| + {1:~ }{n: place }{1: }| + {1:~ }{n: undefine }{1: }| + {1:~ }{s: unplace }{1: }| + :sign unplace^ | + ]]) + + feed('x') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + :sign unplacex^ | + ]]) + + feed('<esc>') + + -- check positioning with multibyte char in pattern + command("e långfile1") + command("sp långfile2") + feed(':b lå<tab>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {4:långfile2 }| + | + {1:~ }| + {1:~ }{s: långfile1 }{1: }| + {3:lå}{n: långfile2 }{3: }| + :b långfile1^ | + ]]) + + -- check doesn't crash on screen resize + screen:try_resize(20,6) + screen:expect([[ + | + {1:~ }| + {4:långfile2 }| + {s: långfile1 } | + {3:lå}{n: långfile2 }{3: }| + :b långfile1^ | + ]]) + + screen:try_resize(50,15) + screen:expect([[ + | + {1:~ }| + {4:långfile2 }| + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }{s: långfile1 }{1: }| + {3:lå}{n: långfile2 }{3: }| + :b långfile1^ | + ]]) + end) + it("'pumblend' RGB-color", function() screen:try_resize(60,14) screen:set_default_attr_ids({ diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index 8b1b77eb81..d072444ee1 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -937,10 +937,7 @@ function Screen:_handle_option_set(name, value) end function Screen:_handle_popupmenu_show(items, selected, row, col, grid) - if (not self._options.ext_multigrid) and grid == 1 then - grid = nil - end - self.popupmenu = {items=items, pos=selected, anchor={row, col, grid}} + self.popupmenu = {items=items, pos=selected, anchor={grid, row, col}} end function Screen:_handle_popupmenu_select(selected) diff --git a/test/functional/ui/wildmode_spec.lua b/test/functional/ui/wildmode_spec.lua index 7cd09fb222..cf22bb0a6f 100644 --- a/test/functional/ui/wildmode_spec.lua +++ b/test/functional/ui/wildmode_spec.lua @@ -29,8 +29,7 @@ describe("'wildmenu'", function() end it(':sign <tab> shows wildmenu completions', function() - command('set wildmode=full') - command('set wildmenu') + command('set wildmenu wildmode=full') feed(':sign <tab>') screen:expect([[ | @@ -201,14 +200,28 @@ describe('command line completion', function() ]]) end) + it('completes env var names #9681', function() + clear() + screen:attach() + command('let $XTEST_1 = "foo" | let $XTEST_2 = "bar"') + command('set wildmenu wildmode=full') + feed(':!echo $XTEST_<tab>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {2:XTEST_1}{3: XTEST_2 }| + :!echo $XTEST_1^ | + ]]) + end) + it('completes (multibyte) env var names #9655', function() clear({env={ ['XTEST_1AaあB']='foo', ['XTEST_2']='bar', }}) screen:attach() - command('set wildmode=full') - command('set wildmenu') + command('set wildmenu wildmode=full') feed(':!echo $XTEST_<tab>') screen:expect([[ | |