diff options
Diffstat (limited to 'src')
37 files changed, 1077 insertions, 477 deletions
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index ea5125e4e7..ef9e11fbef 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -76,7 +76,6 @@ set(CONV_SOURCES diff.c edit.c eval.c - ex_cmds2.c ex_cmds.c ex_docmd.c ex_getln.c diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index b7a86af134..fa4b8e5f7d 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -19,6 +19,7 @@ #include "nvim/mark.h" #include "nvim/fileio.h" #include "nvim/move.h" +#include "nvim/syntax.h" #include "nvim/window.h" #include "nvim/undo.h" @@ -514,6 +515,99 @@ ArrayOf(Integer, 2) buffer_get_mark(Buffer buffer, String name, Error *err) return rv; } +/// Adds a highlight to buffer. +/// +/// This can be used for plugins which dynamically generate highlights to a +/// buffer (like a semantic highlighter or linter). The function adds a single +/// highlight to a buffer. Unlike matchaddpos() highlights follow changes to +/// line numbering (as lines are inserted/removed above the highlighted line), +/// like signs and marks do. +/// +/// "src_id" is useful for batch deletion/updating of a set of highlights. When +/// called with src_id = 0, an unique source id is generated and returned. +/// Succesive calls can pass in it as "src_id" to add new highlights to the same +/// source group. All highlights in the same group can then be cleared with +/// buffer_clear_highlight. If the highlight never will be manually deleted +/// pass in -1 for "src_id". +/// +/// If "hl_group" is the empty string no highlight is added, but a new src_id +/// is still returned. This is useful for an external plugin to synchrounously +/// request an unique src_id at initialization, and later asynchronously add and +/// clear highlights in response to buffer changes. +/// +/// @param buffer The buffer handle +/// @param src_id Source group to use or 0 to use a new group, +/// or -1 for ungrouped highlight +/// @param hl_group Name of the highlight group to use +/// @param line The line to highlight +/// @param col_start Start of range of columns to highlight +/// @param col_end End of range of columns to highlight, +/// or -1 to highlight to end of line +/// @param[out] err Details of an error that may have occurred +/// @return The src_id that was used +Integer buffer_add_highlight(Buffer buffer, + Integer src_id, + String hl_group, + Integer line, + Integer col_start, + Integer col_end, + Error *err) +{ + buf_T *buf = find_buffer_by_handle(buffer, err); + if (!buf) { + return 0; + } + + if (line < 0 || line >= MAXLNUM) { + api_set_error(err, Validation, _("Line number outside range")); + return 0; + } + if (col_start < 0 || col_start > MAXCOL) { + api_set_error(err, Validation, _("Column value outside range")); + return 0; + } + if (col_end < 0 || col_end > MAXCOL) { + col_end = MAXCOL; + } + + int hlg_id = syn_name2id((char_u*)hl_group.data); + src_id = bufhl_add_hl(buf, (int)src_id, hlg_id, (linenr_T)line+1, + (colnr_T)col_start+1, (colnr_T)col_end); + return src_id; +} + +/// Clears highlights from a given source group and a range of lines +/// +/// To clear a source group in the entire buffer, pass in 1 and -1 to +/// line_start and line_end respectively. +/// +/// @param buffer The buffer handle +/// @param src_id Highlight source group to clear, or -1 to clear all groups. +/// @param line_start Start of range of lines to clear +/// @param line_end End of range of lines to clear (exclusive) +/// or -1 to clear to end of file. +/// @param[out] err Details of an error that may have occurred +void buffer_clear_highlight(Buffer buffer, + Integer src_id, + Integer line_start, + Integer line_end, + Error *err) +{ + buf_T *buf = find_buffer_by_handle(buffer, err); + if (!buf) { + return; + } + + if (line_start < 0 || line_start >= MAXLNUM) { + api_set_error(err, Validation, _("Line number outside range")); + return; + } + if (line_end < 0 || line_end > MAXLNUM) { + line_end = MAXLNUM; + } + + bufhl_clear_line_range(buf, (int)src_id, (int)line_start+1, (int)line_end); +} // Check if deleting lines made the cursor position invalid. // Changed the lines from "lo" to "hi" and added "extra" lines (negative if diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua index aa4a8d8332..8d891effae 100644 --- a/src/nvim/auevents.lua +++ b/src/nvim/auevents.lua @@ -83,6 +83,7 @@ return { 'TermResponse', -- after setting "v:termresponse" 'TextChanged', -- text was modified 'TextChangedI', -- text was modified in Insert mode + 'TextYankPost', -- after a yank or delete was done (y, d, c) 'User', -- user defined autocommand 'VimEnter', -- after starting Vim 'VimLeave', -- before exiting Vim diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index c05090bbf6..62ab7495da 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -580,16 +580,17 @@ free_buffer_stuff ( ) { if (free_options) { - clear_wininfo(buf); /* including window-local options */ - free_buf_options(buf, TRUE); + clear_wininfo(buf); // including window-local options + free_buf_options(buf, true); ga_clear(&buf->b_s.b_langp); } - vars_clear(&buf->b_vars->dv_hashtab); /* free all internal variables */ + vars_clear(&buf->b_vars->dv_hashtab); // free all internal variables hash_init(&buf->b_vars->dv_hashtab); - uc_clear(&buf->b_ucmds); /* clear local user commands */ - buf_delete_signs(buf); /* delete any signs */ - map_clear_int(buf, MAP_ALL_MODES, TRUE, FALSE); /* clear local mappings */ - map_clear_int(buf, MAP_ALL_MODES, TRUE, TRUE); /* clear local abbrevs */ + uc_clear(&buf->b_ucmds); // clear local user commands + buf_delete_signs(buf); // delete any signs + bufhl_clear_all(buf); // delete any highligts + map_clear_int(buf, MAP_ALL_MODES, true, false); // clear local mappings + map_clear_int(buf, MAP_ALL_MODES, true, true); // clear local abbrevs xfree(buf->b_start_fenc); buf->b_start_fenc = NULL; } @@ -4870,6 +4871,224 @@ void sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_a } } +// bufhl: plugin highlights associated with a buffer + +/// Adds a highlight to buffer. +/// +/// Unlike matchaddpos() highlights follow changes to line numbering (as lines +/// are inserted/removed above the highlighted line), like signs and marks do. +/// +/// When called with "src_id" set to 0, a unique source id is generated and +/// returned. Succesive calls can pass it in as "src_id" to add new highlights +/// to the same source group. All highlights in the same group can be cleared +/// at once. If the highlight never will be manually deleted pass in -1 for +/// "src_id" +/// +/// if "hl_id" or "lnum" is invalid no highlight is added, but a new src_id +/// is still returned. +/// +/// @param buf The buffer to add highlights to +/// @param src_id src_id to use or 0 to use a new src_id group, +/// or -1 for ungrouped highlight. +/// @param hl_id Id of the highlight group to use +/// @param lnum The line to highlight +/// @param col_start First column to highlight +/// @param col_end The last column to highlight, +/// or -1 to highlight to end of line +/// @return The src_id that was used +int bufhl_add_hl(buf_T *buf, + int src_id, + int hl_id, + linenr_T lnum, + colnr_T col_start, + colnr_T col_end) { + static int next_src_id = 1; + if (src_id == 0) { + src_id = next_src_id++; + } + if (hl_id <= 0) { + // no highlight group or invalid line, just return src_id + return src_id; + } + if (!buf->b_bufhl_info) { + buf->b_bufhl_info = map_new(linenr_T, bufhl_vec_T)(); + } + bufhl_vec_T* lineinfo = map_ref(linenr_T, bufhl_vec_T)(buf->b_bufhl_info, + lnum, true); + + bufhl_hl_item_T *hlentry = kv_pushp(bufhl_hl_item_T, *lineinfo); + hlentry->src_id = src_id; + hlentry->hl_id = hl_id; + hlentry->start = col_start; + hlentry->stop = col_end; + + if (0 < lnum && lnum <= buf->b_ml.ml_line_count) { + changed_lines_buf(buf, lnum, lnum+1, 0); + redraw_buf_later(buf, VALID); + } + return src_id; +} + +/// Clear bufhl highlights from a given source group and range of lines. +/// +/// @param buf The buffer to remove highlights from +/// @param src_id highlight source group to clear, or -1 to clear all groups. +/// @param line_start first line to clear +/// @param line_end last line to clear or MAXLNUM to clear to end of file. +void bufhl_clear_line_range(buf_T *buf, + int src_id, + linenr_T line_start, + linenr_T line_end) { + if (!buf->b_bufhl_info) { + return; + } + linenr_T line; + linenr_T first_changed = MAXLNUM, last_changed = -1; + // In the case line_start - line_end << bufhl_info->size + // it might be better to reverse this, i e loop over the lines + // to clear on. + bufhl_vec_T unused; + map_foreach(buf->b_bufhl_info, line, unused, { + (void)unused; + if (line_start <= line && line <= line_end) { + if (bufhl_clear_line(buf->b_bufhl_info, src_id, line)) { + if (line > last_changed) { + last_changed = line; + } + if (line < first_changed) { + first_changed = line; + } + } + } + }) + + if (last_changed != -1) { + changed_lines_buf(buf, first_changed, last_changed+1, 0); + redraw_buf_later(buf, VALID); + } +} + +/// Clear bufhl highlights from a given source group and given line +/// +/// @param bufhl_info The highlight info for the buffer +/// @param src_id Highlight source group to clear, or -1 to clear all groups. +/// @param lnum Linenr where the highlight should be cleared +static bool bufhl_clear_line(bufhl_info_T *bufhl_info, int src_id, int lnum) { + bufhl_vec_T* lineinfo = map_ref(linenr_T, bufhl_vec_T)(bufhl_info, + lnum, false); + size_t oldsize = kv_size(*lineinfo); + if (src_id < 0) { + kv_size(*lineinfo) = 0; + } else { + size_t newind = 0; + for (size_t i = 0; i < kv_size(*lineinfo); i++) { + if (kv_A(*lineinfo, i).src_id != src_id) { + if (i != newind) { + kv_A(*lineinfo, newind) = kv_A(*lineinfo, i); + } + newind++; + } + } + kv_size(*lineinfo) = newind; + } + + if (kv_size(*lineinfo) == 0) { + kv_destroy(*lineinfo); + map_del(linenr_T, bufhl_vec_T)(bufhl_info, lnum); + } + return kv_size(*lineinfo) != oldsize; +} + +/// Remove all highlights and free the highlight data +void bufhl_clear_all(buf_T* buf) { + if (!buf->b_bufhl_info) { + return; + } + bufhl_clear_line_range(buf, -1, 1, MAXLNUM); + map_free(linenr_T, bufhl_vec_T)(buf->b_bufhl_info); + buf->b_bufhl_info = NULL; +} + +/// Adjust a placed highlight for inserted/deleted lines. +void bufhl_mark_adjust(buf_T* buf, + linenr_T line1, + linenr_T line2, + long amount, + long amount_after) { + if (!buf->b_bufhl_info) { + return; + } + + bufhl_info_T *newmap = map_new(linenr_T, bufhl_vec_T)(); + linenr_T line; + bufhl_vec_T lineinfo; + map_foreach(buf->b_bufhl_info, line, lineinfo, { + if (line >= line1 && line <= line2) { + if (amount == MAXLNUM) { + bufhl_clear_line(buf->b_bufhl_info, -1, line); + continue; + } else { + line += amount; + } + } else if (line > line2) { + line += amount_after; + } + map_put(linenr_T, bufhl_vec_T)(newmap, line, lineinfo); + }); + map_free(linenr_T, bufhl_vec_T)(buf->b_bufhl_info); + buf->b_bufhl_info = newmap; +} + + +/// Get highlights to display at a specific line +/// +/// @param buf The buffer handle +/// @param lnum The line number +/// @param[out] info The highligts for the line +/// @return true if there was highlights to display +bool bufhl_start_line(buf_T *buf, linenr_T lnum, bufhl_lineinfo_T *info) { + if (!buf->b_bufhl_info) { + return false; + } + + info->valid_to = -1; + info->entries = map_get(linenr_T, bufhl_vec_T)(buf->b_bufhl_info, lnum); + return kv_size(info->entries) > 0; +} + +/// get highlighting at column col +/// +/// It is is assumed this will be called with +/// non-decreasing column nrs, so that it is +/// possible to only recalculate highlights +/// at endpoints. +/// +/// @param info The info returned by bufhl_start_line +/// @param col The column to get the attr for +/// @return The highilight attr to display at the column +int bufhl_get_attr(bufhl_lineinfo_T *info, colnr_T col) { + if (col <= info->valid_to) { + return info->current; + } + int attr = 0; + info->valid_to = MAXCOL; + for (size_t i = 0; i < kv_size(info->entries); i++) { + bufhl_hl_item_T entry = kv_A(info->entries, i); + if (entry.start <= col && col <= entry.stop) { + int entry_attr = syn_id2attr(entry.hl_id); + attr = hl_combine_attr(attr, entry_attr); + if (entry.stop < info->valid_to) { + info->valid_to = entry.stop; + } + } else if (col < entry.start && entry.start-1 < info->valid_to) { + info->valid_to = entry.start-1; + } + } + info->current = attr; + return attr; +} + + /* * Set 'buflisted' for curbuf to "on" and trigger autocommands if it changed. */ diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index bdea609820..936a14b903 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -28,6 +28,8 @@ typedef struct file_buffer buf_T; // Forward declaration #include "nvim/profile.h" // for String #include "nvim/api/private/defs.h" +// for Map(K, V) +#include "nvim/map.h" #define MODIFIABLE(buf) (!buf->terminal && buf->b_p_ma) @@ -59,21 +61,21 @@ typedef struct file_buffer buf_T; // Forward declaration #define VALID_BOTLINE_AP 0x40 /* w_botine is approximated */ #define VALID_TOPLINE 0x80 /* w_topline is valid (for cursor position) */ -/* flags for b_flags */ -#define BF_RECOVERED 0x01 /* buffer has been recovered */ -#define BF_CHECK_RO 0x02 /* need to check readonly when loading file - into buffer (set by ":e", may be reset by - ":buf" */ -#define BF_NEVERLOADED 0x04 /* file has never been loaded into buffer, - many variables still need to be set */ -#define BF_NOTEDITED 0x08 /* Set when file name is changed after - starting to edit, reset when file is - written out. */ -#define BF_NEW 0x10 /* file didn't exist when editing started */ -#define BF_NEW_W 0x20 /* Warned for BF_NEW and file created */ -#define BF_READERR 0x40 /* got errors while reading the file */ -#define BF_DUMMY 0x80 /* dummy buffer, only used internally */ -#define BF_PRESERVED 0x100 /* ":preserve" was used */ +// flags for b_flags +#define BF_RECOVERED 0x01 // buffer has been recovered +#define BF_CHECK_RO 0x02 // need to check readonly when loading file + // into buffer (set by ":e", may be reset by + // ":buf") +#define BF_NEVERLOADED 0x04 // file has never been loaded into buffer, + // many variables still need to be set +#define BF_NOTEDITED 0x08 // Set when file name is changed after + // starting to edit, reset when file is + // written out. +#define BF_NEW 0x10 // file didn't exist when editing started +#define BF_NEW_W 0x20 // Warned for BF_NEW and file created +#define BF_READERR 0x40 // got errors while reading the file +#define BF_DUMMY 0x80 // dummy buffer, only used internally +#define BF_PRESERVED 0x100 // ":preserve" was used /* Mask to check for flags that prevent normal writing */ #define BF_WRITE_MASK (BF_NOTEDITED + BF_NEW + BF_READERR) @@ -101,6 +103,11 @@ typedef int scid_T; /* script ID */ // for signlist_T #include "nvim/sign_defs.h" +// for bufhl_*_T +#include "nvim/bufhl_defs.h" + +typedef Map(linenr_T, bufhl_vec_T) bufhl_info_T; + // for FileID #include "nvim/os/fs_defs.h" @@ -754,6 +761,8 @@ struct file_buffer { dict_T *additional_data; // Additional data from shada file if any. int b_mapped_ctrl_c; // modes where CTRL-C is mapped + + bufhl_info_T *b_bufhl_info; // buffer stored highlights }; /* diff --git a/src/nvim/bufhl_defs.h b/src/nvim/bufhl_defs.h new file mode 100644 index 0000000000..e47bb2a238 --- /dev/null +++ b/src/nvim/bufhl_defs.h @@ -0,0 +1,25 @@ +#ifndef NVIM_BUFHL_DEFS_H +#define NVIM_BUFHL_DEFS_H + +#include "nvim/pos.h" +#include "nvim/lib/kvec.h" +// bufhl: buffer specific highlighting + +struct bufhl_hl_item +{ + int src_id; + int hl_id; // highlight group + colnr_T start; // first column to highlight + colnr_T stop; // last column to highlight +}; +typedef struct bufhl_hl_item bufhl_hl_item_T; + +typedef kvec_t(struct bufhl_hl_item) bufhl_vec_T; + +typedef struct { + bufhl_vec_T entries; + int current; + colnr_T valid_to; +} bufhl_lineinfo_T; + +#endif // NVIM_BUFHL_DEFS_H diff --git a/src/nvim/edit.c b/src/nvim/edit.c index a983fa5772..6313ea2e81 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -1247,7 +1247,7 @@ normalchar: static void insert_do_complete(InsertState *s) { compl_busy = true; - if (ins_complete(s->c) == FAIL) { + if (ins_complete(s->c, true) == FAIL) { compl_cont_status = 0; } compl_busy = false; @@ -2321,9 +2321,10 @@ static int ins_compl_make_cyclic(void) */ void set_completion(colnr_T startcol, list_T *list) { - /* If already doing completions stop it. */ - if (ctrl_x_mode != 0) + // If already doing completions stop it. + if (ctrl_x_mode != 0) { ins_compl_prep(' '); + } ins_compl_clear(); if (stop_arrow() == FAIL) @@ -2349,16 +2350,23 @@ void set_completion(colnr_T startcol, list_T *list) compl_started = TRUE; compl_used_match = TRUE; compl_cont_status = 0; + int save_w_wrow = curwin->w_wrow; compl_curr_match = compl_first_match; if (compl_no_insert) { - ins_complete(K_DOWN); + ins_complete(K_DOWN, false); } else { - ins_complete(Ctrl_N); + ins_complete(Ctrl_N, false); if (compl_no_select) { - ins_complete(Ctrl_P); + ins_complete(Ctrl_P, false); } } + + // Lazily show the popup menu, unless we got interrupted. + if (!compl_interrupted) { + show_pum(save_w_wrow); + } + ui_flush(); } @@ -2935,18 +2943,17 @@ static void ins_compl_new_leader(void) ins_bytes(compl_leader + ins_compl_len()); compl_used_match = FALSE; - if (compl_started) + if (compl_started) { ins_compl_set_original_text(compl_leader); - else { - spell_bad_len = 0; /* need to redetect bad word */ - /* - * Matches were cleared, need to search for them now. - * Set "compl_restarting" to avoid that the first match is inserted. - */ - compl_restarting = TRUE; - if (ins_complete(Ctrl_N) == FAIL) + } else { + spell_bad_len = 0; // need to redetect bad word + // Matches were cleared, need to search for them now. + // Set "compl_restarting" to avoid that the first match is inserted. + compl_restarting = true; + if (ins_complete(Ctrl_N, true) == FAIL) { compl_cont_status = 0; - compl_restarting = FALSE; + } + compl_restarting = false; } compl_enter_selects = !compl_used_match; @@ -4298,7 +4305,7 @@ static int ins_compl_use_match(int c) * Called when character "c" was typed, which has a meaning for completion. * Returns OK if completion was done, FAIL if something failed. */ -static int ins_complete(int c) +static int ins_complete(int c, bool enable_pum) { char_u *line; int startcol = 0; /* column where searched text starts */ @@ -4781,20 +4788,9 @@ static int ins_complete(int c) } } - /* Show the popup menu, unless we got interrupted. */ - if (!compl_interrupted) { - /* RedrawingDisabled may be set when invoked through complete(). */ - n = RedrawingDisabled; - RedrawingDisabled = 0; - - /* If the cursor moved we need to remove the pum first. */ - setcursor(); - if (save_w_wrow != curwin->w_wrow) - ins_compl_del_pum(); - - ins_compl_show_pum(); - setcursor(); - RedrawingDisabled = n; + // Show the popup menu, unless we got interrupted. + if (enable_pum && !compl_interrupted) { + show_pum(save_w_wrow); } compl_was_interrupted = compl_interrupted; compl_interrupted = FALSE; @@ -8520,3 +8516,20 @@ static char_u *do_insert_char_pre(int c) return res; } + +static void show_pum(int save_w_wrow) +{ + // RedrawingDisabled may be set when invoked through complete(). + int n = RedrawingDisabled; + RedrawingDisabled = 0; + + // If the cursor moved we need to remove the pum first. + setcursor(); + if (save_w_wrow != curwin->w_wrow) { + ins_compl_del_pum(); + } + + ins_compl_show_pum(); + setcursor(); + RedrawingDisabled = n; +} diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 96fc79504f..0e5da13242 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -378,6 +378,7 @@ static struct vimvar { { VV_NAME("option_type", VAR_STRING), VV_RO }, { VV_NAME("errors", VAR_LIST), 0 }, { VV_NAME("msgpack_types", VAR_DICT), VV_RO }, + { VV_NAME("event", VAR_DICT), VV_RO }, }; /* shorthand */ @@ -545,6 +546,10 @@ void eval_init(void) set_vim_var_dict(VV_MSGPACK_TYPES, msgpack_types_dict); set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc()); + + dict_T *v_event = dict_alloc(); + v_event->dv_lock = VAR_FIXED; + set_vim_var_dict(VV_EVENT, v_event); set_vim_var_list(VV_ERRORS, list_alloc()); set_vim_var_nr(VV_SEARCHFORWARD, 1L); set_vim_var_nr(VV_HLSEARCH, 1L); @@ -4010,12 +4015,22 @@ eval6 ( * When either side is a float the result is a float. */ if (use_float) { - if (op == '*') + if (op == '*') { f1 = f1 * f2; - else if (op == '/') { - /* We rely on the floating point library to handle divide - * by zero to result in "inf" and not a crash. */ - f1 = f2 != 0 ? f1 / f2 : INFINITY; + } else if (op == '/') { + // Division by zero triggers error from AddressSanitizer + f1 = (f2 == 0 + ? ( +#ifdef NAN + f1 == 0 + ? NAN + : +#endif + (f1 > 0 + ? INFINITY + : -INFINITY) + ) + : f1 / f2); } else { EMSG(_("E804: Cannot use '%' with Float")); return FAIL; @@ -6007,6 +6022,27 @@ static void rettv_dict_alloc(typval_T *rettv) ++d->dv_refcount; } +/// Clear all the keys of a Dictionary. "d" remains a valid empty Dictionary. +/// +/// @param d The Dictionary to clear +void dict_clear(dict_T *d) + FUNC_ATTR_NONNULL_ALL +{ + hash_lock(&d->dv_hashtab); + assert(d->dv_hashtab.ht_locked > 0); + + size_t todo = d->dv_hashtab.ht_used; + for (hashitem_T *hi = d->dv_hashtab.ht_array; todo > 0; hi++) { + if (!HASHITEM_EMPTY(hi)) { + dictitem_free(HI2DI(hi)); + hash_remove(&d->dv_hashtab, hi); + todo--; + } + } + + hash_unlock(&d->dv_hashtab); +} + /* * Unreference a Dictionary: decrement the reference count and free it when it @@ -6248,6 +6284,24 @@ int dict_add_list(dict_T *d, char *key, list_T *list) return OK; } +/// Set all existing keys in "dict" as read-only. +/// +/// This does not protect against adding new keys to the Dictionary. +/// +/// @param dict The dict whose keys should be frozen +void dict_set_keys_readonly(dict_T *dict) + FUNC_ATTR_NONNULL_ALL +{ + size_t todo = dict->dv_hashtab.ht_used; + for (hashitem_T *hi = dict->dv_hashtab.ht_array; todo > 0 ; hi++) { + if (HASHITEM_EMPTY(hi)) { + continue; + } + todo--; + HI2DI(hi)->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX; + } +} + /* * Get the number of items in a Dictionary. */ @@ -6848,9 +6902,25 @@ vim_to_msgpack_error_ret: \ #define CONV_FLOAT(flt) \ do { \ - char numbuf[NUMBUFLEN]; \ - vim_snprintf(numbuf, NUMBUFLEN - 1, "%g", (flt)); \ - ga_concat(gap, (char_u *) numbuf); \ + const float_T flt_ = (flt); \ + switch (fpclassify(flt_)) { \ + case FP_NAN: { \ + ga_concat(gap, (char_u *) "str2float('nan')"); \ + break; \ + } \ + case FP_INFINITE: { \ + if (flt_ < 0) { \ + ga_append(gap, '-'); \ + } \ + ga_concat(gap, (char_u *) "str2float('inf')"); \ + break; \ + } \ + default: { \ + char numbuf[NUMBUFLEN]; \ + vim_snprintf(numbuf, NUMBUFLEN - 1, "%g", flt_); \ + ga_concat(gap, (char_u *) numbuf); \ + } \ + } \ } while (0) #define CONV_FUNC(fun) \ @@ -7290,7 +7360,7 @@ static struct fst { { "pathshorten", 1, 1, f_pathshorten }, { "pow", 2, 2, f_pow }, { "prevnonblank", 1, 1, f_prevnonblank }, - { "printf", 2, 19, f_printf }, + { "printf", 2, MAX_FUNC_ARGS, f_printf }, { "pumvisible", 0, 0, f_pumvisible }, { "py3eval", 1, 1, f_py3eval }, { "pyeval", 1, 1, f_pyeval }, @@ -7304,8 +7374,8 @@ static struct fst { { "resolve", 1, 1, f_resolve }, { "reverse", 1, 1, f_reverse }, { "round", 1, 1, f_round }, - { "rpcnotify", 2, 64, f_rpcnotify }, - { "rpcrequest", 2, 64, f_rpcrequest }, + { "rpcnotify", 2, MAX_FUNC_ARGS, f_rpcnotify }, + { "rpcrequest", 2, MAX_FUNC_ARGS, f_rpcrequest }, { "rpcstart", 1, 2, f_rpcstart }, { "rpcstop", 1, 1, f_rpcstop }, { "screenattr", 2, 2, f_screenattr }, @@ -7324,10 +7394,10 @@ static struct fst { { "setcharsearch", 1, 1, f_setcharsearch }, { "setcmdpos", 1, 1, f_setcmdpos }, { "setline", 2, 2, f_setline }, - { "setloclist", 2, 3, f_setloclist }, + { "setloclist", 2, 4, f_setloclist }, { "setmatches", 1, 1, f_setmatches }, { "setpos", 2, 2, f_setpos }, - { "setqflist", 1, 2, f_setqflist }, + { "setqflist", 1, 3, f_setqflist }, { "setreg", 2, 3, f_setreg }, { "settabvar", 3, 3, f_settabvar }, { "settabwinvar", 4, 4, f_settabwinvar }, @@ -10553,8 +10623,6 @@ static void f_getregtype(typval_T *argvars, typval_T *rettv) { char_u *strregname; int regname; - char_u buf[NUMBUFLEN + 2]; - long reglen = 0; if (argvars[0].v_type != VAR_UNKNOWN) { strregname = get_tv_string_chk(&argvars[0]); @@ -10571,18 +10639,13 @@ static void f_getregtype(typval_T *argvars, typval_T *rettv) if (regname == 0) regname = '"'; - buf[0] = NUL; - buf[1] = NUL; - switch (get_reg_type(regname, ®len)) { - case MLINE: buf[0] = 'V'; break; - case MCHAR: buf[0] = 'v'; break; - case MBLOCK: - buf[0] = Ctrl_V; - sprintf((char *)buf + 1, "%" PRId64, (int64_t)(reglen + 1)); - break; - } + colnr_T reglen = 0; + char buf[NUMBUFLEN + 2]; + char_u reg_type = get_reg_type(regname, ®len); + format_reg_type(reg_type, reglen, buf, ARRAY_SIZE(buf)); + rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_strsave(buf); + rettv->vval.v_string = (char_u *)xstrdup(buf); } /* @@ -15215,14 +15278,26 @@ static void f_setline(typval_T *argvars, typval_T *rettv) appended_lines_mark(lcount, added); } - -/* - * Used by "setqflist()" and "setloclist()" functions - */ -static void set_qf_ll_list(win_T *wp, typval_T *list_arg, typval_T *action_arg, typval_T *rettv) +/// Create quickfix/location list from VimL values +/// +/// Used by `setqflist()` and `setloclist()` functions. Accepts invalid +/// list_arg, action_arg and title_arg arguments in which case errors out, +/// including VAR_UNKNOWN parameters. +/// +/// @param[in,out] wp Window to create location list for. May be NULL in +/// which case quickfix list will be created. +/// @param[in] list_arg Quickfix list contents. +/// @param[in] action_arg Action to perform: append to an existing list, +/// replace its content or create a new one. +/// @param[in] title_arg New list title. Defaults to caller function name. +/// @param[out] rettv Return value: 0 in case of success, -1 otherwise. +static void set_qf_ll_list(win_T *wp, typval_T *list_arg, typval_T *action_arg, + typval_T *title_arg, typval_T *rettv) + FUNC_ATTR_NONNULL_ARG(2, 3, 4, 5) { char_u *act; int action = ' '; + char_u *title = NULL; rettv->vval.v_number = -1; @@ -15231,7 +15306,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *list_arg, typval_T *action_arg, else { list_T *l = list_arg->vval.v_list; - if (action_arg->v_type == VAR_STRING) { + if (action_arg->v_type != VAR_UNKNOWN) { act = get_tv_string_chk(action_arg); if (act == NULL) return; /* type error; errmsg already given */ @@ -15239,9 +15314,20 @@ static void set_qf_ll_list(win_T *wp, typval_T *list_arg, typval_T *action_arg, action = *act; } - if (l != NULL && set_errorlist(wp, l, action, - (char_u *)(wp == NULL ? "setqflist()" : "setloclist()")) == OK) + if (title_arg->v_type == VAR_STRING) { + title = get_tv_string_chk(title_arg); + if (!title) { + return; // type error; errmsg already given + } + } + + if (!title) { + title = (char_u*)(wp ? "setloclist()" : "setqflist()"); + } + + if (l && set_errorlist(wp, l, action, title) == OK) { rettv->vval.v_number = 0; + } } } @@ -15255,8 +15341,9 @@ static void f_setloclist(typval_T *argvars, typval_T *rettv) rettv->vval.v_number = -1; win = find_win_by_nr(&argvars[0], NULL); - if (win != NULL) - set_qf_ll_list(win, &argvars[1], &argvars[2], rettv); + if (win != NULL) { + set_qf_ll_list(win, &argvars[1], &argvars[2], &argvars[3], rettv); + } } /* @@ -15392,7 +15479,7 @@ static void f_setpos(typval_T *argvars, typval_T *rettv) */ static void f_setqflist(typval_T *argvars, typval_T *rettv) { - set_qf_ll_list(NULL, &argvars[0], &argvars[1], rettv); + set_qf_ll_list(NULL, &argvars[0], &argvars[1], &argvars[2], rettv); } /* @@ -18104,23 +18191,16 @@ void set_vim_var_list(int idx, list_T *val) } /// Set Dictionary v: variable to "val". -void set_vim_var_dict(int idx, dict_T *val) FUNC_ATTR_NONNULL_ALL +void set_vim_var_dict(int idx, dict_T *val) { dict_unref(vimvars[idx].vv_dict); + vimvars[idx].vv_dict = val; - // Set readonly - int todo = (int)val->dv_hashtab.ht_used; - for (hashitem_T *hi = val->dv_hashtab.ht_array; todo > 0 ; ++hi) { - if (HASHITEM_EMPTY(hi)) { - continue; - } - - --todo; - HI2DI(hi)->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; + if (val != NULL) { + ++val->dv_refcount; + // Set readonly + dict_set_keys_readonly(val); } - - vimvars[idx].vv_dict = val; - ++val->dv_refcount; } /* diff --git a/src/nvim/eval.h b/src/nvim/eval.h index 79a1341d98..f51b0f4921 100644 --- a/src/nvim/eval.h +++ b/src/nvim/eval.h @@ -113,7 +113,8 @@ enum { VV_OPTION_TYPE, VV_ERRORS, VV_MSGPACK_TYPES, - VV_LEN, /* number of v: vars */ + VV_EVENT, + VV_LEN, // number of v: vars }; /// Maximum number of function arguments diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 4a423269cc..96a7c8c5af 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -3017,7 +3017,7 @@ void do_sub(exarg_T *eap) // The number of lines joined is the number of lines in the range linenr_T joined_lines_count = eap->line2 - eap->line1 + 1 // plus one extra line if not at the end of file. - + eap->line2 < curbuf->b_ml.ml_line_count ? 1 : 0; + + (eap->line2 < curbuf->b_ml.ml_line_count ? 1 : 0); if (joined_lines_count > 1) { do_join(joined_lines_count, FALSE, TRUE, FALSE, true); sub_nsubs = joined_lines_count - 1; @@ -4877,16 +4877,13 @@ helptags_one ( int fi; char_u *s; char_u *fname; - int dirlen; int utf8 = MAYBE; int this_utf8; int firstline; int mix = FALSE; /* detected mixed encodings */ - /* - * Find all *.txt files. - */ - dirlen = (int)STRLEN(dir); + // Find all *.txt files. + size_t dirlen = STRLEN(dir); STRCPY(NameBuff, dir); STRCAT(NameBuff, "/**/*"); STRCAT(NameBuff, ext); @@ -4908,7 +4905,7 @@ helptags_one ( */ STRCPY(NameBuff, dir); add_pathsep((char *)NameBuff); - STRCAT(NameBuff, tagfname); + STRNCAT(NameBuff, tagfname, sizeof(NameBuff) - dirlen - 2); fd_tags = mch_fopen((char *)NameBuff, "w"); if (fd_tags == NULL) { EMSG2(_("E152: Cannot open %s for writing"), NameBuff); diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 47774f5a99..71ea170e1c 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -57,23 +57,23 @@ typedef struct scriptitem_S { char_u *sn_name; bool file_id_valid; FileID file_id; - int sn_prof_on; /* TRUE when script is/was profiled */ - int sn_pr_force; /* forceit: profile functions in this script */ - proftime_T sn_pr_child; /* time set when going into first child */ - int sn_pr_nest; /* nesting for sn_pr_child */ - /* profiling the script as a whole */ - int sn_pr_count; /* nr of times sourced */ - proftime_T sn_pr_total; /* time spent in script + children */ - proftime_T sn_pr_self; /* time spent in script itself */ - proftime_T sn_pr_start; /* time at script start */ - proftime_T sn_pr_children; /* time in children after script start */ - /* profiling the script per line */ - garray_T sn_prl_ga; /* things stored for every line */ - proftime_T sn_prl_start; /* start time for current line */ - proftime_T sn_prl_children; /* time spent in children for this line */ - proftime_T sn_prl_wait; /* wait start time for current line */ - int sn_prl_idx; /* index of line being timed; -1 if none */ - int sn_prl_execed; /* line being timed was executed */ + bool sn_prof_on; ///< true when script is/was profiled + int sn_pr_force; ///< forceit: profile functions in this script + proftime_T sn_pr_child; ///< time set when going into first child + int sn_pr_nest; ///< nesting for sn_pr_child + // profiling the script as a whole + int sn_pr_count; ///< nr of times sourced + proftime_T sn_pr_total; ///< time spent in script + children + proftime_T sn_pr_self; ///< time spent in script itself + proftime_T sn_pr_start; ///< time at script start + proftime_T sn_pr_children; ///< time in children after script start + // profiling the script per line + garray_T sn_prl_ga; ///< things stored for every line + proftime_T sn_prl_start; ///< start time for current line + proftime_T sn_prl_children; ///< time spent in children for this line + proftime_T sn_prl_wait; ///< wait start time for current line + linenr_T sn_prl_idx; ///< index of line being timed; -1 if none + int sn_prl_execed; ///< line being timed was executed } scriptitem_T; static garray_T script_items = {0, 0, sizeof(scriptitem_T), 4, NULL}; @@ -81,9 +81,9 @@ static garray_T script_items = {0, 0, sizeof(scriptitem_T), 4, NULL}; /* Struct used in sn_prl_ga for every line of a script. */ typedef struct sn_prl_S { - int snp_count; /* nr of times line was executed */ - proftime_T sn_prl_total; /* time spent in a line + children */ - proftime_T sn_prl_self; /* time spent in a line itself */ + int snp_count; ///< nr of times line was executed + proftime_T sn_prl_total; ///< time spent in a line + children + proftime_T sn_prl_self; ///< time spent in a line itself } sn_prl_T; /* @@ -93,18 +93,18 @@ typedef struct sn_prl_S { * sourcing can be done recursively. */ struct source_cookie { - FILE *fp; /* opened file for sourcing */ - char_u *nextline; /* if not NULL: line that was read ahead */ - int finished; /* ":finish" used */ + FILE *fp; ///< opened file for sourcing + char_u *nextline; ///< if not NULL: line that was read ahead + int finished; ///< ":finish" used #if defined(USE_CRNL) - int fileformat; /* EOL_UNKNOWN, EOL_UNIX or EOL_DOS */ - int error; /* TRUE if LF found after CR-LF */ + int fileformat; ///< EOL_UNKNOWN, EOL_UNIX or EOL_DOS + int error; ///< TRUE if LF found after CR-LF #endif - linenr_T breakpoint; /* next line with breakpoint or zero */ - char_u *fname; /* name of sourced file */ - int dbg_tick; /* debug_tick when breakpoint was set */ - int level; /* top nesting level of sourced file */ - vimconv_T conv; /* type of conversion */ + linenr_T breakpoint; ///< next line with breakpoint or zero + char_u *fname; ///< name of sourced file + int dbg_tick; ///< debug_tick when breakpoint was set + int level; ///< top nesting level of sourced file + vimconv_T conv; ///< type of conversion }; # define PRL_ITEM(si, idx) (((sn_prl_T *)(si)->sn_prl_ga.ga_data)[(idx)]) @@ -272,7 +272,7 @@ void do_debug(char_u *cmd) xfree(cmdline); } - lines_left = Rows - 1; + lines_left = (int)(Rows - 1); } xfree(cmdline); @@ -281,7 +281,7 @@ void do_debug(char_u *cmd) redraw_all_later(NOT_VALID); need_wait_return = FALSE; msg_scroll = save_msg_scroll; - lines_left = Rows - 1; + lines_left = (int)(Rows - 1); State = save_State; did_emsg = save_did_emsg; cmd_silent = save_cmd_silent; @@ -392,12 +392,12 @@ int dbg_check_skipped(exarg_T *eap) * This is a grow-array of structs. */ struct debuggy { - int dbg_nr; /* breakpoint number */ - int dbg_type; /* DBG_FUNC or DBG_FILE */ - char_u *dbg_name; /* function or file name */ - regprog_T *dbg_prog; /* regexp program */ - linenr_T dbg_lnum; /* line number in function or file */ - int dbg_forceit; /* ! used */ + int dbg_nr; ///< breakpoint number + int dbg_type; ///< DBG_FUNC or DBG_FILE + char_u *dbg_name; ///< function or file name + regprog_T *dbg_prog; ///< regexp program + linenr_T dbg_lnum; ///< line number in function or file + int dbg_forceit; ///< ! used }; static garray_T dbg_breakp = {0, 0, sizeof(struct debuggy), 4, NULL}; @@ -563,13 +563,14 @@ void ex_breakdel(exarg_T *eap) } if (ascii_isdigit(*eap->arg)) { - /* ":breakdel {nr}" */ - nr = atol((char *)eap->arg); - for (int i = 0; i < gap->ga_len; ++i) + // ":breakdel {nr}" + nr = atoi((char *)eap->arg); + for (int i = 0; i < gap->ga_len; ++i) { if (DEBUGGY(gap, i).dbg_nr == nr) { todel = i; break; } + } } else if (*eap->arg == '*') { todel = 0; del_all = TRUE; @@ -602,11 +603,13 @@ void ex_breakdel(exarg_T *eap) --gap->ga_len; if (todel < gap->ga_len) memmove(&DEBUGGY(gap, todel), &DEBUGGY(gap, todel + 1), - (gap->ga_len - todel) * sizeof(struct debuggy)); - if (eap->cmdidx == CMD_breakdel) + (size_t)(gap->ga_len - todel) * sizeof(struct debuggy)); + if (eap->cmdidx == CMD_breakdel) { ++debug_tick; - if (!del_all) + } + if (!del_all) { break; + } } /* If all breakpoints were removed clear the array. */ @@ -810,8 +813,8 @@ void ex_pydo3(exarg_T *eap) /* Command line expansion for :profile. */ static enum { - PEXP_SUBCMD, /* expand :profile sub-commands */ - PEXP_FUNC /* expand :profile func {funcname} */ + PEXP_SUBCMD, ///< expand :profile sub-commands + PEXP_FUNC ///< expand :profile func {funcname} } pexpand_what; static char *pexpand_cmds[] = { @@ -892,7 +895,7 @@ static void profile_reset(void) for (int id = 1; id <= script_items.ga_len; id++) { scriptitem_T *si = &SCRIPT_ITEM(id); if (si->sn_prof_on) { - si->sn_prof_on = 0; + si->sn_prof_on = false; si->sn_pr_force = 0; si->sn_pr_child = profile_zero(); si->sn_pr_nest = 0; @@ -949,7 +952,7 @@ static void profile_init(scriptitem_T *si) ga_init(&si->sn_prl_ga, sizeof(sn_prl_T), 100); si->sn_prl_idx = -1; - si->sn_prof_on = TRUE; + si->sn_prof_on = true; si->sn_pr_nest = 0; } @@ -1255,7 +1258,7 @@ check_changed_any ( int save; int i; int bufnum = 0; - int bufcount = 0; + size_t bufcount = 0; int *bufnrs; FOR_ALL_BUFFERS(buf) { @@ -1520,7 +1523,7 @@ do_arglist ( didone = TRUE; xfree(ARGLIST[match].ae_fname); memmove(ARGLIST + match, ARGLIST + match + 1, - (ARGCOUNT - match - 1) * sizeof(aentry_T)); + (size_t)(ARGCOUNT - match - 1) * sizeof(aentry_T)); --ALIST(curwin)->al_ga.ga_len; if (curwin->w_arg_idx > match) --curwin->w_arg_idx; @@ -1537,9 +1540,7 @@ do_arglist ( int i = expand_wildcards(new_ga.ga_len, (char_u **)new_ga.ga_data, &exp_count, &exp_files, EW_DIR|EW_FILE|EW_ADDSLASH|EW_NOTFOUND); ga_clear(&new_ga); - if (i == FAIL) - return FAIL; - if (exp_count == 0) { + if (i == FAIL || exp_count == 0) { EMSG(_(e_nomatch)); return FAIL; } @@ -1702,10 +1703,11 @@ void ex_argument(exarg_T *eap) { int i; - if (eap->addr_count > 0) - i = eap->line2 - 1; - else + if (eap->addr_count > 0) { + i = (int)eap->line2 - 1; + } else { i = curwin->w_arg_idx; + } do_argfile(eap, i); } @@ -1844,22 +1846,25 @@ void ex_argadd(exarg_T *eap) void ex_argdelete(exarg_T *eap) { if (eap->addr_count > 0) { - /* ":1,4argdel": Delete all arguments in the range. */ - if (eap->line2 > ARGCOUNT) + // ":1,4argdel": Delete all arguments in the range. + if (eap->line2 > ARGCOUNT) { eap->line2 = ARGCOUNT; - int n = eap->line2 - eap->line1 + 1; - if (*eap->arg != NUL || n <= 0) + } + linenr_T n = eap->line2 - eap->line1 + 1; + if (*eap->arg != NUL || n <= 0) { EMSG(_(e_invarg)); - else { - for (int i = eap->line1; i <= eap->line2; ++i) + } else { + for (linenr_T i = eap->line1; i <= eap->line2; ++i) { xfree(ARGLIST[i - 1].ae_fname); + } memmove(ARGLIST + eap->line1 - 1, ARGLIST + eap->line2, - (size_t)((ARGCOUNT - eap->line2) * sizeof(aentry_T))); - ALIST(curwin)->al_ga.ga_len -= n; - if (curwin->w_arg_idx >= eap->line2) - curwin->w_arg_idx -= n; - else if (curwin->w_arg_idx > eap->line1) - curwin->w_arg_idx = eap->line1; + (size_t)(ARGCOUNT - eap->line2) * sizeof(aentry_T)); + ALIST(curwin)->al_ga.ga_len -= (int)n; + if (curwin->w_arg_idx >= eap->line2) { + curwin->w_arg_idx -= (int)n; + } else if (curwin->w_arg_idx > eap->line1) { + curwin->w_arg_idx = (int)eap->line1; + } } } else if (*eap->arg == NUL) EMSG(_(e_argreq)); @@ -1909,7 +1914,7 @@ void ex_listdo(exarg_T *eap) } break; case CMD_argdo: - i = eap->line1 - 1; + i = (int)eap->line1 - 1; break; default: break; @@ -1942,10 +1947,11 @@ void ex_listdo(exarg_T *eap) ex_cc(eap); buf = curbuf; - i = eap->line1 - 1; + i = (int)eap->line1 - 1; if (eap->addr_count <= 0) { // Default to all quickfix/location list entries. - eap->line2 = qf_size; + assert(qf_size < MAXLNUM); + eap->line2 = (linenr_T)qf_size; } } } else { @@ -2098,7 +2104,7 @@ alist_add_list ( after = ARGCOUNT; if (after < ARGCOUNT) memmove(&(ARGLIST[after + count]), &(ARGLIST[after]), - (ARGCOUNT - after) * sizeof(aentry_T)); + (size_t)(ARGCOUNT - after) * sizeof(aentry_T)); for (int i = 0; i < count; ++i) { ARGLIST[after + i].ae_fname = files[i]; ARGLIST[after + i].ae_fnum = buflist_add(files[i], BLN_LISTED); @@ -2571,7 +2577,7 @@ do_source ( while (script_items.ga_len < current_SID) { ++script_items.ga_len; SCRIPT_ITEM(script_items.ga_len).sn_name = NULL; - SCRIPT_ITEM(script_items.ga_len).sn_prof_on = FALSE; + SCRIPT_ITEM(script_items.ga_len).sn_prof_on = false; } si = &SCRIPT_ITEM(current_SID); si->sn_name = fname_exp; @@ -3386,8 +3392,8 @@ static void script_host_execute(char *name, exarg_T *eap) // script list_append_string(args, script ? script : eap->arg, -1); // current range - list_append_number(args, eap->line1); - list_append_number(args, eap->line2); + list_append_number(args, (int)eap->line1); + list_append_number(args, (int)eap->line2); (void)eval_call_provider(name, "execute", args); } @@ -3403,16 +3409,16 @@ static void script_host_execute_file(char *name, exarg_T *eap) // filename list_append_string(args, buffer, -1); // current range - list_append_number(args, eap->line1); - list_append_number(args, eap->line2); + list_append_number(args, (int)eap->line1); + list_append_number(args, (int)eap->line2); (void)eval_call_provider(name, "execute_file", args); } static void script_host_do_range(char *name, exarg_T *eap) { list_T *args = list_alloc(); - list_append_number(args, eap->line1); - list_append_number(args, eap->line2); + list_append_number(args, (int)eap->line1); + list_append_number(args, (int)eap->line2); list_append_string(args, eap->arg, -1); (void)eval_call_provider(name, "do_range", args); } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 2d17b31f0f..a7e98e7f04 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -4320,7 +4320,7 @@ static void ex_unmap(exarg_T *eap) */ static void ex_mapclear(exarg_T *eap) { - map_clear(eap->cmd, eap->arg, eap->forceit, FALSE); + map_clear_mode(eap->cmd, eap->arg, eap->forceit, false); } /* @@ -4328,7 +4328,7 @@ static void ex_mapclear(exarg_T *eap) */ static void ex_abclear(exarg_T *eap) { - map_clear(eap->cmd, eap->arg, TRUE, TRUE); + map_clear_mode(eap->cmd, eap->arg, true, true); } static void ex_autocmd(exarg_T *eap) @@ -6290,10 +6290,8 @@ void ex_splitview(exarg_T *eap) if (eap->cmdidx == CMD_tabedit || eap->cmdidx == CMD_tabfind || eap->cmdidx == CMD_tabnew) { - if (win_new_tabpage(cmdmod.tab != 0 ? cmdmod.tab - : eap->addr_count == 0 ? 0 - : (int)eap->line2 + 1) != FAIL) { - apply_autocmds(EVENT_TABNEW, eap->arg, eap->arg, FALSE, curbuf); + if (win_new_tabpage(cmdmod.tab != 0 ? cmdmod.tab : eap->addr_count == 0 + ? 0 : (int)eap->line2 + 1, eap->arg) != FAIL) { do_exedit(eap, old_curwin); apply_autocmds(EVENT_TABNEWENTERED, NULL, NULL, FALSE, curbuf); diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 73b3de0b5d..437495faa4 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -2915,9 +2915,9 @@ do_map ( did_it = TRUE; } } - if (mp->m_mode == 0) { /* entry can be deleted */ - map_free(mpp); - continue; /* continue with *mpp */ + if (mp->m_mode == 0) { // entry can be deleted + mapblock_free(mpp); + continue; // continue with *mpp } /* @@ -3012,7 +3012,7 @@ theend: * Delete one entry from the abbrlist or maphash[]. * "mpp" is a pointer to the m_next field of the PREVIOUS entry! */ -static void map_free(mapblock_T **mpp) +static void mapblock_free(mapblock_T **mpp) { mapblock_T *mp; @@ -3080,7 +3080,7 @@ int get_map_mode(char_u **cmdp, int forceit) * Clear all mappings or abbreviations. * 'abbr' should be FALSE for mappings, TRUE for abbreviations. */ -void map_clear(char_u *cmdp, char_u *arg, int forceit, int abbr) +void map_clear_mode(char_u *cmdp, char_u *arg, int forceit, int abbr) { int mode; int local; @@ -3132,8 +3132,8 @@ map_clear_int ( mp = *mpp; if (mp->m_mode & mode) { mp->m_mode &= ~mode; - if (mp->m_mode == 0) { /* entry can be deleted */ - map_free(mpp); + if (mp->m_mode == 0) { // entry can be deleted + mapblock_free(mpp); continue; } /* diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c index d236501b3f..7169a1d963 100644 --- a/src/nvim/if_cscope.c +++ b/src/nvim/if_cscope.c @@ -1632,77 +1632,79 @@ static char *cs_pathcomponents(char *path) return s; } -/* - * PRIVATE: cs_print_tags_priv - * - * called from cs_manage_matches() - */ +/// Print cscope output that was converted into ctags style entries. +/// +/// Only called from cs_manage_matches(). +/// +/// @param matches Array of cscope lines in ctags style. Every entry was +// produced with a format string of the form +// "%s\t%s\t%s;\"\t%s" or +// "%s\t%s\t%s;\"" +// by cs_make_vim_style_matches(). +/// @param cntxts Context for matches. +/// @param num_matches Number of entries in matches/cntxts, always greater 0. static void cs_print_tags_priv(char **matches, char **cntxts, - size_t num_matches) + size_t num_matches) FUNC_ATTR_NONNULL_ALL { - char *ptag; - char *fname, *lno, *extra, *tbuf; - size_t num; - char *globalcntx = "GLOBAL"; - char *context; - char *cstag_msg = _("Cscope tag: %s"); + char *globalcntx = "GLOBAL"; + char *cstag_msg = _("Cscope tag: %s"); - assert (num_matches > 0); + assert(num_matches > 0); + assert(strcnt(matches[0], '\t') >= 2); - tbuf = xmalloc(strlen(matches[0]) + 1); + char *ptag = matches[0]; + char *ptag_end = strchr(ptag, '\t'); + assert(ptag_end >= ptag); + // NUL terminate tag string in matches[0]. + *ptag_end = NUL; - strcpy(tbuf, matches[0]); - ptag = strtok(tbuf, "\t"); - - size_t newsize = strlen(cstag_msg) + strlen(ptag); + // The "%s" in cstag_msg won't appear in the result string, so we don't need + // extra memory for terminating NUL. + size_t newsize = strlen(cstag_msg) + (size_t)(ptag_end - ptag); char *buf = xmalloc(newsize); size_t bufsize = newsize; // Track available bufsize - (void)sprintf(buf, cstag_msg, ptag); + (void)snprintf(buf, bufsize, cstag_msg, ptag); MSG_PUTS_ATTR(buf, hl_attr(HLF_T)); + msg_clr_eos(); - xfree(tbuf); + // restore matches[0] + *ptag_end = '\t'; - MSG_PUTS_ATTR(_("\n # line"), hl_attr(HLF_T)); /* strlen is 7 */ + // Column headers for match number, line number and filename. + MSG_PUTS_ATTR(_("\n # line"), hl_attr(HLF_T)); msg_advance(msg_col + 2); MSG_PUTS_ATTR(_("filename / context / line\n"), hl_attr(HLF_T)); - num = 1; for (size_t i = 0; i < num_matches; i++) { - size_t idx = i; - - /* if we really wanted to, we could avoid this malloc and strcpy - * by parsing matches[i] on the fly and placing stuff into buf - * directly, but that's too much of a hassle - */ - tbuf = xmalloc(strlen(matches[idx]) + 1); - (void)strcpy(tbuf, matches[idx]); - - if (strtok(tbuf, (const char *)"\t") == NULL) - continue; - if ((fname = strtok(NULL, (const char *)"\t")) == NULL) - continue; - if ((lno = strtok(NULL, (const char *)"\t")) == NULL) - continue; - extra = strtok(NULL, (const char *)"\t"); - - lno[strlen(lno)-2] = '\0'; /* ignore ;" at the end */ + assert(strcnt(matches[i], '\t') >= 2); + + // Parse filename, line number and optional part. + char *fname = strchr(matches[i], '\t') + 1; + char *fname_end = strchr(fname, '\t'); + // Replace second '\t' in matches[i] with NUL to terminate fname. + *fname_end = NUL; + + char *lno = fname_end + 1; + char *extra = xstrchrnul(lno, '\t'); + // Ignore ;" at the end of lno. + char *lno_end = extra - 2; + *lno_end = NUL; + // Do we have an optional part? + extra = *extra ? extra + 1 : NULL; const char *csfmt_str = "%4zu %6s "; - /* hopefully 'num' (num of matches) will be less than 10^16 */ - newsize = strlen(csfmt_str) + 16 + strlen(lno); + // hopefully num_matches will be less than 10^16 + newsize = strlen(csfmt_str) + 16 + (size_t)(lno_end - lno); if (bufsize < newsize) { buf = xrealloc(buf, newsize); bufsize = newsize; } - (void)sprintf(buf, csfmt_str, num, lno); + (void)snprintf(buf, bufsize, csfmt_str, i + 1, lno); MSG_PUTS_ATTR(buf, hl_attr(HLF_CM)); MSG_PUTS_LONG_ATTR(cs_pathcomponents(fname), hl_attr(HLF_CM)); - /* compute the required space for the context */ - if (cntxts[idx] != NULL) - context = cntxts[idx]; - else - context = globalcntx; + // compute the required space for the context + char *context = cntxts[i] ? cntxts[i] : globalcntx; const char *cntxformat = " <<%s>>"; // '%s' won't appear in result string, so: @@ -1713,11 +1715,13 @@ static void cs_print_tags_priv(char **matches, char **cntxts, buf = xrealloc(buf, newsize); bufsize = newsize; } - (void)sprintf(buf, cntxformat, context); + int buf_len = snprintf(buf, bufsize, cntxformat, context); + assert(buf_len >= 0); - /* print the context only if it fits on the same line */ - if (msg_col + (int)strlen(buf) >= (int)Columns) + // Print the context only if it fits on the same line. + if (msg_col + buf_len >= (int)Columns) { msg_putchar('\n'); + } msg_advance(12); MSG_PUTS_LONG(buf); msg_putchar('\n'); @@ -1726,23 +1730,23 @@ static void cs_print_tags_priv(char **matches, char **cntxts, MSG_PUTS_LONG(extra); } - xfree(tbuf); /* only after printing extra due to strtok use */ + // restore matches[i] + *fname_end = '\t'; + *lno_end = ';'; - if (msg_col) + if (msg_col) { msg_putchar('\n'); + } os_breakcheck(); if (got_int) { - got_int = FALSE; /* don't print any more matches */ + got_int = false; // don't print any more matches break; } - - num++; - } /* for all matches */ + } xfree(buf); -} /* cs_print_tags_priv */ - +} /* * PRIVATE: cs_read_prompt diff --git a/src/nvim/lib/khash.h b/src/nvim/lib/khash.h index 56be29d14c..8287cb14da 100644 --- a/src/nvim/lib/khash.h +++ b/src/nvim/lib/khash.h @@ -184,7 +184,7 @@ typedef khint_t khiter_t; #define kfree(P) xfree(P) #endif -static const double __ac_HASH_UPPER = 0.77; +#define __ac_HASH_UPPER 0.77 #define __KHASH_TYPE(name, khkey_t, khval_t) \ typedef struct { \ diff --git a/src/nvim/lib/kvec.h b/src/nvim/lib/kvec.h index 0466cb229c..53ecf232c6 100644 --- a/src/nvim/lib/kvec.h +++ b/src/nvim/lib/kvec.h @@ -77,10 +77,10 @@ int main() { (v).items[(v).size++] = (x); \ } while (0) -#define kv_pushp(type, v) (((v).size == (v).capacity)? \ +#define kv_pushp(type, v) ((((v).size == (v).capacity)? \ ((v).capacity = ((v).capacity? (v).capacity<<1 : 8), \ (v).items = (type*)xrealloc((v).items, sizeof(type) * (v).capacity), 0) \ - : 0), ((v).items + ((v).size++)) + : 0), ((v).items + ((v).size++))) #define kv_a(type, v, i) (((v).capacity <= (size_t)(i)? \ ((v).capacity = (v).size = (i) + 1, kv_roundup32((v).capacity), \ diff --git a/src/nvim/main.c b/src/nvim/main.c index d3cdfe3edf..5b5c8a22aa 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -317,14 +317,16 @@ int main(int argc, char **argv) } // open terminals when opening files that start with term:// - do_cmdline_cmd("autocmd BufReadCmd term://* " +#define PROTO "term://" + do_cmdline_cmd("autocmd BufReadCmd " PROTO "* nested " ":call termopen( " // Capture the command string "matchstr(expand(\"<amatch>\"), " - "'\\c\\mterm://\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), " + "'\\c\\m" PROTO "\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), " // capture the working directory "{'cwd': get(matchlist(expand(\"<amatch>\"), " - "'\\c\\mterm://\\(.\\{-}\\)//'), 1, '')})"); + "'\\c\\m" PROTO "\\(.\\{-}\\)//'), 1, '')})"); +#undef PROTO /* Execute --cmd arguments. */ exe_pre_commands(¶ms); @@ -657,6 +659,9 @@ static void init_locale(void) setlocale(LC_NUMERIC, "C"); # endif +# ifdef LOCALE_INSTALL_DIR // gnu/linux standard: $prefix/share/locale + bindtextdomain(PROJECT_NAME, LOCALE_INSTALL_DIR); +# else // old vim style: $runtime/lang { char_u *p; @@ -665,11 +670,12 @@ static void init_locale(void) p = (char_u *)vim_getenv("VIMRUNTIME"); if (p != NULL && *p != NUL) { vim_snprintf((char *)NameBuff, MAXPATHL, "%s/lang", p); - bindtextdomain(VIMPACKAGE, (char *)NameBuff); + bindtextdomain(PROJECT_NAME, (char *)NameBuff); } xfree(p); - textdomain(VIMPACKAGE); } +# endif + textdomain(PROJECT_NAME); TIME_MSG("locale set"); } #endif diff --git a/src/nvim/map.c b/src/nvim/map.c index ed7bda4cce..d4262ae9a8 100644 --- a/src/nvim/map.c +++ b/src/nvim/map.c @@ -18,6 +18,9 @@ #define uint32_t_eq kh_int_hash_equal #define int_hash kh_int_hash_func #define int_eq kh_int_hash_equal +#define linenr_T_hash kh_int_hash_func +#define linenr_T_eq kh_int_hash_equal + #if defined(ARCH_64) #define ptr_t_hash(key) uint64_t_hash((uint64_t)key) @@ -78,6 +81,25 @@ return rv; \ } \ \ + U *map_##T##_##U##_ref(Map(T, U) *map, T key, bool put) \ + { \ + int ret; \ + khiter_t k; \ + if (put) { \ + k = kh_put(T##_##U##_map, map->table, key, &ret); \ + if (ret) { \ + kh_val(map->table, k) = INITIALIZER(T, U); \ + } \ + } else { \ + k = kh_get(T##_##U##_map, map->table, key); \ + if (k == kh_end(map->table)) { \ + return NULL; \ + } \ + } \ + \ + return &kh_val(map->table, k); \ + } \ + \ U map_##T##_##U##_del(Map(T, U) *map, T key) \ { \ U rv = INITIALIZER(T, U); \ @@ -118,3 +140,5 @@ MAP_IMPL(ptr_t, ptr_t, DEFAULT_INITIALIZER) MAP_IMPL(uint64_t, ptr_t, DEFAULT_INITIALIZER) #define MSGPACK_HANDLER_INITIALIZER {.fn = NULL, .async = false} MAP_IMPL(String, MsgpackRpcRequestHandler, MSGPACK_HANDLER_INITIALIZER) +#define KVEC_INITIALIZER { .size = 0, .capacity = 0, .items = NULL } +MAP_IMPL(linenr_T, bufhl_vec_T, KVEC_INITIALIZER) diff --git a/src/nvim/map.h b/src/nvim/map.h index c0e2ca3aac..e90cc360ce 100644 --- a/src/nvim/map.h +++ b/src/nvim/map.h @@ -6,6 +6,7 @@ #include "nvim/map_defs.h" #include "nvim/api/private/defs.h" #include "nvim/msgpack_rpc/defs.h" +#include "nvim/bufhl_defs.h" #define MAP_DECLS(T, U) \ KHASH_DECLARE(T##_##U##_map, T, U) \ @@ -19,6 +20,7 @@ U map_##T##_##U##_get(Map(T, U) *map, T key); \ bool map_##T##_##U##_has(Map(T, U) *map, T key); \ U map_##T##_##U##_put(Map(T, U) *map, T key, U value); \ + U *map_##T##_##U##_ref(Map(T, U) *map, T key, bool put); \ U map_##T##_##U##_del(Map(T, U) *map, T key); \ void map_##T##_##U##_clear(Map(T, U) *map); @@ -28,12 +30,14 @@ MAP_DECLS(cstr_t, ptr_t) MAP_DECLS(ptr_t, ptr_t) MAP_DECLS(uint64_t, ptr_t) MAP_DECLS(String, MsgpackRpcRequestHandler) +MAP_DECLS(linenr_T, bufhl_vec_T) #define map_new(T, U) map_##T##_##U##_new #define map_free(T, U) map_##T##_##U##_free #define map_get(T, U) map_##T##_##U##_get #define map_has(T, U) map_##T##_##U##_has #define map_put(T, U) map_##T##_##U##_put +#define map_ref(T, U) map_##T##_##U##_ref #define map_del(T, U) map_##T##_##U##_del #define map_clear(T, U) map_##T##_##U##_clear diff --git a/src/nvim/mark.c b/src/nvim/mark.c index e2f212340c..fe802e48ba 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -922,6 +922,7 @@ void mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after) } sign_mark_adjust(line1, line2, amount, amount_after); + bufhl_mark_adjust(curbuf, line1, line2, amount, amount_after); } /* previous context mark */ diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index fdd83f9dac..f0a249919f 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -571,8 +571,8 @@ char_u * mb_init(void) #ifdef HAVE_WORKING_LIBINTL /* GNU gettext 0.10.37 supports this feature: set the codeset used for * translated messages independently from the current locale. */ - (void)bind_textdomain_codeset(VIMPACKAGE, - enc_utf8 ? "utf-8" : (char *)p_enc); + (void)bind_textdomain_codeset(PROJECT_NAME, + enc_utf8 ? "utf-8" : (char *)p_enc); #endif diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index 6c969a43fc..db303fd54a 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -1985,13 +1985,13 @@ changed_lines ( changed_common(lnum, col, lnume, xtra); } -static void -changed_lines_buf ( - buf_T *buf, - linenr_T lnum, /* first line with change */ - linenr_T lnume, /* line below last changed line */ - long xtra /* number of extra lines (negative when deleting) */ -) +/// Mark line range in buffer as changed. +/// +/// @param buf the buffer where lines were changed +/// @param lnum first line with change +/// @param lnume line below last changed line +/// @param xtra number of extra lines (negative when deleting) +void changed_lines_buf(buf_T *buf, linenr_T lnum, linenr_T lnume, long xtra) { if (buf->b_mod_set) { /* find the maximum area that must be redisplayed */ diff --git a/src/nvim/msgpack_rpc/defs.h b/src/nvim/msgpack_rpc/defs.h index d97cf28ca1..5611636d4f 100644 --- a/src/nvim/msgpack_rpc/defs.h +++ b/src/nvim/msgpack_rpc/defs.h @@ -1,8 +1,6 @@ #ifndef NVIM_MSGPACK_RPC_DEFS_H #define NVIM_MSGPACK_RPC_DEFS_H -#include <msgpack.h> - /// The rpc_method_handlers table, used in msgpack_rpc_dispatch(), stores /// functions of this type. @@ -24,22 +22,6 @@ void msgpack_rpc_add_method_handler(String method, void msgpack_rpc_init_function_metadata(Dictionary *metadata); -/// Dispatches to the actual API function after basic payload validation by -/// `msgpack_rpc_call`. It is responsible for validating/converting arguments -/// to C types, and converting the return value back to msgpack types. -/// The implementation is generated at compile time with metadata extracted -/// from the api/*.h headers, -/// -/// @param channel_id The channel id -/// @param method_id The method id -/// @param req The parsed request object -/// @param error Pointer to error structure -/// @return Some object -Object msgpack_rpc_dispatch(uint64_t channel_id, - msgpack_object *req, - Error *error) - FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_NONNULL_ARG(3); - MsgpackRpcRequestHandler msgpack_rpc_get_handler_for(const char *name, size_t name_len) FUNC_ATTR_NONNULL_ARG(1); diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 9ee7a97270..2895816b8f 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -2344,9 +2344,12 @@ do_mouse ( if (mouse_row == 0 && firstwin->w_winrow > 0) { if (is_drag) { if (in_tab_line) { - tabpage_move(tab_page_click_defs[mouse_col].type == kStlClickTabClose - ? 9999 - : tab_page_click_defs[mouse_col].tabnr - 1); + if (tab_page_click_defs[mouse_col].type == kStlClickTabClose) { + tabpage_move(9999); + } else { + int tabnr = tab_page_click_defs[mouse_col].tabnr; + tabpage_move(tabnr < tabpage_index(curtab) ? tabnr - 1 : tabnr); + } } return false; } diff --git a/src/nvim/ops.c b/src/nvim/ops.c index b1adc85e1d..8c7805ba04 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -19,6 +19,7 @@ #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_getln.h" +#include "nvim/fileio.h" #include "nvim/fold.h" #include "nvim/getchar.h" #include "nvim/indent.h" @@ -835,12 +836,13 @@ int do_record(int c) yankreg_T *old_y_previous; int retval; - if (Recording == FALSE) { /* start recording */ - /* registers 0-9, a-z and " are allowed */ - if (c < 0 || (!ASCII_ISALNUM(c) && c != '"')) + if (Recording == false) { + // start recording + // registers 0-9, a-z and " are allowed + if (c < 0 || (!ASCII_ISALNUM(c) && c != '"')) { retval = FAIL; - else { - Recording = TRUE; + } else { + Recording = c; showmode(); regname = c; retval = OK; @@ -1409,8 +1411,9 @@ int op_delete(oparg_T *oap) op_yank_reg(oap, false, reg, false); } - if(oap->regname == 0) { + if (oap->regname == 0) { set_clipboard(0, reg); + yank_do_autocmd(oap, reg); } } @@ -2309,6 +2312,8 @@ bool op_yank(oparg_T *oap, bool message) yankreg_T *reg = get_yank_register(oap->regname, YREG_YANK); op_yank_reg(oap, message, reg, is_append_register(oap->regname)); set_clipboard(oap->regname, reg); + yank_do_autocmd(oap, reg); + return true; } @@ -2524,6 +2529,58 @@ static void yank_copy_line(yankreg_T *reg, struct block_def *bd, long y_idx) *pnew = NUL; } +/// Execute autocommands for TextYankPost. +/// +/// @param oap Operator arguments. +/// @param reg The yank register used. +static void yank_do_autocmd(oparg_T *oap, yankreg_T *reg) + FUNC_ATTR_NONNULL_ALL +{ + static bool recursive = false; + + if (recursive || !has_event(EVENT_TEXTYANKPOST)) { + // No autocommand was defined + // or we yanked from this autocommand. + return; + } + + recursive = true; + + // set v:event to a dictionary with information about the yank + dict_T *dict = get_vim_var_dict(VV_EVENT); + + // the yanked text + list_T *list = list_alloc(); + for (linenr_T i = 0; i < reg->y_size; i++) { + list_append_string(list, reg->y_array[i], -1); + } + list->lv_lock = VAR_FIXED; + dict_add_list(dict, "regcontents", list); + + // the register type + char buf[NUMBUFLEN+2]; + format_reg_type(reg->y_type, reg->y_width, buf, ARRAY_SIZE(buf)); + dict_add_nr_str(dict, "regtype", 0, (char_u *)buf); + + // name of requested register or the empty string for an unnamed operation. + buf[0] = (char)oap->regname; + buf[1] = NUL; + dict_add_nr_str(dict, "regname", 0, (char_u *)buf); + + // kind of operation (yank/delete/change) + buf[0] = get_op_char(oap->op_type); + buf[1] = NUL; + dict_add_nr_str(dict, "operator", 0, (char_u *)buf); + + dict_set_keys_readonly(dict); + textlock++; + apply_autocmds(EVENT_TEXTYANKPOST, NULL, NULL, false, curbuf); + textlock--; + dict_clear(dict); + + recursive = false; +} + /* * Put contents of register "regname" into the text. @@ -4631,7 +4688,7 @@ theend: * Used for getregtype() * Returns MAUTO for error. */ -char_u get_reg_type(int regname, long *reglen) +char_u get_reg_type(int regname, colnr_T *reg_width) { switch (regname) { case '%': /* file name */ @@ -4654,13 +4711,45 @@ char_u get_reg_type(int regname, long *reglen) yankreg_T *reg = get_yank_register(regname, YREG_PASTE); if (reg->y_array != NULL) { - if (reglen != NULL && reg->y_type == MBLOCK) - *reglen = reg->y_width; + if (reg_width != NULL && reg->y_type == MBLOCK) { + *reg_width = reg->y_width; + } return reg->y_type; } return MAUTO; } +/// Format the register type as a string. +/// +/// @param reg_type The register type. +/// @param reg_width The width, only used if "reg_type" is MBLOCK. +/// @param[out] buf Buffer to store formatted string. The allocated size should +/// be at least NUMBUFLEN+2 to always fit the value. +/// @param buf_len The allocated size of the buffer. +void format_reg_type(char_u reg_type, colnr_T reg_width, + char* buf, size_t buf_len) + FUNC_ATTR_NONNULL_ALL +{ + assert(buf_len > 1); + switch (reg_type) { + case MLINE: + buf[0] = 'V'; + buf[1] = NUL; + break; + case MCHAR: + buf[0] = 'v'; + buf[1] = NUL; + break; + case MBLOCK: + snprintf(buf, buf_len, CTRL_V_STR "%" PRIdCOLNR, reg_width + 1); + break; + case MAUTO: + buf[0] = NUL; + break; + } +} + + /// When `flags` has `kGRegList` return a list with text `s`. /// Otherwise just return `s`. /// diff --git a/src/nvim/option.c b/src/nvim/option.c index 0f6874e941..5efd71444a 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -5947,13 +5947,17 @@ option_value2string ( if (opp->flags & P_NUM) { long wc = 0; - if (wc_use_keyname(varp, &wc)) - STRCPY(NameBuff, get_special_key_name((int)wc, 0)); - else if (wc != 0) - STRCPY(NameBuff, transchar((int)wc)); - else - sprintf((char *)NameBuff, "%" PRId64, (int64_t)*(long *)varp); - } else { /* P_STRING */ + if (wc_use_keyname(varp, &wc)) { + STRLCPY(NameBuff, get_special_key_name((int)wc, 0), sizeof(NameBuff)); + } else if (wc != 0) { + STRLCPY(NameBuff, transchar((int)wc), sizeof(NameBuff)); + } else { + snprintf((char *)NameBuff, + sizeof(NameBuff), + "%" PRId64, + (int64_t)*(long *)varp); + } + } else { // P_STRING varp = *(char_u **)(varp); if (varp == NULL) /* just in case */ NameBuff[0] = NUL; @@ -6163,16 +6167,14 @@ int has_format_option(int x) return vim_strchr(curbuf->b_p_fo, x) != NULL; } -/* - * Return TRUE if "x" is present in 'shortmess' option, or - * 'shortmess' contains 'a' and "x" is present in SHM_A. - */ -int shortmess(int x) +/// @returns true if "x" is present in 'shortmess' option, or +/// 'shortmess' contains 'a' and "x" is present in SHM_ALL_ABBREVIATIONS. +bool shortmess(int x) { return p_shm != NULL && - ( vim_strchr(p_shm, x) != NULL - || (vim_strchr(p_shm, 'a') != NULL - && vim_strchr((char_u *)SHM_A, x) != NULL)); + (vim_strchr(p_shm, x) != NULL + || (vim_strchr(p_shm, 'a') != NULL + && vim_strchr((char_u *)SHM_ALL_ABBREVIATIONS, x) != NULL)); } /* diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 10706a0753..547bd9442c 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -151,26 +151,41 @@ #define COCU_ALL "nvic" /* flags for 'concealcursor' */ -/* characters for p_shm option: */ -#define SHM_RO 'r' /* readonly */ -#define SHM_MOD 'm' /* modified */ -#define SHM_FILE 'f' /* (file 1 of 2) */ -#define SHM_LAST 'i' /* last line incomplete */ -#define SHM_TEXT 'x' /* tx instead of textmode */ -#define SHM_LINES 'l' /* "L" instead of "lines" */ -#define SHM_NEW 'n' /* "[New]" instead of "[New file]" */ -#define SHM_WRI 'w' /* "[w]" instead of "written" */ -#define SHM_A "rmfixlnw" /* represented by 'a' flag */ -#define SHM_WRITE 'W' /* don't use "written" at all */ -#define SHM_TRUNC 't' /* trunctate file messages */ -#define SHM_TRUNCALL 'T' /* trunctate all messages */ -#define SHM_OVER 'o' /* overwrite file messages */ -#define SHM_OVERALL 'O' /* overwrite more messages */ -#define SHM_SEARCH 's' /* no search hit bottom messages */ -#define SHM_ATTENTION 'A' /* no ATTENTION messages */ -#define SHM_INTRO 'I' /* intro messages */ -#define SHM_COMPLETIONMENU 'c' // completion menu messages -#define SHM_ALL "rmfixlnwaWtToOsAIc" /* all possible flags for 'shm' */ +/// characters for p_shm option: +enum { + SHM_RO = 'r', ///< Readonly. + SHM_MOD = 'm', ///< Modified. + SHM_FILE = 'f', ///< (file 1 of 2) + SHM_LAST = 'i', ///< Last line incomplete. + SHM_TEXT = 'x', ///< Tx instead of textmode. + SHM_LINES = 'l', ///< "L" instead of "lines". + SHM_NEW = 'n', ///< "[New]" instead of "[New file]". + SHM_WRI = 'w', ///< "[w]" instead of "written". + SHM_ABBREVIATIONS = 'a', ///< Use abbreviations from #SHM_ALL_ABBREVIATIONS. + SHM_WRITE = 'W', ///< Don't use "written" at all. + SHM_TRUNC = 't', ///< Trunctate file messages. + SHM_TRUNCALL = 'T', ///< Trunctate all messages. + SHM_OVER = 'o', ///< Overwrite file messages. + SHM_OVERALL = 'O', ///< Overwrite more messages. + SHM_SEARCH = 's', ///< No search hit bottom messages. + SHM_ATTENTION = 'A', ///< No ATTENTION messages. + SHM_INTRO = 'I', ///< Intro messages. + SHM_COMPLETIONMENU = 'c', ///< Completion menu messages. + SHM_RECORDING = 'q', ///< Short recording message. +}; +/// Represented by 'a' flag. +#define SHM_ALL_ABBREVIATIONS ((char_u[]) { \ + SHM_RO, SHM_MOD, SHM_FILE, SHM_LAST, SHM_TEXT, SHM_LINES, SHM_NEW, SHM_WRI, \ + 0, \ +}) +/// All possible flags for 'shm'. +#define SHM_ALL ((char_u[]) { \ + SHM_RO, SHM_MOD, SHM_FILE, SHM_LAST, SHM_TEXT, SHM_LINES, SHM_NEW, SHM_WRI, \ + SHM_ABBREVIATIONS, SHM_WRITE, SHM_TRUNC, SHM_TRUNCALL, SHM_OVER, \ + SHM_OVERALL, SHM_SEARCH, SHM_ATTENTION, SHM_INTRO, SHM_COMPLETIONMENU, \ + SHM_RECORDING, \ + 0, \ +}) /* characters for p_go: */ #define GO_ASEL 'a' /* autoselect */ diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index 41ce8ddbc2..384a17004e 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -759,15 +759,15 @@ char_u * home_replace_save(buf_T *buf, char_u *src) FUNC_ATTR_NONNULL_RET void vim_setenv(const char *name, const char *val) { os_setenv(name, val, 1); - /* - * When setting $VIMRUNTIME adjust the directory to find message - * translations to $VIMRUNTIME/lang. - */ +#ifndef LOCALE_INSTALL_DIR + // When setting $VIMRUNTIME adjust the directory to find message + // translations to $VIMRUNTIME/lang. if (*val != NUL && STRICMP(name, "VIMRUNTIME") == 0) { char *buf = (char *)concat_str((char_u *)val, (char_u *)"/lang"); - bindtextdomain(VIMPACKAGE, buf); + bindtextdomain(PROJECT_NAME, buf); xfree(buf); } +#endif } diff --git a/src/nvim/po/CMakeLists.txt b/src/nvim/po/CMakeLists.txt index d1e08db65e..184c4894b9 100644 --- a/src/nvim/po/CMakeLists.txt +++ b/src/nvim/po/CMakeLists.txt @@ -72,8 +72,8 @@ if(HAVE_WORKING_LIBINTL AND GETTEXT_FOUND AND XGETTEXT_PRG AND ICONV_PRG) install_helper( FILES ${moFile} - DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/nvim/runtime/lang/${name}/LC_MESSAGES - RENAME nvim.mo) + DESTINATION ${CMAKE_INSTALL_LOCALEDIR}/${name}/LC_MESSAGES + RENAME ${PROJECT_NAME}.mo) list(APPEND LANGUAGE_MO_FILES ${moFile}) endmacro() diff --git a/src/nvim/pos.h b/src/nvim/pos.h index 7071df51e8..864f3fe866 100644 --- a/src/nvim/pos.h +++ b/src/nvim/pos.h @@ -2,7 +2,11 @@ #define NVIM_POS_H typedef long linenr_T; // line number type -typedef int colnr_T; // column number type + +/// Column number type +typedef int colnr_T; +/// Format used to print values which have colnr_T type +#define PRIdCOLNR "d" #define MAXLNUM 0x7fffffff // maximum (invalid) line number #define MAXCOL 0x7fffffff // maximum column number, 31 bits diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index f3abf864fb..4a8391430b 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -2299,13 +2299,15 @@ static void qf_fill_buffer(qf_info_T *qi) if (qfp->qf_fnum != 0 && (errbuf = buflist_findnr(qfp->qf_fnum)) != NULL && errbuf->b_fname != NULL) { - if (qfp->qf_type == 1) /* :helpgrep */ - STRCPY(IObuff, path_tail(errbuf->b_fname)); - else - STRCPY(IObuff, errbuf->b_fname); + if (qfp->qf_type == 1) { // :helpgrep + STRLCPY(IObuff, path_tail(errbuf->b_fname), sizeof(IObuff)); + } else { + STRLCPY(IObuff, errbuf->b_fname, sizeof(IObuff)); + } len = (int)STRLEN(IObuff); - } else + } else { len = 0; + } IObuff[len++] = '|'; if (qfp->qf_lnum > 0) { diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 3b5836f0b5..1cc270023c 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -2184,6 +2184,10 @@ win_line ( int prev_c1 = 0; /* first composing char for prev_c */ int did_line_attr = 0; + bool has_bufhl = false; // this buffer has highlight matches + int bufhl_attr = 0; // attributes desired by bufhl + bufhl_lineinfo_T bufhl_info; // bufhl data for this line + /* draw_state: items that are drawn in sequence: */ #define WL_START 0 /* nothing done yet */ # define WL_CMDLINE WL_START + 1 /* cmdline window column */ @@ -2244,6 +2248,11 @@ win_line ( } } + if (bufhl_start_line(wp->w_buffer, lnum, &bufhl_info)) { + has_bufhl = true; + extra_check = true; + } + /* Check for columns to display for 'colorcolumn'. */ color_cols = wp->w_buffer->terminal ? NULL : wp->w_p_cc_cols; if (color_cols != NULL) @@ -3335,6 +3344,17 @@ win_line ( char_attr = hl_combine_attr(spell_attr, char_attr); } + if (has_bufhl && v > 0) { + bufhl_attr = bufhl_get_attr(&bufhl_info, (colnr_T)v); + if (bufhl_attr != 0) { + if (!attr_pri) { + char_attr = hl_combine_attr(char_attr, bufhl_attr); + } else { + char_attr = hl_combine_attr(bufhl_attr, char_attr); + } + } + } + if (wp->w_buffer->terminal) { char_attr = hl_combine_attr(char_attr, term_attrs[vcol]); } @@ -6760,8 +6780,8 @@ int showmode(void) if (Recording && edit_submode == NULL /* otherwise it gets too long */ ) { - MSG_PUTS_ATTR(_("recording"), attr); - need_clear = TRUE; + recording_mode(attr); + need_clear = true; } mode_displayed = TRUE; @@ -6800,26 +6820,33 @@ static void msg_pos_mode(void) msg_row = Rows - 1; } -/* - * Delete mode message. Used when ESC is typed which is expected to end - * Insert mode (but Insert mode didn't end yet!). - * Caller should check "mode_displayed". - */ -void unshowmode(int force) +/// Delete mode message. Used when ESC is typed which is expected to end +/// Insert mode (but Insert mode didn't end yet!). +/// Caller should check "mode_displayed". +void unshowmode(bool force) { - /* - * Don't delete it right now, when not redrawing or inside a mapping. - */ - if (!redrawing() || (!force && char_avail() && !KeyTyped)) - redraw_cmdline = TRUE; /* delete mode later */ - else { + // Don't delete it right now, when not redrawing or inside a mapping. + if (!redrawing() || (!force && char_avail() && !KeyTyped)) { + redraw_cmdline = true; // delete mode later + } else { msg_pos_mode(); - if (Recording) - MSG_PUTS_ATTR(_("recording"), hl_attr(HLF_CM)); + if (Recording) { + recording_mode(hl_attr(HLF_CM)); + } msg_clr_eos(); } } +static void recording_mode(int attr) +{ + MSG_PUTS_ATTR(_("recording"), attr); + if (!shortmess(SHM_RECORDING)) { + char_u s[4]; + vim_snprintf((char *)s, ARRAY_SIZE(s), " @%c", Recording); + MSG_PUTS_ATTR(s, attr); + } +} + /* * Draw the tab pages line at the top of the Vim window. */ diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index fb74569e3b..42fd81f643 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -141,10 +141,6 @@ struct terminal { int pressed_button; // pending width/height bool pending_resize; - // color palette. this isn't set directly in the vterm instance because - // the default values are used to obtain the color numbers passed to cterm - // colors - RgbValue colors[256]; // With a reference count of 0 the terminal can be freed. size_t refcount; }; @@ -205,6 +201,7 @@ void terminal_teardown(void) Terminal *terminal_open(TerminalOptions opts) { + bool true_color = ui_rgb_attached(); // Create a new terminal instance and configure it Terminal *rv = xcalloc(1, sizeof(Terminal)); rv->opts = opts; @@ -220,7 +217,7 @@ Terminal *terminal_open(TerminalOptions opts) // Set up screen rv->vts = vterm_obtain_screen(rv->vt); vterm_screen_enable_altscreen(rv->vts, true); - // delete empty lines at the end of the buffer + // delete empty lines at the end of the buffer vterm_screen_set_callbacks(rv->vts, &vterm_screen_callbacks, rv); vterm_screen_set_damage_merge(rv->vts, VTERM_DAMAGE_SCROLL); vterm_screen_reset(rv->vts, 1); @@ -250,12 +247,18 @@ Terminal *terminal_open(TerminalOptions opts) rv->sb_size = MIN(rv->sb_size, 100000); rv->sb_buffer = xmalloc(sizeof(ScrollbackLine *) * rv->sb_size); + if (!true_color) { + return rv; + } + + vterm_state_set_bold_highbright(state, true); + // Configure the color palette. Try to get the color from: // // - b:terminal_color_{NUM} // - g:terminal_color_{NUM} // - the VTerm instance - for (int i = 0; i < (int)ARRAY_SIZE(rv->colors); i++) { + for (int i = 0; i < 16; i++) { RgbValue color_val = -1; char var[64]; snprintf(var, sizeof(var), "terminal_color_%d", i); @@ -265,16 +268,13 @@ Terminal *terminal_open(TerminalOptions opts) xfree(name); if (color_val != -1) { - rv->colors[i] = color_val; + VTermColor color; + color.red = (uint8_t)((color_val >> 16) & 0xFF); + color.green = (uint8_t)((color_val >> 8) & 0xFF); + color.blue = (uint8_t)((color_val >> 0) & 0xFF); + vterm_state_set_palette_color(state, i, &color); } } - - if (color_val == -1) { - // the default is taken from vterm - VTermColor color; - vterm_state_get_palette_color(state, i, &color); - rv->colors[i] = RGB(color.red, color.green, color.blue); - } } return rv; @@ -548,10 +548,6 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr, map_get(int, int)(color_indexes, vt_fg) : 0; int vt_bg_idx = vt_bg != default_vt_bg ? map_get(int, int)(color_indexes, vt_bg) : 0; - // The index is now used to get the final rgb value from the - // user-customizable palette. - int vt_fg_rgb = vt_fg_idx != 0 ? term->colors[vt_fg_idx - 1] : -1; - int vt_bg_rgb = vt_bg_idx != 0 ? term->colors[vt_bg_idx - 1] : -1; int hl_attrs = (cell.attrs.bold ? HL_BOLD : 0) | (cell.attrs.italic ? HL_ITALIC : 0) @@ -566,8 +562,8 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr, .cterm_fg_color = vt_fg_idx, .cterm_bg_color = vt_bg_idx, .rgb_ae_attr = (int16_t)hl_attrs, - .rgb_fg_color = vt_fg_rgb, - .rgb_bg_color = vt_bg_rgb, + .rgb_fg_color = vt_fg, + .rgb_bg_color = vt_bg, }); } diff --git a/src/nvim/undo.c b/src/nvim/undo.c index b8cdffcda0..4a8a24d79d 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -4,29 +4,29 @@ * The saved lines are stored in a list of lists (one for each buffer): * * b_u_oldhead------------------------------------------------+ - * | - * V - * +--------------+ +--------------+ +--------------+ - * b_u_newhead--->| u_header | | u_header | | u_header | - * | uh_next------>| uh_next------>| uh_next---->NULL - * NULL<--------uh_prev |<---------uh_prev |<---------uh_prev | - * | uh_entry | | uh_entry | | uh_entry | - * +--------|-----+ +--------|-----+ +--------|-----+ - * | | | - * V V V - * +--------------+ +--------------+ +--------------+ - * | u_entry | | u_entry | | u_entry | - * | ue_next | | ue_next | | ue_next | - * +--------|-----+ +--------|-----+ +--------|-----+ - * | | | - * V V V - * +--------------+ NULL NULL - * | u_entry | - * | ue_next | - * +--------|-----+ - * | - * V - * etc. + * | + * V + * +--------------+ +--------------+ +--------------+ + * b_u_newhead--->| u_header | | u_header | | u_header | + * | uh_next------>| uh_next------>| uh_next---->NULL + * NULL<--------uh_prev |<---------uh_prev |<---------uh_prev | + * | uh_entry | | uh_entry | | uh_entry | + * +--------|-----+ +--------|-----+ +--------|-----+ + * | | | + * V V V + * +--------------+ +--------------+ +--------------+ + * | u_entry | | u_entry | | u_entry | + * | ue_next | | ue_next | | ue_next | + * +--------|-----+ +--------|-----+ +--------|-----+ + * | | | + * V V V + * +--------------+ NULL NULL + * | u_entry | + * | ue_next | + * +--------|-----+ + * | + * V + * etc. * * Each u_entry list contains the information for one undo or redo. * curbuf->b_u_curhead points to the header of the last undo (the next redo), @@ -37,30 +37,30 @@ * uh_seq field is numbered sequentially to be able to find a newer or older * branch. * - * +---------------+ +---------------+ - * b_u_oldhead --->| u_header | | u_header | - * | uh_alt_next ---->| uh_alt_next ----> NULL - * NULL <----- uh_alt_prev |<------ uh_alt_prev | - * | uh_prev | | uh_prev | - * +-----|---------+ +-----|---------+ - * | | - * V V - * +---------------+ +---------------+ - * | u_header | | u_header | - * | uh_alt_next | | uh_alt_next | - * b_u_newhead --->| uh_alt_prev | | uh_alt_prev | - * | uh_prev | | uh_prev | - * +-----|---------+ +-----|---------+ - * | | - * V V - * NULL +---------------+ +---------------+ - * | u_header | | u_header | - * | uh_alt_next ---->| uh_alt_next | - * | uh_alt_prev |<------ uh_alt_prev | - * | uh_prev | | uh_prev | - * +-----|---------+ +-----|---------+ - * | | - * etc. etc. + * +---------------+ +---------------+ + * b_u_oldhead --->| u_header | | u_header | + * | uh_alt_next ---->| uh_alt_next ----> NULL + * NULL <----- uh_alt_prev |<------ uh_alt_prev | + * | uh_prev | | uh_prev | + * +-----|---------+ +-----|---------+ + * | | + * V V + * +---------------+ +---------------+ + * | u_header | | u_header | + * | uh_alt_next | | uh_alt_next | + * b_u_newhead --->| uh_alt_prev | | uh_alt_prev | + * | uh_prev | | uh_prev | + * +-----|---------+ +-----|---------+ + * | | + * V V + * NULL +---------------+ +---------------+ + * | u_header | | u_header | + * | uh_alt_next ---->| uh_alt_next | + * | uh_alt_prev |<------ uh_alt_prev | + * | uh_prev | | uh_prev | + * +-----|---------+ +-----|---------+ + * | | + * etc. etc. * * * All data is allocated and will all be freed when the buffer is unloaded. diff --git a/src/nvim/version.c b/src/nvim/version.c index cd31143be7..bc10187aa3 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -178,7 +178,7 @@ static int included_patches[] = { // 1115 NA // 1114, // 1113, - // 1112, + 1112, // 1111, // 1110, // 1109 NA @@ -297,7 +297,7 @@ static int included_patches[] = { // 996 NA // 995 NA // 994 NA - // 993, + // 993 NA // 992 NA 991, // 990 NA @@ -316,12 +316,12 @@ static int included_patches[] = { 977, // 976 NA 975, - // 974, + 974, // 973, 972, // 971 NA // 970 NA - // 969, + // 969 NA // 968 NA // 967 NA // 966 NA @@ -334,20 +334,20 @@ static int included_patches[] = { // 959 NA // 958, // 957, - // 956, + // 956 NA 955, // 954 NA 953, - // 952, + 952, // 951, 950, 949, // 948 NA - // 947, + // 947 NA 946, 945, 944, - // 943, + // 943 NA // 942, // 941, // 940 NA @@ -365,7 +365,7 @@ static int included_patches[] = { // 928 NA // 927 NA 926, - // 925, + 925, // 924 NA // 923 NA 922, @@ -376,17 +376,17 @@ static int included_patches[] = { // 917 NA 916, 915, - // 914, + // 914 NA // 913 NA 912, // 911 NA // 910 NA - // 909, + // 909 NA // 908 NA // 907 NA // 906 NA - // 905, - // 904, + // 905 NA + // 904 NA 903, // 902 NA 901, @@ -398,7 +398,7 @@ static int included_patches[] = { 895, // 894 NA 893, - // 892, + // 892 NA 891, // 890 NA // 889, @@ -433,7 +433,7 @@ static int included_patches[] = { // 860 NA 859, 858, - // 857, + 857, 856, // 855 NA // 854 NA diff --git a/src/nvim/vim.h b/src/nvim/vim.h index 762d349470..545b903d2f 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -27,12 +27,6 @@ Error: configure did not run properly.Check auto/config.log. # endif #endif - -/* Can't use "PACKAGE" here, conflicts with a Perl include file. */ -#ifndef VIMPACKAGE -# define VIMPACKAGE "nvim" -#endif - #include "nvim/os/os_defs.h" /* bring lots of system header files */ /// length of a buffer to store a number in ASCII (64 bits binary + NUL) diff --git a/src/nvim/window.c b/src/nvim/window.c index 4a2912d724..1b8c953596 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -248,7 +248,7 @@ newwindow: /* First create a new tab with the window, then go back to * the old tab and close the window there. */ wp = curwin; - if (win_new_tabpage((int)Prenum) == OK + if (win_new_tabpage((int)Prenum, NULL) == OK && valid_tabpage(oldtab)) { newtab = curtab; goto_tabpage_tp(oldtab, TRUE, TRUE); @@ -2952,14 +2952,15 @@ void free_tabpage(tabpage_T *tp) xfree(tp); } -/* - * Create a new Tab page with one window. - * It will edit the current buffer, like after ":split". - * When "after" is 0 put it just after the current Tab page. - * Otherwise put it just before tab page "after". - * Return FAIL or OK. - */ -int win_new_tabpage(int after) +/// Create a new tabpage with one window. +/// +/// It will edit the current buffer, like after :split. +/// +/// @param after Put new tabpage after tabpage "after", or after the current +/// tabpage in case of 0. +/// @param filename Will be passed to apply_autocmds(). +/// @return Was the new tabpage created successfully? FAIL or OK. +int win_new_tabpage(int after, char_u *filename) { tabpage_T *tp = curtab; tabpage_T *newtp; @@ -2999,10 +3000,12 @@ int win_new_tabpage(int after) newtp->tp_topframe = topframe; last_status(FALSE); - redraw_all_later(CLEAR); - apply_autocmds(EVENT_WINENTER, NULL, NULL, FALSE, curbuf); - apply_autocmds(EVENT_TABENTER, NULL, NULL, FALSE, curbuf); + + apply_autocmds(EVENT_TABNEW, filename, filename, false, curbuf); + apply_autocmds(EVENT_WINENTER, NULL, NULL, false, curbuf); + apply_autocmds(EVENT_TABENTER, NULL, NULL, false, curbuf); + return OK; } @@ -3023,7 +3026,7 @@ int may_open_tabpage(void) if (n != 0) { cmdmod.tab = 0; /* reset it to avoid doing it twice */ postponed_split_tab = 0; - return win_new_tabpage(n); + return win_new_tabpage(n, NULL); } return FAIL; } @@ -3047,9 +3050,11 @@ int make_tabpages(int maxcount) */ block_autocmds(); - for (todo = count - 1; todo > 0; --todo) - if (win_new_tabpage(0) == FAIL) + for (todo = count - 1; todo > 0; --todo) { + if (win_new_tabpage(0, NULL) == FAIL) { break; + } + } unblock_autocmds(); |