diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/nvim/edit.c | 111 | ||||
| -rw-r--r-- | src/nvim/eval.c | 18 | ||||
| -rw-r--r-- | src/nvim/ex_cmds.c | 23 | ||||
| -rw-r--r-- | src/nvim/ex_getln.c | 2 | ||||
| -rw-r--r-- | src/nvim/farsi.c | 2 | ||||
| -rw-r--r-- | src/nvim/fold.c | 2 | ||||
| -rw-r--r-- | src/nvim/memline.c | 2 | ||||
| -rw-r--r-- | src/nvim/misc1.c | 40 | ||||
| -rw-r--r-- | src/nvim/ops.c | 117 | ||||
| -rw-r--r-- | src/nvim/screen.c | 24 | ||||
| -rw-r--r-- | src/nvim/spell.c | 4 | ||||
| -rw-r--r-- | src/nvim/testdir/Makefile | 2 | ||||
| -rw-r--r-- | src/nvim/testdir/test_getvar.vim | 104 | ||||
| -rw-r--r-- | src/nvim/testdir/test_highlight.vim | 535 | ||||
| -rw-r--r-- | src/nvim/testdir/test_visual.vim | 125 | ||||
| -rw-r--r-- | src/nvim/testdir/test_window_cmd.vim | 90 | ||||
| -rw-r--r-- | src/nvim/testdir/view_util.vim | 21 | ||||
| -rw-r--r-- | src/nvim/undo.c | 9 | ||||
| -rw-r--r-- | src/nvim/window.c | 47 | 
19 files changed, 1099 insertions, 179 deletions
diff --git a/src/nvim/edit.c b/src/nvim/edit.c index b5c702828c..0d99aa8fb2 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -240,8 +240,8 @@ static int ins_need_undo;               /* call u_save() before inserting a                                             char.  Set when edit() is called.                                             after that arrow_used is used. */ -static int did_add_space = FALSE;       /* auto_format() added an extra space -                                           under the cursor */ +static bool did_add_space = false;      // auto_format() added an extra space +                                        // under the cursor  static TriState dont_sync_undo = kFalse;  // CTRL-G U prevents syncing undo                                            // for the next left/right cursor @@ -1766,8 +1766,8 @@ change_indent (      /* We only put back the new line up to the cursor */      new_line[curwin->w_cursor.col] = NUL; -    /* Put back original line */ -    ml_replace(curwin->w_cursor.lnum, orig_line, FALSE); +    // Put back original line +    ml_replace(curwin->w_cursor.lnum, orig_line, false);      curwin->w_cursor.col = orig_col;      /* Backspace from cursor to start of line */ @@ -5693,8 +5693,8 @@ auto_format (    pos = curwin->w_cursor;    old = get_cursor_line_ptr(); -  /* may remove added space */ -  check_auto_format(FALSE); +  // may remove added space +  check_auto_format(false);    /* Don't format in Insert mode when the cursor is on a trailing blank, the     * user might insert normal text next.  Also skip formatting when "1" is @@ -5760,12 +5760,13 @@ auto_format (        pnew = vim_strnsave(new, len + 2);        pnew[len] = ' ';        pnew[len + 1] = NUL; -      ml_replace(curwin->w_cursor.lnum, pnew, FALSE); -      /* remove the space later */ -      did_add_space = TRUE; -    } else -      /* may remove added space */ -      check_auto_format(FALSE); +      ml_replace(curwin->w_cursor.lnum, pnew, false); +      // remove the space later +      did_add_space = true; +    } else { +      // may remove added space +      check_auto_format(false); +    }    }    check_cursor(); @@ -5776,9 +5777,8 @@ auto_format (   * delete it now.  The space must be under the cursor, just after the insert   * position.   */ -static void -check_auto_format ( -    int end_insert                     /* TRUE when ending Insert mode */ +static void check_auto_format( +    bool end_insert                   // true when ending Insert mode  )  {    int c = ' '; @@ -5786,19 +5786,19 @@ check_auto_format (    if (did_add_space) {      cc = gchar_cursor(); -    if (!WHITECHAR(cc)) -      /* Somehow the space was removed already. */ -      did_add_space = FALSE; -    else { +    if (!WHITECHAR(cc)) { +      // Somehow the space was removed already. +      did_add_space = false; +    } else {        if (!end_insert) {          inc_cursor();          c = gchar_cursor();          dec_cursor();        }        if (c != NUL) { -        /* The space is no longer at the end of the line, delete it. */ -        del_char(FALSE); -        did_add_space = FALSE; +        // The space is no longer at the end of the line, delete it. +        del_char(false); +        did_add_space = false;        }      }    } @@ -6023,8 +6023,8 @@ stop_insert (        }      } -    /* If a space was inserted for auto-formatting, remove it now. */ -    check_auto_format(TRUE); +    // If a space was inserted for auto-formatting, remove it now. +    check_auto_format(true);      /* If we just did an auto-indent, remove the white space from the end       * of the line, and put the cursor back. @@ -6043,10 +6043,12 @@ stop_insert (          if (gchar_cursor() == NUL && curwin->w_cursor.col > 0)            --curwin->w_cursor.col;          cc = gchar_cursor(); -        if (!ascii_iswhite(cc)) +        if (!ascii_iswhite(cc)) {            break; -        if (del_char(TRUE) == FAIL) -          break;            /* should not happen */ +        } +        if (del_char(true) == FAIL) { +          break;            // should not happen +        }        }        if (curwin->w_cursor.lnum != tpos.lnum)          curwin->w_cursor = tpos; @@ -6698,8 +6700,8 @@ static void replace_do_bs(int limit_col)         * text aligned. */        curwin->w_cursor.col += ins_len;        while (vcol > orig_vcols && gchar_cursor() == ' ') { -        del_char(FALSE); -        ++orig_vcols; +        del_char(false); +        orig_vcols++;        }        curwin->w_cursor.col -= ins_len;      } @@ -7441,13 +7443,15 @@ static void ins_shift(int c, int lastc)     */    if (c == Ctrl_D && (lastc == '0' || lastc == '^')        && curwin->w_cursor.col > 0) { -    --curwin->w_cursor.col; -    (void)del_char(FALSE);              /* delete the '^' or '0' */ -    /* In Replace mode, restore the characters that '^' or '0' replaced. */ -    if (State & REPLACE_FLAG) +    curwin->w_cursor.col--; +    (void)del_char(false);              // delete the '^' or '0' +    // In Replace mode, restore the characters that '^' or '0' replaced. +    if (State & REPLACE_FLAG) {        replace_pop_ins(); -    if (lastc == '^') -      old_indent = get_indent();        /* remember curr. indent */ +    } +    if (lastc == '^') { +      old_indent = get_indent();        // remember curr. indent +    }      change_indent(INDENT_SET, 0, TRUE, 0, TRUE);    } else      change_indent(c == Ctrl_D ? INDENT_DEC : INDENT_INC, 0, TRUE, 0, TRUE); @@ -7463,17 +7467,23 @@ static void ins_shift(int c, int lastc)  static void ins_del(void)  { -  int temp; - -  if (stop_arrow() == FAIL) +  if (stop_arrow() == FAIL) {      return; -  if (gchar_cursor() == NUL) {          /* delete newline */ -    temp = curwin->w_cursor.col; +  } +  if (gchar_cursor() == NUL) {          // delete newline +    const int temp = curwin->w_cursor.col;      if (!can_bs(BS_EOL)  // only if "eol" included          || do_join(2, false, true, false, false) == FAIL) {        vim_beep(BO_BS);      } else {        curwin->w_cursor.col = temp; +      // Adjust orig_line_count in case more lines have been deleted than +      // have been added. That makes sure, that open_line() later +      // can access all buffer lines correctly +      if (State & VREPLACE_FLAG +          && orig_line_count > curbuf->b_ml.ml_line_count) { +        orig_line_count = curbuf->b_ml.ml_line_count; +      }      }    } else if (del_char(false) == FAIL) {  // delete char under cursor      vim_beep(BO_BS); @@ -7499,8 +7509,9 @@ static void ins_bs_one(colnr_T *vcolp)      if (curwin->w_cursor.lnum != Insstart.lnum          || curwin->w_cursor.col >= Insstart.col)        replace_do_bs(-1); -  } else -    (void)del_char(FALSE); +  } else { +    (void)del_char(false); +  }  }  /// Handle Backspace, delete-word and delete-line in Insert mode. @@ -7764,16 +7775,16 @@ static bool ins_bs(int c, int mode, int *inserted_space_p)          else {            const bool l_enc_utf8 = enc_utf8;            const int l_p_deco = p_deco; -          if (l_enc_utf8 && l_p_deco) +          if (l_enc_utf8 && l_p_deco) {              (void)utfc_ptr2char(get_cursor_pos_ptr(), cpc); -          (void)del_char(FALSE); -          /* -           * If there are combining characters and 'delcombine' is set -           * move the cursor back.  Don't back up before the base -           * character. -           */ -          if (l_enc_utf8 && l_p_deco && cpc[0] != NUL) +          } +          (void)del_char(false); +          // If there are combining characters and 'delcombine' is set +          // move the cursor back.  Don't back up before the base +          // character. +          if (l_enc_utf8 && l_p_deco && cpc[0] != NUL) {              inc_cursor(); +          }            if (revins_chars) {              revins_chars--;              revins_legal++; diff --git a/src/nvim/eval.c b/src/nvim/eval.c index ca3c650466..9765b04922 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -10210,32 +10210,34 @@ static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)  static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)  {    win_T *oldcurwin; -  tabpage_T *tp, *oldtabpage; -  dictitem_T  *v; +  tabpage_T *oldtabpage;    bool done = false;    rettv->v_type = VAR_STRING;    rettv->vval.v_string = NULL;    const char *const varname = tv_get_string_chk(&argvars[1]); -  tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); +  tabpage_T *const tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL));    if (tp != NULL && varname != NULL) {      // Set tp to be our tabpage, temporarily.  Also set the window to the      // first window in the tabpage, otherwise the window is not valid. -    win_T *window = tp->tp_firstwin == NULL ? firstwin : tp->tp_firstwin; +    win_T *const window = tp == curtab || tp->tp_firstwin == NULL +        ? firstwin +        : tp->tp_firstwin;      if (switch_win(&oldcurwin, &oldtabpage, window, tp, true) == OK) {        // look up the variable        // Let gettabvar({nr}, "") return the "t:" dictionary. -      v = find_var_in_ht(&tp->tp_vars->dv_hashtab, 't', -                         varname, strlen(varname), false); +      const dictitem_T *const v = find_var_in_ht(&tp->tp_vars->dv_hashtab, 't', +                                                 varname, strlen(varname), +                                                 false);        if (v != NULL) {          tv_copy(&v->di_tv, rettv);          done = true;        }      } -    /* restore previous notion of curwin */ -    restore_win(oldcurwin, oldtabpage, TRUE); +    // restore previous notion of curwin +    restore_win(oldcurwin, oldtabpage, true);    }    if (!done && argvars[2].v_type != VAR_UNKNOWN) { diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index e99de1ad41..1420a9aae4 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -716,11 +716,13 @@ void ex_retab(exarg_T *eap)              memmove(new_line + start_col + len,                  ptr + col, (size_t)(old_len - col + 1));              ptr = new_line + start_col; -            for (col = 0; col < len; col++) +            for (col = 0; col < len; col++) {                ptr[col] = (col < num_tabs) ? '\t' : ' '; -            ml_replace(lnum, new_line, FALSE); -            if (first_line == 0) +            } +            ml_replace(lnum, new_line, false); +            if (first_line == 0) {                first_line = lnum; +            }              last_line = lnum;              ptr = new_line;              col = start_col + len; @@ -1598,6 +1600,7 @@ void ex_file(exarg_T *eap)      // print full file name if :cd used      fileinfo(false, false, eap->forceit);    } +  redraw_tabline = true;  }  /* @@ -3624,7 +3627,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout,                  // before the cursor.                  len_change = (int)STRLEN(new_line) - (int)STRLEN(orig_line);                  curwin->w_cursor.col += len_change; -                ml_replace(lnum, new_line, FALSE); +                ml_replace(lnum, new_line, false);                }                search_match_lines = regmatch.endpos[0].lnum @@ -3666,9 +3669,10 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout,                msg_col = 0;                gotocmdline(TRUE); -              /* restore the line */ -              if (orig_line != NULL) -                ml_replace(lnum, orig_line, FALSE); +              // restore the line +              if (orig_line != NULL) { +                ml_replace(lnum, orig_line, false); +              }              }              need_wait_return = FALSE;             /* no hit-return prompt */ @@ -3925,9 +3929,10 @@ skip:              prev_matchcol = (colnr_T)STRLEN(sub_firstline)                              - prev_matchcol; -            if (u_savesub(lnum) != OK) +            if (u_savesub(lnum) != OK) {                break; -            ml_replace(lnum, new_start, TRUE); +            } +            ml_replace(lnum, new_start, true);              if (nmatch_tl > 0) {                /* diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 353f724fe4..1810056a4a 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -6084,7 +6084,7 @@ static int open_cmdwin(void)    /* Replace the empty last line with the current command-line and put the     * cursor there. */ -  ml_replace(curbuf->b_ml.ml_line_count, ccline.cmdbuff, TRUE); +  ml_replace(curbuf->b_ml.ml_line_count, ccline.cmdbuff, true);    curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;    curwin->w_cursor.col = ccline.cmdpos;    changed_line_abv_curs(); diff --git a/src/nvim/farsi.c b/src/nvim/farsi.c index 6de84fbf4d..9862c50ab9 100644 --- a/src/nvim/farsi.c +++ b/src/nvim/farsi.c @@ -1081,7 +1081,7 @@ int fkmap(int c)        if (gchar_cursor() == _LAM) {          chg_l_toXor_X(); -        del_char(FALSE); +        del_char(false);          AppendCharToRedobuff(K_BS);          if (!p_ri) { diff --git a/src/nvim/fold.c b/src/nvim/fold.c index 6aae927483..b00c45413a 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -1687,7 +1687,7 @@ static void foldDelMarker(linenr_T lnum, char_u *marker, size_t markerlen)        assert(p >= line);        memcpy(newline, line, (size_t)(p - line));        STRCPY(newline + (p - line), p + len); -      ml_replace(lnum, newline, FALSE); +      ml_replace(lnum, newline, false);      }      break;    } diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 84ceaf0973..9a1e61d40b 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -2378,7 +2378,7 @@ static int ml_delete_int(buf_T *buf, linenr_T lnum, int message)          )        set_keep_msg((char_u *)_(no_lines_msg), 0); -    i = ml_replace((linenr_T)1, (char_u *)"", TRUE); +    i = ml_replace((linenr_T)1, (char_u *)"", true);      buf->b_ml.ml_flags |= ML_EMPTY;      return i; diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index 72490a376f..84d8c995a6 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -771,7 +771,7 @@ open_line (        (void)u_save_cursor();                        /* errors are ignored! */        vr_lines_changed++;      } -    ml_replace(curwin->w_cursor.lnum, p_extra, TRUE); +    ml_replace(curwin->w_cursor.lnum, p_extra, true);      changed_bytes(curwin->w_cursor.lnum, 0);      curwin->w_cursor.lnum--;      did_append = FALSE; @@ -831,12 +831,13 @@ open_line (    if (dir == FORWARD) {      if (trunc_line || (State & INSERT)) { -      /* truncate current line at cursor */ +      // truncate current line at cursor        saved_line[curwin->w_cursor.col] = NUL; -      /* Remove trailing white space, unless OPENLINE_KEEPTRAIL used. */ -      if (trunc_line && !(flags & OPENLINE_KEEPTRAIL)) +      // Remove trailing white space, unless OPENLINE_KEEPTRAIL used. +      if (trunc_line && !(flags & OPENLINE_KEEPTRAIL)) {          truncate_spaces(saved_line); -      ml_replace(curwin->w_cursor.lnum, saved_line, FALSE); +      } +      ml_replace(curwin->w_cursor.lnum, saved_line, false);        saved_line = NULL;        if (did_append) {          changed_lines(curwin->w_cursor.lnum, curwin->w_cursor.col, @@ -912,8 +913,8 @@ open_line (      /* Put new line in p_extra */      p_extra = vim_strsave(get_cursor_line_ptr()); -    /* Put back original line */ -    ml_replace(curwin->w_cursor.lnum, next_line, FALSE); +    // Put back original line +    ml_replace(curwin->w_cursor.lnum, next_line, false);      /* Insert new stuff into line again */      curwin->w_cursor.col = 0; @@ -1497,8 +1498,8 @@ void ins_char_bytes(char_u *buf, size_t charlen)      p[i] = ' ';    } -  /* Replace the line in the buffer. */ -  ml_replace(lnum, newp, FALSE); +  // Replace the line in the buffer. +  ml_replace(lnum, newp, false);    // mark the buffer as changed and prepare for displaying    changed_bytes(lnum, (colnr_T)col); @@ -1548,19 +1549,17 @@ void ins_str(char_u *s)      memmove(newp, oldp, (size_t)col);    memmove(newp + col, s, (size_t)newlen);    memmove(newp + col + newlen, oldp + col, (size_t)(oldlen - col + 1)); -  ml_replace(lnum, newp, FALSE); +  ml_replace(lnum, newp, false);    changed_bytes(lnum, col);    curwin->w_cursor.col += newlen;  } -/* - * Delete one character under the cursor. - * If "fixpos" is TRUE, don't leave the cursor on the NUL after the line. - * Caller must have prepared for undo. - * - * return FAIL for failure, OK otherwise - */ -int del_char(int fixpos) +// Delete one character under the cursor. +// If "fixpos" is true, don't leave the cursor on the NUL after the line. +// Caller must have prepared for undo. +// +// return FAIL for failure, OK otherwise +int del_char(bool fixpos)  {    if (has_mbyte) {      /* Make sure the cursor is at the start of a character. */ @@ -1666,8 +1665,9 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine)      memmove(newp, oldp, (size_t)col);    }    memmove(newp + col, oldp + col + count, (size_t)movelen); -  if (!was_alloced) -    ml_replace(lnum, newp, FALSE); +  if (!was_alloced) { +    ml_replace(lnum, newp, false); +  }    /* mark the buffer as changed and prepare for displaying */    changed_bytes(lnum, curwin->w_cursor.col); diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 041443d472..7defde731a 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -310,30 +310,32 @@ void shift_line(   */  static void shift_block(oparg_T *oap, int amount)  { -  int left = (oap->op_type == OP_LSHIFT); -  int oldstate = State; -  int total; -  char_u              *newp, *oldp; -  int oldcol = curwin->w_cursor.col; -  int p_sw = get_sw_value(curbuf); -  int p_ts = (int)curbuf->b_p_ts; +  const bool left = (oap->op_type == OP_LSHIFT); +  const int oldstate = State; +  char_u *newp; +  const int oldcol = curwin->w_cursor.col; +  const int p_sw = get_sw_value(curbuf); +  const int p_ts = (int)curbuf->b_p_ts;    struct block_def bd;    int incr; -  colnr_T ws_vcol;    int i = 0, j = 0; -  int len; -  int old_p_ri = p_ri; +  const int old_p_ri = p_ri;    p_ri = 0;                     /* don't want revins in indent */ -  State = INSERT;               /* don't want REPLACE for State */ -  block_prep(oap, &bd, curwin->w_cursor.lnum, TRUE); -  if (bd.is_short) +  State = INSERT;               // don't want REPLACE for State +  block_prep(oap, &bd, curwin->w_cursor.lnum, true); +  if (bd.is_short) {      return; +  } -  /* total is number of screen columns to be inserted/removed */ -  total = amount * p_sw; -  oldp = get_cursor_line_ptr(); +  // total is number of screen columns to be inserted/removed +  int total = (int)((unsigned)amount * (unsigned)p_sw); +  if ((total / p_sw) != amount) { +    return;   // multiplication overflow +  } + +  char_u *const oldp = get_cursor_line_ptr();    if (!left) {      /* @@ -342,8 +344,8 @@ static void shift_block(oparg_T *oap, int amount)       *  3. Divvy into TABs & spp       *  4. Construct new string       */ -    total += bd.pre_whitesp;     /* all virtual WS up to & incl a split TAB */ -    ws_vcol = bd.start_vcol - bd.pre_whitesp; +    total += bd.pre_whitesp;    // all virtual WS up to & incl a split TAB +    colnr_T ws_vcol = bd.start_vcol - bd.pre_whitesp;      if (bd.startspaces) {        if (has_mbyte) {          if ((*mb_ptr2len)(bd.textstart) == 1) { @@ -372,8 +374,8 @@ static void shift_block(oparg_T *oap, int amount)        j = total;      /* if we're splitting a TAB, allow for it */      bd.textcol -= bd.pre_whitesp_c - (bd.startspaces != 0); -    len = (int)STRLEN(bd.textstart) + 1; -    newp = (char_u *) xmalloc((size_t)(bd.textcol + i + j + len)); +    const int len = (int)STRLEN(bd.textstart) + 1; +    newp = (char_u *)xmalloc((size_t)(bd.textcol + i + j + len));      memset(newp, NUL, (size_t)(bd.textcol + i + j + len));      memmove(newp, oldp, (size_t)bd.textcol);      memset(newp + bd.textcol, TAB, (size_t)i); @@ -390,10 +392,7 @@ static void shift_block(oparg_T *oap, int amount)      size_t fill;                  // nr of spaces that replace a TAB      size_t new_line_len;          // the length of the line after the                                    // block shift -    colnr_T block_space_width; -    colnr_T shift_amount;      char_u      *non_white = bd.textstart; -    colnr_T non_white_col;      /*       * Firstly, let's find the first non-whitespace character that is @@ -410,19 +409,20 @@ static void shift_block(oparg_T *oap, int amount)        MB_PTR_ADV(non_white);      } -    /* The character's column is in "bd.start_vcol".  */ -    non_white_col = bd.start_vcol; +    // The character's column is in "bd.start_vcol". +    colnr_T non_white_col = bd.start_vcol;      while (ascii_iswhite(*non_white)) {        incr = lbr_chartabsize_adv(bd.textstart, &non_white, non_white_col);        non_white_col += incr;      } -    block_space_width = non_white_col - oap->start_vcol; -    /* We will shift by "total" or "block_space_width", whichever is less. -     */ -    shift_amount = (block_space_width < total ? block_space_width : total); +    const colnr_T block_space_width = non_white_col - oap->start_vcol; +    // We will shift by "total" or "block_space_width", whichever is less. +    const colnr_T shift_amount = block_space_width < total +        ? block_space_width +        : total;      // The column to which we will shift the text.      destination_col = non_white_col - shift_amount; @@ -454,7 +454,7 @@ static void shift_block(oparg_T *oap, int amount)      fill = (size_t)(destination_col - verbatim_copy_width);      assert(verbatim_copy_end - oldp >= 0); -    size_t verbatim_diff = (size_t)(verbatim_copy_end - oldp); +    const size_t verbatim_diff = (size_t)(verbatim_copy_end - oldp);      // The replacement line will consist of:      // - the beginning of the original line up to "verbatim_copy_end",      // - "fill" number of spaces, @@ -466,8 +466,8 @@ static void shift_block(oparg_T *oap, int amount)      memset(newp + verbatim_diff, ' ', fill);      STRMOVE(newp + verbatim_diff + fill, non_white);    } -  /* replace the line */ -  ml_replace(curwin->w_cursor.lnum, newp, FALSE); +  // replace the line +  ml_replace(curwin->w_cursor.lnum, newp, false);    changed_bytes(curwin->w_cursor.lnum, (colnr_T)bd.textcol);    State = oldstate;    curwin->w_cursor.col = oldcol; @@ -561,7 +561,7 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def        offset += count;      STRMOVE(newp + offset, oldp); -    ml_replace(lnum, newp, FALSE); +    ml_replace(lnum, newp, false);      if (lnum == oap->end.lnum) {        /* Set "']" mark to the end of the block instead of the end of @@ -1427,10 +1427,11 @@ int op_delete(oparg_T *oap)        return FAIL;      } -    for (lnum = curwin->w_cursor.lnum; lnum <= oap->end.lnum; ++lnum) { -      block_prep(oap, &bd, lnum, TRUE); -      if (bd.textlen == 0)              /* nothing to delete */ +    for (lnum = curwin->w_cursor.lnum; lnum <= oap->end.lnum; lnum++) { +      block_prep(oap, &bd, lnum, true); +      if (bd.textlen == 0) {            // nothing to delete          continue; +      }        /* Adjust cursor position for tab replaced by spaces and 'lbr'. */        if (lnum == curwin->w_cursor.lnum) { @@ -1656,11 +1657,12 @@ int op_replace(oparg_T *oap, int c)     */    if (oap->motion_type == kMTBlockWise) {      bd.is_MAX = (curwin->w_curswant == MAXCOL); -    for (; curwin->w_cursor.lnum <= oap->end.lnum; ++curwin->w_cursor.lnum) { -      curwin->w_cursor.col = 0;        /* make sure cursor position is valid */ -      block_prep(oap, &bd, curwin->w_cursor.lnum, TRUE); -      if (bd.textlen == 0 && (!virtual_op || bd.is_MAX)) -        continue;                   /* nothing to replace */ +    for (; curwin->w_cursor.lnum <= oap->end.lnum; curwin->w_cursor.lnum++) { +      curwin->w_cursor.col = 0;       // make sure cursor position is valid +      block_prep(oap, &bd, curwin->w_cursor.lnum, true); +      if (bd.textlen == 0 && (!virtual_op || bd.is_MAX)) { +        continue;                     // nothing to replace +      }        /* n == number of extra chars required         * If we split a TAB, it may be replaced by several characters. @@ -1747,8 +1749,8 @@ int op_replace(oparg_T *oap, int c)          after_p = (char_u *)xmalloc(after_p_len);          memmove(after_p, oldp, after_p_len);        } -      /* replace the line */ -      ml_replace(curwin->w_cursor.lnum, newp, FALSE); +      // replace the line +      ml_replace(curwin->w_cursor.lnum, newp, false);        if (after_p != NULL) {          ml_append(curwin->w_cursor.lnum++, after_p, (int)after_p_len, false);          appended_lines_mark(curwin->w_cursor.lnum, 1L); @@ -1852,7 +1854,7 @@ void op_tilde(oparg_T *oap)      for (; pos.lnum <= oap->end.lnum; pos.lnum++) {        int one_change; -      block_prep(oap, &bd, pos.lnum, FALSE); +      block_prep(oap, &bd, pos.lnum, false);        pos.col = bd.textcol;        one_change = swapchars(oap->op_type, &pos, bd.textlen);        did_change |= one_change; @@ -1956,7 +1958,7 @@ int swapchar(int op_type, pos_T *pos)      /* Special handling of German sharp s: change to "SS". */      curwin->w_cursor = *pos; -    del_char(FALSE); +    del_char(false);      ins_char('S');      ins_char('S');      curwin->w_cursor = sp; @@ -2030,8 +2032,8 @@ void op_insert(oparg_T *oap, long count1)          --curwin->w_cursor.col;        ve_flags = old_ve_flags;      } -    /* Get the info about the block before entering the text */ -    block_prep(oap, &bd, oap->start.lnum, TRUE); +    // Get the info about the block before entering the text +    block_prep(oap, &bd, oap->start.lnum, true);      firstline = ml_get(oap->start.lnum) + bd.textcol;      if (oap->op_type == OP_APPEND)        firstline += bd.textlen; @@ -2119,7 +2121,7 @@ void op_insert(oparg_T *oap, long count1)       * tabs.  Get the starting column again and correct the length.       * Don't do this when "$" used, end-of-line will have changed.       */ -    block_prep(oap, &bd2, oap->start.lnum, TRUE); +    block_prep(oap, &bd2, oap->start.lnum, true);      if (!bd.is_MAX || bd2.textlen < bd.textlen) {        if (oap->op_type == OP_APPEND) {          pre_textlen += bd2.textlen - bd.textlen; @@ -2239,7 +2241,7 @@ int op_change(oparg_T *oap)        STRLCPY(ins_text, firstline + bd.textcol, ins_len + 1);        for (linenr = oap->start.lnum + 1; linenr <= oap->end.lnum;             linenr++) { -        block_prep(oap, &bd, linenr, TRUE); +        block_prep(oap, &bd, linenr, true);          if (!bd.is_short || virtual_op) {            pos_T vpos; @@ -2263,7 +2265,7 @@ int op_change(oparg_T *oap)            offset += ins_len;            oldp += bd.textcol;            STRMOVE(newp + offset, oldp); -          ml_replace(linenr, newp, FALSE); +          ml_replace(linenr, newp, false);          }        }        check_cursor(); @@ -3117,10 +3119,10 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)              ptr += yanklen;            }            STRMOVE(ptr, oldp + col); -          ml_replace(lnum, newp, FALSE); -          /* Place cursor on last putted char. */ +          ml_replace(lnum, newp, false); +          // Place cursor on last putted char.            if (lnum == curwin->w_cursor.lnum) { -            /* make sure curwin->w_virtcol is updated */ +            // make sure curwin->w_virtcol is updated              changed_cline_bef_curs();              curwin->w_cursor.col += (colnr_T)(totlen - 1);            } @@ -3164,7 +3166,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)            memmove(newp, oldp, (size_t)col);            /* append to first line */            memmove(newp + col, y_array[0], (size_t)(yanklen + 1)); -          ml_replace(lnum, newp, FALSE); +          ml_replace(lnum, newp, false);            curwin->w_cursor.lnum = lnum;            i = 1; @@ -3687,7 +3689,7 @@ int do_join(size_t count,        curr = skipwhite(curr);      currsize = (int)STRLEN(curr);    } -  ml_replace(curwin->w_cursor.lnum, newp, FALSE); +  ml_replace(curwin->w_cursor.lnum, newp, false);    if (setmark) {      // Set the '] mark. @@ -4239,7 +4241,8 @@ int paragraph_start(linenr_T lnum)   * - start/endspaces is the number of columns of the first/last yanked char   *   that are to be yanked.   */ -static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, int is_del) +static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, +                       bool is_del)  {    int incr = 0;    char_u      *pend; @@ -5376,7 +5379,7 @@ void cursor_pos_info(dict_T *dict)          switch (l_VIsual_mode) {          case Ctrl_V:            virtual_op = virtual_active(); -          block_prep(&oparg, &bd, lnum, 0); +          block_prep(&oparg, &bd, lnum, false);            virtual_op = kNone;            s = bd.textstart;            len = (long)bd.textlen; diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 8e7a84c6b1..f7fdc6060d 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -2197,7 +2197,7 @@ win_line (    match_T     *shl;                     // points to search_hl or a match    int shl_flag;                         // flag to indicate whether search_hl                                          // has been processed or not -  int prevcol_hl_flag;                  // flag to indicate whether prevcol +  bool prevcol_hl_flag;                 // flag to indicate whether prevcol                                          // equals startcol of search_hl or one                                          // of the matches    int prev_c = 0;                       // previous Arabic character @@ -3026,6 +3026,12 @@ win_line (            if (shl != &search_hl && cur != NULL)              cur = cur->next;          } +        // Only highlight one character after the last column. +        if (*ptr == NUL +            && (did_line_attr >= 1 +                || (wp->w_p_list && lcs_eol_one == -1))) { +          search_attr = 0; +        }        }        if (diff_hlf != (hlf_T)0) { @@ -3674,7 +3680,9 @@ win_line (            // don't do search HL for the rest of the line            if ((line_attr_lowprio || line_attr) -              && char_attr == search_attr && col > 0) { +              && char_attr == search_attr +              && (did_line_attr > 1 +                  || (wp->w_p_list && lcs_eol > 0))) {              char_attr = line_attr;            }            if (diff_hlf == HLF_TXD) { @@ -3833,9 +3841,12 @@ win_line (                     || lnum == VIsual.lnum                     || lnum == curwin->w_cursor.lnum)                 && c == NUL) -              /* highlight 'hlsearch' match at end of line */ -              || (prevcol_hl_flag == TRUE && did_line_attr <= 1) -              )) { +              // highlight 'hlsearch' match at end of line +              || (prevcol_hl_flag +                  && !(wp->w_p_cul && lnum == wp->w_cursor.lnum +                       && !(wp == curwin && VIsual_active)) +                  && diff_hlf == (hlf_T)0 +                  && did_line_attr <= 1))) {          int n = 0;          if (wp->w_p_rl) { @@ -5805,7 +5816,8 @@ void screen_fill(int start_row, int end_row, int start_col, int end_col, int c1,      // TODO(bfredl): The relevant caller should do this      if (row == Rows - 1) {  // overwritten the command line        redraw_cmdline = true; -      if (c1 == ' ' && c2 == ' ') { +      if (start_col == 0 && end_col == Columns +          && c1 == ' ' && c2 == ' ' && attr == 0) {          clear_cmdline = false;  // command line has been cleared        }        if (start_col == 0) { diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 7f1cc98849..1cb679245b 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -2941,7 +2941,7 @@ void spell_suggest(int count)      memmove(p, line, c);      STRCPY(p + c, stp->st_word);      STRCAT(p, sug.su_badptr + stp->st_orglen); -    ml_replace(curwin->w_cursor.lnum, p, FALSE); +    ml_replace(curwin->w_cursor.lnum, p, false);      curwin->w_cursor.col = c;      // For redo we use a change-word command. @@ -3059,7 +3059,7 @@ void ex_spellrepall(exarg_T *eap)        memmove(p, line, curwin->w_cursor.col);        STRCPY(p + curwin->w_cursor.col, repl_to);        STRCAT(p, line + curwin->w_cursor.col + STRLEN(repl_from)); -      ml_replace(curwin->w_cursor.lnum, p, FALSE); +      ml_replace(curwin->w_cursor.lnum, p, false);        changed_bytes(curwin->w_cursor.lnum, curwin->w_cursor.col);        if (curwin->w_cursor.lnum != prev_lnum) { diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index 0379235ec0..361db47fc7 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -60,12 +60,14 @@ NEW_TESTS ?= \  	    test_fnameescape.res \  	    test_fold.res \  	    test_ga.res \ +	    test_getvar.res \  	    test_glob2regpat.res \  	    test_gf.res \  	    test_gn.res \  	    test_hardcopy.res \  	    test_help_tagjump.res \  	    test_hide.res \ +	    test_highlight.res \  	    test_history.res \  	    test_hlsearch.res \  	    test_increment.res \ diff --git a/src/nvim/testdir/test_getvar.vim b/src/nvim/testdir/test_getvar.vim new file mode 100644 index 0000000000..d6b6b69aa8 --- /dev/null +++ b/src/nvim/testdir/test_getvar.vim @@ -0,0 +1,104 @@ +" Tests for getwinvar(), gettabvar() and gettabwinvar(). +func Test_var() +  " Use strings to test for memory leaks.  First, check that in an empty +  " window, gettabvar() returns the correct value +  let t:testvar='abcd' +  call assert_equal('abcd', gettabvar(1, 'testvar')) +  call assert_equal('abcd', gettabvar(1, 'testvar')) + +  " test for getwinvar() +  let w:var_str = "Dance" +  let def_str = "Chance" +  call assert_equal('Dance', getwinvar(1, 'var_str')) +  call assert_equal('Dance', getwinvar(1, 'var_str', def_str)) +  call assert_equal({'var_str': 'Dance'}, getwinvar(1, '')) +  call assert_equal({'var_str': 'Dance'}, getwinvar(1, '', def_str)) +  unlet w:var_str +  call assert_equal('Chance', getwinvar(1, 'var_str', def_str)) +  call assert_equal({}, getwinvar(1, '')) +  call assert_equal({}, getwinvar(1, '', def_str)) +  call assert_equal('', getwinvar(9, '')) +  call assert_equal('Chance', getwinvar(9, '', def_str)) +  call assert_equal(0, getwinvar(1, '&nu')) +  call assert_equal(0, getwinvar(1, '&nu', 1)) +  unlet def_str + +  " test for gettabvar() +  tabnew +  tabnew +  let t:var_list = [1, 2, 3] +  let t:other = 777 +  let def_list = [4, 5, 6, 7] +  tabrewind +  call assert_equal([1, 2, 3], gettabvar(3, 'var_list')) +  call assert_equal([1, 2, 3], gettabvar(3, 'var_list', def_list)) +  call assert_equal({'var_list': [1, 2, 3], 'other': 777}, gettabvar(3, '')) +  call assert_equal({'var_list': [1, 2, 3], 'other': 777}, +					\ gettabvar(3, '', def_list)) + +  tablast +  unlet t:var_list +  tabrewind +  call assert_equal([4, 5, 6, 7], gettabvar(3, 'var_list', def_list)) +  call assert_equal('', gettabvar(9, '')) +  call assert_equal([4, 5, 6, 7], gettabvar(9, '', def_list)) +  call assert_equal('', gettabvar(3, '&nu')) +  call assert_equal([4, 5, 6, 7], gettabvar(3, '&nu', def_list)) +  unlet def_list +  tabonly + +  " test for gettabwinvar() +  tabnew +  tabnew +  tabprev +  split +  split +  wincmd w +  vert split +  wincmd w +  let w:var_dict = {'dict': 'tabwin'} +  let def_dict = {'dict2': 'newval'} +  wincmd b +  tabrewind +  call assert_equal({'dict': 'tabwin'}, gettabwinvar(2, 3, 'var_dict')) +  call assert_equal({'dict': 'tabwin'}, +				\ gettabwinvar(2, 3, 'var_dict', def_dict)) +  call assert_equal({'var_dict': {'dict': 'tabwin'}}, gettabwinvar(2, 3, '')) +  call assert_equal({'var_dict': {'dict': 'tabwin'}}, +				\ gettabwinvar(2, 3, '', def_dict)) + +  tabnext +  3wincmd w +  unlet w:var_dict +  tabrewind +  call assert_equal({'dict2': 'newval'}, +				\ gettabwinvar(2, 3, 'var_dict', def_dict)) +  call assert_equal({}, gettabwinvar(2, 3, '')) +  call assert_equal({}, gettabwinvar(2, 3, '', def_dict)) +  call assert_equal("", gettabwinvar(2, 9, '')) +  call assert_equal({'dict2': 'newval'}, gettabwinvar(2, 9, '', def_dict)) +  call assert_equal('', gettabwinvar(9, 3, '')) +  call assert_equal({'dict2': 'newval'}, gettabwinvar(9, 3, '', def_dict)) + +  unlet def_dict + +  call assert_equal('', gettabwinvar(2, 3, '&nux')) +  call assert_equal(1, gettabwinvar(2, 3, '&nux', 1)) +  tabonly +endfunc + +" It was discovered that "gettabvar()" would fail if called from within the +" tabline when the user closed a window.  This test confirms the fix. +func Test_gettabvar_in_tabline() +  let t:var_str = 'value' + +  set tabline=%{assert_equal('value',gettabvar(1,'var_str'))} +  set showtabline=2 + +  " Simulate the user opening a split (which becomes window #1) and then +  " closing the split, which triggers the redrawing of the tabline. +  leftabove split +  redrawstatus! +  close +  redrawstatus! +endfunc diff --git a/src/nvim/testdir/test_highlight.vim b/src/nvim/testdir/test_highlight.vim new file mode 100644 index 0000000000..33df79581c --- /dev/null +++ b/src/nvim/testdir/test_highlight.vim @@ -0,0 +1,535 @@ +" Tests for ":highlight" and highlighting. + +source view_util.vim + +func Test_highlight() +  " basic test if ":highlight" doesn't crash +  highlight +  hi Search + +  " test setting colors. +  " test clearing one color and all doesn't generate error or warning +  silent! hi NewGroup term=bold cterm=italic ctermfg=DarkBlue ctermbg=Grey gui= guifg=#00ff00 guibg=Cyan +  silent! hi Group2 term= cterm= +  hi Group3 term=underline cterm=bold + +  let res = split(execute("hi NewGroup"), "\n")[0] +  " filter ctermfg and ctermbg, the numbers depend on the terminal +  let res = substitute(res, 'ctermfg=\d*', 'ctermfg=2', '') +  let res = substitute(res, 'ctermbg=\d*', 'ctermbg=3', '') +  call assert_equal("NewGroup       xxx cterm=italic ctermfg=2 ctermbg=3", +				\ res) +  call assert_equal("Group2         xxx cleared", +				\ split(execute("hi Group2"), "\n")[0]) +  call assert_equal("Group3         xxx cterm=bold", +				\ split(execute("hi Group3"), "\n")[0]) + +  hi clear NewGroup +  call assert_equal("NewGroup       xxx cleared", +				\ split(execute("hi NewGroup"), "\n")[0]) +  call assert_equal("Group2         xxx cleared", +				\ split(execute("hi Group2"), "\n")[0]) +  hi Group2 NONE +  call assert_equal("Group2         xxx cleared", +				\ split(execute("hi Group2"), "\n")[0]) +  hi clear +  call assert_equal("Group3         xxx cleared", +				\ split(execute("hi Group3"), "\n")[0]) +  call assert_fails("hi Crash term='asdf", "E475:") +endfunc + +function! HighlightArgs(name) +  return 'hi ' . substitute(split(execute('hi ' . a:name), '\n')[0], '\<xxx\>', '', '') +endfunction + +function! IsColorable() +  return has('gui_running') || str2nr(&t_Co) >= 8 +endfunction + +function! HiCursorLine() +  let hiCursorLine = HighlightArgs('CursorLine') +  if has('gui_running') +    let guibg = matchstr(hiCursorLine, 'guibg=\w\+') +    let hi_ul = 'hi CursorLine gui=underline guibg=NONE' +    let hi_bg = 'hi CursorLine gui=NONE ' . guibg +  else +    let hi_ul = 'hi CursorLine cterm=underline ctermbg=NONE' +    let hi_bg = 'hi CursorLine cterm=NONE ctermbg=Gray' +  endif +  return [hiCursorLine, hi_ul, hi_bg] +endfunction + +function! Check_lcs_eol_attrs(attrs, row, col) +  let save_lcs = &lcs +  set list + +  call assert_equal(a:attrs, ScreenAttrs(a:row, a:col)[0]) + +  set nolist +  let &lcs = save_lcs +endfunction + +func Test_highlight_eol_with_cursorline() +  let [hiCursorLine, hi_ul, hi_bg] = HiCursorLine() + +  call NewWindow('topleft 5', 20) +  call setline(1, 'abcd') +  call matchadd('Search', '\n') + +  " expected: +  " 'abcd      ' +  "  ^^^^ ^^^^^   no highlight +  "      ^        'Search' highlight +  let attrs0 = ScreenAttrs(1, 10)[0] +  call assert_equal(repeat([attrs0[0]], 4), attrs0[0:3]) +  call assert_equal(repeat([attrs0[0]], 5), attrs0[5:9]) +  call assert_notequal(attrs0[0], attrs0[4]) + +  setlocal cursorline + +  " underline +  exe hi_ul + +  " expected: +  " 'abcd      ' +  "  ^^^^         underline +  "      ^        'Search' highlight with underline +  "       ^^^^^   underline +  let attrs = ScreenAttrs(1, 10)[0] +  call assert_equal(repeat([attrs[0]], 4), attrs[0:3]) +  call assert_equal([attrs[4]] + repeat([attrs[5]], 5), attrs[4:9]) +  call assert_notequal(attrs[0], attrs[4]) +  call assert_notequal(attrs[4], attrs[5]) +  call assert_notequal(attrs0[0], attrs[0]) +  call assert_notequal(attrs0[4], attrs[4]) +  call Check_lcs_eol_attrs(attrs, 1, 10) + +  if IsColorable() +    " bg-color +    exe hi_bg + +    " expected: +    " 'abcd      ' +    "  ^^^^         bg-color of 'CursorLine' +    "      ^        'Search' highlight +    "       ^^^^^   bg-color of 'CursorLine' +    let attrs = ScreenAttrs(1, 10)[0] +    call assert_equal(repeat([attrs[0]], 4), attrs[0:3]) +    call assert_equal(repeat([attrs[5]], 5), attrs[5:9]) +    call assert_equal(attrs0[4], attrs[4]) +    call assert_notequal(attrs[0], attrs[4]) +    call assert_notequal(attrs[4], attrs[5]) +    call assert_notequal(attrs0[0], attrs[0]) +    call assert_notequal(attrs0[5], attrs[5]) +    call Check_lcs_eol_attrs(attrs, 1, 10) +  endif + +  call CloseWindow() +  exe hiCursorLine +endfunc + +func Test_highlight_eol_with_cursorline_vertsplit() +  if !has('vertsplit') +    return +  endif + +  let [hiCursorLine, hi_ul, hi_bg] = HiCursorLine() + +  call NewWindow('topleft 5', 5) +  call setline(1, 'abcd') +  call matchadd('Search', '\n') + +  let expected = "abcd |abcd     " +  let actual = ScreenLines(1, 15)[0] +  call assert_equal(expected, actual) + +  " expected: +  " 'abcd |abcd     ' +  "  ^^^^  ^^^^^^^^^   no highlight +  "      ^             'Search' highlight +  "       ^            'VertSplit' highlight +  let attrs0 = ScreenAttrs(1, 15)[0] +  call assert_equal(repeat([attrs0[0]], 4), attrs0[0:3]) +  call assert_equal(repeat([attrs0[0]], 9), attrs0[6:14]) +  call assert_notequal(attrs0[0], attrs0[4]) +  call assert_notequal(attrs0[0], attrs0[5]) +  call assert_notequal(attrs0[4], attrs0[5]) + +  setlocal cursorline + +  " expected: +  " 'abcd |abcd     ' +  "  ^^^^              underline +  "      ^             'Search' highlight with underline +  "       ^            'VertSplit' highlight +  "        ^^^^^^^^^   no highlight + +  " underline +  exe hi_ul + +  let actual = ScreenLines(1, 15)[0] +  call assert_equal(expected, actual) + +  let attrs = ScreenAttrs(1, 15)[0] +  call assert_equal(repeat([attrs[0]], 4), attrs[0:3]) +  call assert_equal(repeat([attrs[6]], 9), attrs[6:14]) +  call assert_equal(attrs0[5:14], attrs[5:14]) +  call assert_notequal(attrs[0], attrs[4]) +  call assert_notequal(attrs[0], attrs[5]) +  call assert_notequal(attrs[0], attrs[6]) +  call assert_notequal(attrs[4], attrs[5]) +  call assert_notequal(attrs[5], attrs[6]) +  call assert_notequal(attrs0[0], attrs[0]) +  call assert_notequal(attrs0[4], attrs[4]) +  call Check_lcs_eol_attrs(attrs, 1, 15) + +  if IsColorable() +    " bg-color +    exe hi_bg + +    let actual = ScreenLines(1, 15)[0] +    call assert_equal(expected, actual) + +    let attrs = ScreenAttrs(1, 15)[0] +    call assert_equal(repeat([attrs[0]], 4), attrs[0:3]) +    call assert_equal(repeat([attrs[6]], 9), attrs[6:14]) +    call assert_equal(attrs0[5:14], attrs[5:14]) +    call assert_notequal(attrs[0], attrs[4]) +    call assert_notequal(attrs[0], attrs[5]) +    call assert_notequal(attrs[0], attrs[6]) +    call assert_notequal(attrs[4], attrs[5]) +    call assert_notequal(attrs[5], attrs[6]) +    call assert_notequal(attrs0[0], attrs[0]) +    call assert_equal(attrs0[4], attrs[4]) +    call Check_lcs_eol_attrs(attrs, 1, 15) +  endif + +  call CloseWindow() +  exe hiCursorLine +endfunc + +func Test_highlight_eol_with_cursorline_rightleft() +  if !has('rightleft') +    return +  endif + +  let [hiCursorLine, hi_ul, hi_bg] = HiCursorLine() + +  call NewWindow('topleft 5', 10) +  setlocal rightleft +  call setline(1, 'abcd') +  call matchadd('Search', '\n') +  let attrs0 = ScreenAttrs(1, 10)[0] + +  setlocal cursorline + +  " underline +  exe hi_ul + +  " expected: +  " '      dcba' +  "        ^^^^   underline +  "       ^       'Search' highlight with underline +  "  ^^^^^        underline +  let attrs = ScreenAttrs(1, 10)[0] +  call assert_equal(repeat([attrs[9]], 4), attrs[6:9]) +  call assert_equal(repeat([attrs[4]], 5) + [attrs[5]], attrs[0:5]) +  call assert_notequal(attrs[9], attrs[5]) +  call assert_notequal(attrs[4], attrs[5]) +  call assert_notequal(attrs0[9], attrs[9]) +  call assert_notequal(attrs0[5], attrs[5]) +  call Check_lcs_eol_attrs(attrs, 1, 10) + +  if IsColorable() +    " bg-color +    exe hi_bg + +    " expected: +    " '      dcba' +    "        ^^^^   bg-color of 'CursorLine' +    "       ^       'Search' highlight +    "  ^^^^^        bg-color of 'CursorLine' +    let attrs = ScreenAttrs(1, 10)[0] +    call assert_equal(repeat([attrs[9]], 4), attrs[6:9]) +    call assert_equal(repeat([attrs[4]], 5), attrs[0:4]) +    call assert_equal(attrs0[5], attrs[5]) +    call assert_notequal(attrs[9], attrs[5]) +    call assert_notequal(attrs[5], attrs[4]) +    call assert_notequal(attrs0[9], attrs[9]) +    call assert_notequal(attrs0[4], attrs[4]) +    call Check_lcs_eol_attrs(attrs, 1, 10) +  endif + +  call CloseWindow() +  exe hiCursorLine +endfunc + +func Test_highlight_eol_with_cursorline_linewrap() +  let [hiCursorLine, hi_ul, hi_bg] = HiCursorLine() + +  call NewWindow('topleft 5', 10) +  call setline(1, [repeat('a', 51) . 'bcd', '']) +  call matchadd('Search', '\n') + +  setlocal wrap +  normal! gg$ +  let attrs0 = ScreenAttrs(5, 10)[0] +  setlocal cursorline + +  " underline +  exe hi_ul + +  " expected: +  " 'abcd      ' +  "  ^^^^         underline +  "      ^        'Search' highlight with underline +  "       ^^^^^   underline +  let attrs = ScreenAttrs(5, 10)[0] +  call assert_equal(repeat([attrs[0]], 4), attrs[0:3]) +  call assert_equal([attrs[4]] + repeat([attrs[5]], 5), attrs[4:9]) +  call assert_notequal(attrs[0], attrs[4]) +  call assert_notequal(attrs[4], attrs[5]) +  call assert_notequal(attrs0[0], attrs[0]) +  call assert_notequal(attrs0[4], attrs[4]) +  call Check_lcs_eol_attrs(attrs, 5, 10) + +  if IsColorable() +    " bg-color +    exe hi_bg + +    " expected: +    " 'abcd      ' +    "  ^^^^         bg-color of 'CursorLine' +    "      ^        'Search' highlight +    "       ^^^^^   bg-color of 'CursorLine' +    let attrs = ScreenAttrs(5, 10)[0] +    call assert_equal(repeat([attrs[0]], 4), attrs[0:3]) +    call assert_equal(repeat([attrs[5]], 5), attrs[5:9]) +    call assert_equal(attrs0[4], attrs[4]) +    call assert_notequal(attrs[0], attrs[4]) +    call assert_notequal(attrs[4], attrs[5]) +    call assert_notequal(attrs0[0], attrs[0]) +    call assert_notequal(attrs0[5], attrs[5]) +    call Check_lcs_eol_attrs(attrs, 5, 10) +  endif + +  setlocal nocursorline nowrap +  normal! gg$ +  let attrs0 = ScreenAttrs(1, 10)[0] +  setlocal cursorline + +  " underline +  exe hi_ul + +  " expected: +  " 'aaabcd    ' +  "  ^^^^^^       underline +  "        ^      'Search' highlight with underline +  "         ^^^   underline +  let attrs = ScreenAttrs(1, 10)[0] +  call assert_equal(repeat([attrs[0]], 6), attrs[0:5]) +  call assert_equal([attrs[6]] + repeat([attrs[7]], 3), attrs[6:9]) +  call assert_notequal(attrs[0], attrs[6]) +  call assert_notequal(attrs[6], attrs[7]) +  call assert_notequal(attrs0[0], attrs[0]) +  call assert_notequal(attrs0[6], attrs[6]) +  call Check_lcs_eol_attrs(attrs, 1, 10) + +  if IsColorable() +    " bg-color +    exe hi_bg + +    " expected: +    " 'aaabcd    ' +    "  ^^^^^^       bg-color of 'CursorLine' +    "        ^      'Search' highlight +    "         ^^^   bg-color of 'CursorLine' +    let attrs = ScreenAttrs(1, 10)[0] +    call assert_equal(repeat([attrs[0]], 6), attrs[0:5]) +    call assert_equal(repeat([attrs[7]], 3), attrs[7:9]) +    call assert_equal(attrs0[6], attrs[6]) +    call assert_notequal(attrs[0], attrs[6]) +    call assert_notequal(attrs[6], attrs[7]) +    call assert_notequal(attrs0[0], attrs[0]) +    call assert_notequal(attrs0[7], attrs[7]) +    call Check_lcs_eol_attrs(attrs, 1, 10) +  endif + +  call CloseWindow() +  exe hiCursorLine +endfunc + +func Test_highlight_eol_with_cursorline_sign() +  if !has('signs') +    return +  endif + +  let [hiCursorLine, hi_ul, hi_bg] = HiCursorLine() + +  call NewWindow('topleft 5', 10) +  call setline(1, 'abcd') +  call matchadd('Search', '\n') + +  sign define Sign text=>> +  exe 'sign place 1 line=1 name=Sign buffer=' . bufnr('') +  let attrs0 = ScreenAttrs(1, 10)[0] +  setlocal cursorline + +  " underline +  exe hi_ul + +  " expected: +  " '>>abcd    ' +  "  ^^           sign +  "    ^^^^       underline +  "        ^      'Search' highlight with underline +  "         ^^^   underline +  let attrs = ScreenAttrs(1, 10)[0] +  call assert_equal(repeat([attrs[2]], 4), attrs[2:5]) +  call assert_equal([attrs[6]] + repeat([attrs[7]], 3), attrs[6:9]) +  call assert_notequal(attrs[2], attrs[6]) +  call assert_notequal(attrs[6], attrs[7]) +  call assert_notequal(attrs0[2], attrs[2]) +  call assert_notequal(attrs0[6], attrs[6]) +  call Check_lcs_eol_attrs(attrs, 1, 10) + +  if IsColorable() +    " bg-color +    exe hi_bg + +    " expected: +    " '>>abcd    ' +    "  ^^           sign +    "    ^^^^       bg-color of 'CursorLine' +    "        ^      'Search' highlight +    "         ^^^   bg-color of 'CursorLine' +    let attrs = ScreenAttrs(1, 10)[0] +    call assert_equal(repeat([attrs[2]], 4), attrs[2:5]) +    call assert_equal(repeat([attrs[7]], 3), attrs[7:9]) +    call assert_equal(attrs0[6], attrs[6]) +    call assert_notequal(attrs[2], attrs[6]) +    call assert_notequal(attrs[6], attrs[7]) +    call assert_notequal(attrs0[2], attrs[2]) +    call assert_notequal(attrs0[7], attrs[7]) +    call Check_lcs_eol_attrs(attrs, 1, 10) +  endif + +  sign unplace 1 +  call CloseWindow() +  exe hiCursorLine +endfunc + +func Test_highlight_eol_with_cursorline_breakindent() +  if !has('linebreak') +    return +  endif + +  let [hiCursorLine, hi_ul, hi_bg] = HiCursorLine() + +  call NewWindow('topleft 5', 10) +  setlocal breakindent breakindentopt=min:0,shift:1 showbreak=> +  call setline(1, ' ' . repeat('a', 9) . 'bcd') +  call matchadd('Search', '\n') +  let attrs0 = ScreenAttrs(2, 10)[0] +  setlocal cursorline + +  " underline +  exe hi_ul + +  " expected: +  " '  >bcd    ' +  "  ^^^          breakindent and showbreak +  "     ^^^       underline +  "        ^      'Search' highlight with underline +  "         ^^^   underline +  let attrs = ScreenAttrs(2, 10)[0] +  call assert_equal(repeat([attrs[0]], 2), attrs[0:1]) +  call assert_equal(repeat([attrs[3]], 3), attrs[3:5]) +  call assert_equal([attrs[6]] + repeat([attrs[7]], 3), attrs[6:9]) +  call assert_equal(attrs0[0], attrs[0]) +  call assert_notequal(attrs[0], attrs[2]) +  call assert_notequal(attrs[2], attrs[3]) +  call assert_notequal(attrs[3], attrs[6]) +  call assert_notequal(attrs[6], attrs[7]) +  call assert_notequal(attrs0[2], attrs[2]) +  call assert_notequal(attrs0[3], attrs[3]) +  call assert_notequal(attrs0[6], attrs[6]) +  call Check_lcs_eol_attrs(attrs, 2, 10) + +  if IsColorable() +    " bg-color +    exe hi_bg + +    " expected: +    " '  >bcd    ' +    "  ^^^          breakindent and showbreak +    "     ^^^       bg-color of 'CursorLine' +    "        ^      'Search' highlight +    "         ^^^   bg-color of 'CursorLine' +    let attrs = ScreenAttrs(2, 10)[0] +    call assert_equal(repeat([attrs[0]], 2), attrs[0:1]) +    call assert_equal(repeat([attrs[3]], 3), attrs[3:5]) +    call assert_equal(repeat([attrs[7]], 3), attrs[7:9]) +    call assert_equal(attrs0[0], attrs[0]) +    call assert_equal(attrs0[6], attrs[6]) +    call assert_notequal(attrs[0], attrs[2]) +    call assert_notequal(attrs[2], attrs[3]) +    call assert_notequal(attrs[3], attrs[6]) +    call assert_notequal(attrs[6], attrs[7]) +    call assert_notequal(attrs0[2], attrs[2]) +    call assert_notequal(attrs0[3], attrs[3]) +    call assert_notequal(attrs0[7], attrs[7]) +    call Check_lcs_eol_attrs(attrs, 2, 10) +  endif + +  call CloseWindow() +  set showbreak= +  exe hiCursorLine +endfunc + +func Test_highlight_eol_on_diff() +  call setline(1, ['abcd', '']) +  call matchadd('Search', '\n') +  let attrs0 = ScreenAttrs(1, 10)[0] + +  diffthis +  botright new +  diffthis + +  " expected: +  " '  abcd    ' +  "  ^^           sign +  "    ^^^^ ^^^   'DiffAdd' highlight +  "        ^      'Search' highlight +  let attrs = ScreenAttrs(1, 10)[0] +  call assert_equal(repeat([attrs[0]], 2), attrs[0:1]) +  call assert_equal(repeat([attrs[2]], 4), attrs[2:5]) +  call assert_equal(repeat([attrs[2]], 3), attrs[7:9]) +  call assert_equal(attrs0[4], attrs[6]) +  call assert_notequal(attrs[0], attrs[2]) +  call assert_notequal(attrs[0], attrs[6]) +  call assert_notequal(attrs[2], attrs[6]) +  call Check_lcs_eol_attrs(attrs, 1, 10) + +  bwipe! +  diffoff +endfunc + +func Test_termguicolors() +  if !exists('+termguicolors') +    return +  endif +  if has('vtp') && !has('vcon') +    " Win32: 'guicolors' doesn't work without virtual console. +    call assert_fails('set termguicolors', 'E954:') +    return +  endif + +  " Basic test that setting 'termguicolors' works with one color. +  set termguicolors +  redraw +  set t_Co=1 +  redraw +  set t_Co=0 +  redraw +endfunc diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim index 0f2e7e493e..756a455ebd 100644 --- a/src/nvim/testdir/test_visual.vim +++ b/src/nvim/testdir/test_visual.vim @@ -17,6 +17,14 @@ func Test_block_shift_multibyte()    q!  endfunc +func Test_block_shift_overflow() +  " This used to cause a multiplication overflow followed by a crash. +  new +  normal ii +  exe "normal \<C-V>876543210>" +  q! +endfunc +  func Test_Visual_ctrl_o()    new    call setline(1, ['one', 'two', 'three']) @@ -118,9 +126,34 @@ func Test_blockwise_visual()    enew!  endfunc +" Test swapping corners in blockwise visual mode with o and O +func Test_blockwise_visual_o_O() +  enew! + +  exe "norm! 10i.\<Esc>Y4P3lj\<C-V>4l2jr " +  exe "norm! gvO\<Esc>ra" +  exe "norm! gvO\<Esc>rb" +  exe "norm! gvo\<C-c>rc" +  exe "norm! gvO\<C-c>rd" + +  call assert_equal(['..........', +        \            '...c   d..', +        \            '...     ..', +        \            '...a   b..', +        \            '..........'], getline(1, '$')) + +  enew! +endfun +  " Test Virtual replace mode.  func Test_virtual_replace()    throw 'skipped: TODO: ' +  if exists('&t_kD') +    let save_t_kD = &t_kD +  endif +  if exists('&t_kb') +    let save_t_kb = &t_kb +  endif    exe "set t_kD=\<C-V>x7f t_kb=\<C-V>x08"    enew!    exe "normal a\nabcdefghi\njk\tlmn\n    opq	rst\n\<C-D>uvwxyz" @@ -151,4 +184,96 @@ func Test_virtual_replace()    call assert_equal(['AB......CDEFGHI.Jkl',  	      \ 'AB	IJKLMNO	QRst'], getline(12, 13))    enew! +  set noai bs&vim +  if exists('save_t_kD') +    let &t_kD = save_t_kD +  endif +  if exists('save_t_kb') +    let &t_kb = save_t_kb +  endif +endfunc + +" Test Virtual replace mode. +func Test_virtual_replace2() +  enew! +  set bs=2 +  exe "normal a\nabcdefghi\njk\tlmn\n    opq	rst\n\<C-D>uvwxyz" +  call cursor(1,1) +  " Test 1: Test that del deletes the newline +  exe "normal gR0\<del> 1\nA\nBCDEFGHIJ\n\tKL\nMNO\nPQR" +  call assert_equal(['0 1', +	      \ 'A', +	      \ 'BCDEFGHIJ', +	      \ '	KL', +	      \ 'MNO', +	      \ 'PQR', +	      \ ], getline(1, 6)) +  " Test 2: +  " a newline is not deleted, if no newline has been added in virtual replace mode +  %d_ +  call setline(1, ['abcd', 'efgh', 'ijkl']) +  call cursor(2,1) +  exe "norm! gR1234\<cr>5\<bs>\<bs>\<bs>" +  call assert_equal(['abcd', +        \ '123h', +        \ 'ijkl'], getline(1, '$')) +  " Test 3: +  " a newline is deleted, if a newline has been inserted before in virtual replace mode +  %d_ +  call setline(1, ['abcd', 'efgh', 'ijkl']) +  call cursor(2,1) +  exe "norm! gR1234\<cr>\<cr>56\<bs>\<bs>\<bs>" +  call assert_equal(['abcd', +        \ '1234', +        \ 'ijkl'], getline(1, '$')) +  " Test 4: +  " delete add a newline, delete it, add it again and check undo +  %d_ +  call setline(1, ['abcd', 'efgh', 'ijkl']) +  call cursor(2,1) +  " break undo sequence explicitly +  let &ul = &ul +  exe "norm! gR1234\<cr>\<bs>\<del>56\<cr>" +  let &ul = &ul +  call assert_equal(['abcd', +        \ '123456', +        \ ''], getline(1, '$')) +  norm! u +  call assert_equal(['abcd', +        \ 'efgh', +        \ 'ijkl'], getline(1, '$')) +  " clean up +  %d_ +  set bs&vim +endfunc + +" Test for Visual mode not being reset causing E315 error. +func TriggerTheProblem() +  " At this point there is no visual selection because :call reset it. +  " Let's restore the selection: +  normal gv +  '<,'>del _ +  try +      exe "normal \<Esc>" +  catch /^Vim\%((\a\+)\)\=:E315/ +      echom 'Snap! E315 error!' +      let g:msg = 'Snap! E315 error!' +  endtry +endfunc + +func Test_visual_mode_reset() +  set belloff=all +  enew +  let g:msg = "Everything's fine." +  enew +  setl buftype=nofile +  call append(line('$'), 'Delete this line.') + +  " NOTE: this has to be done by a call to a function because executing :del +  " the ex-way will require the colon operator which resets the visual mode +  " thus preventing the problem: +  exe "normal! GV:call TriggerTheProblem()\<CR>" +  call assert_equal("Everything's fine.", g:msg) + +  set belloff&  endfunc diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim index 139d29a48b..ad60c8d3c7 100644 --- a/src/nvim/testdir/test_window_cmd.vim +++ b/src/nvim/testdir/test_window_cmd.vim @@ -17,7 +17,7 @@ func Test_window_cmd_ls0_with_split()  endfunc  func Test_window_cmd_cmdwin_with_vsp() -  let efmt='Expected 0 but got %d (in ls=%d, %s window)' +  let efmt = 'Expected 0 but got %d (in ls=%d, %s window)'    for v in range(0, 2)      exec "set ls=" . v      vsplit @@ -417,4 +417,92 @@ func Test_window_newtab()  endfunc +" Tests for adjusting window and contents +func GetScreenStr(row) +   let str = "" +   for c in range(1,3) +       let str .= nr2char(screenchar(a:row, c)) +   endfor +   return str +endfunc + +func Test_window_contents() +  enew! | only | new +  call setline(1, range(1,256)) + +  exe "norm! \<C-W>t\<C-W>=1Gzt\<C-W>w\<C-W>+" +  redraw +  let s3 = GetScreenStr(1) +  wincmd p +  call assert_equal(1, line("w0")) +  call assert_equal('1  ', s3) + +  exe "norm! \<C-W>t\<C-W>=50Gzt\<C-W>w\<C-W>+" +  redraw +  let s3 = GetScreenStr(1) +  wincmd p +  call assert_equal(50, line("w0")) +  call assert_equal('50 ', s3) + +  exe "norm! \<C-W>t\<C-W>=59Gzt\<C-W>w\<C-W>+" +  redraw +  let s3 = GetScreenStr(1) +  wincmd p +  call assert_equal(59, line("w0")) +  call assert_equal('59 ', s3) + +  bwipeout! +  call test_garbagecollect_now() +endfunc + +func Test_access_freed_mem() +  " This was accessing freed memory +  au * 0 vs xxx +  arg 0 +  argadd +  all +  all +  au! +  bwipe xxx +endfunc + +func Test_visual_cleared_after_window_split() +  new | only! +  let smd_save = &showmode +  set showmode +  let ls_save = &laststatus +  set laststatus=1 +  call setline(1, ['a', 'b', 'c', 'd', '']) +  norm! G +  exe "norm! kkvk" +  redraw +  exe "norm! \<C-W>v" +  redraw +  " check if '-- VISUAL --' disappeared from command line +  let columns = range(1, &columns) +  let cmdlinechars = map(columns, 'nr2char(screenchar(&lines, v:val))') +  let cmdline = join(cmdlinechars, '') +  let cmdline_ltrim = substitute(cmdline, '^\s*', "", "") +  let mode_shown = substitute(cmdline_ltrim, '\s*$', "", "") +  call assert_equal('', mode_shown) +  let &showmode = smd_save +  let &laststatus = ls_save +  bwipe! +endfunc + +func Test_winrestcmd() +  2split +  3vsplit +  let a = winrestcmd() +  call assert_equal(2, winheight(0)) +  call assert_equal(3, winwidth(0)) +  wincmd = +  call assert_notequal(2, winheight(0)) +  call assert_notequal(3, winwidth(0)) +  exe a +  call assert_equal(2, winheight(0)) +  call assert_equal(3, winwidth(0)) +  only +endfunc +  " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/view_util.vim b/src/nvim/testdir/view_util.vim index eb92630761..29ea073f97 100644 --- a/src/nvim/testdir/view_util.vim +++ b/src/nvim/testdir/view_util.vim @@ -1,5 +1,10 @@  " Functions about view shared by several tests +" Only load this script once. +if exists('*ScreenLines') +  finish +endif +  " ScreenLines(lnum, width) or  " ScreenLines([start, end], width)  function! ScreenLines(lnum, width) abort @@ -18,6 +23,22 @@ function! ScreenLines(lnum, width) abort    return lines  endfunction +function! ScreenAttrs(lnum, width) abort +  redraw! +  if type(a:lnum) == v:t_list +    let start = a:lnum[0] +    let end = a:lnum[1] +  else +    let start = a:lnum +    let end = a:lnum +  endif +  let attrs = [] +  for l in range(start, end) +    let attrs += [map(range(1, a:width), 'screenattr(l, v:val)')] +  endfor +  return attrs +endfunction +  function! NewWindow(height, width) abort    exe a:height . 'new'    exe a:width . 'vsp' diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 2055b4879e..f4eb50b3b5 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -2233,10 +2233,11 @@ static void u_undoredo(int undo, bool do_buf_event)           * If the file is empty, there is an empty line 1 that we           * should get rid of, by replacing it with the new line           */ -        if (empty_buffer && lnum == 0) -          ml_replace((linenr_T)1, uep->ue_array[i], TRUE); -        else +        if (empty_buffer && lnum == 0) { +          ml_replace((linenr_T)1, uep->ue_array[i], true); +        } else {            ml_append(lnum, uep->ue_array[i], (colnr_T)0, FALSE); +        }          xfree(uep->ue_array[i]);        }        xfree((char_u *)uep->ue_array); @@ -2902,7 +2903,7 @@ void u_undoline(void)            curbuf->b_u_line_lnum + 1, (linenr_T)0, FALSE) == FAIL)      return;    oldp = u_save_line(curbuf->b_u_line_lnum); -  ml_replace(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr, TRUE); +  ml_replace(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr, true);    changed_bytes(curbuf->b_u_line_lnum, 0);    xfree(curbuf->b_u_line_ptr);    curbuf->b_u_line_ptr = oldp; diff --git a/src/nvim/window.c b/src/nvim/window.c index 9286894e08..8239061a0c 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -2296,6 +2296,9 @@ winframe_remove (      if (frp2->fr_win != NULL)        frp2->fr_win->w_frame = frp2->fr_parent;      frp = frp2->fr_parent; +    if (topframe->fr_child == frp2) { +      topframe->fr_child = frp; +    }      xfree(frp2);      frp2 = frp->fr_parent; @@ -2317,6 +2320,9 @@ winframe_remove (            break;          }        } +      if (topframe->fr_child == frp) { +        topframe->fr_child = frp2; +      }        xfree(frp);      }    } @@ -2959,7 +2965,6 @@ static int win_alloc_firstwin(win_T *oldwin)    topframe = curwin->w_frame;    topframe->fr_width = Columns;    topframe->fr_height = Rows - p_ch; -  topframe->fr_win = curwin;    return OK;  } @@ -3970,18 +3975,20 @@ win_remove (      tabpage_T *tp                /* tab page "win" is in, NULL for current */  )  { -  if (wp->w_prev != NULL) +  if (wp->w_prev != NULL) {      wp->w_prev->w_next = wp->w_next; -  else if (tp == NULL) -    firstwin = wp->w_next; -  else +  } else if (tp == NULL) { +    firstwin = curtab->tp_firstwin = wp->w_next; +  } else {      tp->tp_firstwin = wp->w_next; -  if (wp->w_next != NULL) +  } +  if (wp->w_next != NULL) {      wp->w_next->w_prev = wp->w_prev; -  else if (tp == NULL) -    lastwin = wp->w_prev; -  else +  } else if (tp == NULL) { +    lastwin = curtab->tp_lastwin = wp->w_prev; +  } else {      tp->tp_lastwin = wp->w_prev; +  }  }  /* @@ -4015,12 +4022,18 @@ static void frame_insert(frame_T *before, frame_T *frp)   */  static void frame_remove(frame_T *frp)  { -  if (frp->fr_prev != NULL) +  if (frp->fr_prev != NULL) {      frp->fr_prev->fr_next = frp->fr_next; -  else +  } else {      frp->fr_parent->fr_child = frp->fr_next; -  if (frp->fr_next != NULL) +    // special case: topframe->fr_child == frp +    if (topframe->fr_child == frp) { +      topframe->fr_child = frp->fr_next; +    } +  } +  if (frp->fr_next != NULL) {      frp->fr_next->fr_prev = frp->fr_prev; +  }  } @@ -5489,12 +5502,10 @@ int switch_win(win_T **save_curwin, tabpage_T **save_curtab, win_T *win, tabpage    return OK;  } -/* - * Restore current tabpage and window saved by switch_win(), if still valid. - * When "no_display" is TRUE the display won't be affected, no redraw is - * triggered. - */ -void restore_win(win_T *save_curwin, tabpage_T *save_curtab, int no_display) +// Restore current tabpage and window saved by switch_win(), if still valid. +// When "no_display" is true the display won't be affected, no redraw is +// triggered. +void restore_win(win_T *save_curwin, tabpage_T *save_curtab, bool no_display)  {    if (save_curtab != NULL && valid_tabpage(save_curtab)) {      if (no_display) {  | 
