patch 9.1.1473: inconsistent range arg for :diffget/diffput Commit: https://github.com/vim/vim/commit/d75ab0cbf5cfaefab3edb0aa553954de70b236f8 Author: Yee Cheng Chin <ychin....@gmail.com> Date: Fri Jun 20 18:44:18 2025 +0200
patch 9.1.1473: inconsistent range arg for :diffget/diffput Problem: inconsistent range arg for :diffget/diffput Solution: fix the range specification, place the cursor for :diffput and :diffget consistently on the last line (Yee Cheng Chin) Previously, `:<range>diffget` only allowed using 1 or above in the range value, making it impossible to use the command for a diff block at the beginning of the file. Fix the range specification so the user can now use 0 to specify the space before the first line. This allows `:0,$+1diffget` to work to retrieve all the changes from the other file instead of missing the first diff block. Also do this for `:diffput`. Also, make `:diffput` work more similar to `:diffget`. Make it so that if the cursor is on the last line and a new line is inserted in the other file, doing `:diffput` will select that diff block below the line, just like `:diffget` would. Also clean up the logic a little bit for edge cases and for handling line matched diff blocks better. closes: #17579 Signed-off-by: Yee Cheng Chin <ychin....@gmail.com> Signed-off-by: Christian Brabandt <c...@256bit.org> diff --git a/runtime/doc/diff.txt b/runtime/doc/diff.txt index 81564664e..493c740ad 100644 --- a/runtime/doc/diff.txt +++ b/runtime/doc/diff.txt @@ -1,4 +1,4 @@ -*diff.txt* For Vim version 9.1. Last change: 2025 Mar 28 +*diff.txt* For Vim version 9.1. Last change: 2025 Jun 20 VIM REFERENCE MANUAL by Bram Moolenaar @@ -304,18 +304,20 @@ that the buffers will be equal within the specified range. When no [range] is given, the diff at the cursor position or just above it is -affected. When [range] is used, Vim tries to only put or get the specified -lines. When there are deleted lines, this may not always be possible. +affected. There can be deleted lines below the last line of the buffer. When +the cursor is on the last line in the buffer and there is no diff above this +line, and no [range] is given, the diff below the cursor position will be used +instead. -There can be deleted lines below the last line of the buffer. When the cursor -is on the last line in the buffer and there is no diff above this line, the -":diffget" and "do" commands will obtain lines from the other buffer. +When [range] is used, Vim tries to only put or get the specified lines. When +there are deleted lines, they will be used if they are between the lines +specified by [range]. -To be able to get those lines from another buffer in a [range] it's allowed to -use the last line number plus one. This command gets all diffs from the other -buffer: > +To be able to put or get those lines to/from another buffer in a [range] it's +allowed to use 0 and the last line number plus one. This command gets all +diffs from the other buffer: > - :1,$+1diffget + :0,$+1diffget Note that deleted lines are displayed, but not counted as text lines. You can't move the cursor into them. To fill the deleted lines with the lines diff --git a/src/diff.c b/src/diff.c index c4f550d63..b212e71fe 100644 --- a/src/diff.c +++ b/src/diff.c @@ -3874,10 +3874,13 @@ ex_diffgetput(exarg_T *eap) { // Make it possible that ":diffget" on the last line gets line below // the cursor line when there is no difference above the cursor. - if (eap->cmdidx == CMD_diffget - && eap->line1 == curbuf->b_ml.ml_line_count - && diff_check(curwin, eap->line1) == 0 - && (eap->line1 == 1 || diff_check(curwin, eap->line1 - 1) == 0)) + int linestatus = 0; + if (eap->line1 == curbuf->b_ml.ml_line_count + && (diff_check_with_linestatus(curwin, eap->line1, &linestatus) == 0 + && linestatus == 0) + && (eap->line1 == 1 || + (diff_check_with_linestatus(curwin, eap->line1 - 1, &linestatus) >= 0 + && linestatus == 0))) ++eap->line2; else if (eap->line1 > 0) --eap->line1; diff --git a/src/ex_cmds.h b/src/ex_cmds.h index fb8d62f60..2bbf5ef00 100644 --- a/src/ex_cmds.h +++ b/src/ex_cmds.h @@ -486,7 +486,7 @@ EXCMD(CMD_diffupdate, "diffupdate", ex_diffupdate, EX_BANG|EX_TRLBAR, ADDR_NONE), EXCMD(CMD_diffget, "diffget", ex_diffgetput, - EX_RANGE|EX_EXTRA|EX_TRLBAR|EX_MODIFY, + EX_RANGE|EX_ZEROR|EX_EXTRA|EX_TRLBAR|EX_MODIFY, ADDR_LINES), EXCMD(CMD_diffoff, "diffoff", ex_diffoff, EX_BANG|EX_TRLBAR, @@ -495,7 +495,7 @@ EXCMD(CMD_diffpatch, "diffpatch", ex_diffpatch, EX_EXTRA|EX_FILE1|EX_TRLBAR|EX_MODIFY, ADDR_NONE), EXCMD(CMD_diffput, "diffput", ex_diffgetput, - EX_RANGE|EX_EXTRA|EX_TRLBAR, + EX_RANGE|EX_ZEROR|EX_EXTRA|EX_TRLBAR, ADDR_LINES), EXCMD(CMD_diffsplit, "diffsplit", ex_diffsplit, EX_EXTRA|EX_FILE1|EX_TRLBAR, diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 08d2c1e66..4ccc5f759 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -4850,7 +4850,8 @@ invalid_range(exarg_T *eap) case ADDR_LINES: if (eap->line2 > curbuf->b_ml.ml_line_count #ifdef FEAT_DIFF - + (eap->cmdidx == CMD_diffget) + + (eap->cmdidx == CMD_diffget || + eap->cmdidx == CMD_diffput) #endif ) return _(e_invalid_range); diff --git a/src/testdir/test_diffmode.vim b/src/testdir/test_diffmode.vim index 1ab194568..a348d3a40 100644 --- a/src/testdir/test_diffmode.vim +++ b/src/testdir/test_diffmode.vim @@ -288,6 +288,63 @@ func Test_diffget_diffput_range() %bw! endfunc +" Test :diffget/:diffput handling of added/deleted lines +func Test_diffget_diffput_deleted_lines() + call setline(1, ['2','4','6']) + diffthis + new + call setline(1, range(1,7)) + diffthis + wincmd w + + 3,3diffget " get nothing + call assert_equal(['2', '4', '6'], getline(1, '$')) + 3,4diffget " get the last insertion past the end of file + call assert_equal(['2', '4', '6', '7'], getline(1, '$')) + 0,1diffget " get the first insertion above first line + call assert_equal(['1', '2', '4', '6', '7'], getline(1, '$')) + + " When using non-range diffget on the last line, it should get the + " change above or at the line as usual, but if the only change is below the + " last line, diffget should get that instead. + 1,$delete + call setline(1, ['2','4','6']) + diffupdate + norm Gdo + call assert_equal(['2', '4', '5', '6'], getline(1, '$')) + norm Gdo + call assert_equal(['2', '4', '5', '6', '7'], getline(1, '$')) + + " Test non-range diffput on last line with the same logic + 1,$delete + call setline(1, ['2','4','6']) + diffupdate + norm Gdp + wincmd w + call assert_equal(['1', '2', '3', '4', '6', '7'], getline(1, '$')) + wincmd w + norm Gdp + wincmd w + call assert_equal(['1', '2', '3', '4', '6'], getline(1, '$')) + call setline(1, range(1,7)) + diffupdate + wincmd w + + " Test that 0,$+1 will get/put all changes from/to the other buffer + 1,$delete + call setline(1, ['2','4','6']) + diffupdate + 0,$+1diffget + call assert_equal(['1', '2', '3', '4', '5', '6', '7'], getline(1, '$')) + 1,$delete + call setline(1, ['2','4','6']) + diffupdate + 0,$+1diffput + wincmd w + call assert_equal(['2', '4', '6'], getline(1, '$')) + %bw! +endfunc + " Test for :diffget/:diffput with an empty buffer and a non-empty buffer func Test_diffget_diffput_empty_buffer() %d _ diff --git a/src/version.c b/src/version.c index e8673ed06..bafcef18b 100644 --- a/src/version.c +++ b/src/version.c @@ -709,6 +709,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1473, /**/ 1472, /**/ -- -- You received this message from the "vim_dev" maillist. Do not top-post! Type your reply below the text you are replying to. For more information, visit http://www.vim.org/maillist.php --- You received this message because you are subscribed to the Google Groups "vim_dev" group. To unsubscribe from this group and stop receiving emails from it, send an email to vim_dev+unsubscr...@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/vim_dev/E1uSf61-00ASPa-0d%40256bit.org.