diff options
30 files changed, 901 insertions, 72 deletions
diff --git a/runtime/filetype.vim b/runtime/filetype.vim index 6a13e67ac5..4130db2534 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -1330,6 +1330,9 @@ au BufNewFile,BufRead *.pml setf promela au BufNewFile,BufRead *.proto setf proto au BufNewFile,BufRead *.pbtxt setf pbtxt +" Poke +au BufNewFile,BufRead *.pk setf poke + " Protocols au BufNewFile,BufRead */etc/protocols setf protocols diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 586123aac1..1b428718b5 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -39,6 +39,7 @@ #include "nvim/eval/typval.h" #include "nvim/eval/userfunc.h" #include "nvim/fileio.h" +#include "nvim/move.h" #include "nvim/ops.h" #include "nvim/option.h" #include "nvim/state.h" @@ -1246,6 +1247,99 @@ fail: return 0; } +/// Open a terminal instance in a buffer +/// +/// By default (and currently the only option) the terminal will not be +/// connected to an external process. Instead, input send on the channel +/// will be echoed directly by the terminal. This is useful to disply +/// ANSI terminal sequences returned as part of a rpc message, or similar. +/// +/// Note: to directly initiate the terminal using the right size, display the +/// buffer in a configured window before calling this. For instance, for a +/// floating display, first create an empty buffer using |nvim_create_buf()|, +/// then display it using |nvim_open_win()|, and then call this function. +/// Then |nvim_chan_send()| cal be called immediately to process sequences +/// in a virtual terminal having the intended size. +/// +/// @param buffer the buffer to use (expected to be empty) +/// @param opts Optional parameters. Reserved for future use. +/// @param[out] err Error details, if any +Integer nvim_open_term(Buffer buffer, Dictionary opts, Error *err) + FUNC_API_SINCE(7) +{ + buf_T *buf = find_buffer_by_handle(buffer, err); + if (!buf) { + return 0; + } + + if (opts.size > 0) { + api_set_error(err, kErrorTypeValidation, "opts dict isn't empty"); + return 0; + } + + TerminalOptions topts; + Channel *chan = channel_alloc(kChannelStreamInternal); + topts.data = chan; + // NB: overriden in terminal_check_size if a window is already + // displaying the buffer + topts.width = (uint16_t)MAX(curwin->w_width_inner - win_col_off(curwin), 0); + topts.height = (uint16_t)curwin->w_height_inner; + topts.write_cb = term_write; + topts.resize_cb = term_resize; + topts.close_cb = term_close; + Terminal *term = terminal_open(buf, topts); + terminal_check_size(term); + chan->term = term; + channel_incref(chan); + return (Integer)chan->id; +} + +static void term_write(char *buf, size_t size, void *data) +{ + // TODO(bfredl): lua callback +} + +static void term_resize(uint16_t width, uint16_t height, void *data) +{ + // TODO(bfredl): lua callback +} + +static void term_close(void *data) +{ + Channel *chan = data; + terminal_destroy(chan->term); + chan->term = NULL; + channel_decref(chan); +} + + +/// Send data to channel `id`. For a job, it writes it to the +/// stdin of the process. For the stdio channel |channel-stdio|, +/// it writes to Nvim's stdout. For an internal terminal instance +/// (|nvim_open_term()|) it writes directly to terimal output. +/// See |channel-bytes| for more information. +/// +/// This function writes raw data, not RPC messages. If the channel +/// was created with `rpc=true` then the channel expects RPC +/// messages, use |vim.rpcnotify()| and |vim.rpcrequest()| instead. +/// +/// @param chan id of the channel +/// @param data data to write. 8-bit clean: can contain NUL bytes. +/// @param[out] err Error details, if any +void nvim_chan_send(Integer chan, String data, Error *err) + FUNC_API_SINCE(7) FUNC_API_REMOTE_ONLY FUNC_API_LUA_ONLY +{ + const char *error = NULL; + if (!data.size) { + return; + } + + channel_send((uint64_t)chan, data.data, data.size, &error); + if (error) { + api_set_error(err, kErrorTypeValidation, "%s", error); + } +} + /// Open a new window. /// /// Currently this is used to open floating and external windows. diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 3de2e0f342..2ddb925d40 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -968,7 +968,7 @@ static int do_autocmd_event(event_T event, // Implementation of ":doautocmd [group] event [fname]". // Return OK for success, FAIL for failure; int do_doautocmd(char_u *arg, - int do_msg, // give message for no matching autocmds? + bool do_msg, // give message for no matching autocmds? bool *did_something) { char_u *fname; @@ -1017,11 +1017,12 @@ int do_doautocmd(char_u *arg, // ":doautoall": execute autocommands for each loaded buffer. void ex_doautoall(exarg_T *eap) { - int retval; + int retval = OK; aco_save_T aco; char_u *arg = eap->arg; int call_do_modelines = check_nomodeline(&arg); bufref_T bufref; + bool did_aucmd; // This is a bit tricky: For some commands curwin->w_buffer needs to be // equal to curbuf, but for some buffers there may not be a window. @@ -1029,14 +1030,14 @@ void ex_doautoall(exarg_T *eap) // gives problems when the autocommands make changes to the list of // buffers or windows... FOR_ALL_BUFFERS(buf) { - if (buf->b_ml.ml_mfp == NULL) { + // Only do loaded buffers and skip the current buffer, it's done last. + if (buf->b_ml.ml_mfp == NULL || buf == curbuf) { continue; } // Find a window for this buffer and save some values. aucmd_prepbuf(&aco, buf); set_bufref(&bufref, buf); - bool did_aucmd; // execute the autocommands for this buffer retval = do_doautocmd(arg, false, &did_aucmd); @@ -1052,10 +1053,19 @@ void ex_doautoall(exarg_T *eap) // Stop if there is some error or buffer was deleted. if (retval == FAIL || !bufref_valid(&bufref)) { + retval = FAIL; break; } } + // Execute autocommands for the current buffer last. + if (retval == OK) { + (void)do_doautocmd(arg, false, &did_aucmd); + if (call_do_modelines && did_aucmd) { + do_modelines(0); + } + } + check_cursor(); // just in case lines got deleted } @@ -1661,11 +1671,13 @@ static bool apply_autocmds_group(event_T event, did_filetype = false; while (au_pending_free_buf != NULL) { buf_T *b = au_pending_free_buf->b_next; + xfree(au_pending_free_buf); au_pending_free_buf = b; } while (au_pending_free_win != NULL) { win_T *w = au_pending_free_win->w_next; + xfree(au_pending_free_win); au_pending_free_win = w; } diff --git a/src/nvim/channel.c b/src/nvim/channel.c index 09a34ca9fe..7a08ba58d0 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -161,7 +161,7 @@ void channel_init(void) /// /// Channel is allocated with refcount 1, which should be decreased /// when the underlying stream closes. -static Channel *channel_alloc(ChannelStreamType type) +Channel *channel_alloc(ChannelStreamType type) { Channel *chan = xcalloc(1, sizeof(*chan)); if (type == kChannelStreamStdio) { @@ -503,7 +503,7 @@ size_t channel_send(uint64_t id, char *data, size_t len, const char **error) { Channel *chan = find_channel(id); if (!chan) { - EMSG(_(e_invchan)); + *error = _(e_invchan); goto err; } @@ -518,6 +518,11 @@ size_t channel_send(uint64_t id, char *data, size_t len, const char **error) return len * written; } + if (chan->streamtype == kChannelStreamInternal && chan->term) { + terminal_receive(chan->term, data, len); + return len; + } + Stream *in = channel_instream(chan); if (in->closed) { @@ -724,8 +729,8 @@ static void channel_callback_call(Channel *chan, CallbackReader *reader) /// Open terminal for channel /// /// Channel `chan` is assumed to be an open pty channel, -/// and curbuf is assumed to be a new, unmodified buffer. -void channel_terminal_open(Channel *chan) +/// and `buf` is assumed to be a new, unmodified buffer. +void channel_terminal_open(buf_T *buf, Channel *chan) { TerminalOptions topts; topts.data = chan; @@ -734,8 +739,8 @@ void channel_terminal_open(Channel *chan) topts.write_cb = term_write; topts.resize_cb = term_resize; topts.close_cb = term_close; - curbuf->b_p_channel = (long)chan->id; // 'channel' option - Terminal *term = terminal_open(topts); + buf->b_p_channel = (long)chan->id; // 'channel' option + Terminal *term = terminal_open(buf, topts); chan->term = term; channel_incref(chan); } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 63d5216cc4..f190ef14c4 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3007,7 +3007,8 @@ static size_t varnamebuflen = 0; /* * Function to concatenate a prefix and a variable name. */ -static char_u *cat_prefix_varname(int prefix, char_u *name) +char_u *cat_prefix_varname(int prefix, const char_u *name) + FUNC_ATTR_NONNULL_ALL { size_t len = STRLEN(name) + 3; diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 60229e1ebc..2b04469af7 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -117,8 +117,12 @@ char_u *get_function_name(expand_T *xp, int idx) intidx = -1; if (intidx < 0) { name = get_user_func_name(xp, idx); - if (name != NULL) + if (name != NULL) { + if (*name != '<' && STRNCMP("g:", xp->xp_pattern, 2) == 0) { + return cat_prefix_varname('g', name); + } return name; + } } while ((size_t)++intidx < ARRAY_SIZE(functions) && functions[intidx].name[0] == '\0') { @@ -10731,7 +10735,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) INTEGER_OBJ(pid), false, false, &err); api_clear_error(&err); - channel_terminal_open(chan); + channel_terminal_open(curbuf, chan); channel_create_event(chan, NULL); } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 5979f4d3a0..9977be56ca 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -5117,6 +5117,18 @@ ExpandFromContext ( if (xp->xp_context == EXPAND_PACKADD) { return ExpandPackAddDir(pat, num_file, file); } + + // When expanding a function name starting with s:, match the <SNR>nr_ + // prefix. + char_u *tofree = NULL; + if (xp->xp_context == EXPAND_USER_FUNC && STRNCMP(pat, "^s:", 3) == 0) { + const size_t len = STRLEN(pat) + 20; + + tofree = xmalloc(len); + snprintf((char *)tofree, len, "^<SNR>\\d\\+_%s", pat + 3); + pat = tofree; + } + if (xp->xp_context == EXPAND_LUA) { ILOG("PAT %s", pat); return nlua_expand_pat(xp, pat, num_file, file); @@ -5195,6 +5207,7 @@ ExpandFromContext ( } vim_regfree(regmatch.regprog); + xfree(tofree); return ret; } diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c index 63789b3981..09453e100d 100644 --- a/src/nvim/ex_session.c +++ b/src/nvim/ex_session.c @@ -387,9 +387,10 @@ static int put_view( if (wp->w_alt_fnum) { buf_T *const alt = buflist_findnr(wp->w_alt_fnum); - // Set the alternate file. + // Set the alternate file if the buffer is listed. if ((flagp == &ssop_flags) && alt != NULL && alt->b_fname != NULL && *alt->b_fname != NUL + && alt->b_p_bl && (fputs("balt ", fd) < 0 || ses_fname(fd, alt, flagp, true) == FAIL)) { return FAIL; diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua index 7e78b9e9d6..d2a7c16186 100644 --- a/src/nvim/generators/gen_api_dispatch.lua +++ b/src/nvim/generators/gen_api_dispatch.lua @@ -104,8 +104,11 @@ for _,f in ipairs(shallowcopy(functions)) do elseif startswith(f.name, "nvim_tabpage_") then ismethod = true end + f.remote = f.remote_only or not f.lua_only + f.lua = f.lua_only or not f.remote_only + f.eval = (not f.lua_only) and (not f.remote_only) else - f.remote_only = true + f.remote = true f.since = 0 f.deprecated_since = 1 end @@ -127,7 +130,8 @@ for _,f in ipairs(shallowcopy(functions)) do newf.return_type = "Object" end newf.impl_name = f.name - newf.remote_only = true + newf.lua = false + newf.eval = false newf.since = 0 newf.deprecated_since = 1 functions[#functions+1] = newf @@ -192,7 +196,7 @@ end -- the real API. for i = 1, #functions do local fn = functions[i] - if fn.impl_name == nil and not fn.lua_only then + if fn.impl_name == nil and fn.remote then local args = {} output:write('Object handle_'..fn.name..'(uint64_t channel_id, Array args, Error *error)') @@ -323,7 +327,7 @@ void msgpack_rpc_init_method_table(void) for i = 1, #functions do local fn = functions[i] - if not fn.lua_only then + if fn.remote then output:write(' msgpack_rpc_add_method_handler('.. '(String) {.data = "'..fn.name..'", '.. '.size = sizeof("'..fn.name..'") - 1}, '.. @@ -492,7 +496,7 @@ local function process_function(fn) end for _, fn in ipairs(functions) do - if not fn.remote_only or fn.name:sub(1, 4) == '_vim' then + if fn.lua or fn.name:sub(1, 4) == '_vim' then process_function(fn) end end diff --git a/src/nvim/generators/gen_eval.lua b/src/nvim/generators/gen_eval.lua index d16453530f..679895421a 100644 --- a/src/nvim/generators/gen_eval.lua +++ b/src/nvim/generators/gen_eval.lua @@ -25,7 +25,7 @@ local gperfpipe = io.open(funcsfname .. '.gperf', 'wb') local funcs = require('eval').funcs local metadata = mpack.unpack(io.open(metadata_file, 'rb'):read("*all")) for _,fun in ipairs(metadata) do - if not (fun.remote_only or fun.lua_only) then + if fun.eval then funcs[fun.name] = { args=#fun.parameters, func='api_wrapper', diff --git a/src/nvim/grid_defs.h b/src/nvim/grid_defs.h index e14aae73d8..752be4f5a8 100644 --- a/src/nvim/grid_defs.h +++ b/src/nvim/grid_defs.h @@ -76,6 +76,12 @@ typedef struct { int comp_row; int comp_col; + // Requested width and height of the grid upon resize. Used by + // `ui_compositor` to correctly determine which regions need to + // be redrawn. + int comp_width; + int comp_height; + // z-index of the grid. Grids with higher index is draw on top. // default_grid.comp_index is always zero. size_t comp_index; @@ -86,6 +92,6 @@ typedef struct { } ScreenGrid; #define SCREEN_GRID_INIT { 0, NULL, NULL, NULL, NULL, NULL, 0, 0, false, \ - false, 0, 0, false, true, 0, 0, 0, false } + false, 0, 0, false, true, 0, 0, 0, 0, 0, false } #endif // NVIM_GRID_DEFS_H diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index f28658aa29..9dc85797b4 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -717,23 +717,22 @@ static int mouse_adjust_click(win_T *wp, int row, int col) // Check clicked cell is foldcolumn int mouse_check_fold(void) { - int grid = mouse_grid; - int row = mouse_row; - int col = mouse_col; + int click_grid = mouse_grid; + int click_row = mouse_row; + int click_col = mouse_col; int mouse_char = ' '; win_T *wp; - wp = mouse_find_win(&grid, &row, &col); + wp = mouse_find_win(&click_grid, &click_row, &click_col); if (wp && mouse_row >= 0 && mouse_row < Rows && mouse_col >= 0 && mouse_col <= Columns) { int multigrid = ui_has(kUIMultigrid); ScreenGrid *gp = multigrid ? &wp->w_grid : &default_grid; int fdc = win_fdccol_count(wp); - - row = multigrid && mouse_grid == 0 ? row : mouse_row; - col = multigrid && mouse_grid == 0 ? col : mouse_col; + int row = multigrid && mouse_grid == 0 ? click_row : mouse_row; + int col = multigrid && mouse_grid == 0 ? click_col : mouse_col; // Remember the character under the mouse, might be one of foldclose or // foldopen fillchars in the fold column. @@ -743,8 +742,8 @@ int mouse_check_fold(void) } // Check for position outside of the fold column. - if (wp->w_p_rl ? col < wp->w_width_inner - fdc : - col >= fdc + (cmdwin_type == 0 ? 0 : 1)) { + if (wp->w_p_rl ? click_col < wp->w_width_inner - fdc : + click_col >= fdc + (cmdwin_type == 0 ? 0 : 1)) { mouse_char = ' '; } } diff --git a/src/nvim/option.c b/src/nvim/option.c index ac25c86b5f..dbd8ceb55c 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -2086,9 +2086,12 @@ int was_set_insecurely(win_T *const wp, char_u *opt, int opt_flags) /// Get a pointer to the flags used for the P_INSECURE flag of option /// "opt_idx". For some local options a local flags field is used. +/// NOTE: Caller must make sure that "wp" is set to the window from which +/// the option is used. static uint32_t *insecure_flag(win_T *const wp, int opt_idx, int opt_flags) { - if (opt_flags & OPT_LOCAL) + if (opt_flags & OPT_LOCAL) { + assert(wp != NULL); switch ((int)options[opt_idx].indir) { case PV_STL: return &wp->w_p_stl_flags; case PV_FDE: return &wp->w_p_fde_flags; @@ -2097,6 +2100,7 @@ static uint32_t *insecure_flag(win_T *const wp, int opt_idx, int opt_flags) case PV_FEX: return &wp->w_buffer->b_p_fex_flags; case PV_INEX: return &wp->w_buffer->b_p_inex_flags; } + } // Nothing special, return global flags field. return &options[opt_idx].flags; @@ -6178,6 +6182,8 @@ set_context_in_set_cmd( xp->xp_backslash = XP_BS_THREE; else xp->xp_backslash = XP_BS_ONE; + } else if (p == (char_u *)&p_ft) { + xp->xp_context = EXPAND_FILETYPE; } else { xp->xp_context = EXPAND_FILES; // for 'tags' need three backslashes for a space diff --git a/src/nvim/options.lua b/src/nvim/options.lua index fe108ef1cc..f4c1ac9131 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -900,6 +900,7 @@ return { normal_fname_chars=true, vi_def=true, alloced=true, + expand=true, varname='p_ft', defaults={if_true={vi=""}} }, diff --git a/src/nvim/screen.c b/src/nvim/screen.c index aa3a7ae7ed..467cac4f27 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -6274,6 +6274,9 @@ retry: tab_page_click_defs = new_tab_page_click_defs; tab_page_click_defs_size = Columns; + default_grid.comp_height = Rows; + default_grid.comp_width = Columns; + default_grid.row_offset = 0; default_grid.col_offset = 0; default_grid.handle = DEFAULT_GRID_HANDLE; diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index f6995cddb6..913ef3baed 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -169,19 +169,20 @@ void terminal_teardown(void) multiqueue_free(refresh_timer.events); time_watcher_close(&refresh_timer, NULL); pmap_free(ptr_t)(invalidated_terminals); + invalidated_terminals = NULL; } // public API {{{ -Terminal *terminal_open(TerminalOptions opts) +Terminal *terminal_open(buf_T *buf, TerminalOptions opts) { // Create a new terminal instance and configure it Terminal *rv = xcalloc(1, sizeof(Terminal)); rv->opts = opts; rv->cursor.visible = true; // Associate the terminal instance with the new buffer - rv->buf_handle = curbuf->handle; - curbuf->terminal = rv; + rv->buf_handle = buf->handle; + buf->terminal = rv; // Create VTerm rv->vt = vterm_new(opts.height, opts.width); vterm_set_utf8(rv->vt, 1); @@ -198,28 +199,36 @@ Terminal *terminal_open(TerminalOptions opts) // have as many lines as screen rows when refresh_scrollback is called rv->invalid_start = 0; rv->invalid_end = opts.height; - refresh_screen(rv, curbuf); + + aco_save_T aco; + aucmd_prepbuf(&aco, buf); + + refresh_screen(rv, buf); set_option_value("buftype", 0, "terminal", OPT_LOCAL); // -V666 // Default settings for terminal buffers - curbuf->b_p_ma = false; // 'nomodifiable' - curbuf->b_p_ul = -1; // 'undolevels' - curbuf->b_p_scbk = // 'scrollback' (initialize local from global) + buf->b_p_ma = false; // 'nomodifiable' + buf->b_p_ul = -1; // 'undolevels' + buf->b_p_scbk = // 'scrollback' (initialize local from global) (p_scbk < 0) ? 10000 : MAX(1, p_scbk); - curbuf->b_p_tw = 0; // 'textwidth' + buf->b_p_tw = 0; // 'textwidth' set_option_value("wrap", false, NULL, OPT_LOCAL); set_option_value("list", false, NULL, OPT_LOCAL); - buf_set_term_title(curbuf, (char *)curbuf->b_ffname); + if (buf->b_ffname != NULL) { + buf_set_term_title(buf, (char *)buf->b_ffname); + } RESET_BINDING(curwin); // Reset cursor in current window. curwin->w_cursor = (pos_T){ .lnum = 1, .col = 0, .coladd = 0 }; // Apply TermOpen autocmds _before_ configuring the scrollback buffer. - apply_autocmds(EVENT_TERMOPEN, NULL, NULL, false, curbuf); + apply_autocmds(EVENT_TERMOPEN, NULL, NULL, false, buf); // Local 'scrollback' _after_ autocmds. - curbuf->b_p_scbk = (curbuf->b_p_scbk < 1) ? SB_MAX : curbuf->b_p_scbk; + buf->b_p_scbk = (buf->b_p_scbk < 1) ? SB_MAX : buf->b_p_scbk; + + aucmd_restbuf(&aco); // Configure the scrollback buffer. - rv->sb_size = (size_t)curbuf->b_p_scbk; + rv->sb_size = (size_t)buf->b_p_scbk; rv->sb_buffer = xmalloc(sizeof(ScrollbackLine *) * rv->sb_size); // Configure the color palette. Try to get the color from: @@ -511,7 +520,9 @@ void terminal_destroy(Terminal *term) } if (!term->refcount) { - if (pmap_has(ptr_t)(invalidated_terminals, term)) { + // might be destroyed after terminal_teardown is invoked + if (invalidated_terminals + && pmap_has(ptr_t)(invalidated_terminals, term)) { // flush any pending changes to the buffer block_autocmds(); refresh_terminal(term); @@ -1324,6 +1335,7 @@ static void refresh_scrollback(Terminal *term, buf_T *buf) // focused) of a invalidated terminal static void refresh_screen(Terminal *term, buf_T *buf) { + assert(buf == curbuf); // TODO(bfredl): remove this condition int changed = 0; int added = 0; int height; diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 1f3a45a9ab..7788e09d43 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -1922,20 +1922,28 @@ func Test_autocmd_window() %bw! edit one.txt tabnew two.txt + vnew three.txt + tabnew four.txt + tabprevious let g:blist = [] - augroup aucmd_win_test + augroup aucmd_win_test1 au! au BufEnter * call add(g:blist, [expand('<afile>'), \ win_gettype(bufwinnr(expand('<afile>')))]) augroup END doautoall BufEnter - call assert_equal([['one.txt', 'autocmd'], ['two.txt', '']], g:blist) + call assert_equal([ + \ ['one.txt', 'autocmd'], + \ ['two.txt', ''], + \ ['four.txt', 'autocmd'], + \ ['three.txt', ''], + \ ], g:blist) - augroup aucmd_win_test + augroup aucmd_win_test1 au! augroup END - augroup! aucmd_win_test + augroup! aucmd_win_test1 %bw! endfunc diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index a66aee5e02..489b2477e6 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -569,6 +569,21 @@ func Test_cmdline_complete_user_cmd() delcommand Foo endfunc +func s:ScriptLocalFunction() + echo 'yes' +endfunc + +func Test_cmdline_complete_user_func() + call feedkeys(":func Test_cmdline_complete_user\<Tab>\<Home>\"\<cr>", 'tx') + call assert_match('"func Test_cmdline_complete_user', @:) + call feedkeys(":func s:ScriptL\<Tab>\<Home>\"\<cr>", 'tx') + call assert_match('"func <SNR>\d\+_ScriptLocalFunction', @:) + + " g: prefix also works + call feedkeys(":echo g:Test_cmdline_complete_user_f\<Tab>\<Home>\"\<cr>", 'tx') + call assert_match('"echo g:Test_cmdline_complete_user_func', @:) +endfunc + func Test_cmdline_complete_user_names() if has('unix') && executable('whoami') let whoami = systemlist('whoami')[0] diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 6bc5fba5db..76e5de7df9 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -358,6 +358,7 @@ let s:filename_checks = { \ 'po': ['file.po', 'file.pot'], \ 'pod': ['file.pod'], \ 'pod6': ['file.pod6'], + \ 'poke': ['file.pk'], \ 'postscr': ['file.ps', 'file.pfa', 'file.afm', 'file.eps', 'file.epsf', 'file.epsi', 'file.ai'], \ 'pov': ['file.pov'], \ 'povini': ['.povrayrc'], diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim index 7c7804212b..8486f3ff68 100644 --- a/src/nvim/testdir/test_mksession.vim +++ b/src/nvim/testdir/test_mksession.vim @@ -331,6 +331,20 @@ func Test_mkview_no_balt() %bwipe endfunc +func Test_mksession_no_balt() + edit Xtestfile1 + edit Xtestfile2 + + bdelete Xtestfile1 + mksession! Xtestview + + source Xtestview + call assert_equal(0, buflisted('Xtestfile1')) + + call delete('Xtestview') + %bwipe +endfunc + " Test :mkview with a file argument. func Test_mkview_file() " Create a view with line number and a fold. diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim index 1202b842fd..79481097ec 100644 --- a/src/nvim/testdir/test_options.vim +++ b/src/nvim/testdir/test_options.vim @@ -230,6 +230,13 @@ func Test_set_completion() call feedkeys(":set tags=./\\\\ dif\<C-A>\<C-B>\"\<CR>", 'tx') call assert_equal('"set tags=./\\ diff diffexpr diffopt', @:) set tags& + + " Expand values for 'filetype' + call feedkeys(":set filetype=sshdconfi\<Tab>\<C-B>\"\<CR>", 'xt') + call assert_equal('"set filetype=sshdconfig', @:) + call feedkeys(":set filetype=a\<C-A>\<C-B>\"\<CR>", 'xt') + " call assert_equal('"set filetype=' .. getcompletion('a*', 'filetype')->join(), @:) + call assert_equal('"set filetype=' .. join(getcompletion('a*', 'filetype')), @:) endfunc func Test_set_errors() @@ -627,6 +634,28 @@ func Test_opt_winminheight_term() call delete('Xwinminheight') endfunc +func Test_opt_winminheight_term_tabs() + " See test/functional/legacy/options_spec.lua + CheckRunVimInTerminal + + " The tabline should be taken into account. + let lines =<< trim END + set wmh=0 stal=2 + split + split + split + split + tabnew + END + call writefile(lines, 'Xwinminheight') + let buf = RunVimInTerminal('-S Xwinminheight', #{rows: 11}) + call term_sendkeys(buf, ":set wmh=1\n") + call WaitForAssert({-> assert_match('E36: Not enough room', term_getline(buf, 11))}) + + call StopVimInTerminal(buf) + call delete('Xwinminheight') +endfunc + " Test for setting option value containing spaces with isfname+=32 func Test_isfname_with_options() set isfname+=32 diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim index e6ad92f483..eebf85af6f 100644 --- a/src/nvim/testdir/test_startup.vim +++ b/src/nvim/testdir/test_startup.vim @@ -111,10 +111,8 @@ func Test_pack_in_rtp_when_plugins_run() endfunc func Test_help_arg() - if !has('unix') && has('gui') - " this doesn't work with gvim on MS-Windows - return - endif + CheckNotMSWindows + if RunVim([], [], '--help >Xtestout') let lines = readfile('Xtestout') call assert_true(len(lines) > 20) @@ -412,6 +410,134 @@ func Test_A_F_H_arg() call delete('Xtestout') endfunc +" Test the --echo-wid argument (for GTK GUI only). +func Test_echo_wid() + CheckCanRunGui + CheckFeature gui_gtk + + if RunVim([], [], '-g --echo-wid -cq >Xtest_echo_wid') + let lines = readfile('Xtest_echo_wid') + call assert_equal(1, len(lines)) + call assert_match('^WID: \d\+$', lines[0]) + endif + + call delete('Xtest_echo_wid') +endfunction + +" Test the -reverse and +reverse arguments (for GUI only). +func Test_reverse() + CheckCanRunGui + CheckNotMSWindows + + let after =<< trim [CODE] + call writefile([&background], "Xtest_reverse") + qall + [CODE] + if RunVim([], after, '-f -g -reverse') + let lines = readfile('Xtest_reverse') + call assert_equal(['dark'], lines) + endif + if RunVim([], after, '-f -g +reverse') + let lines = readfile('Xtest_reverse') + call assert_equal(['light'], lines) + endif + + call delete('Xtest_reverse') +endfunc + +" Test the -background and -foreground arguments (for GUI only). +func Test_background_foreground() + CheckCanRunGui + CheckNotMSWindows + + " Is there a better way to check the effect of -background & -foreground + " other than merely looking at &background (dark or light)? + let after =<< trim [CODE] + call writefile([&background], "Xtest_fg_bg") + qall + [CODE] + if RunVim([], after, '-f -g -background darkred -foreground yellow') + let lines = readfile('Xtest_fg_bg') + call assert_equal(['dark'], lines) + endif + if RunVim([], after, '-f -g -background ivory -foreground darkgreen') + let lines = readfile('Xtest_fg_bg') + call assert_equal(['light'], lines) + endif + + call delete('Xtest_fg_bg') +endfunc + +" Test the -font argument (for GUI only). +func Test_font() + CheckCanRunGui + CheckNotMSWindows + + if has('gui_gtk') + let font = 'Courier 14' + elseif has('gui_motif') || has('gui_athena') + let font = '-misc-fixed-bold-*' + else + throw 'Skipped: test does not set a valid font for this GUI' + endif + + let after =<< trim [CODE] + call writefile([&guifont], "Xtest_font") + qall + [CODE] + + if RunVim([], after, '--nofork -g -font "' .. font .. '"') + let lines = readfile('Xtest_font') + call assert_equal([font], lines) + endif + + call delete('Xtest_font') +endfunc + +" Test the -geometry argument (for GUI only). +func Test_geometry() + CheckCanRunGui + CheckNotMSWindows + + if has('gui_motif') || has('gui_athena') + " FIXME: With GUI Athena or Motif, the value of getwinposx(), + " getwinposy() and getwinpos() do not match exactly the + " value given in -geometry. Why? + " So only check &columns and &lines for those GUIs. + let after =<< trim [CODE] + call writefile([&columns, &lines], "Xtest_geometry") + qall + [CODE] + if RunVim([], after, '-f -g -geometry 31x13+41+43') + let lines = readfile('Xtest_geometry') + call assert_equal(['31', '13'], lines) + endif + else + let after =<< trim [CODE] + call writefile([&columns, &lines, getwinposx(), getwinposy(), string(getwinpos())], "Xtest_geometry") + qall + [CODE] + if RunVim([], after, '-f -g -geometry 31x13+41+43') + let lines = readfile('Xtest_geometry') + call assert_equal(['31', '13', '41', '43', '[41, 43]'], lines) + endif + endif + + call delete('Xtest_geometry') +endfunc + +" Test the -iconic argument (for GUI only). +func Test_iconic() + CheckCanRunGui + CheckNotMSWindows + + call RunVim([], [], '-f -g -iconic -cq') + + " TODO: currently only start vim iconified, but does not + " check that vim is iconified. How could this be checked? +endfunc + + func Test_invalid_args() if !has('unix') || has('gui_running') " can't get output of Vim. @@ -777,17 +903,12 @@ func Test_progname() let prognames = ['nvim'] for progname in prognames - if empty($DISPLAY) - if progname =~# 'g' - " Can't run gvim, gview (etc.) if $DISPLAY is not setup. - continue - endif - if has('gui') && (progname ==# 'evim' || progname ==# 'eview') - " evim or eview will start the GUI if there is gui support. - " So don't try to start them either if $DISPLAY is not setup. - continue - endif - endif + let run_with_gui = (progname =~# 'g') || (has('gui') && (progname ==# 'evim' || progname ==# 'eview')) + + if empty($DISPLAY) && run_with_gui + " Can't run gvim, gview (etc.) if $DISPLAY is not setup. + continue + endif exe 'silent !ln -s -f ' ..exepath(GetVimProg()) .. ' Xprogname/' .. progname @@ -801,7 +922,15 @@ func Test_progname() if progname =~# 'g' && !has('gui') call assert_equal("E25: GUI cannot be used: Not enabled at compile time\n", stdout_stderr, progname) else - call assert_equal('', stdout_stderr, progname) + " GUI motif can output some warnings like this: + " Warning: + " Name: subMenu + " Class: XmCascadeButton + " Illegal mnemonic character; Could not convert X KEYSYM to a keycode + " So don't check that stderr is empty with GUI Motif. + if run_with_gui && !has('gui_motif') + call assert_equal('', stdout_stderr, progname) + endif call assert_equal(expectations[progname], readfile('Xprogname_out'), progname) endif diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c index 06efc9fa99..946215d957 100644 --- a/src/nvim/ui_compositor.c +++ b/src/nvim/ui_compositor.c @@ -127,6 +127,9 @@ bool ui_comp_put_grid(ScreenGrid *grid, int row, int col, int height, int width, bool valid, bool on_top) { bool moved; + + grid->comp_height = height; + grid->comp_width = width; if (grid->comp_index != 0) { moved = (row != grid->comp_row) || (col != grid->comp_col); if (ui_comp_should_draw()) { @@ -334,17 +337,25 @@ static void compose_line(Integer row, Integer startcol, Integer endcol, sattr_T *bg_attrs = &default_grid.attrs[default_grid.line_offset[row] +(size_t)startcol]; + int grid_width, grid_height; while (col < endcol) { int until = 0; for (size_t i = 0; i < kv_size(layers); i++) { ScreenGrid *g = kv_A(layers, i); - if (g->comp_row > row || row >= g->comp_row + g->Rows + // compose_line may have been called after a shrinking operation but + // before the resize has actually been applied. Therefore, we need to + // first check to see if any grids have pending updates to width/height, + // to ensure that we don't accidentally put any characters into `linebuf` + // that have been invalidated. + grid_width = MIN(g->Columns, g->comp_width); + grid_height = MIN(g->Rows, g->comp_height); + if (g->comp_row > row || row >= g->comp_row + grid_height || g->comp_disabled) { continue; } - if (g->comp_col <= col && col < g->comp_col+g->Columns) { + if (g->comp_col <= col && col < g->comp_col + grid_width) { grid = g; - until = g->comp_col+g->Columns; + until = g->comp_col + grid_width; } else if (g->comp_col > col) { until = MIN(until, g->comp_col); } diff --git a/src/nvim/window.c b/src/nvim/window.c index 0f717a2f90..aa8d8727e7 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -710,7 +710,7 @@ int win_fdccol_count(win_T *wp) } -static void ui_ext_win_position(win_T *wp) +void ui_ext_win_position(win_T *wp) { if (!wp->w_floating) { ui_call_win_pos(wp->w_grid.handle, wp->handle, wp->w_winrow, @@ -5501,8 +5501,8 @@ void win_setminheight(void) // loop until there is a 'winminheight' that is possible while (p_wmh > 0) { - const int room = Rows - p_ch - tabline_height(); - const int needed = frame_minheight(topframe, NULL); + const int room = Rows - p_ch; + const int needed = min_rows() - 1; // 1 was added for the cmdline if (room >= needed) { break; } diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 437a1858f3..3db44f3f11 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -2087,4 +2087,67 @@ describe('API', function() eq("", meths.exec("messages", true)) end) end) + + + describe('nvim_open_term', function() + local screen + + before_each(function() + clear() + screen = Screen.new(100, 35) + screen:attach() + screen:set_default_attr_ids({ + [0] = {bold=true, foreground=Screen.colors.Blue}, + [1] = {background = Screen.colors.Plum1}; + [2] = {background = tonumber('0xffff40'), bg_indexed = true}; + [3] = {background = Screen.colors.Plum1, fg_indexed = true, foreground = tonumber('0x00e000')}; + [4] = {bold = true, reverse = true, background = Screen.colors.Plum1}; + }) + end) + + it('can batch process sequences', function() + local b = meths.create_buf(true,true) + meths.open_win(b, false, {width=79, height=31, row=1, col=1, relative='editor'}) + local t = meths.open_term(b, {}) + + meths.chan_send(t, io.open("test/functional/fixtures/smile2.cat", "r"):read("*a")) + screen:expect{grid=[[ + ^ | + {0:~}{1::smile }{0: }| + {0:~}{1: }{2:oooo$$$$$$$$$$$$oooo}{1: }{0: }| + {0:~}{1: }{2:oo$$$$$$$$$$$$$$$$$$$$$$$$o}{1: }{0: }| + {0:~}{1: }{2:oo$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o}{1: }{2:o$}{1: }{2:$$}{1: }{2:o$}{1: }{0: }| + {0:~}{1: }{2:o}{1: }{2:$}{1: }{2:oo}{1: }{2:o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o}{1: }{2:$$}{1: }{2:$$}{1: }{2:$$o$}{1: }{0: }| + {0:~}{1: }{2:oo}{1: }{2:$}{1: }{2:$}{1: "}{2:$}{1: }{2:o$$$$$$$$$}{1: }{2:$$$$$$$$$$$$$}{1: }{2:$$$$$$$$$o}{1: }{2:$$$o$$o$}{1: }{0: }| + {0:~}{1: "}{2:$$$$$$o$}{1: }{2:o$$$$$$$$$}{1: }{2:$$$$$$$$$$$}{1: }{2:$$$$$$$$$$o}{1: }{2:$$$$$$$$}{1: }{0: }| + {0:~}{1: }{2:$$$$$$$}{1: }{2:$$$$$$$$$$$}{1: }{2:$$$$$$$$$$$}{1: }{2:$$$$$$$$$$$$$$$$$$$$$$$}{1: }{0: }| + {0:~}{1: }{2:$$$$$$$$$$$$$$$$$$$$$$$}{1: }{2:$$$$$$$$$$$$$}{1: }{2:$$$$$$$$$$$$$$}{1: """}{2:$$$}{1: }{0: }| + {0:~}{1: "}{2:$$$}{1:""""}{2:$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{1: "}{2:$$$}{1: }{0: }| + {0:~}{1: }{2:$$$}{1: }{2:o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{1: "}{2:$$$o}{1: }{0: }| + {0:~}{1: }{2:o$$}{1:" }{2:$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{1: }{2:$$$o}{1: }{0: }| + {0:~}{1: }{2:$$$}{1: }{2:$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{1:" "}{2:$$$$$$ooooo$$$$o}{1: }{0: }| + {0:~}{1: }{2:o$$$oooo$$$$$}{1: }{2:$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{1: }{2:o$$$$$$$$$$$$$$$$$}{1: }{0: }| + {0:~}{1: }{2:$$$$$$$$}{1:"}{2:$$$$}{1: }{2:$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{1: }{2:$$$$}{1:"""""""" }{0: }| + {0:~}{1: """" }{2:$$$$}{1: "}{2:$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{1:" }{2:o$$$}{1: }{0: }| + {0:~}{1: "}{2:$$$o}{1: """}{2:$$$$$$$$$$$$$$$$$$}{1:"}{2:$$}{1:" }{2:$$$}{1: }{0: }| + {0:~}{1: }{2:$$$o}{1: "}{2:$$}{1:""}{2:$$$$$$}{1:"""" }{2:o$$$}{1: }{0: }| + {0:~}{1: }{2:$$$$o}{1: }{2:o$$$}{1:" }{0: }| + {0:~}{1: "}{2:$$$$o}{1: }{2:o$$$$$$o}{1:"}{2:$$$$o}{1: }{2:o$$$$}{1: }{0: }| + {0:~}{1: "}{2:$$$$$oo}{1: ""}{2:$$$$o$$$$$o}{1: }{2:o$$$$}{1:"" }{0: }| + {0:~}{1: ""}{2:$$$$$oooo}{1: "}{2:$$$o$$$$$$$$$}{1:""" }{0: }| + {0:~}{1: ""}{2:$$$$$$$oo}{1: }{2:$$$$$$$$$$}{1: }{0: }| + {0:~}{1: """"}{2:$$$$$$$$$$$}{1: }{0: }| + {0:~}{1: }{2:$$$$$$$$$$$$}{1: }{0: }| + {0:~}{1: }{2:$$$$$$$$$$}{1:" }{0: }| + {0:~}{1: "}{2:$$$}{1:"""" }{0: }| + {0:~}{1: }{0: }| + {0:~}{3:Press ENTER or type command to continue}{1: }{0: }| + {0:~}{4:term://~/config2/docs/pres//32693:vim --clean +smile 29,39 All}{0: }| + {0:~}{1::call nvim__screenshot("smile2.cat") }{0: }| + {0:~ }| + {0:~ }| + | + ]]} + end) + end) end) diff --git a/test/functional/fixtures/smile2.cat b/test/functional/fixtures/smile2.cat new file mode 100644 index 0000000000..0feb32f293 --- /dev/null +++ b/test/functional/fixtures/smile2.cat @@ -0,0 +1,32 @@ +31,79 +[?25l[H[2J:smile
+ [103moooo$$$$$$$$$$$$oooo(B[m
+ [103moo$$$$$$$$$$$$$$$$$$$$$$$$o(B[m
+ [103moo$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o(B[m [103mo$(B[m [103m$$(B[m [103mo$(B[m
+ [103mo(B[m [103m$(B[m [103moo(B[m [103mo$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o(B[m [103m$$(B[m [103m$$(B[m [103m$$o$(B[m
+ [103moo(B[m [103m$(B[m [103m$(B[m "[103m$(B[m [103mo$$$$$$$$$(B[m [103m$$$$$$$$$$$$$(B[m [103m$$$$$$$$$o(B[m [103m$$$o$$o$(B[m
+ "[103m$$$$$$o$(B[m [103mo$$$$$$$$$(B[m [103m$$$$$$$$$$$(B[m [103m$$$$$$$$$$o(B[m [103m$$$$$$$$(B[m
+ [103m$$$$$$$(B[m [103m$$$$$$$$$$$(B[m [103m$$$$$$$$$$$(B[m [103m$$$$$$$$$$$$$$$$$$$$$$$(B[m
+ [103m$$$$$$$$$$$$$$$$$$$$$$$(B[m [103m$$$$$$$$$$$$$(B[m [103m$$$$$$$$$$$$$$(B[m """[103m$$$(B[m
+ "[103m$$$(B[m""""[103m$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$(B[m "[103m$$$(B[m
+ [103m$$$(B[m [103mo$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$(B[m "[103m$$$o(B[m
+ [103mo$$(B[m" [103m$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$(B[m [103m$$$o(B[m
+ [103m$$$(B[m [103m$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$(B[m" "[103m$$$$$$ooooo$$$$o(B[m
+ [103mo$$$oooo$$$$$(B[m [103m$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$(B[m [103mo$$$$$$$$$$$$$$$$$(B[m
+ [103m$$$$$$$$(B[m"[103m$$$$(B[m [103m$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$(B[m [103m$$$$(B[m""""""""
+ """" [103m$$$$(B[m "[103m$$$$$$$$$$$$$$$$$$$$$$$$$$$$(B[m" [103mo$$$(B[m
+ "[103m$$$o(B[m """[103m$$$$$$$$$$$$$$$$$$(B[m"[103m$$(B[m" [103m$$$(B[m
+ [103m$$$o(B[m "[103m$$(B[m""[103m$$$$$$(B[m"""" [103mo$$$(B[m
+ [103m$$$$o(B[m [103mo$$$(B[m"
+ "[103m$$$$o(B[m [103mo$$$$$$o(B[m"[103m$$$$o(B[m [103mo$$$$(B[m
+ "[103m$$$$$oo(B[m ""[103m$$$$o$$$$$o(B[m [103mo$$$$(B[m""
+ ""[103m$$$$$oooo(B[m "[103m$$$o$$$$$$$$$(B[m"""
+ ""[103m$$$$$$$oo(B[m [103m$$$$$$$$$$(B[m
+ """"[103m$$$$$$$$$$$(B[m
+ [103m$$$$$$$$$$$$(B[m
+ [103m$$$$$$$$$$(B[m"
+ "[103m$$$(B[m""""
+
+[32mPress ENTER or type command to continue(B[m
+(B[0;1;7mterm://~/config2/docs/pres//32693:vim --clean +smile 29,39 All
+(B[m:call nvim__screenshot("smile2.cat") [?25h
\ No newline at end of file diff --git a/test/functional/legacy/memory_usage_spec.lua b/test/functional/legacy/memory_usage_spec.lua index 5f7bbd887f..97ac96804e 100644 --- a/test/functional/legacy/memory_usage_spec.lua +++ b/test/functional/legacy/memory_usage_spec.lua @@ -157,8 +157,8 @@ describe('memory usage', function() -- The usage may be a bit less than the last value, use 80%. -- Allow for 20% tolerance at the upper limit. That's very permissive, but -- otherwise the test fails sometimes. On Sourcehut CI with FreeBSD we need to - -- be even more permissive. - local upper_multiplier = uname() == 'freebsd' and 15 or 12 + -- be even much more permissive. + local upper_multiplier = uname() == 'freebsd' and 19 or 12 local lower = before.last * 8 / 10 local upper = load_adjust((after.max + (after.last - before.last)) * upper_multiplier / 10) check_result({before=before, after=after, last=last}, diff --git a/test/functional/legacy/options_spec.lua b/test/functional/legacy/options_spec.lua index d7f5df3a1e..023cdd4ae1 100644 --- a/test/functional/legacy/options_spec.lua +++ b/test/functional/legacy/options_spec.lua @@ -42,6 +42,20 @@ describe('set', function() matches('E36: Not enough room', exc_exec('set wmh=1')) end) + it('winminheight works with tabline', function() + local screen = Screen.new(20, 11) + screen:attach() + source([[ + set wmh=0 stal=2 + split + split + split + split + tabnew + ]]) + matches('E36: Not enough room', exc_exec('set wmh=1')) + end) + it('scroll works', function() local screen = Screen.new(42, 16) screen:attach() diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua index 32f9ae030f..3ad14e749e 100644 --- a/test/functional/ui/float_spec.lua +++ b/test/functional/ui/float_spec.lua @@ -5433,6 +5433,155 @@ describe('floatwin', function() ]]) end end) + + it("correctly redraws when overlaid windows are resized #13991", function() + helpers.source([[ + let popup_config = {"relative" : "editor", + \ "width" : 7, + \ "height" : 3, + \ "row" : 1, + \ "col" : 1, + \ "style" : "minimal"} + + let border_config = {"relative" : "editor", + \ "width" : 9, + \ "height" : 5, + \ "row" : 0, + \ "col" : 0, + \ "style" : "minimal"} + + let popup_buffer = nvim_create_buf(v:false, v:true) + let border_buffer = nvim_create_buf(v:false, v:true) + let popup_win = nvim_open_win(popup_buffer, v:true, popup_config) + let border_win = nvim_open_win(border_buffer, v:false, border_config) + + call nvim_buf_set_lines(popup_buffer, 0, -1, v:true, + \ ["long", "longer", "longest"]) + + call nvim_buf_set_lines(border_buffer, 0, -1, v:true, + \ ["---------", "- -", "- -"]) + ]]) + + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + | + ## grid 5 + {2:^long }| + {2:longer }| + {2:longest}| + ## grid 6 + {2:---------}| + {2:- -}| + {2:- -}| + {2: }| + {2: }| + ]], attr_ids={ + [1] = {foreground = Screen.colors.Blue1, bold = true}; + [2] = {background = Screen.colors.LightMagenta}; + }, float_pos={ + [5] = { { + id = 1002 + }, "NW", 1, 1, 1, true }, + [6] = { { + id = 1003 + }, "NW", 1, 0, 0, true } + }} + else + screen:expect([[ + {1:---------} | + {1:-^long -}{0: }| + {1:-longer -}{0: }| + {1: longest }{0: }| + {1: }{0: }| + {0:~ }| + | + ]]) + end + + helpers.source([[ + let new_popup_config = {"width" : 1, "height" : 3} + let new_border_config = {"width" : 3, "height" : 5} + + function! Resize() + call nvim_win_set_config(g:popup_win, g:new_popup_config) + call nvim_win_set_config(g:border_win, g:new_border_config) + + call nvim_buf_set_lines(g:border_buffer, 0, -1, v:true, + \ ["---", "- -", "- -"]) + endfunction + + nnoremap zz <cmd>call Resize()<cr> + ]]) + + helpers.feed("zz") + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + | + ## grid 5 + {2:^l}| + {2:o}| + {2:n}| + ## grid 6 + {2:---}| + {2:- -}| + {2:- -}| + {2: }| + {2: }| + ]], attr_ids={ + [1] = {foreground = Screen.colors.Blue1, bold = true}; + [2] = {background = Screen.colors.LightMagenta}; + }, float_pos={ + [5] = { { + id = 1002 + }, "NW", 1, 1, 1, true }, + [6] = { { + id = 1003 + }, "NW", 1, 0, 0, true } + }} + else + screen:expect([[ + {1:---} | + {1:-^l-}{0: }| + {1:-o-}{0: }| + {1: n }{0: }| + {1: }{0: }| + {0:~ }| + | + ]]) + end + end) end describe('with ext_multigrid', function() diff --git a/test/functional/ui/fold_spec.lua b/test/functional/ui/fold_spec.lua index d3b1d33956..8883ad8270 100644 --- a/test/functional/ui/fold_spec.lua +++ b/test/functional/ui/fold_spec.lua @@ -464,6 +464,206 @@ describe("folded lines", function() end end) + it("works with vsplit", function() + insert([[ + aa + bb + cc + dd + ee + ff]]) + feed_command('2') + command("set foldcolumn=1") + feed('zf3j') + feed_command('1') + feed('zf2j') + feed('zO') + feed_command("rightbelow vnew") + insert([[ + aa + bb + cc + dd + ee + ff]]) + feed_command('2') + command("set foldcolumn=1") + feed('zf3j') + feed_command('1') + feed('zf2j') + if multigrid then + meths.input_mouse('left', 'press', '', 4, 0, 0) + screen:expect([[ + ## grid 1 + [2:----------------------]{2:│}[4:----------------------]| + [2:----------------------]{2:│}[4:----------------------]| + [2:----------------------]{2:│}[4:----------------------]| + [2:----------------------]{2:│}[4:----------------------]| + [2:----------------------]{2:│}[4:----------------------]| + [2:----------------------]{2:│}[4:----------------------]| + {2:[No Name] [+] }{3:[No Name] [+] }| + [3:---------------------------------------------]| + ## grid 2 + {7:-}aa | + {7:-}bb | + {7:2}cc | + {7:2}dd | + {7:2}ee | + {7:│}ff | + ## grid 3 + :1 | + ## grid 4 + {7:-}^aa | + {7:+}{5:+--- 4 lines: bb····}| + {7:│}ff | + {1:~ }| + {1:~ }| + {1:~ }| + ]]) + else + meths.input_mouse('left', 'press', '', 0, 0, 23) + screen:expect([[ + {7:-}aa {2:│}{7:-}^aa | + {7:-}bb {2:│}{7:+}{5:+--- 4 lines: bb····}| + {7:2}cc {2:│}{7:│}ff | + {7:2}dd {2:│}{1:~ }| + {7:2}ee {2:│}{1:~ }| + {7:│}ff {2:│}{1:~ }| + {2:[No Name] [+] }{3:[No Name] [+] }| + :1 | + ]]) + end + + if multigrid then + meths.input_mouse('left', 'press', '', 4, 1, 0) + screen:expect([[ + ## grid 1 + [2:----------------------]{2:│}[4:----------------------]| + [2:----------------------]{2:│}[4:----------------------]| + [2:----------------------]{2:│}[4:----------------------]| + [2:----------------------]{2:│}[4:----------------------]| + [2:----------------------]{2:│}[4:----------------------]| + [2:----------------------]{2:│}[4:----------------------]| + {2:[No Name] [+] }{3:[No Name] [+] }| + [3:---------------------------------------------]| + ## grid 2 + {7:-}aa | + {7:-}bb | + {7:2}cc | + {7:2}dd | + {7:2}ee | + {7:│}ff | + ## grid 3 + :1 | + ## grid 4 + {7:-}^aa | + {7:-}bb | + {7:2}cc | + {7:2}dd | + {7:2}ee | + {7:│}ff | + ]]) + else + meths.input_mouse('left', 'press', '', 0, 1, 23) + screen:expect([[ + {7:-}aa {2:│}{7:-}^aa | + {7:-}bb {2:│}{7:-}bb | + {7:2}cc {2:│}{7:2}cc | + {7:2}dd {2:│}{7:2}dd | + {7:2}ee {2:│}{7:2}ee | + {7:│}ff {2:│}{7:│}ff | + {2:[No Name] [+] }{3:[No Name] [+] }| + :1 | + ]]) + end + + if multigrid then + meths.input_mouse('left', 'press', '', 2, 1, 0) + screen:expect([[ + ## grid 1 + [2:----------------------]{2:│}[4:----------------------]| + [2:----------------------]{2:│}[4:----------------------]| + [2:----------------------]{2:│}[4:----------------------]| + [2:----------------------]{2:│}[4:----------------------]| + [2:----------------------]{2:│}[4:----------------------]| + [2:----------------------]{2:│}[4:----------------------]| + {3:[No Name] [+] }{2:[No Name] [+] }| + [3:---------------------------------------------]| + ## grid 2 + {7:-}aa | + {7:+}{5:^+--- 4 lines: bb····}| + {7:│}ff | + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + :1 | + ## grid 4 + {7:-}aa | + {7:-}bb | + {7:2}cc | + {7:2}dd | + {7:2}ee | + {7:│}ff | + ]]) + else + meths.input_mouse('left', 'press', '', 0, 1, 0) + screen:expect([[ + {7:-}aa {2:│}{7:-}aa | + {7:+}{5:^+--- 4 lines: bb····}{2:│}{7:-}bb | + {7:│}ff {2:│}{7:2}cc | + {1:~ }{2:│}{7:2}dd | + {1:~ }{2:│}{7:2}ee | + {1:~ }{2:│}{7:│}ff | + {3:[No Name] [+] }{2:[No Name] [+] }| + :1 | + ]]) + end + + if multigrid then + meths.input_mouse('left', 'press', '', 2, 0, 0) + screen:expect([[ + ## grid 1 + [2:----------------------]{2:│}[4:----------------------]| + [2:----------------------]{2:│}[4:----------------------]| + [2:----------------------]{2:│}[4:----------------------]| + [2:----------------------]{2:│}[4:----------------------]| + [2:----------------------]{2:│}[4:----------------------]| + [2:----------------------]{2:│}[4:----------------------]| + {3:[No Name] [+] }{2:[No Name] [+] }| + [3:---------------------------------------------]| + ## grid 2 + {7:+}{5:^+-- 6 lines: aa·····}| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + :1 | + ## grid 4 + {7:-}aa | + {7:-}bb | + {7:2}cc | + {7:2}dd | + {7:2}ee | + {7:│}ff | + ]]) + else + meths.input_mouse('left', 'press', '', 0, 0, 0) + screen:expect([[ + {7:+}{5:^+-- 6 lines: aa·····}{2:│}{7:-}aa | + {1:~ }{2:│}{7:-}bb | + {1:~ }{2:│}{7:2}cc | + {1:~ }{2:│}{7:2}dd | + {1:~ }{2:│}{7:2}ee | + {1:~ }{2:│}{7:│}ff | + {3:[No Name] [+] }{2:[No Name] [+] }| + :1 | + ]]) + end + end) + it("works with tab", function() insert([[ aa |