vim-patch:9.1.1473: inconsistent range arg for :diffget/diffput (#34588)

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: vim/vim#17579

d75ab0cbf5

Co-authored-by: Yee Cheng Chin <ychin.git@gmail.com>
This commit is contained in:
zeertzjq
2025-06-22 08:00:17 +08:00
committed by GitHub
parent 0980617c0d
commit ee2fc31b36
5 changed files with 80 additions and 17 deletions

View File

@ -292,18 +292,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

View File

@ -3492,10 +3492,13 @@ void ex_diffgetput(exarg_T *eap)
if (eap->addr_count == 0) {
// 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--;

View File

@ -752,7 +752,7 @@ M.cmds = {
},
{
command = 'diffget',
flags = bit.bor(RANGE, EXTRA, TRLBAR, MODIFY),
flags = bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, MODIFY),
addr_type = 'ADDR_LINES',
func = 'ex_diffgetput',
},
@ -770,7 +770,7 @@ M.cmds = {
},
{
command = 'diffput',
flags = bit.bor(RANGE, EXTRA, TRLBAR),
flags = bit.bor(RANGE, ZEROR, EXTRA, TRLBAR),
addr_type = 'ADDR_LINES',
func = 'ex_diffgetput',
},

View File

@ -3728,8 +3728,9 @@ char *invalid_range(exarg_T *eap)
if (eap->argt & EX_RANGE) {
switch (eap->addr_type) {
case ADDR_LINES:
if (eap->line2 > (curbuf->b_ml.ml_line_count
+ (eap->cmdidx == CMD_diffget))) {
if (eap->line2 >
(curbuf->b_ml.ml_line_count
+ (eap->cmdidx == CMD_diffget || eap->cmdidx == CMD_diffput))) {
return _(e_invrange);
}
break;

View File

@ -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 _