patch 9.1.1557: not possible to anchor specific lines in difff mode
Problem: not possible to anchor specific lines in difff mode
Solution: Add support for the anchoring lines in diff mode using the
'diffanchor' option (Yee Cheng Chin).
Adds support for anchoring specific lines to each other while viewing a
diff. While lines are anchored, they are guaranteed to be aligned to
each other in a diff view, allowing the user to control and inform the
diff algorithm what the desired alignment is. Internally, this is done
by splitting up the buffer at each anchor and run the diff algorithm on
each split section separately, and then merge the results back for a
logically consistent diff result.
To do this, add a new "diffanchors" option that takes a list of
`{address}`, and a new "diffopt" option value "anchor". Each address
specified will be an anchor, and the user can choose to use any type of
address, including marks, line numbers, or pattern search. Anchors are
sorted by line number in each file, and it's possible to have multiple
anchors on the same line (this is useful when doing multi-buffer diff).
Update documentation to provide examples.
This is similar to Git diff's `--anchored` flag. Other diff tools like
Meld/Araxis Merge also have similar features (called "synchronization
points" or "synchronization links"). We are not using Git/Xdiff's
`--anchored` implementation here because it has a very limited API
(it requires usage of the Patience algorithm, and can only anchor
unique lines that are the same across both files).
Because the user could anchor anywhere, diff anchors could result in
adjacent diff blocks (one block is directly touching another without a
gap), if there is a change right above the anchor point. We don't want
to merge these diff blocks because we want to line up the change at the
anchor. Adjacent diff blocks were first allowed when linematch was
added, but the existing code had a lot of branched paths where
line-matched diff blocks were handled differently. As a part of this
change, refactor them to have a more unified code path that is
generalized enough to handle adjacent diff blocks correctly and without
needing to carve in exceptions all over the place.
closes: #17615
Signed-off-by: Yee Cheng Chin <ychin.git@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
committed by
Christian Brabandt
parent
393d398247
commit
0d9160e11c
@ -1,4 +1,4 @@
|
||||
*diff.txt* For Vim version 9.1. Last change: 2025 Jun 20
|
||||
*diff.txt* For Vim version 9.1. Last change: 2025 Jul 26
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||
@ -14,7 +14,8 @@ The basics are explained in section |08.7| of the user manual.
|
||||
2. Viewing diffs |view-diffs|
|
||||
3. Jumping to diffs |jumpto-diffs|
|
||||
4. Copying diffs |copy-diffs|
|
||||
5. Diff options |diff-options|
|
||||
5. Diff anchors |diff-anchors|
|
||||
6. Diff options |diff-options|
|
||||
|
||||
==============================================================================
|
||||
1. Starting diff mode *start-vimdiff*
|
||||
@ -336,7 +337,129 @@ name or a part of a buffer name. Examples:
|
||||
diff mode (e.g., "file.c.v2")
|
||||
|
||||
==============================================================================
|
||||
5. Diff options *diff-options*
|
||||
5. Diff anchors *diff-anchors*
|
||||
|
||||
Diff anchors allow you to control where the diff algorithm aligns and
|
||||
synchronize text across files. Each anchor matches each other in each file,
|
||||
allowing you to control the output of a diff.
|
||||
|
||||
This is useful when a change involves complicated edits. For example, if a
|
||||
function was moved to another location and further edited. By default, the
|
||||
algorithm aims to create the smallest diff, which results in that entire
|
||||
function being considered to be deleted and added on the other side, making it
|
||||
hard to see what the actual edit on it was. You can use diff anchors to pin
|
||||
that function so the diff algorithm will align based on it.
|
||||
|
||||
To use it, set anchors using 'diffanchors' which is a comma-separated list of
|
||||
{address} in each file, and then add "anchor" to 'diffopt'. Internaly, Vim
|
||||
splits each file up into sections split by the anchors. It performs the diff
|
||||
on each pair of sections separately before merging the results back.
|
||||
|
||||
Setting 'diffanchors' will update the diff immediately. If an anchor is tied
|
||||
to a mark, and you change what the mark is pointed to, you need to manually
|
||||
call |:diffupdate| afterwards to get the updated diff results.
|
||||
|
||||
Example:
|
||||
|
||||
Let's say we have the following files, side-by-side. We are interested in the
|
||||
change that happened to the function `foo()`, which was both edited and moved.
|
||||
|
||||
File A: >
|
||||
int foo() {
|
||||
int n = 1;
|
||||
return n;
|
||||
}
|
||||
|
||||
int g = 1;
|
||||
|
||||
int bar(int a) {
|
||||
a *= 2;
|
||||
a += 3;
|
||||
return a;
|
||||
}
|
||||
<File B: >
|
||||
int bar(int a) {
|
||||
a *= 2;
|
||||
a += 3;
|
||||
return a;
|
||||
}
|
||||
|
||||
int foo() {
|
||||
int n = 999;
|
||||
return n;
|
||||
}
|
||||
|
||||
int g = 1;
|
||||
<
|
||||
A normal diff will usually align the diff result as such: >
|
||||
|
||||
int foo() { |----------------
|
||||
int n = 1; |----------------
|
||||
return n; |----------------
|
||||
} |----------------
|
||||
|----------------
|
||||
int g = 1; |----------------
|
||||
|----------------
|
||||
int bar(int a) {|int bar(int a) {
|
||||
a *= 2; | a *= 2;
|
||||
a += 3; | a += 3;
|
||||
return a; | return a;
|
||||
} |}
|
||||
----------------|
|
||||
----------------|int foo() {
|
||||
----------------| int n = 999;
|
||||
----------------| return n;
|
||||
----------------|}
|
||||
----------------|
|
||||
----------------|int g = 1;
|
||||
<
|
||||
What we want is to instead ask the diff to align on `foo()`: >
|
||||
|
||||
----------------|int bar(int a) {
|
||||
----------------| a *= 2;
|
||||
----------------| a += 3;
|
||||
----------------| return a;
|
||||
----------------|}
|
||||
----------------|
|
||||
int foo() { |int foo() {
|
||||
int n = 1; | int n = 999;
|
||||
return n; | return n;
|
||||
} |}
|
||||
|
|
||||
int g = 1; |int g = 1;
|
||||
|----------------
|
||||
int bar(int a) {|----------------
|
||||
a *= 2; |----------------
|
||||
a += 3; |----------------
|
||||
return a; |----------------
|
||||
} |----------------
|
||||
<
|
||||
|
||||
Below are some ways of setting diff anchors to get the above result. In each
|
||||
example, 'diffopt' needs to have `anchor` set for this to take effect.
|
||||
|
||||
Marks: Set the |'a| mark on the `int foo()` lines in each file first before
|
||||
setting the anchors: >
|
||||
set diffanchors='a
|
||||
|
||||
Pattern: Specify the anchor using a |pattern| (see |:/|). Here, we make sure
|
||||
to always start search from line 1 for consistency: >
|
||||
set diffanchors=1/int\ foo(/
|
||||
<
|
||||
Selection: Use visual mode to select the entire `foo()` function body in each
|
||||
file. Here, we use two anchors. This does a better job of making sure only
|
||||
the function bodies are anchored against each other but not the lines after
|
||||
it. Note the `'>+1` below. The "+1" is necessary as we want the split to
|
||||
happen below the last line of the function, not above: >
|
||||
set diffanchors='<,'>+1
|
||||
<
|
||||
Manually set two anchors using line numbers via buffer-local options: >
|
||||
setlocal diffanchors=1,5
|
||||
wincmd w
|
||||
setlocal diffanchors=7,11
|
||||
<
|
||||
==============================================================================
|
||||
6. Diff options *diff-options*
|
||||
|
||||
Also see |'diffopt'| and the "diff" item of |'fillchars'|.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user