mirror of
https://github.com/neovim/neovim.git
synced 2024-09-17 20:58:20 -04:00
Compare commits
28 Commits
8e44cc44d2
...
493804eece
Author | SHA1 | Date | |
---|---|---|---|
|
493804eece | ||
|
931b3f0693 | ||
|
e78ed6f347 | ||
|
66df7c3413 | ||
|
66715e9ce3 | ||
|
9ebcc1131b | ||
|
8985e9292b | ||
|
0e110d180f | ||
|
7d07185d99 | ||
|
729fa8ea7d | ||
|
eae703b044 | ||
|
867ee3a892 | ||
|
65f6a6b76b | ||
|
209fc88c8f | ||
|
9247062387 | ||
|
d9a3363896 | ||
|
0371a006d0 | ||
|
34cf5a3fee | ||
|
f40e576ebc | ||
|
e76b0f2990 | ||
|
d21977db34 | ||
|
b3119cba2b | ||
|
027ee9ebed | ||
|
17e1d68529 | ||
|
95c2958324 | ||
|
bcd5311279 | ||
|
7e63013bd3 | ||
|
0db7e59325 |
@ -763,6 +763,8 @@ struct diffblock_S {
|
||||
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?
|
||||
size_t n_charmatch;
|
||||
int *charmatchp; // values for charmatch
|
||||
};
|
||||
|
||||
#define SNAP_HELP_IDX 0
|
||||
|
380
src/nvim/diff.c
380
src/nvim/diff.c
@ -10,6 +10,7 @@
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -84,11 +85,15 @@ static bool diff_need_update = false; // ex_diffupdate needs to be called
|
||||
#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 DIFF_CHARDIFF 0x2000 // character-wise matching
|
||||
#define DIFF_WORDDIFF 0x4000 // character-wise matching
|
||||
#define ALL_WHITE_DIFF (DIFF_IWHITE | DIFF_IWHITEALL | DIFF_IWHITEEOL)
|
||||
static int diff_flags = DIFF_INTERNAL | DIFF_FILLER | DIFF_CLOSE_OFF;
|
||||
|
||||
static int diff_algorithm = 0;
|
||||
static int linematch_lines = 0;
|
||||
static int chardiff_chars = 0;
|
||||
static int worddiff_words = 0;
|
||||
|
||||
#define LBUFLEN 50 // length of line in diff file
|
||||
|
||||
@ -130,6 +135,11 @@ typedef enum {
|
||||
DIFF_NONE,
|
||||
} diffstyle_T;
|
||||
|
||||
typedef enum {
|
||||
LINEMATCH,
|
||||
CHARMATCH,
|
||||
WORDMATCH,
|
||||
} diff_allignment_T;
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "diff.c.generated.h"
|
||||
#endif
|
||||
@ -522,6 +532,7 @@ static diff_T *diff_alloc_new(tabpage_T *tp, diff_T *dprev, diff_T *dp)
|
||||
{
|
||||
diff_T *dnew = xmalloc(sizeof(*dnew));
|
||||
|
||||
dnew->charmatchp = NULL;
|
||||
dnew->is_linematched = false;
|
||||
dnew->df_next = dp;
|
||||
if (dprev == NULL) {
|
||||
@ -536,6 +547,7 @@ static diff_T *diff_alloc_new(tabpage_T *tp, diff_T *dprev, diff_T *dp)
|
||||
static diff_T *diff_free(tabpage_T *tp, diff_T *dprev, diff_T *dp)
|
||||
{
|
||||
diff_T *ret = dp->df_next;
|
||||
xfree(dp->charmatchp);
|
||||
xfree(dp);
|
||||
|
||||
if (dprev == NULL) {
|
||||
@ -1647,6 +1659,7 @@ static void process_hunk(diff_T **dpp, diff_T **dprevp, int idx_orig, int idx_ne
|
||||
|
||||
while (dn != dp->df_next) {
|
||||
dpl = dn->df_next;
|
||||
xfree(dn->charmatchp);
|
||||
xfree(dn);
|
||||
dn = dpl;
|
||||
}
|
||||
@ -1753,11 +1766,18 @@ void diff_clear(tabpage_T *tp)
|
||||
diff_T *next_p;
|
||||
for (diff_T *p = tp->tp_first_diff; p != NULL; p = next_p) {
|
||||
next_p = p->df_next;
|
||||
xfree(p->charmatchp);
|
||||
xfree(p);
|
||||
}
|
||||
tp->tp_first_diff = NULL;
|
||||
}
|
||||
|
||||
/// Return true if char diff option is enabled.
|
||||
bool chardiff(void)
|
||||
{
|
||||
return (diff_flags & DIFF_CHARDIFF) || (diff_flags & DIFF_WORDDIFF);
|
||||
}
|
||||
|
||||
/// Return true if the options are set to use diff linematch.
|
||||
bool diff_linematch(diff_T *dp)
|
||||
{
|
||||
@ -2001,13 +2021,23 @@ static void apply_linematch_results(diff_T *dp, size_t decisions_length, const i
|
||||
dp->is_linematched = true;
|
||||
}
|
||||
|
||||
static void run_linematch_algorithm(diff_T *dp)
|
||||
static void run_alignment_algorithm(diff_T *dp, diff_allignment_T diff_allignment)
|
||||
{
|
||||
// define buffers for diff algorithm
|
||||
mmfile_t diffbufs_mm[DB_COUNT];
|
||||
const char *diffbufs[DB_COUNT];
|
||||
int diff_length[DB_COUNT];
|
||||
mmfile_t diffbufs_mm[DB_COUNT] = { 0 };
|
||||
char *diffbufs[DB_COUNT] = { 0 };
|
||||
int diff_length[DB_COUNT] = { 0 };
|
||||
int diff_lines[DB_COUNT] = { 0 };
|
||||
size_t ndiffs = 0;
|
||||
size_t total_word_count = 0;
|
||||
size_t total_chars_length = 0;
|
||||
size_t *word_offset_size[DB_COUNT] = { 0 }; // mapping array used for charmatch
|
||||
size_t *word_offset[DB_COUNT] = { 0 }; // mapping array used for charmatch
|
||||
size_t word_offset_result_index[DB_COUNT] = { 0 }; // mapping array used for charmatch
|
||||
size_t *iwhite_index_offset = NULL; // mapping array used for charmatch
|
||||
size_t result_diff_start_pos[DB_COUNT]; // the position in the result array where this
|
||||
// an array for index mapping with iwhite
|
||||
const bool iwhite = (diff_flags & (DIFF_IWHITEALL | DIFF_IWHITE)) > 0;
|
||||
for (int i = 0; i < DB_COUNT; i++) {
|
||||
if (curtab->tp_diffbuf[i] != NULL) {
|
||||
// write the contents of the entire buffer to
|
||||
@ -2019,28 +2049,216 @@ static void run_linematch_algorithm(diff_T *dp)
|
||||
// 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];
|
||||
diff_lines[ndiffs] = dp->df_count[i];
|
||||
if (diff_allignment == CHARMATCH || diff_allignment == WORDMATCH) {
|
||||
// before removing whitespace for charmatch
|
||||
result_diff_start_pos[ndiffs] = total_chars_length;
|
||||
// get the length of each of the diffs
|
||||
int lines = dp->df_count[i];
|
||||
const char *p = diffbufs[ndiffs];
|
||||
while (lines) {
|
||||
total_chars_length++; // increment the total characters counter
|
||||
if (*p == '\n') {
|
||||
lines--;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
} else if (diff_allignment == LINEMATCH) {
|
||||
// LINEMATCH
|
||||
// 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);
|
||||
|
||||
// allocate all the memory we will need to keep track of tokens positions and their respective
|
||||
// lengths. For word matching, this is the 'word' as vim defines it, for character matching, the
|
||||
// token is the one or more 8 bit 'chars' that make up a utf character
|
||||
if (diff_allignment == WORDMATCH || diff_allignment == CHARMATCH) {
|
||||
// are we ignoring whitespace in the comparison?
|
||||
if (iwhite) {
|
||||
// allocate array for index mapping of result array
|
||||
iwhite_index_offset = xmalloc(total_chars_length * sizeof(size_t));
|
||||
}
|
||||
for (size_t i = 0; i < ndiffs; i++) {
|
||||
word_offset[i] = xmalloc(total_chars_length * sizeof(size_t));
|
||||
word_offset_size[i] = xmalloc(total_chars_length * sizeof(size_t));
|
||||
for (size_t j = 0; j < total_chars_length; j++) {
|
||||
word_offset_size[i][j] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
// calculate the token lengths and white space offset and pre process the contents of the diffs to
|
||||
// remove white space if necessary
|
||||
for (size_t i = 0; i < ndiffs; i++) {
|
||||
XFREE_CLEAR(diffbufs_mm[i].ptr);
|
||||
int cls = INT_MIN; // keep track of what type of character this is, to determine when we are
|
||||
// moving to a different word
|
||||
|
||||
size_t j = 0; // j will iterate over each character in each of the diffs
|
||||
|
||||
size_t k = 0; // k represents the index of the current character if there were no white spaces,
|
||||
// so we will use k and j to calculate the white space offset and use it later to
|
||||
// populate the final results for drawing to the screen
|
||||
// if 'iwhite' is not used, k will always be the same as j
|
||||
|
||||
size_t lines = (size_t)diff_lines[i]; // we iterate over each line of this part of the diff
|
||||
|
||||
size_t w = result_diff_start_pos[i]; // keep track of the offset of all the characters without
|
||||
// any whitespace, so that we can ignore the white space
|
||||
// while calculating the diff, and then use this to
|
||||
// populate the results
|
||||
size_t cur_char_length = 0;
|
||||
|
||||
while (lines > 0) {
|
||||
if (iwhite && (diffbufs[i][j] == ' ' || diffbufs[i][j] == '\t')) {
|
||||
// we are using 'iwhite' and this is a whitespace, so it will not be included as a token in
|
||||
// the diff algorithm
|
||||
// we are ignoring whitespace and this is a whitespace ' ' or '\t' reset the class definition
|
||||
cls = INT_MIN;
|
||||
} else {
|
||||
// we have a character which is not a blank (or we are not using iwhite)
|
||||
|
||||
// how we determine when there is a new token depends on if this is chardiff or worddiff
|
||||
if (diff_allignment == WORDMATCH) {
|
||||
// WORDMATCH
|
||||
if (utf_class(diffbufs[i][j]) != cls || diffbufs[i][j] == '\n') {
|
||||
// this is a new token
|
||||
word_offset[i][diff_length[i]] = k; // mark the offset of this without whitespace
|
||||
diff_length[i]++; // this diff length has another token, so it gets longer
|
||||
total_word_count++;
|
||||
}
|
||||
cls = utf_class(diffbufs[i][j]);
|
||||
word_offset_size[i][diff_length[i] - 1]++; // still the same class (iterating over the
|
||||
// same type of word), so the current word
|
||||
// length is getting longer
|
||||
} else if (diff_allignment == CHARMATCH) {
|
||||
// CHARMATCH
|
||||
if (cur_char_length == 0) {
|
||||
// get the length of current character
|
||||
if (diffbufs[i][j] == '\n') {
|
||||
// this is the last character of the line
|
||||
cur_char_length = 1;
|
||||
} else {
|
||||
cur_char_length = (size_t)utfc_ptr2len((const char *const)&diffbufs[i][j]);
|
||||
}
|
||||
word_offset[i][diff_length[i]] = k;
|
||||
diff_length[i]++;
|
||||
total_word_count++;
|
||||
// the token size is the length of this utf character
|
||||
word_offset_size[i][diff_length[i] - 1] = cur_char_length;
|
||||
}
|
||||
cur_char_length--;
|
||||
}
|
||||
// if ignoring whitespace, keep track of the white space index
|
||||
if (iwhite && (diff_allignment == CHARMATCH || diff_allignment == WORDMATCH)) {
|
||||
// keep track of the index offset with ignoring whitespace to use when populating the results
|
||||
iwhite_index_offset[w++] = j - k;
|
||||
}
|
||||
diffbufs[i][k++] = diffbufs[i][j];
|
||||
}
|
||||
if (diffbufs[i][j++] == '\n') {
|
||||
lines--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apply_linematch_results(dp, decisions_length, decisions);
|
||||
// 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)
|
||||
if (diff_allignment == LINEMATCH) {
|
||||
int *decisions = NULL;
|
||||
size_t decisions_length = linematch_nbuffers((const char **)diffbufs, diff_length, ndiffs,
|
||||
&decisions, 0, NULL, NULL);
|
||||
apply_linematch_results(dp, decisions_length, decisions);
|
||||
xfree(decisions);
|
||||
} else if (diff_allignment == CHARMATCH || diff_allignment == WORDMATCH) {
|
||||
dp->charmatchp = xmalloc(total_chars_length * sizeof(int)); // will hold results
|
||||
dp->n_charmatch = total_chars_length;
|
||||
|
||||
xfree(decisions);
|
||||
bool lim_exceeded = false;
|
||||
if (diff_allignment == CHARMATCH && total_chars_length > (size_t)chardiff_chars) {
|
||||
lim_exceeded = true;
|
||||
} else if (diff_allignment == WORDMATCH && total_word_count > (size_t)worddiff_words) {
|
||||
lim_exceeded = true;
|
||||
}
|
||||
|
||||
if (lim_exceeded == true) {
|
||||
// do not run charmatch on the entire diff block
|
||||
// we will attempt to run charmatch on the individual lines later
|
||||
// for now, just initialize the result memory
|
||||
for (size_t i = 0; i < total_chars_length; i++) {
|
||||
dp->charmatchp[i] = -1; // -1 indicates that algorithm has not yet ran
|
||||
}
|
||||
} else {
|
||||
for (size_t i = 0; i < total_chars_length; i++) {
|
||||
dp->charmatchp[i] = 0; // default to not highlighted
|
||||
}
|
||||
|
||||
// check is this a line that does not exist in other buffers?
|
||||
// if so, highlight it as a 'newline', and we don't need to run the algorithm
|
||||
bool newline = true;
|
||||
for (size_t i = 0, c = 0; i < ndiffs; i++) {
|
||||
if (diff_length[i] > 0) {
|
||||
c++;
|
||||
}
|
||||
if (c > 1) {
|
||||
newline = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (newline == true) {
|
||||
for (size_t i = 0; i < total_chars_length; i++) {
|
||||
dp->charmatchp[i] = 2;
|
||||
}
|
||||
} else {
|
||||
int *decisions = NULL;
|
||||
size_t decisions_length = linematch_nbuffers((const char **)diffbufs, diff_length, ndiffs,
|
||||
&decisions, 1, word_offset, word_offset_size);
|
||||
for (size_t i = 0; i < decisions_length; i++) {
|
||||
if (decisions[i] == (pow(2, (double)ndiffs) - 1)) {
|
||||
// it's a comparison of all the buffers (don't highlight)
|
||||
for (size_t j = 0; j < ndiffs; j++) {
|
||||
for (size_t k = 0;
|
||||
k <
|
||||
(diff_allignment ==
|
||||
WORDMATCH ? word_offset_size[j][word_offset_result_index[j]] : 1); k++) {
|
||||
size_t l = result_diff_start_pos[j]++;
|
||||
dp->charmatchp[iwhite_index_offset ? iwhite_index_offset[l] + l : l] = 0;
|
||||
}
|
||||
word_offset_result_index[j]++;
|
||||
}
|
||||
} else {
|
||||
// it's a skip in a single buffer (highlight as changed)
|
||||
for (size_t j = 0; j < ndiffs; j++) {
|
||||
if (decisions[i] & (1 << j)) {
|
||||
for (size_t k = 0;
|
||||
k <
|
||||
(diff_allignment ==
|
||||
WORDMATCH ? word_offset_size[j][word_offset_result_index[j]] : 1); k++) {
|
||||
size_t l = result_diff_start_pos[j]++;
|
||||
dp->charmatchp[iwhite_index_offset ? iwhite_index_offset[l] + l : l] = 1;
|
||||
}
|
||||
word_offset_result_index[j]++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
xfree(decisions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ndiffs; i++) {
|
||||
xfree(word_offset[i]);
|
||||
xfree(word_offset_size[i]);
|
||||
XFREE_CLEAR(diffbufs_mm[i].ptr);
|
||||
}
|
||||
xfree(iwhite_index_offset);
|
||||
}
|
||||
|
||||
/// Check diff status for line "lnum" in buffer "buf":
|
||||
@ -2105,7 +2323,7 @@ int diff_check_with_linestatus(win_T *wp, linenr_T lnum, int *linestatus)
|
||||
// above the screen.
|
||||
if (lnum >= wp->w_topline && lnum < wp->w_botline
|
||||
&& !dp->is_linematched && diff_linematch(dp)) {
|
||||
run_linematch_algorithm(dp);
|
||||
run_alignment_algorithm(dp, LINEMATCH);
|
||||
}
|
||||
|
||||
if (dp->is_linematched) {
|
||||
@ -2412,6 +2630,8 @@ int diffopt_changed(void)
|
||||
{
|
||||
int diff_context_new = 6;
|
||||
int linematch_lines_new = 0;
|
||||
int chardiff_chars_new = 0;
|
||||
int worddiff_words_new = 0;
|
||||
int diff_flags_new = 0;
|
||||
int diff_foldcolumn_new = 2;
|
||||
int diff_algorithm_new = 0;
|
||||
@ -2487,6 +2707,14 @@ int diffopt_changed(void)
|
||||
p += 10;
|
||||
linematch_lines_new = getdigits_int(&p, false, linematch_lines_new);
|
||||
diff_flags_new |= DIFF_LINEMATCH;
|
||||
} else if ((strncmp(p, "chardiff:", 9) == 0) && ascii_isdigit(p[9])) {
|
||||
p += 9;
|
||||
chardiff_chars_new = getdigits_int(&p, false, chardiff_chars_new);
|
||||
diff_flags_new |= DIFF_CHARDIFF;
|
||||
} else if ((strncmp(p, "worddiff:", 9) == 0) && ascii_isdigit(p[9])) {
|
||||
p += 9;
|
||||
worddiff_words_new = getdigits_int(&p, false, worddiff_words_new);
|
||||
diff_flags_new |= DIFF_WORDDIFF;
|
||||
}
|
||||
|
||||
if ((*p != ',') && (*p != NUL)) {
|
||||
@ -2516,6 +2744,8 @@ int diffopt_changed(void)
|
||||
diff_flags = diff_flags_new;
|
||||
diff_context = diff_context_new == 0 ? 1 : diff_context_new;
|
||||
linematch_lines = linematch_lines_new;
|
||||
chardiff_chars = chardiff_chars_new;
|
||||
worddiff_words = worddiff_words_new;
|
||||
diff_foldcolumn = diff_foldcolumn_new;
|
||||
diff_algorithm = diff_algorithm_new;
|
||||
|
||||
@ -2563,7 +2793,8 @@ bool diffopt_filler(void)
|
||||
/// @param endp last char of the change
|
||||
///
|
||||
/// @return true if the line was added, no other buffer has it.
|
||||
bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
|
||||
bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp, int **hlresult,
|
||||
bool *diffchars_lim_exceeded, size_t *diffchars_line_len)
|
||||
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
// Make a copy of the line, the next ml_get() will invalidate it.
|
||||
@ -2604,6 +2835,97 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
|
||||
bool added = true;
|
||||
|
||||
linenr_T off = lnum - dp->df_lnum[idx];
|
||||
if (chardiff()) {
|
||||
diff_allignment_T diff_allignment;
|
||||
if (diff_flags & DIFF_CHARDIFF) {
|
||||
// if both chardiff & worddiff are enabled, it will pick chardiff
|
||||
diff_allignment = CHARMATCH;
|
||||
} else if (diff_flags & DIFF_WORDDIFF) {
|
||||
diff_allignment = WORDMATCH;
|
||||
}
|
||||
if (dp->charmatchp == NULL) {
|
||||
// get the first buffers
|
||||
// try running on the whole diff buffer first
|
||||
run_alignment_algorithm(dp, diff_allignment);
|
||||
}
|
||||
size_t charcount = 0;
|
||||
for (int i = 0; i < DB_COUNT; i++) {
|
||||
// for each diff buffer
|
||||
if (curtab->tp_diffbuf[i] != NULL) {
|
||||
for (int j = 0; j < dp->df_count[i]; j++) {
|
||||
// for each line in that buffer
|
||||
// get a pointer to the line
|
||||
char *diffline = ml_get_buf(curtab->tp_diffbuf[i], dp->df_lnum[i] + j);
|
||||
while (*diffline != '\0') {
|
||||
diffline++; charcount++;
|
||||
}
|
||||
charcount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dp->n_charmatch != charcount) {
|
||||
// we need to re run if the length of the diff has changed
|
||||
// count the number of characters in this diff
|
||||
// the line is currently being edited in insert mode, so pause highlighting until the diff is
|
||||
// recalculated, then resume the charmatch highlighting
|
||||
(*hlresult) = NULL;
|
||||
} else {
|
||||
// charmatchp is not null, is the whole thing already diffed?
|
||||
// get the correct offset for hlresult
|
||||
//
|
||||
// if the character count is not null
|
||||
size_t hlresult_line_offset = 0;
|
||||
// get the offset for the highlight of this line
|
||||
*diffchars_line_len = strlen(ml_get_buf(curtab->tp_diffbuf[idx], dp->df_lnum[idx] + off));
|
||||
hlresult_line_offset = get_buffer_position(idx, dp, off);
|
||||
if (*(dp->charmatchp + hlresult_line_offset) == -1) {
|
||||
diff_T dp_tmp;
|
||||
for (int i = 0; i < DB_COUNT; i++) {
|
||||
if (curtab->tp_diffbuf[i] != NULL) {
|
||||
dp_tmp.df_lnum[i] = dp->df_lnum[i] + off;
|
||||
dp_tmp.df_count[i] = off < dp->df_count[i] ? 1 : 0;
|
||||
}
|
||||
}
|
||||
// this line has not yet been calculated
|
||||
// run charmatch on this line of the diff
|
||||
// figure out how many buffers we are diffing
|
||||
// what line number is this in each buffer?
|
||||
run_alignment_algorithm(&dp_tmp, diff_allignment);
|
||||
if (dp_tmp.n_charmatch > 0) {
|
||||
for (int i = 0, p = 0; i < DB_COUNT; i++) {
|
||||
if (curtab->tp_diffbuf[i] != NULL) {
|
||||
// get the offset in the original charmatchp
|
||||
if (off < dp->df_count[i]) {
|
||||
size_t length = strlen(ml_get_buf(curtab->tp_diffbuf[i], dp->df_lnum[i] + off)) + 1;
|
||||
size_t k = get_buffer_position(i, dp, off);
|
||||
for (size_t m = 0; m < length; m++) {
|
||||
int val = dp_tmp.charmatchp[p++];
|
||||
dp->charmatchp[k + m] = val == -1 ? -2 : val; // if this individual line is still
|
||||
// too long to diff, mark it as a
|
||||
// -2, meaning it's been attempted
|
||||
// already
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// extract the results from here
|
||||
}
|
||||
xfree(dp_tmp.charmatchp);
|
||||
}
|
||||
(*hlresult) = dp->charmatchp + hlresult_line_offset;
|
||||
}
|
||||
if ((*hlresult) == NULL) {
|
||||
xfree(line_org);
|
||||
return false;
|
||||
} else if ((*hlresult)[0] != -2) { // -2 indicates that we've attempted a character wise diff with the
|
||||
xfree(line_org);
|
||||
return false; // entire block, and with this individual line, and still exceeded
|
||||
} else { // the character limit
|
||||
//
|
||||
*diffchars_lim_exceeded = true; // go to the default highlighting behaviour without character
|
||||
} // wise matching
|
||||
}
|
||||
|
||||
for (int i = 0; i < DB_COUNT; i++) {
|
||||
if ((curtab->tp_diffbuf[i] != NULL) && (i != idx)) {
|
||||
// Skip lines that are not in the other change (filler lines).
|
||||
@ -3440,3 +3762,25 @@ static int xdiff_out(int start_a, int count_a, int start_b, int count_b, void *p
|
||||
}));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// get the position in the character diff buffer of this line
|
||||
static size_t get_buffer_position(const int idx, const diff_T *dp, linenr_T offset)
|
||||
{
|
||||
size_t comparison_mem_offset = 0;
|
||||
for (int i = 0; i < DB_COUNT; i++) {
|
||||
if ((curtab->tp_diffbuf[i] != NULL)) {
|
||||
for (int j = 0; j < ((i == idx) ? offset : dp->df_count[i]); j++) {
|
||||
char *diffline = ml_get_buf(curtab->tp_diffbuf[i], dp->df_lnum[i] + j);
|
||||
while (*diffline != '\0') {
|
||||
diffline++; comparison_mem_offset++;
|
||||
}
|
||||
comparison_mem_offset++; // count the '\0' character as the newline marker for each line
|
||||
}
|
||||
if (i == idx) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// what is the line length for this pointer?
|
||||
return comparison_mem_offset;
|
||||
}
|
||||
|
@ -1137,10 +1137,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
|
||||
int bg_attr = win_bg_attr(wp);
|
||||
|
||||
int linestatus = 0;
|
||||
bool diffchars_lim_exceeded = false;
|
||||
size_t diffchars_line_len = 0;
|
||||
int *hlresult = NULL;
|
||||
wlv.filler_lines = diff_check_with_linestatus(wp, lnum, &linestatus);
|
||||
if (wlv.filler_lines < 0 || linestatus < 0) {
|
||||
if (wlv.filler_lines == -1 || linestatus == -1) {
|
||||
if (diff_find_change(wp, lnum, &change_start, &change_end)) {
|
||||
if (diff_find_change(wp, lnum, &change_start, &change_end, &hlresult,
|
||||
&diffchars_lim_exceeded, &diffchars_line_len)) {
|
||||
wlv.diff_hlf = HLF_ADD; // added line
|
||||
} else if (change_start == 0) {
|
||||
wlv.diff_hlf = HLF_TXD; // changed text
|
||||
@ -1721,16 +1725,32 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
|
||||
}
|
||||
|
||||
if (wlv.diff_hlf != (hlf_T)0) {
|
||||
// When there is extra text (eg: virtual text) it gets the
|
||||
// diff highlighting for the line, but not for changed text.
|
||||
if (wlv.diff_hlf == HLF_CHD && ptr - line >= change_start
|
||||
&& wlv.n_extra == 0) {
|
||||
wlv.diff_hlf = HLF_TXD; // changed text
|
||||
}
|
||||
if (wlv.diff_hlf == HLF_TXD && ((ptr - line > change_end && wlv.n_extra == 0)
|
||||
|| (wlv.n_extra > 0 && wlv.extra_for_extmark))) {
|
||||
wlv.diff_hlf = HLF_CHD; // changed line
|
||||
if (chardiff() && !diffchars_lim_exceeded) {
|
||||
if (wlv.diff_hlf != HLF_ADD) {
|
||||
if (hlresult == NULL) {
|
||||
wlv.diff_hlf = HLF_CHD;
|
||||
} else if (hlresult[0] == 2) {
|
||||
wlv.diff_hlf = HLF_ADD;
|
||||
} else if ((size_t)(ptr - line) < diffchars_line_len
|
||||
&& (hlresult[ptr - line] == 1 || hlresult[ptr - line] == -2)) {
|
||||
wlv.diff_hlf = HLF_TXD;
|
||||
} else {
|
||||
wlv.diff_hlf = HLF_CHD;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// When there is extra text (eg: virtual text) it gets the
|
||||
// diff highlighting for the line, but not for changed text.
|
||||
if (wlv.diff_hlf == HLF_CHD && ptr - line >= change_start
|
||||
&& wlv.n_extra == 0) {
|
||||
wlv.diff_hlf = HLF_TXD; // changed text
|
||||
}
|
||||
if (wlv.diff_hlf == HLF_TXD && ((ptr - line > change_end && wlv.n_extra == 0)
|
||||
|| (wlv.n_extra > 0 && wlv.extra_for_extmark))) {
|
||||
wlv.diff_hlf = HLF_CHD; // changed line
|
||||
}
|
||||
}
|
||||
|
||||
wlv.line_attr = win_hl_attr(wp, (int)wlv.diff_hlf);
|
||||
// Overlay CursorLine onto diff-mode highlight.
|
||||
if (wlv.cul_attr) {
|
||||
|
@ -1304,6 +1304,9 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
static int change_start = 0;
|
||||
static int change_end = 0;
|
||||
static hlf_T hlID = (hlf_T)0;
|
||||
bool diffchars_lim_exceeded = false;
|
||||
size_t diffchars_line_len = 0;
|
||||
int *hlresult = NULL;
|
||||
|
||||
if (lnum < 0) { // ignore type error in {lnum} arg
|
||||
lnum = 0;
|
||||
@ -1318,7 +1321,8 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
if (filler_lines == -1 || linestatus == -1) {
|
||||
change_start = MAXCOL;
|
||||
change_end = -1;
|
||||
if (diff_find_change(curwin, lnum, &change_start, &change_end)) {
|
||||
if (diff_find_change(curwin, lnum, &change_start, &change_end, &hlresult,
|
||||
&diffchars_lim_exceeded, &diffchars_line_len)) {
|
||||
hlID = HLF_ADD; // added line
|
||||
} else {
|
||||
hlID = HLF_CHD; // changed line
|
||||
@ -1336,10 +1340,26 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
|
||||
if (hlID == HLF_CHD || hlID == HLF_TXD) {
|
||||
int col = (int)tv_get_number(&argvars[1]) - 1; // Ignore type error in {col}.
|
||||
if (col >= change_start && col <= change_end) {
|
||||
hlID = HLF_TXD; // Changed text.
|
||||
//
|
||||
if (chardiff() && !diffchars_lim_exceeded) {
|
||||
if (hlID != HLF_ADD) {
|
||||
if (hlresult == NULL) {
|
||||
hlID = HLF_CHD;
|
||||
} else if (hlresult[0] == 2) {
|
||||
hlID = HLF_ADD;
|
||||
} else if ((size_t)col < diffchars_line_len
|
||||
&& (hlresult[col] == 1 || hlresult[col] == -2)) {
|
||||
hlID = HLF_TXD;
|
||||
} else {
|
||||
hlID = HLF_CHD;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
hlID = HLF_CHD; // Changed line.
|
||||
if (col >= change_start && col <= change_end) {
|
||||
hlID = HLF_TXD; // Changed text.
|
||||
} else {
|
||||
hlID = HLF_CHD; // Changed line.
|
||||
}
|
||||
}
|
||||
}
|
||||
rettv->vval.v_number = hlID == (hlf_T)0 ? 0 : (hlID + 1);
|
||||
|
@ -38,38 +38,6 @@ static size_t line_len(const char *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] = NUL;
|
||||
}
|
||||
int matching = matching_chars(strsproc[0], strsproc[1]);
|
||||
xfree(strsproc[0]);
|
||||
xfree(strsproc[1]);
|
||||
return matching;
|
||||
}
|
||||
|
||||
#define MATCH_CHAR_MAX_LEN 800
|
||||
|
||||
/// Return matching characters between "s1" and "s2" whilst respecting sequence order.
|
||||
@ -119,7 +87,7 @@ static int matching_chars(const char *s1, const char *s2)
|
||||
/// @param sp
|
||||
/// @param fomvals
|
||||
/// @param n
|
||||
static int count_n_matched_chars(const char **sp, const size_t n, bool iwhite)
|
||||
static int count_n_matched_chars(const char **sp, const size_t n)
|
||||
{
|
||||
int matched_chars = 0;
|
||||
int matched = 0;
|
||||
@ -127,9 +95,7 @@ static int count_n_matched_chars(const char **sp, const size_t n, bool iwhite)
|
||||
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]);
|
||||
matched_chars += matching_chars(sp[i], sp[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -145,7 +111,7 @@ static int count_n_matched_chars(const char **sp, const size_t n, bool iwhite)
|
||||
|
||||
void fastforward_buf_to_lnum(const char **s, linenr_T lnum)
|
||||
{
|
||||
for (int i = 0; i < lnum - 1; i++) {
|
||||
for (long i = 0; i < lnum; i++) {
|
||||
*s = strchr(*s, '\n');
|
||||
if (!*s) {
|
||||
return;
|
||||
@ -168,20 +134,32 @@ void fastforward_buf_to_lnum(const char **s, linenr_T lnum)
|
||||
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)
|
||||
bool charmatch, size_t **word_offset, size_t **word_offset_size)
|
||||
{
|
||||
if (path_idx == npaths) {
|
||||
if ((*choice) > 0) {
|
||||
int from_vals[LN_MAX_BUFS] = { 0 };
|
||||
const int *to_vals = df_iters;
|
||||
const char *current_lines[LN_MAX_BUFS];
|
||||
size_t word_len[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, df_iters[k]);
|
||||
if (charmatch) {
|
||||
if (word_offset[k] != NULL) {
|
||||
// get start position
|
||||
p += word_offset[k][df_iters[k] - 1];
|
||||
word_len[k] = word_offset_size[k][df_iters[k] - 1];
|
||||
// index of the word for comparison
|
||||
} else {
|
||||
p += df_iters[k] - 1; // advance by the character count
|
||||
}
|
||||
} else {
|
||||
fastforward_buf_to_lnum(&p, df_iters[k] - 1);
|
||||
}
|
||||
current_lines[k] = p;
|
||||
} else {
|
||||
current_lines[k] = NULL;
|
||||
@ -189,7 +167,42 @@ static void try_possible_paths(const int *df_iters, const size_t *paths, const i
|
||||
}
|
||||
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 matched_chars = 0;
|
||||
if (charmatch) {
|
||||
// only two valid options for charmatch
|
||||
// 1. skip of a single character
|
||||
// 2. a combination of 'ndiffs' characters, when not equal to '\n'
|
||||
char t[256];
|
||||
t[0] = '\0';
|
||||
size_t t_l = 0;
|
||||
size_t compared = 0;
|
||||
for (size_t i = 0; i < ndiffs; i++) {
|
||||
if (current_lines[i] != NULL) {
|
||||
compared++;
|
||||
if ((t[0] != '\0' && !compare(current_lines[i],
|
||||
(word_offset[i] != NULL ? word_len[i] : 1), t, t_l)) // if theres more than one to compare, and
|
||||
// they're not matching
|
||||
|| (t[0] != '\0' && current_lines[i][0] == '\n')) { // if there's more than one to compare, and
|
||||
// at least one is a '\n'
|
||||
return; // not a possible path
|
||||
} else if (t[0] != '\0') {
|
||||
matched_chars = 1; // comparison of all buffers
|
||||
}
|
||||
size_t this_word_length = word_offset[i] != NULL ? word_len[i] : 1;
|
||||
t_l = this_word_length;
|
||||
for (size_t l = 0; l < this_word_length; l++) {
|
||||
t[l] = current_lines[i][l];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(compared == ndiffs || compared == 1)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
matched_chars = count_n_matched_chars(current_lines, ndiffs);
|
||||
}
|
||||
|
||||
int score = diffcmppath[unwrapped_idx_from].df_lev_score + matched_chars;
|
||||
if (score > diffcmppath[unwrapped_idx_to].df_lev_score) {
|
||||
diffcmppath[unwrapped_idx_to].df_path_n = 1;
|
||||
@ -207,10 +220,12 @@ static void try_possible_paths(const int *df_iters, const size_t *paths, const i
|
||||
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);
|
||||
diffcmppath, diff_len, ndiffs, diff_blk, charmatch, word_offset,
|
||||
word_offset_size);
|
||||
*(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);
|
||||
diffcmppath, diff_len, ndiffs, diff_blk, charmatch, word_offset,
|
||||
word_offset_size);
|
||||
}
|
||||
|
||||
/// unwrap indexes to access n dimensional tensor
|
||||
@ -245,7 +260,7 @@ static size_t unwrap_indexes(const int *values, const int *diff_len, const size_
|
||||
/// @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)
|
||||
bool charmatch, size_t **word_offset, size_t **word_offset_size)
|
||||
{
|
||||
if (ch_dim == ndiffs) {
|
||||
int npaths = 0;
|
||||
@ -259,16 +274,19 @@ static void populate_tensor(int *df_iters, const size_t ch_dim, diffcmppath_T *d
|
||||
}
|
||||
int choice = 0;
|
||||
size_t unwrapper_idx_to = unwrap_indexes(df_iters, diff_len, ndiffs);
|
||||
diffcmppath[unwrapper_idx_to].df_lev_score = -1;
|
||||
if (unwrapper_idx_to > 0) {
|
||||
diffcmppath[unwrapper_idx_to].df_lev_score = -1;
|
||||
}
|
||||
try_possible_paths(df_iters, paths, npaths, 0, &choice, diffcmppath,
|
||||
diff_len, ndiffs, diff_blk, iwhite);
|
||||
diff_len, ndiffs, diff_blk, charmatch, word_offset,
|
||||
word_offset_size);
|
||||
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);
|
||||
ndiffs, diff_blk, charmatch, word_offset, word_offset_size);
|
||||
}
|
||||
}
|
||||
|
||||
@ -328,7 +346,8 @@ static void populate_tensor(int *df_iters, const size_t ch_dim, diffcmppath_T *d
|
||||
/// @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)
|
||||
int **decisions, bool charmatch, size_t **word_offset,
|
||||
size_t **word_offset_size)
|
||||
{
|
||||
assert(ndiffs <= LN_MAX_BUFS);
|
||||
|
||||
@ -353,7 +372,8 @@ size_t linematch_nbuffers(const char **diff_blk, const int *diff_len, const size
|
||||
|
||||
// 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);
|
||||
populate_tensor(df_iters, 0, diffcmppath, diff_len, ndiffs, diff_blk, charmatch,
|
||||
word_offset, word_offset_size);
|
||||
|
||||
const size_t u = unwrap_indexes(diff_len, diff_len, ndiffs);
|
||||
diffcmppath_T *startNode = &diffcmppath[u];
|
||||
@ -402,3 +422,16 @@ static size_t test_charmatch_paths(diffcmppath_T *node, int lastdecision)
|
||||
}
|
||||
return (size_t)node->df_choice_mem[lastdecision];
|
||||
}
|
||||
// return true if these two strings are equal
|
||||
static bool compare(const char *s1, size_t l1, const char *s2, size_t l2)
|
||||
{
|
||||
if (l1 != l2) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < l1; i++) {
|
||||
if (s1[i] != s2[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -67,14 +67,29 @@ static void get_linematch_results(lua_State *lstate, mmfile_t *ma, mmfile_t *mb,
|
||||
int count_a, int start_b, int 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 };
|
||||
char *diff_begin[2] = { ma->ptr, mb->ptr };
|
||||
int diff_length[2] = { count_a, count_b };
|
||||
|
||||
fastforward_buf_to_lnum(&diff_begin[0], (linenr_T)start_a + 1);
|
||||
fastforward_buf_to_lnum(&diff_begin[1], (linenr_T)start_b + 1);
|
||||
fastforward_buf_to_lnum((const char **)(&diff_begin[0]), (linenr_T)start_a + 1);
|
||||
fastforward_buf_to_lnum((const char **)(&diff_begin[1]), (linenr_T)start_b + 1);
|
||||
|
||||
if (iwhite) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
size_t j = 0, k = 0, lines = (size_t)diff_length[i];
|
||||
while (lines > 0) {
|
||||
if (diff_begin[i][j] != ' ' && diff_begin[i][j] != '\t') {
|
||||
diff_begin[i][k++] = diff_begin[i][j];
|
||||
}
|
||||
if (diff_begin[i][j++] == '\n') {
|
||||
lines--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int *decisions = NULL;
|
||||
size_t decisions_length = linematch_nbuffers(diff_begin, diff_length, 2, &decisions, iwhite);
|
||||
size_t decisions_length = linematch_nbuffers((const char **)diff_begin, diff_length, 2,
|
||||
&decisions, 0, NULL, NULL);
|
||||
|
||||
int lnuma = start_a;
|
||||
int lnumb = start_b;
|
||||
|
@ -803,6 +803,73 @@ void testFunction () {
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
describe('setup a diff with 2 files and set linematch:30', function()
|
||||
before_each(function()
|
||||
feed(':set diffopt+=linematch:30<cr>')
|
||||
local f1 = [[
|
||||
start
|
||||
?a
|
||||
]]
|
||||
local f2 = [[
|
||||
start
|
||||
!b
|
||||
!a
|
||||
!c
|
||||
]]
|
||||
write_file(fname, f1, false)
|
||||
write_file(fname_2, f2, false)
|
||||
reread()
|
||||
end)
|
||||
it('display results', function()
|
||||
screen:expect([[
|
||||
{7: }{8: 1 }^start │{7: }{8: 1 }start |
|
||||
{7: }{8: 2 }{22:!b }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 3 }{22:!a }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 4 }{4: }{27:!c}{4: }│{7: }{8: 2 }{4: }{27:?a}{4: }|
|
||||
{7: }{8: 5 } │{7: }{8: 3 } |
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }|
|
||||
:e |
|
||||
]])
|
||||
end)
|
||||
it('display results with ignore white', function()
|
||||
feed(':set diffopt+=iwhiteall<cr>')
|
||||
screen:expect([[
|
||||
{7: }{8: 1 }^start │{7: }{8: 1 }start |
|
||||
{7: }{8: 2 }{22:!b }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 3 }{27:!}{4:a }│{7: }{8: 2 }{4: }{27:?}{4:a }|
|
||||
{7: }{8: 4 }{22: !c }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 5 } │{7: }{8: 3 } |
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }|
|
||||
:set diffopt+=iwhiteall |
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
describe('a diff that would result in multiple groups before grouping optimization', function()
|
||||
before_each(function()
|
||||
feed(':set diffopt+=linematch:30<cr>')
|
||||
@ -1084,6 +1151,436 @@ something
|
||||
end
|
||||
)
|
||||
end)
|
||||
describe('show a diff with charmatch enabled', function()
|
||||
before_each(function()
|
||||
local f1 = [[
|
||||
abbcabbcdefghijklmnop
|
||||
]]
|
||||
local f2 = [[
|
||||
abca?bc
|
||||
dfgh?ijl
|
||||
mnop?
|
||||
]]
|
||||
write_file(fname, f1, false)
|
||||
write_file(fname_2, f2, false)
|
||||
reread()
|
||||
end)
|
||||
describe('when the entire hunk is compared, cross-line', function()
|
||||
before_each(function()
|
||||
feed(':set diffopt+=chardiff:100<cr>')
|
||||
end)
|
||||
it('display results', function()
|
||||
screen:expect([[
|
||||
{7: }{8: 1 }{4:^abca}{27:?}{4:bc }│{7: }{8: 1 }{4:a}{27:b}{4:bca}{27:b}{4:bcd}{27:e}{4:fghij}{27:k}{4:lmnop }|
|
||||
{7: }{8: 2 }{4:dfgh}{27:?}{4:ijl }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 3 }{4:mnop}{27:?}{4: }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 4 } │{7: }{8: 2 } |
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }|
|
||||
:set diffopt+=chardiff:100 |
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
describe('when the single line is compared, cross-line', function()
|
||||
before_each(function()
|
||||
feed(':set diffopt+=chardiff:30<cr>')
|
||||
end)
|
||||
it('display results', function()
|
||||
screen:expect([[
|
||||
{7: }{8: 1 }{4:^abca}{27:?}{4:bc }│{7: }{8: 1 }{4:a}{27:b}{4:bca}{27:b}{4:bc}{27:defghijklmnop}{4: }|
|
||||
{7: }{8: 2 }{22:dfgh?ijl }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 3 }{22:mnop? }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 4 } │{7: }{8: 2 } |
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }|
|
||||
:set diffopt+=chardiff:30 |
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
describe('when the diff hunk and the single line are too long to run chardiff', function()
|
||||
before_each(function()
|
||||
feed(':set diffopt+=chardiff:10<cr>')
|
||||
end)
|
||||
it('display results', function()
|
||||
screen:expect([[
|
||||
{7: }{8: 1 }{4:^ab}{27:ca?bc}{4: }│{7: }{8: 1 }{4:ab}{27:bcabbcdefghijklmnop}{4: }|
|
||||
{7: }{8: 2 }{22:dfgh?ijl }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 3 }{22:mnop? }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 4 } │{7: }{8: 2 } |
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }|
|
||||
:set diffopt+=chardiff:10 |
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
describe('show a diff with wordmatch enabled', function()
|
||||
before_each(function()
|
||||
local f1 = [[
|
||||
wA w1 wB w1 w2 wC w3 w4
|
||||
]]
|
||||
local f2 = [[
|
||||
w1 w2
|
||||
w2 w3
|
||||
w4 w5
|
||||
]]
|
||||
write_file(fname, f1, false)
|
||||
write_file(fname_2, f2, false)
|
||||
reread()
|
||||
end)
|
||||
|
||||
describe('when the entire hunk is compared, cross-line', function()
|
||||
it('display results', function()
|
||||
feed(':set diffopt+=worddiff:30<cr>')
|
||||
screen:expect([[
|
||||
{7: }{8: 1 }{4:^w1 w2 }│{7: }{8: 1 }{27:wA w1 wB }{4:w1 w2}{27: wC}{4: w3}{27: }{4:w4 }|
|
||||
{7: }{8: 2 }{27:w2}{4: w3 }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 3 }{4:w4}{27: w5}{4: }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 4 } │{7: }{8: 2 } |
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }|
|
||||
:set diffopt+=worddiff:30 |
|
||||
]])
|
||||
end)
|
||||
it('display results, with ignore white', function()
|
||||
feed(':set diffopt+=worddiff:20<cr>:set diffopt+=iwhiteall<cr>')
|
||||
screen:expect([[
|
||||
{7: }{8: 1 }{4:^w1 w2 }│{7: }{8: 1 }{27:wA}{4: }{27:w1}{4: }{27:wB}{4: w1 w2 }{27:wC}{4: w3 w4 }|
|
||||
{7: }{8: 2 }{27:w2}{4: w3 }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 3 }{4:w4 }{27:w5}{4: }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 4 } │{7: }{8: 2 } |
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }|
|
||||
:set diffopt+=iwhiteall |
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('when the single line is compared, cross-line', function()
|
||||
it('display results', function()
|
||||
feed(':set diffopt+=worddiff:20<cr>')
|
||||
screen:expect([[
|
||||
{7: }{8: 1 }{4:^w1 w2 }│{7: }{8: 1 }{27:wA w1 wB }{4:w1 w2}{27: wC w3 w4}{4: }|
|
||||
{7: }{8: 2 }{22:w2 w3 }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 3 }{22:w4 w5 }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 4 } │{7: }{8: 2 } |
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }|
|
||||
:set diffopt+=worddiff:20 |
|
||||
]])
|
||||
end)
|
||||
it('display results, with ignore white', function()
|
||||
feed(':set diffopt+=worddiff:15<cr>:set diffopt+=iwhiteall<cr>')
|
||||
screen:expect([[
|
||||
{7: }{8: 1 }{4:^w1 w2 }│{7: }{8: 1 }{27:wA}{4: }{27:w1}{4: }{27:wB}{4: w1 w2 }{27:wC}{4: }{27:w3}{4: }{27:w4}{4: }|
|
||||
{7: }{8: 2 }{22:w2 w3 }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 3 }{22:w4 w5 }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 4 } │{7: }{8: 2 } |
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }|
|
||||
:set diffopt+=iwhiteall |
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
describe('when the diff hunk and the single line are too long to run chardiff', function()
|
||||
it('display results', function()
|
||||
feed(':set diffopt+=worddiff:10<cr>')
|
||||
screen:expect([[
|
||||
{7: }{8: 1 }{4:^w}{27:1 w2}{4: }│{7: }{8: 1 }{4:w}{27:A w1 wB w1 w2 wC w3 w4}{4: }|
|
||||
{7: }{8: 2 }{22:w2 w3 }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 3 }{22:w4 w5 }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 4 } │{7: }{8: 2 } |
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }|
|
||||
:set diffopt+=worddiff:10 |
|
||||
]])
|
||||
end)
|
||||
it('display results, with ignore white', function()
|
||||
feed(':set diffopt+=worddiff:10<cr>:set diffopt+=iwhiteall<cr>')
|
||||
screen:expect([[
|
||||
{7: }{8: 1 }{4:^w}{27:1 w2}{4: }│{7: }{8: 1 }{4:w}{27:A w1 wB w1 w2 wC w3 w4}{4: }|
|
||||
{7: }{8: 2 }{22:w2 w3 }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 3 }{22:w4 w5 }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 4 } │{7: }{8: 2 } |
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }|
|
||||
:set diffopt+=iwhiteall |
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
describe('show a diff with charmatch enabled, with and without ignore white', function()
|
||||
before_each(function()
|
||||
local f1 = [[
|
||||
ababcabcdabcde
|
||||
]]
|
||||
local f2 = [[
|
||||
abc abcd abcde abcdef
|
||||
]]
|
||||
write_file(fname, f1, false)
|
||||
write_file(fname_2, f2, false)
|
||||
reread()
|
||||
end)
|
||||
describe('normal comparison, including whitespace', function()
|
||||
before_each(function()
|
||||
feed(':set diffopt+=chardiff:100<cr>')
|
||||
end)
|
||||
it('display results', function()
|
||||
screen:expect([[
|
||||
{7: }{8: 1 }{4:^ab}{27:c }{4:abc}{27:d }{4:abcd}{27:e }{4:abcde}{27:f}{4: }│{7: }{8: 1 }{4:ababcabcdabcde }|
|
||||
{7: }{8: 2 } │{7: }{8: 2 } |
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }|
|
||||
:set diffopt+=chardiff:100 |
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
describe('ignore whitespace', function()
|
||||
before_each(function()
|
||||
feed(':set diffopt+=chardiff:100<cr>:set diffopt+=iwhiteall<cr>')
|
||||
end)
|
||||
it('display results', function()
|
||||
screen:expect([[
|
||||
{7: }{8: 1 }{4:^ab}{27:c}{4: abc}{27:d}{4: abcd}{27:e}{4: abcde}{27:f}{4: }│{7: }{8: 1 }{4:ababcabcdabcde }|
|
||||
{7: }{8: 2 } │{7: }{8: 2 } |
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }|
|
||||
:set diffopt+=iwhiteall |
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
describe('show a diff with charmatch enabled, with different UTF-8 character', function()
|
||||
before_each(function()
|
||||
local f1 = [[
|
||||
aaaहaaa
|
||||
]]
|
||||
local f2 = [[
|
||||
aaaसaaa
|
||||
]]
|
||||
write_file(fname, f1, false)
|
||||
write_file(fname_2, f2, false)
|
||||
reread()
|
||||
end)
|
||||
describe('normal comparison, including whitespace', function()
|
||||
before_each(function()
|
||||
feed(':set diffopt+=chardiff:100<cr>')
|
||||
end)
|
||||
it('display results', function()
|
||||
screen:expect([[
|
||||
{7: }{8: 1 }{4:^aaa}{27:स}{4:a}{27:a}{4:a }│{7: }{8: 1 }{4:aaa}{27:ह}{4:a}{27:a}{4:a }|
|
||||
{7: }{8: 2 } │{7: }{8: 2 } |
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }|
|
||||
:set diffopt+=chardiff:100 |
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
describe('show a diff with charmatch enabled, with same UTF-8 character', function()
|
||||
before_each(function()
|
||||
local f1 = [[
|
||||
aaaहaaa
|
||||
]]
|
||||
local f2 = [[
|
||||
aaaहaaa
|
||||
]]
|
||||
write_file(fname, f1, false)
|
||||
write_file(fname_2, f2, false)
|
||||
reread()
|
||||
end)
|
||||
describe('normal comparison, including whitespace', function()
|
||||
before_each(function()
|
||||
feed(':set diffopt+=chardiff:100<cr>')
|
||||
end)
|
||||
it('display results', function()
|
||||
screen:expect([[
|
||||
{7:+ }{8: 1 }{13:^+-- 2 lines: aaaहaaa······················}│{7:+ }{8: 1 }{13:+-- 2 lines: aaaहaaa·······················}|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }|
|
||||
:set diffopt+=chardiff:100 |
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('regressions', function()
|
||||
|
Loading…
Reference in New Issue
Block a user