diff options
Diffstat (limited to 'src')
41 files changed, 1078 insertions, 892 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 bdea609820..936a14b903 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -28,6 +28,8 @@ typedef struct file_buffer buf_T; // Forward declaration  #include "nvim/profile.h"  // for String  #include "nvim/api/private/defs.h" +// for Map(K, V) +#include "nvim/map.h"  #define MODIFIABLE(buf) (!buf->terminal && buf->b_p_ma) @@ -59,21 +61,21 @@ typedef struct file_buffer buf_T; // Forward declaration  #define VALID_BOTLINE_AP 0x40   /* w_botine is approximated */  #define VALID_TOPLINE   0x80    /* w_topline is valid (for cursor position) */ -/* flags for b_flags */ -#define BF_RECOVERED    0x01    /* buffer has been recovered */ -#define BF_CHECK_RO     0x02    /* need to check readonly when loading file -                                   into buffer (set by ":e", may be reset by -                                   ":buf" */ -#define BF_NEVERLOADED  0x04    /* file has never been loaded into buffer, -                                   many variables still need to be set */ -#define BF_NOTEDITED    0x08    /* Set when file name is changed after -                                   starting to edit, reset when file is -                                   written out. */ -#define BF_NEW          0x10    /* file didn't exist when editing started */ -#define BF_NEW_W        0x20    /* Warned for BF_NEW and file created */ -#define BF_READERR      0x40    /* got errors while reading the file */ -#define BF_DUMMY        0x80    /* dummy buffer, only used internally */ -#define BF_PRESERVED    0x100   /* ":preserve" was used */ +// flags for b_flags +#define BF_RECOVERED    0x01    // buffer has been recovered +#define BF_CHECK_RO     0x02    // need to check readonly when loading file +                                // into buffer (set by ":e", may be reset by +                                // ":buf") +#define BF_NEVERLOADED  0x04    // file has never been loaded into buffer, +                                // many variables still need to be set +#define BF_NOTEDITED    0x08    // Set when file name is changed after +                                // starting to edit, reset when file is +                                // written out. +#define BF_NEW          0x10    // file didn't exist when editing started +#define BF_NEW_W        0x20    // Warned for BF_NEW and file created +#define BF_READERR      0x40    // got errors while reading the file +#define BF_DUMMY        0x80    // dummy buffer, only used internally +#define BF_PRESERVED    0x100   // ":preserve" was used  /* Mask to check for flags that prevent normal writing */  #define BF_WRITE_MASK   (BF_NOTEDITED + BF_NEW + BF_READERR) @@ -101,6 +103,11 @@ typedef int scid_T;                     /* script ID */  // for signlist_T  #include "nvim/sign_defs.h" +// for bufhl_*_T +#include "nvim/bufhl_defs.h" + +typedef Map(linenr_T, bufhl_vec_T) bufhl_info_T; +  // for FileID  #include "nvim/os/fs_defs.h" @@ -754,6 +761,8 @@ struct file_buffer {    dict_T *additional_data;      // Additional data from shada file if any.    int b_mapped_ctrl_c;          // modes where CTRL-C is mapped + +  bufhl_info_T *b_bufhl_info;   // buffer stored highlights  };  /* diff --git a/src/nvim/bufhl_defs.h b/src/nvim/bufhl_defs.h new file mode 100644 index 0000000000..e47bb2a238 --- /dev/null +++ b/src/nvim/bufhl_defs.h @@ -0,0 +1,25 @@ +#ifndef NVIM_BUFHL_DEFS_H +#define NVIM_BUFHL_DEFS_H + +#include "nvim/pos.h" +#include "nvim/lib/kvec.h" +// bufhl: buffer specific highlighting + +struct bufhl_hl_item +{ +  int src_id; +  int hl_id;  // highlight group +  colnr_T start;  // first column to highlight +  colnr_T stop;  // last column to highlight +}; +typedef struct bufhl_hl_item bufhl_hl_item_T; + +typedef kvec_t(struct bufhl_hl_item) bufhl_vec_T; + +typedef struct { +  bufhl_vec_T entries; +  int current; +  colnr_T valid_to; +} bufhl_lineinfo_T; + +#endif  // NVIM_BUFHL_DEFS_H diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 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 ec085efc07..31175773f0 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7324,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 }, @@ -10698,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) @@ -10717,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. @@ -10734,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) @@ -15209,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; @@ -15225,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 */ @@ -15233,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; +    }    }  } @@ -15249,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); +  }  }  /* @@ -15386,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);  }  /* @@ -15560,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);    }  } @@ -18092,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;  }  /* @@ -20914,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) { @@ -22187,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/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/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/option.c b/src/nvim/option.c index 4bf7ec5405..af7b272467 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -2077,7 +2077,7 @@ static void didset_options2(void)    (void)highlight_changed();    // Parse default for 'clipboard'. -  opt_strings_flags(p_cb, p_cb_values, &cb_flags, true); +  (void)opt_strings_flags(p_cb, p_cb_values, &cb_flags, true);    // Parse default for 'fillchars'.    (void)set_chars_option(&p_fcs); @@ -5947,13 +5947,17 @@ option_value2string (    if (opp->flags & P_NUM) {      long wc = 0; -    if (wc_use_keyname(varp, &wc)) -      STRCPY(NameBuff, get_special_key_name((int)wc, 0)); -    else if (wc != 0) -      STRCPY(NameBuff, transchar((int)wc)); -    else -      sprintf((char *)NameBuff, "%" PRId64, (int64_t)*(long *)varp); -  } else { /* P_STRING */ +    if (wc_use_keyname(varp, &wc)) { +      STRLCPY(NameBuff, get_special_key_name((int)wc, 0), sizeof(NameBuff)); +    } else if (wc != 0) { +      STRLCPY(NameBuff, transchar((int)wc), sizeof(NameBuff)); +    } else { +      snprintf((char *)NameBuff, +               sizeof(NameBuff), +               "%" PRId64, +               (int64_t)*(long *)varp); +    } +  } else {  // P_STRING      varp = *(char_u **)(varp);      if (varp == NULL)                       /* just in case */        NameBuff[0] = NUL; diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index 10f56b520b..384a17004e 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 e0e5f19911..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) { 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 3b5836f0b5..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) @@ -3335,6 +3344,17 @@ win_line (              char_attr = hl_combine_attr(spell_attr, char_attr);          } +        if (has_bufhl && v > 0) { +          bufhl_attr = bufhl_get_attr(&bufhl_info, (colnr_T)v); +          if (bufhl_attr != 0) { +            if (!attr_pri) { +              char_attr = hl_combine_attr(char_attr, bufhl_attr); +            } else { +              char_attr = hl_combine_attr(bufhl_attr, char_attr); +            } +          } +        } +          if (wp->w_buffer->terminal) {            char_attr = hl_combine_attr(char_attr, term_attrs[vcol]);          } 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 0a7807d811..42fd81f643 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -141,10 +141,6 @@ struct terminal {    int pressed_button;    // pending width/height    bool pending_resize; -  // color palette. this isn't set directly in the vterm instance because -  // the default values are used to obtain the color numbers passed to cterm -  // colors -  RgbValue colors[256];    // With a reference count of 0 the terminal can be freed.    size_t refcount;  }; @@ -205,6 +201,7 @@ void terminal_teardown(void)  Terminal *terminal_open(TerminalOptions opts)  { +  bool true_color = ui_rgb_attached();    // Create a new terminal instance and configure it    Terminal *rv = xcalloc(1, sizeof(Terminal));    rv->opts = opts; @@ -220,7 +217,7 @@ Terminal *terminal_open(TerminalOptions opts)    // Set up screen    rv->vts = vterm_obtain_screen(rv->vt);    vterm_screen_enable_altscreen(rv->vts, true); -    // delete empty lines at the end of the buffer +  // delete empty lines at the end of the buffer    vterm_screen_set_callbacks(rv->vts, &vterm_screen_callbacks, rv);    vterm_screen_set_damage_merge(rv->vts, VTERM_DAMAGE_SCROLL);    vterm_screen_reset(rv->vts, 1); @@ -236,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:    // @@ -250,12 +247,18 @@ Terminal *terminal_open(TerminalOptions opts)    rv->sb_size = MIN(rv->sb_size, 100000);    rv->sb_buffer = xmalloc(sizeof(ScrollbackLine *) * rv->sb_size); +  if (!true_color) { +    return rv; +  } + +  vterm_state_set_bold_highbright(state, true); +    // Configure the color palette. Try to get the color from:    //    // - b:terminal_color_{NUM}    // - g:terminal_color_{NUM}    // - the VTerm instance -  for (int i = 0; i < (int)ARRAY_SIZE(rv->colors); i++) { +  for (int i = 0; i < 16; i++) {      RgbValue color_val = -1;      char var[64];      snprintf(var, sizeof(var), "terminal_color_%d", i); @@ -265,16 +268,13 @@ Terminal *terminal_open(TerminalOptions opts)        xfree(name);        if (color_val != -1) { -        rv->colors[i] = color_val; +        VTermColor color; +        color.red = (uint8_t)((color_val >> 16) & 0xFF); +        color.green = (uint8_t)((color_val >> 8) & 0xFF); +        color.blue = (uint8_t)((color_val >> 0) & 0xFF); +        vterm_state_set_palette_color(state, i, &color);        }      } - -    if (color_val == -1) { -      // the default is taken from vterm -      VTermColor color; -      vterm_state_get_palette_color(state, i, &color); -      rv->colors[i] = RGB(color.red, color.green, color.blue); -    }    }    return rv; @@ -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) @@ -543,10 +548,6 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr,                      map_get(int, int)(color_indexes, vt_fg) : 0;      int vt_bg_idx = vt_bg != default_vt_bg ?                      map_get(int, int)(color_indexes, vt_bg) : 0; -    // The index is now used to get the final rgb value from the -    // user-customizable palette. -    int vt_fg_rgb = vt_fg_idx != 0 ? term->colors[vt_fg_idx - 1] : -1; -    int vt_bg_rgb = vt_bg_idx != 0 ? term->colors[vt_bg_idx - 1] : -1;      int hl_attrs = (cell.attrs.bold ? HL_BOLD : 0)                   | (cell.attrs.italic ? HL_ITALIC : 0) @@ -561,8 +562,8 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr,          .cterm_fg_color = vt_fg_idx,          .cterm_bg_color = vt_bg_idx,          .rgb_ae_attr = (int16_t)hl_attrs, -        .rgb_fg_color = vt_fg_rgb, -        .rgb_bg_color = vt_bg_rgb, +        .rgb_fg_color = vt_fg, +        .rgb_bg_color = vt_bg,        });      } diff --git a/src/nvim/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/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 39b8e3db84..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, @@ -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,32 +387,32 @@ 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,    // 884 NA    883,    // 882, -  // 881, +  881,    // 880 NA -  // 879, -  // 878, +  879, +  878,    877,    // 876 NA    // 875 NA @@ -418,7 +420,7 @@ static int included_patches[] = {    // 873 NA    // 872 NA    // 871, -  // 870, +  870,    // 869 NA    868,    // 867 NA @@ -443,7 +445,7 @@ static int included_patches[] = {    848,    847,    // 846 NA -  // 845, +  845,    844,    843,    // 842 NA @@ -456,8 +458,8 @@ static int included_patches[] = {    835,    834,    833, -  // 832, -  // 831, +  832, +  831,    830,    // 829 NA    828, 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); | 
