# HG changeset patch # User Bram Moolenaar # Date 1573909205 -3600 # Node ID 788d76db02ac7c19bd5e482f28979e8164c4d19c # Parent 74422854344df6d2503afcb91de418da745999a7 patch 8.1.2302: :lockmarks does not work for '[ and '] Commit: https://github.com/vim/vim/commit/f4a1d1c0542df151bc59ac3b798ed198b5c71ccc Author: Bram Moolenaar Date: Sat Nov 16 13:50:25 2019 +0100 patch 8.1.2302: :lockmarks does not work for '[ and '] Problem: :lockmarks does not work for '[ and ']. Solution: save and restore '[ and '] marks. (James McCoy, closes https://github.com/vim/vim/issues/5222) diff --git a/src/bufwrite.c b/src/bufwrite.c --- a/src/bufwrite.c +++ b/src/bufwrite.c @@ -683,6 +683,8 @@ buf_write( context_sha256_T sha_ctx; #endif unsigned int bkc = get_bkc_value(buf); + pos_T orig_start = buf->b_op_start; + pos_T orig_end = buf->b_op_end; if (fname == NULL || *fname == NUL) // safety check return FAIL; @@ -875,6 +877,13 @@ buf_write( #endif ) { + if (buf != NULL && cmdmod.lockmarks) + { + // restore the original '[ and '] positions + buf->b_op_start = orig_start; + buf->b_op_end = orig_end; + } + --no_wait_return; msg_scroll = msg_save; if (nofile_err) @@ -952,6 +961,13 @@ buf_write( fname = buf->b_sfname; } + if (cmdmod.lockmarks) + { + // restore the original '[ and '] positions + buf->b_op_start = orig_start; + buf->b_op_end = orig_end; + } + #ifdef FEAT_NETBEANS_INTG if (netbeans_active() && isNetbeansBuffer(buf)) { diff --git a/src/diff.c b/src/diff.c --- a/src/diff.c +++ b/src/diff.c @@ -772,6 +772,7 @@ diff_write(buf_T *buf, diffin_T *din) { int r; char_u *save_ff; + int save_lockmarks; if (din->din_fname == NULL) return diff_write_buffer(buf, din); @@ -779,9 +780,14 @@ diff_write(buf_T *buf, diffin_T *din) // Always use 'fileformat' set to "unix". save_ff = buf->b_p_ff; buf->b_p_ff = vim_strsave((char_u *)FF_UNIX); + save_lockmarks = cmdmod.lockmarks; + // Writing the buffer is an implementation detail of performing the diff, + // so it shouldn't update the '[ and '] marks. + cmdmod.lockmarks = TRUE; r = buf_write(buf, din->din_fname, NULL, (linenr_T)1, buf->b_ml.ml_line_count, NULL, FALSE, FALSE, FALSE, TRUE); + cmdmod.lockmarks = save_lockmarks; free_string_option(buf->b_p_ff); buf->b_p_ff = save_ff; return r; diff --git a/src/ex_cmds.c b/src/ex_cmds.c --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -749,8 +749,11 @@ do_move(linenr_T line1, linenr_T line2, foldMoveRange(&win->w_folds, line1, line2, dest); } #endif - curbuf->b_op_start.lnum = dest - num_lines + 1; - curbuf->b_op_end.lnum = dest; + if (!cmdmod.lockmarks) + { + curbuf->b_op_start.lnum = dest - num_lines + 1; + curbuf->b_op_end.lnum = dest; + } } else { @@ -761,10 +764,14 @@ do_move(linenr_T line1, linenr_T line2, foldMoveRange(&win->w_folds, dest + 1, line1 - 1, line2); } #endif - curbuf->b_op_start.lnum = dest + 1; - curbuf->b_op_end.lnum = dest + num_lines; + if (!cmdmod.lockmarks) + { + curbuf->b_op_start.lnum = dest + 1; + curbuf->b_op_end.lnum = dest + num_lines; + } } - curbuf->b_op_start.col = curbuf->b_op_end.col = 0; + if (!cmdmod.lockmarks) + curbuf->b_op_start.col = curbuf->b_op_end.col = 0; mark_adjust_nofold(last_line - num_lines + 1, last_line, -(last_line - dest - extra), 0L); @@ -813,9 +820,12 @@ ex_copy(linenr_T line1, linenr_T line2, char_u *p; count = line2 - line1 + 1; - curbuf->b_op_start.lnum = n + 1; - curbuf->b_op_end.lnum = n + count; - curbuf->b_op_start.col = curbuf->b_op_end.col = 0; + if (!cmdmod.lockmarks) + { + curbuf->b_op_start.lnum = n + 1; + curbuf->b_op_end.lnum = n + count; + curbuf->b_op_start.col = curbuf->b_op_end.col = 0; + } /* * there are three situations: @@ -1055,10 +1065,17 @@ do_filter( char_u *cmd_buf; buf_T *old_curbuf = curbuf; int shell_flags = 0; + pos_T orig_start = curbuf->b_op_start; + pos_T orig_end = curbuf->b_op_end; + int save_lockmarks = cmdmod.lockmarks; if (*cmd == NUL) /* no filter command */ return; + // Temporarily disable lockmarks since that's needed to propagate changed + // regions of the buffer for foldUpdate(), linecount, etc. + cmdmod.lockmarks = 0; + cursor_save = curwin->w_cursor; linecount = line2 - line1 + 1; curwin->w_cursor.lnum = line1; @@ -1287,11 +1304,18 @@ error: filterend: + cmdmod.lockmarks = save_lockmarks; if (curbuf != old_curbuf) { --no_wait_return; emsg(_("E135: *Filter* Autocommands must not change current buffer")); } + else if (cmdmod.lockmarks) + { + curbuf->b_op_start = orig_start; + curbuf->b_op_end = orig_end; + } + if (itmp != NULL) mch_remove(itmp); if (otmp != NULL) @@ -3276,13 +3300,16 @@ ex_append(exarg_T *eap) * eap->line2 pointed to the end of the buffer and nothing was appended) * "end" is set to lnum when something has been appended, otherwise * it is the same than "start" -- Acevedo */ - curbuf->b_op_start.lnum = (eap->line2 < curbuf->b_ml.ml_line_count) ? - eap->line2 + 1 : curbuf->b_ml.ml_line_count; - if (eap->cmdidx != CMD_append) - --curbuf->b_op_start.lnum; - curbuf->b_op_end.lnum = (eap->line2 < lnum) - ? lnum : curbuf->b_op_start.lnum; - curbuf->b_op_start.col = curbuf->b_op_end.col = 0; + if (!cmdmod.lockmarks) + { + curbuf->b_op_start.lnum = (eap->line2 < curbuf->b_ml.ml_line_count) ? + eap->line2 + 1 : curbuf->b_ml.ml_line_count; + if (eap->cmdidx != CMD_append) + --curbuf->b_op_start.lnum; + curbuf->b_op_end.lnum = (eap->line2 < lnum) + ? lnum : curbuf->b_op_start.lnum; + curbuf->b_op_start.col = curbuf->b_op_end.col = 0; + } curwin->w_cursor.lnum = lnum; check_cursor_lnum(); beginline(BL_SOL | BL_FIX); @@ -4592,10 +4619,13 @@ outofmem: if (sub_nsubs > start_nsubs) { - /* Set the '[ and '] marks. */ - curbuf->b_op_start.lnum = eap->line1; - curbuf->b_op_end.lnum = line2; - curbuf->b_op_start.col = curbuf->b_op_end.col = 0; + if (!cmdmod.lockmarks) + { + // Set the '[ and '] marks. + curbuf->b_op_start.lnum = eap->line1; + curbuf->b_op_end.lnum = line2; + curbuf->b_op_start.col = curbuf->b_op_end.col = 0; + } if (!global_busy) { diff --git a/src/fileio.c b/src/fileio.c --- a/src/fileio.c +++ b/src/fileio.c @@ -188,6 +188,7 @@ readfile( wasn't possible */ char_u conv_rest[CONV_RESTLEN]; int conv_restlen = 0; /* nr of bytes in conv_rest[] */ + pos_T orig_start; buf_T *old_curbuf; char_u *old_b_ffname; char_u *old_b_fname; @@ -250,9 +251,7 @@ readfile( */ if (!filtering && !read_stdin && !read_buffer) { - pos_T pos; - - pos = curbuf->b_op_start; + orig_start = curbuf->b_op_start; /* Set '[ mark to the line above where the lines go (line 1 if zero). */ curbuf->b_op_start.lnum = ((from == 0) ? 1 : from); @@ -276,7 +275,7 @@ readfile( return OK; #endif - curbuf->b_op_start = pos; + curbuf->b_op_start = orig_start; } if ((shortmess(SHM_OVER) || curbuf->b_help) && p_verbose == 0) @@ -617,6 +616,7 @@ readfile( /* * Set '[ mark to the line above where the lines go (line 1 if zero). */ + orig_start = curbuf->b_op_start; curbuf->b_op_start.lnum = ((from == 0) ? 1 : from); curbuf->b_op_start.col = 0; @@ -658,6 +658,7 @@ readfile( try_mac = (vim_strchr(p_ffs, 'm') != NULL); try_dos = (vim_strchr(p_ffs, 'd') != NULL); try_unix = (vim_strchr(p_ffs, 'x') != NULL); + curbuf->b_op_start = orig_start; if (msg_scrolled == n) msg_scroll = m; @@ -2471,13 +2472,14 @@ failed: check_cursor_lnum(); beginline(BL_WHITE | BL_FIX); /* on first non-blank */ - /* - * Set '[ and '] marks to the newly read lines. - */ - curbuf->b_op_start.lnum = from + 1; - curbuf->b_op_start.col = 0; - curbuf->b_op_end.lnum = from + linecnt; - curbuf->b_op_end.col = 0; + if (!cmdmod.lockmarks) + { + // Set '[ and '] marks to the newly read lines. + curbuf->b_op_start.lnum = from + 1; + curbuf->b_op_start.col = 0; + curbuf->b_op_end.lnum = from + linecnt; + curbuf->b_op_end.col = 0; + } #ifdef MSWIN /* diff --git a/src/indent.c b/src/indent.c --- a/src/indent.c +++ b/src/indent.c @@ -1001,9 +1001,12 @@ op_reindent(oparg_T *oap, int (*how)(voi smsg(NGETTEXT("%ld line indented ", "%ld lines indented ", i), i); } - // set '[ and '] marks - curbuf->b_op_start = oap->start; - curbuf->b_op_end = oap->end; + if (!cmdmod.lockmarks) + { + // set '[ and '] marks + curbuf->b_op_start = oap->start; + curbuf->b_op_end = oap->end; + } } #endif // defined(FEAT_LISP) || defined(FEAT_CINDENT) diff --git a/src/ops.c b/src/ops.c --- a/src/ops.c +++ b/src/ops.c @@ -206,14 +206,15 @@ op_shift(oparg_T *oap, int curs_top, int msg((char *)IObuff); } - /* - * Set "'[" and "']" marks. - */ - curbuf->b_op_start = oap->start; - curbuf->b_op_end.lnum = oap->end.lnum; - curbuf->b_op_end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum)); - if (curbuf->b_op_end.col > 0) - --curbuf->b_op_end.col; + if (!cmdmod.lockmarks) + { + // Set "'[" and "']" marks. + curbuf->b_op_start = oap->start; + curbuf->b_op_end.lnum = oap->end.lnum; + curbuf->b_op_end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum)); + if (curbuf->b_op_end.col > 0) + --curbuf->b_op_end.col; + } } /* @@ -981,14 +982,17 @@ op_delete(oparg_T *oap) msgmore(curbuf->b_ml.ml_line_count - old_lcount); setmarks: - if (oap->block_mode) + if (!cmdmod.lockmarks) { - curbuf->b_op_end.lnum = oap->end.lnum; - curbuf->b_op_end.col = oap->start.col; + if (oap->block_mode) + { + curbuf->b_op_end.lnum = oap->end.lnum; + curbuf->b_op_end.col = oap->start.col; + } + else + curbuf->b_op_end = oap->start; + curbuf->b_op_start = oap->start; } - else - curbuf->b_op_end = oap->start; - curbuf->b_op_start = oap->start; return OK; } @@ -1252,9 +1256,12 @@ op_replace(oparg_T *oap, int c) check_cursor(); changed_lines(oap->start.lnum, oap->start.col, oap->end.lnum + 1, 0L); - /* Set "'[" and "']" marks. */ - curbuf->b_op_start = oap->start; - curbuf->b_op_end = oap->end; + if (!cmdmod.lockmarks) + { + /* Set "'[" and "']" marks. */ + curbuf->b_op_start = oap->start; + curbuf->b_op_end = oap->end; + } return OK; } @@ -1362,11 +1369,12 @@ op_tilde(oparg_T *oap) /* No change: need to remove the Visual selection */ redraw_curbuf_later(INVERTED); - /* - * Set '[ and '] marks. - */ - curbuf->b_op_start = oap->start; - curbuf->b_op_end = oap->end; + if (!cmdmod.lockmarks) + { + // Set '[ and '] marks. + curbuf->b_op_start = oap->start; + curbuf->b_op_end = oap->end; + } if (oap->line_count > p_report) smsg(NGETTEXT("%ld line changed", "%ld lines changed", @@ -1973,7 +1981,7 @@ do_join( for (t = 0; t < count; ++t) { curr = curr_start = ml_get((linenr_T)(curwin->w_cursor.lnum + t)); - if (t == 0 && setmark) + if (t == 0 && setmark && !cmdmod.lockmarks) { /* Set the '[ mark. */ curwin->w_buffer->b_op_start.lnum = curwin->w_cursor.lnum; @@ -2129,7 +2137,7 @@ do_join( #endif ml_replace(curwin->w_cursor.lnum, newp, FALSE); - if (setmark) + if (setmark && !cmdmod.lockmarks) { /* Set the '] mark. */ curwin->w_buffer->b_op_end.lnum = curwin->w_cursor.lnum; @@ -2268,8 +2276,9 @@ op_format( /* When there is no change: need to remove the Visual selection */ redraw_curbuf_later(INVERTED); - /* Set '[ mark at the start of the formatted area */ - curbuf->b_op_start = oap->start; + if (!cmdmod.lockmarks) + /* Set '[ mark at the start of the formatted area */ + curbuf->b_op_start = oap->start; /* For "gw" remember the cursor position and put it back below (adjusted * for joined and split lines). */ @@ -2289,8 +2298,9 @@ op_format( old_line_count = curbuf->b_ml.ml_line_count - old_line_count; msgmore(old_line_count); - /* put '] mark on the end of the formatted area */ - curbuf->b_op_end = curwin->w_cursor; + if (!cmdmod.lockmarks) + /* put '] mark on the end of the formatted area */ + curbuf->b_op_end = curwin->w_cursor; if (keep_cursor) { @@ -2984,7 +2994,7 @@ op_addsub( /* Set '[ mark if something changed. Keep the last end * position from do_addsub(). */ - if (change_cnt > 0) + if (change_cnt > 0 && !cmdmod.lockmarks) curbuf->b_op_start = startpos; if (change_cnt > p_report) @@ -3384,7 +3394,7 @@ do_addsub( --curwin->w_cursor.col; } - if (did_change) + if (did_change && !cmdmod.lockmarks) { /* set the '[ and '] marks */ curbuf->b_op_start = startpos; @@ -3905,6 +3915,8 @@ op_function(oparg_T *oap UNUSED) #ifdef FEAT_EVAL typval_T argv[2]; int save_virtual_op = virtual_op; + pos_T orig_start = curbuf->b_op_start; + pos_T orig_end = curbuf->b_op_end; if (*p_opfunc == NUL) emsg(_("E774: 'operatorfunc' is empty")); @@ -3933,6 +3945,11 @@ op_function(oparg_T *oap UNUSED) (void)call_func_retnr(p_opfunc, 1, argv); virtual_op = save_virtual_op; + if (cmdmod.lockmarks) + { + curbuf->b_op_start = orig_start; + curbuf->b_op_end = orig_end; + } } #else emsg(_("E775: Eval feature not available")); diff --git a/src/register.c b/src/register.c --- a/src/register.c +++ b/src/register.c @@ -1316,13 +1316,16 @@ op_yank(oparg_T *oap, int deleting, int } } - // Set "'[" and "']" marks. - curbuf->b_op_start = oap->start; - curbuf->b_op_end = oap->end; - if (yanktype == MLINE && !oap->block_mode) + if (!cmdmod.lockmarks) { - curbuf->b_op_start.col = 0; - curbuf->b_op_end.col = MAXCOL; + // Set "'[" and "']" marks. + curbuf->b_op_start = oap->start; + curbuf->b_op_end = oap->end; + if (yanktype == MLINE && !oap->block_mode) + { + curbuf->b_op_start.col = 0; + curbuf->b_op_end.col = MAXCOL; + } } #ifdef FEAT_CLIPBOARD @@ -1474,6 +1477,8 @@ do_put( char_u *insert_string = NULL; int allocated = FALSE; long cnt; + pos_T orig_start = curbuf->b_op_start; + pos_T orig_end = curbuf->b_op_end; #ifdef FEAT_CLIPBOARD // Adjust register name for "unnamed" in 'clipboard'. @@ -2100,6 +2105,11 @@ error: curwin->w_set_curswant = TRUE; end: + if (cmdmod.lockmarks) + { + curbuf->b_op_start = orig_start; + curbuf->b_op_end = orig_end; + } if (allocated) vim_free(insert_string); if (regname == '=') diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim --- a/src/testdir/test_autocmd.vim +++ b/src/testdir/test_autocmd.vim @@ -2297,3 +2297,37 @@ func Test_autocmd_was_using_freed_memory split au! WinEnter endfunc + +func Test_BufWrite_lockmarks() + edit! Xtest + call setline(1, ['a', 'b', 'c', 'd']) + + " :lockmarks preserves the marks + call SetChangeMarks(2, 3) + lockmarks write + call assert_equal([2, 3], [line("'["), line("']")]) + + " *WritePre autocmds get the correct line range, but lockmarks preserves the + " original values for the user + augroup lockmarks + au! + au BufWritePre,FilterWritePre * call assert_equal([1, 4], [line("'["), line("']")]) + au FileWritePre * call assert_equal([3, 4], [line("'["), line("']")]) + augroup END + + lockmarks write + call assert_equal([2, 3], [line("'["), line("']")]) + + if executable('cat') + lockmarks %!cat + call assert_equal([2, 3], [line("'["), line("']")]) + endif + + lockmarks 3,4write Xtest2 + call assert_equal([2, 3], [line("'["), line("']")]) + + au! lockmarks + augroup! lockmarks + call delete('Xtest') + call delete('Xtest2') +endfunc diff --git a/src/testdir/test_diffmode.vim b/src/testdir/test_diffmode.vim --- a/src/testdir/test_diffmode.vim +++ b/src/testdir/test_diffmode.vim @@ -989,3 +989,22 @@ func Test_diff_closeoff() diffoff! enew! endfunc + +func Test_diff_maintains_change_mark() + enew! + call setline(1, ['a', 'b', 'c', 'd']) + diffthis + new + call setline(1, ['a', 'b', 'c', 'e']) + " Set '[ and '] marks + 2,3yank + call assert_equal([2, 3], [line("'["), line("']")]) + " Verify they aren't affected by the implicit diff + diffthis + call assert_equal([2, 3], [line("'["), line("']")]) + " Verify they aren't affected by an explicit diff + diffupdate + call assert_equal([2, 3], [line("'["), line("']")]) + bwipe! + bwipe! +endfunc diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -742,6 +742,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2302, +/**/ 2301, /**/ 2300,