diff options
Diffstat (limited to 'src')
47 files changed, 1387 insertions, 1103 deletions
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/buffer.c b/src/nvim/buffer.c index 9806623433..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; } @@ -1741,12 +1742,15 @@ int buflist_findpat( int toggledollar; if (pattern_end == pattern + 1 && (*pattern == '%' || *pattern == '#')) { - if (*pattern == '%') + if (*pattern == '%') { match = curbuf->b_fnum; - else + } else { match = curwin->w_alt_fnum; - if (diffmode && !diff_mode_buf(buflist_findnr(match))) + } + buf_T *found_buf = buflist_findnr(match); + if (diffmode && !(found_buf && diff_mode_buf(found_buf))) { match = -1; + } } /* * Try four ways of matching a listed buffer: @@ -4867,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 709ff3dd0d..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" @@ -592,71 +599,72 @@ struct file_buffer { int b_p_scriptID[BV_COUNT]; /* SIDs for buffer-local options */ - int b_p_ai; /* 'autoindent' */ - int b_p_ai_nopaste; /* b_p_ai saved for paste mode */ - char_u *b_p_bkc; ///< 'backupcopy' - unsigned int b_bkc_flags; ///< flags for 'backupcopy' - int b_p_ci; /* 'copyindent' */ - int b_p_bin; /* 'binary' */ - int b_p_bomb; /* 'bomb' */ - char_u *b_p_bh; /* 'bufhidden' */ - char_u *b_p_bt; /* 'buftype' */ - int b_p_bl; /* 'buflisted' */ - int b_p_cin; /* 'cindent' */ - char_u *b_p_cino; /* 'cinoptions' */ - char_u *b_p_cink; /* 'cinkeys' */ - char_u *b_p_cinw; /* 'cinwords' */ - char_u *b_p_com; /* 'comments' */ - char_u *b_p_cms; /* 'commentstring' */ - char_u *b_p_cpt; /* 'complete' */ - char_u *b_p_cfu; /* 'completefunc' */ - char_u *b_p_ofu; /* 'omnifunc' */ - int b_p_eol; /* 'endofline' */ - int b_p_fixeol; /* 'fixendofline' */ - int b_p_et; /* 'expandtab' */ - int b_p_et_nobin; /* b_p_et saved for binary mode */ - char_u *b_p_fenc; /* 'fileencoding' */ - char_u *b_p_ff; /* 'fileformat' */ - char_u *b_p_ft; /* 'filetype' */ - char_u *b_p_fo; /* 'formatoptions' */ - char_u *b_p_flp; /* 'formatlistpat' */ - int b_p_inf; /* 'infercase' */ - char_u *b_p_isk; /* 'iskeyword' */ - char_u *b_p_def; /* 'define' local value */ - char_u *b_p_inc; /* 'include' */ - char_u *b_p_inex; /* 'includeexpr' */ - uint32_t b_p_inex_flags; /* flags for 'includeexpr' */ - char_u *b_p_inde; /* 'indentexpr' */ - uint32_t b_p_inde_flags; /* flags for 'indentexpr' */ - char_u *b_p_indk; /* 'indentkeys' */ - char_u *b_p_fex; /* 'formatexpr' */ - uint32_t b_p_fex_flags; /* flags for 'formatexpr' */ - char_u *b_p_kp; /* 'keywordprg' */ - int b_p_lisp; /* 'lisp' */ - char_u *b_p_mps; /* 'matchpairs' */ - int b_p_ml; /* 'modeline' */ - int b_p_ml_nobin; /* b_p_ml saved for binary mode */ - int b_p_ma; /* 'modifiable' */ - char_u *b_p_nf; /* 'nrformats' */ - int b_p_pi; /* 'preserveindent' */ - char_u *b_p_qe; /* 'quoteescape' */ - int b_p_ro; /* 'readonly' */ - long b_p_sw; /* 'shiftwidth' */ - int b_p_si; /* 'smartindent' */ - long b_p_sts; /* 'softtabstop' */ - long b_p_sts_nopaste; /* b_p_sts saved for paste mode */ - char_u *b_p_sua; /* 'suffixesadd' */ - bool b_p_swf; /* 'swapfile' */ - long b_p_smc; /* 'synmaxcol' */ - char_u *b_p_syn; /* 'syntax' */ - long b_p_ts; /* 'tabstop' */ - long b_p_tw; /* 'textwidth' */ - long b_p_tw_nobin; /* b_p_tw saved for binary mode */ - long b_p_tw_nopaste; /* b_p_tw saved for paste mode */ - long b_p_wm; /* 'wrapmargin' */ - long b_p_wm_nobin; /* b_p_wm saved for binary mode */ - long b_p_wm_nopaste; /* b_p_wm saved for paste mode */ - char_u *b_p_keymap; /* 'keymap' */ + int b_p_ai; ///< 'autoindent' + int b_p_ai_nopaste; ///< b_p_ai saved for paste mode + char_u *b_p_bkc; ///< 'backupco + unsigned int b_bkc_flags; ///< flags for 'backupco + int b_p_ci; ///< 'copyindent' + int b_p_bin; ///< 'binary' + int b_p_bomb; ///< 'bomb' + char_u *b_p_bh; ///< 'bufhidden' + char_u *b_p_bt; ///< 'buftype' + int b_p_bl; ///< 'buflisted' + int b_p_cin; ///< 'cindent' + char_u *b_p_cino; ///< 'cinoptions' + char_u *b_p_cink; ///< 'cinkeys' + char_u *b_p_cinw; ///< 'cinwords' + char_u *b_p_com; ///< 'comments' + char_u *b_p_cms; ///< 'commentstring' + char_u *b_p_cpt; ///< 'complete' + char_u *b_p_cfu; ///< 'completefunc' + char_u *b_p_ofu; ///< 'omnifunc' + int b_p_eol; ///< 'endofline' + int b_p_fixeol; ///< 'fixendofline' + int b_p_et; ///< 'expandtab' + int b_p_et_nobin; ///< b_p_et saved for binary mode + int b_p_et_nopaste; ///< b_p_et saved for paste mode + char_u *b_p_fenc; ///< 'fileencoding' + char_u *b_p_ff; ///< 'fileformat' + char_u *b_p_ft; ///< 'filetype' + char_u *b_p_fo; ///< 'formatoptions' + char_u *b_p_flp; ///< 'formatlistpat' + int b_p_inf; ///< 'infercase' + char_u *b_p_isk; ///< 'iskeyword' + char_u *b_p_def; ///< 'define' local value + char_u *b_p_inc; ///< 'include' + char_u *b_p_inex; ///< 'includeexpr' + uint32_t b_p_inex_flags; ///< flags for 'includeexpr' + char_u *b_p_inde; ///< 'indentexpr' + uint32_t b_p_inde_flags; ///< flags for 'indentexpr' + char_u *b_p_indk; ///< 'indentkeys' + char_u *b_p_fex; ///< 'formatexpr' + uint32_t b_p_fex_flags; ///< flags for 'formatexpr' + char_u *b_p_kp; ///< 'keywordprg' + int b_p_lisp; ///< 'lisp' + char_u *b_p_mps; ///< 'matchpairs' + int b_p_ml; ///< 'modeline' + int b_p_ml_nobin; ///< b_p_ml saved for binary mode + int b_p_ma; ///< 'modifiable' + char_u *b_p_nf; ///< 'nrformats' + int b_p_pi; ///< 'preserveindent' + char_u *b_p_qe; ///< 'quoteescape' + int b_p_ro; ///< 'readonly' + long b_p_sw; ///< 'shiftwidth' + int b_p_si; ///< 'smartindent' + long b_p_sts; ///< 'softtabstop' + long b_p_sts_nopaste; ///< b_p_sts saved for paste mode + char_u *b_p_sua; ///< 'suffixesadd' + bool b_p_swf; ///< 'swapfile' + long b_p_smc; ///< 'synmaxcol' + char_u *b_p_syn; ///< 'syntax' + long b_p_ts; ///< 'tabstop' + long b_p_tw; ///< 'textwidth' + long b_p_tw_nobin; ///< b_p_tw saved for binary mode + long b_p_tw_nopaste; ///< b_p_tw saved for paste mode + long b_p_wm; ///< 'wrapmargin' + long b_p_wm_nobin; ///< b_p_wm saved for binary mode + long b_p_wm_nopaste; ///< b_p_wm saved for paste mode + char_u *b_p_keymap; ///< 'keymap' /* local values for options which are normally global */ char_u *b_p_gp; /* 'grepprg' local value */ @@ -753,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 d3b556f669..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; @@ -4947,15 +4943,10 @@ int get_literal(void) return cc; } -/* - * Insert character, taking care of special keys and mod_mask - */ -static void -insert_special ( - int c, - int allow_modmask, - int ctrlv /* c was typed after CTRL-V */ -) +/// Insert character, taking care of special keys and mod_mask +/// +/// @param ctrlv `c` was typed after CTRL-V +static void insert_special(int c, int allow_modmask, int ctrlv) { char_u *p; int len; @@ -4967,6 +4958,9 @@ insert_special ( * Only use mod_mask for special keys, to avoid things like <S-Space>, * unless 'allow_modmask' is TRUE. */ + if (mod_mask & MOD_MASK_CMD) { // Command-key never produces a normal key. + allow_modmask = true; + } if (IS_SPECIAL(c) || (mod_mask && allow_modmask)) { p = get_special_key_name(c, mod_mask); len = (int)STRLEN(p); @@ -8522,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 327d0bf637..31175773f0 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3580,9 +3580,10 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) type = TYPE_SEQUAL; break; case 'i': if (p[1] == 's') { - if (p[2] == 'n' && p[3] == 'o' && p[4] == 't') + if (p[2] == 'n' && p[3] == 'o' && p[4] == 't') { len = 5; - if (!vim_isIDc(p[len])) { + } + if (!isalnum(p[len]) && p[len] != '_') { type = len == 2 ? TYPE_EQUAL : TYPE_NEQUAL; type_is = TRUE; } @@ -7323,10 +7324,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 }, @@ -10697,11 +10698,11 @@ getwinvar ( int off /* 1 for gettabwinvar() */ ) { - win_T *win, *oldcurwin; - char_u *varname; - dictitem_T *v; - tabpage_T *tp = NULL; - tabpage_T *oldtabpage = NULL; + win_T *win, *oldcurwin; + char_u *varname; + dictitem_T *v; + tabpage_T *tp = NULL; + tabpage_T *oldtabpage = NULL; bool done = false; if (off == 1) @@ -10716,12 +10717,16 @@ getwinvar ( rettv->vval.v_string = NULL; if (win != NULL && varname != NULL) { - /* Set curwin to be our win, temporarily. Also set the tabpage, - * otherwise the window is not valid. */ - if (switch_win(&oldcurwin, &oldtabpage, win, tp, TRUE) == OK) { - if (*varname == '&') { /* window-local-option */ - if (get_option_tv(&varname, rettv, 1) == OK) + // Set curwin to be our win, temporarily. Also set the tabpage, + // otherwise the window is not valid. Only do this when needed, + // autocommands get blocked. + bool need_switch_win = tp != curtab || win != curwin; + if (!need_switch_win + || switch_win(&oldcurwin, &oldtabpage, win, tp, true) == OK) { + if (*varname == '&') { // window-local-option + if (get_option_tv(&varname, rettv, 1) == OK) { done = true; + } } else { // Look up the variable. // Let getwinvar({nr}, "") return the "w:" dictionary. @@ -10733,8 +10738,10 @@ getwinvar ( } } - /* restore previous notion of curwin */ - restore_win(oldcurwin, oldtabpage, TRUE); + if (need_switch_win) { + // restore previous notion of curwin + restore_win(oldcurwin, oldtabpage, true); + } } if (!done && argvars[off + 2].v_type != VAR_UNKNOWN) @@ -15208,14 +15215,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; @@ -15224,7 +15243,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 */ @@ -15232,9 +15251,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_UNKNOWN) { + 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; + } } } @@ -15248,8 +15278,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); + } } /* @@ -15385,7 +15416,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); } /* @@ -15559,26 +15590,32 @@ static void setwinvar(typval_T *argvars, typval_T *rettv, int off) varname = get_tv_string_chk(&argvars[off + 1]); varp = &argvars[off + 2]; - if (win != NULL && varname != NULL && varp != NULL - && switch_win(&save_curwin, &save_curtab, win, tp, TRUE) == OK) { - if (*varname == '&') { - long numval; - char_u *strval; - int error = FALSE; - - ++varname; - numval = get_tv_number_chk(varp, &error); - strval = get_tv_string_buf_chk(varp, nbuf); - if (!error && strval != NULL) - set_option_value(varname, numval, strval, OPT_LOCAL); - } else { - winvarname = xmalloc(STRLEN(varname) + 3); - STRCPY(winvarname, "w:"); - STRCPY(winvarname + 2, varname); - set_var(winvarname, varp, TRUE); - xfree(winvarname); + if (win != NULL && varname != NULL && varp != NULL) { + bool need_switch_win = tp != curtab || win != curwin; + if (!need_switch_win + || switch_win(&save_curwin, &save_curtab, win, tp, true) == OK) { + if (*varname == '&') { + long numval; + char_u *strval; + int error = false; + + ++varname; + numval = get_tv_number_chk(varp, &error); + strval = get_tv_string_buf_chk(varp, nbuf); + if (!error && strval != NULL) { + set_option_value(varname, numval, strval, OPT_LOCAL); + } + } else { + winvarname = xmalloc(STRLEN(varname) + 3); + STRCPY(winvarname, "w:"); + STRCPY(winvarname + 2, varname); + set_var(winvarname, varp, true); + xfree(winvarname); + } + } + if (need_switch_win) { + restore_win(save_curwin, save_curtab, true); } - restore_win(save_curwin, save_curtab, TRUE); } } @@ -18091,23 +18128,23 @@ 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; + if (val != NULL) { + ++val->dv_refcount; + // Set readonly + size_t todo = 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; } - - --todo; - HI2DI(hi)->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; } - - vimvars[idx].vv_dict = val; - ++val->dv_refcount; } /* @@ -20913,14 +20950,18 @@ call_user_func ( save_sourcing_name = sourcing_name; save_sourcing_lnum = sourcing_lnum; sourcing_lnum = 1; - sourcing_name = xmalloc((save_sourcing_name == NULL ? 0 : STRLEN(save_sourcing_name)) - + STRLEN(fp->uf_name) + 13); + // need space for function name + ("function " + 3) or "[number]" + size_t len = (save_sourcing_name == NULL ? 0 : STRLEN(save_sourcing_name)) + + STRLEN(fp->uf_name) + 20; + sourcing_name = xmalloc(len); { if (save_sourcing_name != NULL - && STRNCMP(save_sourcing_name, "function ", 9) == 0) - sprintf((char *)sourcing_name, "%s..", save_sourcing_name); - else + && STRNCMP(save_sourcing_name, "function ", 9) == 0) { + vim_snprintf((char *)sourcing_name, len, "%s[%zu]..", + save_sourcing_name, save_sourcing_lnum); + } else { STRCPY(sourcing_name, "function "); + } cat_func_name(sourcing_name + STRLEN(sourcing_name), fp); if (p_verbose >= 12) { @@ -22186,7 +22227,6 @@ static void on_process_exit(Process *proc, int status, void *d) char msg[22]; snprintf(msg, sizeof msg, "\r\n[Process exited %d]", proc->status); terminal_close(data->term, msg); - apply_autocmds(EVENT_TERMCLOSE, NULL, NULL, false, curbuf); } if (data->status_ptr) { diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 4d62dd0ff9..ad21e71c51 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -4779,6 +4779,7 @@ void ex_helptags(exarg_T *eap) WILD_LIST_NOTFOUND|WILD_SILENT, WILD_EXPAND_FREE); if (dirname == NULL || !os_isdir(dirname)) { EMSG2(_("E150: Not a directory: %s"), eap->arg); + xfree(dirname); return; } @@ -4876,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); @@ -4907,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_docmd.c b/src/nvim/ex_docmd.c index dfae2b849d..3a24f194c1 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -381,15 +381,14 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, suppress_errthrow = FALSE; } - /* - * If requested, store and reset the global values controlling the - * exception handling (used when debugging). Otherwise clear it to avoid - * a bogus compiler warning when the optimizer uses inline functions... - */ - if (flags & DOCMD_EXCRESET) + // If requested, store and reset the global values controlling the + // exception handling (used when debugging). Otherwise clear it to avoid + // a bogus compiler warning when the optimizer uses inline functions... + if (flags & DOCMD_EXCRESET) { save_dbg_stuff(&debug_saved); - else - memset(&debug_saved, 0, 1); + } else { + memset(&debug_saved, 0, sizeof(debug_saved)); + } initial_trylevel = trylevel; @@ -2679,16 +2678,25 @@ set_one_cmd_context ( p = cmd + 1; } else { p = cmd; - while (ASCII_ISALPHA(*p) || *p == '*') /* Allow * wild card */ - ++p; - /* check for non-alpha command */ - if (p == cmd && vim_strchr((char_u *)"@*!=><&~#", *p) != NULL) - ++p; - /* for python 3.x: ":py3*" commands completion */ + while (ASCII_ISALPHA(*p) || *p == '*') { // Allow * wild card + p++; + } + // a user command may contain digits + if (ASCII_ISUPPER(cmd[0])) { + while (ASCII_ISALNUM(*p) || *p == '*') { + p++; + } + } + // for python 3.x: ":py3*" commands completion if (cmd[0] == 'p' && cmd[1] == 'y' && p == cmd + 2 && *p == '3') { - ++p; - while (ASCII_ISALPHA(*p) || *p == '*') - ++p; + p++; + while (ASCII_ISALPHA(*p) || *p == '*') { + p++; + } + } + // check for non-alpha command + if (p == cmd && vim_strchr((char_u *)"@*!=><&~#", *p) != NULL) { + p++; } len = (int)(p - cmd); @@ -2702,9 +2710,11 @@ set_one_cmd_context ( (size_t)len) == 0) break; - if (cmd[0] >= 'A' && cmd[0] <= 'Z') - while (ASCII_ISALNUM(*p) || *p == '*') /* Allow * wild card */ - ++p; + if (cmd[0] >= 'A' && cmd[0] <= 'Z') { + while (ASCII_ISALNUM(*p) || *p == '*') { // Allow * wild card + p++; + } + } } /* @@ -4310,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); } /* @@ -4318,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) @@ -8774,19 +8784,18 @@ static int ses_do_frame(frame_T *fr) return FALSE; } -/* - * Return non-zero if window "wp" is to be stored in the Session. - */ +/// Return non-zero if window "wp" is to be stored in the Session. static int ses_do_win(win_T *wp) { if (wp->w_buffer->b_fname == NULL - /* When 'buftype' is "nofile" can't restore the window contents. */ - || bt_nofile(wp->w_buffer) - ) + // When 'buftype' is "nofile" can't restore the window contents. + || (!wp->w_buffer->terminal && bt_nofile(wp->w_buffer))) { return ssop_flags & SSOP_BLANK; - if (wp->w_buffer->b_help) + } + if (wp->w_buffer->b_help) { return ssop_flags & SSOP_HELP; - return TRUE; + } + return true; } /* @@ -9352,52 +9361,55 @@ static void ex_nohlsearch(exarg_T *eap) redraw_all_later(SOME_VALID); } -/* - * ":[N]match {group} {pattern}" - * Sets nextcmd to the start of the next command, if any. Also called when - * skipping commands to find the next command. - */ +// ":[N]match {group} {pattern}" +// Sets nextcmd to the start of the next command, if any. Also called when +// skipping commands to find the next command. static void ex_match(exarg_T *eap) { - char_u *p; - char_u *g = NULL; - char_u *end; + char_u *p; + char_u *g = NULL; + char_u *end; int c; int id; - if (eap->line2 <= 3) + if (eap->line2 <= 3) { id = eap->line2; - else { + } else { EMSG(e_invcmd); return; } - /* First clear any old pattern. */ - if (!eap->skip) - match_delete(curwin, id, FALSE); + // First clear any old pattern. + if (!eap->skip) { + match_delete(curwin, id, false); + } - if (ends_excmd(*eap->arg)) + if (ends_excmd(*eap->arg)) { end = eap->arg; - else if ((STRNICMP(eap->arg, "none", 4) == 0 - && (ascii_iswhite(eap->arg[4]) || ends_excmd(eap->arg[4])))) + } else if ((STRNICMP(eap->arg, "none", 4) == 0 + && (ascii_iswhite(eap->arg[4]) || ends_excmd(eap->arg[4])))) { end = eap->arg + 4; - else { + } else { p = skiptowhite(eap->arg); - if (!eap->skip) + if (!eap->skip) { g = vim_strnsave(eap->arg, (int)(p - eap->arg)); + } p = skipwhite(p); if (*p == NUL) { - /* There must be two arguments. */ + // There must be two arguments. + xfree(g); EMSG2(_(e_invarg2), eap->arg); return; } - end = skip_regexp(p + 1, *p, TRUE, NULL); + end = skip_regexp(p + 1, *p, true, NULL); if (!eap->skip) { if (*end != NUL && !ends_excmd(*skipwhite(end + 1))) { + xfree(g); eap->errmsg = e_trailing; return; } if (*end != *p) { + xfree(g); EMSG2(_(e_invarg2), p); return; } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index d015f6b4a0..39bff9b2ad 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -2957,20 +2957,37 @@ ExpandOne ( } } - /* Find longest common part */ + // Find longest common part if (mode == WILD_LONGEST && xp->xp_numfiles > 0) { size_t len; - for (len = 0; xp->xp_files[0][len]; ++len) { - for (i = 0; i < xp->xp_numfiles; ++i) { + size_t mb_len = 1; + int c0; + int ci; + + for (len = 0; xp->xp_files[0][len]; len += mb_len) { + if (has_mbyte) { + mb_len = (* mb_ptr2len)(&xp->xp_files[0][len]); + c0 = (* mb_ptr2char)(&xp->xp_files[0][len]); + } else { + c0 = xp->xp_files[0][len]; + } + for (i = 1; i < xp->xp_numfiles; ++i) { + if (has_mbyte) { + ci =(* mb_ptr2char)(&xp->xp_files[i][len]); + } else { + ci = xp->xp_files[i][len]; + } + if (p_fic && (xp->xp_context == EXPAND_DIRECTORIES || xp->xp_context == EXPAND_FILES || xp->xp_context == EXPAND_SHELLCMD || xp->xp_context == EXPAND_BUFFERS)) { - if (TOLOWER_LOC(xp->xp_files[i][len]) != - TOLOWER_LOC(xp->xp_files[0][len])) + if (vim_tolower(c0) != vim_tolower(ci)) { break; - } else if (xp->xp_files[i][len] != xp->xp_files[0][len]) + } + } else if (c0 != ci) { break; + } } if (i < xp->xp_numfiles) { if (!(options & WILD_NO_BEEP)) { @@ -2979,8 +2996,9 @@ ExpandOne ( break; } } + ss = (char_u *)xstrndup((char *)xp->xp_files[0], len); - findex = -1; /* next p_wc gets first one */ + findex = -1; // next p_wc gets first one } // Concatenate all matching names diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index b213a42c52..2929790ebf 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -484,28 +484,21 @@ vim_findfile_init ( len = (int)(p - search_ctx->ffsc_fix_path) - 1; STRNCAT(ff_expand_buffer, search_ctx->ffsc_fix_path, len); add_pathsep((char *)ff_expand_buffer); - } else + } else { len = (int)STRLEN(search_ctx->ffsc_fix_path); + } if (search_ctx->ffsc_wc_path != NULL) { wc_path = vim_strsave(search_ctx->ffsc_wc_path); temp = xmalloc(STRLEN(search_ctx->ffsc_wc_path) + STRLEN(search_ctx->ffsc_fix_path + len) + 1); - } - - if (temp == NULL || wc_path == NULL) { - xfree(buf); - xfree(temp); + STRCPY(temp, search_ctx->ffsc_fix_path + len); + STRCAT(temp, search_ctx->ffsc_wc_path); + xfree(search_ctx->ffsc_wc_path); xfree(wc_path); - goto error_return; + search_ctx->ffsc_wc_path = temp; } - - STRCPY(temp, search_ctx->ffsc_fix_path + len); - STRCAT(temp, search_ctx->ffsc_wc_path); - xfree(search_ctx->ffsc_wc_path); - xfree(wc_path); - search_ctx->ffsc_wc_path = temp; } xfree(buf); } @@ -1046,41 +1039,44 @@ static ff_visited_list_hdr_T *ff_get_visited_list(char_u *filename, ff_visited_l return retptr; } -/* - * check if two wildcard paths are equal. Returns TRUE or FALSE. - * They are equal if: - * - both paths are NULL - * - they have the same length - * - char by char comparison is OK - * - the only differences are in the counters behind a '**', so - * '**\20' is equal to '**\24' - */ -static int ff_wc_equal(char_u *s1, char_u *s2) +// Check if two wildcard paths are equal. +// They are equal if: +// - both paths are NULL +// - they have the same length +// - char by char comparison is OK +// - the only differences are in the counters behind a '**', so +// '**\20' is equal to '**\24' +static bool ff_wc_equal(char_u *s1, char_u *s2) { - int i; + int i, j; + int c1 = NUL; + int c2 = NUL; int prev1 = NUL; int prev2 = NUL; - if (s1 == s2) - return TRUE; - - if (s1 == NULL || s2 == NULL) - return FALSE; + if (s1 == s2) { + return true; + } - if (STRLEN(s1) != STRLEN(s2)) - return FAIL; + if (s1 == NULL || s2 == NULL) { + return false; + } - for (i = 0; s1[i] != NUL && s2[i] != NUL; i += MB_PTR2LEN(s1 + i)) { - int c1 = PTR2CHAR(s1 + i); - int c2 = PTR2CHAR(s2 + i); + for (i = 0, j = 0; s1[i] != NUL && s2[j] != NUL;) { + c1 = PTR2CHAR(s1 + i); + c2 = PTR2CHAR(s2 + j); if ((p_fic ? vim_tolower(c1) != vim_tolower(c2) : c1 != c2) - && (prev1 != '*' || prev2 != '*')) - return FAIL; + && (prev1 != '*' || prev2 != '*')) { + return false; + } prev2 = prev1; prev1 = c1; + + i += MB_PTR2LEN(s1 + i); + j += MB_PTR2LEN(s2 + j); } - return TRUE; + return s1[i] == s2[j]; } /* @@ -1111,10 +1107,11 @@ static int ff_check_visited(ff_visited_T **visited_list, char_u *fname, char_u * if ((url && fnamecmp(vp->ffv_fname, ff_expand_buffer) == 0) || (!url && vp->file_id_valid && os_fileid_equal(&(vp->file_id), &file_id))) { - /* are the wildcard parts equal */ - if (ff_wc_equal(vp->ffv_wc_path, wc_path) == TRUE) - /* already visited */ + // are the wildcard parts equal + if (ff_wc_equal(vp->ffv_wc_path, wc_path)) { + // already visited return FAIL; + } } } diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 89d22ad811..437495faa4 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1380,13 +1380,15 @@ int vgetc(void) } else { mod_mask = 0x0; last_recorded_len = 0; - for (;; ) { /* this is done twice if there are modifiers */ - if (mod_mask) { /* no mapping after modifier has been read */ + for (;; ) { // this is done twice if there are modifiers + bool did_inc = false; + if (mod_mask) { // no mapping after modifier has been read ++no_mapping; ++allow_keys; + did_inc = true; // mod_mask may change value } - c = vgetorpeek(TRUE); - if (mod_mask) { + c = vgetorpeek(true); + if (did_inc) { --no_mapping; --allow_keys; } @@ -2913,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 } /* @@ -3010,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; @@ -3078,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; @@ -3130,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/indent_c.c b/src/nvim/indent_c.c index 340287499e..17fadc4bfd 100644 --- a/src/nvim/indent_c.c +++ b/src/nvim/indent_c.c @@ -69,23 +69,33 @@ find_start_comment ( /* XXX */ return pos; } -/* - * Find the start of a comment or raw string, not knowing if we are in a - * comment or raw string right now. - * Search starts at w_cursor.lnum and goes backwards. - * Return NULL when not inside a comment or raw string. - * "CORS" -> Comment Or Raw String - */ +/// Find the start of a comment or raw string, not knowing if we are in a +/// comment or raw string right now. +/// Search starts at w_cursor.lnum and goes backwards. +/// +/// @returns NULL when not inside a comment or raw string. +/// +/// @note "CORS" -> Comment Or Raw String static pos_T *ind_find_start_CORS(void) -{ /* XXX */ - pos_T *comment_pos = find_start_comment(curbuf->b_ind_maxcomment); - pos_T *rs_pos = find_start_rawstring(curbuf->b_ind_maxcomment); - - /* If comment_pos is before rs_pos the raw string is inside the comment. - * If rs_pos is before comment_pos the comment is inside the raw string. */ - if (comment_pos == NULL || (rs_pos != NULL && lt(*rs_pos, *comment_pos))) - return rs_pos; - return comment_pos; +{ + // XXX + static pos_T comment_pos_copy; + + pos_T *comment_pos = find_start_comment(curbuf->b_ind_maxcomment); + if (comment_pos != NULL) { + // Need to make a copy of the static pos in findmatchlimit(), + // calling find_start_rawstring() may change it. + comment_pos_copy = *comment_pos; + comment_pos = &comment_pos_copy; + } + pos_T *rs_pos = find_start_rawstring(curbuf->b_ind_maxcomment); + + // If comment_pos is before rs_pos the raw string is inside the comment. + // If rs_pos is before comment_pos the comment is inside the raw string. + if (comment_pos == NULL || (rs_pos != NULL && lt(*rs_pos, *comment_pos))) { + return rs_pos; + } + return comment_pos; } /* @@ -847,13 +857,27 @@ static int cin_isfuncdecl(char_u **sp, linenr_T first_lnum, linenr_T min_lnum) return FALSE; while (*s && *s != '(' && *s != ';' && *s != '\'' && *s != '"') { - if (cin_iscomment(s)) /* ignore comments */ + // ignore comments + if (cin_iscomment(s)) { s = cin_skipcomment(s); - else - ++s; + } else if (*s == ':') { + if (*(s + 1) == ':') { + s += 2; + } else { + // To avoid a mistake in the following situation: + // A::A(int a, int b) + // : a(0) // <--not a function decl + // , b(0) + // {... + return false; + } + } else { + s++; + } + } + if (*s != '(') { + return false; // ';', ' or " before any () or no '(' } - if (*s != '(') - return FALSE; /* ';', ' or " before any () or no '(' */ while (*s && *s != ';' && *s != '\'' && *s != '"') { if (*s == ')' && cin_nocode(s + 1)) { @@ -1122,13 +1146,21 @@ static int cin_is_cpp_baseclass(cpp_baseclass_cache_T *cached) { pos->lnum = lnum; line = ml_get(lnum); - s = cin_skipcomment(line); + s = line; for (;; ) { if (*s == NUL) { - if (lnum == curwin->w_cursor.lnum) + if (lnum == curwin->w_cursor.lnum) { break; - /* Continue in the cursor line. */ + } + // Continue in the cursor line. line = ml_get(++lnum); + s = line; + } + if (s == line) { + // don't recognize "case (foo):" as a baseclass */ + if (cin_iscase(s, false)) { + break; + } s = cin_skipcomment(line); if (*s == NUL) continue; @@ -2707,7 +2739,8 @@ int get_c_indent(void) if (terminated == 0 || (lookfor != LOOKFOR_UNTERM && terminated == ',')) { - if (*skipwhite(l) == '[' || l[STRLEN(l) - 1] == '[') { + if (lookfor != LOOKFOR_ENUM_OR_INIT + && (*skipwhite(l) == '[' || l[STRLEN(l) - 1] == '[')) { amount += ind_continuation; } // If we're in the middle of a paren thing, Go back to the line @@ -2915,34 +2948,35 @@ int get_c_indent(void) continue; } - /* Ignore unterminated lines in between, but - * reduce indent. */ - if (amount > cur_amount) + // Ignore unterminated lines in between, but + // reduce indent. + if (amount > cur_amount) { amount = cur_amount; + } } else { - /* - * Found first unterminated line on a row, may - * line up with this line, remember its indent - * 100 + - * -> here; - */ + // Found first unterminated line on a row, may + // line up with this line, remember its indent + // 100 + // NOLINT(whitespace/tab) + // -> here; // NOLINT(whitespace/tab) l = get_cursor_line_ptr(); amount = cur_amount; - if (*skipwhite(l) == ']' || l[STRLEN(l) - 1] == ']') { + + n = (int)STRLEN(l); + if (terminated == ',' + && (*skipwhite(l) == ']' + || (n >=2 && l[n - 2] == ']'))) { break; } - /* - * If previous line ends in ',', check whether we - * are in an initialization or enum - * struct xxx = - * { - * sizeof a, - * 124 }; - * or a normal possible continuation line. - * but only, of no other statement has been found - * yet. - */ + // If previous line ends in ',', check whether we + // are in an initialization or enum + // struct xxx = + // { + // sizeof a, + // 124 }; + // or a normal possible continuation line. + // but only, of no other statement has been found + // yet. if (lookfor == LOOKFOR_INITIAL && terminated == ',') { if (curbuf->b_ind_js) { // Search for a line ending in a comma diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c index 65c808eb06..6c75d8bdf4 100644 --- a/src/nvim/keymap.c +++ b/src/nvim/keymap.c @@ -1,8 +1,3 @@ -/* - * functions that use lookup tables for various things, generally to do with - * special key codes. - */ - #include <assert.h> #include <inttypes.h> #include <limits.h> @@ -39,7 +34,8 @@ static struct modmasktable { {MOD_MASK_MULTI_CLICK, MOD_MASK_2CLICK, (char_u)'2'}, {MOD_MASK_MULTI_CLICK, MOD_MASK_3CLICK, (char_u)'3'}, {MOD_MASK_MULTI_CLICK, MOD_MASK_4CLICK, (char_u)'4'}, - /* 'A' must be the last one */ + {MOD_MASK_CMD, MOD_MASK_CMD, (char_u)'D'}, + // 'A' must be the last one {MOD_MASK_ALT, MOD_MASK_ALT, (char_u)'A'}, {0, 0, NUL} }; @@ -658,9 +654,11 @@ static int extract_modifiers(int key, int *modp) { int modifiers = *modp; - if ((modifiers & MOD_MASK_SHIFT) && ASCII_ISALPHA(key)) { - key = TOUPPER_ASC(key); - modifiers &= ~MOD_MASK_SHIFT; + if (!(modifiers & MOD_MASK_CMD)) { // Command-key is special + if ((modifiers & MOD_MASK_SHIFT) && ASCII_ISALPHA(key)) { + key = TOUPPER_ASC(key); + modifiers &= ~MOD_MASK_SHIFT; + } } if ((modifiers & MOD_MASK_CTRL) && ((key >= '?' && key <= '_') || ASCII_ISALPHA(key))) { diff --git a/src/nvim/keymap.h b/src/nvim/keymap.h index 766362d145..8f9980c6b4 100644 --- a/src/nvim/keymap.h +++ b/src/nvim/keymap.h @@ -112,11 +112,11 @@ #define TO_SPECIAL(a, b) ((a) == KS_SPECIAL ? K_SPECIAL : (a) == \ KS_ZERO ? K_ZERO : TERMCAP2KEY(a, b)) -/* - * Codes for keys that do not have a termcap name. - * - * K_SPECIAL KS_EXTRA KE_xxx - */ +// Codes for keys that do not have a termcap name. +// +// K_SPECIAL KS_EXTRA KE_xxx +// +// Entries must be in the range 0x02-0x7f (see comment at K_SPECIAL). enum key_extra { KE_NAME = 3 /* name of this terminal entry */ @@ -436,11 +436,12 @@ enum key_extra { /* 0x01 cannot be used, because the modifier must be 0x02 or higher */ #define MOD_MASK_SHIFT 0x02 #define MOD_MASK_CTRL 0x04 -#define MOD_MASK_ALT 0x08 /* aka META */ -#define MOD_MASK_META 0x10 /* META when it's different from ALT */ -#define MOD_MASK_2CLICK 0x20 /* use MOD_MASK_MULTI_CLICK */ -#define MOD_MASK_3CLICK 0x40 /* use MOD_MASK_MULTI_CLICK */ -#define MOD_MASK_4CLICK 0x60 /* use MOD_MASK_MULTI_CLICK */ +#define MOD_MASK_ALT 0x08 // aka META +#define MOD_MASK_META 0x10 // META when it's different from ALT +#define MOD_MASK_2CLICK 0x20 // use MOD_MASK_MULTI_CLICK +#define MOD_MASK_3CLICK 0x40 // use MOD_MASK_MULTI_CLICK +#define MOD_MASK_4CLICK 0x60 // use MOD_MASK_MULTI_CLICK +#define MOD_MASK_CMD 0x80 // "super" key (OSX/Mac: command-key) #define MOD_MASK_MULTI_CLICK (MOD_MASK_2CLICK|MOD_MASK_3CLICK| \ MOD_MASK_4CLICK) @@ -451,14 +452,13 @@ enum key_extra { */ #define MAX_KEY_NAME_LEN 25 -/* Maximum length of a special key event as tokens. This includes modifiers. - * The longest event is something like <M-C-S-T-4-LeftDrag> which would be the - * following string of tokens: - * - * <K_SPECIAL> <KS_MODIFIER> bitmask <K_SPECIAL> <KS_EXTRA> <KT_LEFTDRAG>. - * - * This is a total of 6 tokens, and is currently the longest one possible. - */ +// Maximum length of a special key event as tokens. This includes modifiers. +// The longest event is something like <M-C-S-T-4-LeftDrag> which would be the +// following string of tokens: +// +// <K_SPECIAL> <KS_MODIFIER> bitmask <K_SPECIAL> <KS_EXTRA> <KE_LEFTDRAG>. +// +// This is a total of 6 tokens, and is currently the longest one possible. #define MAX_KEY_CODE_LEN 6 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/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/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/move.c b/src/nvim/move.c index eb55397511..ba79c0411a 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -1288,9 +1288,10 @@ void scroll_cursor_top(int min_scroll, int always) * - at least 'scrolloff' lines above and below the cursor */ validate_cheight(); - int used = curwin->w_cline_height; - if (curwin->w_cursor.lnum < curwin->w_topline) + int used = curwin->w_cline_height; // includes filler lines above + if (curwin->w_cursor.lnum < curwin->w_topline) { scrolled = used; + } if (hasFolding(curwin->w_cursor.lnum, &top, &bot)) { --top; @@ -1301,9 +1302,10 @@ void scroll_cursor_top(int min_scroll, int always) } new_topline = top + 1; - /* count filler lines of the cursor window as context */ + // "used" already contains the number of filler lines above, don't add it + // again. + // Hide filler lines above cursor line by adding them to "extra". int extra = diff_check_fill(curwin, curwin->w_cursor.lnum); - used += extra; /* * Check if the lines from "top" to "bot" fit in the window. If they do, @@ -1312,7 +1314,7 @@ void scroll_cursor_top(int min_scroll, int always) while (top > 0) { int i = hasFolding(top, &top, NULL) ? 1 // count one logical line for a sequence of folded lines - : plines(top); + : plines_nofill(top); used += i; if (extra + i <= off && bot < curbuf->b_ml.ml_line_count) { if (hasFolding(bot, NULL, &bot)) 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 e6c5354941..e064d34e09 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -7783,7 +7783,7 @@ static void nv_open(cmdarg_T *cap) n_opencmd(cap); } -// calculate start/end virtual columns for operating in block mode +// Calculate start/end virtual columns for operating in block mode. static void get_op_vcol( oparg_T *oap, colnr_T redo_VIsual_vcol, @@ -7793,7 +7793,8 @@ static void get_op_vcol( colnr_T start; colnr_T end; - if (VIsual_mode != Ctrl_V) { + if (VIsual_mode != Ctrl_V + || (!initial && oap->end.col < curwin->w_width)) { return; } diff --git a/src/nvim/option.c b/src/nvim/option.c index c11e22703e..af7b272467 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -168,11 +168,12 @@ static int p_ml_nobin; static long p_tw_nobin; static long p_wm_nobin; -/* Saved values for when 'paste' is set */ +// Saved values for when 'paste' is set. +static int p_ai_nopaste; +static int p_et_nopaste; +static long p_sts_nopaste; static long p_tw_nopaste; static long p_wm_nopaste; -static long p_sts_nopaste; -static int p_ai_nopaste; typedef struct vimoption { char *fullname; /* full option name */ @@ -710,15 +711,11 @@ void set_init_1(void) /* Must be before option_expand(), because that one needs vim_isIDc() */ didset_options(); - /* Use the current chartab for the generic chartab. */ + // Use the current chartab for the generic chartab. This is not in + // didset_options() because it only depends on 'encoding'. init_spell_chartab(); /* - * initialize the table for 'breakat'. - */ - fill_breakat_flags(); - - /* * Expand environment variables and things like "~" for the defaults. * If option_expand() returns non-NULL the variable is expanded. This can * only happen for non-indirect options. @@ -750,14 +747,8 @@ void set_init_1(void) } } - /* Initialize the highlight_attr[] table. */ - highlight_changed(); - save_file_ff(curbuf); /* Buffer is unchanged */ - /* Parse default for 'wildmode' */ - check_opt_wim(); - /* Detect use of mlterm. * Mlterm is a terminal emulator akin to xterm that has some special * abilities (bidi namely). @@ -767,11 +758,7 @@ void set_init_1(void) if (os_env_exists("MLTERM")) set_option_value((char_u *)"tbidi", 1L, NULL, 0); - /* Parse default for 'fillchars'. */ - (void)set_chars_option(&p_fcs); - - /* Parse default for 'listchars'. */ - (void)set_chars_option(&p_lcs); + didset_options2(); // enc_locale() will try to find the encoding of the current locale. // This will be used when 'default' is used as encoding specifier @@ -1150,9 +1137,12 @@ do_set ( */ arg += 3; if (*arg == '&') { - ++arg; - /* Only for :set command set global value of local options. */ + arg++; + // Only for :set command set global value of local options. set_options_default(OPT_FREE | opt_flags); + didset_options(); + didset_options2(); + redraw_all_later(CLEAR); } else { showoptions(1, opt_flags); did_show = TRUE; @@ -2072,9 +2062,31 @@ static void didset_options(void) (void)spell_check_msm(); (void)spell_check_sps(); (void)compile_cap_prog(curwin->w_s); - /* set cedit_key */ + (void)did_set_spell_option(true); + // set cedit_key (void)check_cedit(); briopt_check(curwin); + // initialize the table for 'breakat'. + fill_breakat_flags(); +} + +// More side effects of setting options. +static void didset_options2(void) +{ + // Initialize the highlight_attr[] table. + (void)highlight_changed(); + + // Parse default for 'clipboard'. + (void)opt_strings_flags(p_cb, p_cb_values, &cb_flags, true); + + // Parse default for 'fillchars'. + (void)set_chars_option(&p_fcs); + + // Parse default for 'listchars'. + (void)set_chars_option(&p_lcs); + + // Parse default for 'wildmode'. + check_opt_wim(); } /* @@ -2853,22 +2865,7 @@ did_set_string_option ( || varp == &(curwin->w_s->b_p_spf)) { // When 'spelllang' or 'spellfile' is set and there is a window for this // buffer in which 'spell' is set load the wordlists. - if (varp == &(curwin->w_s->b_p_spf)) { - int l = (int)STRLEN(curwin->w_s->b_p_spf); - if (l > 0 - && (l < 4 || STRCMP(curwin->w_s->b_p_spf + l - 4, ".add") != 0)) { - errmsg = e_invarg; - } - } - - if (errmsg == NULL) { - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_buffer == curbuf && wp->w_p_spell) { - errmsg = did_set_spelllang(wp); - break; - } - } - } + errmsg = did_set_spell_option(varp == &(curwin->w_s->b_p_spf)); } /* When 'spellcapcheck' is set compile the regexp program. */ else if (varp == &(curwin->w_s->b_p_spc)) { @@ -3424,6 +3421,30 @@ char_u *check_stl_option(char_u *s) return NULL; } +static char_u *did_set_spell_option(bool is_spellfile) +{ + char_u *errmsg = NULL; + + if (is_spellfile) { + int l = (int)STRLEN(curwin->w_s->b_p_spf); + if (l > 0 + && (l < 4 || STRCMP(curwin->w_s->b_p_spf + l - 4, ".add") != 0)) { + errmsg = e_invarg; + } + } + + if (errmsg == NULL) { + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + if (wp->w_buffer == curbuf && wp->w_p_spell) { + errmsg = did_set_spelllang(wp); + break; + } + } + } + + return errmsg; +} + /* * Set curbuf->b_cap_prog to the regexp program for 'spellcapcheck'. * Return error message when failed, NULL when OK. @@ -5503,6 +5524,7 @@ void buf_copy_options(buf_T *buf, int flags) buf->b_p_et = p_et; buf->b_p_fixeol = p_fixeol; buf->b_p_et_nobin = p_et_nobin; + buf->b_p_et_nopaste = p_et_nopaste; buf->b_p_ml = p_ml; buf->b_p_ml_nobin = p_ml_nobin; buf->b_p_inf = p_inf; @@ -5925,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; @@ -6160,6 +6186,7 @@ static void paste_option_changed(void) { static int old_p_paste = FALSE; static int save_sm = 0; + static int save_sta = 0; static int save_ru = 0; static int save_ri = 0; static int save_hkmap = 0; @@ -6176,40 +6203,44 @@ static void paste_option_changed(void) buf->b_p_wm_nopaste = buf->b_p_wm; buf->b_p_sts_nopaste = buf->b_p_sts; buf->b_p_ai_nopaste = buf->b_p_ai; + buf->b_p_et_nopaste = buf->b_p_et; } - /* save global options */ + // save global options save_sm = p_sm; + save_sta = p_sta; save_ru = p_ru; save_ri = p_ri; save_hkmap = p_hkmap; - /* save global values for local buffer options */ + // save global values for local buffer options + p_ai_nopaste = p_ai; + p_et_nopaste = p_et; + p_sts_nopaste = p_sts; p_tw_nopaste = p_tw; p_wm_nopaste = p_wm; - p_sts_nopaste = p_sts; - p_ai_nopaste = p_ai; } - /* - * Always set the option values, also when 'paste' is set when it is - * already on. - */ - /* set options for each buffer */ + // Always set the option values, also when 'paste' is set when it is + // already on. + // set options for each buffer FOR_ALL_BUFFERS(buf) { - buf->b_p_tw = 0; /* textwidth is 0 */ - buf->b_p_wm = 0; /* wrapmargin is 0 */ - buf->b_p_sts = 0; /* softtabstop is 0 */ - buf->b_p_ai = 0; /* no auto-indent */ - } - - /* set global options */ - p_sm = 0; /* no showmatch */ - if (p_ru) - status_redraw_all(); /* redraw to remove the ruler */ - p_ru = 0; /* no ruler */ - p_ri = 0; /* no reverse insert */ - p_hkmap = 0; /* no Hebrew keyboard */ - /* set global values for local buffer options */ + buf->b_p_tw = 0; // textwidth is 0 + buf->b_p_wm = 0; // wrapmargin is 0 + buf->b_p_sts = 0; // softtabstop is 0 + buf->b_p_ai = 0; // no auto-indent + buf->b_p_et = 0; // no expandtab + } + + // set global options + p_sm = 0; // no showmatch + p_sta = 0; // no smarttab + if (p_ru) { + status_redraw_all(); // redraw to remove the ruler + } + p_ru = 0; // no ruler + p_ri = 0; // no reverse insert + p_hkmap = 0; // no Hebrew keyboard + // set global values for local buffer options p_tw = 0; p_wm = 0; p_sts = 0; @@ -6225,20 +6256,24 @@ static void paste_option_changed(void) buf->b_p_wm = buf->b_p_wm_nopaste; buf->b_p_sts = buf->b_p_sts_nopaste; buf->b_p_ai = buf->b_p_ai_nopaste; + buf->b_p_et = buf->b_p_et_nopaste; } /* restore global options */ p_sm = save_sm; - if (p_ru != save_ru) - status_redraw_all(); /* redraw to draw the ruler */ + p_sta = save_sta; + if (p_ru != save_ru) { + status_redraw_all(); // redraw to draw the ruler + } p_ru = save_ru; p_ri = save_ri; p_hkmap = save_hkmap; - /* set global values for local buffer options */ + // set global values for local buffer options + p_ai = p_ai_nopaste; + p_et = p_et_nopaste; + p_sts = p_sts_nopaste; p_tw = p_tw_nopaste; p_wm = p_wm_nopaste; - p_sts = p_sts_nopaste; - p_ai = p_ai_nopaste; } old_p_paste = p_paste; diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index c1804067e9..41ce8ddbc2 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -262,8 +262,25 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one, startstr_len = (int)STRLEN(startstr); src = skipwhite(srcp); - --dstlen; // leave one char space for "\," + dstlen--; // leave one char space for "\," while (*src && dstlen > 0) { + // Skip over `=expr`. + if (src[0] == '`' && src[1] == '=') { + var = src; + src += 2; + (void)skip_expr(&src); + if (*src == '`') { + src++; + } + size_t len = (size_t)(src - var); + if (len > (size_t)dstlen) { + len = (size_t)dstlen; + } + memcpy((char *)dst, (char *)var, len); + dst += len; + dstlen -= (int)len; + continue; + } copy_char = true; if ((*src == '$') || (*src == '~' && at_start)) { mustfree = false; diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c index e632544856..f317fd6b5a 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -250,6 +250,14 @@ static unsigned int handle_mouse_event(char **ptr, uint8_t *buf, int col, row, advance; if (sscanf(*ptr, "<%d,%d>%n", &col, &row, &advance) != EOF && advance) { if (col >= 0 && row >= 0) { + // Make sure the mouse position is valid. Some terminals may + // return weird values. + if (col >= Columns) { + col = (int)Columns - 1; + } + if (row >= Rows) { + row = (int)Rows - 1; + } mouse_row = row; mouse_col = col; } diff --git a/src/nvim/path.c b/src/nvim/path.c index 8b9a49dfc0..5cd93ab811 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -556,8 +556,9 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, return 0; } - /* make room for file name */ - buf = xmalloc(STRLEN(path) + BASENAMELEN + 5); + // Make room for file name. When doing encoding conversion the actual + // length may be quite a bit longer, thus use the maximum possible length. + buf = xmalloc(MAXPATHL); /* * Find the first part in the path name that contains a wildcard. @@ -1158,12 +1159,17 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, add_pat = -1; p = pat[i]; - if (vim_backtick(p)) + if (vim_backtick(p)) { add_pat = expand_backtick(&ga, p, flags); - else { - /* - * First expand environment variables, "~/" and "~user/". - */ + if (add_pat == -1) { + recursive = false; + FreeWild(ga.ga_len, (char_u **)ga.ga_data); + *num_file = 0; + *file = NULL; + return FAIL; + } + } else { + // First expand environment variables, "~/" and "~user/". if (has_env_var(p) || *p == '~') { p = expand_env_save_opt(p, true); if (p == NULL) @@ -1246,13 +1252,10 @@ static int vim_backtick(char_u *p) return *p == '`' && *(p + 1) != NUL && *(p + STRLEN(p) - 1) == '`'; } -/* - * Expand an item in `backticks` by executing it as a command. - * Currently only works when pat[] starts and ends with a `. - * Returns number of file names found. - */ -static int -expand_backtick ( +// Expand an item in `backticks` by executing it as a command. +// Currently only works when pat[] starts and ends with a `. +// Returns number of file names found, -1 if an error is encountered. +static int expand_backtick( garray_T *gap, char_u *pat, int flags /* EW_* flags */ @@ -1273,8 +1276,9 @@ expand_backtick ( buffer = get_cmd_output(cmd, NULL, (flags & EW_SILENT) ? kShellOptSilent : 0, NULL); xfree(cmd); - if (buffer == NULL) - return 0; + if (buffer == NULL) { + return -1; + } cmd = buffer; while (*cmd != NUL) { @@ -1775,19 +1779,20 @@ bool same_directory(char_u *f1, char_u *f2) */ int pathcmp(const char *p, const char *q, int maxlen) { - int i; + int i, j; int c1, c2; const char *s = NULL; - for (i = 0; maxlen < 0 || i < maxlen; i += MB_PTR2LEN((char_u *)p + i)) { + for (i = 0, j = 0; maxlen < 0 || (i < maxlen && j < maxlen);) { c1 = PTR2CHAR((char_u *)p + i); - c2 = PTR2CHAR((char_u *)q + i); + c2 = PTR2CHAR((char_u *)q + j); /* End of "p": check if "q" also ends or just has a slash. */ if (c1 == NUL) { if (c2 == NUL) /* full match */ return 0; s = q; + i = j; break; } @@ -1811,9 +1816,13 @@ int pathcmp(const char *p, const char *q, int maxlen) return p_fic ? vim_toupper(c1) - vim_toupper(c2) : c1 - c2; /* no match */ } + + i += MB_PTR2LEN((char_u *)p + i); + j += MB_PTR2LEN((char_u *)q + j); } - if (s == NULL) /* "i" ran into "maxlen" */ + if (s == NULL) { // "i" or "j" ran into "maxlen" return 0; + } c1 = PTR2CHAR((char_u *)s + i); c2 = PTR2CHAR((char_u *)s + i + MB_PTR2LEN((char_u *)s + i)); diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index 001740943f..5ad621e666 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -545,7 +545,11 @@ static int pum_set_selected(int n, int repeat) g_do_tagpreview = (int)p_pvh; } RedrawingDisabled++; + // Prevent undo sync here, if an autocommand syncs undo weird + // things can happen to the undo tree. + no_u_sync++; resized = prepare_tagpreview(false); + no_u_sync--; RedrawingDisabled--; g_do_tagpreview = 0; @@ -629,7 +633,9 @@ static int pum_set_selected(int n, int repeat) // the window when needed, otherwise it will always be // redraw. if (resized) { + no_u_sync++; win_enter(curwin_save, true); + no_u_sync--; update_topline(); } @@ -640,7 +646,9 @@ static int pum_set_selected(int n, int repeat) pum_do_redraw = FALSE; if (!resized && win_valid(curwin_save)) { + no_u_sync++; win_enter(curwin_save, true); + no_u_sync--; } // May need to update the screen again when there are diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index dd41535110..e2849fb17a 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -6180,7 +6180,8 @@ static long nfa_regtry(nfa_regprog_T *prog, colnr_T col) if (prog->reghasz == REX_SET) { cleanup_zsubexpr(); re_extmatch_out = make_extmatch(); - for (i = 0; i < subs.synt.in_use; i++) { + // Loop over \z1, \z2, etc. There is no \z0. + for (i = 1; i < subs.synt.in_use; i++) { if (REG_MULTI) { struct multipos *mpos = &subs.synt.list.multi[i]; diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 7495647ff2..cd440fe8dc 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) @@ -2432,13 +2441,18 @@ win_line ( } } - /* find start of trailing whitespace */ - if (wp->w_p_list && lcs_trail) { - trailcol = (colnr_T)STRLEN(ptr); - while (trailcol > (colnr_T)0 && ascii_iswhite(ptr[trailcol - 1])) - --trailcol; - trailcol += (colnr_T) (ptr - line); - extra_check = TRUE; + if (wp->w_p_list) { + if (lcs_space || lcs_trail) { + extra_check = true; + } + // find start of trailing whitespace + if (lcs_trail) { + trailcol = (colnr_T)STRLEN(ptr); + while (trailcol > (colnr_T)0 && ascii_iswhite(ptr[trailcol - 1])) { + trailcol--; + } + trailcol += (colnr_T) (ptr - line); + } } /* @@ -3210,27 +3224,7 @@ win_line ( } } - ++ptr; - - // 'list': change char 160 to lcs_nbsp and space to lcs_space. - if (wp->w_p_list - && (((c == 160 || (mb_utf8 && (mb_c == 160 || mb_c == 0x202f))) - && lcs_nbsp) - || (c == ' ' && lcs_space && ptr - line <= trailcol))) { - c = (c == ' ') ? lcs_space : lcs_nbsp; - if (area_attr == 0 && search_attr == 0) { - n_attr = 1; - extra_attr = hl_attr(HLF_8); - saved_attr2 = char_attr; /* save current attr */ - } - mb_c = c; - if (enc_utf8 && (*mb_char2len)(c) > 1) { - mb_utf8 = TRUE; - u8cc[0] = 0; - c = 0xc0; - } else - mb_utf8 = FALSE; - } + ptr++; if (extra_check) { bool can_spell = true; @@ -3350,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]); } @@ -3377,6 +3382,28 @@ win_line ( } } + // 'list': change char 160 to lcs_nbsp and space to lcs_space. + if (wp->w_p_list + && (((c == 160 + || (mb_utf8 && (mb_c == 160 || mb_c == 0x202f))) + && lcs_nbsp) + || (c == ' ' && lcs_space && ptr - line <= trailcol))) { + c = (c == ' ') ? lcs_space : lcs_nbsp; + if (area_attr == 0 && search_attr == 0) { + n_attr = 1; + extra_attr = hl_attr(HLF_8); + saved_attr2 = char_attr; // save current attr + } + mb_c = c; + if (enc_utf8 && (*mb_char2len)(c) > 1) { + mb_utf8 = true; + u8cc[0] = 0; + c = 0xc0; + } else { + mb_utf8 = false; + } + } + if (trailcol != MAXCOL && ptr > line + trailcol && c == ' ') { c = lcs_trail; if (!attr_pri) { diff --git a/src/nvim/search.c b/src/nvim/search.c index 827473e55d..fffae1ecb2 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -3144,10 +3144,12 @@ current_block ( } if (VIsual_active) { - if (*p_sel == 'e') - ++curwin->w_cursor.col; - if (sol && gchar_cursor() != NUL) - inc(&curwin->w_cursor); /* include the line break */ + if (*p_sel == 'e') { + inc(&curwin->w_cursor); + } + if (sol && gchar_cursor() != NUL) { + inc(&curwin->w_cursor); // include the line break + } VIsual = start_pos; VIsual_mode = 'v'; redraw_curbuf_later(INVERTED); /* update the inversion */ diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index b7d8a19de9..478fa973a1 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -3294,7 +3294,7 @@ static void syn_cmd_onoff(exarg_T *eap, char *name) eap->nextcmd = check_nextcmd(eap->arg); if (!eap->skip) { char buf[100]; - strncpy(buf, "so ", 3); + strncpy(buf, "so ", 4); vim_snprintf(buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name); do_cmdline_cmd(buf); } diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 6d6cf006f6..42fd81f643 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -233,7 +233,7 @@ Terminal *terminal_open(TerminalOptions opts) set_option_value((uint8_t *)"relativenumber", false, NULL, OPT_LOCAL); RESET_BINDING(curwin); // Apply TermOpen autocmds so the user can configure the terminal - apply_autocmds(EVENT_TERMOPEN, NULL, NULL, true, curbuf); + apply_autocmds(EVENT_TERMOPEN, NULL, NULL, false, curbuf); // Configure the scrollback buffer. Try to get the size from: // @@ -288,8 +288,9 @@ void terminal_close(Terminal *term, char *msg) term->forward_mouse = false; term->closed = true; + buf_T *buf = handle_get_buffer(term->buf_handle); + if (!msg || exiting) { - buf_T *buf = handle_get_buffer(term->buf_handle); // If no msg was given, this was called by close_buffer(buffer.c). Or if // exiting, we must inform the buffer the terminal no longer exists so that // close_buffer() doesn't call this again. @@ -304,6 +305,10 @@ void terminal_close(Terminal *term, char *msg) } else { terminal_receive(term, msg, strlen(msg)); } + + if (buf) { + apply_autocmds(EVENT_TERMCLOSE, NULL, NULL, false, buf); + } } void terminal_resize(Terminal *term, uint16_t width, uint16_t height) diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index f077414e18..63ca4cf6c4 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -7,28 +7,37 @@ export SHELL := sh VIMPROG := ../../../build/bin/nvim SCRIPTSOURCE := ../../../runtime -SCRIPTS := \ - test8.out test10.out \ - test11.out test12.out test13.out test14.out \ - test17.out \ - test24.out \ - test30.out \ - test32.out test34.out \ - test36.out test37.out test40.out \ - test42.out \ - test47.out test48.out test49.out \ - test52.out test53.out test55.out \ - test64.out \ - test68.out test69.out \ - test73.out \ - test79.out \ - test88.out \ - test_listlbr.out \ - test_breakindent.out \ - test_charsearch.out \ - test_close_count.out \ - test_marks.out \ - test_match_conceal.out \ +SCRIPTS := \ + test8.out \ + test10.out \ + test12.out \ + test13.out \ + test14.out \ + test17.out \ + test24.out \ + test30.out \ + test32.out \ + test34.out \ + test37.out \ + test40.out \ + test42.out \ + test47.out \ + test48.out \ + test49.out \ + test52.out \ + test53.out \ + test55.out \ + test64.out \ + test68.out \ + test69.out \ + test73.out \ + test79.out \ + test88.out \ + test_listlbr.out \ + test_breakindent.out \ + test_close_count.out \ + test_marks.out \ + test_match_conceal.out \ NEW_TESTS = diff --git a/src/nvim/testdir/test11.in b/src/nvim/testdir/test11.in deleted file mode 100644 index 9e9e257c1d..0000000000 --- a/src/nvim/testdir/test11.in +++ /dev/null @@ -1,84 +0,0 @@ -Tests for autocommands: -- FileWritePre writing a compressed file -- FileReadPost reading a compressed file -- BufNewFile reading a file template -- BufReadPre decompressing the file to be read -- FilterReadPre substituting characters in the temp file -- FilterReadPost substituting characters after filtering -- FileReadPre set options for decompression -- FileReadPost decompress the file - -Note: This test is skipped if "gzip" is not available. -$GZIP is made empty, "-v" would cause trouble. -Use a FileChangedShell autocommand to avoid a prompt for "Xtestfile.gz" being -modified outside of Vim (noticed on Solaris). - -STARTTEST -:so small.vim -:" drop out when there is no gzip program -:if !executable("gzip") -: e! test.ok -: w! test.out -: qa! -:endif -:let $GZIP = "" -:au FileChangedShell * echo "caught FileChangedShell" -:set bin -:au FileWritePre *.gz '[,']!gzip -:au FileWritePost *.gz undo -:/^start of testfile/,/^end of testfile/w! Xtestfile.gz -:au FileReadPost *.gz '[,']!gzip -d -:$r Xtestfile.gz " Read and decompress the testfile -:?startstart?,$w! test.out " Write contents of this file -:au BufNewFile *.c read Xtest.c -:/^start of test.c/+1,/^end of test.c/-1w! Xtest.c -:e! foo.c " Will load Xtest.c -:au FileAppendPre *.out '[,']s/new/NEW/ -:au FileAppendPost *.out !cat Xtest.c >>test.out -:w>>test.out " Append it to the output file -:au! FileAppendPre -:" setup autocommands to decompress before reading and re-compress afterwards -:au BufReadPre *.gz exe '!gzip -d ' . shellescape(expand("<afile>")) -:au BufReadPre *.gz call rename(expand("<afile>:r"), expand("<afile>")) -:au BufReadPost *.gz call rename(expand("<afile>"), expand("<afile>:r")) -:au BufReadPost *.gz exe '!gzip ' . shellescape(expand("<afile>:r")) -:e! Xtestfile.gz " Edit compressed file -:w>>test.out " Append it to the output file -:set shelltemp " need temp files here -:au FilterReadPre *.out call rename(expand("<afile>"), expand("<afile>") . ".t") -:au FilterReadPre *.out exe 'silent !sed s/e/E/ ' . shellescape(expand("<afile>")) . ".t >" . shellescape(expand("<afile>")) -:au FilterReadPre *.out exe 'silent !rm ' . shellescape(expand("<afile>")) . '.t' -:au FilterReadPost *.out '[,']s/x/X/g -:e! test.out " Edit the output file -:23,$!cat -:23,$s/\r$// " remove CR for when sed adds them -:au! FileReadPre *.gz exe 'silent !gzip -d ' . shellescape(expand("<afile>")) -:au FileReadPre *.gz call rename(expand("<afile>:r"), expand("<afile>")) -:au! FileReadPost *.gz '[,']s/l/L/ -:$r Xtestfile.gz " Read compressed file -:w " write it, after filtering -:au! " remove all autocommands -:e " Edit test.out again -:set nobin ff& " use the default fileformat for writing -:w -:qa! -ENDTEST - -startstart -start of testfile -line 2 Abcdefghijklmnopqrstuvwxyz -line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 4 Abcdefghijklmnopqrstuvwxyz -line 5 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 6 Abcdefghijklmnopqrstuvwxyz -line 7 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 8 Abcdefghijklmnopqrstuvwxyz -line 9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 10 Abcdefghijklmnopqrstuvwxyz -end of testfile - -start of test.c -/* - * Here is a new .c file - */ -end of test.c diff --git a/src/nvim/testdir/test11.ok b/src/nvim/testdir/test11.ok deleted file mode 100644 index af8c5ce261..0000000000 --- a/src/nvim/testdir/test11.ok +++ /dev/null @@ -1,61 +0,0 @@ -startstart -start of testfile -line 2 Abcdefghijklmnopqrstuvwxyz -line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 4 Abcdefghijklmnopqrstuvwxyz -line 5 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 6 Abcdefghijklmnopqrstuvwxyz -line 7 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 8 Abcdefghijklmnopqrstuvwxyz -line 9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 10 Abcdefghijklmnopqrstuvwxyz -end of testfile - -start of test.c -/* - * Here is a new .c file - */ -end of test.c -start of testfile -line 2 Abcdefghijklmnopqrstuvwxyz -line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 4 Abcdefghijklmnopqrstuvwxyz -linE 5 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -linE 6 AbcdefghijklmnopqrstuvwXyz -linE 7 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -linE 8 AbcdefghijklmnopqrstuvwXyz -linE 9 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -linE 10 AbcdefghijklmnopqrstuvwXyz -End of testfile - -/* - * HEre is a NEW .c file - */ -/* - * HEre is a new .c file - */ -start of tEstfile -linE 2 AbcdefghijklmnopqrstuvwXyz -linE 3 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -linE 4 AbcdefghijklmnopqrstuvwXyz -linE 5 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -linE 6 AbcdefghijklmnopqrstuvwXyz -linE 7 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -linE 8 AbcdefghijklmnopqrstuvwXyz -linE 9 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -linE 10 AbcdefghijklmnopqrstuvwXyz -End of testfile -/* - * HEre is a new .c file - */ -start of testfiLe -Line 2 Abcdefghijklmnopqrstuvwxyz -Line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -Line 4 Abcdefghijklmnopqrstuvwxyz -Line 5 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -Line 6 Abcdefghijklmnopqrstuvwxyz -Line 7 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -Line 8 Abcdefghijklmnopqrstuvwxyz -Line 9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -Line 10 Abcdefghijklmnopqrstuvwxyz -end of testfiLe diff --git a/src/nvim/testdir/test36.in b/src/nvim/testdir/test36.in deleted file mode 100644 index 8cdb5262bd..0000000000 --- a/src/nvim/testdir/test36.in +++ /dev/null @@ -1,105 +0,0 @@ -Test character classes in regexp using regexpengine 0, 1, 2. - -STARTTEST -/^start-here/+1 -Y:s/\%#=0\d//g -p:s/\%#=1\d//g -p:s/\%#=2\d//g -p:s/\%#=0[0-9]//g -p:s/\%#=1[0-9]//g -p:s/\%#=2[0-9]//g -p:s/\%#=0\D//g -p:s/\%#=1\D//g -p:s/\%#=2\D//g -p:s/\%#=0[^0-9]//g -p:s/\%#=1[^0-9]//g -p:s/\%#=2[^0-9]//g -p:s/\%#=0\o//g -p:s/\%#=1\o//g -p:s/\%#=2\o//g -p:s/\%#=0[0-7]//g -p:s/\%#=1[0-7]//g -p:s/\%#=2[0-7]//g -p:s/\%#=0\O//g -p:s/\%#=1\O//g -p:s/\%#=2\O//g -p:s/\%#=0[^0-7]//g -p:s/\%#=1[^0-7]//g -p:s/\%#=2[^0-7]//g -p:s/\%#=0\x//g -p:s/\%#=1\x//g -p:s/\%#=2\x//g -p:s/\%#=0[0-9A-Fa-f]//g -p:s/\%#=1[0-9A-Fa-f]//g -p:s/\%#=2[0-9A-Fa-f]//g -p:s/\%#=0\X//g -p:s/\%#=1\X//g -p:s/\%#=2\X//g -p:s/\%#=0[^0-9A-Fa-f]//g -p:s/\%#=1[^0-9A-Fa-f]//g -p:s/\%#=2[^0-9A-Fa-f]//g -p:s/\%#=0\w//g -p:s/\%#=1\w//g -p:s/\%#=2\w//g -p:s/\%#=0[0-9A-Za-z_]//g -p:s/\%#=1[0-9A-Za-z_]//g -p:s/\%#=2[0-9A-Za-z_]//g -p:s/\%#=0\W//g -p:s/\%#=1\W//g -p:s/\%#=2\W//g -p:s/\%#=0[^0-9A-Za-z_]//g -p:s/\%#=1[^0-9A-Za-z_]//g -p:s/\%#=2[^0-9A-Za-z_]//g -p:s/\%#=0\h//g -p:s/\%#=1\h//g -p:s/\%#=2\h//g -p:s/\%#=0[A-Za-z_]//g -p:s/\%#=1[A-Za-z_]//g -p:s/\%#=2[A-Za-z_]//g -p:s/\%#=0\H//g -p:s/\%#=1\H//g -p:s/\%#=2\H//g -p:s/\%#=0[^A-Za-z_]//g -p:s/\%#=1[^A-Za-z_]//g -p:s/\%#=2[^A-Za-z_]//g -p:s/\%#=0\a//g -p:s/\%#=1\a//g -p:s/\%#=2\a//g -p:s/\%#=0[A-Za-z]//g -p:s/\%#=1[A-Za-z]//g -p:s/\%#=2[A-Za-z]//g -p:s/\%#=0\A//g -p:s/\%#=1\A//g -p:s/\%#=2\A//g -p:s/\%#=0[^A-Za-z]//g -p:s/\%#=1[^A-Za-z]//g -p:s/\%#=2[^A-Za-z]//g -p:s/\%#=0\l//g -p:s/\%#=1\l//g -p:s/\%#=2\l//g -p:s/\%#=0[a-z]//g -p:s/\%#=1[a-z]//g -p:s/\%#=2[a-z]//g -p:s/\%#=0\L//g -p:s/\%#=1\L//g -p:s/\%#=2\L//g -p:s/\%#=0[^a-z]//g -p:s/\%#=1[^a-z]//g -p:s/\%#=2[^a-z]//g -p:s/\%#=0\u//g -p:s/\%#=1\u//g -p:s/\%#=2\u//g -p:s/\%#=0[A-Z]//g -p:s/\%#=1[A-Z]//g -p:s/\%#=2[A-Z]//g -p:s/\%#=0\U//g -p:s/\%#=1\U//g -p:s/\%#=2\U//g -p:s/\%#=0[^A-Z]//g -p:s/\%#=1[^A-Z]//g -p:s/\%#=2[^A-Z]//g -:/^start-here/+1,$wq! test.out -ENDTEST - -start-here -
!"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé diff --git a/src/nvim/testdir/test36.ok b/src/nvim/testdir/test36.ok deleted file mode 100644 index f72a74b2b7..0000000000 --- a/src/nvim/testdir/test36.ok +++ /dev/null @@ -1,96 +0,0 @@ -
!"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -0123456789 -0123456789 -0123456789 -0123456789 -0123456789 -0123456789 -
!"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -01234567 -01234567 -01234567 -01234567 -01234567 -01234567 -
!"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~€‚›¦±¼ÇÓé -0123456789ABCDEFabcdef -0123456789ABCDEFabcdef -0123456789ABCDEFabcdef -0123456789ABCDEFabcdef -0123456789ABCDEFabcdef -0123456789ABCDEFabcdef -
!"#$%&'()#+'-./:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé -0123456789ABCDEFGHIXYZ_abcdefghiwxyz -0123456789ABCDEFGHIXYZ_abcdefghiwxyz -0123456789ABCDEFGHIXYZ_abcdefghiwxyz -0123456789ABCDEFGHIXYZ_abcdefghiwxyz -0123456789ABCDEFGHIXYZ_abcdefghiwxyz -0123456789ABCDEFGHIXYZ_abcdefghiwxyz -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé -ABCDEFGHIXYZ_abcdefghiwxyz -ABCDEFGHIXYZ_abcdefghiwxyz -ABCDEFGHIXYZ_abcdefghiwxyz -ABCDEFGHIXYZ_abcdefghiwxyz -ABCDEFGHIXYZ_abcdefghiwxyz -ABCDEFGHIXYZ_abcdefghiwxyz -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~€‚›¦±¼ÇÓé -ABCDEFGHIXYZabcdefghiwxyz -ABCDEFGHIXYZabcdefghiwxyz -ABCDEFGHIXYZabcdefghiwxyz -ABCDEFGHIXYZabcdefghiwxyz -ABCDEFGHIXYZabcdefghiwxyz -ABCDEFGHIXYZabcdefghiwxyz -
!"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~€‚›¦±¼ÇÓé -abcdefghiwxyz -abcdefghiwxyz -abcdefghiwxyz -abcdefghiwxyz -abcdefghiwxyz -abcdefghiwxyz -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -ABCDEFGHIXYZ -ABCDEFGHIXYZ -ABCDEFGHIXYZ -ABCDEFGHIXYZ -ABCDEFGHIXYZ -ABCDEFGHIXYZ diff --git a/src/nvim/testdir/test49.vim b/src/nvim/testdir/test49.vim index afee9d882c..70d388b06a 100644 --- a/src/nvim/testdir/test49.vim +++ b/src/nvim/testdir/test49.vim @@ -1,6 +1,6 @@ " Vim script language tests " Author: Servatius Brandt <Servatius.Brandt@fujitsu-siemens.com> -" Last Change: 2013 Jun 06 +" Last Change: 2015 Sep 25 "------------------------------------------------------------------------------- " Test environment {{{1 @@ -5188,19 +5188,19 @@ catch /.*/ Xpath 65536 " X: 65536 let exception = v:exception let throwpoint = v:throwpoint - call CHECK(1, "oops", '\<F\.\.G\.\.T\>', '\<2\>') + call CHECK(1, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>') exec "let exception = v:exception" exec "let throwpoint = v:throwpoint" - call CHECK(2, "oops", '\<F\.\.G\.\.T\>', '\<2\>') + call CHECK(2, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>') CmdException CmdThrowpoint - call CHECK(3, "oops", '\<F\.\.G\.\.T\>', '\<2\>') + call CHECK(3, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>') call FuncException() call FuncThrowpoint() - call CHECK(4, "oops", '\<F\.\.G\.\.T\>', '\<2\>') + call CHECK(4, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>') exec "source" scriptException exec "source" scriptThrowPoint - call CHECK(5, "oops", '\<F\.\.G\.\.T\>', '\<2\>') + call CHECK(5, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>') try Xpath 131072 " X: 131072 call G("arrgh", 4) @@ -5208,7 +5208,7 @@ catch /.*/ Xpath 262144 " X: 262144 let exception = v:exception let throwpoint = v:throwpoint - call CHECK(6, "arrgh", '\<G\.\.T\>', '\<4\>') + call CHECK(6, "arrgh", '\<G\[1]\.\.T\>', '\<4\>') try Xpath 524288 " X: 524288 let g:arg = "autsch" @@ -5226,7 +5226,7 @@ catch /.*/ Xpath 2097152 " X: 2097152 let exception = v:exception let throwpoint = v:throwpoint - call CHECK(8, "arrgh", '\<G\.\.T\>', '\<4\>') + call CHECK(8, "arrgh", '\<G\[1]\.\.T\>', '\<4\>') try Xpath 4194304 " X: 4194304 let g:arg = "brrrr" @@ -5242,27 +5242,27 @@ catch /.*/ Xpath 16777216 " X: 16777216 let exception = v:exception let throwpoint = v:throwpoint - call CHECK(10, "arrgh", '\<G\.\.T\>', '\<4\>') + call CHECK(10, "arrgh", '\<G\[1]\.\.T\>', '\<4\>') endtry Xpath 33554432 " X: 33554432 let exception = v:exception let throwpoint = v:throwpoint - call CHECK(11, "arrgh", '\<G\.\.T\>', '\<4\>') + call CHECK(11, "arrgh", '\<G\[1]\.\.T\>', '\<4\>') endtry Xpath 67108864 " X: 67108864 let exception = v:exception let throwpoint = v:throwpoint - call CHECK(12, "arrgh", '\<G\.\.T\>', '\<4\>') + call CHECK(12, "arrgh", '\<G\[1]\.\.T\>', '\<4\>') finally Xpath 134217728 " X: 134217728 let exception = v:exception let throwpoint = v:throwpoint - call CHECK(13, "oops", '\<F\.\.G\.\.T\>', '\<2\>') + call CHECK(13, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>') endtry Xpath 268435456 " X: 268435456 let exception = v:exception let throwpoint = v:throwpoint - call CHECK(14, "oops", '\<F\.\.G\.\.T\>', '\<2\>') + call CHECK(14, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>') finally Xpath 536870912 " X: 536870912 let exception = v:exception diff --git a/src/nvim/testdir/test_charsearch.in b/src/nvim/testdir/test_charsearch.in deleted file mode 100644 index 5085cb39bc..0000000000 --- a/src/nvim/testdir/test_charsearch.in +++ /dev/null @@ -1,25 +0,0 @@ -Test for character searches - -STARTTEST -:so small.vim -:" check that "fe" and ";" work -/^X -ylfep;;p,,p: -:" check that save/restore works -/^Y -ylfep:let csave = getcharsearch() -fip:call setcharsearch(csave) -;p;p: -:" check that setcharsearch() changes the settins. -/^Z -ylfep:call setcharsearch({'char': 'k'}) -;p:call setcharsearch({'forward': 0}) -$;p:call setcharseearch({'until'}: 1}) -;;p: -:/^X/,$w! test.out -:qa! -ENDTEST - -Xabcdefghijkemnopqretuvwxyz -Yabcdefghijkemnopqretuvwxyz -Zabcdefghijkemnokqretkvwxyz diff --git a/src/nvim/testdir/test_charsearch.ok b/src/nvim/testdir/test_charsearch.ok deleted file mode 100644 index a0c90e24f9..0000000000 --- a/src/nvim/testdir/test_charsearch.ok +++ /dev/null @@ -1,3 +0,0 @@ -XabcdeXfghijkeXmnopqreXtuvwxyz -YabcdeYfghiYjkeYmnopqreYtuvwxyz -ZabcdeZfghijkZemnokZqretkZvwxyz diff --git a/src/nvim/testdir/test_listlbr.in b/src/nvim/testdir/test_listlbr.in index f13eee121e..6084711786 100644 --- a/src/nvim/testdir/test_listlbr.in +++ b/src/nvim/testdir/test_listlbr.in @@ -24,20 +24,24 @@ STARTTEST : $put =g:line : wincmd p :endfu +:" :let g:test="Test 1: set linebreak" :redraw! :let line=ScreenChar(winwidth(0)) :call DoRecordScreen() +:" :let g:test="Test 2: set linebreak + set list" :set linebreak list listchars= :redraw! :let line=ScreenChar(winwidth(0)) :call DoRecordScreen() +:" :let g:test ="Test 3: set linebreak nolist" :set nolist linebreak :redraw! :let line=ScreenChar(winwidth(0)) :call DoRecordScreen() +:" :let g:test ="Test 4: set linebreak with tab and 1 line as long as screen: should break!" :set nolist linebreak ts=8 :let line="1\t".repeat('a', winwidth(0)-2) @@ -51,6 +55,7 @@ STARTTEST :$put =line :$ :norm! zt +:" :let g:test ="Test 5: set linebreak with conceal and set list and tab displayed by different char (line may not be truncated)" :set cpo&vim list linebreak conceallevel=2 concealcursor=nv listchars=tab:ab :syn match ConcealVar contained /_/ conceal @@ -58,6 +63,7 @@ STARTTEST :let line=ScreenChar(winwidth(0)) :call DoRecordScreen() :set cpo&vim linebreak +:" :let g:test ="Test 6: set linebreak with visual block mode" :let line="REMOVE: this not" :$put =g:test @@ -67,20 +73,47 @@ STARTTEST :1/^REMOVE: 0jf x:$put :set cpo&vim linebreak +:" :let g:test ="Test 7: set linebreak with visual block mode and v_b_A" :$put =g:test Golong line: 40afoobar aTARGET at end :exe "norm! $3B\<C-v>eAx\<Esc>" :set cpo&vim linebreak sbr= +:" :let g:test ="Test 8: set linebreak with visual char mode and changing block" :$put =g:test Go1111-1111-1111-11-1111-1111-11110f-lv3lc2222bgj. +:" :let g:test ="Test 9: using redo after block visual mode" :$put =g:test Go aaa aaa a2k2j~e. +:" +:let g:test ="Test 10: using normal commands after block-visual" +:$put =g:test +:set linebreak +Go +abcd{ef +ghijklm +no}pqrs2k0f{c% +:" +:let g:test ="Test 11: using block replace mode after wrapping" +:$put =g:test +:set linebreak wrap +Go150aayypk147|jr0 +:" +:let g:test ="Test 12: set linebreak list listchars=space:_,tab:>-,tail:-,eol:$" +:set list listchars=space:_,trail:-,tab:>-,eol:$ +:$put =g:test +:let line="a aaaaaaaaaaaaaaaaaaaaaa\ta " +:$put =line +:$ +:norm! zt +:redraw! +:let line=ScreenChar(winwidth(0)) +:call DoRecordScreen() :%w! test.out :qa! ENDTEST diff --git a/src/nvim/testdir/test_listlbr.ok b/src/nvim/testdir/test_listlbr.ok index 323bcdee08..b32a54969e 100644 --- a/src/nvim/testdir/test_listlbr.ok +++ b/src/nvim/testdir/test_listlbr.ok @@ -46,3 +46,17 @@ Test 9: using redo after block visual mode AaA AaA A +Test 10: using normal commands after block-visual + +abcdpqrs +Test 11: using block replace mode after wrapping +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0aaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0aaa +Test 12: set linebreak list listchars=space:_,tab:>-,tail:-,eol:$ +a aaaaaaaaaaaaaaaaaaaaaa a + +Test 12: set linebreak list listchars=space:_,tab:>-,tail:-,eol:$ +a_ +aaaaaaaaaaaaaaaaaaaa +aa>-----a-$ +~ 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 4de63ebb10..80b1b236dd 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -69,6 +69,8 @@ static char *features[] = { // clang-format off static int included_patches[] = { + 1366, + // 1219 NA // 1218 NA // 1217 NA @@ -247,7 +249,7 @@ static int included_patches[] = { // 1044 NA, // 1043 NA, // 1042, - // 1041, + 1041, // 1040 NA, // 1039, // 1038 NA, @@ -311,7 +313,7 @@ static int included_patches[] = { 980, // 979 NA 978, - // 977, + 977, // 976 NA 975, // 974, @@ -349,24 +351,24 @@ static int included_patches[] = { // 942, // 941, // 940 NA - // 939, + 939, // 938 NA - // 937, - // 936, - // 935, + 937, + 936, + // 935 NA // 934 NA - // 933, - // 932, + 933, + 932, // 931 NA // 930 NA 929, // 928 NA // 927 NA - // 926, + 926, // 925, // 924 NA // 923 NA - // 922, + 922, // 921 NA // 920 NA // 919 NA @@ -376,7 +378,7 @@ static int included_patches[] = { 915, // 914, // 913 NA - // 912, + 912, // 911 NA // 910 NA // 909, @@ -385,42 +387,42 @@ static int included_patches[] = { // 906 NA // 905, // 904, - // 903, + 903, // 902 NA - // 901, + 901, // 900 NA // 899 NA 898, // 897 NA // 896, - // 895, + 895, // 894 NA - // 893, + 893, // 892, - // 891, + 891, // 890 NA // 889, - // 888, + 888, 887, // 886 NA - // 885, + 885, // 884 NA - // 883, + 883, // 882, - // 881, + 881, // 880 NA - // 879, - // 878, - // 877, + 879, + 878, + 877, // 876 NA // 875 NA // 874 NA // 873 NA // 872 NA // 871, - // 870, + 870, // 869 NA - // 868, + 868, // 867 NA // 866 NA // 865 NA @@ -429,23 +431,23 @@ static int included_patches[] = { // 862 NA // 861 NA // 860 NA - // 859, + 859, 858, // 857, - // 856, + 856, // 855 NA // 854 NA - // 853, + 853, // 852 NA // 851 NA // 850 NA 849, 848, - // 847, + 847, // 846 NA - // 845, - // 844, - // 843, + 845, + 844, + 843, // 842 NA // 841 NA // 840 NA @@ -453,12 +455,12 @@ static int included_patches[] = { // 838 NA // 837 NA 836, - // 835, + 835, 834, - // 833, - // 832, - // 831, - // 830, + 833, + 832, + 831, + 830, // 829 NA 828, // 827 NA @@ -470,7 +472,7 @@ static int included_patches[] = { // 821 NA 820, // 819, - // 818, + 818, 817, 816, 815, diff --git a/src/nvim/window.c b/src/nvim/window.c index 36cb48f3a1..39106a7b8d 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -4575,10 +4575,19 @@ void win_drag_vsep_line(win_T *dragwin, int offset) } assert(fr); - if (room < offset) /* Not enough room */ - offset = room; /* Move as far as we can */ - if (offset <= 0) /* No room at all, quit. */ + // Not enough room + if (room < offset) { + offset = room; // Move as far as we can + } + + // No room at all, quit. + if (offset <= 0) { return; + } + + if (fr == NULL) { + return; // Safety check, should not happen. + } /* grow frame fr by offset lines */ frame_new_width(fr, fr->fr_width + offset, left, FALSE); |