1
0
forked from aniani/vim

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.git@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Yee Cheng Chin 2025-06-20 18:44:18 +02:00 committed by Christian Brabandt
parent 476b65ebac
commit d75ab0cbf5
No known key found for this signature in database
GPG Key ID: F3F92DA383FDDE09
6 changed files with 82 additions and 17 deletions

View File

@ -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 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 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 affected. There can be deleted lines below the last line of the buffer. When
lines. When there are deleted lines, this may not always be possible. 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 When [range] is used, Vim tries to only put or get the specified lines. When
is on the last line in the buffer and there is no diff above this line, the there are deleted lines, they will be used if they are between the lines
":diffget" and "do" commands will obtain lines from the other buffer. specified by [range].
To be able to get those lines from another buffer in a [range] it's allowed to To be able to put or get those lines to/from another buffer in a [range] it's
use the last line number plus one. This command gets all diffs from the other allowed to use 0 and the last line number plus one. This command gets all
buffer: > diffs from the other buffer: >
:1,$+1diffget :0,$+1diffget
Note that deleted lines are displayed, but not counted as text lines. You 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 can't move the cursor into them. To fill the deleted lines with the lines

View File

@ -3874,10 +3874,13 @@ ex_diffgetput(exarg_T *eap)
{ {
// Make it possible that ":diffget" on the last line gets line below // Make it possible that ":diffget" on the last line gets line below
// the cursor line when there is no difference above the cursor. // the cursor line when there is no difference above the cursor.
if (eap->cmdidx == CMD_diffget int linestatus = 0;
&& eap->line1 == curbuf->b_ml.ml_line_count if (eap->line1 == curbuf->b_ml.ml_line_count
&& diff_check(curwin, eap->line1) == 0 && (diff_check_with_linestatus(curwin, eap->line1, &linestatus) == 0
&& (eap->line1 == 1 || diff_check(curwin, eap->line1 - 1) == 0)) && linestatus == 0)
&& (eap->line1 == 1 ||
(diff_check_with_linestatus(curwin, eap->line1 - 1, &linestatus) >= 0
&& linestatus == 0)))
++eap->line2; ++eap->line2;
else if (eap->line1 > 0) else if (eap->line1 > 0)
--eap->line1; --eap->line1;

View File

@ -486,7 +486,7 @@ EXCMD(CMD_diffupdate, "diffupdate", ex_diffupdate,
EX_BANG|EX_TRLBAR, EX_BANG|EX_TRLBAR,
ADDR_NONE), ADDR_NONE),
EXCMD(CMD_diffget, "diffget", ex_diffgetput, 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), ADDR_LINES),
EXCMD(CMD_diffoff, "diffoff", ex_diffoff, EXCMD(CMD_diffoff, "diffoff", ex_diffoff,
EX_BANG|EX_TRLBAR, EX_BANG|EX_TRLBAR,
@ -495,7 +495,7 @@ EXCMD(CMD_diffpatch, "diffpatch", ex_diffpatch,
EX_EXTRA|EX_FILE1|EX_TRLBAR|EX_MODIFY, EX_EXTRA|EX_FILE1|EX_TRLBAR|EX_MODIFY,
ADDR_NONE), ADDR_NONE),
EXCMD(CMD_diffput, "diffput", ex_diffgetput, EXCMD(CMD_diffput, "diffput", ex_diffgetput,
EX_RANGE|EX_EXTRA|EX_TRLBAR, EX_RANGE|EX_ZEROR|EX_EXTRA|EX_TRLBAR,
ADDR_LINES), ADDR_LINES),
EXCMD(CMD_diffsplit, "diffsplit", ex_diffsplit, EXCMD(CMD_diffsplit, "diffsplit", ex_diffsplit,
EX_EXTRA|EX_FILE1|EX_TRLBAR, EX_EXTRA|EX_FILE1|EX_TRLBAR,

View File

@ -4850,7 +4850,8 @@ invalid_range(exarg_T *eap)
case ADDR_LINES: case ADDR_LINES:
if (eap->line2 > curbuf->b_ml.ml_line_count if (eap->line2 > curbuf->b_ml.ml_line_count
#ifdef FEAT_DIFF #ifdef FEAT_DIFF
+ (eap->cmdidx == CMD_diffget) + (eap->cmdidx == CMD_diffget ||
eap->cmdidx == CMD_diffput)
#endif #endif
) )
return _(e_invalid_range); return _(e_invalid_range);

View File

@ -288,6 +288,63 @@ func Test_diffget_diffput_range()
%bw! %bw!
endfunc 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 " Test for :diffget/:diffput with an empty buffer and a non-empty buffer
func Test_diffget_diffput_empty_buffer() func Test_diffget_diffput_empty_buffer()
%d _ %d _

View File

@ -709,6 +709,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
1473,
/**/ /**/
1472, 1472,
/**/ /**/