mirror of
https://github.com/neovim/neovim.git
synced 2025-09-05 19:08:15 +00:00
vim-patch:9.1.1557: not possible to anchor specific lines in diff mode (#34967)
Problem: not possible to anchor specific lines in diff 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: vim/vim#17615
0d9160e11c
Co-authored-by: Yee Cheng Chin <ychin.git@gmail.com>
This commit is contained in:
@@ -324,7 +324,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'|.
|
||||
|
||||
|
@@ -234,6 +234,7 @@ OPTIONS
|
||||
• "o" complete using 'omnifunc'
|
||||
• 'complete' allows limiting matches for sources using "{flag}^<limit>".
|
||||
• 'completeopt' flag "nearset" sorts completion results by distance to cursor.
|
||||
• 'diffanchors' specifies addresses to anchor a diff.
|
||||
• 'diffopt' `inline:` configures diff highlighting for changes within a line.
|
||||
• 'grepformat' is now a |global-local| option.
|
||||
• 'maxsearchcount' sets maximum value for |searchcount()| and defaults to 999.
|
||||
|
@@ -2107,6 +2107,27 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
Join the current window in the group of windows that shows differences
|
||||
between files. See |diff-mode|.
|
||||
|
||||
*'diffanchors'* *'dia'*
|
||||
'diffanchors' 'dia' string (default "")
|
||||
global or local to buffer |global-local|
|
||||
List of {address} in each buffer, separated by commas, that are
|
||||
considered anchors when used for diffing. It's valid to specify "$+1"
|
||||
for 1 past the last line. "%" cannot be used for this option. There
|
||||
can be at most 20 anchors set for each buffer.
|
||||
|
||||
Each anchor line splits the buffer (the split happens above the
|
||||
anchor), with each part being diff'ed separately before the final
|
||||
result is joined. When more than one {address} are provided, the
|
||||
anchors will be sorted interally by line number. If using buffer
|
||||
local options, each buffer should have the same number of anchors
|
||||
(extra anchors will be ignored). This option is only used when
|
||||
'diffopt' has "anchor" set. See |diff-anchors| for more details and
|
||||
examples.
|
||||
*E1550*
|
||||
If some of the {address} do not resolve to a line in each buffer (e.g.
|
||||
a pattern search that does not match anything), none of the anchors
|
||||
will be used.
|
||||
|
||||
*'diffexpr'* *'dex'*
|
||||
'diffexpr' 'dex' string (default "")
|
||||
global
|
||||
@@ -2130,6 +2151,10 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
patience patience diff algorithm
|
||||
histogram histogram diff algorithm
|
||||
|
||||
anchor Anchor specific lines in each buffer to be
|
||||
aligned with each other if 'diffanchors' is
|
||||
set. See |diff-anchors|.
|
||||
|
||||
closeoff When a window is closed where 'diff' is set
|
||||
and there is only one window remaining in the
|
||||
same tab page with 'diff' set, execute
|
||||
@@ -2232,6 +2257,7 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
"linematch:60", as this will enable alignment
|
||||
for a 2 buffer diff hunk of 30 lines each,
|
||||
or a 3 buffer diff hunk of 20 lines each.
|
||||
Implicitly sets "filler" when this is set.
|
||||
|
||||
vertical Start diff mode with vertical splits (unless
|
||||
explicitly specified otherwise).
|
||||
|
@@ -682,6 +682,7 @@ Short explanation of each option: *option-list*
|
||||
'delcombine' 'deco' delete combining characters on their own
|
||||
'dictionary' 'dict' list of file names used for keyword completion
|
||||
'diff' use diff mode for the current window
|
||||
'diffanchors' 'dia' list of {address} to force anchoring of a diff
|
||||
'diffexpr' 'dex' expression used to obtain a diff file
|
||||
'diffopt' 'dip' options for using diff mode
|
||||
'digraph' 'dg' enable the entering of digraphs in Insert mode
|
||||
|
31
runtime/lua/vim/_meta/options.lua
generated
31
runtime/lua/vim/_meta/options.lua
generated
@@ -1708,6 +1708,32 @@ vim.go.dict = vim.go.dictionary
|
||||
vim.o.diff = false
|
||||
vim.wo.diff = vim.o.diff
|
||||
|
||||
--- List of {address} in each buffer, separated by commas, that are
|
||||
--- considered anchors when used for diffing. It's valid to specify "$+1"
|
||||
--- for 1 past the last line. "%" cannot be used for this option. There
|
||||
--- can be at most 20 anchors set for each buffer.
|
||||
---
|
||||
--- Each anchor line splits the buffer (the split happens above the
|
||||
--- anchor), with each part being diff'ed separately before the final
|
||||
--- result is joined. When more than one {address} are provided, the
|
||||
--- anchors will be sorted interally by line number. If using buffer
|
||||
--- local options, each buffer should have the same number of anchors
|
||||
--- (extra anchors will be ignored). This option is only used when
|
||||
--- 'diffopt' has "anchor" set. See `diff-anchors` for more details and
|
||||
--- examples.
|
||||
--- *E1550*
|
||||
--- If some of the {address} do not resolve to a line in each buffer (e.g.
|
||||
--- a pattern search that does not match anything), none of the anchors
|
||||
--- will be used.
|
||||
---
|
||||
--- @type string
|
||||
vim.o.diffanchors = ""
|
||||
vim.o.dia = vim.o.diffanchors
|
||||
vim.bo.diffanchors = vim.o.diffanchors
|
||||
vim.bo.dia = vim.bo.diffanchors
|
||||
vim.go.diffanchors = vim.o.diffanchors
|
||||
vim.go.dia = vim.go.diffanchors
|
||||
|
||||
--- Expression which is evaluated to obtain a diff file (either ed-style
|
||||
--- or unified-style) from two versions of a file. See `diff-diffexpr`.
|
||||
--- This option cannot be set from a `modeline` or in the `sandbox`, for
|
||||
@@ -1731,6 +1757,10 @@ vim.go.dex = vim.go.diffexpr
|
||||
--- patience patience diff algorithm
|
||||
--- histogram histogram diff algorithm
|
||||
---
|
||||
--- anchor Anchor specific lines in each buffer to be
|
||||
--- aligned with each other if 'diffanchors' is
|
||||
--- set. See `diff-anchors`.
|
||||
---
|
||||
--- closeoff When a window is closed where 'diff' is set
|
||||
--- and there is only one window remaining in the
|
||||
--- same tab page with 'diff' set, execute
|
||||
@@ -1833,6 +1863,7 @@ vim.go.dex = vim.go.diffexpr
|
||||
--- "linematch:60", as this will enable alignment
|
||||
--- for a 2 buffer diff hunk of 30 lines each,
|
||||
--- or a 3 buffer diff hunk of 20 lines each.
|
||||
--- Implicitly sets "filler" when this is set.
|
||||
---
|
||||
--- vertical Start diff mode with vertical splits (unless
|
||||
--- explicitly specified otherwise).
|
||||
|
@@ -1,7 +1,7 @@
|
||||
" These commands create the option window.
|
||||
"
|
||||
" Maintainer: The Vim Project <https://github.com/vim/vim>
|
||||
" Last Change: 2025 Jul 10
|
||||
" Last Change: 2025 Jul 16
|
||||
" Former Maintainer: Bram Moolenaar <Bram@vim.org>
|
||||
|
||||
" If there already is an option window, jump to that one.
|
||||
@@ -913,6 +913,9 @@ if has("diff")
|
||||
call <SID>OptionG("dip", &dip)
|
||||
call <SID>AddOption("diffexpr", gettext("expression used to obtain a diff file"))
|
||||
call <SID>OptionG("dex", &dex)
|
||||
call <SID>AddOption("diffanchors", gettext("list of addresses for anchoring a diff"))
|
||||
call append("$", "\t" .. s:global_or_local)
|
||||
call <SID>OptionG("dia", &dia)
|
||||
call <SID>AddOption("patchexpr", gettext("expression used to patch a file"))
|
||||
call <SID>OptionG("pex", &pex)
|
||||
endif
|
||||
|
@@ -2113,6 +2113,7 @@ void free_buf_options(buf_T *buf, bool free_p_ff)
|
||||
clear_string_option(&buf->b_p_ffu);
|
||||
callback_free(&buf->b_ffu_cb);
|
||||
clear_string_option(&buf->b_p_dict);
|
||||
clear_string_option(&buf->b_p_dia);
|
||||
clear_string_option(&buf->b_p_tsr);
|
||||
clear_string_option(&buf->b_p_qe);
|
||||
buf->b_p_ar = -1;
|
||||
|
@@ -619,6 +619,7 @@ struct file_buffer {
|
||||
char *b_p_tc; ///< 'tagcase' local value
|
||||
unsigned b_tc_flags; ///< flags for 'tagcase'
|
||||
char *b_p_dict; ///< 'dictionary' local value
|
||||
char *b_p_dia; ///< 'diffanchors' local value
|
||||
char *b_p_tsr; ///< 'thesaurus' local value
|
||||
char *b_p_tsrfu; ///< 'thesaurusfunc' local value
|
||||
Callback b_tsrfu_cb; ///< 'thesaurusfunc' callback
|
||||
@@ -759,9 +760,11 @@ struct file_buffer {
|
||||
/// and how many lines it occupies in that buffer. When the lines are missing
|
||||
/// in the buffer the df_count[] is zero. This is all counted in
|
||||
/// buffer lines.
|
||||
/// There is always at least one unchanged line in between the diffs (unless
|
||||
/// linematch is used). Otherwise it would have been included in the diff above
|
||||
/// or below it.
|
||||
/// Usually there is always at least one unchanged line in between the diffs as
|
||||
/// otherwise it would have been included in the diff above or below it. When
|
||||
/// linematch or diff anchors are used, this is no longer guaranteed, and we may
|
||||
/// have adjacent diff blocks. In all cases they will not overlap, although it
|
||||
/// is possible to have multiple 0-count diff blocks at the same line.
|
||||
/// df_lnum[] + df_count[] is the lnum below the change. When in one buffer
|
||||
/// lines have been inserted, in the other buffer df_lnum[] is the line below
|
||||
/// the insertion and df_count[] is zero. When appending lines at the end of
|
||||
|
589
src/nvim/diff.c
589
src/nvim/diff.c
@@ -91,6 +91,7 @@ static bool diff_need_update = false; // ex_diffupdate needs to be called
|
||||
#define DIFF_INLINE_SIMPLE 0x4000 // inline highlight with simple algorithm
|
||||
#define DIFF_INLINE_CHAR 0x8000 // inline highlight with character diff
|
||||
#define DIFF_INLINE_WORD 0x10000 // inline highlight with word diff
|
||||
#define DIFF_ANCHOR 0x20000 // use 'diffanchors' to anchor the diff
|
||||
#define ALL_WHITE_DIFF (DIFF_IWHITE | DIFF_IWHITEALL | DIFF_IWHITEEOL)
|
||||
#define ALL_INLINE (DIFF_INLINE_NONE | DIFF_INLINE_SIMPLE | DIFF_INLINE_CHAR | DIFF_INLINE_WORD)
|
||||
#define ALL_INLINE_DIFF (DIFF_INLINE_CHAR | DIFF_INLINE_WORD)
|
||||
@@ -105,6 +106,8 @@ static int linematch_lines = 0;
|
||||
// kNone when not checked yet
|
||||
static TriState diff_a_works = kNone;
|
||||
|
||||
enum { MAX_DIFF_ANCHORS = 20, };
|
||||
|
||||
// used for diff input
|
||||
typedef struct {
|
||||
char *din_fname; // used for external diff
|
||||
@@ -388,9 +391,22 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T
|
||||
|
||||
// 1. change completely above line1: nothing to do
|
||||
if (last >= line1 - 1) {
|
||||
if (diff_busy) {
|
||||
// Currently in the middle of updating diff blocks. All we want
|
||||
// is to adjust the line numbers and nothing else.
|
||||
if (dp->df_lnum[idx] > line2) {
|
||||
dp->df_lnum[idx] += amount_after;
|
||||
}
|
||||
|
||||
// Advance to next entry.
|
||||
dprev = dp;
|
||||
dp = dp->df_next;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 6. change below line2: only adjust for amount_after; also when
|
||||
// "deleted" became zero when deleted all lines between two diffs.
|
||||
if (dp->df_lnum[idx] - (deleted + inserted != 0) > line2 - dp->is_linematched) {
|
||||
if (dp->df_lnum[idx] - (deleted + inserted != 0) > line2) {
|
||||
if (amount_after == 0) {
|
||||
// nothing left to change
|
||||
break;
|
||||
@@ -482,7 +498,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T
|
||||
}
|
||||
|
||||
// check if this block touches the previous one, may merge them.
|
||||
if ((dprev != NULL) && !dp->is_linematched
|
||||
if ((dprev != NULL) && !dp->is_linematched && !diff_busy
|
||||
&& (dprev->df_lnum[idx] + dprev->df_count[idx] == dp->df_lnum[idx])) {
|
||||
for (int i = 0; i < DB_COUNT; i++) {
|
||||
if (tp->tp_diffbuf[i] != NULL) {
|
||||
@@ -700,7 +716,7 @@ void diff_redraw(bool dofold)
|
||||
|
||||
// A change may have made filler lines invalid, need to take care of
|
||||
// that for other windows.
|
||||
int n = diff_check(wp, wp->w_topline);
|
||||
int n = diff_check_fill(wp, wp->w_topline);
|
||||
|
||||
if (((wp != curwin) && (wp->w_topfill > 0)) || (n > 0)) {
|
||||
if (wp->w_topfill > n) {
|
||||
@@ -756,7 +772,11 @@ static void clear_diffout(diffout_T *dout)
|
||||
/// @return FAIL for failure.
|
||||
static int diff_write_buffer(buf_T *buf, mmfile_t *m, linenr_T start, linenr_T end)
|
||||
{
|
||||
if (buf->b_ml.ml_flags & ML_EMPTY) {
|
||||
if (end < 0) {
|
||||
end = buf->b_ml.ml_line_count;
|
||||
}
|
||||
|
||||
if (buf->b_ml.ml_flags & ML_EMPTY || end < start) {
|
||||
m->ptr = NULL;
|
||||
m->size = 0;
|
||||
return OK;
|
||||
@@ -764,10 +784,6 @@ static int diff_write_buffer(buf_T *buf, mmfile_t *m, linenr_T start, linenr_T e
|
||||
|
||||
size_t len = 0;
|
||||
|
||||
if (end < 0) {
|
||||
end = buf->b_ml.ml_line_count;
|
||||
}
|
||||
|
||||
// xdiff requires one big block of memory with all the text.
|
||||
for (linenr_T lnum = start; lnum <= end; lnum++) {
|
||||
len += (size_t)ml_get_buf_len(buf, lnum) + 1;
|
||||
@@ -830,28 +846,52 @@ static int diff_write_buffer(buf_T *buf, mmfile_t *m, linenr_T start, linenr_T e
|
||||
/// @param din
|
||||
///
|
||||
/// @return FAIL for failure
|
||||
static int diff_write(buf_T *buf, diffin_T *din)
|
||||
static int diff_write(buf_T *buf, diffin_T *din, linenr_T start, linenr_T end)
|
||||
{
|
||||
if (din->din_fname == NULL) {
|
||||
return diff_write_buffer(buf, &din->din_mmfile, 1, -1);
|
||||
return diff_write_buffer(buf, &din->din_mmfile, start, end);
|
||||
}
|
||||
|
||||
if (end < 0) {
|
||||
end = buf->b_ml.ml_line_count;
|
||||
}
|
||||
|
||||
// Always use 'fileformat' set to "unix".
|
||||
int save_ml_flags = buf->b_ml.ml_flags;
|
||||
char *save_ff = buf->b_p_ff;
|
||||
buf->b_p_ff = xstrdup("unix");
|
||||
const bool save_cmod_flags = cmdmod.cmod_flags;
|
||||
// Writing the buffer is an implementation detail of performing the diff,
|
||||
// so it shouldn't update the '[ and '] marks.
|
||||
cmdmod.cmod_flags |= CMOD_LOCKMARKS;
|
||||
if (end < start) {
|
||||
// The line range specifies a completely empty file.
|
||||
end = start;
|
||||
buf->b_ml.ml_flags |= ML_EMPTY;
|
||||
}
|
||||
int r = buf_write(buf, din->din_fname, NULL,
|
||||
1, buf->b_ml.ml_line_count,
|
||||
start, end,
|
||||
NULL, false, false, false, true);
|
||||
cmdmod.cmod_flags = save_cmod_flags;
|
||||
free_string_option(buf->b_p_ff);
|
||||
buf->b_p_ff = save_ff;
|
||||
buf->b_ml.ml_flags = save_ml_flags;
|
||||
return r;
|
||||
}
|
||||
|
||||
static int lnum_compare(const void *s1, const void *s2)
|
||||
{
|
||||
linenr_T lnum1 = *(linenr_T *)s1;
|
||||
linenr_T lnum2 = *(linenr_T *)s2;
|
||||
if (lnum1 < lnum2) {
|
||||
return -1;
|
||||
}
|
||||
if (lnum1 > lnum2) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Update the diffs for all buffers involved.
|
||||
///
|
||||
/// @param dio
|
||||
@@ -887,37 +927,110 @@ static void diff_try_update(diffio_T *dio, int idx_orig, exarg_T *eap)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Parse and sort diff anchors if enabled
|
||||
int num_anchors = INT_MAX;
|
||||
linenr_T anchors[DB_COUNT][MAX_DIFF_ANCHORS];
|
||||
CLEAR_FIELD(anchors);
|
||||
if (diff_flags & DIFF_ANCHOR) {
|
||||
for (int idx = 0; idx < DB_COUNT; idx++) {
|
||||
if (curtab->tp_diffbuf[idx] == NULL) {
|
||||
continue;
|
||||
}
|
||||
int buf_num_anchors = 0;
|
||||
if (parse_diffanchors(false,
|
||||
curtab->tp_diffbuf[idx],
|
||||
anchors[idx],
|
||||
&buf_num_anchors) != OK) {
|
||||
emsg(_(e_failed_to_find_all_diff_anchors));
|
||||
num_anchors = 0;
|
||||
CLEAR_FIELD(anchors);
|
||||
break;
|
||||
}
|
||||
if (buf_num_anchors < num_anchors) {
|
||||
num_anchors = buf_num_anchors;
|
||||
}
|
||||
|
||||
if (buf_num_anchors > 0) {
|
||||
qsort((void *)anchors[idx],
|
||||
(size_t)buf_num_anchors,
|
||||
sizeof(linenr_T),
|
||||
lnum_compare);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (num_anchors == INT_MAX) {
|
||||
num_anchors = 0;
|
||||
}
|
||||
|
||||
// Split the files into multiple sections by anchors. Each section starts
|
||||
// from one anchor (inclusive) and ends at the next anchor (exclusive).
|
||||
// Diff each section separately before combining the results. If we don't
|
||||
// have any anchors, we will have one big section of the entire file.
|
||||
for (int anchor_i = 0; anchor_i <= num_anchors; anchor_i++) {
|
||||
diff_T *orig_diff = NULL;
|
||||
if (anchor_i != 0) {
|
||||
orig_diff = curtab->tp_first_diff;
|
||||
curtab->tp_first_diff = NULL;
|
||||
}
|
||||
linenr_T lnum_start = (anchor_i == 0) ? 1 : anchors[idx_orig][anchor_i - 1];
|
||||
linenr_T lnum_end = (anchor_i == num_anchors) ? -1 : anchors[idx_orig][anchor_i] - 1;
|
||||
|
||||
// Write the first buffer to a tempfile or mmfile_t.
|
||||
buf_T *buf = curtab->tp_diffbuf[idx_orig];
|
||||
if (diff_write(buf, &dio->dio_orig) == FAIL) {
|
||||
if (diff_write(buf, &dio->dio_orig, lnum_start, lnum_end) == FAIL) {
|
||||
if (orig_diff != NULL) {
|
||||
// Clean up in-progress diff blocks
|
||||
curtab->tp_first_diff = orig_diff;
|
||||
diff_clear(curtab);
|
||||
}
|
||||
goto theend;
|
||||
}
|
||||
|
||||
// Make a difference between the first buffer and every other.
|
||||
for (int idx_new = idx_orig + 1; idx_new < DB_COUNT; idx_new++) {
|
||||
buf = curtab->tp_diffbuf[idx_new];
|
||||
if (buf == NULL || buf->b_ml.ml_mfp == NULL) {
|
||||
continue; // skip buffer that isn't loaded
|
||||
}
|
||||
lnum_start = anchor_i == 0 ? 1 : anchors[idx_new][anchor_i - 1];
|
||||
lnum_end = anchor_i == num_anchors ? -1 : anchors[idx_new][anchor_i] - 1;
|
||||
|
||||
// Write the other buffer and diff with the first one.
|
||||
if (diff_write(buf, &dio->dio_new, lnum_start, lnum_end) == FAIL) {
|
||||
continue;
|
||||
}
|
||||
if (diff_file(dio) == FAIL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Read the diff output and add each entry to the diff list.
|
||||
diff_read(idx_orig, idx_new, dio);
|
||||
|
||||
clear_diffin(&dio->dio_new);
|
||||
clear_diffout(&dio->dio_diff);
|
||||
}
|
||||
clear_diffin(&dio->dio_orig);
|
||||
|
||||
if (anchor_i != 0) {
|
||||
// Combine the new diff blocks with the existing ones
|
||||
for (diff_T *dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) {
|
||||
for (int idx = 0; idx < DB_COUNT; idx++) {
|
||||
if (anchors[idx][anchor_i - 1] > 0) {
|
||||
dp->df_lnum[idx] += anchors[idx][anchor_i - 1] - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (orig_diff != NULL) {
|
||||
diff_T *last_diff = orig_diff;
|
||||
while (last_diff->df_next != NULL) {
|
||||
last_diff = last_diff->df_next;
|
||||
}
|
||||
last_diff->df_next = curtab->tp_first_diff;
|
||||
curtab->tp_first_diff = orig_diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make a difference between the first buffer and every other.
|
||||
for (int idx_new = idx_orig + 1; idx_new < DB_COUNT; idx_new++) {
|
||||
buf_T *buf = curtab->tp_diffbuf[idx_new];
|
||||
if (buf == NULL || buf->b_ml.ml_mfp == NULL) {
|
||||
continue; // skip buffer that isn't loaded
|
||||
}
|
||||
|
||||
// Write the other buffer and diff with the first one.
|
||||
if (diff_write(buf, &dio->dio_new) == FAIL) {
|
||||
continue;
|
||||
}
|
||||
if (diff_file(dio) == FAIL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Read the diff output and add each entry to the diff list.
|
||||
diff_read(idx_orig, idx_new, dio);
|
||||
|
||||
clear_diffin(&dio->dio_new);
|
||||
clear_diffout(&dio->dio_diff);
|
||||
}
|
||||
clear_diffin(&dio->dio_orig);
|
||||
|
||||
theend:
|
||||
xfree(dio->dio_orig.din_fname);
|
||||
xfree(dio->dio_new.din_fname);
|
||||
@@ -1853,7 +1966,10 @@ static int get_max_diff_length(const diff_T *dp)
|
||||
return maxlength;
|
||||
}
|
||||
|
||||
static void find_top_diff_block(diff_T **thistopdiff, diff_T **nextblockblock, int fromidx,
|
||||
/// Find the first diff block that includes the specified line. Also find the
|
||||
/// next diff block that's not in the current chain of adjacent blocks that are
|
||||
/// all touching each other directly.
|
||||
static void find_top_diff_block(diff_T **thistopdiff, diff_T **next_adjacent_blocks, int fromidx,
|
||||
int topline)
|
||||
{
|
||||
diff_T *topdiff = NULL;
|
||||
@@ -1889,63 +2005,31 @@ static void find_top_diff_block(diff_T **thistopdiff, diff_T **nextblockblock, i
|
||||
// if we already have found that the line number is inside a diff block,
|
||||
// set the marker of the next block and finish the iteration
|
||||
if (*thistopdiff) {
|
||||
(*nextblockblock) = topdiff->df_next;
|
||||
(*next_adjacent_blocks) = topdiff->df_next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void count_filler_lines_and_topline(int *curlinenum_to, int *linesfiller,
|
||||
const diff_T *thistopdiff, const int toidx,
|
||||
int virtual_lines_passed)
|
||||
{
|
||||
const diff_T *curdif = thistopdiff;
|
||||
int ch_virtual_lines = 0;
|
||||
bool isfiller = false;
|
||||
while (virtual_lines_passed > 0) {
|
||||
if (ch_virtual_lines) {
|
||||
virtual_lines_passed--;
|
||||
ch_virtual_lines--;
|
||||
if (!isfiller) {
|
||||
(*curlinenum_to)++;
|
||||
} else {
|
||||
(*linesfiller)++;
|
||||
}
|
||||
} else {
|
||||
(*linesfiller) = 0;
|
||||
if (curdif) {
|
||||
ch_virtual_lines = get_max_diff_length(curdif);
|
||||
isfiller = (curdif->df_count[toidx] ? false : true);
|
||||
}
|
||||
if (isfiller) {
|
||||
while (curdif && curdif->df_next && curdif->df_lnum[toidx] ==
|
||||
curdif->df_next->df_lnum[toidx]
|
||||
&& curdif->df_next->df_count[toidx] == 0) {
|
||||
curdif = curdif->df_next;
|
||||
ch_virtual_lines += get_max_diff_length(curdif);
|
||||
}
|
||||
}
|
||||
if (curdif) {
|
||||
curdif = curdif->df_next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculates topline/topfill of a target diff window to fit the source diff window.
|
||||
static void calculate_topfill_and_topline(const int fromidx, const int toidx, const
|
||||
int from_topline, const int from_topfill, int *topfill,
|
||||
linenr_T *topline)
|
||||
{
|
||||
// 1. find the position from the top of the diff block, and the start
|
||||
// of the next diff block
|
||||
// find the position from the top of the diff block, and the next diff
|
||||
// block that's no longer adjacent to the current block. "Adjacency" means
|
||||
// a chain of diff blocks that are directly touching each other, allowed by
|
||||
// linematch and diff anchors.
|
||||
diff_T *thistopdiff = NULL;
|
||||
diff_T *nextblockblock = NULL;
|
||||
diff_T *next_adjacent_blocks = NULL;
|
||||
int virtual_lines_passed = 0;
|
||||
|
||||
find_top_diff_block(&thistopdiff, &nextblockblock, fromidx, from_topline);
|
||||
find_top_diff_block(&thistopdiff, &next_adjacent_blocks, fromidx, from_topline);
|
||||
|
||||
// count the virtual lines that have been passed
|
||||
// count the virtual lines (either filler or concrete line) that have been
|
||||
// passed in the source buffer. There could be multiple diff blocks if
|
||||
// there are adjacent empty blocks (count == 0 at fromidx).
|
||||
|
||||
diff_T *curdif = thistopdiff;
|
||||
while (curdif && (curdif->df_lnum[fromidx] + curdif->df_count[fromidx])
|
||||
@@ -1955,66 +2039,51 @@ static void calculate_topfill_and_topline(const int fromidx, const int toidx, co
|
||||
curdif = curdif->df_next;
|
||||
}
|
||||
|
||||
if (curdif != nextblockblock) {
|
||||
if (curdif != next_adjacent_blocks) {
|
||||
virtual_lines_passed += from_topline - curdif->df_lnum[fromidx];
|
||||
}
|
||||
virtual_lines_passed -= from_topfill;
|
||||
|
||||
// count the same amount of virtual lines in the toidx buffer
|
||||
int curlinenum_to = thistopdiff->df_lnum[toidx];
|
||||
int linesfiller = 0;
|
||||
count_filler_lines_and_topline(&curlinenum_to, &linesfiller,
|
||||
thistopdiff, toidx, virtual_lines_passed);
|
||||
// clamp negative values in case from_topfill hasn't been updated yet and
|
||||
// is larger than total virtual lines, which could happen when setting
|
||||
// diffopt multiple times
|
||||
if (virtual_lines_passed < 0) {
|
||||
virtual_lines_passed = 0;
|
||||
}
|
||||
|
||||
// count the number of filler lines that would normally be above this line
|
||||
int maxfiller = 0;
|
||||
for (diff_T *dpfillertest = thistopdiff; dpfillertest != NULL;
|
||||
dpfillertest = dpfillertest->df_next) {
|
||||
if (dpfillertest->df_lnum[toidx] == curlinenum_to) {
|
||||
while (dpfillertest && dpfillertest->df_lnum[toidx] == curlinenum_to) {
|
||||
maxfiller += dpfillertest->df_count[toidx] ? 0 : get_max_diff_length(dpfillertest);
|
||||
dpfillertest = dpfillertest->df_next;
|
||||
// move the same amount of virtual lines in the target buffer to find the
|
||||
// cursor's line number
|
||||
int curlinenum_to = thistopdiff->df_lnum[toidx];
|
||||
|
||||
int virt_lines_left = virtual_lines_passed;
|
||||
curdif = thistopdiff;
|
||||
while (virt_lines_left > 0 && curdif != NULL && curdif != next_adjacent_blocks) {
|
||||
curlinenum_to += MIN(virt_lines_left, curdif->df_count[toidx]);
|
||||
virt_lines_left -= MIN(virt_lines_left, get_max_diff_length(curdif));
|
||||
curdif = curdif->df_next;
|
||||
}
|
||||
|
||||
// count the total number of virtual lines between the top diff block and
|
||||
// the found line in the target buffer
|
||||
int max_virt_lines = 0;
|
||||
for (diff_T *dp = thistopdiff; dp != NULL; dp = dp->df_next) {
|
||||
if (dp->df_lnum[toidx] + dp->df_count[toidx] <= curlinenum_to) {
|
||||
max_virt_lines += get_max_diff_length(dp);
|
||||
} else {
|
||||
if (dp->df_lnum[toidx] <= curlinenum_to) {
|
||||
max_virt_lines += curlinenum_to - dp->df_lnum[toidx];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
(*topfill) = maxfiller - linesfiller;
|
||||
|
||||
if (diff_flags & DIFF_FILLER) {
|
||||
// should always be non-negative as max_virt_lines is larger
|
||||
(*topfill) = max_virt_lines - virtual_lines_passed;
|
||||
}
|
||||
(*topline) = curlinenum_to;
|
||||
}
|
||||
|
||||
static int linematched_filler_lines(diff_T *dp, int idx, linenr_T lnum, int *linestatus)
|
||||
{
|
||||
int filler_lines_d1 = 0;
|
||||
while (dp && dp->df_next
|
||||
&& lnum == (dp->df_lnum[idx] + dp->df_count[idx])
|
||||
&& dp->df_next->df_lnum[idx] == lnum) {
|
||||
if (dp->df_count[idx] == 0) {
|
||||
filler_lines_d1 += get_max_diff_length(dp);
|
||||
}
|
||||
dp = dp->df_next;
|
||||
}
|
||||
|
||||
if (dp->df_count[idx] == 0) {
|
||||
filler_lines_d1 += get_max_diff_length(dp);
|
||||
}
|
||||
|
||||
if (lnum < dp->df_lnum[idx] + dp->df_count[idx]) {
|
||||
int j = 0;
|
||||
for (int i = 0; i < DB_COUNT; i++) {
|
||||
if (curtab->tp_diffbuf[i] != NULL) {
|
||||
if (dp->df_count[i]) {
|
||||
j++;
|
||||
}
|
||||
}
|
||||
// is this an added line or a changed line?
|
||||
if (linestatus) {
|
||||
(*linestatus) = (j == 1) ? -2 : -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return filler_lines_d1;
|
||||
}
|
||||
|
||||
// Apply results from the linematch algorithm and apply to 'dp' by splitting it into multiple
|
||||
// adjacent diff blocks.
|
||||
static void apply_linematch_results(diff_T *dp, size_t decisions_length, const int *decisions)
|
||||
@@ -2110,18 +2179,19 @@ static void run_linematch_algorithm(diff_T *dp)
|
||||
|
||||
/// Check diff status for line "lnum" in buffer "buf":
|
||||
///
|
||||
/// Returns 0 for nothing special
|
||||
/// Returns -1 for a line that should be highlighted as changed.
|
||||
/// Returns -2 for a line that should be highlighted as added/deleted.
|
||||
/// Returns > 0 for inserting that many filler lines above it (never happens
|
||||
/// when 'diffopt' doesn't contain "filler").
|
||||
/// when 'diffopt' doesn't contain "filler"). Otherwise returns 0.
|
||||
///
|
||||
/// "linestatus" (can be NULL) will be set to:
|
||||
/// 0 for nothing special.
|
||||
/// -1 for a line that should be highlighted as changed.
|
||||
/// -2 for a line that should be highlighted as added/deleted.
|
||||
///
|
||||
/// This should only be used for windows where 'diff' is set.
|
||||
/// When diffopt contains linematch, a changed/added/deleted line
|
||||
/// may also have filler lines above it. In such a case, the possibilities
|
||||
/// are no longer mutually exclusive. The number of filler lines is
|
||||
/// returned from diff_check, and the integer 'linestatus' passed by
|
||||
/// pointer is set to -1 to indicate a changed line, and -2 to indicate an
|
||||
/// added line
|
||||
///
|
||||
/// Note that it's possible for a changed/added/deleted line to also have filler
|
||||
/// lines above it. This happens when using linematch or using diff anchors (at
|
||||
/// the anchored lines).
|
||||
///
|
||||
/// @param wp
|
||||
/// @param lnum
|
||||
@@ -2132,6 +2202,10 @@ int diff_check_with_linestatus(win_T *wp, linenr_T lnum, int *linestatus)
|
||||
{
|
||||
buf_T *buf = wp->w_buffer;
|
||||
|
||||
if (linestatus != NULL) {
|
||||
*linestatus = 0;
|
||||
}
|
||||
|
||||
if (curtab->tp_diff_invalid) {
|
||||
// update after a big change
|
||||
ex_diffupdate(NULL);
|
||||
@@ -2180,8 +2254,30 @@ int diff_check_with_linestatus(win_T *wp, linenr_T lnum, int *linestatus)
|
||||
run_linematch_algorithm(dp);
|
||||
}
|
||||
|
||||
if (dp->is_linematched) {
|
||||
return linematched_filler_lines(dp, idx, lnum, linestatus);
|
||||
// Insert filler lines above the line just below the change. Will return 0
|
||||
// when this buf had the max count.
|
||||
int num_fill = 0;
|
||||
while (lnum == dp->df_lnum[idx] + dp->df_count[idx]) {
|
||||
// Only calculate fill lines if 'diffopt' contains "filler". Otherwise
|
||||
// returns 0 filler lines.
|
||||
if (diff_flags & DIFF_FILLER) {
|
||||
int maxcount = get_max_diff_length(dp);
|
||||
num_fill += maxcount - dp->df_count[idx];
|
||||
}
|
||||
|
||||
// If there are adjacent blocks (e.g. linematch or anchor), loop
|
||||
// through them. It's possible for multiple adjacent blocks to
|
||||
// contribute to filler lines.
|
||||
// This also helps us find the last diff block in the list of adjacent
|
||||
// blocks which is necessary when it is a change/inserted line right
|
||||
// after added lines.
|
||||
if (dp->df_next != NULL
|
||||
&& lnum >= dp->df_next->df_lnum[idx]
|
||||
&& lnum <= dp->df_next->df_lnum[idx] + dp->df_next->df_count[idx]) {
|
||||
dp = dp->df_next;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (lnum < dp->df_lnum[idx] + dp->df_count[idx]) {
|
||||
@@ -2198,8 +2294,10 @@ int diff_check_with_linestatus(win_T *wp, linenr_T lnum, int *linestatus)
|
||||
zero = true;
|
||||
} else {
|
||||
if (dp->df_count[i] != dp->df_count[idx]) {
|
||||
// nr of lines changed.
|
||||
return -1;
|
||||
if (linestatus) {
|
||||
*linestatus = -1; // nr of lines changed.
|
||||
}
|
||||
return num_fill;
|
||||
}
|
||||
cmp = true;
|
||||
}
|
||||
@@ -2214,7 +2312,10 @@ int diff_check_with_linestatus(win_T *wp, linenr_T lnum, int *linestatus)
|
||||
&& (curtab->tp_diffbuf[i] != NULL)
|
||||
&& (dp->df_count[i] != 0)) {
|
||||
if (!diff_equal_entry(dp, idx, i)) {
|
||||
return -1;
|
||||
if (linestatus) {
|
||||
*linestatus = -1;
|
||||
}
|
||||
return num_fill;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2226,26 +2327,25 @@ int diff_check_with_linestatus(win_T *wp, linenr_T lnum, int *linestatus)
|
||||
// through updating the window. Just report the text as unchanged.
|
||||
// Other windows might still show the change though.
|
||||
if (!zero) {
|
||||
return 0;
|
||||
return num_fill;
|
||||
}
|
||||
return -2;
|
||||
if (linestatus) {
|
||||
*linestatus = -2;
|
||||
}
|
||||
return num_fill;
|
||||
}
|
||||
|
||||
// If 'diffopt' doesn't contain "filler", return 0.
|
||||
if (!(diff_flags & DIFF_FILLER)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Insert filler lines above the line just below the change. Will return
|
||||
// 0 when this buf had the max count.
|
||||
int maxcount = get_max_diff_length(dp);
|
||||
return maxcount - dp->df_count[idx];
|
||||
return num_fill;
|
||||
}
|
||||
|
||||
/// See diff_check_with_linestatus
|
||||
int diff_check(win_T *wp, linenr_T lnum)
|
||||
int diff_check_fill(win_T *wp, linenr_T lnum)
|
||||
{
|
||||
return diff_check_with_linestatus(wp, lnum, NULL);
|
||||
// be quick when there are no filler lines
|
||||
if (!(diff_flags & DIFF_FILLER)) {
|
||||
return 0;
|
||||
}
|
||||
int n = diff_check_with_linestatus(wp, lnum, NULL);
|
||||
return MAX(n, 0);
|
||||
}
|
||||
|
||||
/// Compare two entries in diff "dp" and return true if they are equal.
|
||||
@@ -2405,54 +2505,9 @@ void diff_set_topline(win_T *fromwin, win_T *towin)
|
||||
towin->w_topline = lnum + (dp->df_lnum[toidx] - dp->df_lnum[fromidx]);
|
||||
|
||||
if (lnum >= dp->df_lnum[fromidx]) {
|
||||
if (dp->is_linematched) {
|
||||
calculate_topfill_and_topline(fromidx, toidx, fromwin->w_topline,
|
||||
fromwin->w_topfill, &towin->w_topfill, &towin->w_topline);
|
||||
} else {
|
||||
// Inside a change: compute filler lines. With three or more
|
||||
// buffers we need to know the largest count.
|
||||
linenr_T max_count = 0;
|
||||
|
||||
for (int i = 0; i < DB_COUNT; i++) {
|
||||
if ((curtab->tp_diffbuf[i] != NULL) && (max_count < dp->df_count[i])) {
|
||||
max_count = dp->df_count[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (dp->df_count[toidx] == dp->df_count[fromidx]) {
|
||||
// same number of lines: use same filler count
|
||||
towin->w_topfill = fromwin->w_topfill;
|
||||
} else if (dp->df_count[toidx] > dp->df_count[fromidx]) {
|
||||
if (lnum == dp->df_lnum[fromidx] + dp->df_count[fromidx]) {
|
||||
// more lines in towin and fromwin doesn't show diff
|
||||
// lines, only filler lines
|
||||
if (max_count - fromwin->w_topfill >= dp->df_count[toidx]) {
|
||||
// towin also only shows filler lines
|
||||
towin->w_topline = dp->df_lnum[toidx] + dp->df_count[toidx];
|
||||
towin->w_topfill = fromwin->w_topfill;
|
||||
} else {
|
||||
// towin still has some diff lines to show
|
||||
towin->w_topline = dp->df_lnum[toidx]
|
||||
+ max_count - fromwin->w_topfill;
|
||||
}
|
||||
}
|
||||
} else if (towin->w_topline >= dp->df_lnum[toidx]
|
||||
+ dp->df_count[toidx]) {
|
||||
// less lines in towin and no diff lines to show: compute
|
||||
// filler lines
|
||||
towin->w_topline = dp->df_lnum[toidx] + dp->df_count[toidx];
|
||||
|
||||
if (diff_flags & DIFF_FILLER) {
|
||||
if (lnum == dp->df_lnum[fromidx] + dp->df_count[fromidx]) {
|
||||
// fromwin is also out of diff lines
|
||||
towin->w_topfill = fromwin->w_topfill;
|
||||
} else {
|
||||
// fromwin has some diff lines
|
||||
towin->w_topfill = dp->df_lnum[fromidx] + max_count - lnum;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
calculate_topfill_and_topline(fromidx, toidx,
|
||||
fromwin->w_topline, fromwin->w_topfill,
|
||||
&towin->w_topfill, &towin->w_topline);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2477,6 +2532,99 @@ void diff_set_topline(win_T *fromwin, win_T *towin)
|
||||
hasFolding(towin, towin->w_topline, &towin->w_topline, NULL);
|
||||
}
|
||||
|
||||
/// Parse the diff anchors. If "check_only" is set, will only make sure the
|
||||
/// syntax is correct.
|
||||
static int parse_diffanchors(bool check_only, buf_T *buf, linenr_T *anchors, int *num_anchors)
|
||||
{
|
||||
int i;
|
||||
char *dia = (*buf->b_p_dia == NUL) ? p_dia : buf->b_p_dia;
|
||||
|
||||
buf_T *orig_curbuf = curbuf;
|
||||
win_T *orig_curwin = curwin;
|
||||
|
||||
win_T *bufwin = NULL;
|
||||
if (check_only) {
|
||||
bufwin = curwin;
|
||||
} else {
|
||||
// Find the first window tied to this buffer and ignore the rest. Will
|
||||
// only matter for window-specific addresses like `.` or `''`.
|
||||
for (bufwin = firstwin; bufwin != NULL; bufwin = bufwin->w_next) {
|
||||
if (bufwin->w_buffer == buf && bufwin->w_p_diff) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (bufwin == NULL) {
|
||||
return FAIL; // should not really happen
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_DIFF_ANCHORS && *dia != NUL; i++) {
|
||||
if (*dia == ',') { // don't allow empty values
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
curbuf = buf;
|
||||
curwin = bufwin;
|
||||
const char *errormsg = NULL;
|
||||
linenr_T lnum = get_address(NULL, &dia, ADDR_LINES, check_only, true, false, 1, &errormsg);
|
||||
curbuf = orig_curbuf;
|
||||
curwin = orig_curwin;
|
||||
|
||||
if (errormsg != NULL) {
|
||||
emsg(errormsg);
|
||||
}
|
||||
if (dia == NULL) { // error detected
|
||||
return FAIL;
|
||||
}
|
||||
if (*dia != ',' && *dia != NUL) {
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
if (!check_only
|
||||
&& (lnum == MAXLNUM || lnum <= 0 || lnum > buf->b_ml.ml_line_count + 1)) {
|
||||
emsg(_(e_invrange));
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
if (anchors != NULL) {
|
||||
anchors[i] = lnum;
|
||||
}
|
||||
|
||||
if (*dia == ',') {
|
||||
dia++;
|
||||
}
|
||||
}
|
||||
if (i == MAX_DIFF_ANCHORS && *dia != NUL) {
|
||||
semsg(_(e_cannot_have_more_than_nr_diff_anchors), MAX_DIFF_ANCHORS);
|
||||
return FAIL;
|
||||
}
|
||||
if (num_anchors != NULL) {
|
||||
*num_anchors = i;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
/// This is called when 'diffanchors' is changed.
|
||||
int diffanchors_changed(bool buflocal)
|
||||
{
|
||||
int result = parse_diffanchors(true, curbuf, NULL, NULL);
|
||||
if (result == OK && (diff_flags & DIFF_ANCHOR)) {
|
||||
FOR_ALL_TABS(tp) {
|
||||
if (!buflocal) {
|
||||
tp->tp_diff_invalid = true;
|
||||
} else {
|
||||
for (int idx = 0; idx < DB_COUNT; idx++) {
|
||||
if (tp->tp_diffbuf[idx] == curbuf) {
|
||||
tp->tp_diff_invalid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// This is called when 'diffopt' is changed.
|
||||
///
|
||||
/// @return
|
||||
@@ -2495,6 +2643,9 @@ int diffopt_changed(void)
|
||||
if (strncmp(p, "filler", 6) == 0) {
|
||||
p += 6;
|
||||
diff_flags_new |= DIFF_FILLER;
|
||||
} else if (strncmp(p, "anchor", 6) == 0) {
|
||||
p += 6;
|
||||
diff_flags_new |= DIFF_ANCHOR;
|
||||
} else if ((strncmp(p, "context:", 8) == 0) && ascii_isdigit(p[8])) {
|
||||
p += 8;
|
||||
diff_context_new = getdigits_int(&p, false, diff_context_new);
|
||||
@@ -2581,6 +2732,9 @@ int diffopt_changed(void)
|
||||
p += 10;
|
||||
linematch_lines_new = getdigits_int(&p, false, linematch_lines_new);
|
||||
diff_flags_new |= DIFF_LINEMATCH;
|
||||
|
||||
// linematch does not make sense without filler set
|
||||
diff_flags_new |= DIFF_FILLER;
|
||||
}
|
||||
|
||||
if ((*p != ',') && (*p != NUL)) {
|
||||
@@ -3204,17 +3358,10 @@ bool diff_find_change(win_T *wp, linenr_T lnum, diffline_T *diffline)
|
||||
// search for a change that includes "lnum" in the list of diffblocks.
|
||||
diff_T *dp;
|
||||
FOR_ALL_DIFFBLOCKS_IN_TAB(curtab, dp) {
|
||||
if (lnum <= dp->df_lnum[idx] + dp->df_count[idx]) {
|
||||
if (lnum < dp->df_lnum[idx] + dp->df_count[idx]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (dp && dp->is_linematched) {
|
||||
while (dp && dp->df_next
|
||||
&& lnum == dp->df_count[idx] + dp->df_lnum[idx]
|
||||
&& dp->df_next->df_lnum[idx] == lnum) {
|
||||
dp = dp->df_next;
|
||||
}
|
||||
}
|
||||
if (dp == NULL || diff_check_sanity(curtab, dp) == FAIL) {
|
||||
return false;
|
||||
}
|
||||
@@ -3587,9 +3734,11 @@ static void diffgetput(const int addr_count, const int idx_cur, const int idx_fr
|
||||
|
||||
for (diff_T *dp = curtab->tp_first_diff; dp != NULL;) {
|
||||
if (!addr_count) {
|
||||
// handle the case with adjacent diff blocks
|
||||
while (dp->is_linematched
|
||||
&& dp->df_next
|
||||
// Handle the case with adjacent diff blocks (e.g. using linematch
|
||||
// or anchors) at/above the cursor. Since a range wasn't specified,
|
||||
// we just want to grab one diff block rather than all of them in
|
||||
// the vicinity.
|
||||
while (dp->df_next
|
||||
&& dp->df_next->df_lnum[idx_cur] == dp->df_lnum[idx_cur] + dp->df_count[idx_cur]
|
||||
&& dp->df_next->df_lnum[idx_cur] == line1 + off + 1) {
|
||||
dprev = dp;
|
||||
@@ -4071,7 +4220,7 @@ static int xdiff_out(int start_a, int count_a, int start_b, int count_b, void *p
|
||||
/// "diff_filler()" function
|
||||
void f_diff_filler(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
{
|
||||
rettv->vval.v_number = MAX(0, diff_check(curwin, tv_get_lnum(argvars)));
|
||||
rettv->vval.v_number = MAX(0, diff_check_fill(curwin, tv_get_lnum(argvars)));
|
||||
}
|
||||
|
||||
/// "diff_hlID()" function
|
||||
@@ -4102,9 +4251,9 @@ void f_diff_hlID(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
|| diff_flags != prev_diff_flags) {
|
||||
// New line, buffer, change: need to get the values.
|
||||
int linestatus = 0;
|
||||
int filler_lines = diff_check_with_linestatus(curwin, lnum, &linestatus);
|
||||
if (filler_lines < 0 || linestatus < 0) {
|
||||
if (filler_lines == -1 || linestatus == -1) {
|
||||
diff_check_with_linestatus(curwin, lnum, &linestatus);
|
||||
if (linestatus < 0) {
|
||||
if (linestatus == -1) {
|
||||
change_start = MAXCOL;
|
||||
change_end = -1;
|
||||
if (diff_find_change(curwin, lnum, &diffline)) {
|
||||
|
@@ -1276,8 +1276,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
|
||||
wlv.filler_lines = diff_check_with_linestatus(wp, lnum, &linestatus);
|
||||
diffline_T line_changes = { 0 };
|
||||
int change_index = -1;
|
||||
if (wlv.filler_lines < 0 || linestatus < 0) {
|
||||
if (wlv.filler_lines == -1 || linestatus == -1) {
|
||||
if (linestatus < 0) {
|
||||
if (linestatus == -1) {
|
||||
if (diff_find_change(wp, lnum, &line_changes)) {
|
||||
wlv.diff_hlf = HLF_ADD; // added line
|
||||
} else if (line_changes.num_changes > 0) {
|
||||
@@ -1300,9 +1300,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
|
||||
} else {
|
||||
wlv.diff_hlf = HLF_ADD; // added line
|
||||
}
|
||||
if (linestatus == 0) {
|
||||
wlv.filler_lines = 0;
|
||||
}
|
||||
area_highlighting = true;
|
||||
}
|
||||
VirtLines virt_lines = KV_INITIAL_VALUE;
|
||||
|
@@ -189,6 +189,8 @@ EXTERN const char e_winfixbuf_cannot_go_to_buffer[]
|
||||
INIT(= N_("E1513: Cannot switch buffer. 'winfixbuf' is enabled"));
|
||||
EXTERN const char e_invalid_return_type_from_findfunc[] INIT( = N_("E1514: 'findfunc' did not return a List type"));
|
||||
EXTERN const char e_cannot_switch_to_a_closing_buffer[] INIT( = N_("E1546: Cannot switch to a closing buffer"));
|
||||
EXTERN const char e_cannot_have_more_than_nr_diff_anchors[] INIT( = N_("E1549: Cannot have more than %d diff anchors"));
|
||||
EXTERN const char e_failed_to_find_all_diff_anchors[] INIT( = N_("E1550: Failed to find all diff anchors"));
|
||||
|
||||
EXTERN const char e_trustfile[] INIT(= N_("E5570: Cannot update trust file: %s"));
|
||||
|
||||
|
@@ -3368,9 +3368,9 @@ static const char *addr_error(cmd_addr_T addr_type)
|
||||
/// @param errormsg Error message, if any
|
||||
///
|
||||
/// @return MAXLNUM when no Ex address was found.
|
||||
static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, bool skip, bool silent,
|
||||
int to_other_file, int address_count, const char **errormsg)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, bool skip, bool silent,
|
||||
int to_other_file, int address_count, const char **errormsg)
|
||||
FUNC_ATTR_NONNULL_ARG(2, 8)
|
||||
{
|
||||
int c;
|
||||
int i;
|
||||
@@ -3535,7 +3535,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, bool
|
||||
// next/previous line.
|
||||
curwin->w_cursor.col = (c == '/' && curwin->w_cursor.lnum > 0) ? MAXCOL : 0;
|
||||
searchcmdlen = 0;
|
||||
flags = silent ? 0 : SEARCH_HIS | SEARCH_MSG;
|
||||
flags = silent ? SEARCH_KEEP : SEARCH_HIS | SEARCH_MSG;
|
||||
if (!do_search(NULL, c, c, cmd, strlen(cmd), 1, flags, NULL)) {
|
||||
curwin->w_cursor = pos;
|
||||
cmd = NULL;
|
||||
|
@@ -4478,6 +4478,8 @@ void *get_varp_scope_from(vimoption_T *p, int opt_flags, buf_T *buf, win_T *win)
|
||||
return &(buf->b_p_ise);
|
||||
case kOptDictionary:
|
||||
return &(buf->b_p_dict);
|
||||
case kOptDiffanchors:
|
||||
return &(buf->b_p_dia);
|
||||
case kOptThesaurus:
|
||||
return &(buf->b_p_tsr);
|
||||
case kOptThesaurusfunc:
|
||||
@@ -4565,6 +4567,8 @@ void *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win)
|
||||
return *buf->b_p_ise != NUL ? &(buf->b_p_ise) : p->var;
|
||||
case kOptDictionary:
|
||||
return *buf->b_p_dict != NUL ? &(buf->b_p_dict) : p->var;
|
||||
case kOptDiffanchors:
|
||||
return *buf->b_p_dia != NUL ? &(buf->b_p_dia) : p->var;
|
||||
case kOptThesaurus:
|
||||
return *buf->b_p_tsr != NUL ? &(buf->b_p_tsr) : p->var;
|
||||
case kOptThesaurusfunc:
|
||||
@@ -5258,6 +5262,7 @@ void buf_copy_options(buf_T *buf, int flags)
|
||||
buf->b_p_cot = empty_string_option;
|
||||
buf->b_cot_flags = 0;
|
||||
buf->b_p_dict = empty_string_option;
|
||||
buf->b_p_dia = empty_string_option;
|
||||
buf->b_p_tsr = empty_string_option;
|
||||
buf->b_p_ise = empty_string_option;
|
||||
buf->b_p_tsrfu = empty_string_option;
|
||||
|
@@ -314,6 +314,7 @@ EXTERN char *p_cpo; ///< 'cpoptions'
|
||||
EXTERN char *p_debug; ///< 'debug'
|
||||
EXTERN char *p_def; ///< 'define'
|
||||
EXTERN char *p_inc;
|
||||
EXTERN char *p_dia; ///< 'diffanchors'
|
||||
EXTERN char *p_dip; ///< 'diffopt'
|
||||
EXTERN char *p_dex; ///< 'diffexpr'
|
||||
EXTERN char *p_dict; ///< 'dictionary'
|
||||
|
@@ -2244,6 +2244,36 @@ local options = {
|
||||
short_desc = N_('diff mode for the current window'),
|
||||
type = 'boolean',
|
||||
},
|
||||
{
|
||||
abbreviation = 'dia',
|
||||
cb = 'did_set_diffanchors',
|
||||
defaults = '',
|
||||
desc = [=[
|
||||
List of {address} in each buffer, separated by commas, that are
|
||||
considered anchors when used for diffing. It's valid to specify "$+1"
|
||||
for 1 past the last line. "%" cannot be used for this option. There
|
||||
can be at most 20 anchors set for each buffer.
|
||||
|
||||
Each anchor line splits the buffer (the split happens above the
|
||||
anchor), with each part being diff'ed separately before the final
|
||||
result is joined. When more than one {address} are provided, the
|
||||
anchors will be sorted interally by line number. If using buffer
|
||||
local options, each buffer should have the same number of anchors
|
||||
(extra anchors will be ignored). This option is only used when
|
||||
'diffopt' has "anchor" set. See |diff-anchors| for more details and
|
||||
examples.
|
||||
*E1550*
|
||||
If some of the {address} do not resolve to a line in each buffer (e.g.
|
||||
a pattern search that does not match anything), none of the anchors
|
||||
will be used.
|
||||
]=],
|
||||
full_name = 'diffanchors',
|
||||
list = 'onecomma',
|
||||
scope = { 'global', 'buf' },
|
||||
short_desc = N_('list of addresses for anchoring a diff'),
|
||||
type = 'string',
|
||||
varname = 'p_dia',
|
||||
},
|
||||
{
|
||||
abbreviation = 'dex',
|
||||
cb = 'did_set_optexpr',
|
||||
@@ -2269,6 +2299,7 @@ local options = {
|
||||
-- Keep this in sync with diffopt_changed().
|
||||
values = {
|
||||
'filler',
|
||||
'anchor',
|
||||
'context:',
|
||||
'iblank',
|
||||
'icase',
|
||||
@@ -2301,6 +2332,10 @@ local options = {
|
||||
patience patience diff algorithm
|
||||
histogram histogram diff algorithm
|
||||
|
||||
anchor Anchor specific lines in each buffer to be
|
||||
aligned with each other if 'diffanchors' is
|
||||
set. See |diff-anchors|.
|
||||
|
||||
closeoff When a window is closed where 'diff' is set
|
||||
and there is only one window remaining in the
|
||||
same tab page with 'diff' set, execute
|
||||
@@ -2403,6 +2438,7 @@ local options = {
|
||||
"linematch:60", as this will enable alignment
|
||||
for a 2 buffer diff hunk of 30 lines each,
|
||||
or a 3 buffer diff hunk of 20 lines each.
|
||||
Implicitly sets "filler" when this is set.
|
||||
|
||||
vertical Start diff mode with vertical splits (unless
|
||||
explicitly specified otherwise).
|
||||
|
@@ -170,6 +170,7 @@ void check_buf_options(buf_T *buf)
|
||||
check_string_option(&buf->b_p_tfu);
|
||||
check_string_option(&buf->b_p_tc);
|
||||
check_string_option(&buf->b_p_dict);
|
||||
check_string_option(&buf->b_p_dia);
|
||||
check_string_option(&buf->b_p_tsr);
|
||||
check_string_option(&buf->b_p_tsrfu);
|
||||
check_string_option(&buf->b_p_lw);
|
||||
@@ -1038,6 +1039,16 @@ const char *did_set_cursorlineopt(optset_T *args)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// The 'diffanchors' option is changed.
|
||||
const char *did_set_diffanchors(optset_T *args)
|
||||
{
|
||||
if (diffanchors_changed(args->os_flags & OPT_LOCAL) == FAIL) {
|
||||
return e_invarg;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// The 'diffopt' option is changed.
|
||||
const char *did_set_diffopt(optset_T *args FUNC_ATTR_UNUSED)
|
||||
{
|
||||
|
@@ -732,7 +732,7 @@ int win_get_fill(win_T *wp, linenr_T lnum)
|
||||
|
||||
// be quick when there are no filler lines
|
||||
if (diffopt_filler()) {
|
||||
int n = diff_check(wp, lnum);
|
||||
int n = diff_check_fill(wp, lnum);
|
||||
|
||||
if (n > 0) {
|
||||
return virt_lines + n;
|
||||
@@ -943,7 +943,7 @@ int plines_m_win_fill(win_T *wp, linenr_T first, linenr_T last)
|
||||
if (diffopt_filler()) {
|
||||
for (int lnum = first; lnum <= last; lnum++) {
|
||||
// Note: this also considers folds.
|
||||
int n = diff_check(wp, lnum);
|
||||
int n = diff_check_fill(wp, lnum);
|
||||
count += MAX(n, 0);
|
||||
}
|
||||
}
|
||||
|
@@ -2836,3 +2836,489 @@ it('diff mode algorithm:histogram and inline:char with long lines #34329', funct
|
||||
|
|
||||
]])
|
||||
end)
|
||||
|
||||
-- oldtest: Test_linematch_diff()
|
||||
it([["linematch" in 'diffopt' implies "filler"]], function()
|
||||
local screen = Screen.new(55, 20)
|
||||
command('set diffopt=internal,filler,closeoff,inline:simple,linematch:30')
|
||||
|
||||
write_file('Xdifile1', '')
|
||||
write_file('Xdifile2', '')
|
||||
finally(function()
|
||||
os.remove('Xdifile1')
|
||||
os.remove('Xdifile2')
|
||||
end)
|
||||
|
||||
WriteDiffFiles('// d?\n// d?', '!\nabc d!\nd!')
|
||||
command('args Xdifile1 Xdifile2 | vert all | windo diffthis | 1wincmd w')
|
||||
screen:expect([[
|
||||
{7: }{23:-------------------------}│{7: }{22:! }|
|
||||
{7: }{27:^// d?}{4: }│{7: }{27:abc d!}{4: }|
|
||||
{7: }{27:// d?}{4: }│{7: }{27:d!}{4: }|
|
||||
{1:~ }│{1:~ }|*15
|
||||
{3:Xdifile1 }{2:Xdifile2 }|
|
||||
|
|
||||
]])
|
||||
|
||||
-- test that filler is always implicitly set by linematch
|
||||
command('set diffopt-=filler')
|
||||
screen:expect_unchanged()
|
||||
end)
|
||||
|
||||
describe("'diffanchors'", function()
|
||||
local screen
|
||||
|
||||
before_each(function()
|
||||
screen = Screen.new(32, 20)
|
||||
command('set winwidth=10 nohlsearch shortmess+=s')
|
||||
end)
|
||||
|
||||
after_each(function()
|
||||
os.remove('Xdifile1')
|
||||
os.remove('Xdifile2')
|
||||
os.remove('Xdifile3')
|
||||
end)
|
||||
|
||||
it('works', function()
|
||||
WriteDiffFiles3(
|
||||
'anchorA1\n1\n2\n3\n100\n101\n102\nanchorB\n103\n104\n105',
|
||||
'100\n101\n102\nanchorB\n103\n104\n105\nanchorA2\n1\n2\n3',
|
||||
'100\nanchorB\n103\nanchorA3\n1\n2\n3'
|
||||
)
|
||||
command('args Xdifile1 Xdifile2 Xdifile3 | vert all | windo diffthis | 1wincmd w')
|
||||
|
||||
-- Simple diff without any anchors
|
||||
command('set diffopt=filler,internal')
|
||||
local s0 = [[
|
||||
{7: }{22:^anchorA1}│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{22:1 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{22:2 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{22:3 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }100 │{7: }100 │{7: }100 |
|
||||
{7: }{22:101 }│{7: }{22:101 }│{7: }{23:--------}|
|
||||
{7: }{22:102 }│{7: }{22:102 }│{7: }{23:--------}|
|
||||
{7: }anchorB │{7: }anchorB │{7: }anchorB |
|
||||
{7: }103 │{7: }103 │{7: }103 |
|
||||
{7: }{27:104}{4: }│{7: }{27:104}{4: }│{7: }{27:anchorA3}|
|
||||
{7: }{4:1}{27:05}{4: }│{7: }{4:1}{27:05}{4: }│{7: }{4:1 }|
|
||||
{7: }{23:--------}│{7: }{27:anchorA}{4:2}│{7: }{4:2 }|
|
||||
{7: }{23:--------}│{7: }{27:1}{4: }│{7: }{27:3}{4: }|
|
||||
{7: }{23:--------}│{7: }{22:2 }│{7: }{23:--------}|
|
||||
{7: }{23:--------}│{7: }{22:3 }│{7: }{23:--------}|
|
||||
{1:~ }│{1:~ }│{1:~ }|*3
|
||||
{3:Xdifile1 }{2:Xdifile2 Xdifile3 }|
|
||||
|
|
||||
]]
|
||||
screen:expect(s0)
|
||||
|
||||
-- Setting diffopt+=anchor or diffanchors without the other won't do anything
|
||||
command('set diffopt=filler,internal diffopt+=anchor')
|
||||
screen:expect_unchanged()
|
||||
command('set diffopt=filler,internal dia=1/anchorA/')
|
||||
screen:expect_unchanged()
|
||||
|
||||
-- Use a single anchor by specifying a pattern. Test both internal and
|
||||
-- external diff to make sure both paths work.
|
||||
command('set diffopt=filler dia=1/anchorA/ diffopt+=anchor')
|
||||
local s1 = [[
|
||||
{7: }{23:--------}│{7: }{4:100 }│{7: }{4:100 }|
|
||||
{7: }{23:--------}│{7: }{27:101}{4: }│{7: }{27:anchorB}{4: }|
|
||||
{7: }{23:--------}│{7: }{4:10}{27:2}{4: }│{7: }{4:10}{27:3}{4: }|
|
||||
{7: }{23:--------}│{7: }{22:anchorB }│{7: }{23:--------}|
|
||||
{7: }{23:--------}│{7: }{22:103 }│{7: }{23:--------}|
|
||||
{7: }{23:--------}│{7: }{22:104 }│{7: }{23:--------}|
|
||||
{7: }{23:--------}│{7: }{22:105 }│{7: }{23:--------}|
|
||||
{7: }{4:^anchorA}{27:1}│{7: }{4:anchorA}{27:2}│{7: }{4:anchorA}{27:3}|
|
||||
{7: }1 │{7: }1 │{7: }1 |
|
||||
{7: }2 │{7: }2 │{7: }2 |
|
||||
{7: }3 │{7: }3 │{7: }3 |
|
||||
{7: }{22:100 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{22:101 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{22:102 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{22:anchorB }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{22:103 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{22:104 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{22:105 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{3:Xdifile1 }{2:Xdifile2 Xdifile3 }|
|
||||
|
|
||||
]]
|
||||
screen:expect(s1)
|
||||
command('set diffopt+=internal')
|
||||
screen:expect_unchanged()
|
||||
|
||||
-- Use 2 anchors. They should be sorted by line number, so in file 2/3
|
||||
-- anchorB is used before anchorA.
|
||||
command('set diffopt=filler dia=1/anchorA/,1/anchorB/ diffopt+=anchor')
|
||||
local s2 = [[
|
||||
{7: }{23:--------}│{7: }{4:100 }│{7: }{4:100 }|
|
||||
{7: }{23:--------}│{7: }{22:101 }│{7: }{23:--------}|
|
||||
{7: }{23:--------}│{7: }{22:102 }│{7: }{23:--------}|
|
||||
{7: }{4:^anchor}{27:A1}│{7: }{4:anchor}{27:B}{4: }│{7: }{4:anchor}{27:B}{4: }|
|
||||
{7: }{4:1 }│{7: }{4:1}{27:03}{4: }│{7: }{4:1}{27:03}{4: }|
|
||||
{7: }{27:2}{4: }│{7: }{27:104}{4: }│{7: }{23:--------}|
|
||||
{7: }{27:3}{4: }│{7: }{27:105}{4: }│{7: }{23:--------}|
|
||||
{7: }{22:100 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{22:101 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{22:102 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{4:anchor}{27:B}{4: }│{7: }{4:anchor}{27:A2}│{7: }{4:anchor}{27:A3}|
|
||||
{7: }{4:1}{27:03}{4: }│{7: }{4:1 }│{7: }{4:1 }|
|
||||
{7: }{27:104}{4: }│{7: }{27:2}{4: }│{7: }{27:2}{4: }|
|
||||
{7: }{27:105}{4: }│{7: }{27:3}{4: }│{7: }{27:3}{4: }|
|
||||
{1:~ }│{1:~ }│{1:~ }|*4
|
||||
{3:Xdifile1 }{2:Xdifile2 Xdifile3 }|
|
||||
|
|
||||
]]
|
||||
screen:expect(s2)
|
||||
command('set diffopt+=internal')
|
||||
screen:expect_unchanged()
|
||||
|
||||
-- Set marks and specify addresses using marks and repeat the test
|
||||
command('2wincmd w | 1/anchorA/mark a')
|
||||
command('1/anchorB/mark b')
|
||||
command('3wincmd w | 1/anchorA/mark a')
|
||||
command('1/anchorB/mark b')
|
||||
command('1wincmd w | 1/anchorA/mark a')
|
||||
command('1/anchorB/mark b')
|
||||
|
||||
command("set diffopt=filler,internal dia='a diffopt+=anchor")
|
||||
screen:expect(s1)
|
||||
command("set diffopt=filler,internal dia='a,'b diffopt+=anchor")
|
||||
screen:expect(s2)
|
||||
|
||||
-- Update marks to point somewhere else. When we first set the mark the diff
|
||||
-- won't be updated until we manually invoke :diffupdate.
|
||||
command("set diffopt=filler,internal dia='a diffopt+=anchor")
|
||||
screen:expect(s1)
|
||||
command('1wincmd w | 1/anchorB/mark a')
|
||||
screen:expect_unchanged()
|
||||
command('diffupdate')
|
||||
screen:expect([[
|
||||
{7: }{22:^anchorA1}│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{22:1 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{22:2 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{22:3 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }100 │{7: }100 │{7: }100 |
|
||||
{7: }{27:101}{4: }│{7: }{27:101}{4: }│{7: }{27:anchorB}{4: }|
|
||||
{7: }{4:10}{27:2}{4: }│{7: }{4:10}{27:2}{4: }│{7: }{4:10}{27:3}{4: }|
|
||||
{7: }{23:--------}│{7: }{22:anchorB }│{7: }{23:--------}|
|
||||
{7: }{23:--------}│{7: }{22:103 }│{7: }{23:--------}|
|
||||
{7: }{23:--------}│{7: }{22:104 }│{7: }{23:--------}|
|
||||
{7: }{23:--------}│{7: }{22:105 }│{7: }{23:--------}|
|
||||
{7: }{4:anchor}{27:B}{4: }│{7: }{4:anchor}{27:A2}│{7: }{4:anchor}{27:A3}|
|
||||
{7: }{4:1}{27:03}{4: }│{7: }{4:1 }│{7: }{4:1 }|
|
||||
{7: }{27:104}{4: }│{7: }{27:2}{4: }│{7: }{27:2}{4: }|
|
||||
{7: }{27:105}{4: }│{7: }{27:3}{4: }│{7: }{27:3}{4: }|
|
||||
{1:~ }│{1:~ }│{1:~ }|*3
|
||||
{3:Xdifile1 }{2:Xdifile2 Xdifile3 }|
|
||||
|
|
||||
]])
|
||||
|
||||
-- Use local diff anchors with line numbers, and repeat the same test
|
||||
command('2wincmd w | setlocal dia=8')
|
||||
command('3wincmd w | setlocal dia=4')
|
||||
command('1wincmd w | setlocal dia=1')
|
||||
command('set diffopt=filler,internal diffopt+=anchor')
|
||||
screen:expect(s1)
|
||||
command('2wincmd w | setlocal dia=8,4')
|
||||
command('3wincmd w | setlocal dia=4,2')
|
||||
command('1wincmd w | setlocal dia=1,8')
|
||||
command('set diffopt=filler,internal diffopt+=anchor')
|
||||
screen:expect(s2)
|
||||
|
||||
-- Test multiple diff anchors on the same line in file 1.
|
||||
command('1wincmd w | setlocal dia=1,1')
|
||||
command('set diffopt=filler,internal diffopt+=anchor')
|
||||
screen:expect([[
|
||||
{7: }{23:--------}│{7: }{4:100 }│{7: }{4:100 }|
|
||||
{7: }{23:--------}│{7: }{22:101 }│{7: }{23:--------}|
|
||||
{7: }{23:--------}│{7: }{22:102 }│{7: }{23:--------}|
|
||||
{7: }{23:--------}│{7: }{4:anchorB }│{7: }{4:anchorB }|
|
||||
{7: }{23:--------}│{7: }{4:103 }│{7: }{4:103 }|
|
||||
{7: }{23:--------}│{7: }{22:104 }│{7: }{23:--------}|
|
||||
{7: }{23:--------}│{7: }{22:105 }│{7: }{23:--------}|
|
||||
{7: }{4:^anchorA}{27:1}│{7: }{4:anchorA}{27:2}│{7: }{4:anchorA}{27:3}|
|
||||
{7: }1 │{7: }1 │{7: }1 |
|
||||
{7: }2 │{7: }2 │{7: }2 |
|
||||
{7: }3 │{7: }3 │{7: }3 |
|
||||
{7: }{22:100 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{22:101 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{22:102 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{22:anchorB }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{22:103 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{22:104 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{22:105 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{3:Xdifile1 }{2:Xdifile2 Xdifile3 }|
|
||||
|
|
||||
]])
|
||||
|
||||
-- Test that if one file has fewer diff anchors than others. Vim should only
|
||||
-- use the minimum in this case.
|
||||
command('1wincmd w | setlocal dia=8')
|
||||
command('set diffopt=filler,internal diffopt+=anchor')
|
||||
screen:expect([[
|
||||
{7: }{22:^anchorA1}│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{22:1 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{22:2 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{22:3 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }100 │{7: }100 │{7: }100 |
|
||||
{7: }{22:101 }│{7: }{22:101 }│{7: }{23:--------}|
|
||||
{7: }{22:102 }│{7: }{22:102 }│{7: }{23:--------}|
|
||||
{7: }anchorB │{7: }anchorB │{7: }anchorB |
|
||||
{7: }103 │{7: }103 │{7: }103 |
|
||||
{7: }{27:104}{4: }│{7: }{27:104}{4: }│{7: }{27:anchorA3}|
|
||||
{7: }{4:1}{27:05}{4: }│{7: }{4:1}{27:05}{4: }│{7: }{4:1 }|
|
||||
{7: }{23:--------}│{7: }{27:anchorA}{4:2}│{7: }{4:2 }|
|
||||
{7: }{23:--------}│{7: }{27:1}{4: }│{7: }{27:3}{4: }|
|
||||
{7: }{23:--------}│{7: }{22:2 }│{7: }{23:--------}|
|
||||
{7: }{23:--------}│{7: }{22:3 }│{7: }{23:--------}|
|
||||
{1:~ }│{1:~ }│{1:~ }|*3
|
||||
{3:Xdifile1 }{2:Xdifile2 Xdifile3 }|
|
||||
|
|
||||
]])
|
||||
|
||||
-- $+1 should anchor everything past the last line
|
||||
command('1wincmd w | setlocal dia=$+1')
|
||||
command('set diffopt=filler,internal diffopt+=anchor')
|
||||
screen:expect([[
|
||||
{7: }{22:^anchorA1}│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{22:1 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{22:2 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{22:3 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }100 │{7: }100 │{7: }100 |
|
||||
{7: }{4:101 }│{7: }{4:101 }│{7: }{23:--------}|
|
||||
{7: }{4:102 }│{7: }{4:102 }│{7: }{23:--------}|
|
||||
{7: }{22:anchorB }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{22:103 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{22:104 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{22:105 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{23:--------}│{7: }{4:anchorB }│{7: }{4:anchorB }|
|
||||
{7: }{23:--------}│{7: }{4:103 }│{7: }{4:103 }|
|
||||
{7: }{23:--------}│{7: }{27:104}{4: }│{7: }{27:anchorA3}|
|
||||
{7: }{23:--------}│{7: }{4:1}{27:05}{4: }│{7: }{4:1 }|
|
||||
{7: }{23:--------}│{7: }{27:anchorA}{4:2}│{7: }{4:2 }|
|
||||
{7: }{23:--------}│{7: }{27:1}{4: }│{7: }{27:3}{4: }|
|
||||
{7: }{23:--------}│{7: }{22:2 }│{7: }{23:--------}|
|
||||
{3:Xdifile1 }{2:Xdifile2 Xdifile3 }|
|
||||
|
|
||||
]])
|
||||
|
||||
-- Sorting of diff anchors should work with multiple anchors
|
||||
command('1wincmd w | setlocal dia=1,10,8,2')
|
||||
command('2wincmd w | setlocal dia=1,2,3,4')
|
||||
command('3wincmd w | setlocal dia=4,3,2,1')
|
||||
command('set diffopt=filler,internal diffopt+=anchor')
|
||||
screen:expect([[
|
||||
{7: }{27:anchorA1}│{7: }{27:100}{4: }│{7: }{27:^100}{4: }|
|
||||
{7: }{27:1}{4: }│{7: }{27:101}{4: }│{7: }{27:anchorB}{4: }|
|
||||
{7: }{22:2 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{22:3 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{22:100 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{22:101 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{22:102 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{27:anchorB}{4: }│{7: }{27:102}{4: }│{7: }{27:103}{4: }|
|
||||
{7: }{22:103 }│{7: }{23:--------}│{7: }{23:--------}|
|
||||
{7: }{27:104}{4: }│{7: }{27:anchorB}{4: }│{7: }{27:anchorA3}|
|
||||
{7: }{4:1}{27:05}{4: }│{7: }{4:1}{27:03}{4: }│{7: }{4:1 }|
|
||||
{7: }{23:--------}│{7: }{27:104}{4: }│{7: }{27:2}{4: }|
|
||||
{7: }{23:--------}│{7: }{27:105}{4: }│{7: }{27:3}{4: }|
|
||||
{7: }{23:--------}│{7: }{22:anchorA2}│{7: }{23:--------}|
|
||||
{7: }{23:--------}│{7: }{22:1 }│{7: }{23:--------}|
|
||||
{7: }{23:--------}│{7: }{22:2 }│{7: }{23:--------}|
|
||||
{7: }{23:--------}│{7: }{22:3 }│{7: }{23:--------}|
|
||||
{1:~ }│{1:~ }│{1:~ }|
|
||||
{2:Xdifile1 Xdifile2 }{3:Xdifile3 }|
|
||||
|
|
||||
]])
|
||||
|
||||
-- Intentionally set an invalid anchor with wrong line number. Should fall
|
||||
-- back to treat it as if no anchors are used at all.
|
||||
command('1wincmd w | setlocal dia=1,10,8,2,1000 | silent! diffupdate')
|
||||
screen:expect(s0)
|
||||
end)
|
||||
|
||||
-- oldtest: Test_diffanchors_scrollbind_topline()
|
||||
it('scrollbind works with adjacent diff blocks', function()
|
||||
WriteDiffFiles('anchor1\ndiff1a\nanchor2', 'anchor1\ndiff2a\nanchor2')
|
||||
command('args Xdifile1 Xdifile2 | vert all | windo diffthis | 1wincmd w')
|
||||
|
||||
command('1wincmd w | setlocal dia=2')
|
||||
command('2wincmd w | setlocal dia=3')
|
||||
|
||||
command('set diffopt=filler,internal diffopt+=anchor')
|
||||
screen:expect([[
|
||||
{7: }anchor1 │{7: }^anchor1 |
|
||||
{7: }{23:-------------}│{7: }{22:diff2a }|
|
||||
{7: }{22:diff1a }│{7: }{23:--------------}|
|
||||
{7: }anchor2 │{7: }anchor2 |
|
||||
{1:~ }│{1:~ }|*14
|
||||
{2:Xdifile1 }{3:Xdifile2 }|
|
||||
|
|
||||
]])
|
||||
feed('<C-E>')
|
||||
screen:expect([[
|
||||
{7: }{23:-------------}│{7: }{22:^diff2a }|
|
||||
{7: }{22:diff1a }│{7: }{23:--------------}|
|
||||
{7: }anchor2 │{7: }anchor2 |
|
||||
{1:~ }│{1:~ }|*15
|
||||
{2:Xdifile1 }{3:Xdifile2 }|
|
||||
|
|
||||
]])
|
||||
feed('<C-E>')
|
||||
screen:expect([[
|
||||
{7: }{22:diff1a }│{7: }{23:--------------}|
|
||||
{7: }anchor2 │{7: }^anchor2 |
|
||||
{1:~ }│{1:~ }|*16
|
||||
{2:Xdifile1 }{3:Xdifile2 }|
|
||||
|
|
||||
]])
|
||||
feed('<C-E>')
|
||||
screen:expect([[
|
||||
{7: }anchor2 │{7: }^anchor2 |
|
||||
{1:~ }│{1:~ }|*17
|
||||
{2:Xdifile1 }{3:Xdifile2 }|
|
||||
|
|
||||
]])
|
||||
|
||||
-- Also test no-filler
|
||||
feed('gg')
|
||||
command('set diffopt=filler,internal diffopt+=anchor diffopt-=filler')
|
||||
screen:expect([[
|
||||
{7: }anchor1 │{7: }^anchor1 |
|
||||
{7: }{22:diff1a }│{7: }{22:diff2a }|
|
||||
{7: }anchor2 │{7: }anchor2 |
|
||||
{1:~ }│{1:~ }|*15
|
||||
{2:Xdifile1 }{3:Xdifile2 }|
|
||||
|
|
||||
]])
|
||||
feed('<C-E>')
|
||||
screen:expect([[
|
||||
{7: }{22:diff1a }│{7: }{22:^diff2a }|
|
||||
{7: }anchor2 │{7: }anchor2 |
|
||||
{1:~ }│{1:~ }|*16
|
||||
{2:Xdifile1 }{3:Xdifile2 }|
|
||||
|
|
||||
]])
|
||||
feed('<C-E>')
|
||||
screen:expect([[
|
||||
{7: }anchor2 │{7: }^anchor2 |
|
||||
{1:~ }│{1:~ }|*17
|
||||
{2:Xdifile1 }{3:Xdifile2 }|
|
||||
|
|
||||
]])
|
||||
end)
|
||||
|
||||
-- oldtest: Test_diffanchors_scrollbind_topline2()
|
||||
it('scrollbind works with 3 files and overlapping diff blocks', function()
|
||||
WriteDiffFiles3(
|
||||
'anchor1',
|
||||
'diff2a\ndiff2b\ndiff2c\ndiff2d\nanchor2',
|
||||
'diff3a\ndiff3c\ndiff3d\nanchor3\ndiff3e'
|
||||
)
|
||||
command('args Xdifile1 Xdifile2 Xdifile3 | vert all | windo diffthis | 1wincmd w')
|
||||
|
||||
command('1wincmd w | setlocal dia=1,1,2')
|
||||
command('2wincmd w | setlocal dia=3,5,6')
|
||||
command('3wincmd w | setlocal dia=2,4,5')
|
||||
|
||||
command('set diffopt=filler,internal diffopt+=anchor')
|
||||
screen:expect([[
|
||||
{7: }{23:--------}│{7: }{4:diff}{27:2}{4:a }│{7: }{4:^diff}{27:3}{4:a }|
|
||||
{7: }{23:--------}│{7: }{22:diff2b }│{7: }{23:--------}|
|
||||
{7: }{23:--------}│{7: }{4:diff}{27:2}{4:c }│{7: }{4:diff}{27:3}{4:c }|
|
||||
{7: }{23:--------}│{7: }{4:diff}{27:2}{4:d }│{7: }{4:diff}{27:3}{4:d }|
|
||||
{7: }{4:anchor}{27:1}{4: }│{7: }{4:anchor}{27:2}{4: }│{7: }{4:anchor}{27:3}{4: }|
|
||||
{7: }{23:--------}│{7: }{23:--------}│{7: }{22:diff3e }|
|
||||
{1:~ }│{1:~ }│{1:~ }|*12
|
||||
{2:Xdifile1 Xdifile2 }{3:Xdifile3 }|
|
||||
|
|
||||
]])
|
||||
command('1wincmd w')
|
||||
feed('<C-E>')
|
||||
screen:expect([[
|
||||
{7: }{23:--------}│{7: }{22:diff2b }│{7: }{23:--------}|
|
||||
{7: }{23:--------}│{7: }{4:diff}{27:2}{4:c }│{7: }{4:diff}{27:3}{4:c }|
|
||||
{7: }{23:--------}│{7: }{4:diff}{27:2}{4:d }│{7: }{4:diff}{27:3}{4:d }|
|
||||
{7: }{4:^anchor}{27:1}{4: }│{7: }{4:anchor}{27:2}{4: }│{7: }{4:anchor}{27:3}{4: }|
|
||||
{7: }{23:--------}│{7: }{23:--------}│{7: }{22:diff3e }|
|
||||
{1:~ }│{1:~ }│{1:~ }|*13
|
||||
{3:Xdifile1 }{2:Xdifile2 Xdifile3 }|
|
||||
|
|
||||
]])
|
||||
feed('<C-E>')
|
||||
screen:expect([[
|
||||
{7: }{23:--------}│{7: }{4:diff}{27:2}{4:c }│{7: }{4:diff}{27:3}{4:c }|
|
||||
{7: }{23:--------}│{7: }{4:diff}{27:2}{4:d }│{7: }{4:diff}{27:3}{4:d }|
|
||||
{7: }{4:^anchor}{27:1}{4: }│{7: }{4:anchor}{27:2}{4: }│{7: }{4:anchor}{27:3}{4: }|
|
||||
{7: }{23:--------}│{7: }{23:--------}│{7: }{22:diff3e }|
|
||||
{1:~ }│{1:~ }│{1:~ }|*14
|
||||
{3:Xdifile1 }{2:Xdifile2 Xdifile3 }|
|
||||
|
|
||||
]])
|
||||
feed('<C-E>')
|
||||
screen:expect([[
|
||||
{7: }{23:--------}│{7: }{4:diff}{27:2}{4:d }│{7: }{4:diff}{27:3}{4:d }|
|
||||
{7: }{4:^anchor}{27:1}{4: }│{7: }{4:anchor}{27:2}{4: }│{7: }{4:anchor}{27:3}{4: }|
|
||||
{7: }{23:--------}│{7: }{23:--------}│{7: }{22:diff3e }|
|
||||
{1:~ }│{1:~ }│{1:~ }|*15
|
||||
{3:Xdifile1 }{2:Xdifile2 Xdifile3 }|
|
||||
|
|
||||
]])
|
||||
feed('<C-E>')
|
||||
screen:expect([[
|
||||
{7: }{4:^anchor}{27:1}{4: }│{7: }{4:anchor}{27:2}{4: }│{7: }{4:anchor}{27:3}{4: }|
|
||||
{7: }{23:--------}│{7: }{23:--------}│{7: }{22:diff3e }|
|
||||
{1:~ }│{1:~ }│{1:~ }|*16
|
||||
{3:Xdifile1 }{2:Xdifile2 Xdifile3 }|
|
||||
|
|
||||
]])
|
||||
|
||||
-- Also test no-filler
|
||||
command('3wincmd w')
|
||||
feed('gg')
|
||||
command('set diffopt=filler,internal diffopt+=anchor diffopt-=filler')
|
||||
screen:expect([[
|
||||
{7: }{4:anchor}{27:1}{4: }│{7: }{4:diff}{27:2}{4:a }│{7: }{4:^diff}{27:3}{4:a }|
|
||||
{1:~ }│{7: }{22:diff2b }│{7: }{4:diff}{27:3}{4:c }|
|
||||
{1:~ }│{7: }{4:diff}{27:2}{4:c }│{7: }{4:diff}{27:3}{4:d }|
|
||||
{1:~ }│{7: }{4:diff}{27:2}{4:d }│{7: }{4:anchor}{27:3}{4: }|
|
||||
{1:~ }│{7: }{4:anchor}{27:2}{4: }│{7: }{22:diff3e }|
|
||||
{1:~ }│{1:~ }│{1:~ }|*13
|
||||
{2:Xdifile1 Xdifile2 }{3:Xdifile3 }|
|
||||
|
|
||||
]])
|
||||
feed('<C-E>')
|
||||
screen:expect([[
|
||||
{7: }{4:anchor}{27:1}{4: }│{7: }{4:diff}{27:2}{4:c }│{7: }{4:^diff}{27:3}{4:c }|
|
||||
{1:~ }│{7: }{4:diff}{27:2}{4:d }│{7: }{4:diff}{27:3}{4:d }|
|
||||
{1:~ }│{7: }{4:anchor}{27:2}{4: }│{7: }{4:anchor}{27:3}{4: }|
|
||||
{1:~ }│{1:~ }│{7: }{22:diff3e }|
|
||||
{1:~ }│{1:~ }│{1:~ }|*14
|
||||
{2:Xdifile1 Xdifile2 }{3:Xdifile3 }|
|
||||
|
|
||||
]])
|
||||
feed('<C-E>')
|
||||
screen:expect([[
|
||||
{7: }{4:anchor}{27:1}{4: }│{7: }{4:diff}{27:2}{4:d }│{7: }{4:^diff}{27:3}{4:d }|
|
||||
{1:~ }│{7: }{4:anchor}{27:2}{4: }│{7: }{4:anchor}{27:3}{4: }|
|
||||
{1:~ }│{1:~ }│{7: }{22:diff3e }|
|
||||
{1:~ }│{1:~ }│{1:~ }|*15
|
||||
{2:Xdifile1 Xdifile2 }{3:Xdifile3 }|
|
||||
|
|
||||
]])
|
||||
feed('<C-E>')
|
||||
screen:expect([[
|
||||
{7: }{4:anchor}{27:1}{4: }│{7: }{4:anchor}{27:2}{4: }│{7: }{4:^anchor}{27:3}{4: }|
|
||||
{1:~ }│{1:~ }│{7: }{22:diff3e }|
|
||||
{1:~ }│{1:~ }│{1:~ }|*16
|
||||
{2:Xdifile1 Xdifile2 }{3:Xdifile3 }|
|
||||
|
|
||||
]])
|
||||
feed('<C-E>')
|
||||
screen:expect([[
|
||||
{7: }{4:anchor}{27:1}{4: }│{7: }{4:anchor}{27:2}{4: }│{7: }{22:^diff3e }|
|
||||
{1:~ }│{1:~ }│{1:~ }|*17
|
||||
{2:Xdifile1 Xdifile2 }{3:Xdifile3 }|
|
||||
|
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
|
@@ -86,15 +86,15 @@ describe('Diff mode screen with 3 diffs open', function()
|
||||
screen:expect([[
|
||||
{7: }{8: 1 }^ │{7: }{8: 1 } │{7: }{8: 1 } |
|
||||
{7: }{8: 2 }common line │{7: }{8: 2 }common line │{7: }{8: 2 }common line |
|
||||
{7: }{8: 3 }{4:<<<<<<< HEAD }│{7: }{8: 3 }{4:<<<<<<< HEAD }│{7: }{8: }{23:---------------------------}|
|
||||
{7: }{8: 3 }{22:<<<<<<< HEAD }│{7: }{8: 3 }{22:<<<<<<< HEAD }│{7: }{8: }{23:---------------------------}|
|
||||
{7: }{8: 4 } AAA │{7: }{8: 4 } AAA │{7: }{8: 3 } AAA |
|
||||
{7: }{8: 5 } AAA │{7: }{8: 5 } AAA │{7: }{8: 4 } AAA |
|
||||
{7: }{8: 6 } AAA │{7: }{8: 6 } AAA │{7: }{8: 5 } AAA |
|
||||
{7: }{8: 7 }{4:======= }│{7: }{8: 7 }{4:======= }│{7: }{8: }{23:---------------------------}|
|
||||
{7: }{8: 8 }{4: BBB }│{7: }{8: 8 }{4: BBB }│{7: }{8: }{23:---------------------------}|
|
||||
{7: }{8: 9 }{4: BBB }│{7: }{8: 9 }{4: BBB }│{7: }{8: }{23:---------------------------}|
|
||||
{7: }{8: 10 }{4: BBB }│{7: }{8: 10 }{4: BBB }│{7: }{8: }{23:---------------------------}|
|
||||
{7: }{8: 11 }{4:>>>>>>> branch1 }│{7: }{8: 11 }{4:>>>>>>> branch1 }│{7: }{8: }{23:---------------------------}|
|
||||
{7: }{8: 7 }{22:======= }│{7: }{8: 7 }{22:======= }│{7: }{8: }{23:---------------------------}|
|
||||
{7: }{8: 8 }{22: BBB }│{7: }{8: 8 }{22: BBB }│{7: }{8: }{23:---------------------------}|
|
||||
{7: }{8: 9 }{22: BBB }│{7: }{8: 9 }{22: BBB }│{7: }{8: }{23:---------------------------}|
|
||||
{7: }{8: 10 }{22: BBB }│{7: }{8: 10 }{22: BBB }│{7: }{8: }{23:---------------------------}|
|
||||
{7: }{8: 11 }{22:>>>>>>> branch1 }│{7: }{8: 11 }{22:>>>>>>> branch1 }│{7: }{8: }{23:---------------------------}|
|
||||
{1:~ }│{1:~ }│{1:~ }|*3
|
||||
{3:<-functional-diff-screen-1.3 [+] }{2:<est-functional-diff-screen-1.2 Xtest-functional-diff-screen-1 }|
|
||||
:2,6diffget screen-1.2 |
|
||||
@@ -108,8 +108,8 @@ describe('Diff mode screen with 3 diffs open', function()
|
||||
{7: }{8: 1 } │{7: }{8: 1 }^ │{7: }{8: 1 } |
|
||||
{7: }{8: 2 }common line │{7: }{8: 2 }common line │{7: }{8: 2 }common line |
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 3 }{22:<<<<<<< HEAD }│{7: }{8: }{23:---------------------------}|
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 4 }{4: AAA }│{7: }{8: 3 }{4: AAA }|
|
||||
{7: }{8: 3 }{4: BBB }│{7: }{8: 5 }{4: BBB }│{7: }{8: }{23:---------------------------}|
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 4 }{22: AAA }│{7: }{8: 3 }{22: AAA }|
|
||||
{7: }{8: 3 }{22: BBB }│{7: }{8: 5 }{22: BBB }│{7: }{8: }{23:---------------------------}|
|
||||
{7: }{8: 4 }{4: }{27:BBB}{4: }│{7: }{8: 6 }{4: }{27:BBB}{4: }│{7: }{8: 4 }{4: }{27:AAA}{4: }|
|
||||
{7: }{8: 5 }{4: }{27:BBB}{4: }│{7: }{8: 7 }{4: }{27:BBB}{4: }│{7: }{8: 5 }{4: }{27:AAA}{4: }|
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 8 }{22:>>>>>>> branch1 }│{7: }{8: }{23:---------------------------}|
|
||||
@@ -126,14 +126,14 @@ describe('Diff mode screen with 3 diffs open', function()
|
||||
{7: }{8: 1 } │{7: }{8: 1 } │{7: }{8: 1 }^ |
|
||||
{7: }{8: 2 }common line │{7: }{8: 2 }common line │{7: }{8: 2 }common line |
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 3 }{22:<<<<<<< HEAD }│{7: }{8: }{23:---------------------------}|
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 4 }{4: AAA }│{7: }{8: 3 }{4: AAA }|
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 5 }{4: AAA }│{7: }{8: 4 }{4: AAA }|
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 6 }{4: AAA }│{7: }{8: 5 }{4: AAA }|
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 7 }{4:======= }│{7: }{8: 6 }{4:======= }|
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 4 }{22: AAA }│{7: }{8: 3 }{22: AAA }|
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 5 }{22: AAA }│{7: }{8: 4 }{22: AAA }|
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 6 }{22: AAA }│{7: }{8: 5 }{22: AAA }|
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 7 }{22:======= }│{7: }{8: 6 }{22:======= }|
|
||||
{7: }{8: 3 } BBB │{7: }{8: 8 } BBB │{7: }{8: 7 } BBB |
|
||||
{7: }{8: 4 } BBB │{7: }{8: 9 } BBB │{7: }{8: 8 } BBB |
|
||||
{7: }{8: 5 } BBB │{7: }{8: 10 } BBB │{7: }{8: 9 } BBB |
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 11 }{4:>>>>>>> branch1 }│{7: }{8: 10 }{4:>>>>>>> branch1 }|
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 11 }{22:>>>>>>> branch1 }│{7: }{8: 10 }{22:>>>>>>> branch1 }|
|
||||
{1:~ }│{1:~ }│{1:~ }|*3
|
||||
{2:Xtest-functional-diff-screen-1.3 <est-functional-diff-screen-1.2 }{3:<st-functional-diff-screen-1 [+] }|
|
||||
:5,6diffget screen-1.2 |
|
||||
@@ -147,12 +147,12 @@ describe('Diff mode screen with 3 diffs open', function()
|
||||
{7: }{8: 1 } │{7: }{8: 1 }^ │{7: }{8: 1 } |
|
||||
{7: }{8: 2 }common line │{7: }{8: 2 }common line │{7: }{8: 2 }common line |
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 3 }{22:<<<<<<< HEAD }│{7: }{8: }{23:---------------------------}|
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 4 }{4: AAA }│{7: }{8: 3 }{4: AAA }|
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 5 }{4: AAA }│{7: }{8: 4 }{4: AAA }|
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 6 }{4: AAA }│{7: }{8: 5 }{4: AAA }|
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 7 }{4:======= }│{7: }{8: 6 }{4:======= }|
|
||||
{7: }{8: 3 }{4: BBB }│{7: }{8: 8 }{4: BBB }│{7: }{8: }{23:---------------------------}|
|
||||
{7: }{8: 4 }{4: BBB }│{7: }{8: 9 }{4: BBB }│{7: }{8: }{23:---------------------------}|
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 4 }{22: AAA }│{7: }{8: 3 }{22: AAA }|
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 5 }{22: AAA }│{7: }{8: 4 }{22: AAA }|
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 6 }{22: AAA }│{7: }{8: 5 }{22: AAA }|
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 7 }{22:======= }│{7: }{8: 6 }{22:======= }|
|
||||
{7: }{8: 3 }{22: BBB }│{7: }{8: 8 }{22: BBB }│{7: }{8: }{23:---------------------------}|
|
||||
{7: }{8: 4 }{22: BBB }│{7: }{8: 9 }{22: BBB }│{7: }{8: }{23:---------------------------}|
|
||||
{7: }{8: 5 } BBB │{7: }{8: 10 } BBB │{7: }{8: 7 } BBB |
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 11 }{22:>>>>>>> branch1 }│{7: }{8: }{23:---------------------------}|
|
||||
{1:~ }│{1:~ }│{1:~ }|*3
|
||||
@@ -160,6 +160,7 @@ describe('Diff mode screen with 3 diffs open', function()
|
||||
:6,8diffput screen-1 |
|
||||
]])
|
||||
end)
|
||||
|
||||
it('put from window 2 - part to end', function()
|
||||
feed('2<c-w>w')
|
||||
feed(':6,11diffput screen-1<cr>')
|
||||
@@ -167,14 +168,14 @@ describe('Diff mode screen with 3 diffs open', function()
|
||||
{7: }{8: 1 } │{7: }{8: 1 }^ │{7: }{8: 1 } |
|
||||
{7: }{8: 2 }common line │{7: }{8: 2 }common line │{7: }{8: 2 }common line |
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 3 }{22:<<<<<<< HEAD }│{7: }{8: }{23:---------------------------}|
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 4 }{4: AAA }│{7: }{8: 3 }{4: AAA }|
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 5 }{4: AAA }│{7: }{8: 4 }{4: AAA }|
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 6 }{4: AAA }│{7: }{8: 5 }{4: AAA }|
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 7 }{4:======= }│{7: }{8: 6 }{4:======= }|
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 4 }{22: AAA }│{7: }{8: 3 }{22: AAA }|
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 5 }{22: AAA }│{7: }{8: 4 }{22: AAA }|
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 6 }{22: AAA }│{7: }{8: 5 }{22: AAA }|
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 7 }{22:======= }│{7: }{8: 6 }{22:======= }|
|
||||
{7: }{8: 3 } BBB │{7: }{8: 8 } BBB │{7: }{8: 7 } BBB |
|
||||
{7: }{8: 4 } BBB │{7: }{8: 9 } BBB │{7: }{8: 8 } BBB |
|
||||
{7: }{8: 5 } BBB │{7: }{8: 10 } BBB │{7: }{8: 9 } BBB |
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 11 }{4:>>>>>>> branch1 }│{7: }{8: 10 }{4:>>>>>>> branch1 }|
|
||||
{7: }{8: }{23:---------------------------}│{7: }{8: 11 }{22:>>>>>>> branch1 }│{7: }{8: 10 }{22:>>>>>>> branch1 }|
|
||||
{1:~ }│{1:~ }│{1:~ }|*3
|
||||
{2:Xtest-functional-diff-screen-1.3 }{3:<est-functional-diff-screen-1.2 }{2:<st-functional-diff-screen-1 [+] }|
|
||||
:6,11diffput screen-1 |
|
||||
|
@@ -211,12 +211,17 @@ let test_values = {
|
||||
\ 'line,number'],
|
||||
\ ['', 'xxx', 'line,screenline']],
|
||||
\ 'debug': [['', 'msg', 'throw', 'beep'], ['xxx']],
|
||||
\ 'diffanchors': [['', "'a", '/foo/', "'a-1,'>,/foo,xxx/,'b,123",
|
||||
\ '1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20',
|
||||
\ '1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,'],
|
||||
\ [',', '12,,34', 'xxx', '123,xxx',
|
||||
\ '1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21']],
|
||||
\ 'diffopt': [['', 'filler', 'context:0', 'context:999', 'iblank',
|
||||
\ 'icase', 'iwhite', 'iwhiteall', 'horizontal', 'vertical',
|
||||
\ 'closeoff', 'hiddenoff', 'foldcolumn:0', 'foldcolumn:12',
|
||||
\ 'followwrap', 'internal', 'indent-heuristic', 'algorithm:myers',
|
||||
\ 'icase,iwhite', 'algorithm:minimal', 'algorithm:patience',
|
||||
\ 'algorithm:histogram', 'inline:none', 'inline:simple',
|
||||
\ 'anchor', 'algorithm:histogram', 'inline:none', 'inline:simple',
|
||||
\ 'inline:char', 'inline:word', 'inline:char,inline:word', 'linematch:5'],
|
||||
\ ['xxx', 'foldcolumn:', 'foldcolumn:x', 'foldcolumn:xxx',
|
||||
\ 'linematch:', 'linematch:x', 'linematch:xxx', 'algorithm:',
|
||||
|
@@ -2607,7 +2607,14 @@ func Test_linematch_diff()
|
||||
call WriteDiffFiles(buf, ['// abc d?',
|
||||
\ '// d?',
|
||||
\ '// d?' ],
|
||||
\ ['!',
|
||||
\ 'abc d!',
|
||||
\ 'd!'])
|
||||
call term_sendkeys(buf, ":\<CR>") " clear cmdline
|
||||
call VerifyScreenDump(buf, 'Test_linematch_diff1', {})
|
||||
|
||||
" test that filler is always implicitly set by linematch
|
||||
call term_sendkeys(buf, ":set diffopt-=filler\<CR>")
|
||||
call term_sendkeys(buf, ":\<CR>") " clear cmdline
|
||||
call VerifyScreenDump(buf, 'Test_linematch_diff1', {})
|
||||
|
||||
@@ -2820,4 +2827,440 @@ func Test_linematch_3diffs_sanity_check()
|
||||
let buf = RunVimInTerminal('-d -S Xlinematch_3diffs.vim Xfile_linematch1 Xfile_linematch2 Xfile_linematch3', {})
|
||||
call VerifyScreenDump(buf, 'Test_linematch_3diffs2', {})
|
||||
|
||||
" clean up
|
||||
call StopVimInTerminal(buf)
|
||||
endfunc
|
||||
|
||||
func Test_diffanchors()
|
||||
CheckScreendump
|
||||
call WriteDiffFiles3(0,
|
||||
\ ["anchorA1", "1", "2", "3",
|
||||
\ "100", "101", "102", "anchorB", "103", "104", "105"],
|
||||
\ ["100", "101", "102", "anchorB", "103", "104", "105",
|
||||
\ "anchorA2", "1", "2", "3"],
|
||||
\ ["100", "anchorB", "103",
|
||||
\ "anchorA3", "1", "2", "3"])
|
||||
let buf = RunVimInTerminal('-d Xdifile1 Xdifile2 Xdifile3', {})
|
||||
|
||||
" Simple diff without any anchors
|
||||
call VerifyInternal(buf, "Test_diff_anchors_00", "")
|
||||
|
||||
" Setting diffopt+=anchor or diffanchors without the other won't do anything
|
||||
call VerifyInternal(buf, "Test_diff_anchors_00", " diffopt+=anchor")
|
||||
call VerifyInternal(buf, "Test_diff_anchors_00", " dia=1/anchorA/")
|
||||
|
||||
" Use a single anchor by specifying a pattern. Test both internal and
|
||||
" external diff to make sure both paths work.
|
||||
call VerifyBoth(buf, "Test_diff_anchors_01", " dia=1/anchorA/ diffopt+=anchor")
|
||||
|
||||
" Use 2 anchors. They should be sorted by line number, so in file 2/3
|
||||
" anchorB is used before anchorA.
|
||||
call VerifyBoth(buf, "Test_diff_anchors_02", " dia=1/anchorA/,1/anchorB/ diffopt+=anchor")
|
||||
|
||||
" Set marks and specify addresses using marks and repeat the test
|
||||
call term_sendkeys(buf, ":2wincmd w\<CR>:1/anchorA/mark a\<CR>")
|
||||
call term_sendkeys(buf, ":1/anchorB/mark b\<CR>")
|
||||
call term_sendkeys(buf, ":3wincmd w\<CR>:1/anchorA/mark a\<CR>")
|
||||
call term_sendkeys(buf, ":1/anchorB/mark b\<CR>")
|
||||
call term_sendkeys(buf, ":1wincmd w\<CR>:1/anchorA/mark a\<CR>")
|
||||
call term_sendkeys(buf, ":1/anchorB/mark b\<CR>")
|
||||
|
||||
call VerifyInternal(buf, "Test_diff_anchors_01", " dia='a diffopt+=anchor")
|
||||
call VerifyInternal(buf, "Test_diff_anchors_02", " dia='a,'b diffopt+=anchor")
|
||||
|
||||
" Update marks to point somewhere else. When we first set the mark the diff
|
||||
" won't be updated until we manually invoke :diffupdate.
|
||||
call VerifyInternal(buf, "Test_diff_anchors_01", " dia='a diffopt+=anchor")
|
||||
call term_sendkeys(buf, ":1wincmd w\<CR>:1/anchorB/mark a\<CR>:")
|
||||
call term_wait(buf)
|
||||
call VerifyScreenDump(buf, "Test_diff_anchors_01", {})
|
||||
call term_sendkeys(buf, ":diffupdate\<CR>:")
|
||||
call term_wait(buf)
|
||||
call VerifyScreenDump(buf, "Test_diff_anchors_03", {})
|
||||
|
||||
" Use local diff anchors with line numbers, and repeat the same test
|
||||
call term_sendkeys(buf, ":2wincmd w\<CR>:setlocal dia=8\<CR>")
|
||||
call term_sendkeys(buf, ":3wincmd w\<CR>:setlocal dia=4\<CR>")
|
||||
call term_sendkeys(buf, ":1wincmd w\<CR>:setlocal dia=1\<CR>")
|
||||
call VerifyInternal(buf, "Test_diff_anchors_01", " diffopt+=anchor")
|
||||
call term_sendkeys(buf, ":2wincmd w\<CR>:setlocal dia=8,4\<CR>")
|
||||
call term_sendkeys(buf, ":3wincmd w\<CR>:setlocal dia=4,2\<CR>")
|
||||
call term_sendkeys(buf, ":1wincmd w\<CR>:setlocal dia=1,8\<CR>")
|
||||
call VerifyInternal(buf, "Test_diff_anchors_02", " diffopt+=anchor")
|
||||
|
||||
" Test multiple diff anchors on the same line in file 1.
|
||||
call term_sendkeys(buf, ":1wincmd w\<CR>:setlocal dia=1,1\<CR>")
|
||||
call VerifyInternal(buf, "Test_diff_anchors_04", " diffopt+=anchor")
|
||||
|
||||
" Test that if one file has fewer diff anchors than others. Vim should only
|
||||
" use the minimum in this case.
|
||||
call term_sendkeys(buf, ":1wincmd w\<CR>:setlocal dia=8\<CR>")
|
||||
call VerifyInternal(buf, "Test_diff_anchors_05", " diffopt+=anchor")
|
||||
|
||||
" $+1 should anchor everything past the last line
|
||||
call term_sendkeys(buf, ":1wincmd w\<CR>:setlocal dia=$+1\<CR>")
|
||||
call VerifyInternal(buf, "Test_diff_anchors_06", " diffopt+=anchor")
|
||||
|
||||
" Sorting of diff anchors should work with multiple anchors
|
||||
call term_sendkeys(buf, ":1wincmd w\<CR>:setlocal dia=1,10,8,2\<CR>")
|
||||
call term_sendkeys(buf, ":2wincmd w\<CR>:setlocal dia=1,2,3,4\<CR>")
|
||||
call term_sendkeys(buf, ":3wincmd w\<CR>:setlocal dia=4,3,2,1\<CR>")
|
||||
call VerifyInternal(buf, "Test_diff_anchors_07", " diffopt+=anchor")
|
||||
|
||||
" Intentionally set an invalid anchor with wrong line number. Should fall
|
||||
" back to treat it as if no anchors are used at all.
|
||||
call term_sendkeys(buf, ":1wincmd w\<CR>:setlocal dia=1,10,8,2,1000 | silent! diffupdate\<CR>:")
|
||||
call VerifyScreenDump(buf, "Test_diff_anchors_00", {})
|
||||
|
||||
call StopVimInTerminal(buf)
|
||||
endfunc
|
||||
|
||||
" Test that scrollbind and topline calculations work correctly, even when diff
|
||||
" anchors create adjacent diff blocks which complicates the calculations.
|
||||
func Test_diffanchors_scrollbind_topline()
|
||||
CheckScreendump
|
||||
|
||||
" Simple overlapped line anchored to be adjacent to each other
|
||||
call WriteDiffFiles(0,
|
||||
\ ["anchor1", "diff1a", "anchor2"],
|
||||
\ ["anchor1", "diff2a", "anchor2"])
|
||||
let buf = RunVimInTerminal('-d Xdifile1 Xdifile2', {})
|
||||
|
||||
call term_sendkeys(buf, ":1wincmd w\<CR>:setlocal dia=2\<CR>")
|
||||
call term_sendkeys(buf, ":2wincmd w\<CR>:setlocal dia=3\<CR>")
|
||||
|
||||
call VerifyInternal(buf, "Test_diff_anchors_scrollbind_topline_01", " diffopt+=anchor")
|
||||
call term_sendkeys(buf, "\<Esc>\<C-E>")
|
||||
call VerifyScreenDump(buf, "Test_diff_anchors_scrollbind_topline_02", {})
|
||||
call term_sendkeys(buf, "\<C-E>")
|
||||
call VerifyScreenDump(buf, "Test_diff_anchors_scrollbind_topline_03", {})
|
||||
call term_sendkeys(buf, "\<C-E>")
|
||||
call VerifyScreenDump(buf, "Test_diff_anchors_scrollbind_topline_04", {})
|
||||
|
||||
" Also test no-filler
|
||||
call term_sendkeys(buf, "gg")
|
||||
call VerifyInternal(buf, "Test_diff_anchors_scrollbind_topline_05", " diffopt+=anchor diffopt-=filler")
|
||||
call term_sendkeys(buf, "\<Esc>\<C-E>")
|
||||
call VerifyScreenDump(buf, "Test_diff_anchors_scrollbind_topline_06", {})
|
||||
call term_sendkeys(buf, "\<C-E>")
|
||||
call VerifyScreenDump(buf, "Test_diff_anchors_scrollbind_topline_07", {})
|
||||
|
||||
call StopVimInTerminal(buf)
|
||||
endfunc
|
||||
|
||||
func Test_diffanchors_scrollbind_topline2()
|
||||
CheckScreendump
|
||||
|
||||
" More-complicated case with 3 files and multiple overlapping diff blocks
|
||||
call WriteDiffFiles3(0,
|
||||
\ ["anchor1"],
|
||||
\ ["diff2a", "diff2b", "diff2c", "diff2d", "anchor2"],
|
||||
\ ["diff3a", "diff3c", "diff3d", "anchor3", "diff3e"])
|
||||
let buf = RunVimInTerminal('-d Xdifile1 Xdifile2 Xdifile3', {})
|
||||
|
||||
call term_sendkeys(buf, ":1wincmd w\<CR>:setlocal dia=1,1,2\<CR>")
|
||||
call term_sendkeys(buf, ":2wincmd w\<CR>:setlocal dia=3,5,6\<CR>")
|
||||
call term_sendkeys(buf, ":3wincmd w\<CR>:setlocal dia=2,4,5\<CR>")
|
||||
|
||||
call VerifyInternal(buf, "Test_diff_anchors_scrollbind_topline_08", " diffopt+=anchor")
|
||||
call term_sendkeys(buf, ":1wincmd w\<CR>")
|
||||
call term_sendkeys(buf, "\<C-E>")
|
||||
call VerifyScreenDump(buf, "Test_diff_anchors_scrollbind_topline_09", {})
|
||||
call term_sendkeys(buf, "\<C-E>")
|
||||
call VerifyScreenDump(buf, "Test_diff_anchors_scrollbind_topline_10", {})
|
||||
call term_sendkeys(buf, "\<C-E>")
|
||||
call VerifyScreenDump(buf, "Test_diff_anchors_scrollbind_topline_11", {})
|
||||
call term_sendkeys(buf, "\<C-E>")
|
||||
call VerifyScreenDump(buf, "Test_diff_anchors_scrollbind_topline_12", {})
|
||||
|
||||
" Also test no-filler
|
||||
call term_sendkeys(buf, ":3wincmd w\<CR>gg")
|
||||
call VerifyInternal(buf, "Test_diff_anchors_scrollbind_topline_13", " diffopt+=anchor diffopt-=filler")
|
||||
call term_sendkeys(buf, "\<Esc>\<C-E>")
|
||||
call VerifyScreenDump(buf, "Test_diff_anchors_scrollbind_topline_14", {})
|
||||
call term_sendkeys(buf, "\<C-E>")
|
||||
call VerifyScreenDump(buf, "Test_diff_anchors_scrollbind_topline_15", {})
|
||||
call term_sendkeys(buf, "\<C-E>")
|
||||
call VerifyScreenDump(buf, "Test_diff_anchors_scrollbind_topline_16", {})
|
||||
call term_sendkeys(buf, "\<C-E>")
|
||||
call VerifyScreenDump(buf, "Test_diff_anchors_scrollbind_topline_17", {})
|
||||
|
||||
call StopVimInTerminal(buf)
|
||||
endfunc
|
||||
|
||||
" Test that setting 'diffanchors' will update the diff.
|
||||
func Test_diffanchors_option_set_update()
|
||||
set diffanchors='a diffopt=internal,filler,anchor
|
||||
|
||||
" Set up 3 tabs that share some buffers, and set up marks on each of them.
|
||||
" We want to make sure only relevant tabs are updated if buffer-local diff
|
||||
" anchors are updated, but all tabs should refresh if global diff anchors
|
||||
" are updated (see diffanchors_changed() in code).
|
||||
|
||||
" Tab 1. A buffer here will be reused.
|
||||
call setline(1, range(1, 10))
|
||||
3mark a
|
||||
4mark b
|
||||
diffthis
|
||||
new
|
||||
call setline(1, range(21, 25))
|
||||
let buf = bufnr()
|
||||
1mark a
|
||||
2mark b
|
||||
diffthis
|
||||
call assert_equal(2, diff_filler(1))
|
||||
call assert_equal(0, diff_filler(2))
|
||||
|
||||
" Tab 2. "buf" is here but intentionally not participating in diff.
|
||||
tabnew
|
||||
exec 'buf ' .. buf
|
||||
diffoff
|
||||
new
|
||||
call setline(1, range(31, 40))
|
||||
8mark a
|
||||
9mark b
|
||||
diffthis
|
||||
new
|
||||
call setline(1, range(41, 50))
|
||||
5mark a
|
||||
6mark b
|
||||
diffthis
|
||||
|
||||
call assert_equal(3, diff_filler(5))
|
||||
call assert_equal(0, diff_filler(6))
|
||||
call assert_equal(0, diff_filler(7))
|
||||
|
||||
" Update mark a location, and check that the diff has *not* updated. When
|
||||
" updating marks diff's won't automatically update.
|
||||
7mark a
|
||||
call assert_equal(3, diff_filler(5))
|
||||
call assert_equal(0, diff_filler(6))
|
||||
call assert_equal(0, diff_filler(7))
|
||||
|
||||
" Tab 3. "buf" is used here and also in a diff.
|
||||
tabnew
|
||||
call setline(1, range(51, 65))
|
||||
10mark a
|
||||
11mark b
|
||||
diffthis
|
||||
exec 'sbuffer ' .. buf
|
||||
diffthis
|
||||
|
||||
" Change local diff anchor of "buf" to mark b
|
||||
setlocal diffanchors='b
|
||||
|
||||
" Tab 1 should immediately update the diff to use mark b because the buf
|
||||
" local diff anchor has been changed in "buf".
|
||||
1tabnext
|
||||
call assert_equal(0, diff_filler(1))
|
||||
call assert_equal(1, diff_filler(2))
|
||||
|
||||
" Tab 2 should not immediately update because "buf" is not a diff buffer
|
||||
" here.
|
||||
2tabnext
|
||||
call assert_equal(3, diff_filler(5))
|
||||
call assert_equal(0, diff_filler(6))
|
||||
call assert_equal(0, diff_filler(7))
|
||||
|
||||
" Manual diff update would refresh the diff since we previously changed mark
|
||||
" a's location above.
|
||||
diffupdate
|
||||
call assert_equal(0, diff_filler(5))
|
||||
call assert_equal(0, diff_filler(6))
|
||||
call assert_equal(1, diff_filler(7))
|
||||
|
||||
" Go back to tab 1. Reset diff anchor to global value and make sure it uses
|
||||
" mark a again.
|
||||
1tabnext
|
||||
set diffanchors<
|
||||
call assert_equal(2, diff_filler(1))
|
||||
call assert_equal(0, diff_filler(2))
|
||||
|
||||
" Now, change the global diff anchor to mark b. This should affect all tabs
|
||||
" including tab 2 which should update automatically.
|
||||
set diffanchors='b
|
||||
call assert_equal(0, diff_filler(1))
|
||||
call assert_equal(2, diff_filler(2))
|
||||
|
||||
2tabnext
|
||||
call assert_equal(0, diff_filler(5))
|
||||
call assert_equal(3, diff_filler(6))
|
||||
call assert_equal(0, diff_filler(7))
|
||||
|
||||
%bw!
|
||||
set diffopt&
|
||||
set diffanchors&
|
||||
endfunc
|
||||
|
||||
" Test that using diff anchors with window/buffer-local addresses will work as
|
||||
" expected and use the relevant window/buffer instead of curbuf/curwin.
|
||||
func Test_diffanchors_buf_win_local_addresses()
|
||||
" Win 1-3 point to buffer 1. Set up different window-specific jump history
|
||||
" Win 2 is the one we activate diff mode on.
|
||||
call setline(1, range(1, 15))
|
||||
norm 2gg
|
||||
norm 3gg
|
||||
|
||||
split
|
||||
norm 4gg
|
||||
norm 5gg
|
||||
|
||||
split
|
||||
norm 11gg
|
||||
norm 12gg
|
||||
call setline(10, 'new text 1') " update the '. mark to line 10
|
||||
|
||||
" Win 4 points to buffer 2
|
||||
botright vert new
|
||||
call setline(1, range(101, 110))
|
||||
norm 8gg
|
||||
norm 9gg
|
||||
call setline(3, 'new text 2') " update the '. mark to line 3
|
||||
|
||||
2wincmd w
|
||||
diffthis
|
||||
4wincmd w
|
||||
diffthis
|
||||
|
||||
" Test buffer-local marks using '. Should be anchored to lines 10 / 3.
|
||||
set diffopt=internal,filler,anchor
|
||||
set diffanchors='.
|
||||
4wincmd w
|
||||
call assert_equal(7, diff_filler(3))
|
||||
|
||||
" Test window-local marks using '' Should be anchored to lines 4 / 8.
|
||||
" Note that windows 1 & 3 point to the buffer being diff'ed but are not used
|
||||
" for diffing themselves and therefore should not be used. Windows 2 & 4
|
||||
" should be used.
|
||||
set diffanchors=''
|
||||
2wincmd w
|
||||
call assert_equal(4, diff_filler(4))
|
||||
|
||||
" Also test "." for the current cursor position, which is also
|
||||
" window-specific. Make sure the cursor position at the longer file doesn't
|
||||
" result in the other file using out of bounds line number.
|
||||
4wincmd w
|
||||
norm G
|
||||
2wincmd w
|
||||
norm G
|
||||
set diffanchors=.
|
||||
diffupdate
|
||||
4wincmd w
|
||||
call assert_equal(5, diff_filler(10))
|
||||
|
||||
%bw!
|
||||
set diffopt&
|
||||
set diffanchors&
|
||||
endfunc
|
||||
|
||||
" Test diff anchors error handling for anchors that fail to resolve to a line.
|
||||
" These are not handled during option parsing because they depend on the
|
||||
" specifics of the buffer at diff time.
|
||||
func Test_diffanchors_invalid()
|
||||
call setline(1, range(1, 5))
|
||||
new
|
||||
call setline(1, range(11, 20))
|
||||
set diffopt=internal,filler,anchor
|
||||
windo diffthis
|
||||
1wincmd w
|
||||
|
||||
" Line numbers that are out of bounds should be an error
|
||||
set diffanchors=0
|
||||
call assert_fails('diffupdate', 'E16:')
|
||||
set diffanchors=1
|
||||
diffupdate
|
||||
set diffanchors=$
|
||||
diffupdate
|
||||
set diffanchors=$+1
|
||||
diffupdate
|
||||
set diffanchors=$+2
|
||||
call assert_fails('diffupdate', 'E16:')
|
||||
|
||||
" Test that non-existent marks in any one buffer will be detected
|
||||
set diffanchors='a
|
||||
call assert_fails('diffupdate', 'E20:')
|
||||
2mark a
|
||||
call assert_fails('diffupdate', 'E20:')
|
||||
|
||||
set diffanchors=1
|
||||
setlocal diffanchors='a
|
||||
diffupdate
|
||||
|
||||
set diffanchors<
|
||||
windo 2mark a
|
||||
set diffanchors='b
|
||||
call assert_fails('diffupdate', 'E20:')
|
||||
set diffanchors='a
|
||||
diffupdate
|
||||
|
||||
" File marks are ok to use for anchors only if it is in the same file
|
||||
1wincmd w
|
||||
3mark C
|
||||
setlocal diffanchors='C
|
||||
diffupdate
|
||||
set diffanchors='C
|
||||
call assert_fails('diffupdate', 'E20:')
|
||||
|
||||
" Buffer-local marks also can only be used in buffers that have them.
|
||||
set diffanchors=1
|
||||
exec "norm 1ggVj\<Esc>"
|
||||
setlocal diffanchors='<
|
||||
diffupdate
|
||||
set diffanchors='<
|
||||
call assert_fails('diffupdate', 'E20:')
|
||||
|
||||
" Pattern search that failed will be an error too
|
||||
let @/='orig_search_pat'
|
||||
set diffanchors=1/5/
|
||||
diffupdate
|
||||
call assert_equal('orig_search_pat', @/) " also check we don't pollute the search register
|
||||
set diffanchors=1/does_not_exist/
|
||||
call assert_fails('diffupdate', 'E1550:')
|
||||
call assert_equal('orig_search_pat', @/)
|
||||
|
||||
%bw!
|
||||
set diffopt&
|
||||
set diffanchors&
|
||||
endfunc
|
||||
|
||||
" Test diffget/diffput behaviors when using diff anchors which could create
|
||||
" adjacent diff blocks.
|
||||
func Test_diffget_diffput_diffanchors()
|
||||
set diffanchors=1/anchor/
|
||||
set diffopt=internal,filler,anchor
|
||||
|
||||
call setline(1, ['1', 'anchor1', '4'])
|
||||
diffthis
|
||||
new
|
||||
call setline(1, ['2', '3', 'anchor2', '4', '5'])
|
||||
diffthis
|
||||
wincmd w
|
||||
|
||||
" Test using no-range diffget. It should grab the closest diff block only,
|
||||
" even if there are multiple adjacent blocks.
|
||||
2
|
||||
diffget
|
||||
call assert_equal(['1', 'anchor2', '4'], getline(1, '$'))
|
||||
diffget
|
||||
call assert_equal(['2', '3', 'anchor2', '4'], getline(1, '$'))
|
||||
|
||||
" Test using a range to get/put all the adjacent diff blocks.
|
||||
1,$delete
|
||||
call setline(1, ['anchor1', '4'])
|
||||
0,1 diffget
|
||||
call assert_equal(['2', '3', 'anchor2', '4'], getline(1, '$'))
|
||||
|
||||
1,$delete
|
||||
call setline(1, ['anchor1', '4'])
|
||||
0,$+1 diffget
|
||||
call assert_equal(['2', '3', 'anchor2', '4', '5'], getline(1, '$'))
|
||||
|
||||
1,$delete
|
||||
call setline(1, ['anchor1', '4'])
|
||||
0,1 diffput
|
||||
wincmd w
|
||||
call assert_equal(['anchor1', '4', '5'], getline(1,'$'))
|
||||
|
||||
%bw!
|
||||
set diffopt&
|
||||
set diffanchors&
|
||||
|
@@ -299,13 +299,13 @@ endfun
|
||||
|
||||
func Test_set_completion()
|
||||
call feedkeys(":set di\<C-A>\<C-B>\"\<CR>", 'tx')
|
||||
call assert_equal('"set dictionary diff diffexpr diffopt digraph directory display', @:)
|
||||
call assert_equal('"set dictionary diff diffanchors diffexpr diffopt digraph directory display', @:)
|
||||
|
||||
call feedkeys(":setlocal di\<C-A>\<C-B>\"\<CR>", 'tx')
|
||||
call assert_equal('"setlocal dictionary diff diffexpr diffopt digraph directory display', @:)
|
||||
call assert_equal('"setlocal dictionary diff diffanchors diffexpr diffopt digraph directory display', @:)
|
||||
|
||||
call feedkeys(":setglobal di\<C-A>\<C-B>\"\<CR>", 'tx')
|
||||
call assert_equal('"setglobal dictionary diff diffexpr diffopt digraph directory display', @:)
|
||||
call assert_equal('"setglobal dictionary diff diffanchors diffexpr diffopt digraph directory display', @:)
|
||||
|
||||
" Expand boolean options. When doing :set no<Tab> Vim prefixes the option
|
||||
" names with "no".
|
||||
@@ -357,7 +357,7 @@ func Test_set_completion()
|
||||
call assert_match(' ./samples/.* ./summarize.vim', @:)
|
||||
|
||||
call feedkeys(":set tags=./\\\\ dif\<C-A>\<C-B>\"\<CR>", 'tx')
|
||||
call assert_equal('"set tags=./\\ diff diffexpr diffopt', @:)
|
||||
call assert_equal('"set tags=./\\ diff diffanchors diffexpr diffopt', @:)
|
||||
|
||||
" Expand files with spaces/commas in them. Make sure we delimit correctly.
|
||||
"
|
||||
@@ -740,7 +740,7 @@ func Test_set_completion_string_values()
|
||||
set diffopt=
|
||||
call assert_equal([], getcompletion('set diffopt-=', 'cmdline'))
|
||||
" Test all possible values
|
||||
call assert_equal(['filler', 'context:', 'iblank', 'icase', 'iwhite', 'iwhiteall', 'iwhiteeol', 'horizontal',
|
||||
call assert_equal(['filler', 'anchor', 'context:', 'iblank', 'icase', 'iwhite', 'iwhiteall', 'iwhiteeol', 'horizontal',
|
||||
\ 'vertical', 'closeoff', 'hiddenoff', 'foldcolumn:', 'followwrap', 'internal', 'indent-heuristic', 'algorithm:', 'inline:', 'linematch:'],
|
||||
\ getcompletion('set diffopt=', 'cmdline'))
|
||||
set diffopt&
|
||||
|
Reference in New Issue
Block a user