Enable new diff option linematch (#14537)

Co-authored-by: Lewis Russell <me@lewisr.dev>
This commit is contained in:
Jonathon
2022-11-04 05:07:22 -04:00
committed by GitHub
parent cc5b7368d6
commit 04fbb1de44
11 changed files with 2049 additions and 81 deletions

View File

@@ -677,6 +677,9 @@ vim.diff({a}, {b}, {opts}) *vim.diff()*
• "unified": (default) String in unified format.
• "indices": Array of hunk locations.
Note: This option is ignored if `on_hunk` is used.
• `linematch` (boolean): Run linematch on the resulting hunks
from xdiff. Requires `result_type = indices`, ignored
otherwise.
• `algorithm` (string):
Diff algorithm to use. Values:
• "myers" the default algorithm

View File

@@ -2017,6 +2017,16 @@ A jump table for the options with a short description can be found at |Q_op|.
Use the indent heuristic for the internal
diff library.
linematch:{n} Enable a second stage diff on each generated
hunk in order to align lines. When the total
number of lines in a hunk exceeds {n}, the
second stage diff will not be performed as
very large hunks can cause noticeable lag. A
recommended setting is "linematch:60", as this
will enable alignment for a 2 buffer diff with
hunks of up to 30 lines each, or a 3 buffer
diff with hunks of up to 20 lines each.
algorithm:{text} Use the specified diff algorithm with the
internal diff engine. Currently supported
algorithms are:

View File

@@ -894,6 +894,8 @@ struct diffblock_S {
diff_T *df_next;
linenr_T df_lnum[DB_COUNT]; // line number in buffer
linenr_T df_count[DB_COUNT]; // nr of inserted/changed lines
bool is_linematched; // has the linematch algorithm ran on this diff hunk to divide it into
// smaller diff hunks?
};
#define SNAP_HELP_IDX 0

View File

@@ -27,6 +27,7 @@
#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/garray.h"
#include "nvim/linematch.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
@@ -62,10 +63,12 @@ static bool diff_need_update = false; // ex_diffupdate needs to be called
#define DIFF_INTERNAL 0x200 // use internal xdiff algorithm
#define DIFF_CLOSE_OFF 0x400 // diffoff when closing window
#define DIFF_FOLLOWWRAP 0x800 // follow the wrap option
#define DIFF_LINEMATCH 0x1000 // match most similar lines within diff
#define ALL_WHITE_DIFF (DIFF_IWHITE | DIFF_IWHITEALL | DIFF_IWHITEEOL)
static int diff_flags = DIFF_INTERNAL | DIFF_FILLER | DIFF_CLOSE_OFF;
static long diff_algorithm = 0;
static int linematch_lines = 0;
#define LBUFLEN 50 // length of line in diff file
@@ -362,7 +365,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T
if (last >= line1 - 1) {
// 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) {
if (dp->df_lnum[idx] - (deleted + inserted != 0) > line2 - dp->is_linematched) {
if (amount_after == 0) {
// nothing left to change
break;
@@ -454,7 +457,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)
if ((dprev != NULL) && !dp->is_linematched
&& (dprev->df_lnum[idx] + dprev->df_count[idx] == dp->df_lnum[idx])) {
int i;
for (i = 0; i < DB_COUNT; i++) {
@@ -513,6 +516,7 @@ static diff_T *diff_alloc_new(tabpage_T *tp, diff_T *dprev, diff_T *dp)
{
diff_T *dnew = xmalloc(sizeof(*dnew));
dnew->is_linematched = false;
dnew->df_next = dp;
if (dprev == NULL) {
tp->tp_first_diff = dnew;
@@ -727,12 +731,16 @@ static void clear_diffout(diffout_T *dout)
/// @param din
///
/// @return FAIL for failure.
static int diff_write_buffer(buf_T *buf, mmfile_t *m)
static int diff_write_buffer(buf_T *buf, mmfile_t *m, linenr_T start, linenr_T end)
{
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 = 1; lnum <= buf->b_ml.ml_line_count; lnum++) {
for (linenr_T lnum = start; lnum <= end; lnum++) {
len += strlen(ml_get_buf(buf, lnum, false)) + 1;
}
char *ptr = try_malloc(len);
@@ -753,7 +761,7 @@ static int diff_write_buffer(buf_T *buf, mmfile_t *m)
m->size = (long)len;
len = 0;
for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) {
for (linenr_T lnum = start; lnum <= end; lnum++) {
char *s = ml_get_buf(buf, lnum, false);
if (diff_flags & DIFF_ICASE) {
while (*s != NUL) {
@@ -792,7 +800,7 @@ static int diff_write_buffer(buf_T *buf, mmfile_t *m)
static int diff_write(buf_T *buf, diffin_T *din)
{
if (din->din_fname == NULL) {
return diff_write_buffer(buf, &din->din_mmfile);
return diff_write_buffer(buf, &din->din_mmfile, 1, -1);
}
// Always use 'fileformat' set to "unix".
@@ -1784,6 +1792,294 @@ void diff_clear(tabpage_T *tp)
tp->tp_first_diff = NULL;
}
///
/// return true if the options are set to use diff linematch
///
bool diff_linematch(diff_T *dp)
{
if (!(diff_flags & DIFF_LINEMATCH)) {
return false;
}
// are there more than three diff buffers?
int tsize = 0;
for (int i = 0; i < DB_COUNT; i++) {
if (curtab->tp_diffbuf[i] != NULL) {
// for the rare case (bug?) that the count of a diff block is negative, do
// not run the algorithm because this will try to allocate a negative
// amount of space and crash
if (dp->df_count[i] < 0) {
return false;
}
tsize += dp->df_count[i];
}
}
// avoid allocating a huge array because it will lag
return tsize <= linematch_lines;
}
static int get_max_diff_length(const diff_T *dp)
{
int maxlength = 0;
for (int k = 0; k < DB_COUNT; k++) {
if (curtab->tp_diffbuf[k] != NULL) {
if (dp->df_count[k] > maxlength) {
maxlength = dp->df_count[k];
}
}
}
return maxlength;
}
static void find_top_diff_block(diff_T **thistopdiff, diff_T **nextblockblock, int fromidx,
int topline)
{
diff_T *topdiff = NULL;
diff_T *localtopdiff = NULL;
int topdiffchange = 0;
for (topdiff = curtab->tp_first_diff; topdiff != NULL; topdiff = topdiff->df_next) {
// set the top of the current overlapping diff block set as we
// iterate through all of the sets of overlapping diff blocks
if (!localtopdiff || topdiffchange) {
localtopdiff = topdiff;
topdiffchange = 0;
}
// check if the fromwin topline is matched by the current diff. if so, set it to the top of the diff block
if (topline >= topdiff->df_lnum[fromidx] && topline <=
(topdiff->df_lnum[fromidx] + topdiff->df_count[fromidx])) {
// this line is inside the current diff block, so we will save the
// top block of the set of blocks to refer to later
if ((*thistopdiff) == NULL) {
(*thistopdiff) = localtopdiff;
}
}
// check if the next set of overlapping diff blocks is next
if (!(topdiff->df_next && (topdiff->df_next->df_lnum[fromidx] ==
(topdiff->df_lnum[fromidx] + topdiff->df_count[fromidx])))) {
// mark that the next diff block is belongs to a different set of
// overlapping diff blocks
topdiffchange = 1;
// 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;
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;
int isfiller = 0;
while (virtual_lines_passed) {
if (ch_virtual_lines) {
virtual_lines_passed--;
ch_virtual_lines--;
if (!isfiller) {
(*curlinenum_to)++;
} else {
(*linesfiller)++;
}
} else {
(*linesfiller) = 0;
ch_virtual_lines = get_max_diff_length(curdif);
isfiller = (curdif->df_count[toidx] ? 0 : 1);
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;
}
}
}
}
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
diff_T *thistopdiff = NULL;
diff_T *nextblockblock = NULL;
int virtual_lines_passed = 0;
find_top_diff_block(&thistopdiff, &nextblockblock, fromidx, from_topline);
// count the virtual lines that have been passed
diff_T *curdif = thistopdiff;
while (curdif && (curdif->df_lnum[fromidx] + curdif->df_count[fromidx])
<= from_topline) {
virtual_lines_passed += get_max_diff_length(curdif);
curdif = curdif->df_next;
}
if (curdif != nextblockblock) {
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
curdif = thistopdiff;
int curlinenum_to = thistopdiff->df_lnum[toidx];
int linesfiller = 0;
count_filler_lines_and_topline(&curlinenum_to, &linesfiller,
thistopdiff, toidx, virtual_lines_passed);
// 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;
}
break;
}
}
(*topfill) = maxfiller - linesfiller;
(*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)
{
// get the start line number here in each diff buffer, and then increment
int line_numbers[DB_COUNT];
int outputmap[DB_COUNT];
size_t ndiffs = 0;
for (int i = 0; i < DB_COUNT; i++) {
if (curtab->tp_diffbuf[i] != NULL) {
line_numbers[i] = dp->df_lnum[i];
dp->df_count[i] = 0;
// Keep track of the index of the diff buffer we are using here.
// We will use this to write the output of the algorithm to
// diff_T structs at the correct indexes
outputmap[ndiffs] = i;
ndiffs++;
}
}
// write the diffs starting with the current diff block
diff_T *dp_s = dp;
for (size_t i = 0; i < decisions_length; i++) {
// Don't allocate on first iter since we can reuse the initial diffblock
if (i != 0 && (decisions[i - 1] != decisions[i])) {
// create new sub diff blocks to segment the original diff block which we
// further divided by running the linematch algorithm
dp_s = diff_alloc_new(curtab, dp_s, dp_s->df_next);
dp_s->is_linematched = true;
for (int j = 0; j < DB_COUNT; j++) {
if (curtab->tp_diffbuf[j] != NULL) {
dp_s->df_lnum[j] = line_numbers[j];
dp_s->df_count[j] = 0;
}
}
}
for (size_t j = 0; j < ndiffs; j++) {
if (decisions[i] & (1 << j)) {
// will need to use the map here
dp_s->df_count[outputmap[j]]++;
line_numbers[outputmap[j]]++;
}
}
}
dp->is_linematched = true;
}
static void run_linematch_algorithm(diff_T *dp)
{
// define buffers for diff algorithm
mmfile_t diffbufs_mm[DB_COUNT];
const char *diffbufs[DB_COUNT];
int diff_length[DB_COUNT];
size_t ndiffs = 0;
for (int i = 0; i < DB_COUNT; i++) {
if (curtab->tp_diffbuf[i] != NULL) {
// write the contents of the entire buffer to
// diffbufs_mm[diffbuffers_count]
diff_write_buffer(curtab->tp_diffbuf[i], &diffbufs_mm[ndiffs],
dp->df_lnum[i], dp->df_lnum[i] + dp->df_count[i] - 1);
// we want to get the char* to the diff buffer that was just written
// we add it to the array of char*, diffbufs
diffbufs[ndiffs] = diffbufs_mm[ndiffs].ptr;
// keep track of the length of this diff block to pass it to the linematch
// algorithm
diff_length[ndiffs] = dp->df_count[i];
// increment the amount of diff buffers we are passing to the algorithm
ndiffs++;
}
}
// we will get the output of the linematch algorithm in the format of an array
// of integers (*decisions) and the length of that array (decisions_length)
int *decisions = NULL;
const bool iwhite = (diff_flags & (DIFF_IWHITEALL | DIFF_IWHITE)) > 0;
size_t decisions_length = linematch_nbuffers(diffbufs, diff_length, ndiffs, &decisions, iwhite);
for (size_t i = 0; i < ndiffs; i++) {
XFREE_CLEAR(diffbufs_mm[i].ptr);
}
apply_linematch_results(dp, decisions_length, decisions);
xfree(decisions);
}
/// Check diff status for line "lnum" in buffer "buf":
///
/// Returns 0 for nothing special
@@ -1795,9 +2091,10 @@ void diff_clear(tabpage_T *tp)
///
/// @param wp
/// @param lnum
/// @param[out] linestatus
///
/// @return diff status.
int diff_check(win_T *wp, linenr_T lnum)
int diff_check_with_linestatus(win_T *wp, linenr_T lnum, int *linestatus)
{
buf_T *buf = wp->w_buffer;
@@ -1840,6 +2137,14 @@ int diff_check(win_T *wp, linenr_T lnum)
return 0;
}
if (!dp->is_linematched && diff_linematch(dp)) {
run_linematch_algorithm(dp);
}
if (dp->is_linematched) {
return linematched_filler_lines(dp, idx, lnum, linestatus);
}
if (lnum < dp->df_lnum[idx] + dp->df_count[idx]) {
int zero = false;
@@ -1894,15 +2199,16 @@ int diff_check(win_T *wp, linenr_T lnum)
// Insert filler lines above the line just below the change. Will return
// 0 when this buf had the max count.
linenr_T maxcount = 0;
for (int i = 0; i < DB_COUNT; i++) {
if ((curtab->tp_diffbuf[i] != NULL) && (dp->df_count[i] > maxcount)) {
maxcount = dp->df_count[i];
}
}
int maxcount = get_max_diff_length(dp);
return maxcount - dp->df_count[idx];
}
/// See diff_check_with_linestatus
int diff_check(win_T *wp, linenr_T lnum)
{
return diff_check_with_linestatus(wp, lnum, NULL);
}
/// Compare two entries in diff "dp" and return true if they are equal.
///
/// @param dp diff
@@ -2062,46 +2368,51 @@ 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]) {
// Inside a change: compute filler lines. With three or more
// buffers we need to know the largest count.
linenr_T max_count = 0;
if (diff_flags & DIFF_LINEMATCH) {
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;
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];
}
}
} 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 (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]) {
// 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;
// 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;
}
}
}
}
@@ -2136,6 +2447,7 @@ void diff_set_topline(win_T *fromwin, win_T *towin)
int diffopt_changed(void)
{
int diff_context_new = 6;
int linematch_lines_new = 0;
int diff_flags_new = 0;
int diff_foldcolumn_new = 2;
long diff_algorithm_new = 0;
@@ -2205,6 +2517,10 @@ int diffopt_changed(void)
} else {
return FAIL;
}
} else if ((STRNCMP(p, "linematch:", 10) == 0) && ascii_isdigit(p[10])) {
p += 10;
linematch_lines_new = getdigits_int(&p, false, linematch_lines_new);
diff_flags_new |= DIFF_LINEMATCH;
}
if ((*p != ',') && (*p != NUL)) {
@@ -2233,6 +2549,7 @@ int diffopt_changed(void)
diff_flags = diff_flags_new;
diff_context = diff_context_new == 0 ? 1 : diff_context_new;
linematch_lines = linematch_lines_new;
diff_foldcolumn = diff_foldcolumn_new;
diff_algorithm = diff_algorithm_new;
@@ -2300,6 +2617,13 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
break;
}
}
if (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)) {
xfree(line_org);
@@ -2674,6 +2998,17 @@ static void diffgetput(const int addr_count, const int idx_cur, const int idx_fr
diff_T *dprev = NULL;
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
&& 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;
dp = dp->df_next;
}
}
if (dp->df_lnum[idx_cur] > line2 + off) {
// past the range that was specified
break;

View File

@@ -804,9 +804,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
int bg_attr = win_bg_attr(wp);
filler_lines = diff_check(wp, lnum);
if (filler_lines < 0) {
if (filler_lines == -1) {
int linestatus = 0;
filler_lines = diff_check_with_linestatus(wp, lnum, &linestatus);
if (filler_lines < 0 || linestatus < 0) {
if (filler_lines == -1 || linestatus == -1) {
if (diff_find_change(wp, lnum, &change_start, &change_end)) {
diff_hlf = HLF_ADD; // added line
} else if (change_start == 0) {
@@ -817,7 +818,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
} else {
diff_hlf = HLF_ADD; // added line
}
filler_lines = 0;
if (linestatus == 0) {
filler_lines = 0;
}
area_highlighting = true;
}
VirtLines virt_lines = KV_INITIAL_VALUE;

View File

@@ -1573,9 +1573,10 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|| changedtick != buf_get_changedtick(curbuf)
|| fnum != curbuf->b_fnum) {
// New line, buffer, change: need to get the values.
int filler_lines = diff_check(curwin, lnum);
if (filler_lines < 0) {
if (filler_lines == -1) {
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) {
change_start = MAXCOL;
change_end = -1;
if (diff_find_change(curwin, lnum, &change_start, &change_end)) {

377
src/nvim/linematch.c Normal file
View File

@@ -0,0 +1,377 @@
#include "nvim/linematch.h"
#include "nvim/memory.h"
#include "nvim/vim.h"
// struct for running the diff linematch algorithm
typedef struct {
int *df_decision; // to keep track of this path traveled
int df_lev_score; // to keep track of the total score of this path
size_t df_path_idx; // current index of this path
} diffcmppath_T;
#define LN_MAX_BUFS 8
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "linematch.c.generated.h"
#endif
static size_t line_len(const char *s)
{
char *end = strchr(s, '\n');
if (end) {
return (size_t)(end - s);
}
return STRLEN(s);
}
/// Same as matching_chars but ignore whitespace
///
/// @param s1
/// @param s2
static int matching_chars_iwhite(const char *s1, const char *s2)
{
// the newly processed strings that will be compared
// delete the white space characters, and/or replace all upper case with lower
char *strsproc[2];
const char *strsorig[2] = { s1, s2 };
for (int k = 0; k < 2; k++) {
size_t d = 0;
size_t i = 0;
size_t slen = line_len(strsorig[k]);
strsproc[k] = xmalloc((slen + 1) * sizeof(char));
while (d + i < slen) {
char e = strsorig[k][i + d];
if (e != ' ' && e != '\t') {
strsproc[k][i] = e;
i++;
} else {
d++;
}
}
strsproc[k][i] = '\0';
}
int matching = matching_chars(strsproc[0], strsproc[1]);
xfree(strsproc[0]);
xfree(strsproc[1]);
return matching;
}
/// update the path of a point in the diff linematch algorithm
/// @param diffcmppath
/// @param score
/// @param to
/// @param from
/// @param choice
static void update_path_flat(diffcmppath_T *diffcmppath, int score, size_t to, size_t from,
const int choice)
{
size_t path_idx = diffcmppath[from].df_path_idx;
for (size_t k = 0; k < path_idx; k++) {
diffcmppath[to].df_decision[k] = diffcmppath[from].df_decision[k];
}
diffcmppath[to].df_decision[path_idx] = choice;
diffcmppath[to].df_lev_score = score;
diffcmppath[to].df_path_idx = path_idx + 1;
}
#define MATCH_CHAR_MAX_LEN 500
/// Return matching characters between "s1" and "s2" whilst respecting sequence order.
/// Consider the case of two strings 'AAACCC' and 'CCCAAA', the
/// return value from this function will be 3, either to match
/// the 3 C's, or the 3 A's.
///
/// Examples:
/// matching_chars("aabc", "acba") -> 2 // 'a' and 'b' in common
/// matching_chars("123hello567", "he123ll567o") -> 8 // '123', 'll' and '567' in common
/// matching_chars("abcdefg", "gfedcba") -> 1 // all characters in common,
/// // but only at most 1 in sequence
///
/// @param s1
/// @param s2
static int matching_chars(const char *s1, const char *s2)
{
size_t s1len = MIN(MATCH_CHAR_MAX_LEN, line_len(s1));
size_t s2len = MIN(MATCH_CHAR_MAX_LEN, line_len(s2));
int matrix[2][MATCH_CHAR_MAX_LEN] = { 0 };
bool icur = 1; // save space by storing only two rows for i axis
for (size_t i = 0; i < s1len; i++) {
icur = !icur;
int *e1 = matrix[icur];
int *e2 = matrix[!icur];
for (size_t j = 0; j < s2len; j++) {
// skip char in s1
if (e2[j + 1] > e1[j + 1]) {
e1[j + 1] = e2[j + 1];
}
// skip char in s2
if (e1[j] > e1[j + 1]) {
e1[j + 1] = e1[j];
}
// compare char in s1 and s2
if ((s1[i] == s2[j]) && (e2[j] + 1) > e1[j + 1]) {
e1[j + 1] = e2[j] + 1;
}
}
}
return matrix[icur][s2len];
}
/// count the matching characters between a variable number of strings "sp"
/// mark the strings that have already been compared to extract them later
/// without re-running the character match counting.
/// @param sp
/// @param fomvals
/// @param n
static int count_n_matched_chars(const char **sp, const size_t n, bool iwhite)
{
int matched_chars = 0;
int matched = 0;
for (size_t i = 0; i < n; i++) {
for (size_t j = i + 1; j < n; j++) {
if (sp[i] != NULL && sp[j] != NULL) {
matched++;
// TODO(lewis6991): handle whitespace ignoring higher up in the stack
matched_chars += iwhite ? matching_chars_iwhite(sp[i], sp[j])
: matching_chars(sp[i], sp[j]);
}
}
}
// prioritize a match of 3 (or more lines) equally to a match of 2 lines
if (matched >= 2) {
matched_chars *= 2;
matched_chars /= matched;
}
return matched_chars;
}
void fastforward_buf_to_lnum(const char **s, size_t lnum)
{
assert(lnum > 0);
for (size_t j = 0; j < lnum - 1; j++) {
*s = strchr(*s, '\n');
(*s)++;
}
}
/// try all the different ways to compare these lines and use the one that
/// results in the most matching characters
/// @param df_iters
/// @param paths
/// @param npaths
/// @param path_idx
/// @param choice
/// @param diffcmppath
/// @param diff_len
/// @param ndiffs
/// @param diff_blk
static void try_possible_paths(const int *df_iters, const size_t *paths, const int npaths,
const int path_idx, int *choice, diffcmppath_T *diffcmppath,
const int *diff_len, const size_t ndiffs, const char **diff_blk,
bool iwhite)
{
if (path_idx == npaths) {
if ((*choice) > 0) {
int from_vals[LN_MAX_BUFS];
const int *to_vals = df_iters;
const char *current_lines[LN_MAX_BUFS];
for (size_t k = 0; k < ndiffs; k++) {
from_vals[k] = df_iters[k];
// get the index at all of the places
if ((*choice) & (1 << k)) {
from_vals[k]--;
const char *p = diff_blk[k];
fastforward_buf_to_lnum(&p, (size_t)df_iters[k]);
current_lines[k] = p;
} else {
current_lines[k] = NULL;
}
}
size_t unwrapped_idx_from = unwrap_indexes(from_vals, diff_len, ndiffs);
size_t unwrapped_idx_to = unwrap_indexes(to_vals, diff_len, ndiffs);
int matched_chars = count_n_matched_chars(current_lines, ndiffs, iwhite);
int score = diffcmppath[unwrapped_idx_from].df_lev_score + matched_chars;
if (score > diffcmppath[unwrapped_idx_to].df_lev_score) {
update_path_flat(diffcmppath, score, unwrapped_idx_to, unwrapped_idx_from, *choice);
}
} else {
// initialize the 0, 0, 0 ... choice
size_t i = 0;
while (i < ndiffs && df_iters[i] == 0) {
i++;
if (i == ndiffs) {
diffcmppath[0].df_lev_score = 0;
diffcmppath[0].df_path_idx = 0;
}
}
}
return;
}
size_t bit_place = paths[path_idx];
*(choice) |= (1 << bit_place); // set it to 1
try_possible_paths(df_iters, paths, npaths, path_idx + 1, choice,
diffcmppath, diff_len, ndiffs, diff_blk, iwhite);
*(choice) &= ~(1 << bit_place); // set it to 0
try_possible_paths(df_iters, paths, npaths, path_idx + 1, choice,
diffcmppath, diff_len, ndiffs, diff_blk, iwhite);
}
/// unwrap indexes to access n dimensional tensor
/// @param values
/// @param diff_len
/// @param ndiffs
static size_t unwrap_indexes(const int *values, const int *diff_len, const size_t ndiffs)
{
size_t num_unwrap_scalar = 1;
for (size_t k = 0; k < ndiffs; k++) {
num_unwrap_scalar *= (size_t)diff_len[k] + 1;
}
size_t path_idx = 0;
for (size_t k = 0; k < ndiffs; k++) {
num_unwrap_scalar /= (size_t)diff_len[k] + 1;
// (k == 0) space optimization
int n = k == 0 ? values[k] % 2 : values[k];
path_idx += num_unwrap_scalar * (size_t)n;
}
return path_idx;
}
/// populate the values of the linematch algorithm tensor, and find the best
/// decision for how to compare the relevant lines from each of the buffers at
/// each point in the tensor
/// @param df_iters
/// @param ch_dim
/// @param diffcmppath
/// @param diff_len
/// @param ndiffs
/// @param diff_blk
static void populate_tensor(int *df_iters, const size_t ch_dim, diffcmppath_T *diffcmppath,
const int *diff_len, const size_t ndiffs, const char **diff_blk,
bool iwhite)
{
if (ch_dim == ndiffs) {
int npaths = 0;
size_t paths[LN_MAX_BUFS];
for (size_t j = 0; j < ndiffs; j++) {
if (df_iters[j] > 0) {
paths[npaths] = j;
npaths++;
}
}
int choice = 0;
size_t unwrapper_idx_to = unwrap_indexes(df_iters, diff_len, ndiffs);
diffcmppath[unwrapper_idx_to].df_lev_score = -1;
try_possible_paths(df_iters, paths, npaths, 0, &choice, diffcmppath,
diff_len, ndiffs, diff_blk, iwhite);
return;
}
for (int i = 0; i <= diff_len[ch_dim]; i++) {
df_iters[ch_dim] = i;
populate_tensor(df_iters, ch_dim + 1, diffcmppath, diff_len,
ndiffs, diff_blk, iwhite);
}
}
/// algorithm to find an optimal alignment of lines of a diff block with 2 or
/// more files. The algorithm is generalized to work for any number of files
/// which corresponds to another dimmension added to the tensor used in the
/// algorithm
///
/// for questions and information about the linematch algorithm please contact
/// Jonathon White (jonathonwhite@protonmail.com)
///
/// for explanation, a summary of the algorithm in 3 dimmensions (3 files
/// compared) follows
///
/// The 3d case (for 3 buffers) of the algorithm implemented when diffopt
/// 'linematch' is enabled. The algorithm constructs a 3d tensor to
/// compare a diff between 3 buffers. The dimmensions of the tensor are
/// the length of the diff in each buffer plus 1 A path is constructed by
/// moving from one edge of the cube/3d tensor to the opposite edge.
/// Motions from one cell of the cube to the next represent decisions. In
/// a 3d cube, there are a total of 7 decisions that can be made,
/// represented by the enum df_path3_choice which is defined in
/// buffer_defs.h a comparison of buffer 0 and 1 represents a motion
/// toward the opposite edge of the cube with components along the 0 and
/// 1 axes. a comparison of buffer 0, 1, and 2 represents a motion
/// toward the opposite edge of the cube with components along the 0, 1,
/// and 2 axes. A skip of buffer 0 represents a motion along only the 0
/// axis. For each action, a point value is awarded, and the path is
/// saved for reference later, if it is found to have been the optimal
/// path. The optimal path has the highest score. The score is
/// calculated as the summation of the total characters matching between
/// all of the lines which were compared. The structure of the algorithm
/// is that of a dynamic programming problem. We can calculate a point
/// i,j,k in the cube as a function of i-1, j-1, and k-1. To find the
/// score and path at point i,j,k, we must determine which path we want
/// to use, this is done by looking at the possibilities and choosing
/// the one which results in the local highest score. The total highest
/// scored path is, then in the end represented by the cell in the
/// opposite corner from the start location. The entire algorithm
/// consits of populating the 3d cube with the optimal paths from which
/// it may have came.
///
/// Optimizations:
/// As the function to calculate the cell of a tensor at point i,j,k is a
/// function of the cells at i-1, j-1, k-1, the whole tensor doesn't need
/// to be stored in memory at once. In the case of the 3d cube, only two
/// slices (along k and j axis) are stored in memory. For the 2d matrix
/// (for 2 files), only two rows are stored at a time. The next/previous
/// slice (or row) is always calculated from the other, and they alternate
/// at each iteration.
/// In the 3d case, 3 arrays are populated to memorize the score (matched
/// characters) of the 3 buffers, so a redundant calculation of the
/// scores does not occur
/// @param diff_blk
/// @param diff_len
/// @param ndiffs
/// @param [out] [allocated] decisions
/// @return the length of decisions
size_t linematch_nbuffers(const char **diff_blk, const int *diff_len, const size_t ndiffs,
int **decisions, bool iwhite)
{
assert(ndiffs <= LN_MAX_BUFS);
size_t memsize = 1;
size_t memsize_decisions = 0;
for (size_t i = 0; i < ndiffs; i++) {
assert(diff_len[i] >= 0);
memsize *= i == 0 ? 2 : (size_t)(diff_len[i] + 1);
memsize_decisions += (size_t)diff_len[i];
}
// create the flattened path matrix
diffcmppath_T *diffcmppath = xmalloc(sizeof(diffcmppath_T) * memsize);
// allocate memory here
for (size_t i = 0; i < memsize; i++) {
diffcmppath[i].df_decision = xmalloc(memsize_decisions * sizeof(int));
}
// memory for avoiding repetitive calculations of score
int df_iters[LN_MAX_BUFS];
populate_tensor(df_iters, 0, diffcmppath, diff_len, ndiffs, diff_blk, iwhite);
const size_t u = unwrap_indexes(diff_len, diff_len, ndiffs);
const size_t best_path_idx = diffcmppath[u].df_path_idx;
const int *best_path_decisions = diffcmppath[u].df_decision;
*decisions = xmalloc(sizeof(int) * best_path_idx);
for (size_t i = 0; i < best_path_idx; i++) {
(*decisions)[i] = best_path_decisions[i];
}
for (size_t i = 0; i < memsize; i++) {
xfree(diffcmppath[i].df_decision);
}
xfree(diffcmppath);
return best_path_idx;
}

10
src/nvim/linematch.h Normal file
View File

@@ -0,0 +1,10 @@
#ifndef NVIM_LINEMATCH_H
#define NVIM_LINEMATCH_H
#include <stddef.h>
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "linematch.h.generated.h"
#endif
#endif // NVIM_LINEMATCH_H

View File

@@ -10,12 +10,16 @@
#include <string.h>
#include "nvim/api/private/helpers.h"
#include "nvim/linematch.h"
#include "nvim/lua/converter.h"
#include "nvim/lua/executor.h"
#include "nvim/lua/xdiff.h"
#include "nvim/vim.h"
#include "xdiff/xdiff.h"
#define COMPARED_BUFFER0 (1 << 0)
#define COMPARED_BUFFER1 (1 << 1)
typedef enum {
kNluaXdiffModeUnified = 0,
kNluaXdiffModeOnHunkCB,
@@ -25,12 +29,72 @@ typedef enum {
typedef struct {
lua_State *lstate;
Error *err;
mmfile_t *ma;
mmfile_t *mb;
bool linematch;
bool iwhite;
} hunkpriv_t;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "lua/xdiff.c.generated.h"
#endif
static void lua_pushhunk(lua_State *lstate, long start_a, long count_a, long start_b, long count_b)
{
lua_createtable(lstate, 0, 0);
lua_pushinteger(lstate, start_a);
lua_rawseti(lstate, -2, 1);
lua_pushinteger(lstate, count_a);
lua_rawseti(lstate, -2, 2);
lua_pushinteger(lstate, start_b);
lua_rawseti(lstate, -2, 3);
lua_pushinteger(lstate, count_b);
lua_rawseti(lstate, -2, 4);
lua_rawseti(lstate, -2, (signed)lua_objlen(lstate, -2) + 1);
}
static void get_linematch_results(lua_State *lstate, mmfile_t *ma, mmfile_t *mb, long start_a,
long count_a, long start_b, long count_b, bool iwhite)
{
// get the pointer to char of the start of the diff to pass it to linematch algorithm
const char *diff_begin[2] = { ma->ptr, mb->ptr };
int diff_length[2] = { (int)count_a, (int)count_b };
fastforward_buf_to_lnum(&diff_begin[0], (size_t)start_a);
fastforward_buf_to_lnum(&diff_begin[1], (size_t)start_b);
int *decisions = NULL;
size_t decisions_length = linematch_nbuffers(diff_begin, diff_length, 2, &decisions, iwhite);
long lnuma = start_a, lnumb = start_b;
long hunkstarta = lnuma;
long hunkstartb = lnumb;
long hunkcounta = 0;
long hunkcountb = 0;
for (size_t i = 0; i < decisions_length; i++) {
if (i && (decisions[i - 1] != decisions[i])) {
lua_pushhunk(lstate, hunkstarta, hunkcounta, hunkstartb, hunkcountb);
hunkstarta = lnuma;
hunkstartb = lnumb;
hunkcounta = 0;
hunkcountb = 0;
// create a new hunk
}
if (decisions[i] & COMPARED_BUFFER0) {
lnuma++;
hunkcounta++;
}
if (decisions[i] & COMPARED_BUFFER1) {
lnumb++;
hunkcountb++;
}
}
lua_pushhunk(lstate, hunkstarta, hunkcounta, hunkstartb, hunkcountb);
xfree(decisions);
}
static int write_string(void *priv, mmbuffer_t *mb, int nbuf)
{
luaL_Buffer *buf = (luaL_Buffer *)priv;
@@ -61,20 +125,14 @@ static int hunk_locations_cb(long start_a, long count_a, long start_b, long coun
if (count_b > 0) {
start_b += 1;
}
lua_State *lstate = (lua_State *)cb_data;
lua_createtable(lstate, 0, 0);
lua_pushinteger(lstate, start_a);
lua_rawseti(lstate, -2, 1);
lua_pushinteger(lstate, count_a);
lua_rawseti(lstate, -2, 2);
lua_pushinteger(lstate, start_b);
lua_rawseti(lstate, -2, 3);
lua_pushinteger(lstate, count_b);
lua_rawseti(lstate, -2, 4);
lua_rawseti(lstate, -2, (signed)lua_objlen(lstate, -2) + 1);
hunkpriv_t *priv = (hunkpriv_t *)cb_data;
lua_State *lstate = priv->lstate;
if (priv->linematch) {
get_linematch_results(lstate, priv->ma, priv->mb, start_a, count_a, start_b, count_b,
priv->iwhite);
} else {
lua_pushhunk(lstate, start_a, count_a, start_b, count_b);
}
return 0;
}
@@ -149,7 +207,7 @@ static bool check_xdiff_opt(ObjectType actType, ObjectType expType, const char *
}
static NluaXdiffMode process_xdl_diff_opts(lua_State *lstate, xdemitconf_t *cfg, xpparam_t *params,
Error *err)
bool *linematch, Error *err)
{
const DictionaryOf(LuaRef) opts = nlua_pop_Dictionary(lstate, true, err);
@@ -205,6 +263,11 @@ static NluaXdiffMode process_xdl_diff_opts(lua_State *lstate, xdemitconf_t *cfg,
goto exit_1;
}
cfg->interhunkctxlen = v->data.integer;
} else if (strequal("linematch", k.data)) {
*linematch = api_object_to_bool(*v, "linematch", false, err);
if (ERROR_SET(err)) {
goto exit_1;
}
} else {
struct {
const char *name;
@@ -244,10 +307,8 @@ static NluaXdiffMode process_xdl_diff_opts(lua_State *lstate, xdemitconf_t *cfg,
if (had_on_hunk) {
mode = kNluaXdiffModeOnHunkCB;
cfg->hunk_func = call_on_hunk_cb;
} else if (had_result_type_indices) {
mode = kNluaXdiffModeLocations;
cfg->hunk_func = hunk_locations_cb;
}
exit_1:
@@ -268,6 +329,7 @@ int nlua_xdl_diff(lua_State *lstate)
xdemitconf_t cfg;
xpparam_t params;
xdemitcb_t ecb;
bool linematch = false;
CLEAR_FIELD(cfg);
CLEAR_FIELD(params);
@@ -280,7 +342,7 @@ int nlua_xdl_diff(lua_State *lstate)
return luaL_argerror(lstate, 3, "expected table");
}
mode = process_xdl_diff_opts(lstate, &cfg, &params, &err);
mode = process_xdl_diff_opts(lstate, &cfg, &params, &linematch, &err);
if (ERROR_SET(&err)) {
goto exit_0;
@@ -288,7 +350,7 @@ int nlua_xdl_diff(lua_State *lstate)
}
luaL_Buffer buf;
hunkpriv_t *priv = NULL;
hunkpriv_t priv;
switch (mode) {
case kNluaXdiffModeUnified:
luaL_buffinit(lstate, &buf);
@@ -296,14 +358,24 @@ int nlua_xdl_diff(lua_State *lstate)
ecb.out_line = write_string;
break;
case kNluaXdiffModeOnHunkCB:
priv = xmalloc(sizeof(*priv));
priv->lstate = lstate;
priv->err = &err;
ecb.priv = priv;
cfg.hunk_func = call_on_hunk_cb;
priv = (hunkpriv_t) {
.lstate = lstate,
.err = &err,
};
ecb.priv = &priv;
break;
case kNluaXdiffModeLocations:
cfg.hunk_func = hunk_locations_cb;
priv = (hunkpriv_t) {
.lstate = lstate,
.ma = &ma,
.mb = &mb,
.linematch = linematch,
.iwhite = (params.flags & XDF_IGNORE_WHITESPACE) > 0
};
ecb.priv = &priv;
lua_createtable(lstate, 0, 0);
ecb.priv = lstate;
break;
}
@@ -314,8 +386,6 @@ int nlua_xdl_diff(lua_State *lstate)
}
}
XFREE_CLEAR(priv);
exit_0:
if (ERROR_SET(&err)) {
luaL_where(lstate, 1);

View File

@@ -1073,6 +1073,182 @@ int main(int argc, char **argv)
:e |
]])
end)
describe('line matching diff algorithm', function()
setup(function()
local f1 = [[if __name__ == "__main__":
import sys
app = QWidgets.QApplication(sys.args)
MainWindow = QtWidgets.QMainWindow()
ui = UI_MainWindow()
ui.setupUI(MainWindow)
MainWindow.show()
sys.exit(app.exec_())]]
write_file(fname, f1, false)
local f2 = [[if __name__ == "__main__":
import sys
comment these things
#app = QWidgets.QApplication(sys.args)
#MainWindow = QtWidgets.QMainWindow()
add a completely different line here
#ui = UI_MainWindow()
add another new line
ui.setupUI(MainWindow)
MainWindow.show()
sys.exit(app.exec_())]]
write_file(fname_2, f2, false)
end)
it('diffopt+=linematch:20', function()
reread()
feed(':set diffopt=internal,filler<cr>')
screen:expect([[
{1: }^if __name__ == "__│{1: }if __name__ == "_|
{1: } import sys │{1: } import sys |
{1: }{9: }{8:app = QWidgets}│{1: }{9: }{8:comment these}|
{1: }{9: }{8:MainWindow = Q}│{1: }{9: }{8:#app = QWidge}|
{1: }{9: }{8:ui = UI_}{9:MainWi}│{1: }{9: }{8:#MainWindow =}|
{1: }{2:------------------}│{1: }{4: add a complet}|
{1: }{2:------------------}│{1: }{4: #ui = UI_Main}|
{1: }{2:------------------}│{1: }{4: add another n}|
{1: } ui.setupUI(Mai│{1: } ui.setupUI(Ma|
{1: } MainWindow.sho│{1: } MainWindow.sh|
{1: } sys.exit(app.e│{1: } sys.exit(app.|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt=internal,filler |
]])
feed('G')
feed(':set diffopt+=linematch:20<cr>')
screen:expect([[
{1: }if __name__ == "__│{1: }if __name__ == "_|
{1: } import sys │{1: } import sys |
{1: }{2:------------------}│{1: }{4: comment these}|
{1: }{9: app = QWidgets}│{1: }{9: }{8:#}{9:app = QWidge}|
{1: }{9: MainWindow = Q}│{1: }{9: }{8:#}{9:MainWindow =}|
{1: }{2:------------------}│{1: }{4: add a complet}|
{1: }{9: ui = UI_MainWi}│{1: }{9: }{8:#}{9:ui = UI_Main}|
{1: }{2:------------------}│{1: }{4: add another n}|
{1: } ui.setupUI(Mai│{1: } ui.setupUI(Ma|
{1: } MainWindow.sho│{1: } MainWindow.sh|
{1: } ^sys.exit(app.e│{1: } sys.exit(app.|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt+=linematch:20 |
]])
end)
end)
describe('line matching diff algorithm with icase', function()
setup(function()
local f1 = [[DDD
_aa]]
write_file(fname, f1, false)
local f2 = [[DDD
AAA
ccca]]
write_file(fname_2, f2, false)
end)
it('diffopt+=linematch:20,icase', function()
reread()
feed(':set diffopt=internal,filler,linematch:20<cr>')
screen:expect([[
{1: }^DDD │{1: }DDD |
{1: }{2:------------------}│{1: }{4:AAA }|
{1: }{8:_a}{9:a }│{1: }{8:ccc}{9:a }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
|
]])
feed(':set diffopt+=icase<cr>')
screen:expect([[
{1: }^DDD │{1: }DDD |
{1: }{8:_}{9:aa }│{1: }{8:A}{9:AA }|
{1: }{2:------------------}│{1: }{4:ccca }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt+=icase |
]])
end)
end)
describe('line matching diff algorithm with iwhiteall', function()
setup(function()
local f1 = [[BB
AAA]]
write_file(fname, f1, false)
local f2 = [[BB
AAB
AAAB]]
write_file(fname_2, f2, false)
end)
it('diffopt+=linematch:20,iwhiteall', function()
reread()
feed(':set diffopt=internal,filler,linematch:20<cr>')
screen:expect{grid=[[
{1: }^BB │{1: }BB |
{1: }{9: AA}{8:A}{9: }│{1: }{9: AA}{8:B}{9: }|
{1: }{2:------------------}│{1: }{4:AAAB }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
|
]]}
feed(':set diffopt+=iwhiteall<cr>')
screen:expect{grid=[[
{1: }^BB │{1: }BB |
{1: }{2:------------------}│{1: }{4: AAB }|
{1: }{9: AAA }│{1: }{9:AAA}{8:B}{9: }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt+=iwhiteall |
]]}
end)
end)
end)
it('win_update redraws lines properly', function()

View File

@@ -0,0 +1,981 @@
local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local feed = helpers.feed
local clear = helpers.clear
local write_file = helpers.write_file
describe('Diff mode screen with 3 diffs open', function()
local fname = 'Xtest-functional-diff-screen-1'
local fname_2 = fname .. '.2'
local fname_3 = fname .. '.3'
local screen
local reread = function()
feed(':e<cr><c-w>w:e<cr><c-w>w:e<cr><c-w>w')
end
setup(function()
clear()
os.remove(fname)
os.remove(fname_2)
os.remove(fname_3)
end)
teardown(function()
os.remove(fname)
os.remove(fname_2)
os.remove(fname_3)
end)
before_each(function()
clear()
feed(':set diffopt+=linematch:30<cr>')
feed(':e ' .. fname .. '<cr>')
feed(':vnew ' .. fname_2 .. '<cr>')
feed(':vnew ' .. fname_3 .. '<cr>')
feed(':windo diffthis<cr>')
screen = Screen.new(100, 16)
screen:attach()
screen:set_default_attr_ids({
[1] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.Gray};
[2] = {foreground = Screen.colors.Blue1, bold = true, background = Screen.colors.LightCyan1};
[3] = {reverse = true};
[4] = {background = Screen.colors.LightBlue};
[5] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGray};
[6] = {foreground = Screen.colors.Blue1, bold = true};
[7] = {reverse = true, bold = true};
[8] = {background = Screen.colors.Red1, bold = true};
[10] = {foreground = Screen.colors.Brown};
[9] = {background = Screen.colors.Plum1};
})
feed('<c-w>=')
feed(':windo set nu!<cr>')
end)
describe('setup the diff screen to look like a merge conflict with 3 files in diff mode', function()
before_each(function()
local f1 = [[
common line
AAA
AAA
AAA
]]
local f2 = [[
common line
<<<<<<< HEAD
AAA
AAA
AAA
=======
BBB
BBB
BBB
>>>>>>> branch1
]]
local f3 = [[
common line
BBB
BBB
BBB
]]
write_file(fname, f1, false)
write_file(fname_2, f2, false)
write_file(fname_3, f3, false)
reread()
end)
it('get from window 1', function()
feed('1<c-w>w')
feed(':2,6diffget screen-1.2<cr>')
screen:expect([[
{1: }{10: 1 }^ │{1: }{10: 1 } │{1: }{10: 1 } |
{1: }{10: 2 }common line │{1: }{10: 2 }common line │{1: }{10: 2 }common line |
{1: }{10: 3 }{9:<<<<<<< HEAD }│{1: }{10: 3 }{9:<<<<<<< HEAD }│{1: }{10: }{2:---------------------------}|
{1: }{10: 4 } AAA │{1: }{10: 4 } AAA │{1: }{10: 3 } AAA |
{1: }{10: 5 } AAA │{1: }{10: 5 } AAA │{1: }{10: 4 } AAA |
{1: }{10: 6 } AAA │{1: }{10: 6 } AAA │{1: }{10: 5 } AAA |
{1: }{10: 7 }{9:======= }│{1: }{10: 7 }{9:======= }│{1: }{10: }{2:---------------------------}|
{1: }{10: 8 }{9: BBB }│{1: }{10: 8 }{9: BBB }│{1: }{10: }{2:---------------------------}|
{1: }{10: 9 }{9: BBB }│{1: }{10: 9 }{9: BBB }│{1: }{10: }{2:---------------------------}|
{1: }{10: 10 }{9: BBB }│{1: }{10: 10 }{9: BBB }│{1: }{10: }{2:---------------------------}|
{1: }{10: 11 }{9:>>>>>>> branch1 }│{1: }{10: 11 }{9:>>>>>>> branch1 }│{1: }{10: }{2:---------------------------}|
{1: }{10: 12 } │{1: }{10: 12 } │{1: }{10: 6 } |
{6:~ }│{6:~ }│{6:~ }|
{6:~ }│{6:~ }│{6:~ }|
{7:<-functional-diff-screen-1.3 [+] }{3:<est-functional-diff-screen-1.2 Xtest-functional-diff-screen-1 }|
:2,6diffget screen-1.2 |
]])
end)
it('get from window 2', function()
feed('2<c-w>w')
feed(':5,7diffget screen-1.3<cr>')
screen:expect([[
{1: }{10: 1 } │{1: }{10: 1 }^ │{1: }{10: 1 } |
{1: }{10: 2 }common line │{1: }{10: 2 }common line │{1: }{10: 2 }common line |
{1: }{10: }{2:---------------------------}│{1: }{10: 3 }{4:<<<<<<< HEAD }│{1: }{10: }{2:---------------------------}|
{1: }{10: }{2:---------------------------}│{1: }{10: 4 }{9: AAA }│{1: }{10: 3 }{9: AAA }|
{1: }{10: 3 }{9: BBB }│{1: }{10: 5 }{9: BBB }│{1: }{10: }{2:---------------------------}|
{1: }{10: 4 }{9: }{8:BBB}{9: }│{1: }{10: 6 }{9: }{8:BBB}{9: }│{1: }{10: 4 }{9: }{8:AAA}{9: }|
{1: }{10: 5 }{9: }{8:BBB}{9: }│{1: }{10: 7 }{9: }{8:BBB}{9: }│{1: }{10: 5 }{9: }{8:AAA}{9: }|
{1: }{10: }{2:---------------------------}│{1: }{10: 8 }{4:>>>>>>> branch1 }│{1: }{10: }{2:---------------------------}|
{1: }{10: 6 } │{1: }{10: 9 } │{1: }{10: 6 } |
{6:~ }│{6:~ }│{6:~ }|
{6:~ }│{6:~ }│{6:~ }|
{6:~ }│{6:~ }│{6:~ }|
{6:~ }│{6:~ }│{6:~ }|
{6:~ }│{6:~ }│{6:~ }|
{3:<test-functional-diff-screen-1.3 }{7:<functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }|
:5,7diffget screen-1.3 |
]])
end)
it('get from window 3', function()
feed('3<c-w>w')
feed(':5,6diffget screen-1.2<cr>')
screen:expect([[
{1: }{10: 1 } │{1: }{10: 1 } │{1: }{10: 1 }^ |
{1: }{10: 2 }common line │{1: }{10: 2 }common line │{1: }{10: 2 }common line |
{1: }{10: }{2:---------------------------}│{1: }{10: 3 }{4:<<<<<<< HEAD }│{1: }{10: }{2:---------------------------}|
{1: }{10: }{2:---------------------------}│{1: }{10: 4 }{9: AAA }│{1: }{10: 3 }{9: AAA }|
{1: }{10: }{2:---------------------------}│{1: }{10: 5 }{9: AAA }│{1: }{10: 4 }{9: AAA }|
{1: }{10: }{2:---------------------------}│{1: }{10: 6 }{9: AAA }│{1: }{10: 5 }{9: AAA }|
{1: }{10: }{2:---------------------------}│{1: }{10: 7 }{9:======= }│{1: }{10: 6 }{9:======= }|
{1: }{10: 3 } BBB │{1: }{10: 8 } BBB │{1: }{10: 7 } BBB |
{1: }{10: 4 } BBB │{1: }{10: 9 } BBB │{1: }{10: 8 } BBB |
{1: }{10: 5 } BBB │{1: }{10: 10 } BBB │{1: }{10: 9 } BBB |
{1: }{10: }{2:---------------------------}│{1: }{10: 11 }{9:>>>>>>> branch1 }│{1: }{10: 10 }{9:>>>>>>> branch1 }|
{1: }{10: 6 } │{1: }{10: 12 } │{1: }{10: 11 } |
{6:~ }│{6:~ }│{6:~ }|
{6:~ }│{6:~ }│{6:~ }|
{3:<test-functional-diff-screen-1.3 <est-functional-diff-screen-1.2 }{7:<st-functional-diff-screen-1 [+] }|
:5,6diffget screen-1.2 |
]])
end)
it('put from window 2 - part', function()
feed('2<c-w>w')
feed(':6,8diffput screen-1<cr>')
screen:expect([[
{1: }{10: 1 } │{1: }{10: 1 }^ │{1: }{10: 1 } |
{1: }{10: 2 }common line │{1: }{10: 2 }common line │{1: }{10: 2 }common line |
{1: }{10: }{2:---------------------------}│{1: }{10: 3 }{4:<<<<<<< HEAD }│{1: }{10: }{2:---------------------------}|
{1: }{10: }{2:---------------------------}│{1: }{10: 4 }{9: AAA }│{1: }{10: 3 }{9: AAA }|
{1: }{10: }{2:---------------------------}│{1: }{10: 5 }{9: AAA }│{1: }{10: 4 }{9: AAA }|
{1: }{10: }{2:---------------------------}│{1: }{10: 6 }{9: AAA }│{1: }{10: 5 }{9: AAA }|
{1: }{10: }{2:---------------------------}│{1: }{10: 7 }{9:======= }│{1: }{10: 6 }{9:======= }|
{1: }{10: 3 }{9: BBB }│{1: }{10: 8 }{9: BBB }│{1: }{10: }{2:---------------------------}|
{1: }{10: 4 }{9: BBB }│{1: }{10: 9 }{9: BBB }│{1: }{10: }{2:---------------------------}|
{1: }{10: 5 } BBB │{1: }{10: 10 } BBB │{1: }{10: 7 } BBB |
{1: }{10: }{2:---------------------------}│{1: }{10: 11 }{4:>>>>>>> branch1 }│{1: }{10: }{2:---------------------------}|
{1: }{10: 6 } │{1: }{10: 12 } │{1: }{10: 8 } |
{6:~ }│{6:~ }│{6:~ }|
{6:~ }│{6:~ }│{6:~ }|
{3:<test-functional-diff-screen-1.3 }{7:<est-functional-diff-screen-1.2 }{3:<st-functional-diff-screen-1 [+] }|
: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>')
screen:expect([[
{1: }{10: 1 } │{1: }{10: 1 }^ │{1: }{10: 1 } |
{1: }{10: 2 }common line │{1: }{10: 2 }common line │{1: }{10: 2 }common line |
{1: }{10: }{2:---------------------------}│{1: }{10: 3 }{4:<<<<<<< HEAD }│{1: }{10: }{2:---------------------------}|
{1: }{10: }{2:---------------------------}│{1: }{10: 4 }{9: AAA }│{1: }{10: 3 }{9: AAA }|
{1: }{10: }{2:---------------------------}│{1: }{10: 5 }{9: AAA }│{1: }{10: 4 }{9: AAA }|
{1: }{10: }{2:---------------------------}│{1: }{10: 6 }{9: AAA }│{1: }{10: 5 }{9: AAA }|
{1: }{10: }{2:---------------------------}│{1: }{10: 7 }{9:======= }│{1: }{10: 6 }{9:======= }|
{1: }{10: 3 } BBB │{1: }{10: 8 } BBB │{1: }{10: 7 } BBB |
{1: }{10: 4 } BBB │{1: }{10: 9 } BBB │{1: }{10: 8 } BBB |
{1: }{10: 5 } BBB │{1: }{10: 10 } BBB │{1: }{10: 9 } BBB |
{1: }{10: }{2:---------------------------}│{1: }{10: 11 }{9:>>>>>>> branch1 }│{1: }{10: 10 }{9:>>>>>>> branch1 }|
{1: }{10: 6 } │{1: }{10: 12 } │{1: }{10: 11 } |
{6:~ }│{6:~ }│{6:~ }|
{6:~ }│{6:~ }│{6:~ }|
{3:<test-functional-diff-screen-1.3 }{7:<est-functional-diff-screen-1.2 }{3:<st-functional-diff-screen-1 [+] }|
:6,11diffput screen-1 |
]])
end)
end)
end)
describe('Diff mode screen with 2 diffs open', function()
local fname = 'Xtest-functional-diff-screen-1'
local fname_2 = fname .. '.2'
local screen
local reread = function()
feed(':e<cr><c-w>w:e<cr><c-w>w:e<cr><c-w>w')
end
setup(function()
clear()
os.remove(fname)
os.remove(fname_2)
end)
teardown(function()
os.remove(fname)
os.remove(fname_2)
end)
before_each(function()
clear()
feed(':e ' .. fname .. '<cr>')
feed(':vnew ' .. fname_2 .. '<cr>')
feed(':windo diffthis<cr>')
screen = Screen.new(100, 20)
screen:attach()
screen:set_default_attr_ids({
[1] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.Gray};
[2] = {foreground = Screen.colors.Blue1, bold = true, background = Screen.colors.LightCyan1};
[3] = {reverse = true};
[4] = {background = Screen.colors.LightBlue};
[5] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGray};
[6] = {foreground = Screen.colors.Blue1, bold = true};
[7] = {reverse = true, bold = true};
[8] = {background = Screen.colors.Red1, bold = true};
[10] = {foreground = Screen.colors.Brown};
[9] = {background = Screen.colors.Plum1};
})
feed('<c-w>=')
feed(':windo set nu!<cr>')
end)
describe('setup a diff with 2 files and set linematch:30', function()
before_each(function()
feed(':set diffopt+=linematch:30<cr>')
local f1 = [[
common line
common line
DEFabc
xyz
xyz
xyz
DEFabc
DEFabc
DEFabc
common line
common line
DEF
common line
DEF
something
]]
local f2 = [[
common line
common line
ABCabc
ABCabc
ABCabc
ABCabc
common line
common line
common line
something
]]
write_file(fname, f1, false)
write_file(fname_2, f2, false)
reread()
end)
it('get from window 1 from line 5 to 9', function()
feed('1<c-w>w')
feed(':5,9diffget<cr>')
screen:expect([[
{1:+ }{10: 1 }{5:^+-- 7 lines: common line··················}│{1:+ }{10: 1 }{5:+-- 7 lines: common line···················}|
{1: }{10: 8 }xyz │{1: }{10: 8 }xyz |
{1: }{10: 9 }DEFabc │{1: }{10: 9 }DEFabc |
{1: }{10: 10 }DEFabc │{1: }{10: 10 }DEFabc |
{1: }{10: 11 }DEFabc │{1: }{10: 11 }DEFabc |
{1: }{10: 12 }common line │{1: }{10: 12 }common line |
{1: }{10: 13 }common line │{1: }{10: 13 }common line |
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }|
{1: }{10: 14 }common line │{1: }{10: 15 }common line |
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 16 }{4:DEF }|
{1: }{10: 15 }something │{1: }{10: 17 }something |
{1: }{10: 16 } │{1: }{10: 18 } |
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{7:Xtest-functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }|
:5,9diffget |
]])
end)
it('get from window 2 from line 5 to 10', function()
feed('2<c-w>w')
feed(':5,10diffget<cr>')
screen:expect([[
{1:- }{10: 1 } │{1:- }{10: 1 }^ |
{1: }{10: 2 }common line │{1: }{10: 2 }common line |
{1: }{10: 3 }common line │{1: }{10: 3 }common line |
{1: }{10: 4 } │{1: }{10: 4 } |
{1: }{10: 5 }ABCabc │{1: }{10: 5 }ABCabc |
{1: }{10: 6 }ABCabc │{1: }{10: 6 }ABCabc |
{1: }{10: 7 }ABCabc │{1: }{10: 7 }ABCabc |
{1: }{10: 8 }{8:ABC}{9:abc }│{1: }{10: 8 }{8:DEF}{9:abc }|
{1: }{10: 9 }common line │{1: }{10: 9 }common line |
{1: }{10: 10 }common line │{1: }{10: 10 }common line |
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 11 }{4:DEF }|
{1: }{10: 11 }common line │{1: }{10: 12 }common line |
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 13 }{4:DEF }|
{1: }{10: 12 }something │{1: }{10: 14 }something |
{1: }{10: 13 } │{1: }{10: 15 } |
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{3:Xtest-functional-diff-screen-1.2 }{7:Xtest-functional-diff-screen-1 [+] }|
:5,10diffget |
]])
end)
it('get all from window 2', function()
feed('2<c-w>w')
feed(':4,17diffget<cr>')
screen:expect([[
{1: }{10: 1 } │{1: }{10: 1 }^ |
{1: }{10: 2 }common line │{1: }{10: 2 }common line |
{1: }{10: 3 }common line │{1: }{10: 3 }common line |
{1: }{10: 4 } │{1: }{10: 4 } |
{1: }{10: 5 }ABCabc │{1: }{10: 5 }ABCabc |
{1: }{10: 6 }ABCabc │{1: }{10: 6 }ABCabc |
{1: }{10: 7 }ABCabc │{1: }{10: 7 }ABCabc |
{1: }{10: 8 }ABCabc │{1: }{10: 8 }ABCabc |
{1: }{10: 9 }common line │{1: }{10: 9 }common line |
{1: }{10: 10 }common line │{1: }{10: 10 }common line |
{1: }{10: 11 }common line │{1: }{10: 11 }common line |
{1: }{10: 12 }something │{1: }{10: 12 }something |
{1: }{10: 13 } │{1: }{10: 13 } |
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{3:Xtest-functional-diff-screen-1.2 }{7:Xtest-functional-diff-screen-1 [+] }|
:4,17diffget |
]])
end)
it('get all from window 1', function()
feed('1<c-w>w')
feed(':4,12diffget<cr>')
screen:expect([[
{1: }{10: 1 }^ │{1: }{10: 1 } |
{1: }{10: 2 }common line │{1: }{10: 2 }common line |
{1: }{10: 3 }common line │{1: }{10: 3 }common line |
{1: }{10: 4 } │{1: }{10: 4 } |
{1: }{10: 5 }DEFabc │{1: }{10: 5 }DEFabc |
{1: }{10: 6 }xyz │{1: }{10: 6 }xyz |
{1: }{10: 7 }xyz │{1: }{10: 7 }xyz |
{1: }{10: 8 }xyz │{1: }{10: 8 }xyz |
{1: }{10: 9 }DEFabc │{1: }{10: 9 }DEFabc |
{1: }{10: 10 }DEFabc │{1: }{10: 10 }DEFabc |
{1: }{10: 11 }DEFabc │{1: }{10: 11 }DEFabc |
{1: }{10: 12 }common line │{1: }{10: 12 }common line |
{1: }{10: 13 }common line │{1: }{10: 13 }common line |
{1: }{10: 14 }DEF │{1: }{10: 14 }DEF |
{1: }{10: 15 }common line │{1: }{10: 15 }common line |
{1: }{10: 16 }DEF │{1: }{10: 16 }DEF |
{1: }{10: 17 }something │{1: }{10: 17 }something |
{1: }{10: 18 } │{1: }{10: 18 } |
{7:Xtest-functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }|
:4,12diffget |
]])
end)
it('get from window 1 using do 1 line 5', function()
feed('1<c-w>w')
feed('5gg')
feed('do')
screen:expect([[
{1: }{10: 1 } │{1: }{10: 1 } |
{1: }{10: 2 }common line │{1: }{10: 2 }common line |
{1: }{10: 3 }common line │{1: }{10: 3 }common line |
{1: }{10: 4 } │{1: }{10: 4 } |
{1: }{10: 5 }^DEFabc │{1: }{10: 5 }DEFabc |
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }|
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }|
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }|
{1: }{10: 6 }{8:ABC}{9:abc }│{1: }{10: 9 }{8:DEF}{9:abc }|
{1: }{10: 7 }{8:ABC}{9:abc }│{1: }{10: 10 }{8:DEF}{9:abc }|
{1: }{10: 8 }{8:ABC}{9:abc }│{1: }{10: 11 }{8:DEF}{9:abc }|
{1: }{10: 9 }common line │{1: }{10: 12 }common line |
{1: }{10: 10 }common line │{1: }{10: 13 }common line |
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }|
{1: }{10: 11 }common line │{1: }{10: 15 }common line |
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 16 }{4:DEF }|
{1: }{10: 12 }something │{1: }{10: 17 }something |
{1: }{10: 13 } │{1: }{10: 18 } |
{7:Xtest-functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }|
:e |
]])
end)
it('get from window 1 using do 2 line 6', function()
feed('1<c-w>w')
feed('6gg')
feed('do')
screen:expect([[
{1: }{10: 1 } │{1: }{10: 1 } |
{1: }{10: 2 }common line │{1: }{10: 2 }common line |
{1: }{10: 3 }common line │{1: }{10: 3 }common line |
{1: }{10: 4 } │{1: }{10: 4 } |
{1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }|
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }|
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }|
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }|
{1: }{10: 6 }^DEFabc │{1: }{10: 9 }DEFabc |
{1: }{10: 7 }DEFabc │{1: }{10: 10 }DEFabc |
{1: }{10: 8 }DEFabc │{1: }{10: 11 }DEFabc |
{1: }{10: 9 }common line │{1: }{10: 12 }common line |
{1: }{10: 10 }common line │{1: }{10: 13 }common line |
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }|
{1: }{10: 11 }common line │{1: }{10: 15 }common line |
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 16 }{4:DEF }|
{1: }{10: 12 }something │{1: }{10: 17 }something |
{1: }{10: 13 } │{1: }{10: 18 } |
{7:Xtest-functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }|
:e |
]])
end)
it('get from window 1 using do 2 line 7', function()
feed('1<c-w>w')
feed('7gg')
feed('do')
screen:expect([[
{1: }{10: 1 } │{1: }{10: 1 } |
{1: }{10: 2 }common line │{1: }{10: 2 }common line |
{1: }{10: 3 }common line │{1: }{10: 3 }common line |
{1: }{10: 4 } │{1: }{10: 4 } |
{1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }|
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }|
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }|
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }|
{1: }{10: 6 }DEFabc │{1: }{10: 9 }DEFabc |
{1: }{10: 7 }^DEFabc │{1: }{10: 10 }DEFabc |
{1: }{10: 8 }DEFabc │{1: }{10: 11 }DEFabc |
{1: }{10: 9 }common line │{1: }{10: 12 }common line |
{1: }{10: 10 }common line │{1: }{10: 13 }common line |
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }|
{1: }{10: 11 }common line │{1: }{10: 15 }common line |
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 16 }{4:DEF }|
{1: }{10: 12 }something │{1: }{10: 17 }something |
{1: }{10: 13 } │{1: }{10: 18 } |
{7:Xtest-functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }|
:e |
]])
end)
it('get from window 1 using do 2 line 11', function()
feed('1<c-w>w')
feed('11gg')
feed('do')
screen:expect([[
{1: }{10: 1 } │{1: }{10: 1 } |
{1: }{10: 2 }common line │{1: }{10: 2 }common line |
{1: }{10: 3 }common line │{1: }{10: 3 }common line |
{1: }{10: 4 } │{1: }{10: 4 } |
{1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }|
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }|
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }|
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }|
{1: }{10: 6 }{8:ABC}{9:abc }│{1: }{10: 9 }{8:DEF}{9:abc }|
{1: }{10: 7 }{8:ABC}{9:abc }│{1: }{10: 10 }{8:DEF}{9:abc }|
{1: }{10: 8 }{8:ABC}{9:abc }│{1: }{10: 11 }{8:DEF}{9:abc }|
{1: }{10: 9 }common line │{1: }{10: 12 }common line |
{1: }{10: 10 }common line │{1: }{10: 13 }common line |
{1: }{10: 11 }DEF │{1: }{10: 14 }DEF |
{1: }{10: 12 }^common line │{1: }{10: 15 }common line |
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 16 }{4:DEF }|
{1: }{10: 13 }something │{1: }{10: 17 }something |
{1: }{10: 14 } │{1: }{10: 18 } |
{7:Xtest-functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }|
:e |
]])
end)
it('get from window 1 using do 2 line 12', function()
feed('1<c-w>w')
feed('12gg')
feed('do')
screen:expect([[
{1: }{10: 1 } │{1: }{10: 1 } |
{1: }{10: 2 }common line │{1: }{10: 2 }common line |
{1: }{10: 3 }common line │{1: }{10: 3 }common line |
{1: }{10: 4 } │{1: }{10: 4 } |
{1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }|
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }|
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }|
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }|
{1: }{10: 6 }{8:ABC}{9:abc }│{1: }{10: 9 }{8:DEF}{9:abc }|
{1: }{10: 7 }{8:ABC}{9:abc }│{1: }{10: 10 }{8:DEF}{9:abc }|
{1: }{10: 8 }{8:ABC}{9:abc }│{1: }{10: 11 }{8:DEF}{9:abc }|
{1: }{10: 9 }common line │{1: }{10: 12 }common line |
{1: }{10: 10 }common line │{1: }{10: 13 }common line |
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }|
{1: }{10: 11 }common line │{1: }{10: 15 }common line |
{1: }{10: 12 }DEF │{1: }{10: 16 }DEF |
{1: }{10: 13 }^something │{1: }{10: 17 }something |
{1: }{10: 14 } │{1: }{10: 18 } |
{7:Xtest-functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }|
:e |
]])
end)
it('put from window 1 using dp 1 line 5', function()
feed('1<c-w>w')
feed('5gg')
feed('dp')
screen:expect([[
{1: }{10: 1 } │{1: }{10: 1 } |
{1: }{10: 2 }common line │{1: }{10: 2 }common line |
{1: }{10: 3 }common line │{1: }{10: 3 }common line |
{1: }{10: 4 } │{1: }{10: 4 } |
{1: }{10: 5 }^ABCabc │{1: }{10: 5 }ABCabc |
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }|
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }|
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }|
{1: }{10: 6 }{8:ABC}{9:abc }│{1: }{10: 9 }{8:DEF}{9:abc }|
{1: }{10: 7 }{8:ABC}{9:abc }│{1: }{10: 10 }{8:DEF}{9:abc }|
{1: }{10: 8 }{8:ABC}{9:abc }│{1: }{10: 11 }{8:DEF}{9:abc }|
{1: }{10: 9 }common line │{1: }{10: 12 }common line |
{1: }{10: 10 }common line │{1: }{10: 13 }common line |
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }|
{1: }{10: 11 }common line │{1: }{10: 15 }common line |
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 16 }{4:DEF }|
{1: }{10: 12 }something │{1: }{10: 17 }something |
{1: }{10: 13 } │{1: }{10: 18 } |
{7:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 [+] }|
:e |
]])
end)
it('put from window 1 using dp 2 line 6', function()
feed('1<c-w>w')
feed('6gg')
feed('dp')
screen:expect([[
{1: }{10: 1 } │{1: }{10: 1 } |
{1: }{10: 2 }common line │{1: }{10: 2 }common line |
{1: }{10: 3 }common line │{1: }{10: 3 }common line |
{1: }{10: 4 } │{1: }{10: 4 } |
{1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }|
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }|
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }|
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }|
{1: }{10: 6 }^ABCabc │{1: }{10: 9 }ABCabc |
{1: }{10: 7 }ABCabc │{1: }{10: 10 }ABCabc |
{1: }{10: 8 }ABCabc │{1: }{10: 11 }ABCabc |
{1: }{10: 9 }common line │{1: }{10: 12 }common line |
{1: }{10: 10 }common line │{1: }{10: 13 }common line |
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }|
{1: }{10: 11 }common line │{1: }{10: 15 }common line |
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 16 }{4:DEF }|
{1: }{10: 12 }something │{1: }{10: 17 }something |
{1: }{10: 13 } │{1: }{10: 18 } |
{7:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 [+] }|
:e |
]])
end)
it('put from window 1 using dp 2 line 7', function()
feed('1<c-w>w')
feed('7gg')
feed('dp')
screen:expect([[
{1: }{10: 1 } │{1: }{10: 1 } |
{1: }{10: 2 }common line │{1: }{10: 2 }common line |
{1: }{10: 3 }common line │{1: }{10: 3 }common line |
{1: }{10: 4 } │{1: }{10: 4 } |
{1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }|
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }|
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }|
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }|
{1: }{10: 6 }ABCabc │{1: }{10: 9 }ABCabc |
{1: }{10: 7 }^ABCabc │{1: }{10: 10 }ABCabc |
{1: }{10: 8 }ABCabc │{1: }{10: 11 }ABCabc |
{1: }{10: 9 }common line │{1: }{10: 12 }common line |
{1: }{10: 10 }common line │{1: }{10: 13 }common line |
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }|
{1: }{10: 11 }common line │{1: }{10: 15 }common line |
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 16 }{4:DEF }|
{1: }{10: 12 }something │{1: }{10: 17 }something |
{1: }{10: 13 } │{1: }{10: 18 } |
{7:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 [+] }|
:e |
]])
end)
it('put from window 1 using dp 2 line 11', function()
feed('1<c-w>w')
feed('11gg')
feed('dp')
screen:expect([[
{1: }{10: 1 } │{1: }{10: 1 } |
{1: }{10: 2 }common line │{1: }{10: 2 }common line |
{1: }{10: 3 }common line │{1: }{10: 3 }common line |
{1: }{10: 4 } │{1: }{10: 4 } |
{1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }|
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }|
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }|
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }|
{1: }{10: 6 }{8:ABC}{9:abc }│{1: }{10: 9 }{8:DEF}{9:abc }|
{1: }{10: 7 }{8:ABC}{9:abc }│{1: }{10: 10 }{8:DEF}{9:abc }|
{1: }{10: 8 }{8:ABC}{9:abc }│{1: }{10: 11 }{8:DEF}{9:abc }|
{1: }{10: 9 }common line │{1: }{10: 12 }common line |
{1: }{10: 10 }common line │{1: }{10: 13 }common line |
{1: }{10: 11 }^common line │{1: }{10: 14 }common line |
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 15 }{4:DEF }|
{1: }{10: 12 }something │{1: }{10: 16 }something |
{1: }{10: 13 } │{1: }{10: 17 } |
{6:~ }│{6:~ }|
{7:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 [+] }|
:e |
]])
end)
it('put from window 1 using dp 2 line 12', function()
feed('1<c-w>w')
feed('12gg')
feed('dp')
screen:expect([[
{1: }{10: 1 } │{1: }{10: 1 } |
{1: }{10: 2 }common line │{1: }{10: 2 }common line |
{1: }{10: 3 }common line │{1: }{10: 3 }common line |
{1: }{10: 4 } │{1: }{10: 4 } |
{1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }|
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }|
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }|
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }|
{1: }{10: 6 }{8:ABC}{9:abc }│{1: }{10: 9 }{8:DEF}{9:abc }|
{1: }{10: 7 }{8:ABC}{9:abc }│{1: }{10: 10 }{8:DEF}{9:abc }|
{1: }{10: 8 }{8:ABC}{9:abc }│{1: }{10: 11 }{8:DEF}{9:abc }|
{1: }{10: 9 }common line │{1: }{10: 12 }common line |
{1: }{10: 10 }common line │{1: }{10: 13 }common line |
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }|
{1: }{10: 11 }common line │{1: }{10: 15 }common line |
{1: }{10: 12 }^something │{1: }{10: 16 }something |
{1: }{10: 13 } │{1: }{10: 17 } |
{6:~ }│{6:~ }|
{7:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 [+] }|
:e |
]])
end)
it('put from window 2 using dp line 6', function()
feed('2<c-w>w')
feed('6gg')
feed('dp')
screen:expect([[
{1: }{10: 1 } │{1: }{10: 1 } |
{1: }{10: 2 }common line │{1: }{10: 2 }common line |
{1: }{10: 3 }common line │{1: }{10: 3 }common line |
{1: }{10: 4 } │{1: }{10: 4 } |
{1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }|
{1: }{10: 6 }xyz │{1: }{10: 6 }^xyz |
{1: }{10: 7 }xyz │{1: }{10: 7 }xyz |
{1: }{10: 8 }xyz │{1: }{10: 8 }xyz |
{1: }{10: 9 }{8:ABC}{9:abc }│{1: }{10: 9 }{8:DEF}{9:abc }|
{1: }{10: 10 }{8:ABC}{9:abc }│{1: }{10: 10 }{8:DEF}{9:abc }|
{1: }{10: 11 }{8:ABC}{9:abc }│{1: }{10: 11 }{8:DEF}{9:abc }|
{1: }{10: 12 }common line │{1: }{10: 12 }common line |
{1: }{10: 13 }common line │{1: }{10: 13 }common line |
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }|
{1: }{10: 14 }common line │{1: }{10: 15 }common line |
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 16 }{4:DEF }|
{1: }{10: 15 }something │{1: }{10: 17 }something |
{1: }{10: 16 } │{1: }{10: 18 } |
{3:Xtest-functional-diff-screen-1.2 [+] }{7:Xtest-functional-diff-screen-1 }|
:e |
]])
end)
it('put from window 2 using dp line 8', function()
feed('2<c-w>w')
feed('8gg')
feed('dp')
screen:expect([[
{1: }{10: 1 } │{1: }{10: 1 } |
{1: }{10: 2 }common line │{1: }{10: 2 }common line |
{1: }{10: 3 }common line │{1: }{10: 3 }common line |
{1: }{10: 4 } │{1: }{10: 4 } |
{1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }|
{1: }{10: 6 }xyz │{1: }{10: 6 }xyz |
{1: }{10: 7 }xyz │{1: }{10: 7 }xyz |
{1: }{10: 8 }xyz │{1: }{10: 8 }^xyz |
{1: }{10: 9 }{8:ABC}{9:abc }│{1: }{10: 9 }{8:DEF}{9:abc }|
{1: }{10: 10 }{8:ABC}{9:abc }│{1: }{10: 10 }{8:DEF}{9:abc }|
{1: }{10: 11 }{8:ABC}{9:abc }│{1: }{10: 11 }{8:DEF}{9:abc }|
{1: }{10: 12 }common line │{1: }{10: 12 }common line |
{1: }{10: 13 }common line │{1: }{10: 13 }common line |
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }|
{1: }{10: 14 }common line │{1: }{10: 15 }common line |
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 16 }{4:DEF }|
{1: }{10: 15 }something │{1: }{10: 17 }something |
{1: }{10: 16 } │{1: }{10: 18 } |
{3:Xtest-functional-diff-screen-1.2 [+] }{7:Xtest-functional-diff-screen-1 }|
:e |
]])
end)
it('put from window 2 using dp line 9', function()
feed('2<c-w>w')
feed('9gg')
feed('dp')
screen:expect([[
{1: }{10: 1 } │{1: }{10: 1 } |
{1: }{10: 2 }common line │{1: }{10: 2 }common line |
{1: }{10: 3 }common line │{1: }{10: 3 }common line |
{1: }{10: 4 } │{1: }{10: 4 } |
{1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }|
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }|
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }|
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }|
{1: }{10: 6 }DEFabc │{1: }{10: 9 }^DEFabc |
{1: }{10: 7 }DEFabc │{1: }{10: 10 }DEFabc |
{1: }{10: 8 }DEFabc │{1: }{10: 11 }DEFabc |
{1: }{10: 9 }common line │{1: }{10: 12 }common line |
{1: }{10: 10 }common line │{1: }{10: 13 }common line |
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }|
{1: }{10: 11 }common line │{1: }{10: 15 }common line |
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 16 }{4:DEF }|
{1: }{10: 12 }something │{1: }{10: 17 }something |
{1: }{10: 13 } │{1: }{10: 18 } |
{3:Xtest-functional-diff-screen-1.2 [+] }{7:Xtest-functional-diff-screen-1 }|
:e |
]])
end)
it('put from window 2 using dp line 17', function()
feed('2<c-w>w')
feed('17gg')
feed('dp')
screen:expect([[
{1: }{10: 1 } │{1: }{10: 1 } |
{1: }{10: 2 }common line │{1: }{10: 2 }common line |
{1: }{10: 3 }common line │{1: }{10: 3 }common line |
{1: }{10: 4 } │{1: }{10: 4 } |
{1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }|
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }|
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }|
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }|
{1: }{10: 6 }{8:ABC}{9:abc }│{1: }{10: 9 }{8:DEF}{9:abc }|
{1: }{10: 7 }{8:ABC}{9:abc }│{1: }{10: 10 }{8:DEF}{9:abc }|
{1: }{10: 8 }{8:ABC}{9:abc }│{1: }{10: 11 }{8:DEF}{9:abc }|
{1: }{10: 9 }common line │{1: }{10: 12 }common line |
{1: }{10: 10 }common line │{1: }{10: 13 }common line |
{1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }|
{1: }{10: 11 }common line │{1: }{10: 15 }common line |
{1: }{10: 12 }DEF │{1: }{10: 16 }DEF |
{1: }{10: 13 }something │{1: }{10: 17 }^something |
{1: }{10: 14 } │{1: }{10: 18 } |
{3:Xtest-functional-diff-screen-1.2 [+] }{7:Xtest-functional-diff-screen-1 }|
:e |
]])
end)
end)
describe('setup a diff with 2 files and set linematch:10', function()
before_each(function()
feed(':set diffopt+=linematch:10<cr>')
local f1 = [[
common line
HIL
aABCabc
aABCabc
aABCabc
aABCabc
common line
HIL
common line
something
]]
local f2 = [[
common line
DEF
GHI
something
aDEFabc
xyz
xyz
xyz
aDEFabc
aDEFabc
aDEFabc
common line
DEF
GHI
something else
common line
something
]]
write_file(fname, f1, false)
write_file(fname_2, f2, false)
reread()
end)
it('enable linematch for the longest diff block by increasing the number argument passed to linematch', function()
feed('1<c-w>w')
-- linematch is disabled for the longest diff because it's combined line length is over 10
screen:expect([[
{1: }{10: 1 }^common line │{1: }{10: 1 }common line |
{1: }{10: 2 }{4:DEF }│{1: }{10: }{2:--------------------------------------------}|
{1: }{10: 3 }{8:GHI}{9: }│{1: }{10: 2 }{8:HIL}{9: }|
{1: }{10: 4 }{4:something }│{1: }{10: }{2:--------------------------------------------}|
{1: }{10: 5 } │{1: }{10: 3 } |
{1: }{10: 6 }{9:a}{8:DEF}{9:abc }│{1: }{10: 4 }{9:a}{8:ABC}{9:abc }|
{1: }{10: 7 }{8:xyz}{9: }│{1: }{10: 5 }{8:aABCabc}{9: }|
{1: }{10: 8 }{8:xyz}{9: }│{1: }{10: 6 }{8:aABCabc}{9: }|
{1: }{10: 9 }{8:xyz}{9: }│{1: }{10: 7 }{8:aABCabc}{9: }|
{1: }{10: 10 }{4:aDEFabc }│{1: }{10: }{2:--------------------------------------------}|
{1: }{10: 11 }{4:aDEFabc }│{1: }{10: }{2:--------------------------------------------}|
{1: }{10: 12 }{4:aDEFabc }│{1: }{10: }{2:--------------------------------------------}|
{1: }{10: 13 }common line │{1: }{10: 8 }common line |
{1: }{10: 14 }{4:DEF }│{1: }{10: }{2:--------------------------------------------}|
{1: }{10: 15 }{8:GHI}{9: }│{1: }{10: 9 }{8:HIL}{9: }|
{1: }{10: 16 }{4:something else }│{1: }{10: }{2:--------------------------------------------}|
{1: }{10: 17 }common line │{1: }{10: 10 }common line |
{1: }{10: 18 }something │{1: }{10: 11 }something |
{7:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 }|
:e |
]])
-- enable it by increasing the number
feed(":set diffopt-=linematch:10<cr>")
feed(":set diffopt+=linematch:30<cr>")
screen:expect([[
{1: }{10: 1 }^common line │{1: }{10: 1 }common line |
{1: }{10: 2 }{4:DEF }│{1: }{10: }{2:--------------------------------------------}|
{1: }{10: 3 }{8:GHI}{9: }│{1: }{10: 2 }{8:HIL}{9: }|
{1: }{10: 4 }{4:something }│{1: }{10: }{2:--------------------------------------------}|
{1: }{10: 5 } │{1: }{10: 3 } |
{1: }{10: 6 }{9:a}{8:DEF}{9:abc }│{1: }{10: 4 }{9:a}{8:ABC}{9:abc }|
{1: }{10: 7 }{4:xyz }│{1: }{10: }{2:--------------------------------------------}|
{1: }{10: 8 }{4:xyz }│{1: }{10: }{2:--------------------------------------------}|
{1: }{10: 9 }{4:xyz }│{1: }{10: }{2:--------------------------------------------}|
{1: }{10: 10 }{9:a}{8:DEF}{9:abc }│{1: }{10: 5 }{9:a}{8:ABC}{9:abc }|
{1: }{10: 11 }{9:a}{8:DEF}{9:abc }│{1: }{10: 6 }{9:a}{8:ABC}{9:abc }|
{1: }{10: 12 }{9:a}{8:DEF}{9:abc }│{1: }{10: 7 }{9:a}{8:ABC}{9:abc }|
{1: }{10: 13 }common line │{1: }{10: 8 }common line |
{1: }{10: 14 }{4:DEF }│{1: }{10: }{2:--------------------------------------------}|
{1: }{10: 15 }{8:GHI}{9: }│{1: }{10: 9 }{8:HIL}{9: }|
{1: }{10: 16 }{4:something else }│{1: }{10: }{2:--------------------------------------------}|
{1: }{10: 17 }common line │{1: }{10: 10 }common line |
{1: }{10: 18 }something │{1: }{10: 11 }something |
{7:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 }|
:set diffopt+=linematch:30 |
]])
end)
it('get all from second window', function()
feed('2<c-w>w')
feed(':1,12diffget<cr>')
screen:expect([[
{1: }{10: 1 }common line │{1: }{10: 1 }^common line |
{1: }{10: 2 }DEF │{1: }{10: 2 }DEF |
{1: }{10: 3 }GHI │{1: }{10: 3 }GHI |
{1: }{10: 4 }something │{1: }{10: 4 }something |
{1: }{10: 5 } │{1: }{10: 5 } |
{1: }{10: 6 }aDEFabc │{1: }{10: 6 }aDEFabc |
{1: }{10: 7 }xyz │{1: }{10: 7 }xyz |
{1: }{10: 8 }xyz │{1: }{10: 8 }xyz |
{1: }{10: 9 }xyz │{1: }{10: 9 }xyz |
{1: }{10: 10 }aDEFabc │{1: }{10: 10 }aDEFabc |
{1: }{10: 11 }aDEFabc │{1: }{10: 11 }aDEFabc |
{1: }{10: 12 }aDEFabc │{1: }{10: 12 }aDEFabc |
{1: }{10: 13 }common line │{1: }{10: 13 }common line |
{1: }{10: 14 }DEF │{1: }{10: 14 }DEF |
{1: }{10: 15 }GHI │{1: }{10: 15 }GHI |
{1: }{10: 16 }something else │{1: }{10: 16 }something else |
{1: }{10: 17 }common line │{1: }{10: 17 }common line |
{1: }{10: 18 }something │{1: }{10: 18 }something |
{3:Xtest-functional-diff-screen-1.2 }{7:Xtest-functional-diff-screen-1 [+] }|
:1,12diffget |
]])
end)
it('get all from first window', function()
feed('1<c-w>w')
feed(':1,19diffget<cr>')
screen:expect([[
{1: }{10: 1 }^common line │{1: }{10: 1 }common line |
{1: }{10: 2 }HIL │{1: }{10: 2 }HIL |
{1: }{10: 3 } │{1: }{10: 3 } |
{1: }{10: 4 }aABCabc │{1: }{10: 4 }aABCabc |
{1: }{10: 5 }aABCabc │{1: }{10: 5 }aABCabc |
{1: }{10: 6 }aABCabc │{1: }{10: 6 }aABCabc |
{1: }{10: 7 }aABCabc │{1: }{10: 7 }aABCabc |
{1: }{10: 8 }common line │{1: }{10: 8 }common line |
{1: }{10: 9 }HIL │{1: }{10: 9 }HIL |
{1: }{10: 10 }common line │{1: }{10: 10 }common line |
{1: }{10: 11 }something │{1: }{10: 11 }something |
{1: }{10: 12 } │{1: }{10: 12 } |
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{6:~ }│{6:~ }|
{7:Xtest-functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }|
:1,19diffget |
]])
end)
it('get part of the non linematched diff block in window 2 line 7 - 8 (non line matched block)', function()
feed('2<c-w>w')
feed(':7,8diffget<cr>')
screen:expect([[
{1: }{10: 1 }common line │{1: }{10: 1 }^common line |
{1: }{10: 2 }{4:DEF }│{1: }{10: }{2:--------------------------------------------}|
{1: }{10: 3 }{8:GHI}{9: }│{1: }{10: 2 }{8:HIL}{9: }|
{1: }{10: 4 }{4:something }│{1: }{10: }{2:--------------------------------------------}|
{1: }{10: 5 } │{1: }{10: 3 } |
{1: }{10: 6 }{9:a}{8:DEF}{9:abc }│{1: }{10: 4 }{9:a}{8:ABC}{9:abc }|
{1: }{10: 7 }{8:xyz}{9: }│{1: }{10: 5 }{8:aABCabc}{9: }|
{1: }{10: 8 }{8:xyz}{9: }│{1: }{10: 6 }{8:aABCabc}{9: }|
{1: }{10: 9 }xyz │{1: }{10: 7 }xyz |
{1: }{10: 10 }aDEFabc │{1: }{10: 8 }aDEFabc |
{1: }{10: 11 }aDEFabc │{1: }{10: 9 }aDEFabc |
{1: }{10: 12 }aDEFabc │{1: }{10: 10 }aDEFabc |
{1: }{10: 13 }common line │{1: }{10: 11 }common line |
{1: }{10: 14 }{4:DEF }│{1: }{10: }{2:--------------------------------------------}|
{1: }{10: 15 }{8:GHI}{9: }│{1: }{10: 12 }{8:HIL}{9: }|
{1: }{10: 16 }{4:something else }│{1: }{10: }{2:--------------------------------------------}|
{1: }{10: 17 }common line │{1: }{10: 13 }common line |
{1: }{10: 18 }something │{1: }{10: 14 }something |
{3:Xtest-functional-diff-screen-1.2 }{7:Xtest-functional-diff-screen-1 [+] }|
:7,8diffget |
]])
end)
it('get part of the non linematched diff block in window 2 line 8 - 10 (line matched block)', function()
feed('2<c-w>w')
feed(':8,10diffget<cr>')
screen:expect([[
{1: }{10: 1 }common line │{1: }{10: 1 }^common line |
{1: }{10: 2 }{4:DEF }│{1: }{10: }{2:--------------------------------------------}|
{1: }{10: 3 }{8:GHI}{9: }│{1: }{10: 2 }{8:HIL}{9: }|
{1: }{10: 4 }{4:something }│{1: }{10: }{2:--------------------------------------------}|
{1: }{10: 5 } │{1: }{10: 3 } |
{1: }{10: 6 }{9:a}{8:DEF}{9:abc }│{1: }{10: 4 }{9:a}{8:ABC}{9:abc }|
{1: }{10: 7 }{8:xyz}{9: }│{1: }{10: 5 }{8:aABCabc}{9: }|
{1: }{10: 8 }{8:xyz}{9: }│{1: }{10: 6 }{8:aABCabc}{9: }|
{1: }{10: 9 }{8:xyz}{9: }│{1: }{10: 7 }{8:aABCabc}{9: }|
{1: }{10: 10 }{4:aDEFabc }│{1: }{10: }{2:--------------------------------------------}|
{1: }{10: 11 }{4:aDEFabc }│{1: }{10: }{2:--------------------------------------------}|
{1: }{10: 12 }{4:aDEFabc }│{1: }{10: }{2:--------------------------------------------}|
{1: }{10: 13 }common line │{1: }{10: 8 }common line |
{1: }{10: 14 }DEF │{1: }{10: 9 }DEF |
{1: }{10: 15 }GHI │{1: }{10: 10 }GHI |
{1: }{10: 16 }something else │{1: }{10: 11 }something else |
{1: }{10: 17 }common line │{1: }{10: 12 }common line |
{1: }{10: 18 }something │{1: }{10: 13 }something |
{3:Xtest-functional-diff-screen-1.2 }{7:Xtest-functional-diff-screen-1 [+] }|
:8,10diffget |
]])
end)
end)
end)