diff options
Diffstat (limited to 'src')
53 files changed, 922 insertions, 422 deletions
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 7807125b92..95ca1052af 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -164,8 +164,8 @@ if(NOT MSVC) set_source_files_properties( ${CONV_SOURCES} PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -Wno-conversion") # gperf generates ANSI-C with incorrect linkage, ignore it. - check_c_compiler_flag(-Wno-static-in-inline HAS_WNO_STATIC_IN_INLINE_FLAG) - if(HAS_WNO_STATIC_IN_INLINE_FLAG) + check_c_compiler_flag(-Wstatic-in-inline HAS_WSTATIC_IN_INLINE) + if(HAS_WSTATIC_IN_INLINE) set_source_files_properties( eval.c PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -Wno-static-in-inline -Wno-conversion") else() diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 4f28ea5af3..20ed77afad 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -123,6 +123,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, ui->mode_change = remote_ui_mode_change; ui->grid_scroll = remote_ui_grid_scroll; ui->hl_attr_define = remote_ui_hl_attr_define; + ui->hl_group_set = remote_ui_hl_group_set; ui->raw_line = remote_ui_raw_line; ui->bell = remote_ui_bell; ui->visual_bell = remote_ui_visual_bell; diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h index a1d25766fe..41bf0af65b 100644 --- a/src/nvim/api/ui_events.in.h +++ b/src/nvim/api/ui_events.in.h @@ -73,6 +73,8 @@ void default_colors_set(Integer rgb_fg, Integer rgb_bg, Integer rgb_sp, void hl_attr_define(Integer id, HlAttrs rgb_attrs, HlAttrs cterm_attrs, Array info) FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_BRIDGE_IMPL; +void hl_group_set(String name, Integer id) + FUNC_API_SINCE(6) FUNC_API_BRIDGE_IMPL; void grid_resize(Integer grid, Integer width, Integer height) FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL; void grid_clear(Integer grid) diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 8e5650633a..dbe3b66fd5 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1064,6 +1064,19 @@ fail: /// - `external`: GUI should display the window as an external /// top-level window. Currently accepts no other positioning /// configuration together with this. +/// - `style`: Configure the apparance of the window. Currently only takes +/// one non-empty value: +/// - "minimal" Nvim will display the window with many UI options +/// disabled. This is useful when displaing a temporary +/// float where the text should not be edited. Disables +/// 'number', 'relativenumber', 'cursorline', 'cursorcolumn', +/// 'spell' and 'list' options. 'signcolumn' is changed to +/// `auto`. The end-of-buffer region is hidden by setting +/// `eob` flag of 'fillchars' to a space char, and clearing +/// the |EndOfBuffer| region in 'winhighlight'. +/// +/// top-level window. Currently accepts no other positioning +/// configuration together with this. /// @param[out] err Error details, if any /// /// @return Window handle, or 0 on error @@ -1085,6 +1098,11 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dictionary config, if (buffer > 0) { nvim_win_set_buf(wp->handle, buffer, err); } + + if (fconfig.style == kWinStyleMinimal) { + win_set_minimal_style(wp); + didset_window_options(wp); + } return wp->handle; } diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index 9fd1818a5c..4922dd7efc 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -13,6 +13,7 @@ #include "nvim/vim.h" #include "nvim/buffer.h" #include "nvim/cursor.h" +#include "nvim/option.h" #include "nvim/window.h" #include "nvim/screen.h" #include "nvim/move.h" @@ -475,6 +476,10 @@ void nvim_win_set_config(Window window, Dictionary config, Error *err) win_config_float(win, fconfig); win->w_pos_changed = true; } + if (fconfig.style == kWinStyleMinimal) { + win_set_minimal_style(win); + didset_window_options(win); + } } /// Return window configuration. diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua index ef528f72b8..12fc8fd02a 100644 --- a/src/nvim/auevents.lua +++ b/src/nvim/auevents.lua @@ -77,6 +77,7 @@ return { 'Signal', -- after nvim process received a signal 'SourceCmd', -- sourcing a Vim script using command 'SourcePre', -- before sourcing a Vim script + 'SourcePost', -- after sourcing a Vim script 'SpellFileMissing', -- spell file missing 'StdinReadPost', -- after reading from stdin 'StdinReadPre', -- before reading from stdin diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index d9ed9c6861..34fe52c10e 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -2543,6 +2543,11 @@ void get_winopts(buf_T *buf) } else copy_winopt(&curwin->w_allbuf_opt, &curwin->w_onebuf_opt); + if (curwin->w_float_config.style == kWinStyleMinimal) { + didset_window_options(curwin); + win_set_minimal_style(curwin); + } + // Set 'foldlevel' to 'foldlevelstart' if it's not negative. if (p_fdls >= 0) { curwin->w_p_fdl = p_fdls; diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index ad10f6baa2..a27672488e 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -972,7 +972,6 @@ struct matchitem { }; typedef int FloatAnchor; -typedef int FloatRelative; enum { kFloatAnchorEast = 1, @@ -985,15 +984,20 @@ enum { // SE -> kFloatAnchorSouth | kFloatAnchorEast EXTERN const char *const float_anchor_str[] INIT(= { "NW", "NE", "SW", "SE" }); -enum { +typedef enum { kFloatRelativeEditor = 0, kFloatRelativeWindow = 1, kFloatRelativeCursor = 2, -}; +} FloatRelative; EXTERN const char *const float_relative_str[] INIT(= { "editor", "window", "cursor" }); +typedef enum { + kWinStyleUnused = 0, + kWinStyleMinimal, /// Minimal UI: no number column, eob markers, etc +} WinStyle; + typedef struct { Window window; int height, width; @@ -1002,12 +1006,14 @@ typedef struct { FloatRelative relative; bool external; bool focusable; + WinStyle style; } FloatConfig; #define FLOAT_CONFIG_INIT ((FloatConfig){ .height = 0, .width = 0, \ .row = 0, .col = 0, .anchor = 0, \ .relative = 0, .external = false, \ - .focusable = true }) + .focusable = true, \ + .style = kWinStyleUnused }) // Structure to store last cursor position and topline. Used by check_lnums() // and reset_lnums(). diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 57c4a5395c..6a37c52e3f 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -649,7 +649,9 @@ static int insert_execute(VimState *state, int key) s->c = key; // Don't want K_EVENT with cursorhold for the second key, e.g., after CTRL-V. - did_cursorhold = true; + if (key != K_EVENT) { + did_cursorhold = true; + } if (p_hkmap && KeyTyped) { s->c = hkmap(s->c); // Hebrew mode mapping diff --git a/src/nvim/eval.c b/src/nvim/eval.c index becd4eaca1..7a8402a6bb 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1782,6 +1782,15 @@ static void list_hashtable_vars(hashtab_T *ht, const char *prefix, int empty, if (!HASHITEM_EMPTY(hi)) { todo--; di = TV_DICT_HI2DI(hi); + char buf[IOSIZE]; + + // apply :filter /pat/ to variable name + xstrlcpy(buf, prefix, IOSIZE - 1); + xstrlcat(buf, (char *)di->di_key, IOSIZE); + if (message_filtered((char_u *)buf)) { + continue; + } + if (empty || di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string != NULL) { list_one_var(di, prefix, first); @@ -2360,6 +2369,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, /* Can't add "v:" variable. */ if (lp->ll_dict == &vimvardict) { EMSG2(_(e_illvar), name); + tv_clear(&var1); return NULL; } @@ -4326,7 +4336,7 @@ static int eval7( // Dictionary: {key: val, key: val} case '{': ret = get_lambda_tv(arg, rettv, evaluate); if (ret == NOTDONE) { - ret = get_dict_tv(arg, rettv, evaluate); + ret = dict_get_tv(arg, rettv, evaluate); } break; @@ -5710,7 +5720,7 @@ static bool set_ref_in_funccal(funccall_T *fc, int copyID) * Allocate a variable for a Dictionary and fill it from "*arg". * Return OK or FAIL. Returns NOTDONE for {expr}. */ -static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate) +static int dict_get_tv(char_u **arg, typval_T *rettv, int evaluate) { dict_T *d = NULL; typval_T tvkey; @@ -7097,10 +7107,14 @@ static void f_assert_fails(typval_T *argvars, typval_T *rettv, FunPtr fptr) { const char *const cmd = tv_get_string_chk(&argvars[0]); garray_T ga; + int save_trylevel = trylevel; + // trylevel must be zero for a ":throw" command to be considered failed + trylevel = 0; called_emsg = false; suppress_errthrow = true; emsg_silent = true; + do_cmdline_cmd(cmd); if (!called_emsg) { prepare_assert_error(&ga); @@ -7122,6 +7136,7 @@ static void f_assert_fails(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } + trylevel = save_trylevel; called_emsg = false; suppress_errthrow = false; emsg_silent = false; @@ -7289,6 +7304,14 @@ static buf_T *find_buffer(typval_T *avar) return buf; } +// "bufadd(expr)" function +static void f_bufadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + char_u *name = (char_u *)tv_get_string(&argvars[0]); + + rettv->vval.v_number = buflist_add(*name == NUL ? NULL : name, 0); +} + /* * "bufexists(expr)" function */ @@ -7308,6 +7331,21 @@ static void f_buflisted(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = (buf != NULL && buf->b_p_bl); } +// "bufload(expr)" function +static void f_bufload(typval_T *argvars, typval_T *unused, FunPtr fptr) +{ + buf_T *buf = get_buf_arg(&argvars[0]); + + if (buf != NULL && buf->b_ml.ml_mfp == NULL) { + aco_save_T aco; + + aucmd_prepbuf(&aco, buf); + swap_exists_action = SEA_NONE; + open_buffer(false, NULL, 0); + aucmd_restbuf(&aco); + } +} + /* * "bufloaded(expr)" function */ @@ -12189,8 +12227,16 @@ static void f_jobstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } + const char *error = NULL; + if (data->is_rpc) { + // Ignore return code, but show error later. + (void)channel_close(data->id, kChannelPartRpc, &error); + } process_stop((Process *)&data->stream.proc); rettv->vval.v_number = 1; + if (error) { + EMSG(error); + } } // "jobwait(ids[, timeout])" function @@ -13802,7 +13848,7 @@ static void f_reltimestr(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; if (list2proftime(&argvars[0], &tm) == OK) { - rettv->vval.v_string = (char_u *) xstrdup(profile_msg(tm)); + rettv->vval.v_string = (char_u *)xstrdup(profile_msg(tm)); } } @@ -15850,11 +15896,10 @@ static void f_sign_getplaced(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if ((di = tv_dict_find(dict, "lnum", -1)) != NULL) { // get signs placed at this line - lnum = (linenr_T)tv_get_number_chk(&di->di_tv, ¬anum); - if (notanum) { + lnum = tv_get_lnum(&di->di_tv); + if (lnum <= 0) { return; } - lnum = tv_get_lnum(&di->di_tv); } if ((di = tv_dict_find(dict, "id", -1)) != NULL) { // get sign placed with this identifier @@ -16536,7 +16581,7 @@ static void f_reltimefloat(typval_T *argvars , typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_FLOAT; rettv->vval.v_float = 0; if (list2proftime(&argvars[0], &tm) == OK) { - rettv->vval.v_float = ((float_T)tm) / 1000000; + rettv->vval.v_float = ((float_T)tm) / 1000000000; } } @@ -20682,11 +20727,11 @@ void ex_echohl(exarg_T *eap) */ void ex_execute(exarg_T *eap) { - char_u *arg = eap->arg; + char_u *arg = eap->arg; typval_T rettv; int ret = OK; garray_T ga; - int save_did_emsg = did_emsg; + int save_did_emsg; ga_init(&ga, 1, 80); @@ -20824,6 +20869,9 @@ void ex_function(exarg_T *eap) if (!HASHITEM_EMPTY(hi)) { --todo; fp = HI2UF(hi); + if (message_filtered(fp->uf_name)) { + continue; + } if (!func_name_refcount(fp->uf_name)) { list_func_head(fp, false); } diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 9deee69f32..089b08d5d1 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -40,11 +40,13 @@ return { atan2={args=2}, browse={args=4}, browsedir={args=2}, + bufadd={args=1}, bufexists={args=1}, buffer_exists={args=1, func='f_bufexists'}, -- obsolete buffer_name={args=1, func='f_bufname'}, -- obsolete buffer_number={args=1, func='f_bufnr'}, -- obsolete buflisted={args=1}, + bufload={args=1}, bufloaded={args=1}, bufname={args=1}, bufnr={args={1, 2}}, diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h index 623bdfc93b..289c3ee99c 100644 --- a/src/nvim/eval/typval_encode.c.h +++ b/src/nvim/eval/typval_encode.c.h @@ -382,7 +382,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( case VAR_SPECIAL: { switch (tv->vval.v_special) { case kSpecialVarNull: { - TYPVAL_ENCODE_CONV_NIL(tv); + TYPVAL_ENCODE_CONV_NIL(tv); // -V1037 break; } case kSpecialVarTrue: diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c index 7a8a39dbcf..990dee0c7f 100644 --- a/src/nvim/event/process.c +++ b/src/nvim/event/process.c @@ -26,6 +26,11 @@ // For PTY processes SIGTERM is sent first (in case SIGHUP was not enough). #define KILL_TIMEOUT_MS 2000 +/// Externally defined with gcov. +#ifdef USE_GCOV +void __gcov_flush(void); +#endif + static bool process_is_tearing_down = false; /// @returns zero on success, or negative error code @@ -50,6 +55,11 @@ int process_spawn(Process *proc, bool in, bool out, bool err) proc->err.closed = true; } +#ifdef USE_GCOV + // Flush coverage data before forking, to avoid "Merge mismatch" errors. + __gcov_flush(); +#endif + int status; switch (proc->type) { case kProcessTypeUv: diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 2fb818760a..c092036ce9 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -3039,6 +3039,7 @@ int do_source(char_u *fname, int check_other, int is_vimrc) int save_debug_break_level = debug_break_level; scriptitem_T *si = NULL; proftime_T wait_start; + bool trigger_source_post = false; p = expand_env_save(fname); if (p == NULL) { @@ -3059,6 +3060,10 @@ int do_source(char_u *fname, int check_other, int is_vimrc) && apply_autocmds(EVENT_SOURCECMD, fname_exp, fname_exp, false, curbuf)) { retval = aborting() ? FAIL : OK; + if (retval == OK) { + // Apply SourcePost autocommands. + apply_autocmds(EVENT_SOURCEPOST, fname_exp, fname_exp, false, curbuf); + } goto theend; } @@ -3181,7 +3186,7 @@ int do_source(char_u *fname, int check_other, int is_vimrc) } si = &SCRIPT_ITEM(current_SID); si->sn_name = fname_exp; - fname_exp = NULL; + fname_exp = vim_strsave(si->sn_name); // used for autocmd if (file_id_ok) { si->file_id_valid = true; si->file_id = file_id; @@ -3261,6 +3266,10 @@ int do_source(char_u *fname, int check_other, int is_vimrc) time_pop(rel_time); } + if (!got_int) { + trigger_source_post = true; + } + // After a "finish" in debug mode, need to break at first command of next // sourced file. if (save_debug_break_level > ex_nesting_level @@ -3278,6 +3287,10 @@ int do_source(char_u *fname, int check_other, int is_vimrc) xfree(firstline); convert_setup(&cookie.conv, NULL, NULL); + if (trigger_source_post) { + apply_autocmds(EVENT_SOURCEPOST, fname_exp, fname_exp, false, curbuf); + } + theend: xfree(fname_exp); return retval; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index a494463f89..59e6f227e4 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -1108,9 +1108,8 @@ static int current_tab_nr(tabpage_T *tab) #define CURRENT_TAB_NR current_tab_nr(curtab) #define LAST_TAB_NR current_tab_nr(NULL) -/* -* Figure out the address type for ":wincmd". -*/ + +/// Figure out the address type for ":wincmd". static void get_wincmd_addr_type(char_u *arg, exarg_T *eap) { switch (*arg) { @@ -1156,13 +1155,13 @@ static void get_wincmd_addr_type(char_u *arg, exarg_T *eap) case Ctrl_I: case 'd': case Ctrl_D: - /* window size or any count */ - eap->addr_type = ADDR_LINES; + // window size or any count + eap->addr_type = ADDR_LINES; // -V1037 break; case Ctrl_HAT: case '^': - /* buffer number */ + // buffer number eap->addr_type = ADDR_BUFFERS; break; @@ -1177,7 +1176,7 @@ static void get_wincmd_addr_type(char_u *arg, exarg_T *eap) case 'W': case 'x': case Ctrl_X: - /* window number */ + // window number eap->addr_type = ADDR_WINDOWS; break; @@ -1192,7 +1191,7 @@ static void get_wincmd_addr_type(char_u *arg, exarg_T *eap) case Ctrl_P: case '=': case CAR: - /* no count */ + // no count eap->addr_type = 0; break; } @@ -6922,16 +6921,17 @@ static void ex_resize(exarg_T *eap) n = atol((char *)eap->arg); if (cmdmod.split & WSP_VERT) { - if (*eap->arg == '-' || *eap->arg == '+') + if (*eap->arg == '-' || *eap->arg == '+') { n += curwin->w_width; - else if (n == 0 && eap->arg[0] == NUL) /* default is very wide */ - n = 9999; + } else if (n == 0 && eap->arg[0] == NUL) { // default is very wide + n = Columns; + } win_setwidth_win(n, wp); } else { if (*eap->arg == '-' || *eap->arg == '+') { n += curwin->w_height; } else if (n == 0 && eap->arg[0] == NUL) { // default is very high - n = 9999; + n = Rows-1; } win_setheight_win(n, wp); } @@ -8452,24 +8452,23 @@ static void ex_tag_cmd(exarg_T *eap, char_u *name) int cmd; switch (name[1]) { - case 'j': cmd = DT_JUMP; /* ":tjump" */ + case 'j': cmd = DT_JUMP; // ":tjump" break; - case 's': cmd = DT_SELECT; /* ":tselect" */ + case 's': cmd = DT_SELECT; // ":tselect" break; - case 'p': cmd = DT_PREV; /* ":tprevious" */ + case 'p': // ":tprevious" + case 'N': cmd = DT_PREV; // ":tNext" break; - case 'N': cmd = DT_PREV; /* ":tNext" */ + case 'n': cmd = DT_NEXT; // ":tnext" break; - case 'n': cmd = DT_NEXT; /* ":tnext" */ + case 'o': cmd = DT_POP; // ":pop" break; - case 'o': cmd = DT_POP; /* ":pop" */ + case 'f': // ":tfirst" + case 'r': cmd = DT_FIRST; // ":trewind" break; - case 'f': /* ":tfirst" */ - case 'r': cmd = DT_FIRST; /* ":trewind" */ + case 'l': cmd = DT_LAST; // ":tlast" break; - case 'l': cmd = DT_LAST; /* ":tlast" */ - break; - default: /* ":tag" */ + default: // ":tag" if (p_cst && *eap->arg != NUL) { ex_cstag(eap); return; @@ -9322,26 +9321,30 @@ static frame_T *ses_skipframe(frame_T *fr) { frame_T *frc; - for (frc = fr; frc != NULL; frc = frc->fr_next) - if (ses_do_frame(frc)) + FOR_ALL_FRAMES(frc, fr) { + if (ses_do_frame(frc)) { break; + } + } return frc; } -/* - * Return TRUE if frame "fr" has a window somewhere that we want to save in - * the Session. - */ -static int ses_do_frame(frame_T *fr) +// Return true if frame "fr" has a window somewhere that we want to save in +// the Session. +static bool ses_do_frame(const frame_T *fr) + FUNC_ATTR_NONNULL_ARG(1) { - frame_T *frc; + const frame_T *frc; - if (fr->fr_layout == FR_LEAF) + if (fr->fr_layout == FR_LEAF) { return ses_do_win(fr->fr_win); - for (frc = fr->fr_child; frc != NULL; frc = frc->fr_next) - if (ses_do_frame(frc)) - return TRUE; - return FALSE; + } + FOR_ALL_FRAMES(frc, fr->fr_child) { + if (ses_do_frame(frc)) { + return true; + } + } + return false; } /// Return non-zero if window "wp" is to be stored in the Session. diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua index 95dcbc732e..76dcf849d1 100644 --- a/src/nvim/generators/gen_api_dispatch.lua +++ b/src/nvim/generators/gen_api_dispatch.lua @@ -198,7 +198,8 @@ for i = 1, #functions do output:write('Object handle_'..fn.name..'(uint64_t channel_id, Array args, Error *error)') output:write('\n{') output:write('\n#if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL') - output:write('\n logmsg(DEBUG_LOG_LEVEL, "RPC: ", NULL, -1, true, "invoke '..fn.name..'");') + output:write('\n logmsg(DEBUG_LOG_LEVEL, "RPC: ", NULL, -1, true, "ch %" PRIu64 ": invoke ' + ..fn.name..'", channel_id);') output:write('\n#endif') output:write('\n Object ret = NIL;') -- Declare/initialize variables that will hold converted arguments diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 7efae1e637..64722ef35d 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -15,6 +15,7 @@ #include <string.h> #include <inttypes.h> +#include "nvim/assert.h" #include "nvim/vim.h" #include "nvim/ascii.h" #include "nvim/getchar.h" @@ -905,18 +906,19 @@ int ins_typebuf(char_u *str, int noremap, int offset, int nottyped, bool silent) s2 = xmalloc((size_t)newlen); typebuf.tb_buflen = newlen; - /* copy the old chars, before the insertion point */ - memmove(s1 + newoff, typebuf.tb_buf + typebuf.tb_off, - (size_t)offset); - /* copy the new chars */ + // copy the old chars, before the insertion point + memmove(s1 + newoff, typebuf.tb_buf + typebuf.tb_off, (size_t)offset); + // copy the new chars memmove(s1 + newoff + offset, str, (size_t)addlen); - /* copy the old chars, after the insertion point, including the NUL at - * the end */ + // copy the old chars, after the insertion point, including the NUL at + // the end + int bytes = typebuf.tb_len - offset + 1; + assert(bytes > 0); memmove(s1 + newoff + offset + addlen, - typebuf.tb_buf + typebuf.tb_off + offset, - (size_t)(typebuf.tb_len - offset + 1)); - if (typebuf.tb_buf != typebuf_init) + typebuf.tb_buf + typebuf.tb_off + offset, (size_t)bytes); + if (typebuf.tb_buf != typebuf_init) { xfree(typebuf.tb_buf); + } typebuf.tb_buf = s1; memmove(s2 + newoff, typebuf.tb_noremap + typebuf.tb_off, @@ -1062,11 +1064,12 @@ void del_typebuf(int len, int offset) typebuf.tb_noremap + typebuf.tb_off, (size_t)offset); typebuf.tb_off = MAXMAPLEN; } - /* adjust typebuf.tb_buf (include the NUL at the end) */ + // adjust typebuf.tb_buf (include the NUL at the end) + int bytes = typebuf.tb_len - offset + 1; + assert(bytes > 0); memmove(typebuf.tb_buf + typebuf.tb_off + offset, - typebuf.tb_buf + i + len, - (size_t)(typebuf.tb_len - offset + 1)); - /* adjust typebuf.tb_noremap[] */ + typebuf.tb_buf + i + len, (size_t)bytes); + // adjust typebuf.tb_noremap[] memmove(typebuf.tb_noremap + typebuf.tb_off + offset, typebuf.tb_noremap + i + len, (size_t)(typebuf.tb_len - offset)); diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 139ffe2144..1cb2f4592a 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -435,10 +435,11 @@ EXTERN win_T *firstwin; /* first window */ EXTERN win_T *lastwin; /* last window */ EXTERN win_T *prevwin INIT(= NULL); /* previous window */ # define ONE_WINDOW (firstwin == lastwin) -/* - * When using this macro "break" only breaks out of the inner loop. Use "goto" - * to break out of the tabpage loop. - */ +# define FOR_ALL_FRAMES(frp, first_frame) \ + for (frp = first_frame; frp != NULL; frp = frp->fr_next) // NOLINT + +// When using this macro "break" only breaks out of the inner loop. Use "goto" +// to break out of the tabpage loop. # define FOR_ALL_TAB_WINDOWS(tp, wp) \ FOR_ALL_TABS(tp) \ FOR_ALL_WINDOWS_IN_TAB(wp, tp) diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index 15c3d0eb7b..f11880cb2b 100644 --- a/src/nvim/highlight.c +++ b/src/nvim/highlight.c @@ -106,14 +106,19 @@ static int get_attr_entry(HlEntry entry) /// When a UI connects, we need to send it the table of highlights used so far. void ui_send_all_hls(UI *ui) { - if (!ui->hl_attr_define) { - return; + if (ui->hl_attr_define) { + for (size_t i = 1; i < kv_size(attr_entries); i++) { + Array inspect = hl_inspect((int)i); + ui->hl_attr_define(ui, (Integer)i, kv_A(attr_entries, i).attr, + kv_A(attr_entries, i).attr, inspect); + api_free_array(inspect); + } } - for (size_t i = 1; i < kv_size(attr_entries); i++) { - Array inspect = hl_inspect((int)i); - ui->hl_attr_define(ui, (Integer)i, kv_A(attr_entries, i).attr, - kv_A(attr_entries, i).attr, inspect); - api_free_array(inspect); + if (ui->hl_group_set) { + for (size_t hlf = 0; hlf < HLF_COUNT; hlf++) { + ui->hl_group_set(ui, cstr_as_string((char *)hlf_names[hlf]), + highlight_attr[hlf]); + } } } @@ -141,10 +146,12 @@ int hl_get_ui_attr(int idx, int final_id, bool optional) HlAttrs attrs = HLATTRS_INIT; bool available = false; - int syn_attr = syn_id2attr(final_id); - if (syn_attr != 0) { - attrs = syn_attr2entry(syn_attr); - available = true; + if (final_id > 0) { + int syn_attr = syn_id2attr(final_id); + if (syn_attr != 0) { + attrs = syn_attr2entry(syn_attr); + available = true; + } } if (HLF_PNI <= idx && idx <= HLF_PST) { @@ -176,15 +183,14 @@ void update_window_hl(win_T *wp, bool invalid) // determine window specific background set in 'winhighlight' bool float_win = wp->w_floating && !wp->w_float_config.external; - if (wp != curwin && wp->w_hl_ids[HLF_INACTIVE] > 0) { + if (wp != curwin && wp->w_hl_ids[HLF_INACTIVE] != 0) { wp->w_hl_attr_normal = hl_get_ui_attr(HLF_INACTIVE, wp->w_hl_ids[HLF_INACTIVE], !has_blend); - } else if (float_win && wp->w_hl_ids[HLF_NFLOAT] > 0) { + } else if (float_win && wp->w_hl_ids[HLF_NFLOAT] != 0) { wp->w_hl_attr_normal = hl_get_ui_attr(HLF_NFLOAT, - // 'cursorline' wp->w_hl_ids[HLF_NFLOAT], !has_blend); - } else if (wp->w_hl_id_normal > 0) { + } else if (wp->w_hl_id_normal != 0) { wp->w_hl_attr_normal = hl_get_ui_attr(-1, wp->w_hl_id_normal, !has_blend); } else { wp->w_hl_attr_normal = float_win ? HL_ATTR(HLF_NFLOAT) : 0; @@ -199,14 +205,14 @@ void update_window_hl(win_T *wp, bool invalid) } } - if (wp != curwin) { + if (wp != curwin && wp->w_hl_ids[HLF_INACTIVE] == 0) { wp->w_hl_attr_normal = hl_combine_attr(HL_ATTR(HLF_INACTIVE), wp->w_hl_attr_normal); } for (int hlf = 0; hlf < (int)HLF_COUNT; hlf++) { int attr; - if (wp->w_hl_ids[hlf] > 0) { + if (wp->w_hl_ids[hlf] != 0) { attr = hl_get_ui_attr(hlf, wp->w_hl_ids[hlf], false); } else { attr = HL_ATTR(hlf); @@ -250,6 +256,7 @@ void clear_hl_tables(bool reinit) map_clear(int, int)(combine_attr_entries); map_clear(int, int)(blend_attr_entries); map_clear(int, int)(blendthrough_attr_entries); + memset(highlight_attr_last, -1, sizeof(highlight_attr_last)); highlight_attr_set_all(); highlight_changed(); screen_invalidate_highlights(); diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h index 60f571ff0f..afccf9e6f6 100644 --- a/src/nvim/highlight_defs.h +++ b/src/nvim/highlight_defs.h @@ -36,6 +36,7 @@ typedef struct attr_entry { .rgb_sp_color = -1, \ .cterm_fg_color = 0, \ .cterm_bg_color = 0, \ + .hl_blend = -1, \ } /// Values for index in highlight_attr[]. @@ -149,6 +150,7 @@ EXTERN const char *hlf_names[] INIT(= { EXTERN int highlight_attr[HLF_COUNT]; // Highl. attr for each context. +EXTERN int highlight_attr_last[HLF_COUNT]; // copy for detecting changed groups EXTERN int highlight_user[9]; // User[1-9] attributes EXTERN int highlight_stlnc[9]; // On top of user EXTERN int cterm_normal_fg_color INIT(= 0); diff --git a/src/nvim/main.c b/src/nvim/main.c index 306d2f7bf5..b3654a0690 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -54,6 +54,7 @@ #include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/ui.h" +#include "nvim/ui_compositor.h" #include "nvim/version.h" #include "nvim/window.h" #include "nvim/shada.h" @@ -220,6 +221,7 @@ void early_init(void) set_lang_var(); // set v:lang and v:ctype init_signs(); + ui_comp_syn_init(); } #ifdef MAKE_LIB diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 2f2f2a7d74..3736004527 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -656,48 +656,51 @@ show_one_mark( int c, char_u *arg, pos_T *p, - char_u *name, - int current /* in current file */ + char_u *name_arg, + int current // in current file ) { - static int did_title = FALSE; - int mustfree = FALSE; - - if (c == -1) { /* finish up */ - if (did_title) - did_title = FALSE; - else { - if (arg == NULL) + static bool did_title = false; + bool mustfree = false; + char_u *name = name_arg; + + if (c == -1) { // finish up + if (did_title) { + did_title = false; + } else { + if (arg == NULL) { MSG(_("No marks set")); - else + } else { EMSG2(_("E283: No marks matching \"%s\""), arg); + } } - } - /* don't output anything if 'q' typed at --more-- prompt */ - else if (!got_int - && (arg == NULL || vim_strchr(arg, c) != NULL) - && p->lnum != 0) { - if (!did_title) { - /* Highlight title */ - MSG_PUTS_TITLE(_("\nmark line col file/text")); - did_title = TRUE; + } else if (!got_int + && (arg == NULL || vim_strchr(arg, c) != NULL) + && p->lnum != 0) { + // don't output anything if 'q' typed at --more-- prompt + if (name == NULL && current) { + name = mark_line(p, 15); + mustfree = true; } - msg_putchar('\n'); - if (!got_int) { - sprintf((char *)IObuff, " %c %6ld %4d ", c, p->lnum, p->col); - msg_outtrans(IObuff); - if (name == NULL && current) { - name = mark_line(p, 15); - mustfree = TRUE; + if (!message_filtered(name)) { + if (!did_title) { + // Highlight title + msg_puts_title(_("\nmark line col file/text")); + did_title = true; } - if (name != NULL) { - msg_outtrans_attr(name, current ? HL_ATTR(HLF_D) : 0); - if (mustfree) { - xfree(name); + msg_putchar('\n'); + if (!got_int) { + snprintf((char *)IObuff, IOSIZE, " %c %6ld %4d ", c, p->lnum, p->col); + msg_outtrans(IObuff); + if (name != NULL) { + msg_outtrans_attr(name, current ? HL_ATTR(HLF_D) : 0); } } + ui_flush(); // show one line at a time + } + if (mustfree) { + xfree(name); } - ui_flush(); /* show one line at a time */ } } @@ -786,8 +789,12 @@ void ex_jumps(exarg_T *eap) for (i = 0; i < curwin->w_jumplistlen && !got_int; ++i) { if (curwin->w_jumplist[i].fmark.mark.lnum != 0) { name = fm_getname(&curwin->w_jumplist[i].fmark, 16); - if (name == NULL) /* file name not available */ + + // apply :filter /pat/ or file name not available + if (name == NULL || message_filtered(name)) { + xfree(name); continue; + } msg_putchar('\n'); if (got_int) { diff --git a/src/nvim/message.c b/src/nvim/message.c index 86c185dbc2..df130565e0 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1878,8 +1878,8 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, msg_ext_last_attr = attr; } // Concat pieces with the same highlight - size_t len = strnlen((char *)str, maxlen); - ga_concat_len(&msg_ext_last_chunk, (char *)str, len); // -V781 + size_t len = strnlen((char *)str, maxlen); // -V781 + ga_concat_len(&msg_ext_last_chunk, (char *)str, len); msg_ext_cur_len += len; return; } diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index 6738e59bb2..5a5bd16b98 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -495,9 +495,14 @@ open_line ( } if (lead_len > 0) { // allocate buffer (may concatenate p_extra later) - leader = xmalloc((size_t)(lead_len + lead_repl_len + extra_space - + extra_len + (second_line_indent > 0 - ? second_line_indent : 0) + 1)); + int bytes = lead_len + + lead_repl_len + + extra_space + + extra_len + + (second_line_indent > 0 ? second_line_indent : 0) + + 1; + assert(bytes >= 0); + leader = xmalloc((size_t)bytes); allocated = leader; // remember to free it later STRLCPY(leader, saved_line, lead_len + 1); @@ -1556,11 +1561,14 @@ void ins_str(char_u *s) oldp = ml_get(lnum); oldlen = (int)STRLEN(oldp); - newp = (char_u *) xmalloc((size_t)(oldlen + newlen + 1)); - if (col > 0) + newp = (char_u *)xmalloc((size_t)oldlen + (size_t)newlen + 1); + if (col > 0) { memmove(newp, oldp, (size_t)col); + } memmove(newp + col, s, (size_t)newlen); - memmove(newp + col + newlen, oldp + col, (size_t)(oldlen - col + 1)); + int bytes = oldlen - col + 1; + assert(bytes >= 0); + memmove(newp + col + newlen, oldp + col, (size_t)bytes); ml_replace(lnum, newp, false); changed_bytes(lnum, col); curwin->w_cursor.col += newlen; diff --git a/src/nvim/move.c b/src/nvim/move.c index e076543614..05db39b981 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -119,7 +119,6 @@ static void redraw_for_cursorline(win_T *wp) // the current window. redrawWinline(wp, wp->w_last_cursorline); redrawWinline(wp, wp->w_cursor.lnum); - redraw_win_later(wp, VALID); } else { redraw_win_later(wp, SOME_VALID); } diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c index 3f768dcc0c..18b0bf3c16 100644 --- a/src/nvim/msgpack_rpc/helpers.c +++ b/src/nvim/msgpack_rpc/helpers.c @@ -384,10 +384,7 @@ void msgpack_rpc_from_object(const Object result, msgpack_packer *const res) && kObjectTypeTabpage == kObjectTypeWindow + 1, "Buffer, window and tabpage enum items are in order"); switch (cur.aobj->type) { - case kObjectTypeNil: { - msgpack_pack_nil(res); - break; - } + case kObjectTypeNil: case kObjectTypeLuaRef: { // TODO(bfredl): could also be an error. Though kObjectTypeLuaRef // should only appear when the caller has opted in to handle references, diff --git a/src/nvim/normal.c b/src/nvim/normal.c index de8ff52fe1..4ef4b69def 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1376,9 +1376,8 @@ static void set_vcount_ca(cmdarg_T *cap, bool *set_prevcount) *set_prevcount = false; /* only set v:prevcount once */ } -/* - * Handle an operator after visual mode or when the movement is finished - */ +// Handle an operator after Visual mode or when the movement is finished. +// "gui_yank" is true when yanking text for the clipboard. void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) { oparg_T *oap = cap->oap; @@ -1402,8 +1401,12 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) * If an operation is pending, handle it... */ if ((finish_op - || VIsual_active - ) && oap->op_type != OP_NOP) { + || VIsual_active) + && oap->op_type != OP_NOP) { + // Yank can be redone when 'y' is in 'cpoptions', but not when yanking + // for the clipboard. + const bool redo_yank = vim_strchr(p_cpo, CPO_YANK) != NULL && !gui_yank; + // Avoid a problem with unwanted linebreaks in block mode if (curwin->w_p_lbr) { curwin->w_valid &= ~VALID_VIRTCOL; @@ -1433,9 +1436,9 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) VIsual_reselect = false; } - /* Only redo yank when 'y' flag is in 'cpoptions'. */ - /* Never redo "zf" (define fold). */ - if ((vim_strchr(p_cpo, CPO_YANK) != NULL || oap->op_type != OP_YANK) + // Only redo yank when 'y' flag is in 'cpoptions'. + // Never redo "zf" (define fold). + if ((redo_yank || oap->op_type != OP_YANK) && ((!VIsual_active || oap->motion_force) // Also redo Operator-pending Visual mode mappings. || (cap->cmdchar == ':' && oap->op_type != OP_COLON)) @@ -1608,8 +1611,8 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) resel_VIsual_line_count = oap->line_count; } - /* can't redo yank (unless 'y' is in 'cpoptions') and ":" */ - if ((vim_strchr(p_cpo, CPO_YANK) != NULL || oap->op_type != OP_YANK) + // can't redo yank (unless 'y' is in 'cpoptions') and ":" + if ((redo_yank || oap->op_type != OP_YANK) && oap->op_type != OP_COLON && oap->op_type != OP_FOLD && oap->op_type != OP_FOLDOPEN diff --git a/src/nvim/ops.c b/src/nvim/ops.c index b96e075f66..0f26d83597 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -379,8 +379,10 @@ static void shift_block(oparg_T *oap, int amount) /* if we're splitting a TAB, allow for it */ bd.textcol -= bd.pre_whitesp_c - (bd.startspaces != 0); const int len = (int)STRLEN(bd.textstart) + 1; - newp = (char_u *)xmalloc((size_t)(bd.textcol + i + j + len)); - memset(newp, NUL, (size_t)(bd.textcol + i + j + len)); + int col = bd.textcol + i +j + len; + assert(col >= 0); + newp = (char_u *)xmalloc((size_t)col); + memset(newp, NUL, (size_t)col); memmove(newp, oldp, (size_t)bd.textcol); memset(newp + bd.textcol, TAB, (size_t)i); memset(newp + bd.textcol + i, ' ', (size_t)j); @@ -1471,7 +1473,8 @@ int op_delete(oparg_T *oap) // copy up to deleted part memmove(newp, oldp, (size_t)bd.textcol); // insert spaces - memset(newp + bd.textcol, ' ', (size_t)(bd.startspaces + bd.endspaces)); + memset(newp + bd.textcol, ' ', (size_t)bd.startspaces + + (size_t)bd.endspaces); // copy the part after the deleted part oldp += bd.textcol + bd.textlen; STRMOVE(newp + bd.textcol + bd.startspaces + bd.endspaces, oldp); @@ -1743,7 +1746,7 @@ int op_replace(oparg_T *oap, int c) oldp = get_cursor_line_ptr(); oldlen = (int)STRLEN(oldp); - size_t newp_size = (size_t)(bd.textcol + bd.startspaces); + size_t newp_size = (size_t)bd.textcol + (size_t)bd.startspaces; if (had_ctrl_v_cr || (c != '\r' && c != '\n')) { newp_size += (size_t)numc; if (!bd.is_short) { @@ -1760,6 +1763,8 @@ int op_replace(oparg_T *oap, int c) // insert replacement chars CHECK FOR ALLOCATED SPACE // REPLACE_CR_NCHAR/REPLACE_NL_NCHAR is used for entering CR literally. size_t after_p_len = 0; + int col = oldlen - bd.textcol - bd.textlen + 1; + assert(col >= 0); if (had_ctrl_v_cr || (c != '\r' && c != '\n')) { // strlen(newp) at this point int newp_len = bd.textcol + bd.startspaces; @@ -1771,12 +1776,11 @@ int op_replace(oparg_T *oap, int c) memset(newp + newp_len, ' ', (size_t)bd.endspaces); newp_len += bd.endspaces; // copy the part after the changed part - memmove(newp + newp_len, oldp, - (size_t)(oldlen - bd.textcol - bd.textlen + 1)); + memmove(newp + newp_len, oldp, (size_t)col); } } else { // Replacing with \r or \n means splitting the line. - after_p_len = (size_t)(oldlen - bd.textcol - bd.textlen + 1); + after_p_len = (size_t)col; after_p = (char_u *)xmalloc(after_p_len); memmove(after_p, oldp, after_p_len); } @@ -2602,8 +2606,9 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) static void yank_copy_line(yankreg_T *reg, struct block_def *bd, size_t y_idx) { - char_u *pnew = xmallocz((size_t)(bd->startspaces + bd->endspaces - + bd->textlen)); + int size = bd->startspaces + bd->endspaces + bd->textlen; + assert(size >= 0); + char_u *pnew = xmallocz((size_t)size); reg->y_array[y_idx] = pnew; memset(pnew, ' ', (size_t)bd->startspaces); pnew += bd->startspaces; @@ -3085,8 +3090,9 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) memset(ptr, ' ', (size_t)bd.endspaces); ptr += bd.endspaces; // move the text after the cursor to the end of the line. - memmove(ptr, oldp + bd.textcol + delcount, - (size_t)((int)oldlen - bd.textcol - delcount + 1)); + int columns = (int)oldlen - bd.textcol - delcount + 1; + assert(columns >= 0); + memmove(ptr, oldp + bd.textcol + delcount, (size_t)columns); ml_replace(curwin->w_cursor.lnum, newp, false); ++curwin->w_cursor.lnum; @@ -3209,11 +3215,11 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) xfree(newp); oldp = ml_get(lnum); - newp = (char_u *) xmalloc((size_t)(col + yanklen + 1)); - /* copy first part of line */ + newp = (char_u *)xmalloc((size_t)col + (size_t)yanklen + 1); + // copy first part of line memmove(newp, oldp, (size_t)col); - /* append to first line */ - memmove(newp + col, y_array[0], (size_t)(yanklen + 1)); + // append to first line + memmove(newp + col, y_array[0], (size_t)yanklen + 1); ml_replace(lnum, newp, false); curwin->w_cursor.lnum = lnum; @@ -3705,11 +3711,11 @@ int do_join(size_t count, } } - /* store the column position before last line */ + // store the column position before last line col = sumsize - currsize - spaces[count - 1]; - /* allocate the space for the new line */ - newp = (char_u *) xmalloc((size_t)(sumsize + 1)); + // allocate the space for the new line + newp = (char_u *)xmalloc((size_t)sumsize + 1); cend = newp + sumsize; *cend = 0; @@ -5472,7 +5478,7 @@ void cursor_pos_info(dict_T *dict) byte_count_cursor = byte_count + line_count_info(ml_get(lnum), &word_count_cursor, &char_count_cursor, - (varnumber_T)(curwin->w_cursor.col + 1), + (varnumber_T)curwin->w_cursor.col + 1, eol_size); } } @@ -5490,8 +5496,10 @@ void cursor_pos_info(dict_T *dict) if (l_VIsual_active) { if (l_VIsual_mode == Ctrl_V && curwin->w_curswant < MAXCOL) { getvcols(curwin, &min_pos, &max_pos, &min_pos.col, &max_pos.col); + int64_t cols; + STRICT_SUB(oparg.end_vcol + 1, oparg.start_vcol, &cols, int64_t); vim_snprintf((char *)buf1, sizeof(buf1), _("%" PRId64 " Cols; "), - (int64_t)(oparg.end_vcol - oparg.start_vcol + 1)); + cols); } else { buf1[0] = NUL; } diff --git a/src/nvim/option.c b/src/nvim/option.c index 62f736ea31..8483c02bbe 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -2191,6 +2191,7 @@ static void didset_options(void) (void)opt_strings_flags(p_vop, p_ssop_values, &vop_flags, true); (void)opt_strings_flags(p_fdo, p_fdo_values, &fdo_flags, true); (void)opt_strings_flags(p_dy, p_dy_values, &dy_flags, true); + (void)opt_strings_flags(p_rdb, p_rdb_values, &rdb_flags, true); (void)opt_strings_flags(p_tc, p_tc_values, &tc_flags, false); (void)opt_strings_flags(p_ve, p_ve_values, &ve_flags, true); (void)opt_strings_flags(p_wop, p_wop_values, &wop_flags, true); @@ -2650,6 +2651,10 @@ did_set_string_option( if (opt_strings_flags(p_vop, p_ssop_values, &vop_flags, true) != OK) { errmsg = e_invarg; } + } else if (varp == &p_rdb) { // 'redrawdebug' + if (opt_strings_flags(p_rdb, p_rdb_values, &rdb_flags, true) != OK) { + errmsg = e_invarg; + } } else if (varp == &p_sbo) { // 'scrollopt' if (check_opt_strings(p_sbo, p_scbopt_values, true) != OK) { errmsg = e_invarg; @@ -3762,7 +3767,8 @@ static bool parse_winhl_opt(win_T *wp) size_t nlen = (size_t)(colon-p); char *hi = colon+1; char *commap = xstrchrnul(hi, ','); - int hl_id = syn_check_group((char_u *)hi, (int)(commap-hi)); + int len = (int)(commap-hi); + int hl_id = len ? syn_check_group((char_u *)hi, len) : -1; if (strncmp("Normal", p, nlen) == 0) { w_hl_id_normal = hl_id; @@ -5041,6 +5047,11 @@ showoptions( // collect the items in items[] item_count = 0; for (p = &options[0]; p->fullname != NULL; p++) { + // apply :filter /pat/ + if (message_filtered((char_u *)p->fullname)) { + continue; + } + varp = NULL; if (opt_flags != 0) { if (p->indir != PV_NONE) { diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 35fe3b5b00..a480de735d 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -511,6 +511,13 @@ EXTERN char_u *p_pm; // 'patchmode' EXTERN char_u *p_path; // 'path' EXTERN char_u *p_cdpath; // 'cdpath' EXTERN long p_pyx; // 'pyxversion' +EXTERN char_u *p_rdb; // 'redrawdebug' +EXTERN unsigned rdb_flags; +# ifdef IN_OPTION_C +static char *(p_rdb_values[]) = { "compositor", NULL }; +# endif +# define RDB_COMPOSITOR 0x001 + EXTERN long p_rdt; // 'redrawtime' EXTERN int p_remap; // 'remap' EXTERN long p_re; // 'regexpengine' diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 27f72f6441..c48366e205 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -1848,6 +1848,13 @@ return { defaults={if_true={vi=false}} }, { + full_name='redrawdebug', abbreviation='rdb', + type='string', list='onecomma', scope={'global'}, + vi_def=true, + varname='p_rdb', + defaults={if_true={vi=''}} + }, + { full_name='redrawtime', abbreviation='rdt', type='number', scope={'global'}, vi_def=true, diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 65362b545f..4a10b5199c 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -364,7 +364,7 @@ static bool is_executable_in_path(const char_u *name, char_u **abspath) #ifdef WIN32 // Prepend ".;" to $PATH. size_t pathlen = strlen(path_env); - char *path = memcpy(xmallocz(pathlen + 3), "." ENV_SEPSTR, 2); + char *path = memcpy(xmallocz(pathlen + 2), "." ENV_SEPSTR, 2); memcpy(path + 2, path_env, pathlen); #else char *path = xstrdup(path_env); diff --git a/src/nvim/os/pty_process_unix.c b/src/nvim/os/pty_process_unix.c index 97545a6cb1..5fdf0e6181 100644 --- a/src/nvim/os/pty_process_unix.c +++ b/src/nvim/os/pty_process_unix.c @@ -36,11 +36,6 @@ # include "os/pty_process_unix.c.generated.h" #endif -/// Externally defined with gcov. -#ifdef USE_GCOV -void __gcov_flush(void); -#endif - /// termios saved at startup (for TUI) or initialized by pty_process_spawn(). static struct termios termios_default; @@ -64,11 +59,6 @@ int pty_process_spawn(PtyProcess *ptyproc) init_termios(&termios_default); } -#ifdef USE_GCOV - // Flush coverage data before forking, to avoid "Merge mismatch" errors. - __gcov_flush(); -#endif - int status = 0; // zero or negative error code (libuv convention) Process *proc = (Process *)ptyproc; assert(proc->err.closed); diff --git a/src/nvim/os/time.c b/src/nvim/os/time.c index 18239c5566..4dd0614fe2 100644 --- a/src/nvim/os/time.c +++ b/src/nvim/os/time.c @@ -31,27 +31,6 @@ void time_init(void) uv_cond_init(&delay_cond); } -/// Gets the current time with microsecond (μs) precision. -/// -/// Subject to system-clock quirks (drift, going backwards, skipping). -/// But it is much faster than os_hrtime() on some systems. #10328 -/// -/// @see gettimeofday(2) -/// -/// @return Current time in microseconds. -uint64_t os_utime(void) - FUNC_ATTR_WARN_UNUSED_RESULT -{ - uv_timeval64_t tm; - int e = uv_gettimeofday(&tm); - if (e != 0 || tm.tv_sec < 0 || tm.tv_usec < 0) { - return 0; - } - uint64_t rv = (uint64_t)tm.tv_sec * 1000 * 1000; // s => μs - STRICT_ADD(rv, tm.tv_usec, &rv, uint64_t); - return rv; -} - /// Gets a high-resolution (nanosecond), monotonically-increasing time relative /// to an arbitrary time in the past. /// diff --git a/src/nvim/profile.c b/src/nvim/profile.c index cc12e00396..a6314e7b9d 100644 --- a/src/nvim/profile.c +++ b/src/nvim/profile.c @@ -5,6 +5,7 @@ #include <math.h> #include <assert.h> +#include "nvim/assert.h" #include "nvim/profile.h" #include "nvim/os/time.h" #include "nvim/func_attr.h" @@ -23,7 +24,7 @@ static proftime_T prof_wait_time; /// @return the current time proftime_T profile_start(void) FUNC_ATTR_WARN_UNUSED_RESULT { - return os_utime(); + return os_hrtime(); } /// Computes the time elapsed. @@ -31,7 +32,7 @@ proftime_T profile_start(void) FUNC_ATTR_WARN_UNUSED_RESULT /// @return Elapsed time from `tm` until now. proftime_T profile_end(proftime_T tm) FUNC_ATTR_WARN_UNUSED_RESULT { - return profile_sub(os_utime(), tm); + return profile_sub(os_hrtime(), tm); } /// Gets a string representing time `tm`. @@ -43,7 +44,8 @@ proftime_T profile_end(proftime_T tm) FUNC_ATTR_WARN_UNUSED_RESULT const char *profile_msg(proftime_T tm) FUNC_ATTR_WARN_UNUSED_RESULT { static char buf[50]; - snprintf(buf, sizeof(buf), "%10.6lf", (double)tm / 1000000.0); + snprintf(buf, sizeof(buf), "%10.6lf", + (double)profile_signed(tm) / 1000000000.0); return buf; } @@ -59,10 +61,9 @@ proftime_T profile_setlimit(int64_t msec) FUNC_ATTR_WARN_UNUSED_RESULT // no limit return profile_zero(); } - assert(msec <= (INT64_MAX / 1000LL) - 1); - - proftime_T usec = (proftime_T)msec * 1000ULL; - return os_utime() + usec; + assert(msec <= (INT64_MAX / 1000000LL) - 1); + proftime_T nsec = (proftime_T)msec * 1000000ULL; + return os_hrtime() + nsec; } /// Checks if current time has passed `tm`. @@ -75,7 +76,8 @@ bool profile_passed_limit(proftime_T tm) FUNC_ATTR_WARN_UNUSED_RESULT // timer was not set return false; } - return profile_cmp(os_utime(), tm) < 0; + + return profile_cmp(os_hrtime(), tm) < 0; } /// Gets the zero time. @@ -108,10 +110,15 @@ proftime_T profile_add(proftime_T tm1, proftime_T tm2) FUNC_ATTR_CONST /// Subtracts time `tm2` from `tm1`. /// +/// Unsigned overflow (wraparound) occurs if `tm2` is greater than `tm1`. +/// Use `profile_signed()` to get the signed integer value. +/// +/// @see profile_signed +/// /// @return `tm1` - `tm2` proftime_T profile_sub(proftime_T tm1, proftime_T tm2) FUNC_ATTR_CONST { - return tm1 > tm2 ? tm1 - tm2 : 0; // os_utime() may go backwards. + return tm1 - tm2; } /// Adds the `self` time from the total time and the `children` time. @@ -162,22 +169,31 @@ bool profile_equal(proftime_T tm1, proftime_T tm2) FUNC_ATTR_CONST return tm1 == tm2; } -/// Calculates the sign of a 64-bit integer. +/// Converts a proftime_T value `tm` to a signed integer. /// -/// @return -1, 0, or +1 -static inline int sgn64(int64_t x) FUNC_ATTR_CONST +/// @return signed representation of the given time value +int64_t profile_signed(proftime_T tm) + FUNC_ATTR_CONST { - return (int) ((x > 0) - (x < 0)); + // (tm > INT64_MAX) is >=150 years, so we can assume it was produced by + // arithmetic of two proftime_T values. For human-readable representation + // (and Vim-compat) we want the difference after unsigned wraparound. #10452 + return (tm <= INT64_MAX) ? (int64_t)tm : -(int64_t)(UINT64_MAX - tm); } /// Compares profiling times. /// /// Times `tm1` and `tm2` must be less than 150 years apart. /// -/// @return <0, 0 or >0 if `tm2` < `tm1`, `tm2` == `tm1` or `tm2` > `tm1` +/// @return <0: `tm2` < `tm1` +/// 0: `tm2` == `tm1` +/// >0: `tm2` > `tm1` int profile_cmp(proftime_T tm1, proftime_T tm2) FUNC_ATTR_CONST { - return sgn64((int64_t)(tm2 - tm1)); + if (tm1 == tm2) { + return 0; + } + return profile_signed(tm2 - tm1) < 0 ? -1 : 1; } /// globals for use in the startuptime related functionality (time_*). @@ -219,7 +235,7 @@ void time_pop(proftime_T tp) static void time_diff(proftime_T then, proftime_T now) { proftime_T diff = profile_sub(now, then); - fprintf(time_fd, "%07.3lf", (double)diff / 1.0E3); + fprintf(time_fd, "%07.3lf", (double)diff / 1.0E6); } /// Initializes the startuptime code. diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index dc1ab971ab..a8919560a0 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -1263,8 +1263,8 @@ static int nfa_regatom(void) IEMSGN("INTERNAL: Unknown character class char: %" PRId64, c); return FAIL; } - /* When '.' is followed by a composing char ignore the dot, so that - * the composing char is matched here. */ + // When '.' is followed by a composing char ignore the dot, so that + // the composing char is matched here. if (enc_utf8 && c == Magic('.') && utf_iscomposing(peekchr())) { old_regparse = regparse; c = getchr(); @@ -1279,25 +1279,26 @@ static int nfa_regatom(void) break; case Magic('n'): - if (reg_string) - /* In a string "\n" matches a newline character. */ + if (reg_string) { + // In a string "\n" matches a newline character. EMIT(NL); - else { - /* In buffer text "\n" matches the end of a line. */ + } else { + // In buffer text "\n" matches the end of a line. EMIT(NFA_NEWL); regflags |= RF_HASNL; } break; case Magic('('): - if (nfa_reg(REG_PAREN) == FAIL) - return FAIL; /* cascaded error */ + if (nfa_reg(REG_PAREN) == FAIL) { + return FAIL; // cascaded error + } break; case Magic('|'): case Magic('&'): case Magic(')'): - EMSGN(_(e_misplaced), no_Magic(c)); + EMSGN(_(e_misplaced), no_Magic(c)); // -V1037 return FAIL; case Magic('='): @@ -1306,7 +1307,7 @@ static int nfa_regatom(void) case Magic('@'): case Magic('*'): case Magic('{'): - /* these should follow an atom, not form an atom */ + // these should follow an atom, not form an atom EMSGN(_(e_misplaced), no_Magic(c)); return FAIL; @@ -1314,8 +1315,8 @@ static int nfa_regatom(void) { char_u *lp; - /* Previous substitute pattern. - * Generated as "\%(pattern\)". */ + // Previous substitute pattern. + // Generated as "\%(pattern\)". if (reg_prev_sub == NULL) { EMSG(_(e_nopresub)); return FAIL; @@ -3002,8 +3003,8 @@ static nfa_state_T *post2nfa(int *postfix, int *end, int nfa_calc_size) return NULL; \ } - if (nfa_calc_size == FALSE) { - /* Allocate space for the stack. Max states on the stack : nstate */ + if (nfa_calc_size == false) { + // Allocate space for the stack. Max states on the stack: "nstate". stack = xmalloc((nstate + 1) * sizeof(Frag_T)); stackp = stack; stack_end = stack + (nstate + 1); diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 846ffeb442..2f8b1b6b02 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -1612,7 +1612,8 @@ static void win_draw_end(win_T *wp, int c1, int c2, bool draw_margin, int row, } } - int attr = hl_combine_attr(wp->w_hl_attr_normal, win_hl_attr(wp, hl)); + int attr = hl_combine_attr(wp->w_hl_attr_normal, + hl ? win_hl_attr(wp, hl) : 0); if (wp->w_p_rl) { grid_fill(&wp->w_grid, row, endrow, wp->w_wincol, W_ENDCOL(wp) - 1 - n, @@ -3777,7 +3778,7 @@ win_line ( * At end of the text line or just after the last character. */ if (c == NUL) { - long prevcol = (long)(ptr - line) - (c == NUL); + long prevcol = (long)(ptr - line) - 1; /* we're not really at that column when skipping some text */ if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol) @@ -4570,17 +4571,21 @@ void redraw_statuslines(void) /* * Redraw all status lines at the bottom of frame "frp". */ -void win_redraw_last_status(frame_T *frp) +void win_redraw_last_status(const frame_T *frp) + FUNC_ATTR_NONNULL_ARG(1) { - if (frp->fr_layout == FR_LEAF) - frp->fr_win->w_redr_status = TRUE; - else if (frp->fr_layout == FR_ROW) { - for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) + if (frp->fr_layout == FR_LEAF) { + frp->fr_win->w_redr_status = true; + } else if (frp->fr_layout == FR_ROW) { + FOR_ALL_FRAMES(frp, frp->fr_child) { win_redraw_last_status(frp); - } else { /* frp->fr_layout == FR_COL */ + } + } else { + assert(frp->fr_layout == FR_COL); frp = frp->fr_child; - while (frp->fr_next != NULL) + while (frp->fr_next != NULL) { frp = frp->fr_next; + } win_redraw_last_status(frp); } } @@ -6078,8 +6083,8 @@ void grid_alloc(ScreenGrid *grid, int rows, int columns, bool copy, bool valid) { int new_row; ScreenGrid new = *grid; - - size_t ncells = (size_t)((rows+1) * columns); + assert(rows >= 0 && columns >= 0); + size_t ncells = (size_t)rows * columns; new.chars = xmalloc(ncells * sizeof(schar_T)); new.attrs = xmalloc(ncells * sizeof(sattr_T)); new.line_offset = xmalloc((size_t)(rows * sizeof(unsigned))); diff --git a/src/nvim/search.c b/src/nvim/search.c index ed18df3877..ed5934cec2 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -1217,7 +1217,7 @@ int do_search( xfree(msgbuf); msgbuf = r; // move reversed text to beginning of buffer - while (*r != NUL && *r == ' ') { + while (*r == ' ') { r++; } size_t pat_len = msgbuf + STRLEN(msgbuf) - r; diff --git a/src/nvim/sign.c b/src/nvim/sign.c index 8c85fbdaa7..9c391428ca 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -83,11 +83,8 @@ static signgroup_T * sign_group_ref(const char_u *groupname) hi = hash_lookup(&sg_table, (char *)groupname, STRLEN(groupname), hash); if (HASHITEM_EMPTY(hi)) { // new group - group = (signgroup_T *)xmalloc( - (unsigned)(sizeof(signgroup_T) + STRLEN(groupname))); - if (group == NULL) { - return NULL; - } + group = xmalloc((unsigned)(sizeof(signgroup_T) + STRLEN(groupname))); + STRCPY(group->sg_name, groupname); group->refcount = 1; group->next_sign_id = 1; @@ -188,10 +185,6 @@ static void insert_sign( newsign->typenr = typenr; if (group != NULL) { newsign->group = sign_group_ref(group); - if (newsign->group == NULL) { - xfree(newsign); - return; - } } else { newsign->group = NULL; } @@ -1347,8 +1340,8 @@ static void sign_getinfo(sign_T *sp, dict_T *retdict) /// Otherwise, return information about the specified sign. void sign_getlist(const char_u *name, list_T *retlist) { - sign_T *sp = first_sign; - dict_T *dict; + sign_T *sp = first_sign; + dict_T *dict; if (name != NULL) { sp = sign_find(name, NULL); @@ -1358,9 +1351,7 @@ void sign_getlist(const char_u *name, list_T *retlist) } for (; sp != NULL && !got_int; sp = sp->sn_next) { - if ((dict = tv_dict_alloc()) == NULL) { - return; - } + dict = tv_dict_alloc(); tv_list_append_dict(retlist, dict); sign_getinfo(sp, dict); @@ -1374,14 +1365,13 @@ void sign_getlist(const char_u *name, list_T *retlist) list_T *get_buffer_signs(buf_T *buf) FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { - signlist_T *sign; - dict_T *d; + signlist_T *sign; + dict_T *d; list_T *const l = tv_list_alloc(kListLenMayKnow); FOR_ALL_SIGNS_IN_BUF(buf, sign) { - if ((d = sign_get_info(sign)) != NULL) { - tv_list_append_dict(l, d); - } + d = sign_get_info(sign); + tv_list_append_dict(l, d); } return l; } @@ -1394,21 +1384,16 @@ static void sign_get_placed_in_buf( const char_u *sign_group, list_T *retlist) { - dict_T *d; - list_T *l; - signlist_T *sign; - dict_T *sdict; + dict_T *d; + list_T *l; + signlist_T *sign; - if ((d = tv_dict_alloc()) == NULL) { - return; - } + d = tv_dict_alloc(); tv_list_append_dict(retlist, d); tv_dict_add_nr(d, S_LEN("bufnr"), (long)buf->b_fnum); - if ((l = tv_list_alloc(kListLenMayKnow)) == NULL) { - return; - } + l = tv_list_alloc(kListLenMayKnow); tv_dict_add_list(d, S_LEN("signs"), l); FOR_ALL_SIGNS_IN_BUF(buf, sign) { @@ -1419,9 +1404,7 @@ static void sign_get_placed_in_buf( || (sign_id == 0 && lnum == sign->lnum) || (lnum == 0 && sign_id == sign->id) || (lnum == sign->lnum && sign_id == sign->id)) { - if ((sdict = sign_get_info(sign)) != NULL) { - tv_list_append_dict(l, sdict); - } + tv_list_append_dict(l, sign_get_info(sign)); } } } diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 6fd22a6537..17306744ad 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -5283,7 +5283,7 @@ add_sound_suggest ( } // Go over the list of good words that produce this soundfold word - nrline = ml_get_buf(slang->sl_sugbuf, (linenr_T)(sfwordnr + 1), FALSE); + nrline = ml_get_buf(slang->sl_sugbuf, (linenr_T)sfwordnr + 1, false); orgnr = 0; while (*nrline != NUL) { // The wordnr was stored in a minimal nr of bytes as an offset to the diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 0d204b2f43..4fa70c0684 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -14,6 +14,7 @@ #include "nvim/vim.h" #include "nvim/ascii.h" +#include "nvim/api/private/helpers.h" #include "nvim/syntax.h" #include "nvim/charset.h" #include "nvim/cursor_shape.h" @@ -350,7 +351,7 @@ static reg_extmatch_T *next_match_extmatch = NULL; /* * A state stack is an array of integers or stateitem_T, stored in a - * garray_T. A state stack is invalid if it's itemsize entry is zero. + * garray_T. A state stack is invalid if its itemsize entry is zero. */ #define INVALID_STATE(ssp) ((ssp)->ga_itemsize == 0) #define VALID_STATE(ssp) ((ssp)->ga_itemsize != 0) @@ -3608,7 +3609,7 @@ syn_list_one( continue; } - (void)syn_list_header(did_header, 999, id); + (void)syn_list_header(did_header, 0, id, true); did_header = true; last_matchgroup = 0; if (spp->sp_type == SPTYPE_MATCH) { @@ -3657,7 +3658,7 @@ syn_list_one( /* list the link, if there is one */ if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int) { - (void)syn_list_header(did_header, 999, id); + (void)syn_list_header(did_header, 0, id, true); msg_puts_attr("links to", attr); msg_putchar(' '); msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name); @@ -3799,7 +3800,6 @@ static bool syn_list_keywords( const int attr ) { - int outlen; int prev_contained = 0; const int16_t *prev_next_list = NULL; const int16_t *prev_cont_in_list = NULL; @@ -3817,17 +3817,20 @@ static bool syn_list_keywords( todo--; for (keyentry_T *kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next) { if (kp->k_syn.id == id) { + int outlen = 0; + bool force_newline = false; if (prev_contained != (kp->flags & HL_CONTAINED) || prev_skipnl != (kp->flags & HL_SKIPNL) || prev_skipwhite != (kp->flags & HL_SKIPWHITE) || prev_skipempty != (kp->flags & HL_SKIPEMPTY) || prev_cont_in_list != kp->k_syn.cont_in_list - || prev_next_list != kp->next_list) - outlen = 9999; - else + || prev_next_list != kp->next_list) { + force_newline = true; + } else { outlen = (int)STRLEN(kp->keyword); - /* output "contained" and "nextgroup" on each line */ - if (syn_list_header(did_header, outlen, id)) { + } + // output "contained" and "nextgroup" on each line + if (syn_list_header(did_header, outlen, id, force_newline)) { prev_contained = 0; prev_next_list = NULL; prev_cont_in_list = NULL; @@ -5970,6 +5973,10 @@ static const char *highlight_init_both[] = { "default link Whitespace NonText", "default link MsgSeparator StatusLine", "default link NormalFloat Pmenu", + "RedrawDebugNormal cterm=reverse gui=reverse", + "RedrawDebugClear ctermbg=Yellow guibg=Yellow", + "RedrawDebugComposed ctermbg=Green guibg=Green", + "RedrawDebugRecompose ctermbg=Red guibg=Red", NULL }; @@ -7044,6 +7051,10 @@ static void highlight_list_one(const int id) struct hl_group *const sgp = &HL_TABLE()[id - 1]; // index is ID minus one bool didh = false; + if (message_filtered(sgp->sg_name)) { + return; + } + didh = highlight_list_arg(id, didh, LIST_ATTR, sgp->sg_cterm, NULL, "cterm"); didh = highlight_list_arg(id, didh, LIST_INT, @@ -7064,7 +7075,7 @@ static void highlight_list_one(const int id) sgp->sg_blend+1, NULL, "blend"); if (sgp->sg_link && !got_int) { - (void)syn_list_header(didh, 9999, id); + (void)syn_list_header(didh, 0, id, true); didh = true; msg_puts_attr("links to", HL_ATTR(HLF_D)); msg_putchar(' '); @@ -7109,7 +7120,8 @@ static bool highlight_list_arg( } } - (void)syn_list_header(didh, (int)(vim_strsize(ts) + STRLEN(name) + 1), id); + (void)syn_list_header(didh, (int)(vim_strsize(ts) + STRLEN(name) + 1), id, + false); didh = true; if (!got_int) { if (*name != NUL) { @@ -7227,9 +7239,10 @@ const char *highlight_color(const int id, const char *const what, /// @param did_header did header already /// @param outlen length of string that comes /// @param id highlight group id +/// @param force_newline always start a new line /// @return true when started a new line. static bool syn_list_header(const bool did_header, const int outlen, - const int id) + const int id, bool force_newline) { int endcol = 19; bool newline = true; @@ -7242,10 +7255,10 @@ static bool syn_list_header(const bool did_header, const int outlen, } msg_outtrans(HL_TABLE()[id - 1].sg_name); endcol = 15; - } else if (ui_has(kUIMessages) || msg_silent) { + } else if ((ui_has(kUIMessages) || msg_silent) && !force_newline) { msg_putchar(' '); adjust = false; - } else if (msg_col + outlen + 1 >= Columns) { + } else if (msg_col + outlen + 1 >= Columns || force_newline) { msg_putchar('\n'); if (got_int) { return true; @@ -7518,6 +7531,12 @@ void highlight_changed(void) highlight_attr[hlf] = hl_get_ui_attr(hlf, final_id, hlf == (int)HLF_INACTIVE); + + if (highlight_attr[hlf] != highlight_attr_last[hlf]) { + ui_call_hl_group_set(cstr_as_string((char *)hlf_names[hlf]), + highlight_attr[hlf]); + highlight_attr_last[hlf] = highlight_attr[hlf]; + } } /* Setup the user highlights diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index fe9e0bc9c8..8b43d91e25 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -99,23 +99,24 @@ RM_ON_RUN := test.out X* viminfo RM_ON_START := test.ok RUN_VIM := $(TOOL) $(NVIM_PRG) -u unix.vim -U NONE -i viminfo --headless --noplugin -s dotest.in +CLEAN_FILES := *.out \ + *.failed \ + *.res \ + *.rej \ + *.orig \ + *.tlog \ + test.log \ + messages \ + $(RM_ON_RUN) \ + $(RM_ON_START) \ + valgrind.* \ + .*.swp \ + .*.swo \ + .gdbinit \ + $(TMPDIR) \ + del clean: - -rm -rf *.out \ - *.failed \ - *.res \ - *.rej \ - *.orig \ - *.tlog \ - test.log \ - messages \ - $(RM_ON_RUN) \ - $(RM_ON_START) \ - valgrind.* \ - .*.swp \ - .*.swo \ - .gdbinit \ - $(TMPDIR) \ - del + $(RM) -rf $(CLEAN_FILES) test1.out: .gdbinit test1.in @echo "[OLDTEST-PREP] Running test1" diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim index 605e7c73eb..009908ec09 100644 --- a/src/nvim/testdir/runtest.vim +++ b/src/nvim/testdir/runtest.vim @@ -263,7 +263,7 @@ else endif " Names of flaky tests. -let s:flaky = [ +let s:flaky_tests = [ \ 'Test_cursorhold_insert()', \ 'Test_exit_callback_interval()', \ 'Test_oneshot()', @@ -281,6 +281,9 @@ let s:flaky = [ \ 'Test_lambda_with_timer()', \ ] +" Pattern indicating a common flaky test failure. +let s:flaky_errors_re = 'StopVimInTerminal\|VerifyScreenDump' + " Locate Test_ functions and execute them. redir @q silent function /^Test_ @@ -305,7 +308,9 @@ for s:test in sort(s:tests) " Repeat a flaky test. Give up when: " - it fails again with the same message " - it fails five times (with a different mesage) - if len(v:errors) > 0 && index(s:flaky, s:test) >= 0 + if len(v:errors) > 0 + \ && (index(s:flaky_tests, s:test) >= 0 + \ || v:errors[0] =~ s:flaky_errors_re) while 1 call add(s:messages, 'Found errors in ' . s:test . ':') call extend(s:messages, v:errors) diff --git a/src/nvim/testdir/test_assert.vim b/src/nvim/testdir/test_assert.vim index fe87bd6ef5..a4c8ce7e43 100644 --- a/src/nvim/testdir/test_assert.vim +++ b/src/nvim/testdir/test_assert.vim @@ -1,5 +1,11 @@ " Test that the methods used for testing work. +func Test_assert_fails_in_try_block() + try + call assert_equal(0, assert_fails('throw "error"')) + endtry +endfunc + " Must be last. func Test_zz_quit_detected() " Verify that if a test function ends Vim the test script detects this. diff --git a/src/nvim/testdir/test_filter_cmd.vim b/src/nvim/testdir/test_filter_cmd.vim index 86347ab77f..0c45db049b 100644 --- a/src/nvim/testdir/test_filter_cmd.vim +++ b/src/nvim/testdir/test_filter_cmd.vim @@ -87,3 +87,61 @@ func Test_filter_cmd_with_filter() call assert_equal('a|b', out) set shelltemp& endfunction + +func Test_filter_commands() + let g:test_filter_a = 1 + let b:test_filter_b = 2 + let test_filter_c = 3 + + " Test filtering :let command + let res = split(execute("filter /^test_filter/ let"), "\n") + call assert_equal(["test_filter_a #1"], res) + + let res = split(execute("filter /\\v^(b:)?test_filter/ let"), "\n") + call assert_equal(["test_filter_a #1", "b:test_filter_b #2"], res) + + unlet g:test_filter_a + unlet b:test_filter_b + unlet test_filter_c + + " Test filtering :set command + let helplang=&helplang + set helplang=en + let res = join(split(execute("filter /^help/ set"), "\n")[1:], " ") + call assert_match('^\s*helplang=\w*$', res) + let &helplang=helplang + + " Test filtering :llist command + call setloclist(0, [{"filename": "/path/vim.c"}, {"filename": "/path/vim.h"}, {"module": "Main.Test"}]) + let res = split(execute("filter /\\.c$/ llist"), "\n") + call assert_equal([" 1 /path/vim.c: "], res) + + let res = split(execute("filter /\\.Test$/ llist"), "\n") + call assert_equal([" 3 Main.Test: "], res) + + " Test filtering :jump command + e file.c + e file.h + e file.hs + let res = split(execute("filter /\.c$/ jumps"), "\n")[1:] + call assert_equal([" 2 1 0 file.c", ">"], res) + + " Test filtering :marks command + b file.c + mark A + b file.h + mark B + let res = split(execute("filter /\.c$/ marks"), "\n")[1:] + call assert_equal([" A 1 0 file.c"], res) + + call setline(1, ['one', 'two', 'three']) + 1mark a + 2mark b + 3mark c + let res = split(execute("filter /two/ marks abc"), "\n")[1:] + call assert_equal([" b 2 0 two"], res) + + bwipe! file.c + bwipe! file.h + bwipe! file.hs +endfunc diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 0c3c356622..615536baef 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -1188,3 +1188,45 @@ func Test_libcall_libcallnr() call assert_fails("call libcall('Xdoesnotexist_', 'getenv', 'HOME')", 'E364:') call assert_fails("call libcallnr('Xdoesnotexist_', 'strlen', 'abcd')", 'E364:') endfunc + +func Test_bufadd_bufload() + call assert_equal(0, bufexists('someName')) + let buf = bufadd('someName') + call assert_notequal(0, buf) + call assert_equal(1, bufexists('someName')) + call assert_equal(0, getbufvar(buf, '&buflisted')) + call assert_equal(0, bufloaded(buf)) + call bufload(buf) + call assert_equal(1, bufloaded(buf)) + call assert_equal([''], getbufline(buf, 1, '$')) + + let curbuf = bufnr('') + call writefile(['some', 'text'], 'otherName') + let buf = bufadd('otherName') + call assert_notequal(0, buf) + call assert_equal(1, bufexists('otherName')) + call assert_equal(0, getbufvar(buf, '&buflisted')) + call assert_equal(0, bufloaded(buf)) + call bufload(buf) + call assert_equal(1, bufloaded(buf)) + call assert_equal(['some', 'text'], getbufline(buf, 1, '$')) + call assert_equal(curbuf, bufnr('')) + + let buf1 = bufadd('') + let buf2 = bufadd('') + call assert_notequal(0, buf1) + call assert_notequal(0, buf2) + call assert_notequal(buf1, buf2) + call assert_equal(1, bufexists(buf1)) + call assert_equal(1, bufexists(buf2)) + call assert_equal(0, bufloaded(buf1)) + exe 'bwipe ' .. buf1 + call assert_equal(0, bufexists(buf1)) + call assert_equal(1, bufexists(buf2)) + exe 'bwipe ' .. buf2 + call assert_equal(0, bufexists(buf2)) + + bwipe someName + bwipe otherName + call assert_equal(0, bufexists('someName')) +endfunc diff --git a/src/nvim/testdir/test_source.vim b/src/nvim/testdir/test_source.vim index 42ac0c4d0f..697b552df4 100644 --- a/src/nvim/testdir/test_source.vim +++ b/src/nvim/testdir/test_source.vim @@ -8,3 +8,40 @@ func Test_source_sandbox() call assert_fails('sandbox source! Xsourcehello', 'E48:') bwipe! endfunc + +func Test_source_autocmd() + call writefile([ + \ 'let did_source = 1', + \ ], 'Xsourced') + au SourcePre *source* let did_source_pre = 1 + au SourcePost *source* let did_source_post = 1 + + source Xsourced + + call assert_equal(g:did_source, 1) + call assert_equal(g:did_source_pre, 1) + call assert_equal(g:did_source_post, 1) + + call delete('Xsourced') + au! SourcePre + au! SourcePost + unlet g:did_source + unlet g:did_source_pre + unlet g:did_source_post +endfunc + +func Test_source_cmd() + au SourceCmd *source* let did_source = expand('<afile>') + au SourcePre *source* let did_source_pre = 2 + au SourcePost *source* let did_source_post = 2 + + source Xsourced + + call assert_equal(g:did_source, 'Xsourced') + call assert_false(exists('g:did_source_pre')) + call assert_equal(g:did_source_post, 2) + + au! SourceCmd + au! SourcePre + au! SourcePost +endfunc diff --git a/src/nvim/testdir/test_suspend.vim b/src/nvim/testdir/test_suspend.vim index e569e49055..ef5a96bd72 100644 --- a/src/nvim/testdir/test_suspend.vim +++ b/src/nvim/testdir/test_suspend.vim @@ -2,6 +2,20 @@ source shared.vim +func CheckSuspended(buf, fileExists) + call WaitForAssert({-> assert_match('[$#] $', term_getline(a:buf, '.'))}) + + if a:fileExists + call assert_equal(['foo'], readfile('Xfoo')) + else + " Without 'autowrite', buffer should not be written. + call assert_equal(0, filereadable('Xfoo')) + endif + + call term_sendkeys(a:buf, "fg\<CR>\<C-L>") + call WaitForAssert({-> assert_equal(' 1 foo', term_getline(a:buf, '.'))}) +endfunc + func Test_suspend() if !has('terminal') || !executable('/bin/sh') return @@ -26,13 +40,7 @@ func Test_suspend() \ "\<C-Z>"] " Suspend and wait for shell prompt. call term_sendkeys(buf, suspend_cmd) - call WaitForAssert({-> assert_match('[$#] $', term_getline(buf, '.'))}) - - " Without 'autowrite', buffer should not be written. - call assert_equal(0, filereadable('Xfoo')) - - call term_sendkeys(buf, "fg\<CR>") - call WaitForAssert({-> assert_equal(' 1 foo', term_getline(buf, '.'))}) + call CheckSuspended(buf, 0) endfor " Test that :suspend! with 'autowrite' writes content of buffers if modified. @@ -40,14 +48,13 @@ func Test_suspend() call assert_equal(0, filereadable('Xfoo')) call term_sendkeys(buf, ":suspend\<CR>") " Wait for shell prompt. - call WaitForAssert({-> assert_match('[$#] $', term_getline(buf, '.'))}) - call assert_equal(['foo'], readfile('Xfoo')) - call term_sendkeys(buf, "fg\<CR>") - call WaitForAssert({-> assert_equal(' 1 foo', term_getline(buf, '.'))}) + call CheckSuspended(buf, 1) " Quit gracefully to dump coverage information. call term_sendkeys(buf, ":qall!\<CR>") call term_wait(buf) + " Wait until Vim actually exited and shell shows a prompt + call WaitForAssert({-> assert_match('[$#] $', term_getline(buf, '.'))}) call Stop_shell_in_terminal(buf) exe buf . 'bwipe!' diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim index 003a23ea7b..2a07a04401 100644 --- a/src/nvim/testdir/test_window_cmd.vim +++ b/src/nvim/testdir/test_window_cmd.vim @@ -518,6 +518,43 @@ func Test_winrestcmd() only endfunc +function! Fun_RenewFile() + sleep 2 + silent execute '!echo "1" > tmp.txt' + sp + wincmd p + edit! tmp.txt +endfunction + +func Test_window_prevwin() + " Can we make this work on MS-Windows? + if !has('unix') + return + endif + + set hidden autoread + call writefile(['2'], 'tmp.txt') + new tmp.txt + q + " Need to wait a bit for the timestamp to be older. + call Fun_RenewFile() + call assert_equal(2, winnr()) + wincmd p + call assert_equal(1, winnr()) + wincmd p + q + call Fun_RenewFile() + call assert_equal(2, winnr()) + wincmd p + call assert_equal(1, winnr()) + wincmd p + " reset + q + call delete('tmp.txt') + set hidden&vim autoread&vim + delfunc Fun_RenewFile +endfunc + func Test_relative_cursor_position_in_one_line_window() new only diff --git a/src/nvim/ugrid.c b/src/nvim/ugrid.c index 8adb421ee1..9e4aaff878 100644 --- a/src/nvim/ugrid.c +++ b/src/nvim/ugrid.c @@ -6,6 +6,7 @@ #include <stdio.h> #include <limits.h> +#include "nvim/assert.h" #include "nvim/vim.h" #include "nvim/ui.h" #include "nvim/ugrid.h" @@ -72,8 +73,9 @@ void ugrid_scroll(UGrid *grid, int top, int bot, int left, int right, int count) for (i = start; i != stop; i += step) { UCell *target_row = grid->cells[i] + left; UCell *source_row = grid->cells[i + count] + left; + assert(right >= left && left >= 0); memcpy(target_row, source_row, - sizeof(UCell) * (size_t)(right - left + 1)); + sizeof(UCell) * ((size_t)right - (size_t)left + 1)); } } diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 7dbb8ec790..fc4a3a403d 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -341,15 +341,15 @@ void ui_line(ScreenGrid *grid, int row, int startcol, int endcol, int clearcol, flags, (const schar_T *)grid->chars + off, (const sattr_T *)grid->attrs + off); - if (p_wd) { // 'writedelay': flush & delay each time. - int old_row = cursor_row, old_col = cursor_col; - handle_T old_grid = cursor_grid_handle; + // 'writedelay': flush & delay each time. + if (p_wd && !(rdb_flags & RDB_COMPOSITOR)) { // If 'writedelay' is active, set the cursor to indicate what was drawn. - ui_grid_cursor_goto(grid->handle, row, MIN(clearcol, (int)Columns-1)); - ui_flush(); + ui_call_grid_cursor_goto(grid->handle, row, + MIN(clearcol, (int)grid->Columns-1)); + ui_call_flush(); uint64_t wd = (uint64_t)labs(p_wd); os_microdelay(wd * 1000u, true); - ui_grid_cursor_goto(old_grid, old_row, old_col); + pending_cursor_update = true; // restore the cursor later } } @@ -372,6 +372,14 @@ void ui_grid_cursor_goto(handle_T grid_handle, int new_row, int new_col) pending_cursor_update = true; } +/// moving the cursor grid will implicitly move the cursor +void ui_check_cursor_grid(handle_T grid_handle) +{ + if (cursor_grid_handle == grid_handle) { + pending_cursor_update = true; + } +} + void ui_mode_info_set(void) { pending_mode_info_update = true; diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c index d12a411019..d0b21ae591 100644 --- a/src/nvim/ui_compositor.c +++ b/src/nvim/ui_compositor.c @@ -49,6 +49,8 @@ static bool valid_screen = true; static bool msg_scroll_mode = false; static int msg_first_invalid = 0; +static int dbghl_normal, dbghl_clear, dbghl_composed, dbghl_recompose; + void ui_comp_init(void) { if (compositor != NULL) { @@ -81,6 +83,13 @@ void ui_comp_init(void) ui_attach_impl(compositor); } +void ui_comp_syn_init(void) +{ + dbghl_normal = syn_check_group((char_u *)S_LEN("RedrawDebugNormal")); + dbghl_clear = syn_check_group((char_u *)S_LEN("RedrawDebugClear")); + dbghl_composed = syn_check_group((char_u *)S_LEN("RedrawDebugComposed")); + dbghl_recompose = syn_check_group((char_u *)S_LEN("RedrawDebugRecompose")); +} void ui_comp_attach(UI *ui) { @@ -290,10 +299,14 @@ static void compose_line(Integer row, Integer startcol, Integer endcol, { // in case we start on the right half of a double-width char, we need to // check the left half. But skip it in output if it wasn't doublewidth. - int skip = 0; + int skipstart = 0, skipend = 0; if (startcol > 0 && (flags & kLineFlagInvalid)) { startcol--; - skip = 1; + skipstart = 1; + } + if (endcol < default_grid.Columns && (flags & kLineFlagInvalid)) { + endcol++; + skipend = 1; } int col = (int)startcol; @@ -331,11 +344,22 @@ static void compose_line(Integer row, Integer startcol, Integer endcol, // 'pumblend' and 'winblend' if (grid->blending) { - for (int i = col-(int)startcol; i < until-startcol; i++) { - bool thru = strequal((char *)linebuf[i], " "); // negative space + int width; + for (int i = col-(int)startcol; i < until-startcol; i += width) { + width = 1; + // negative space + bool thru = strequal((char *)linebuf[i], " ") && bg_line[i][0] != NUL; + if (i+1 < endcol-startcol && bg_line[i+1][0] == NUL) { + width = 2; + thru &= strequal((char *)linebuf[i+1], " "); + } attrbuf[i] = (sattr_T)hl_blend_attrs(bg_attrs[i], attrbuf[i], &thru); + if (width == 2) { + attrbuf[i+1] = (sattr_T)hl_blend_attrs(bg_attrs[i+1], + attrbuf[i+1], &thru); + } if (thru) { - memcpy(linebuf[i], bg_line[i], sizeof(linebuf[i])); + memcpy(linebuf[i], bg_line[i], (size_t)width * sizeof(linebuf[i])); } } } @@ -345,20 +369,27 @@ static void compose_line(Integer row, Integer startcol, Integer endcol, if (linebuf[col-startcol][0] == NUL) { linebuf[col-startcol][0] = ' '; linebuf[col-startcol][1] = NUL; + if (col == endcol-1) { + skipend = 0; + } } else if (n > 1 && linebuf[col-startcol+1][0] == NUL) { - skip = 0; + skipstart = 0; } if (grid->comp_col+grid->Columns > until && grid->chars[off+n][0] == NUL) { linebuf[until-1-startcol][0] = ' '; linebuf[until-1-startcol][1] = '\0'; if (col == startcol && n == 1) { - skip = 0; + skipstart = 0; } } col = until; } + if (linebuf[endcol-startcol-1][0] == NUL) { + skipend = 0; + } + assert(endcol <= chk_width); assert(row < chk_height); @@ -368,14 +399,48 @@ static void compose_line(Integer row, Integer startcol, Integer endcol, flags = flags & ~kLineFlagWrap; } - ui_composed_call_raw_line(1, row, startcol+skip, endcol, endcol, 0, flags, - (const schar_T *)linebuf+skip, - (const sattr_T *)attrbuf+skip); + ui_composed_call_raw_line(1, row, startcol+skipstart, + endcol-skipend, endcol-skipend, 0, flags, + (const schar_T *)linebuf+skipstart, + (const sattr_T *)attrbuf+skipstart); } +static void compose_debug(Integer startrow, Integer endrow, Integer startcol, + Integer endcol, int syn_id, bool delay) +{ + if (!(rdb_flags & RDB_COMPOSITOR)) { + return; + } + + endrow = MIN(endrow, default_grid.Rows); + endcol = MIN(endcol, default_grid.Columns); + int attr = syn_id2attr(syn_id); + + for (int row = (int)startrow; row < endrow; row++) { + ui_composed_call_raw_line(1, row, startcol, startcol, endcol, attr, false, + (const schar_T *)linebuf, + (const sattr_T *)attrbuf); + } + + + if (delay) { + debug_delay(endrow-startrow); + } +} + +static void debug_delay(Integer lines) +{ + ui_call_flush(); + uint64_t wd = (uint64_t)labs(p_wd); + uint64_t factor = (uint64_t)MAX(MIN(lines, 5), 1); + os_microdelay(factor * wd * 1000u, true); +} + + static void compose_area(Integer startrow, Integer endrow, Integer startcol, Integer endcol) { + compose_debug(startrow, endrow, startcol, endcol, dbghl_recompose, true); endrow = MIN(endrow, default_grid.Rows); endcol = MIN(endcol, default_grid.Columns); if (endcol <= startcol) { @@ -420,8 +485,11 @@ static void ui_comp_raw_line(UI *ui, Integer grid, Integer row, if (flags & kLineFlagInvalid || kv_size(layers) > curgrid->comp_index+1 || curgrid->blending) { + compose_debug(row, row+1, startcol, clearcol, dbghl_composed, true); compose_line(row, startcol, clearcol, flags); } else { + compose_debug(row, row+1, startcol, endcol, dbghl_normal, false); + compose_debug(row, row+1, endcol, clearcol, dbghl_clear, true); ui_composed_call_raw_line(1, row, startcol, endcol, clearcol, clearattr, flags, chunk, attrs); } @@ -481,6 +549,9 @@ static void ui_comp_grid_scroll(UI *ui, Integer grid, Integer top, } else { msg_first_invalid = MIN(msg_first_invalid, (int)top); ui_composed_call_grid_scroll(1, top, bot, left, right, rows, cols); + if (rdb_flags & RDB_COMPOSITOR) { + debug_delay(2); + } } } diff --git a/src/nvim/window.c b/src/nvim/window.c index a3b1efeaaa..38246bfe2a 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -375,7 +375,7 @@ newwindow: /* set current window height */ case Ctrl__: case '_': - win_setheight(Prenum ? (int)Prenum : 9999); + win_setheight(Prenum ? (int)Prenum : Rows-1); break; /* increase current window width */ @@ -390,7 +390,7 @@ newwindow: /* set current window width */ case '|': - win_setwidth(Prenum != 0 ? (int)Prenum : 9999); + win_setwidth(Prenum != 0 ? (int)Prenum : Columns); break; /* jump to tag and split window if tag exists (in preview window) */ @@ -584,15 +584,43 @@ win_T *win_new_float(win_T *wp, FloatConfig fconfig, Error *err) wp->w_status_height = 0; wp->w_vsep_width = 0; - // TODO(bfredl): use set_option_to() after merging #9110 ? - wp->w_p_nu = false; - wp->w_allbuf_opt.wo_nu = false; win_config_float(wp, fconfig); wp->w_pos_changed = true; redraw_win_later(wp, VALID); return wp; } +void win_set_minimal_style(win_T *wp) +{ + wp->w_p_nu = false; + wp->w_p_rnu = false; + wp->w_p_cul = false; + wp->w_p_cuc = false; + wp->w_p_spell = false; + wp->w_p_list = false; + + // Hide EOB region: use " " fillchar and cleared highlighting + if (wp->w_p_fcs_chars.eob != ' ') { + char_u *old = wp->w_p_fcs; + wp->w_p_fcs = ((*old == NUL) + ? (char_u *)xstrdup("eob: ") + : concat_str(old, (char_u *)",eob: ")); + xfree(old); + } + if (wp->w_hl_ids[HLF_EOB] != -1) { + char_u *old = wp->w_p_winhl; + wp->w_p_winhl = ((*old == NUL) + ? (char_u *)xstrdup("EndOfBuffer:") + : concat_str(old, (char_u *)",EndOfBuffer:")); + xfree(old); + } + + if (wp->w_p_scl[0] != 'a') { + xfree(wp->w_p_scl); + wp->w_p_scl = (char_u *)xstrdup("auto"); + } +} + void win_config_float(win_T *wp, FloatConfig fconfig) { wp->w_width = MAX(fconfig.width, 1); @@ -666,6 +694,7 @@ static void ui_ext_win_position(win_T *wp) bool on_top = (curwin == wp) || !curwin->w_floating; ui_comp_put_grid(&wp->w_grid, comp_row, comp_col, wp->w_height, wp->w_width, valid, on_top); + ui_check_cursor_grid(wp->w_grid.handle); if (!valid) { wp->w_grid.valid = false; redraw_win_later(wp, NOT_VALID); @@ -821,6 +850,20 @@ bool parse_float_config(Dictionary config, FloatConfig *fconfig, bool reconf, "'focusable' key must be Boolean"); return false; } + } else if (!strcmp(key, "style")) { + if (val.type != kObjectTypeString) { + api_set_error(err, kErrorTypeValidation, + "'style' key must be String"); + return false; + } + if (val.data.string.data[0] == NUL) { + fconfig->style = kWinStyleUnused; + } else if (striequal(val.data.string.data, "minimal")) { + fconfig->style = kWinStyleMinimal; + } else { + api_set_error(err, kErrorTypeValidation, + "Invalid value of 'style' key"); + } } else { api_set_error(err, kErrorTypeValidation, "Invalid key '%s'", key); @@ -981,7 +1024,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) for (frp = oldwin->w_frame->fr_parent; frp != NULL; frp = frp->fr_parent) { if (frp->fr_layout == FR_ROW) { - for (frp2 = frp->fr_child; frp2 != NULL; frp2 = frp2->fr_next) { + FOR_ALL_FRAMES(frp2, frp->fr_child) { if (frp2 != prevfrp) { minwidth += frame_minwidth(frp2, NOWIN); } @@ -1059,7 +1102,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) for (frp = oldwin->w_frame->fr_parent; frp != NULL; frp = frp->fr_parent) { if (frp->fr_layout == FR_COL) { - for (frp2 = frp->fr_child; frp2 != NULL; frp2 = frp2->fr_next) { + FOR_ALL_FRAMES(frp2, frp->fr_child) { if (frp2 != prevfrp) { minheight += frame_minheight(frp2, NOWIN); } @@ -1204,11 +1247,13 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) curfrp->fr_child = frp; curfrp->fr_win = NULL; curfrp = frp; - if (frp->fr_win != NULL) + if (frp->fr_win != NULL) { oldwin->w_frame = frp; - else - for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) + } else { + FOR_ALL_FRAMES(frp, frp->fr_child) { frp->fr_parent = curfrp; + } + } } if (new_wp == NULL) @@ -1674,13 +1719,13 @@ static void win_rotate(bool upwards, int count) return; } - /* Check if all frames in this row/col have one window. */ - for (frp = curwin->w_frame->fr_parent->fr_child; frp != NULL; - frp = frp->fr_next) + // Check if all frames in this row/col have one window. + FOR_ALL_FRAMES(frp, curwin->w_frame->fr_parent->fr_child) { if (frp->fr_win == NULL) { EMSG(_("E443: Cannot rotate when another window is split")); return; } + } while (count--) { if (upwards) { /* first window becomes last window */ @@ -1918,10 +1963,10 @@ static void win_equal_rec( room = 0; } else { next_curwin_size = -1; - for (fr = topfr->fr_child; fr != NULL; fr = fr->fr_next) { - /* If 'winfixwidth' set keep the window width if - * possible. - * Watch out for this window being the next_curwin. */ + FOR_ALL_FRAMES(fr, topfr->fr_child) { + // If 'winfixwidth' set keep the window width if + // possible. + // Watch out for this window being the next_curwin. if (!frame_fixed_width(fr)) { continue; } @@ -1964,7 +2009,7 @@ static void win_equal_rec( --totwincount; /* don't count curwin */ } - for (fr = topfr->fr_child; fr != NULL; fr = fr->fr_next) { + FOR_ALL_FRAMES(fr, topfr->fr_child) { wincount = 1; if (fr->fr_next == NULL) /* last frame gets all that remains (avoid roundoff error) */ @@ -2039,10 +2084,10 @@ static void win_equal_rec( room = 0; } else { next_curwin_size = -1; - for (fr = topfr->fr_child; fr != NULL; fr = fr->fr_next) { - /* If 'winfixheight' set keep the window height if - * possible. - * Watch out for this window being the next_curwin. */ + FOR_ALL_FRAMES(fr, topfr->fr_child) { + // If 'winfixheight' set keep the window height if + // possible. + // Watch out for this window being the next_curwin. if (!frame_fixed_height(fr)) { continue; } @@ -2085,7 +2130,7 @@ static void win_equal_rec( --totwincount; /* don't count curwin */ } - for (fr = topfr->fr_child; fr != NULL; fr = fr->fr_next) { + FOR_ALL_FRAMES(fr, topfr->fr_child) { wincount = 1; if (fr->fr_next == NULL) /* last frame gets all that remains (avoid roundoff error) */ @@ -2772,8 +2817,9 @@ winframe_remove ( * and remove it. */ frp2->fr_parent->fr_layout = frp2->fr_layout; frp2->fr_parent->fr_child = frp2->fr_child; - for (frp = frp2->fr_child; frp != NULL; frp = frp->fr_next) + FOR_ALL_FRAMES(frp, frp2->fr_child) { frp->fr_parent = frp2->fr_parent; + } frp2->fr_parent->fr_win = frp2->fr_win; if (frp2->fr_win != NULL) frp2->fr_win->w_frame = frp2->fr_parent; @@ -2894,13 +2940,14 @@ static win_T *frame2win(frame_T *frp) /// /// @param frp frame /// @param wp window -static bool frame_has_win(frame_T *frp, win_T *wp) +static bool frame_has_win(const frame_T *frp, const win_T *wp) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1) { if (frp->fr_layout == FR_LEAF) { return frp->fr_win == wp; } - for (frame_T *p = frp->fr_child; p != NULL; p = p->fr_next) { + const frame_T *p; + FOR_ALL_FRAMES(p, frp->fr_child) { if (frame_has_win(p, wp)) { return true; } @@ -2931,8 +2978,8 @@ frame_new_height ( height - topfrp->fr_win->w_status_height); } else if (topfrp->fr_layout == FR_ROW) { do { - /* All frames in this row get the same new height. */ - for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) { + // All frames in this row get the same new height. + FOR_ALL_FRAMES(frp, topfrp->fr_child) { frame_new_height(frp, height, topfirst, wfh); if (frp->fr_height > height) { /* Could not fit the windows, make the whole row higher. */ @@ -3013,7 +3060,7 @@ static bool frame_fixed_height(frame_T *frp) if (frp->fr_layout == FR_ROW) { // The frame is fixed height if one of the frames in the row is fixed // height. - for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) { + FOR_ALL_FRAMES(frp, frp->fr_child) { if (frame_fixed_height(frp)) { return true; } @@ -3023,7 +3070,7 @@ static bool frame_fixed_height(frame_T *frp) // frp->fr_layout == FR_COL: The frame is fixed height if all of the // frames in the row are fixed height. - for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) { + FOR_ALL_FRAMES(frp, frp->fr_child) { if (!frame_fixed_height(frp)) { return false; } @@ -3047,7 +3094,7 @@ static bool frame_fixed_width(frame_T *frp) if (frp->fr_layout == FR_COL) { // The frame is fixed width if one of the frames in the row is fixed // width. - for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) { + FOR_ALL_FRAMES(frp, frp->fr_child) { if (frame_fixed_width(frp)) { return true; } @@ -3057,7 +3104,7 @@ static bool frame_fixed_width(frame_T *frp) // frp->fr_layout == FR_ROW: The frame is fixed width if all of the // frames in the row are fixed width. - for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) { + FOR_ALL_FRAMES(frp, frp->fr_child) { if (!frame_fixed_width(frp)) { return false; } @@ -3081,13 +3128,15 @@ static void frame_add_statusline(frame_T *frp) wp->w_status_height = STATUS_HEIGHT; } } else if (frp->fr_layout == FR_ROW) { - /* Handle all the frames in the row. */ - for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) + // Handle all the frames in the row. + FOR_ALL_FRAMES(frp, frp->fr_child) { frame_add_statusline(frp); - } else { /* frp->fr_layout == FR_COL */ - /* Only need to handle the last frame in the column. */ - for (frp = frp->fr_child; frp->fr_next != NULL; frp = frp->fr_next) - ; + } + } else { + assert(frp->fr_layout == FR_COL); + // Only need to handle the last frame in the column. + for (frp = frp->fr_child; frp->fr_next != NULL; frp = frp->fr_next) { + } frame_add_statusline(frp); } } @@ -3122,8 +3171,8 @@ frame_new_width ( win_new_width(wp, width - wp->w_vsep_width); } else if (topfrp->fr_layout == FR_COL) { do { - /* All frames in this column get the same new width. */ - for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) { + // All frames in this column get the same new width. + FOR_ALL_FRAMES(frp, topfrp->fr_child) { frame_new_width(frp, width, leftfirst, wfw); if (frp->fr_width > width) { /* Could not fit the windows, make whole column wider. */ @@ -3192,7 +3241,8 @@ frame_new_width ( * Add the vertical separator to windows at the right side of "frp". * Note: Does not check if there is room! */ -static void frame_add_vsep(frame_T *frp) +static void frame_add_vsep(const frame_T *frp) + FUNC_ATTR_NONNULL_ARG(1) { win_T *wp; @@ -3204,11 +3254,13 @@ static void frame_add_vsep(frame_T *frp) wp->w_vsep_width = 1; } } else if (frp->fr_layout == FR_COL) { - /* Handle all the frames in the column. */ - for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) + // Handle all the frames in the column. + FOR_ALL_FRAMES(frp, frp->fr_child) { frame_add_vsep(frp); - } else { /* frp->fr_layout == FR_ROW */ - /* Only need to handle the last frame in the row. */ + } + } else { + assert(frp->fr_layout == FR_ROW); + // Only need to handle the last frame in the row. frp = frp->fr_child; while (frp->fr_next != NULL) frp = frp->fr_next; @@ -3258,7 +3310,7 @@ static int frame_minheight(frame_T *topfrp, win_T *next_curwin) } else if (topfrp->fr_layout == FR_ROW) { /* get the minimal height from each frame in this row */ m = 0; - for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) { + FOR_ALL_FRAMES(frp, topfrp->fr_child) { n = frame_minheight(frp, next_curwin); if (n > m) m = n; @@ -3266,8 +3318,9 @@ static int frame_minheight(frame_T *topfrp, win_T *next_curwin) } else { /* Add up the minimal heights for all frames in this column. */ m = 0; - for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) + FOR_ALL_FRAMES(frp, topfrp->fr_child) { m += frame_minheight(frp, next_curwin); + } } return m; @@ -3301,7 +3354,7 @@ frame_minwidth ( } else if (topfrp->fr_layout == FR_COL) { /* get the minimal width from each frame in this column */ m = 0; - for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) { + FOR_ALL_FRAMES(frp, topfrp->fr_child) { n = frame_minwidth(frp, next_curwin); if (n > m) m = n; @@ -3309,8 +3362,9 @@ frame_minwidth ( } else { /* Add up the minimal widths for all frames in this row. */ m = 0; - for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) + FOR_ALL_FRAMES(frp, topfrp->fr_child) { m += frame_minwidth(frp, next_curwin); + } } return m; @@ -4787,11 +4841,12 @@ static void frame_comp_pos(frame_T *topfrp, int *row, int *col) } else { startrow = *row; startcol = *col; - for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) { - if (topfrp->fr_layout == FR_ROW) - *row = startrow; /* all frames are at the same row */ - else - *col = startcol; /* all frames are at the same col */ + FOR_ALL_FRAMES(frp, topfrp->fr_child) { + if (topfrp->fr_layout == FR_ROW) { + *row = startrow; // all frames are at the same row + } else { + *col = startcol; // all frames are at the same col + } frame_comp_pos(frp, row, col); } } @@ -4823,13 +4878,9 @@ void win_setheight_win(int height, win_T *win) } if (win->w_floating) { - if (win->w_float_config.external) { - win->w_float_config.height = height; - win_config_float(win, win->w_float_config); - } else { - beep_flush(); - return; - } + win->w_float_config.height = height; + win_config_float(win, win->w_float_config); + redraw_win_later(win, NOT_VALID); } else { frame_setheight(win->w_frame, height + win->w_status_height); @@ -4844,9 +4895,9 @@ void win_setheight_win(int height, win_T *win) cmdline_row = row; msg_row = row; msg_col = 0; + redraw_all_later(NOT_VALID); } - redraw_all_later(NOT_VALID); } @@ -4904,15 +4955,16 @@ static void frame_setheight(frame_T *curfrp, int height) for (run = 1; run <= 2; ++run) { room = 0; room_reserved = 0; - for (frp = curfrp->fr_parent->fr_child; frp != NULL; - frp = frp->fr_next) { + FOR_ALL_FRAMES(frp, curfrp->fr_parent->fr_child) { if (frp != curfrp && frp->fr_win != NULL - && frp->fr_win->w_p_wfh) + && frp->fr_win->w_p_wfh) { room_reserved += frp->fr_height; + } room += frp->fr_height; - if (frp != curfrp) + if (frp != curfrp) { room -= frame_minheight(frp, NULL); + } } if (curfrp->fr_width != Columns) { room_cmdline = 0; @@ -5029,21 +5081,17 @@ void win_setwidth_win(int width, win_T *wp) width = 1; } if (wp->w_floating) { - if (wp->w_float_config.external) { - wp->w_float_config.width = width; - win_config_float(wp, wp->w_float_config); - } else { - beep_flush(); - return; - } + wp->w_float_config.width = width; + win_config_float(wp, wp->w_float_config); + redraw_win_later(wp, NOT_VALID); } else { frame_setwidth(wp->w_frame, width + wp->w_vsep_width); // recompute the window positions (void)win_comp_pos(); + redraw_all_later(NOT_VALID); } - redraw_all_later(NOT_VALID); } /* @@ -5089,15 +5137,16 @@ static void frame_setwidth(frame_T *curfrp, int width) for (run = 1; run <= 2; ++run) { room = 0; room_reserved = 0; - for (frp = curfrp->fr_parent->fr_child; frp != NULL; - frp = frp->fr_next) { + FOR_ALL_FRAMES(frp, curfrp->fr_parent->fr_child) { if (frp != curfrp && frp->fr_win != NULL - && frp->fr_win->w_p_wfw) + && frp->fr_win->w_p_wfw) { room_reserved += frp->fr_width; + } room += frp->fr_width; - if (frp != curfrp) + if (frp != curfrp) { room -= frame_minwidth(frp, NULL); + } } if (width <= room) @@ -5261,10 +5310,11 @@ void win_drag_status_line(win_T *dragwin, int offset) room -= p_ch; if (room < 0) room = 0; - /* sum up the room of frames below of the current one */ - for (fr = curfr->fr_next; fr != NULL; fr = fr->fr_next) + // sum up the room of frames below of the current one + FOR_ALL_FRAMES(fr, curfr->fr_next) { room += fr->fr_height - frame_minheight(fr, NULL); - fr = curfr; /* put fr at window that grows */ + } + fr = curfr; // put fr at window that grows } if (room < offset) /* Not enough room */ @@ -5364,9 +5414,10 @@ void win_drag_vsep_line(win_T *dragwin, int offset) left = FALSE; /* sum up the room of frames right of the current one */ room = 0; - for (fr = curfr->fr_next; fr != NULL; fr = fr->fr_next) + FOR_ALL_FRAMES(fr, curfr->fr_next) { room += fr->fr_width - frame_minwidth(fr, NULL); - fr = curfr; /* put fr at window that grows */ + } + fr = curfr; // put fr at window that grows } assert(fr); @@ -5898,9 +5949,10 @@ static void last_status_rec(frame_T *fr, int statusline) redraw_all_later(SOME_VALID); } } else if (fr->fr_layout == FR_ROW) { - /* vertically split windows, set status line for each one */ - for (fp = fr->fr_child; fp != NULL; fp = fp->fr_next) + // vertically split windows, set status line for each one + FOR_ALL_FRAMES(fp, fr->fr_child) { last_status_rec(fp, statusline); + } } else { /* horizontally split window, set status line for last one */ for (fp = fr->fr_child; fp->fr_next != NULL; fp = fp->fr_next) @@ -6516,14 +6568,15 @@ matchitem_T *get_match(win_T *wp, int id) /// /// @param topfrp top frame pointer /// @param height expected height -static bool frame_check_height(frame_T *topfrp, int height) +static bool frame_check_height(const frame_T *topfrp, int height) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { if (topfrp->fr_height != height) { return false; } if (topfrp->fr_layout == FR_ROW) { - for (frame_T *frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) { + const frame_T *frp; + FOR_ALL_FRAMES(frp, topfrp->fr_child) { if (frp->fr_height != height) { return false; } @@ -6536,14 +6589,15 @@ static bool frame_check_height(frame_T *topfrp, int height) /// /// @param topfrp top frame pointer /// @param width expected width -static bool frame_check_width(frame_T *topfrp, int width) +static bool frame_check_width(const frame_T *topfrp, int width) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { if (topfrp->fr_width != width) { return false; } if (topfrp->fr_layout == FR_COL) { - for (frame_T *frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) { + const frame_T *frp; + FOR_ALL_FRAMES(frp, topfrp->fr_child) { if (frp->fr_width != width) { return false; } |