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>
diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt
index 5001205..19b9bc9 100644
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -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 @@
 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 @@
 		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
diff --git a/runtime/doc/diff.txt b/runtime/doc/diff.txt
index 91b0047..05dd4a6 100644
--- a/runtime/doc/diff.txt
+++ b/runtime/doc/diff.txt
@@ -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 @@
 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:
diff --git a/runtime/doc/tags b/runtime/doc/tags
index 7074aab..ebde436 100644
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -4137,6 +4137,7 @@
 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-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*
diff --git a/runtime/doc/todo.txt b/runtime/doc/todo.txt
index bb58597..953ae47 100644
--- a/runtime/doc/todo.txt
+++ b/runtime/doc/todo.txt
@@ -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 @@
 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)
diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt
index 0990a06..0ca19be 100644
--- a/runtime/doc/usr_41.txt
+++ b/runtime/doc/usr_41.txt
@@ -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 @@
 	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