0
0
mirror of https://github.com/vim/vim.git synced 2025-09-24 03:44:06 -04:00

patch 9.1.0096: diff() function uses 'diffexpr'

Problem:  diff() function uses 'diffexpr'
          (rickhowe)
Solution: Make diff() always use internal diff(), add support for
          unified diff context length, sort diff() options in help
          (Yegappan Lakshmanan)

fixes: #13989
closes: #14010

Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Yegappan Lakshmanan
2024-02-11 17:08:29 +01:00
committed by Christian Brabandt
parent 52eb0b8677
commit be156a31c5
4 changed files with 95 additions and 46 deletions

View File

@@ -1,4 +1,4 @@
*builtin.txt* For Vim version 9.1. Last change: 2024 Feb 01 *builtin.txt* For Vim version 9.1. Last change: 2024 Feb 11
VIM REFERENCE MANUAL by Bram Moolenaar VIM REFERENCE MANUAL by Bram Moolenaar
@@ -2074,20 +2074,22 @@ diff({fromlist}, {tolist} [, {options}]) *diff()*
The {options} Dict argument also specifies diff options The {options} Dict argument also specifies diff options
(similar to 'diffopt') and supports the following items: (similar to 'diffopt') and supports the following items:
algorithm Dict specifying the diff algorithm to
use. Supported boolean items are
"myers", "minimal", "patience" and
"histogram".
context unified diff context length. Default
is 1.
iblank ignore changes where lines are all iblank ignore changes where lines are all
blank. blank.
icase ignore changes in case of text. icase ignore changes in case of text.
indent-heuristic use the indent heuristic for the
internal diff library.
iwhite ignore changes in amount of white iwhite ignore changes in amount of white
space. space.
iwhiteall ignore all white space changes. iwhiteall ignore all white space changes.
iwhiteeol ignore white space changes at end of iwhiteeol ignore white space changes at end of
line. line.
indent-heuristic use the indent heuristic for the
internal diff library.
algorithm Dict specifying the diff algorithm to
use. Supported boolean items are
"myers", "minimal", "patience" and
"histogram".
For more information about these options, refer to 'diffopt'. For more information about these options, refer to 'diffopt'.
Returns an empty List or String if {fromlist} and {tolist} are Returns an empty List or String if {fromlist} and {tolist} are

View File

@@ -42,10 +42,6 @@ static int diff_flags = DIFF_INTERNAL | DIFF_FILLER | DIFF_CLOSE_OFF;
static long diff_algorithm = 0; static long diff_algorithm = 0;
#define DIFF_INTERNAL_OUTPUT_UNIFIED 1
#define DIFF_INTERNAL_OUTPUT_INDICES 2
static int diff_internal_output_fmt = DIFF_INTERNAL_OUTPUT_INDICES;
#define LBUFLEN 50 // length of line in diff file #define LBUFLEN 50 // length of line in diff file
static int diff_a_works = MAYBE; // TRUE when "diff -a" works, FALSE when it static int diff_a_works = MAYBE; // TRUE when "diff -a" works, FALSE when it
@@ -76,12 +72,19 @@ typedef struct {
long count_new; long count_new;
} diffhunk_T; } diffhunk_T;
typedef enum {
DIO_OUTPUT_INDICES = 0, // default
DIO_OUTPUT_UNIFIED = 1 // unified diff format
} dio_outfmt_T;
// two diff inputs and one result // two diff inputs and one result
typedef struct { typedef struct {
diffin_T dio_orig; // original file input diffin_T dio_orig; // original file input
diffin_T dio_new; // new file input diffin_T dio_new; // new file input
diffout_T dio_diff; // diff result diffout_T dio_diff; // diff result
int dio_internal; // using internal diff int dio_internal; // using internal diff
dio_outfmt_T dio_outfmt; // internal diff output format
int dio_ctxlen; // unified diff context length
} diffio_T; } diffio_T;
static int diff_buf_idx(buf_T *buf); static int diff_buf_idx(buf_T *buf);
@@ -1145,9 +1148,9 @@ diff_file_internal(diffio_T *diffio)
if (diff_flags & DIFF_IBLANK) if (diff_flags & DIFF_IBLANK)
param.flags |= XDF_IGNORE_BLANK_LINES; param.flags |= XDF_IGNORE_BLANK_LINES;
emit_cfg.ctxlen = 0; // don't need any diff_context here emit_cfg.ctxlen = diffio->dio_ctxlen;
emit_cb.priv = &diffio->dio_diff; emit_cb.priv = &diffio->dio_diff;
if (diff_internal_output_fmt == DIFF_INTERNAL_OUTPUT_INDICES) if (diffio->dio_outfmt == DIO_OUTPUT_INDICES)
emit_cfg.hunk_func = xdiff_out_indices; emit_cfg.hunk_func = xdiff_out_indices;
else else
emit_cb.out_line = xdiff_out_unified; emit_cb.out_line = xdiff_out_unified;
@@ -3476,7 +3479,8 @@ parse_diff_optarg(
typval_T *opts, typval_T *opts,
int *diffopts, int *diffopts,
long *diffalgo, long *diffalgo,
int *diff_output_fmt) dio_outfmt_T *diff_output_fmt,
int *diff_ctxlen)
{ {
dict_T *d = opts->vval.v_dict; dict_T *d = opts->vval.v_dict;
@@ -3497,9 +3501,9 @@ parse_diff_optarg(
if (output_fmt != NULL) if (output_fmt != NULL)
{ {
if (STRNCMP(output_fmt, "unified", 7) == 0) if (STRNCMP(output_fmt, "unified", 7) == 0)
*diff_output_fmt = DIFF_INTERNAL_OUTPUT_UNIFIED; *diff_output_fmt = DIO_OUTPUT_UNIFIED;
else if (STRNCMP(output_fmt, "indices", 7) == 0) else if (STRNCMP(output_fmt, "indices", 7) == 0)
*diff_output_fmt = DIFF_INTERNAL_OUTPUT_INDICES; *diff_output_fmt = DIO_OUTPUT_INDICES;
else else
{ {
semsg(_(e_unsupported_diff_output_format_str), output_fmt); semsg(_(e_unsupported_diff_output_format_str), output_fmt);
@@ -3507,6 +3511,10 @@ parse_diff_optarg(
} }
} }
*diff_ctxlen = dict_get_number_def(d, "context", 1);
if (*diff_ctxlen < 0)
*diff_ctxlen = 1;
if (dict_get_bool(d, "iblank", FALSE)) if (dict_get_bool(d, "iblank", FALSE))
*diffopts |= DIFF_IBLANK; *diffopts |= DIFF_IBLANK;
if (dict_get_bool(d, "icase", FALSE)) if (dict_get_bool(d, "icase", FALSE))
@@ -3602,33 +3610,43 @@ f_diff(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
// Save the 'diffopt' option value and restore it after getting the diff. // Save the 'diffopt' option value and restore it after getting the diff.
int save_diff_flags = diff_flags; int save_diff_flags = diff_flags;
long save_diff_algorithm = diff_algorithm; long save_diff_algorithm = diff_algorithm;
long save_diff_output_fmt = diff_internal_output_fmt;
diff_flags = DIFF_INTERNAL; diff_flags = DIFF_INTERNAL;
diff_algorithm = 0; diff_algorithm = 0;
diff_internal_output_fmt = DIFF_INTERNAL_OUTPUT_UNIFIED; dio.dio_outfmt = DIO_OUTPUT_UNIFIED;
if (argvars[2].v_type != VAR_UNKNOWN) if (argvars[2].v_type != VAR_UNKNOWN)
if (parse_diff_optarg(&argvars[2], &diff_flags, &diff_algorithm, if (parse_diff_optarg(&argvars[2], &diff_flags, &diff_algorithm,
&diff_internal_output_fmt) == FAIL) &dio.dio_outfmt, &dio.dio_ctxlen) == FAIL)
{
diff_internal_output_fmt = save_diff_output_fmt;
return; return;
}
// Concatenate the List of strings into a single string using newline // Concatenate the List of strings into a single string using newline
// separator. Internal diff library expects a single string. // separator. Internal diff library expects a single string.
list_to_diffin(orig_list, &dio.dio_orig, diff_flags & DIFF_ICASE); list_to_diffin(orig_list, &dio.dio_orig, diff_flags & DIFF_ICASE);
list_to_diffin(new_list, &dio.dio_new, diff_flags & DIFF_ICASE); list_to_diffin(new_list, &dio.dio_new, diff_flags & DIFF_ICASE);
// If 'diffexpr' is set, then the internal diff is not used. Set
// 'diffexpr' to an empty string temporarily.
int restore_diffexpr = FALSE;
char_u cc = *p_dex;
if (*p_dex != NUL)
{
restore_diffexpr = TRUE;
*p_dex = NUL;
}
// Compute the diff // Compute the diff
int diff_status = diff_file(&dio); int diff_status = diff_file(&dio);
// restore 'diffexpr'
if (restore_diffexpr)
*p_dex = cc;
if (diff_status == FAIL) if (diff_status == FAIL)
goto done; goto done;
int hunk_idx = 0; int hunk_idx = 0;
dict_T *hunk_dict; dict_T *hunk_dict;
if (diff_internal_output_fmt == DIFF_INTERNAL_OUTPUT_INDICES) if (dio.dio_outfmt == DIO_OUTPUT_INDICES)
{ {
if (rettv_list_alloc(rettv) != OK) if (rettv_list_alloc(rettv) != OK)
goto done; goto done;
@@ -3657,7 +3675,7 @@ f_diff(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
done: done:
clear_diffin(&dio.dio_new); clear_diffin(&dio.dio_new);
if (diff_internal_output_fmt == DIFF_INTERNAL_OUTPUT_INDICES) if (dio.dio_outfmt == DIO_OUTPUT_INDICES)
clear_diffout(&dio.dio_diff); clear_diffout(&dio.dio_diff);
else else
ga_clear(&dio.dio_diff.dout_ga); ga_clear(&dio.dio_diff.dout_ga);
@@ -3665,7 +3683,6 @@ done:
// Restore the 'diffopt' option value. // Restore the 'diffopt' option value.
diff_flags = save_diff_flags; diff_flags = save_diff_flags;
diff_algorithm = save_diff_algorithm; diff_algorithm = save_diff_algorithm;
diff_internal_output_fmt = save_diff_output_fmt;
# endif # endif
} }

View File

@@ -1719,43 +1719,43 @@ endfunc
set nodiff set nodiff
endfunc endfunc
" Test for the diff() function " Test for the diff() function
def Test_diff_func() def Test_diff_func()
# string is added/removed/modified at the beginning # string is added/removed/modified at the beginning
assert_equal("@@ -1 +1,2 @@\n+abc\n def\n", assert_equal("@@ -1 +1,2 @@\n+abc\n def\n",
diff(['def'], ['abc', 'def'], {output: 'unified'})) diff(['def'], ['abc', 'def'], {output: 'unified'}))
assert_equal([{from_idx: 0, from_count: 0, to_idx: 0, to_count: 1}], assert_equal([{from_idx: 0, from_count: 0, to_idx: 0, to_count: 1}],
diff(['def'], ['abc', 'def'], {output: 'indices'})) diff(['def'], ['abc', 'def'], {output: 'indices'}))
assert_equal("@@ -1,2 +1 @@\n-abc\n def\n", assert_equal("@@ -1,2 +1 @@\n-abc\n def\n",
diff(['abc', 'def'], ['def'], {output: 'unified'})) diff(['abc', 'def'], ['def'], {output: 'unified'}))
assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 0}], assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 0}],
diff(['abc', 'def'], ['def'], {output: 'indices'})) diff(['abc', 'def'], ['def'], {output: 'indices'}))
assert_equal("@@ -1,2 +1,2 @@\n-abc\n+abx\n def\n", assert_equal("@@ -1,2 +1,2 @@\n-abc\n+abx\n def\n",
diff(['abc', 'def'], ['abx', 'def'], {output: 'unified'})) diff(['abc', 'def'], ['abx', 'def'], {output: 'unified'}))
assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 1}], assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 1}],
diff(['abc', 'def'], ['abx', 'def'], {output: 'indices'})) diff(['abc', 'def'], ['abx', 'def'], {output: 'indices'}))
# string is added/removed/modified at the end # string is added/removed/modified at the end
assert_equal("@@ -1 +1,2 @@\n abc\n+def\n", assert_equal("@@ -1 +1,2 @@\n abc\n+def\n",
diff(['abc'], ['abc', 'def'], {output: 'unified'})) diff(['abc'], ['abc', 'def'], {output: 'unified'}))
assert_equal([{from_idx: 1, from_count: 0, to_idx: 1, to_count: 1}], assert_equal([{from_idx: 1, from_count: 0, to_idx: 1, to_count: 1}],
diff(['abc'], ['abc', 'def'], {output: 'indices'})) diff(['abc'], ['abc', 'def'], {output: 'indices'}))
assert_equal("@@ -1,2 +1 @@\n abc\n-def\n", assert_equal("@@ -1,2 +1 @@\n abc\n-def\n",
diff(['abc', 'def'], ['abc'], {output: 'unified'})) diff(['abc', 'def'], ['abc'], {output: 'unified'}))
assert_equal([{from_idx: 1, from_count: 1, to_idx: 1, to_count: 0}], assert_equal([{from_idx: 1, from_count: 1, to_idx: 1, to_count: 0}],
diff(['abc', 'def'], ['abc'], {output: 'indices'})) diff(['abc', 'def'], ['abc'], {output: 'indices'}))
assert_equal("@@ -1,2 +1,2 @@\n abc\n-def\n+xef\n", assert_equal("@@ -1,2 +1,2 @@\n abc\n-def\n+xef\n",
diff(['abc', 'def'], ['abc', 'xef'], {output: 'unified'})) diff(['abc', 'def'], ['abc', 'xef'], {output: 'unified'}))
assert_equal([{from_idx: 1, from_count: 1, to_idx: 1, to_count: 1}], assert_equal([{from_idx: 1, from_count: 1, to_idx: 1, to_count: 1}],
diff(['abc', 'def'], ['abc', 'xef'], {output: 'indices'})) diff(['abc', 'def'], ['abc', 'xef'], {output: 'indices'}))
# string is added/removed/modified in the middle # string is added/removed/modified in the middle
assert_equal("@@ -2,2 +2,3 @@\n 222\n+xxx\n 333\n", assert_equal("@@ -2,2 +2,3 @@\n 222\n+xxx\n 333\n",
diff(['111', '222', '333'], ['111', '222', 'xxx', '333'], {output: 'unified'})) diff(['111', '222', '333'], ['111', '222', 'xxx', '333'], {output: 'unified'}))
assert_equal([{from_idx: 2, from_count: 0, to_idx: 2, to_count: 1}], assert_equal([{from_idx: 2, from_count: 0, to_idx: 2, to_count: 1}],
diff(['111', '222', '333'], ['111', '222', 'xxx', '333'], {output: 'indices'})) diff(['111', '222', '333'], ['111', '222', 'xxx', '333'], {output: 'indices'}))
assert_equal("@@ -2,3 +2,2 @@\n 222\n-333\n 444\n", assert_equal("@@ -2,3 +2,2 @@\n 222\n-333\n 444\n",
diff(['111', '222', '333', '444'], ['111', '222', '444'], {output: 'unified'})) diff(['111', '222', '333', '444'], ['111', '222', '444'], {output: 'unified'}))
assert_equal([{from_idx: 2, from_count: 1, to_idx: 2, to_count: 0}], assert_equal([{from_idx: 2, from_count: 1, to_idx: 2, to_count: 0}],
diff(['111', '222', '333', '444'], ['111', '222', '444'], {output: 'indices'})) diff(['111', '222', '333', '444'], ['111', '222', '444'], {output: 'indices'}))
assert_equal("@@ -2,3 +2,3 @@\n 222\n-333\n+xxx\n 444\n", assert_equal("@@ -2,3 +2,3 @@\n 222\n-333\n+xxx\n 444\n",
@@ -1825,18 +1825,17 @@ def Test_diff_func()
END END
tolist =<< trim END tolist =<< trim END
one abc two one abc two
three four three four
five six five six
END END
assert_equal("@@ -1 +1 @@\n-one two\n+one abc two\n@@ -3 +3 @@\n-five abc six\n+five six\n",
assert_equal("@@ -1,3 +1,3 @@\n-one two\n+one abc two\n three four\n-five abc six\n+five six\n", assert_equal("@@ -1,3 +1,3 @@\n-one two\n+one abc two\n three four\n-five abc six\n+five six\n",
diff(fromlist, tolist, {output: 'unified'})) diff(fromlist, tolist, {output: 'unified'}))
assert_equal([{from_idx: 0, from_count: 3, to_idx: 0, to_count: 3}], assert_equal([{from_idx: 0, from_count: 3, to_idx: 0, to_count: 3}],
diff(fromlist, tolist, {output: 'indices'})) diff(fromlist, tolist, {output: 'indices'}))
# add/remove blank lines # add/remove blank lines
assert_equal("@@ -1,4 +1,2 @@\n one\n-\n-\n two\n", assert_equal("@@ -1,4 +1,2 @@\n one\n-\n-\n two\n",
diff(['one', '', '', 'two'], ['one', 'two'], {output: 'unified'})) diff(['one', '', '', 'two'], ['one', 'two'], {output: 'unified'}))
assert_equal([{from_idx: 1, from_count: 2, to_idx: 1, to_count: 0}], assert_equal([{from_idx: 1, from_count: 2, to_idx: 1, to_count: 0}],
diff(['one', '', '', 'two'], ['one', 'two'], {output: 'indices'})) diff(['one', '', '', 'two'], ['one', 'two'], {output: 'indices'}))
assert_equal("@@ -1,2 +1,4 @@\n one\n+\n+\n two\n", assert_equal("@@ -1,2 +1,4 @@\n one\n+\n+\n two\n",
@@ -1887,11 +1886,40 @@ def Test_diff_func()
# identical strings # identical strings
assert_equal('', diff(['111', '222'], ['111', '222'], {output: 'unified'})) assert_equal('', diff(['111', '222'], ['111', '222'], {output: 'unified'}))
assert_equal([], diff(['111', '222'], ['111', '222'], {output: 'indices'})) assert_equal([], diff(['111', '222'], ['111', '222'], {output: 'indices'}))
assert_equal('', diff([], [], {output: 'unified'}))
assert_equal([], diff([], [], {output: 'indices'}))
# If 'diffexpr' is set, it should not be used for diff()
def MyDiffExpr()
enddef
var save_diffexpr = &diffexpr
:set diffexpr=MyDiffExpr()
assert_equal("@@ -1 +1 @@\n-abc\n+\n",
diff(['abc'], [''], {output: 'unified'}))
assert_equal([{'from_idx': 0, 'from_count': 1, 'to_idx': 0, 'to_count': 1}],
diff(['abc'], [''], {output: 'indices'}))
assert_equal('MyDiffExpr()', &diffexpr)
&diffexpr = save_diffexpr
# try different values for unified diff 'context'
assert_equal("@@ -0,0 +1 @@\n+x\n",
diff(['a', 'b', 'c'], ['x', 'a', 'b', 'c']))
assert_equal("@@ -0,0 +1 @@\n+x\n",
diff(['a', 'b', 'c'], ['x', 'a', 'b', 'c'], {context: 0}))
assert_equal("@@ -1 +1,2 @@\n+x\n a\n",
diff(['a', 'b', 'c'], ['x', 'a', 'b', 'c'], {context: 1}))
assert_equal("@@ -1,2 +1,3 @@\n+x\n a\n b\n",
diff(['a', 'b', 'c'], ['x', 'a', 'b', 'c'], {context: 2}))
assert_equal("@@ -1,3 +1,4 @@\n+x\n a\n b\n c\n",
diff(['a', 'b', 'c'], ['x', 'a', 'b', 'c'], {context: 3}))
assert_equal("@@ -1,3 +1,4 @@\n+x\n a\n b\n c\n",
diff(['a', 'b', 'c'], ['x', 'a', 'b', 'c'], {context: 4}))
assert_equal("@@ -1 +1,2 @@\n+x\n a\n", assert_equal("@@ -1 +1,2 @@\n+x\n a\n",
diff(['a', 'b', 'c'], ['x', 'a', 'b', 'c'], {context: -1})) diff(['a', 'b', 'c'], ['x', 'a', 'b', 'c'], {context: -1}))
# Error cases # Error cases
assert_fails('call diff({}, ["a"])', 'E1211:') assert_fails('call diff({}, ["a"])', 'E1211:')
assert_fails('call diff(["a"], {})', 'E1211:')
assert_fails('call diff(["a"], ["a"], [])', 'E1206:') assert_fails('call diff(["a"], ["a"], [])', 'E1206:')
assert_fails('call diff(["a"], ["a"], {output: "xyz"})', 'E106: Unsupported diff output format: xyz') assert_fails('call diff(["a"], ["a"], {output: "xyz"})', 'E106: Unsupported diff output format: xyz')
assert_fails('call diff(["a"], ["a"], {context: []})', 'E745: Using a List as a Number') assert_fails('call diff(["a"], ["a"], {context: []})', 'E745: Using a List as a Number')

View File

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