diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/buffer.c | 39 | ||||
-rw-r--r-- | src/nvim/buffer.h | 7 | ||||
-rw-r--r-- | src/nvim/change.c | 6 | ||||
-rw-r--r-- | src/nvim/diff.c | 14 | ||||
-rw-r--r-- | src/nvim/edit.c | 42 | ||||
-rw-r--r-- | src/nvim/ex_cmds.c | 8 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 4 | ||||
-rw-r--r-- | src/nvim/ex_docmd.h | 1 | ||||
-rw-r--r-- | src/nvim/ex_getln.c | 2 | ||||
-rw-r--r-- | src/nvim/fileio.c | 38 | ||||
-rw-r--r-- | src/nvim/getchar.c | 21 | ||||
-rw-r--r-- | src/nvim/globals.h | 2 | ||||
-rw-r--r-- | src/nvim/memline.c | 159 | ||||
-rw-r--r-- | src/nvim/memory.c | 2 | ||||
-rw-r--r-- | src/nvim/option.c | 66 | ||||
-rw-r--r-- | src/nvim/quickfix.c | 4 | ||||
-rw-r--r-- | src/nvim/state.c | 5 | ||||
-rw-r--r-- | src/nvim/syntax.c | 79 | ||||
-rw-r--r-- | src/nvim/testdir/test_autocmd.vim | 34 | ||||
-rw-r--r-- | src/nvim/testdir/test_diffmode.vim | 37 | ||||
-rw-r--r-- | src/nvim/testdir/test_edit.vim | 23 | ||||
-rw-r--r-- | src/nvim/testdir/test_filetype.vim | 2 | ||||
-rw-r--r-- | src/nvim/testdir/test_highlight.vim | 8 | ||||
-rw-r--r-- | src/nvim/testdir/test_ins_complete.vim | 19 | ||||
-rw-r--r-- | src/nvim/testdir/test_registers.vim | 18 | ||||
-rw-r--r-- | src/nvim/testdir/test_trycatch.vim | 27 | ||||
-rw-r--r-- | src/nvim/window.c | 68 |
27 files changed, 466 insertions, 269 deletions
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index bf592a626d..4ec23244cd 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -174,7 +174,7 @@ int open_buffer(int read_stdin, exarg_T *eap, int flags) if (ml_open(curbuf) == FAIL) { // There MUST be a memfile, otherwise we can't do anything // If we can't create one for the current buffer, take another buffer - close_buffer(NULL, curbuf, 0, false); + close_buffer(NULL, curbuf, 0, false, false); curbuf = NULL; FOR_ALL_BUFFERS(buf) { @@ -402,8 +402,10 @@ bool buf_valid(buf_T *buf) /// there to be only one window with this buffer. e.g. when /// ":quit" is supposed to close the window but autocommands /// close all other windows. +/// @param ignore_abort +/// If true, don't abort even when aborting() returns true. /// @return true when we got to the end and b_nwindows was decremented. -bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last) +bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last, bool ignore_abort) { bool unload_buf = (action != 0); bool del_buf = (action == DOBUF_DEL || action == DOBUF_WIPE); @@ -494,7 +496,8 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last) return false; } } - if (aborting()) { // autocmds may abort script processing + // autocmds may abort script processing + if (!ignore_abort && aborting()) { return false; } } @@ -552,14 +555,16 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last) buf->b_nwindows = nwindows; - buf_freeall(buf, (del_buf ? BFA_DEL : 0) + (wipe_buf ? BFA_WIPE : 0)); + buf_freeall(buf, ((del_buf ? BFA_DEL : 0) + + (wipe_buf ? BFA_WIPE : 0) + + (ignore_abort ? BFA_IGNORE_ABORT : 0))); if (!bufref_valid(&bufref)) { // Autocommands may have deleted the buffer. return false; } - if (aborting()) { - // Autocmds may abort script processing. + // autocmds may abort script processing. + if (!ignore_abort && aborting()) { return false; } @@ -660,9 +665,10 @@ void buf_clear(void) /// buf_freeall() - free all things allocated for a buffer that are related to /// the file. Careful: get here with "curwin" NULL when exiting. /// -/// @param flags BFA_DEL buffer is going to be deleted -/// BFA_WIPE buffer is going to be wiped out -/// BFA_KEEP_UNDO do not free undo information +/// @param flags BFA_DEL buffer is going to be deleted +/// BFA_WIPE buffer is going to be wiped out +/// BFA_KEEP_UNDO do not free undo information +/// BFA_IGNORE_ABORT don't abort even when aborting() returns true void buf_freeall(buf_T *buf, int flags) { bool is_curbuf = (buf == curbuf); @@ -706,7 +712,8 @@ void buf_freeall(buf_T *buf, int flags) goto_tabpage_win(the_curtab, the_curwin); unblock_autocmds(); } - if (aborting()) { // autocmds may abort script processing + // autocmds may abort script processing + if ((flags & BFA_IGNORE_ABORT) == 0 && aborting()) { return; } @@ -877,7 +884,7 @@ void handle_swap_exists(bufref_T *old_curbuf) // open a new, empty buffer. swap_exists_action = SEA_NONE; // don't want it again swap_exists_did_quit = true; - close_buffer(curwin, curbuf, DOBUF_UNLOAD, false); + close_buffer(curwin, curbuf, DOBUF_UNLOAD, false, false); if (old_curbuf == NULL || !bufref_valid(old_curbuf) || old_curbuf->br_buf == curbuf) { @@ -1074,7 +1081,7 @@ static int empty_curbuf(int close_others, int forceit, int action) // the old one. But do_ecmd() may have done that already, check // if the buffer still exists. if (buf != curbuf && bufref_valid(&bufref) && buf->b_nwindows == 0) { - close_buffer(NULL, buf, action, false); + close_buffer(NULL, buf, action, false, false); } if (!close_others) { @@ -1259,7 +1266,7 @@ int do_buffer(int action, int start, int dir, int count, int forceit) if (buf != curbuf) { close_windows(buf, false); if (buf != curbuf && bufref_valid(&bufref) && buf->b_nwindows <= 0) { - close_buffer(NULL, buf, action, false); + close_buffer(NULL, buf, action, false, false); } return OK; } @@ -1485,7 +1492,7 @@ void set_curbuf(buf_T *buf, int action) ? action : (action == DOBUF_GOTO && !buf_hide(prevbuf) && !bufIsChanged(prevbuf)) ? DOBUF_UNLOAD : 0, - false); + false, false); if (curwin != previouswin && win_valid(previouswin)) { // autocommands changed curwin, Grr! curwin = previouswin; @@ -2805,7 +2812,7 @@ int setfname(buf_T *buf, char_u *ffname_arg, char_u *sfname_arg, bool message) return FAIL; } // delete from the list - close_buffer(NULL, obuf, DOBUF_WIPE, false); + close_buffer(NULL, obuf, DOBUF_WIPE, false, false); } sfname = vim_strsave(sfname); #ifdef USE_FNAME_CASE @@ -5650,7 +5657,7 @@ void wipe_buffer(buf_T *buf, bool aucmd) // Don't trigger BufDelete autocommands here. block_autocmds(); } - close_buffer(NULL, buf, DOBUF_WIPE, false); + close_buffer(NULL, buf, DOBUF_WIPE, false, true); if (!aucmd) { unblock_autocmds(); } diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h index 850ab175a5..7f4bbcc9e5 100644 --- a/src/nvim/buffer.h +++ b/src/nvim/buffer.h @@ -58,9 +58,10 @@ enum dobuf_start_values { // flags for buf_freeall() enum bfa_values { - BFA_DEL = 1, // buffer is going to be deleted - BFA_WIPE = 2, // buffer is going to be wiped out - BFA_KEEP_UNDO = 4, // do not free undo information + BFA_DEL = 1, // buffer is going to be deleted + BFA_WIPE = 2, // buffer is going to be wiped out + BFA_KEEP_UNDO = 4, // do not free undo information + BFA_IGNORE_ABORT = 8, // do not abort for aborting() }; #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/change.c b/src/nvim/change.c index 0644b1d601..44abd69733 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -286,9 +286,11 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, long xtra set_topline(wp, wp->w_topline); } - // Relative numbering may require updating more. + // If lines have been added or removed, relative numbering always + // requires a redraw. if (wp->w_p_rnu && xtra != 0) { - redraw_later(wp, SOME_VALID); + wp->w_last_cursor_lnum_rnu = 0; + redraw_later(wp, VALID); } // Cursor line highlighting probably need to be updated with diff --git a/src/nvim/diff.c b/src/nvim/diff.c index a6bbe40999..0b55fb877c 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -743,11 +743,16 @@ static int diff_write_buffer(buf_T *buf, diffin_T *din) for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { for (char_u *s = ml_get_buf(buf, lnum, false); *s != NUL;) { if (diff_flags & DIFF_ICASE) { + int c; char_u cbuf[MB_MAXBYTES + 1]; - // xdiff doesn't support ignoring case, fold-case the text. - int c = utf_ptr2char(s); - c = utf_fold(c); + if (*s == NL) { + c = NUL; + } else { + // xdiff doesn't support ignoring case, fold-case the text. + c = utf_ptr2char(s); + c = utf_fold(c); + } const int orig_len = utfc_ptr2len(s); if (utf_char2bytes(c, cbuf) != orig_len) { // TODO(Bram): handle byte length difference @@ -759,7 +764,8 @@ static int diff_write_buffer(buf_T *buf, diffin_T *din) s += orig_len; len += orig_len; } else { - ptr[len++] = *s++; + ptr[len++] = *s == NL ? NUL : *s; + s++; } } ptr[len++] = NL; diff --git a/src/nvim/edit.c b/src/nvim/edit.c index da0b577056..3eb4ab9517 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -390,12 +390,9 @@ static void insert_enter(InsertState *s) trigger_modechanged(); stop_insert_mode = false; - // Need to recompute the cursor position, it might move when the cursor - // is on a TAB or special character. - // ptr2cells() treats a TAB character as double-width. - if (ptr2cells(get_cursor_pos_ptr()) > 1) { - curwin->w_valid &= ~VALID_VIRTCOL; - curs_columns(curwin, true); + // need to position cursor again when on a TAB + if (gchar_cursor() == TAB) { + curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL); } // Enable langmap or IME, indicated by 'iminsert'. @@ -1396,6 +1393,7 @@ static void insert_do_complete(InsertState *s) compl_cont_status = 0; } compl_busy = false; + can_si = true; // allow smartindenting } static void insert_do_cindent(InsertState *s) @@ -7330,21 +7328,21 @@ static void mb_replace_pop_ins(int cc) // Not a multi-byte char, put it back. replace_push(c); break; + } + + buf[0] = c; + assert(n > 1); + for (i = 1; i < n; i++) { + buf[i] = replace_pop(); + } + if (utf_iscomposing(utf_ptr2char(buf))) { + ins_bytes_len(buf, n); } else { - buf[0] = c; - assert(n > 1); - for (i = 1; i < n; i++) { - buf[i] = replace_pop(); - } - if (utf_iscomposing(utf_ptr2char(buf))) { - ins_bytes_len(buf, n); - } else { - // Not a composing char, put it back. - for (i = n - 1; i >= 0; i--) { - replace_push(buf[i]); - } - break; + // Not a composing char, put it back. + for (i = n - 1; i >= 0; i--) { + replace_push(buf[i]); } + break; } } } @@ -8054,8 +8052,10 @@ static bool ins_esc(long *count, int cmdchar, bool nomove) State = NORMAL; trigger_modechanged(); - // need to position cursor again (e.g. when on a TAB ) - changed_cline_bef_curs(); + // need to position cursor again when on a TAB + if (gchar_cursor() == TAB) { + curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL); + } setmouse(); ui_cursor_shape(); // may show different cursor shape diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 5d5b342c35..98aabe89b3 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -2527,9 +2527,9 @@ int do_ecmd(int fnum, char_u *ffname, char_u *sfname, exarg_T *eap, linenr_T new // Close the link to the current buffer. This will set // oldwin->w_buffer to NULL. u_sync(false); - const bool did_decrement = close_buffer(oldwin, curbuf, - (flags & ECMD_HIDE) || curbuf->terminal ? 0 : DOBUF_UNLOAD, - false); + const bool did_decrement + = close_buffer(oldwin, curbuf, (flags & ECMD_HIDE) || curbuf->terminal ? 0 : DOBUF_UNLOAD, + false, false); // Autocommands may have closed the window. if (win_valid(the_curwin)) { @@ -2875,7 +2875,7 @@ int do_ecmd(int fnum, char_u *ffname, char_u *sfname, exarg_T *eap, linenr_T new redraw_curbuf_later(NOT_VALID); // redraw this buffer later } - if (p_im) { + if (p_im && (State & INSERT) == 0) { need_start_insertmode = true; } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index bb6f3ba395..1f17101aca 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -1248,6 +1248,7 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe const int save_msg_scroll = msg_scroll; cmdmod_T save_cmdmod; const int save_reg_executing = reg_executing; + const bool save_pending_end_reg_executing = pending_end_reg_executing; char_u *cmd; memset(&ea, 0, sizeof(ea)); @@ -2018,6 +2019,7 @@ doend: undo_cmdmod(&ea, save_msg_scroll); cmdmod = save_cmdmod; reg_executing = save_reg_executing; + pending_end_reg_executing = save_pending_end_reg_executing; if (ea.did_sandbox) { sandbox--; @@ -8529,6 +8531,7 @@ bool save_current_state(save_state_T *sst) sst->save_finish_op = finish_op; sst->save_opcount = opcount; sst->save_reg_executing = reg_executing; + sst->save_pending_end_reg_executing = pending_end_reg_executing; msg_scroll = false; // no msg scrolling in Normal mode restart_edit = 0; // don't go to Insert mode @@ -8559,6 +8562,7 @@ void restore_current_state(save_state_T *sst) finish_op = sst->save_finish_op; opcount = sst->save_opcount; reg_executing = sst->save_reg_executing; + pending_end_reg_executing = sst->save_pending_end_reg_executing; // don't reset msg_didout now msg_didout |= sst->save_msg_didout; diff --git a/src/nvim/ex_docmd.h b/src/nvim/ex_docmd.h index be9f97e27d..874e0e599e 100644 --- a/src/nvim/ex_docmd.h +++ b/src/nvim/ex_docmd.h @@ -29,6 +29,7 @@ typedef struct { bool save_finish_op; long save_opcount; int save_reg_executing; + bool save_pending_end_reg_executing; tasave_T tabuf; } save_state_T; diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 9104a2dd5a..dd6e4630d2 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -6544,7 +6544,7 @@ static int open_cmdwin(void) // win_close() may have already wiped the buffer when 'bh' is // set to 'wipe', autocommands may have closed other windows if (bufref_valid(&bufref) && bufref.br_buf != curbuf) { - close_buffer(NULL, bufref.br_buf, DOBUF_WIPE, false); + close_buffer(NULL, bufref.br_buf, DOBUF_WIPE, false, false); } // Restore window sizes. diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index d68dda15f3..d4407b4982 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -1015,27 +1015,27 @@ retry: } read_buf_col += n; break; - } else { - // Append whole line and new-line. Change NL - // to NUL to reverse the effect done below. - for (ni = 0; ni < n; ni++) { - if (p[ni] == NL) { - ptr[tlen++] = NUL; - } else { - ptr[tlen++] = p[ni]; - } + } + + // Append whole line and new-line. Change NL + // to NUL to reverse the effect done below. + for (ni = 0; ni < n; ni++) { + if (p[ni] == NL) { + ptr[tlen++] = NUL; + } else { + ptr[tlen++] = p[ni]; } - ptr[tlen++] = NL; - read_buf_col = 0; - if (++read_buf_lnum > from) { - // When the last line didn't have an - // end-of-line don't add it now either. - if (!curbuf->b_p_eol) { - --tlen; - } - size = tlen; - break; + } + ptr[tlen++] = NL; + read_buf_col = 0; + if (++read_buf_lnum > from) { + // When the last line didn't have an + // end-of-line don't add it now either. + if (!curbuf->b_p_eol) { + tlen--; } + size = tlen; + break; } } } diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index eddd5ccd14..299385cb17 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -2060,7 +2060,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) } /// unget one character (can only be done once!) -/// If the character was stuffed, vgetc() will get it next time it was called. +/// If the character was stuffed, vgetc() will get it next time it is called. /// Otherwise vgetc() will only get it when the stuff buffer is empty. void vungetc(int c) { @@ -2072,6 +2072,20 @@ void vungetc(int c) old_KeyStuffed = KeyStuffed; } +/// When peeking and not getting a character, reg_executing cannot be cleared +/// yet, so set a flag to clear it later. +void check_end_reg_executing(bool advance) +{ + if (reg_executing != 0 && (typebuf.tb_maplen == 0 || pending_end_reg_executing)) { + if (advance) { + reg_executing = 0; + pending_end_reg_executing = false; + } else { + pending_end_reg_executing = true; + } + } +} + /// Gets a byte: /// 1. from the stuffbuffer /// This is used for abbreviated commands like "D" -> "d$". @@ -2126,9 +2140,7 @@ static int vgetorpeek(bool advance) init_typebuf(); start_stuff(); - if (advance && typebuf.tb_maplen == 0) { - reg_executing = 0; - } + check_end_reg_executing(advance); do { // get a character: 1. from the stuffbuffer if (typeahead_char != 0) { @@ -2155,6 +2167,7 @@ static int vgetorpeek(bool advance) // If a mapped key sequence is found we go back to the start to // try re-mapping. for (;;) { + check_end_reg_executing(advance); // os_breakcheck() is slow, don't use it too often when // inside a mapping. But call it each time for typed // characters. diff --git a/src/nvim/globals.h b/src/nvim/globals.h index ace7647b35..4aa49337cf 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -643,6 +643,8 @@ EXTERN bool ex_no_reprint INIT(=false); // No need to print after z or p. EXTERN int reg_recording INIT(= 0); // register for recording or zero EXTERN int reg_executing INIT(= 0); // register being executed or zero +// Flag set when peeking a character and found the end of executed register +EXTERN bool pending_end_reg_executing INIT(= false); EXTERN int reg_recorded INIT(= 0); // last recorded register or zero EXTERN int no_mapping INIT(= false); // currently no mapping allowed diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 418887a6b1..5f6e1ea273 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -2327,95 +2327,88 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b * We are finished, break the loop here. */ break; - } else { // pointer block full - /* - * split the pointer block - * allocate a new pointer block - * move some of the pointer into the new block - * prepare for updating the parent block - */ - for (;;) { // do this twice when splitting block 1 - hp_new = ml_new_ptr(mfp); - if (hp_new == NULL) { // TODO: try to fix tree - return FAIL; - } - pp_new = hp_new->bh_data; - - if (hp->bh_bnum != 1) { - break; - } - - /* - * if block 1 becomes full the tree is given an extra level - * The pointers from block 1 are moved into the new block. - * block 1 is updated to point to the new block - * then continue to split the new block - */ - memmove(pp_new, pp, (size_t)page_size); - pp->pb_count = 1; - pp->pb_pointer[0].pe_bnum = hp_new->bh_bnum; - pp->pb_pointer[0].pe_line_count = buf->b_ml.ml_line_count; - pp->pb_pointer[0].pe_old_lnum = 1; - pp->pb_pointer[0].pe_page_count = 1; - mf_put(mfp, hp, true, false); // release block 1 - hp = hp_new; // new block is to be split - pp = pp_new; - CHECK(stack_idx != 0, _("stack_idx should be 0")); - ip->ip_index = 0; - ++stack_idx; // do block 1 again later - } - /* - * move the pointers after the current one to the new block - * If there are none, the new entry will be in the new block. - */ - total_moved = pp->pb_count - pb_idx - 1; - if (total_moved) { - memmove(&pp_new->pb_pointer[0], - &pp->pb_pointer[pb_idx + 1], - (size_t)(total_moved) * sizeof(PTR_EN)); - pp_new->pb_count = total_moved; - pp->pb_count -= total_moved - 1; - pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right; - pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right; - pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right; - if (lnum_right) { - pp->pb_pointer[pb_idx + 1].pe_old_lnum = lnum_right; - } - } else { - pp_new->pb_count = 1; - pp_new->pb_pointer[0].pe_bnum = bnum_right; - pp_new->pb_pointer[0].pe_line_count = line_count_right; - pp_new->pb_pointer[0].pe_page_count = page_count_right; - pp_new->pb_pointer[0].pe_old_lnum = lnum_right; - } - pp->pb_pointer[pb_idx].pe_bnum = bnum_left; - pp->pb_pointer[pb_idx].pe_line_count = line_count_left; - pp->pb_pointer[pb_idx].pe_page_count = page_count_left; - if (lnum_left) { - pp->pb_pointer[pb_idx].pe_old_lnum = lnum_left; + } + // pointer block full + // + // split the pointer block + // allocate a new pointer block + // move some of the pointer into the new block + // prepare for updating the parent block + for (;;) { // do this twice when splitting block 1 + hp_new = ml_new_ptr(mfp); + if (hp_new == NULL) { // TODO(vim): try to fix tree + return FAIL; } - lnum_left = 0; - lnum_right = 0; + pp_new = hp_new->bh_data; - /* - * recompute line counts - */ - line_count_right = 0; - for (i = 0; i < (int)pp_new->pb_count; ++i) { - line_count_right += pp_new->pb_pointer[i].pe_line_count; + if (hp->bh_bnum != 1) { + break; } - line_count_left = 0; - for (i = 0; i < (int)pp->pb_count; ++i) { - line_count_left += pp->pb_pointer[i].pe_line_count; + + // if block 1 becomes full the tree is given an extra level + // The pointers from block 1 are moved into the new block. + // block 1 is updated to point to the new block + // then continue to split the new block + memmove(pp_new, pp, (size_t)page_size); + pp->pb_count = 1; + pp->pb_pointer[0].pe_bnum = hp_new->bh_bnum; + pp->pb_pointer[0].pe_line_count = buf->b_ml.ml_line_count; + pp->pb_pointer[0].pe_old_lnum = 1; + pp->pb_pointer[0].pe_page_count = 1; + mf_put(mfp, hp, true, false); // release block 1 + hp = hp_new; // new block is to be split + pp = pp_new; + CHECK(stack_idx != 0, _("stack_idx should be 0")); + ip->ip_index = 0; + stack_idx++; // do block 1 again later + } + // move the pointers after the current one to the new block + // If there are none, the new entry will be in the new block. + total_moved = pp->pb_count - pb_idx - 1; + if (total_moved) { + memmove(&pp_new->pb_pointer[0], + &pp->pb_pointer[pb_idx + 1], + (size_t)(total_moved) * sizeof(PTR_EN)); + pp_new->pb_count = total_moved; + pp->pb_count -= total_moved - 1; + pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right; + pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right; + pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right; + if (lnum_right) { + pp->pb_pointer[pb_idx + 1].pe_old_lnum = lnum_right; } + } else { + pp_new->pb_count = 1; + pp_new->pb_pointer[0].pe_bnum = bnum_right; + pp_new->pb_pointer[0].pe_line_count = line_count_right; + pp_new->pb_pointer[0].pe_page_count = page_count_right; + pp_new->pb_pointer[0].pe_old_lnum = lnum_right; + } + pp->pb_pointer[pb_idx].pe_bnum = bnum_left; + pp->pb_pointer[pb_idx].pe_line_count = line_count_left; + pp->pb_pointer[pb_idx].pe_page_count = page_count_left; + if (lnum_left) { + pp->pb_pointer[pb_idx].pe_old_lnum = lnum_left; + } + lnum_left = 0; + lnum_right = 0; - bnum_left = hp->bh_bnum; - bnum_right = hp_new->bh_bnum; - page_count_left = 1; - page_count_right = 1; - mf_put(mfp, hp, true, false); - mf_put(mfp, hp_new, true, false); + // recompute line counts + line_count_right = 0; + for (i = 0; i < (int)pp_new->pb_count; i++) { + line_count_right += pp_new->pb_pointer[i].pe_line_count; } + line_count_left = 0; + for (i = 0; i < (int)pp->pb_count; i++) { + line_count_left += pp->pb_pointer[i].pe_line_count; + } + + bnum_left = hp->bh_bnum; + bnum_right = hp_new->bh_bnum; + page_count_left = 1; + page_count_right = 1; + mf_put(mfp, hp, true, false); + mf_put(mfp, hp_new, true, false); } /* diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 6cdc4f1fde..c895cc1ec6 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -691,7 +691,7 @@ void free_all_mem(void) bufref_T bufref; set_bufref(&bufref, buf); nextbuf = buf->b_next; - close_buffer(NULL, buf, DOBUF_WIPE, false); + close_buffer(NULL, buf, DOBUF_WIPE, false, false); // Didn't work, try next one. buf = bufref_valid(&bufref) ? nextbuf : firstbuf; } diff --git a/src/nvim/option.c b/src/nvim/option.c index 9fd0e0b222..551bc1fbdf 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -5140,43 +5140,43 @@ char *set_option_value(const char *const name, const long number, const char *co s = ""; } return set_string_option(opt_idx, s, opt_flags); - } else { - varp = get_varp_scope(&(options[opt_idx]), opt_flags); - if (varp != NULL) { // hidden option is not changed - if (number == 0 && string != NULL) { - int idx; - - // Either we are given a string or we are setting option - // to zero. - for (idx = 0; string[idx] == '0'; idx++) {} - if (string[idx] != NUL || idx == 0) { - // There's another character after zeros or the string - // is empty. In both cases, we are trying to set a - // num option using a string. - semsg(_("E521: Number required: &%s = '%s'"), - name, string); - return NULL; // do nothing as we hit an error - } - } - long numval = number; - if (opt_flags & OPT_CLEAR) { - if ((int *)varp == &curbuf->b_p_ar) { - numval = -1; - } else if ((long *)varp == &curbuf->b_p_ul) { - numval = NO_LOCAL_UNDOLEVEL; - } else if ((long *)varp == &curwin->w_p_so || (long *)varp == &curwin->w_p_siso) { - numval = -1; - } else { - char *s = NULL; - (void)get_option_value(name, &numval, (char_u **)&s, OPT_GLOBAL); - } + } + + varp = get_varp_scope(&(options[opt_idx]), opt_flags); + if (varp != NULL) { // hidden option is not changed + if (number == 0 && string != NULL) { + int idx; + + // Either we are given a string or we are setting option + // to zero. + for (idx = 0; string[idx] == '0'; idx++) {} + if (string[idx] != NUL || idx == 0) { + // There's another character after zeros or the string + // is empty. In both cases, we are trying to set a + // num option using a string. + semsg(_("E521: Number required: &%s = '%s'"), + name, string); + return NULL; // do nothing as we hit an error } - if (flags & P_NUM) { - return set_num_option(opt_idx, varp, numval, NULL, 0, opt_flags); + } + long numval = number; + if (opt_flags & OPT_CLEAR) { + if ((int *)varp == &curbuf->b_p_ar) { + numval = -1; + } else if ((long *)varp == &curbuf->b_p_ul) { + numval = NO_LOCAL_UNDOLEVEL; + } else if ((long *)varp == &curwin->w_p_so || (long *)varp == &curwin->w_p_siso) { + numval = -1; } else { - return set_bool_option(opt_idx, varp, (int)numval, opt_flags); + char *s = NULL; + (void)get_option_value(name, &numval, (char_u **)&s, OPT_GLOBAL); } } + if (flags & P_NUM) { + return set_num_option(opt_idx, varp, numval, NULL, 0, opt_flags); + } else { + return set_bool_option(opt_idx, varp, (int)numval, opt_flags); + } } } return NULL; diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 1d589e8ca0..4ad5e40fee 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -1721,7 +1721,7 @@ static void wipe_qf_buffer(qf_info_T *qi) if (qfbuf != NULL && qfbuf->b_nwindows == 0) { // If the quickfix buffer is not loaded in any window, then // wipe the buffer. - close_buffer(NULL, qfbuf, DOBUF_WIPE, false); + close_buffer(NULL, qfbuf, DOBUF_WIPE, false, false); qi->qf_bufnr = INVALID_QFBUFNR; } } @@ -5843,7 +5843,7 @@ static void wipe_dummy_buffer(buf_T *buf, char_u *dirname_start) static void unload_dummy_buffer(buf_T *buf, char_u *dirname_start) { if (curbuf != buf) { // safety check - close_buffer(NULL, buf, DOBUF_UNLOAD, false); + close_buffer(NULL, buf, DOBUF_UNLOAD, false, true); // When autocommands/'autochdir' option changed directory: go back. restore_start_dir(dirname_start); diff --git a/src/nvim/state.c b/src/nvim/state.c index 3a7636085b..34e3ddf654 100644 --- a/src/nvim/state.c +++ b/src/nvim/state.c @@ -40,7 +40,7 @@ void state_enter(VimState *s) int key; getkey: - // Expand mappings first by calling vpeekc() directly. + // Apply mappings first by calling vpeekc() directly. // - If vpeekc() returns non-NUL, there is a character already available for processing, so // don't block for events. vgetc() may still block, in case of an incomplete UTF-8 sequence. // - If vpeekc() returns NUL, vgetc() will block, and there are three cases: @@ -76,6 +76,9 @@ getkey: } if (key == K_EVENT) { + // An event handler may use the value of reg_executing. + // Clear it if it should be cleared when getting the next character. + check_end_reg_executing(true); may_sync_undo(); } diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index a82e8fa103..d884ad704b 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -2289,55 +2289,52 @@ static void check_state_ends(void) next_match_idx = 0; next_match_col = MAXCOL; break; - } else { - // handle next_list, unless at end of line and no "skipnl" or - // "skipempty" - current_next_list = cur_si->si_next_list; - current_next_flags = cur_si->si_flags; - if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)) - && syn_getcurline()[current_col] == NUL) { - current_next_list = NULL; - } + } + + // handle next_list, unless at end of line and no "skipnl" or + // "skipempty" + current_next_list = cur_si->si_next_list; + current_next_flags = cur_si->si_flags; + if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)) + && syn_getcurline()[current_col] == NUL) { + current_next_list = NULL; + } + + // When the ended item has "extend", another item with + // "keepend" now needs to check for its end. + had_extend = (cur_si->si_flags & HL_EXTEND); - // When the ended item has "extend", another item with - // "keepend" now needs to check for its end. - had_extend = (cur_si->si_flags & HL_EXTEND); + pop_current_state(); - pop_current_state(); + if (GA_EMPTY(¤t_state)) { + break; + } + if (had_extend && keepend_level >= 0) { + syn_update_ends(false); if (GA_EMPTY(¤t_state)) { break; } + } - if (had_extend && keepend_level >= 0) { - syn_update_ends(false); - if (GA_EMPTY(¤t_state)) { - break; - } - } - - cur_si = &CUR_STATE(current_state.ga_len - 1); + cur_si = &CUR_STATE(current_state.ga_len - 1); - /* - * Only for a region the search for the end continues after - * the end of the contained item. If the contained match - * included the end-of-line, break here, the region continues. - * Don't do this when: - * - "keepend" is used for the contained item - * - not at the end of the line (could be end="x$"me=e-1). - * - "excludenl" is used (HL_HAS_EOL won't be set) - */ - if (cur_si->si_idx >= 0 - && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type - == SPTYPE_START - && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND))) { - update_si_end(cur_si, (int)current_col, true); - check_keepend(); - if ((current_next_flags & HL_HAS_EOL) - && keepend_level < 0 - && syn_getcurline()[current_col] == NUL) { - break; - } + // Only for a region the search for the end continues after + // the end of the contained item. If the contained match + // included the end-of-line, break here, the region continues. + // Don't do this when: + // - "keepend" is used for the contained item + // - not at the end of the line (could be end="x$"me=e-1). + // - "excludenl" is used (HL_HAS_EOL won't be set) + if (cur_si->si_idx >= 0 + && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type == SPTYPE_START + && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND))) { + update_si_end(cur_si, (int)current_col, true); + check_keepend(); + if ((current_next_flags & HL_HAS_EOL) + && keepend_level < 0 + && syn_getcurline()[current_col] == NUL) { + break; } } } else { diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 76c69ad10b..c1a120efa4 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -299,6 +299,40 @@ func Test_WinClosed() unlet g:triggered endfunc +func Test_WinClosed_throws() + vnew + let bnr = bufnr() + call assert_equal(1, bufloaded(bnr)) + augroup test-WinClosed + autocmd WinClosed * throw 'foo' + augroup END + try + close + catch /.*/ + endtry + call assert_equal(0, bufloaded(bnr)) + + autocmd! test-WinClosed + augroup! test-WinClosed +endfunc + +func Test_WinClosed_throws_with_tabs() + tabnew + let bnr = bufnr() + call assert_equal(1, bufloaded(bnr)) + augroup test-WinClosed + autocmd WinClosed * throw 'foo' + augroup END + try + close + catch /.*/ + endtry + call assert_equal(0, bufloaded(bnr)) + + autocmd! test-WinClosed + augroup! test-WinClosed +endfunc + func s:AddAnAutocmd() augroup vimBarTest au BufReadCmd * echo 'hello' diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim index 10eb979b45..be9a77ee75 100644 --- a/src/nvim/testdir/test_diffmode.vim +++ b/src/nvim/testdir/test_diffmode.vim @@ -1295,4 +1295,41 @@ func Test_diff_filler_cursorcolumn() endfunc +func Test_diff_binary() + CheckScreendump + + let content =<< trim END + call setline(1, ['a', 'b', "c\n", 'd', 'e', 'f', 'g']) + vnew + call setline(1, ['A', 'b', 'c', 'd', 'E', 'f', 'g']) + windo diffthis + wincmd p + norm! gg0 + redraw! + END + call writefile(content, 'Xtest_diff_bin') + let buf = RunVimInTerminal('-S Xtest_diff_bin', {}) + + " Test using internal diff + call VerifyScreenDump(buf, 'Test_diff_bin_01', {}) + + " Test using internal diff and case folding + call term_sendkeys(buf, ":set diffopt+=icase\<cr>") + call term_sendkeys(buf, "\<C-l>") + call VerifyScreenDump(buf, 'Test_diff_bin_02', {}) + " Test using external diff + call term_sendkeys(buf, ":set diffopt=filler\<cr>") + call term_sendkeys(buf, "\<C-l>") + call VerifyScreenDump(buf, 'Test_diff_bin_03', {}) + " Test using external diff and case folding + call term_sendkeys(buf, ":set diffopt=filler,icase\<cr>") + call term_sendkeys(buf, "\<C-l>") + call VerifyScreenDump(buf, 'Test_diff_bin_04', {}) + + " clean up + call StopVimInTerminal(buf) + call delete('Xtest_diff_bin') + set diffopt&vim +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim index 360b3aaaa0..eea5d190b2 100644 --- a/src/nvim/testdir/test_edit.vim +++ b/src/nvim/testdir/test_edit.vim @@ -1627,6 +1627,29 @@ func Test_edit_is_a_directory() call delete(dirname, 'rf') endfunc +" Using :edit without leaving 'insertmode' should not cause Insert mode to be +" re-entered immediately after <C-L> +func Test_edit_insertmode_ex_edit() + CheckRunVimInTerminal + + let lines =<< trim END + set insertmode noruler + inoremap <C-B> <Cmd>edit Xfoo<CR> + END + call writefile(lines, 'Xtest_edit_insertmode_ex_edit') + + let buf = RunVimInTerminal('-S Xtest_edit_insertmode_ex_edit', #{rows: 6}) + call TermWait(buf, 50) + call assert_match('^-- INSERT --\s*$', term_getline(buf, 6)) + call term_sendkeys(buf, "\<C-B>\<C-L>") + call TermWait(buf, 50) + call assert_notmatch('^-- INSERT --\s*$', term_getline(buf, 6)) + + " clean up + call StopVimInTerminal(buf) + call delete('Xtest_edit_insertmode_ex_edit') +endfunc + func Test_edit_browse() " in the GUI this opens a file picker, we only test the terminal behavior CheckNotGui diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index a31ff50a48..42271a014d 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -395,7 +395,7 @@ let s:filename_checks = { \ 'perl': ['file.plx', 'file.al', 'file.psgi', 'gitolite.rc', '.gitolite.rc', 'example.gitolite.rc'], \ 'pf': ['pf.conf'], \ 'pfmain': ['main.cf'], - \ 'php': ['file.php', 'file.php9', 'file.phtml', 'file.ctp'], + \ 'php': ['file.php', 'file.php9', 'file.phtml', 'file.ctp', 'file.phpt'], \ 'lpc': ['file.lpc', 'file.ulpc'], \ 'pike': ['file.pike', 'file.pmod'], \ 'cmod': ['file.cmod'], diff --git a/src/nvim/testdir/test_highlight.vim b/src/nvim/testdir/test_highlight.vim index 5588e515e1..efdf44a0d6 100644 --- a/src/nvim/testdir/test_highlight.vim +++ b/src/nvim/testdir/test_highlight.vim @@ -615,6 +615,14 @@ func Test_cursorcolumn_insert_on_tab() call TermWait(buf) call VerifyScreenDump(buf, 'Test_cursorcolumn_insert_on_tab_2', {}) + call term_sendkeys(buf, "\<C-O>") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_cursorcolumn_insert_on_tab_3', {}) + + call term_sendkeys(buf, 'i') + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_cursorcolumn_insert_on_tab_2', {}) + call StopVimInTerminal(buf) call delete('Xcuc_insert_on_tab') endfunc diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim index 186fa8871f..24eaf9e8b1 100644 --- a/src/nvim/testdir/test_ins_complete.vim +++ b/src/nvim/testdir/test_ins_complete.vim @@ -707,4 +707,23 @@ func Test_z1_complete_no_history() close! endfunc +func FooBarComplete(findstart, base) + if a:findstart + return col('.') - 1 + else + return ["Foo", "Bar", "}"] + endif +endfunc + +func Test_complete_smartindent() + new + setlocal smartindent completefunc=FooBarComplete + + exe "norm! o{\<cr>\<c-x>\<c-u>\<c-p>}\<cr>\<esc>" + let result = getline(1,'$') + call assert_equal(['', '{','}',''], result) + bw! + delfunction! FooBarComplete +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim index f78b748d71..c623edd5a1 100644 --- a/src/nvim/testdir/test_registers.vim +++ b/src/nvim/testdir/test_registers.vim @@ -690,5 +690,23 @@ func Test_record_in_select_mode() bwipe! endfunc +func Test_end_reg_executing() + nnoremap s <Nop> + let @a = 's' + call feedkeys("@aqaq\<Esc>", 'tx') + call assert_equal('', @a) + call assert_equal('', getline(1)) + + call setline(1, 'aaa') + nnoremap s qa + let @a = 'fa' + call feedkeys("@asq\<Esc>", 'tx') + call assert_equal('', @a) + call assert_equal('aaa', getline(1)) + + nunmap s + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_trycatch.vim b/src/nvim/testdir/test_trycatch.vim index 7e513180a3..adc1745b39 100644 --- a/src/nvim/testdir/test_trycatch.vim +++ b/src/nvim/testdir/test_trycatch.vim @@ -1972,6 +1972,29 @@ func Test_builtin_func_error() call assert_equal('jlmnpqrtueghivyzACD', g:Xpath) endfunc -" Modelines {{{1 +func Test_reload_in_try_catch() + call writefile(['x'], 'Xreload') + set autoread + edit Xreload + tabnew + call writefile(['xx'], 'Xreload') + augroup ReLoad + au FileReadPost Xreload let x = doesnotexist + au BufReadPost Xreload let x = doesnotexist + augroup END + try + edit Xreload + catch + endtry + tabnew + + tabclose + tabclose + autocmd! ReLoad + set noautoread + bwipe! Xreload + call delete('Xreload') +endfunc + +" Modeline {{{1 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker -"------------------------------------------------------------------------------- diff --git a/src/nvim/window.c b/src/nvim/window.c index cc21bf25b0..9aa2f947cb 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -2541,6 +2541,41 @@ static bool close_last_window_tabpage(win_T *win, bool free_buf, tabpage_T *prev return true; } +/// Close the buffer of "win" and unload it if "free_buf" is true. +/// "abort_if_last" is passed to close_buffer(): abort closing if all other +/// windows are closed. +static void win_close_buffer(win_T *win, bool free_buf, bool abort_if_last) +{ + // Free independent synblock before the buffer is freed. + if (win->w_buffer != NULL) { + reset_synblock(win); + } + + // When a quickfix/location list window is closed and the buffer is + // displayed in only one window, then unlist the buffer. + if (win->w_buffer != NULL && bt_quickfix(win->w_buffer) + && win->w_buffer->b_nwindows == 1) { + win->w_buffer->b_p_bl = false; + } + + // Close the link to the buffer. + if (win->w_buffer != NULL) { + bufref_T bufref; + set_bufref(&bufref, curbuf); + win->w_closing = true; + close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, abort_if_last, true); + if (win_valid_any_tab(win)) { + win->w_closing = false; + } + + // Make sure curbuf is valid. It can become invalid if 'bufhidden' is + // "wipe". + if (!bufref_valid(&bufref)) { + curbuf = firstbuf; + } + } +} + // Close window "win". Only works for the current tab page. // If "free_buf" is true related buffer may be unloaded. // @@ -2679,36 +2714,7 @@ int win_close(win_T *win, bool free_buf, bool force) return OK; } - // Free independent synblock before the buffer is freed. - if (win->w_buffer != NULL) { - reset_synblock(win); - } - - // When a quickfix/location list window is closed and the buffer is - // displayed in only one window, then unlist the buffer. - if (win->w_buffer != NULL && bt_quickfix(win->w_buffer) - && win->w_buffer->b_nwindows == 1) { - win->w_buffer->b_p_bl = false; - } - - /* - * Close the link to the buffer. - */ - if (win->w_buffer != NULL) { - bufref_T bufref; - set_bufref(&bufref, curbuf); - win->w_closing = true; - close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, true); - if (win_valid_any_tab(win)) { - win->w_closing = false; - } - - // Make sure curbuf is valid. It can become invalid if 'bufhidden' is - // "wipe". - if (!bufref_valid(&bufref)) { - curbuf = firstbuf; - } - } + win_close_buffer(win, free_buf, true); if (only_one_window() && win_valid(win) && win->w_buffer == NULL && (last_window(win) || curtab != prev_curtab @@ -2879,7 +2885,7 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp) if (win->w_buffer != NULL) { // Close the link to the buffer. - close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, false); + close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, false, true); } // Careful: Autocommands may have closed the tab page or made it the |