patch 9.1.1473: inconsistent range arg for :diffget/diffput
Commit:
https://github.com/vim/vim/commit/d75ab0cbf5cfaefab3edb0aa553954de70b236f8
Author: Yee Cheng Chin <[email protected]>
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 <[email protected]>
Signed-off-by: Christian Brabandt <[email protected]>
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 [email protected].
To view this discussion visit
https://groups.google.com/d/msgid/vim_dev/E1uSf61-00ASPa-0d%40256bit.org.