mirror of
https://github.com/vim/vim.git
synced 2025-09-24 03:44:06 -04:00
patch 9.1.0071: Need a diff() Vim script function
Problem: Need a diff() Vim script function Solution: Add the diff() Vim script function using the xdiff internal diff library, add support for "unified" and "indices" mode. (Yegappan Lakshmanan) fixes: #4241 closes: #12321 Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
committed by
Christian Brabandt
parent
1226b0584a
commit
fa37835b8c
@@ -1,4 +1,4 @@
|
||||
*builtin.txt* For Vim version 9.1. Last change: 2024 Jan 29
|
||||
*builtin.txt* For Vim version 9.1. Last change: 2024 Feb 01
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||
@@ -147,6 +147,8 @@ delete({fname} [, {flags}]) Number delete the file or directory {fname}
|
||||
deletebufline({buf}, {first} [, {last}])
|
||||
Number delete lines from buffer {buf}
|
||||
did_filetype() Number |TRUE| if FileType autocmd event used
|
||||
diff({fromlist}, {tolist} [, {options}])
|
||||
List diff two Lists of strings
|
||||
diff_filler({lnum}) Number diff filler lines about {lnum}
|
||||
diff_hlID({lnum}, {col}) Number diff highlighting at {lnum}/{col}
|
||||
digraph_get({chars}) String get the |digraph| of {chars}
|
||||
@@ -2046,6 +2048,67 @@ did_filetype() Returns |TRUE| when autocommands are being executed and the
|
||||
editing another buffer to set 'filetype' and load a syntax
|
||||
file.
|
||||
|
||||
diff({fromlist}, {tolist} [, {options}]) *diff()*
|
||||
Returns a String or a List containing the diff between the
|
||||
strings in {fromlist} and {tolist}. Uses the Vim internal
|
||||
diff library to compute the diff.
|
||||
|
||||
*E106*
|
||||
The optional "output" item in {options} specifies the returned
|
||||
diff format. The following values are supported:
|
||||
indices Return a List of the starting and ending
|
||||
indices and a count of the strings in each
|
||||
diff hunk.
|
||||
unified Return the unified diff output as a String.
|
||||
This is the default.
|
||||
|
||||
If the "output" item in {options} is "indices", then a List is
|
||||
returned. Each List item contains a Dict with the following
|
||||
items for each diff hunk:
|
||||
from_idx start index in {fromlist} for this diff hunk.
|
||||
from_count number of strings in {fromlist} that are
|
||||
added/removed/modified in this diff hunk.
|
||||
to_idx start index in {tolist} for this diff hunk.
|
||||
to_count number of strings in {tolist} that are
|
||||
added/removed/modified in this diff hunk.
|
||||
|
||||
The {options} Dict argument also specifies diff options
|
||||
(similar to 'diffopt') and supports the following items:
|
||||
iblank ignore changes where lines are all
|
||||
blank.
|
||||
icase ignore changes in case of text.
|
||||
iwhite ignore changes in amount of white
|
||||
space.
|
||||
iwhiteall ignore all white space changes.
|
||||
iwhiteeol ignore white space changes at end of
|
||||
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'.
|
||||
|
||||
Returns an empty List or String if {fromlist} and {tolist} are
|
||||
identical.
|
||||
|
||||
Examples:
|
||||
:echo diff(['abc'], ['xxx'])
|
||||
@@ -1 +1 @@
|
||||
-abc
|
||||
+xxx
|
||||
|
||||
:echo diff(['abc'], ['xxx'], {'output': 'indices'})
|
||||
[{'from_idx': 0, 'from_count': 1, 'to_idx': 0, 'to_count': 1}]
|
||||
:echo diff(readfile('oldfile'), readfile('newfile'))
|
||||
:echo diff(getbufline(5, 1, '$'), getbufline(6, 1, '$'))
|
||||
|
||||
For more examples, refer to |diff-func-examples|
|
||||
|
||||
Can also be used as a |method|: >
|
||||
GetFromList->diff(to_list)
|
||||
<
|
||||
diff_filler({lnum}) *diff_filler()*
|
||||
Returns the number of filler lines above line {lnum}.
|
||||
These are the lines that were inserted at this point in
|
||||
|
@@ -1,4 +1,4 @@
|
||||
*diff.txt* For Vim version 9.1. Last change: 2023 Apr 04
|
||||
*diff.txt* For Vim version 9.1. Last change: 2024 Feb 01
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||
@@ -476,4 +476,43 @@ Otherwise, the expression is evaluated in the context of the script where the
|
||||
option was set, thus script-local items are available.
|
||||
|
||||
|
||||
DIFF FUNCTION EXAMPLES *diff-func-examples*
|
||||
|
||||
Some examples for using the |diff()| function to compute the diff indices
|
||||
between two Lists of strings are below.
|
||||
>
|
||||
" some lines are changed
|
||||
:echo diff(['abc', 'def', 'ghi'], ['abx', 'rrr', 'xhi'], {'output': 'indices'})
|
||||
[{'from_idx': 0, 'from_count': 3, 'to_idx': 0, 'to_count': 3}]
|
||||
|
||||
" few lines added at the beginning
|
||||
:echo diff(['ghi'], ['abc', 'def', 'ghi'], {'output': 'indices'})
|
||||
[{'from_idx': 0, 'from_count': 0, 'to_idx': 0, 'to_count': 2}]
|
||||
|
||||
" few lines removed from the beginning
|
||||
:echo diff(['abc', 'def', 'ghi'], ['ghi'], {'output': 'indices'})
|
||||
[{'from_idx': 0, 'from_count': 2, 'to_idx': 0, 'to_count': 0}]
|
||||
|
||||
" few lines added in the middle
|
||||
:echo diff(['abc', 'jkl'], ['abc', 'def', 'ghi', 'jkl'], {'output': 'indices'})
|
||||
[{'from_idx': 1, 'from_count': 0, 'to_idx': 1, 'to_count': 2}]
|
||||
|
||||
" few lines removed in the middle
|
||||
:echo diff(['abc', 'def', 'ghi', 'jkl'], ['abc', 'jkl'], {'output': 'indices'})
|
||||
[{'from_idx': 1, 'from_count': 2, 'to_idx': 1, 'to_count': 0}]
|
||||
|
||||
" few lines added at the end
|
||||
:echo diff(['abc'], ['abc', 'def', 'ghi'], {'output': 'indices'})
|
||||
[{'from_idx': 1, 'from_count': 0, 'to_idx': 1, 'to_count': 2}]
|
||||
|
||||
" few lines removed from the end
|
||||
:echo diff(['abc', 'def', 'ghi'], ['abc'], {'output': 'indices'})
|
||||
[{'from_idx': 1, 'from_count': 2, 'to_idx': 1, 'to_count': 0}]
|
||||
|
||||
" disjointed changes
|
||||
:echo diff(['ab', 'def', 'ghi', 'jkl'], ['abc', 'def', 'ghi', 'jk'], {'output': 'indices'})
|
||||
[{'from_idx': 0, 'from_count': 1, 'to_idx': 0, 'to_count': 1},
|
||||
{'from_idx': 3, 'from_count': 1, 'to_idx': 3, 'to_count': 1}]
|
||||
<
|
||||
|
||||
vim:tw=78:ts=8:noet:ft=help:norl:
|
||||
|
@@ -4137,6 +4137,7 @@ E1056 vim9.txt /*E1056*
|
||||
E1057 vim9.txt /*E1057*
|
||||
E1058 vim9.txt /*E1058*
|
||||
E1059 vim9.txt /*E1059*
|
||||
E106 builtin.txt /*E106*
|
||||
E1060 vim9.txt /*E1060*
|
||||
E1061 vim9.txt /*E1061*
|
||||
E1062 eval.txt /*E1062*
|
||||
@@ -6759,7 +6760,9 @@ dict-identity eval.txt /*dict-identity*
|
||||
dict-modification eval.txt /*dict-modification*
|
||||
did_filetype() builtin.txt /*did_filetype()*
|
||||
diff diff.txt /*diff*
|
||||
diff() builtin.txt /*diff()*
|
||||
diff-diffexpr diff.txt /*diff-diffexpr*
|
||||
diff-func-examples diff.txt /*diff-func-examples*
|
||||
diff-mode diff.txt /*diff-mode*
|
||||
diff-options diff.txt /*diff-options*
|
||||
diff-original-file diff.txt /*diff-original-file*
|
||||
|
@@ -1,4 +1,4 @@
|
||||
*todo.txt* For Vim version 9.1. Last change: 2024 Jan 14
|
||||
*todo.txt* For Vim version 9.1. Last change: 2024 Feb 01
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||
@@ -956,9 +956,6 @@ When 'sidescrolloff' is set, using "zl" to go to the end of the line, suddenly
|
||||
scrolls back. Should allow for this scrolling, like 'scrolloff' does when
|
||||
using CTRL-E. (Yee Cheng Chin, #3721)
|
||||
|
||||
Add function to make use of internal diff, working on two lists and returning
|
||||
unified diff (list of lines).
|
||||
|
||||
When splitting a window with few text lines, the relative cursor position is
|
||||
kept, which means part of the text isn't displayed. Better show all the text
|
||||
when possible. (Dylan Lloyd, #3973)
|
||||
|
@@ -1,4 +1,4 @@
|
||||
*usr_41.txt* For Vim version 9.1. Last change: 2024 Jan 13
|
||||
*usr_41.txt* For Vim version 9.1. Last change: 2024 Feb 01
|
||||
|
||||
VIM USER MANUAL - by Bram Moolenaar
|
||||
|
||||
@@ -1368,6 +1368,7 @@ Various: *various-functions*
|
||||
changenr() return number of most recent change
|
||||
cscope_connection() check if a cscope connection exists
|
||||
did_filetype() check if a FileType autocommand was used
|
||||
diff() diff two Lists of strings
|
||||
eventhandler() check if invoked by an event handler
|
||||
getpid() get process ID of Vim
|
||||
getscriptinfo() get list of sourced vim scripts
|
||||
|
236
src/diff.c
236
src/diff.c
@@ -42,6 +42,10 @@ static int diff_flags = DIFF_INTERNAL | DIFF_FILLER | DIFF_CLOSE_OFF;
|
||||
|
||||
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
|
||||
|
||||
static int diff_a_works = MAYBE; // TRUE when "diff -a" works, FALSE when it
|
||||
@@ -97,7 +101,8 @@ static void diff_copy_entry(diff_T *dprev, diff_T *dp, int idx_orig, int idx_new
|
||||
static diff_T *diff_alloc_new(tabpage_T *tp, diff_T *dprev, diff_T *dp);
|
||||
static int parse_diff_ed(char_u *line, diffhunk_T *hunk);
|
||||
static int parse_diff_unified(char_u *line, diffhunk_T *hunk);
|
||||
static int xdiff_out(long start_a, long count_a, long start_b, long count_b, void *priv);
|
||||
static int xdiff_out_indices(long start_a, long count_a, long start_b, long count_b, void *priv);
|
||||
static int xdiff_out_unified(void *priv, mmbuffer_t *mb, int nbuf);
|
||||
|
||||
#define FOR_ALL_DIFFBLOCKS_IN_TAB(tp, dp) \
|
||||
for ((dp) = (tp)->tp_first_diff; (dp) != NULL; (dp) = (dp)->df_next)
|
||||
@@ -1142,7 +1147,10 @@ diff_file_internal(diffio_T *diffio)
|
||||
|
||||
emit_cfg.ctxlen = 0; // don't need any diff_context here
|
||||
emit_cb.priv = &diffio->dio_diff;
|
||||
emit_cfg.hunk_func = xdiff_out;
|
||||
if (diff_internal_output_fmt == DIFF_INTERNAL_OUTPUT_INDICES)
|
||||
emit_cfg.hunk_func = xdiff_out_indices;
|
||||
else
|
||||
emit_cb.out_line = xdiff_out_unified;
|
||||
if (xdl_diff(&diffio->dio_orig.din_mmfile,
|
||||
&diffio->dio_new.din_mmfile,
|
||||
¶m, &emit_cfg, &emit_cb) < 0)
|
||||
@@ -3327,10 +3335,10 @@ parse_diff_unified(
|
||||
|
||||
/*
|
||||
* Callback function for the xdl_diff() function.
|
||||
* Stores the diff output in a grow array.
|
||||
* Stores the diff output (indices) in a grow array.
|
||||
*/
|
||||
static int
|
||||
xdiff_out(
|
||||
xdiff_out_indices(
|
||||
long start_a,
|
||||
long count_a,
|
||||
long start_b,
|
||||
@@ -3357,6 +3365,25 @@ xdiff_out(
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback function for the xdl_diff() function.
|
||||
* Stores the unified diff output in a grow array.
|
||||
*/
|
||||
static int
|
||||
xdiff_out_unified(
|
||||
void *priv,
|
||||
mmbuffer_t *mb,
|
||||
int nbuf)
|
||||
{
|
||||
diffout_T *dout = (diffout_T *)priv;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nbuf; i++)
|
||||
ga_concat_len(&dout->dout_ga, (char_u *)mb[i].ptr, mb[i].size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // FEAT_DIFF
|
||||
|
||||
#if defined(FEAT_EVAL) || defined(PROTO)
|
||||
@@ -3439,4 +3466,205 @@ f_diff_hlID(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the diff options passed in "optarg" to the diff() function and return
|
||||
* the options in "diffopts" and the diff algorithm in "diffalgo".
|
||||
*/
|
||||
static int
|
||||
parse_diff_optarg(
|
||||
typval_T *opts,
|
||||
int *diffopts,
|
||||
long *diffalgo,
|
||||
int *diff_output_fmt)
|
||||
{
|
||||
dict_T *d = opts->vval.v_dict;
|
||||
|
||||
char_u *algo = dict_get_string(d, "algorithm", FALSE);
|
||||
if (algo != NULL)
|
||||
{
|
||||
if (STRNCMP(algo, "myers", 5) == 0)
|
||||
*diffalgo = 0;
|
||||
else if (STRNCMP(algo, "minimal", 7) == 0)
|
||||
*diffalgo = XDF_NEED_MINIMAL;
|
||||
else if (STRNCMP(algo, "patience", 8) == 0)
|
||||
*diffalgo = XDF_PATIENCE_DIFF;
|
||||
else if (STRNCMP(algo, "histogram", 9) == 0)
|
||||
*diffalgo = XDF_HISTOGRAM_DIFF;
|
||||
}
|
||||
|
||||
char_u *output_fmt = dict_get_string(d, "output", FALSE);
|
||||
if (output_fmt != NULL)
|
||||
{
|
||||
if (STRNCMP(output_fmt, "unified", 7) == 0)
|
||||
*diff_output_fmt = DIFF_INTERNAL_OUTPUT_UNIFIED;
|
||||
else if (STRNCMP(output_fmt, "indices", 7) == 0)
|
||||
*diff_output_fmt = DIFF_INTERNAL_OUTPUT_INDICES;
|
||||
else
|
||||
{
|
||||
semsg(_(e_unsupported_diff_output_format_str), output_fmt);
|
||||
return FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
if (dict_get_bool(d, "iblank", FALSE))
|
||||
*diffopts |= DIFF_IBLANK;
|
||||
if (dict_get_bool(d, "icase", FALSE))
|
||||
*diffopts |= DIFF_ICASE;
|
||||
if (dict_get_bool(d, "iwhite", FALSE))
|
||||
*diffopts |= DIFF_IWHITE;
|
||||
if (dict_get_bool(d, "iwhiteall", FALSE))
|
||||
*diffopts |= DIFF_IWHITEALL;
|
||||
if (dict_get_bool(d, "iwhiteeol", FALSE))
|
||||
*diffopts |= DIFF_IWHITEEOL;
|
||||
if (dict_get_bool(d, "indent-heuristic", FALSE))
|
||||
*diffalgo |= XDF_INDENT_HEURISTIC;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Concatenate the List of strings in "l" and store the result in
|
||||
* "din->din_mmfile.ptr" and the length in "din->din_mmfile.size".
|
||||
*/
|
||||
static void
|
||||
list_to_diffin(list_T *l, diffin_T *din, int icase)
|
||||
{
|
||||
garray_T ga;
|
||||
listitem_T *li;
|
||||
char_u *str;
|
||||
|
||||
ga_init2(&ga, 512, 4);
|
||||
|
||||
FOR_ALL_LIST_ITEMS(l, li)
|
||||
{
|
||||
str = tv_get_string(&li->li_tv);
|
||||
if (icase)
|
||||
{
|
||||
str = strlow_save(str);
|
||||
if (str == NULL)
|
||||
continue;
|
||||
}
|
||||
ga_concat(&ga, str);
|
||||
ga_concat(&ga, (char_u *)NL_STR);
|
||||
if (icase)
|
||||
vim_free(str);
|
||||
}
|
||||
if (ga.ga_len > 0)
|
||||
((char *)ga.ga_data)[ga.ga_len] = NUL;
|
||||
|
||||
din->din_mmfile.ptr = (char *)ga.ga_data;
|
||||
din->din_mmfile.size = ga.ga_len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the start and end indices from the diff "hunk".
|
||||
*/
|
||||
static dict_T *
|
||||
get_diff_hunk_indices(diffhunk_T *hunk)
|
||||
{
|
||||
dict_T *hunk_dict;
|
||||
|
||||
hunk_dict = dict_alloc();
|
||||
if (hunk_dict == NULL)
|
||||
return NULL;
|
||||
|
||||
dict_add_number(hunk_dict, "from_idx", hunk->lnum_orig - 1);
|
||||
dict_add_number(hunk_dict, "from_count", hunk->count_orig);
|
||||
dict_add_number(hunk_dict, "to_idx", hunk->lnum_new - 1);
|
||||
dict_add_number(hunk_dict, "to_count", hunk->count_new);
|
||||
|
||||
return hunk_dict;
|
||||
}
|
||||
|
||||
/*
|
||||
* "diff()" function
|
||||
*/
|
||||
void
|
||||
f_diff(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
|
||||
{
|
||||
#ifdef FEAT_DIFF
|
||||
diffio_T dio;
|
||||
|
||||
if (check_for_nonnull_list_arg(argvars, 0) == FAIL
|
||||
|| check_for_nonnull_list_arg(argvars, 1) == FAIL
|
||||
|| check_for_opt_nonnull_dict_arg(argvars, 2) == FAIL)
|
||||
return;
|
||||
|
||||
CLEAR_FIELD(dio);
|
||||
dio.dio_internal = TRUE;
|
||||
ga_init2(&dio.dio_diff.dout_ga, sizeof(char *), 1000);
|
||||
|
||||
list_T *orig_list = argvars[0].vval.v_list;
|
||||
list_T *new_list = argvars[1].vval.v_list;
|
||||
|
||||
// Save the 'diffopt' option value and restore it after getting the diff.
|
||||
int save_diff_flags = diff_flags;
|
||||
long save_diff_algorithm = diff_algorithm;
|
||||
long save_diff_output_fmt = diff_internal_output_fmt;
|
||||
diff_flags = DIFF_INTERNAL;
|
||||
diff_algorithm = 0;
|
||||
diff_internal_output_fmt = DIFF_INTERNAL_OUTPUT_UNIFIED;
|
||||
if (argvars[2].v_type != VAR_UNKNOWN)
|
||||
if (parse_diff_optarg(&argvars[2], &diff_flags, &diff_algorithm,
|
||||
&diff_internal_output_fmt) == FAIL)
|
||||
{
|
||||
diff_internal_output_fmt = save_diff_output_fmt;
|
||||
return;
|
||||
}
|
||||
|
||||
// Concatenate the List of strings into a single string using newline
|
||||
// separator. Internal diff library expects a single string.
|
||||
list_to_diffin(orig_list, &dio.dio_orig, diff_flags & DIFF_ICASE);
|
||||
list_to_diffin(new_list, &dio.dio_new, diff_flags & DIFF_ICASE);
|
||||
|
||||
// Compute the diff
|
||||
int diff_status = diff_file(&dio);
|
||||
|
||||
if (diff_status == FAIL)
|
||||
goto done;
|
||||
|
||||
int hunk_idx = 0;
|
||||
dict_T *hunk_dict;
|
||||
|
||||
if (diff_internal_output_fmt == DIFF_INTERNAL_OUTPUT_INDICES)
|
||||
{
|
||||
if (rettv_list_alloc(rettv) != OK)
|
||||
goto done;
|
||||
list_T *l = rettv->vval.v_list;
|
||||
|
||||
// Process each diff hunk
|
||||
diffhunk_T *hunk = NULL;
|
||||
while (hunk_idx < dio.dio_diff.dout_ga.ga_len)
|
||||
{
|
||||
hunk = ((diffhunk_T **)dio.dio_diff.dout_ga.ga_data)[hunk_idx++];
|
||||
|
||||
hunk_dict = get_diff_hunk_indices(hunk);
|
||||
if (hunk_dict == NULL)
|
||||
goto done;
|
||||
|
||||
list_append_dict(l, hunk_dict);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ga_append(&dio.dio_diff.dout_ga, NUL);
|
||||
rettv->v_type = VAR_STRING;
|
||||
rettv->vval.v_string =
|
||||
vim_strsave((char_u *)dio.dio_diff.dout_ga.ga_data);
|
||||
}
|
||||
|
||||
done:
|
||||
clear_diffin(&dio.dio_new);
|
||||
if (diff_internal_output_fmt == DIFF_INTERNAL_OUTPUT_INDICES)
|
||||
clear_diffout(&dio.dio_diff);
|
||||
else
|
||||
ga_clear(&dio.dio_diff.dout_ga);
|
||||
clear_diffin(&dio.dio_orig);
|
||||
// Restore the 'diffopt' option value.
|
||||
diff_flags = save_diff_flags;
|
||||
diff_algorithm = save_diff_algorithm;
|
||||
diff_internal_output_fmt = save_diff_output_fmt;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -258,8 +258,9 @@ EXTERN char e_escape_not_allowed_in_digraph[]
|
||||
EXTERN char e_using_loadkeymap_not_in_sourced_file[]
|
||||
INIT(= N_("E105: Using :loadkeymap not in a sourced file"));
|
||||
#endif
|
||||
// E106 unused
|
||||
#ifdef FEAT_EVAL
|
||||
EXTERN char e_unsupported_diff_output_format_str[]
|
||||
INIT(= N_("E106: Unsupported diff output format: %s"));
|
||||
EXTERN char e_missing_parenthesis_str[]
|
||||
INIT(= N_("E107: Missing parentheses: %s"));
|
||||
EXTERN char e_no_such_variable_str[]
|
||||
|
@@ -1148,6 +1148,7 @@ static argcheck_T arg3_buffer_number_number[] = {arg_buffer, arg_number, arg_num
|
||||
static argcheck_T arg3_buffer_string_any[] = {arg_buffer, arg_string, arg_any};
|
||||
static argcheck_T arg3_buffer_string_dict[] = {arg_buffer, arg_string, arg_dict_any};
|
||||
static argcheck_T arg3_dict_number_number[] = {arg_dict_any, arg_number, arg_number};
|
||||
static argcheck_T arg3_diff[] = {arg_list_string, arg_list_string, arg_dict_any};
|
||||
static argcheck_T arg3_list_string_dict[] = {arg_list_any, arg_string, arg_dict_any};
|
||||
static argcheck_T arg3_lnum_number_bool[] = {arg_lnum, arg_number, arg_bool};
|
||||
static argcheck_T arg3_number[] = {arg_number, arg_number, arg_number};
|
||||
@@ -1950,6 +1951,8 @@ static funcentry_T global_functions[] =
|
||||
ret_number_bool, f_deletebufline},
|
||||
{"did_filetype", 0, 0, 0, NULL,
|
||||
ret_number_bool, f_did_filetype},
|
||||
{"diff", 2, 3, FEARG_1, arg3_diff,
|
||||
ret_list_dict_any, f_diff},
|
||||
{"diff_filler", 1, 1, FEARG_1, arg1_lnum,
|
||||
ret_number, f_diff_filler},
|
||||
{"diff_hlID", 2, 2, FEARG_1, arg2_lnum_number,
|
||||
|
@@ -30,4 +30,5 @@ linenr_T diff_get_corresponding_line(buf_T *buf1, linenr_T lnum1);
|
||||
linenr_T diff_lnum_win(linenr_T lnum, win_T *wp);
|
||||
void f_diff_filler(typval_T *argvars, typval_T *rettv);
|
||||
void f_diff_hlID(typval_T *argvars, typval_T *rettv);
|
||||
void f_diff(typval_T *argvars, typval_T *rettv);
|
||||
/* vim: set ft=c : */
|
||||
|
@@ -26,6 +26,7 @@ int check_for_opt_list_arg(typval_T *args, int idx);
|
||||
int check_for_dict_arg(typval_T *args, int idx);
|
||||
int check_for_nonnull_dict_arg(typval_T *args, int idx);
|
||||
int check_for_opt_dict_arg(typval_T *args, int idx);
|
||||
int check_for_opt_nonnull_dict_arg(typval_T *args, int idx);
|
||||
int check_for_chan_or_job_arg(typval_T *args, int idx);
|
||||
int check_for_opt_chan_or_job_arg(typval_T *args, int idx);
|
||||
int check_for_job_arg(typval_T *args, int idx);
|
||||
|
@@ -1716,5 +1716,182 @@ func Test_diff_put_and_undo()
|
||||
|
||||
bwipe!
|
||||
bwipe!
|
||||
set nodiff
|
||||
endfunc
|
||||
|
||||
" Test for the diff() function
|
||||
def Test_diff_func()
|
||||
# string is added/removed/modified at the beginning
|
||||
assert_equal("@@ -0,0 +1 @@\n+abc\n",
|
||||
diff(['def'], ['abc', 'def'], {output: 'unified'}))
|
||||
assert_equal([{from_idx: 0, from_count: 0, to_idx: 0, to_count: 1}],
|
||||
diff(['def'], ['abc', 'def'], {output: 'indices'}))
|
||||
assert_equal("@@ -1 +0,0 @@\n-abc\n",
|
||||
diff(['abc', 'def'], ['def'], {output: 'unified'}))
|
||||
assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 0}],
|
||||
diff(['abc', 'def'], ['def'], {output: 'indices'}))
|
||||
assert_equal("@@ -1 +1 @@\n-abc\n+abx\n",
|
||||
diff(['abc', 'def'], ['abx', 'def'], {output: 'unified'}))
|
||||
assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 1}],
|
||||
diff(['abc', 'def'], ['abx', 'def'], {output: 'indices'}))
|
||||
|
||||
# string is added/removed/modified at the end
|
||||
assert_equal("@@ -1,0 +2 @@\n+def\n",
|
||||
diff(['abc'], ['abc', 'def'], {output: 'unified'}))
|
||||
assert_equal([{from_idx: 1, from_count: 0, to_idx: 1, to_count: 1}],
|
||||
diff(['abc'], ['abc', 'def'], {output: 'indices'}))
|
||||
assert_equal("@@ -2 +1,0 @@\n-def\n",
|
||||
diff(['abc', 'def'], ['abc'], {output: 'unified'}))
|
||||
assert_equal([{from_idx: 1, from_count: 1, to_idx: 1, to_count: 0}],
|
||||
diff(['abc', 'def'], ['abc'], {output: 'indices'}))
|
||||
assert_equal("@@ -2 +2 @@\n-def\n+xef\n",
|
||||
diff(['abc', 'def'], ['abc', 'xef'], {output: 'unified'}))
|
||||
assert_equal([{from_idx: 1, from_count: 1, to_idx: 1, to_count: 1}],
|
||||
diff(['abc', 'def'], ['abc', 'xef'], {output: 'indices'}))
|
||||
|
||||
# string is added/removed/modified in the middle
|
||||
assert_equal("@@ -2,0 +3 @@\n+xxx\n",
|
||||
diff(['111', '222', '333'], ['111', '222', 'xxx', '333'], {output: 'unified'}))
|
||||
assert_equal([{from_idx: 2, from_count: 0, to_idx: 2, to_count: 1}],
|
||||
diff(['111', '222', '333'], ['111', '222', 'xxx', '333'], {output: 'indices'}))
|
||||
assert_equal("@@ -3 +2,0 @@\n-333\n",
|
||||
diff(['111', '222', '333', '444'], ['111', '222', '444'], {output: 'unified'}))
|
||||
assert_equal([{from_idx: 2, from_count: 1, to_idx: 2, to_count: 0}],
|
||||
diff(['111', '222', '333', '444'], ['111', '222', '444'], {output: 'indices'}))
|
||||
assert_equal("@@ -3 +3 @@\n-333\n+xxx\n",
|
||||
diff(['111', '222', '333', '444'], ['111', '222', 'xxx', '444'], {output: 'unified'}))
|
||||
assert_equal([{from_idx: 2, from_count: 1, to_idx: 2, to_count: 1}],
|
||||
diff(['111', '222', '333', '444'], ['111', '222', 'xxx', '444'], {output: 'indices'}))
|
||||
|
||||
# new strings are added to an empty List
|
||||
assert_equal("@@ -0,0 +1,2 @@\n+abc\n+def\n",
|
||||
diff([], ['abc', 'def'], {output: 'unified'}))
|
||||
assert_equal([{from_idx: 0, from_count: 0, to_idx: 0, to_count: 2}],
|
||||
diff([], ['abc', 'def'], {output: 'indices'}))
|
||||
|
||||
# all the strings are removed from a List
|
||||
assert_equal("@@ -1,2 +0,0 @@\n-abc\n-def\n",
|
||||
diff(['abc', 'def'], [], {output: 'unified'}))
|
||||
assert_equal([{from_idx: 0, from_count: 2, to_idx: 0, to_count: 0}],
|
||||
diff(['abc', 'def'], [], {output: 'indices'}))
|
||||
|
||||
# First character is added/removed/different
|
||||
assert_equal("@@ -1 +1 @@\n-abc\n+bc\n",
|
||||
diff(['abc'], ['bc'], {output: 'unified'}))
|
||||
assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 1}],
|
||||
diff(['abc'], ['bc'], {output: 'indices'}))
|
||||
assert_equal("@@ -1 +1 @@\n-bc\n+abc\n",
|
||||
diff(['bc'], ['abc'], {output: 'unified'}))
|
||||
assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 1}],
|
||||
diff(['bc'], ['abc'], {output: 'indices'}))
|
||||
assert_equal("@@ -1 +1 @@\n-abc\n+xbc\n",
|
||||
diff(['abc'], ['xbc'], {output: 'unified'}))
|
||||
assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 1}],
|
||||
diff(['abc'], ['xbc'], {output: 'indices'}))
|
||||
|
||||
# Last character is added/removed/different
|
||||
assert_equal("@@ -1 +1 @@\n-abc\n+abcd\n",
|
||||
diff(['abc'], ['abcd'], {output: 'unified'}))
|
||||
assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 1}],
|
||||
diff(['abc'], ['abcd'], {output: 'indices'}))
|
||||
assert_equal("@@ -1 +1 @@\n-abcd\n+abc\n",
|
||||
diff(['abcd'], ['abc'], {output: 'unified'}))
|
||||
assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 1}],
|
||||
diff(['abcd'], ['abc'], {output: 'indices'}))
|
||||
assert_equal("@@ -1 +1 @@\n-abc\n+abx\n",
|
||||
diff(['abc'], ['abx'], {output: 'unified'}))
|
||||
assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 1}],
|
||||
diff(['abc'], ['abx'], {output: 'indices'}))
|
||||
|
||||
# partial string modification at the start and at the end.
|
||||
var fromlist =<< trim END
|
||||
one two
|
||||
three four
|
||||
five six
|
||||
END
|
||||
var tolist =<< trim END
|
||||
one
|
||||
six
|
||||
END
|
||||
assert_equal("@@ -1,3 +1,2 @@\n-one two\n-three four\n-five six\n+one\n+six\n", diff(fromlist, tolist, {output: 'unified'}))
|
||||
assert_equal([{from_idx: 0, from_count: 3, to_idx: 0, to_count: 2}],
|
||||
diff(fromlist, tolist, {output: 'indices'}))
|
||||
|
||||
# non-contiguous modifications
|
||||
fromlist =<< trim END
|
||||
one two
|
||||
three four
|
||||
five abc six
|
||||
END
|
||||
tolist =<< trim END
|
||||
one abc two
|
||||
three four
|
||||
five six
|
||||
END
|
||||
assert_equal("@@ -1 +1 @@\n-one two\n+one abc two\n@@ -3 +3 @@\n-five abc six\n+five six\n",
|
||||
diff(fromlist, tolist, {output: 'unified'}))
|
||||
assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 1},
|
||||
{from_idx: 2, from_count: 1, to_idx: 2, to_count: 1}],
|
||||
diff(fromlist, tolist, {output: 'indices'}))
|
||||
|
||||
# add/remove blank lines
|
||||
assert_equal("@@ -2,2 +1,0 @@\n-\n-\n",
|
||||
diff(['one', '', '', 'two'], ['one', 'two'], {output: 'unified'}))
|
||||
assert_equal([{from_idx: 1, from_count: 2, to_idx: 1, to_count: 0}],
|
||||
diff(['one', '', '', 'two'], ['one', 'two'], {output: 'indices'}))
|
||||
assert_equal("@@ -1,0 +2,2 @@\n+\n+\n",
|
||||
diff(['one', 'two'], ['one', '', '', 'two'], {output: 'unified'}))
|
||||
assert_equal([{'from_idx': 1, 'from_count': 0, 'to_idx': 1, 'to_count': 2}],
|
||||
diff(['one', 'two'], ['one', '', '', 'two'], {output: 'indices'}))
|
||||
|
||||
# diff ignoring case
|
||||
assert_equal('', diff(['abc', 'def'], ['ABC', 'DEF'], {icase: true, output: 'unified'}))
|
||||
assert_equal([], diff(['abc', 'def'], ['ABC', 'DEF'], {icase: true, output: 'indices'}))
|
||||
|
||||
# diff ignoring all whitespace changes except leading whitespace changes
|
||||
assert_equal('', diff(['abc def'], ['abc def '], {iwhite: true}))
|
||||
assert_equal("@@ -1 +1 @@\n- abc\n+ xxx\n", diff([' abc'], [' xxx'], {iwhite: v:true}))
|
||||
assert_equal("@@ -1 +1 @@\n- abc\n+ xxx\n", diff([' abc'], [' xxx'], {iwhite: v:false}))
|
||||
assert_equal("@@ -1 +1 @@\n-abc \n+xxx \n", diff(['abc '], ['xxx '], {iwhite: v:true}))
|
||||
assert_equal("@@ -1 +1 @@\n-abc \n+xxx \n", diff(['abc '], ['xxx '], {iwhite: v:false}))
|
||||
|
||||
# diff ignoring all whitespace changes
|
||||
assert_equal('', diff(['abc def'], [' abc def '], {iwhiteall: true}))
|
||||
assert_equal("@@ -1 +1 @@\n- abc \n+ xxx \n", diff([' abc '], [' xxx '], {iwhiteall: v:true}))
|
||||
assert_equal("@@ -1 +1 @@\n- abc \n+ xxx \n", diff([' abc '], [' xxx '], {iwhiteall: v:false}))
|
||||
|
||||
# diff ignoring trailing whitespace changes
|
||||
assert_equal('', diff(['abc'], ['abc '], {iwhiteeol: true}))
|
||||
|
||||
# diff ignoring blank lines
|
||||
assert_equal('', diff(['one', '', '', 'two'], ['one', 'two'], {iblank: true}))
|
||||
assert_equal('', diff(['one', 'two'], ['one', '', '', 'two'], {iblank: true}))
|
||||
|
||||
# same string
|
||||
assert_equal('', diff(['abc', 'def', 'ghi'], ['abc', 'def', 'ghi']))
|
||||
assert_equal('', diff([''], ['']))
|
||||
assert_equal('', diff([], []))
|
||||
|
||||
# different xdiff algorithms
|
||||
for algo in ['myers', 'minimal', 'patience', 'histogram']
|
||||
assert_equal("@@ -1 +1 @@\n- abc \n+ xxx \n",
|
||||
diff([' abc '], [' xxx '], {algorithm: algo, output: 'unified'}))
|
||||
assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 1}],
|
||||
diff([' abc '], [' xxx '], {algorithm: algo, output: 'indices'}))
|
||||
endfor
|
||||
assert_equal("@@ -1 +1 @@\n- abc \n+ xxx \n",
|
||||
diff([' abc '], [' xxx '], {indent-heuristic: true, output: 'unified'}))
|
||||
assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 1}],
|
||||
diff([' abc '], [' xxx '], {indent-heuristic: true, output: 'indices'}))
|
||||
|
||||
# identical strings
|
||||
assert_equal('', diff(['111', '222'], ['111', '222'], {output: 'unified'}))
|
||||
assert_equal([], diff(['111', '222'], ['111', '222'], {output: 'indices'}))
|
||||
assert_equal('', diff([], [], {output: 'unified'}))
|
||||
assert_equal([], diff([], [], {output: 'indices'}))
|
||||
|
||||
# Error cases
|
||||
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"], {output: "xyz"})', 'E106: Unsupported diff output format: xyz')
|
||||
|
10
src/typval.c
10
src/typval.c
@@ -623,6 +623,16 @@ check_for_opt_dict_arg(typval_T *args, int idx)
|
||||
|| check_for_dict_arg(args, idx) != FAIL) ? OK : FAIL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for an optional non-NULL dict argument at 'idx'
|
||||
*/
|
||||
int
|
||||
check_for_opt_nonnull_dict_arg(typval_T *args, int idx)
|
||||
{
|
||||
return (args[idx].v_type == VAR_UNKNOWN
|
||||
|| check_for_nonnull_dict_arg(args, idx) != FAIL) ? OK : FAIL;
|
||||
}
|
||||
|
||||
#if defined(FEAT_JOB_CHANNEL) || defined(PROTO)
|
||||
/*
|
||||
* Give an error and return FAIL unless "args[idx]" is a channel or a job.
|
||||
|
@@ -704,6 +704,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
71,
|
||||
/**/
|
||||
70,
|
||||
/**/
|
||||
|
Reference in New Issue
Block a user