patch 8.1.1114: confusing overloaded operator "." for string concatenation

Problem:    Confusing overloaded operator "." for string concatenation.
Solution:   Add ".." for string concatenation.  Also "let a ..= b".
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index c764f9a..0e301a3 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -786,10 +786,10 @@
 	expr2 ? expr1 : expr1	if-then-else
 
 |expr2|	expr3
-	expr3 || expr3 ..	logical OR
+	expr3 || expr3 ...	logical OR
 
 |expr3|	expr4
-	expr4 && expr4 ..	logical AND
+	expr4 && expr4 ...	logical AND
 
 |expr4|	expr5
 	expr5 == expr5		equal
@@ -811,14 +811,15 @@
 				instance
 
 |expr5|	expr6
-	expr6 +	 expr6 ..	number addition, list or blob concatenation
-	expr6 -	 expr6 ..	number subtraction
-	expr6 .	 expr6 ..	string concatenation
+	expr6 +	 expr6 ...	number addition, list or blob concatenation
+	expr6 -	 expr6 ...	number subtraction
+	expr6 .	 expr6 ...	string concatenation
+	expr6 .. expr6 ...	string concatenation
 
 |expr6|	expr7
-	expr7 *	 expr7 ..	number multiplication
-	expr7 /	 expr7 ..	number division
-	expr7 %	 expr7 ..	number modulo
+	expr7 *	 expr7 ...	number multiplication
+	expr7 /	 expr7 ...	number division
+	expr7 %	 expr7 ...	number modulo
 
 |expr7|	expr8
 	! expr7			logical NOT
@@ -847,7 +848,7 @@
 	{args -> expr1}		lambda expression
 
 
-".." indicates that the operations in this level can be concatenated.
+"..." indicates that the operations in this level can be concatenated.
 Example: >
 	&nu || &list && &shell == "csh"
 
@@ -1026,13 +1027,17 @@
 
 expr5 and expr6						*expr5* *expr6*
 ---------------
-expr6 + expr6  Number addition, |List| or |Blob| concatenation	*expr-+*
-expr6 - expr6  Number subtraction				*expr--*
-expr6 . expr6  String concatenation				*expr-.*
+expr6 + expr6   Number addition, |List| or |Blob| concatenation	*expr-+*
+expr6 - expr6   Number subtraction				*expr--*
+expr6 . expr6   String concatenation				*expr-.*
+expr6 .. expr6  String concatenation				*expr-..*
 
 For |Lists| only "+" is possible and then both expr6 must be a list.  The
 result is a new list with the two lists Concatenated.
 
+For String concatenation ".." is preferred, since "." is ambiguous, it is also
+used for |Dict| member access and floating point numbers.
+
 expr7 * expr7  Number multiplication				*expr-star*
 expr7 / expr7  Number division					*expr-/*
 expr7 % expr7  Number modulo					*expr-%*
@@ -5800,7 +5805,7 @@
 isnan({expr})						*isnan()*
 		Return |TRUE| if {expr} is a float with value NaN. >
 			echo isnan(0.0 / 0.0)
-<			1 ~
+<			1
 
 		{only available when compiled with the |+float| feature}
 
diff --git a/src/eval.c b/src/eval.c
index ec2dc9a..78e2c60 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -1234,6 +1234,7 @@
  * ":let var /= expr"		assignment command.
  * ":let var %= expr"		assignment command.
  * ":let var .= expr"		assignment command.
+ * ":let var ..= expr"		assignment command.
  * ":let [var1, var2] = expr"	unpack list.
  */
     void
@@ -1255,8 +1256,8 @@
     if (argend > arg && argend[-1] == '.')  // for var.='str'
 	--argend;
     expr = skipwhite(argend);
-    if (*expr != '=' && !(vim_strchr((char_u *)"+-*/%.", *expr) != NULL
-			  && expr[1] == '='))
+    if (*expr != '=' && !((vim_strchr((char_u *)"+-*/%.", *expr) != NULL
+			   && expr[1] == '=') || STRNCMP(expr, "..=", 3) == 0))
     {
 	/*
 	 * ":let" without "=": list variables
@@ -1286,7 +1287,11 @@
 	if (*expr != '=')
 	{
 	    if (vim_strchr((char_u *)"+-*/%.", *expr) != NULL)
+	    {
 		op[0] = *expr;   // +=, -=, *=, /=, %= or .=
+		if (expr[0] == '.' && expr[1] == '.') // ..=
+		    ++expr;
+	    }
 	    expr = skipwhite(expr + 2);
 	}
 	else
@@ -3813,6 +3818,7 @@
  *	+	number addition
  *	-	number subtraction
  *	.	string concatenation
+ *	..	string concatenation
  *
  * "arg" must point to the first non-white of the expression.
  * "arg" is advanced to the next non-white after the recognized expression.
@@ -3872,6 +3878,8 @@
 	/*
 	 * Get the second variable.
 	 */
+	if (op == '.' && *(*arg + 1) == '.')  // .. string concatenation
+	    ++*arg;
 	*arg = skipwhite(*arg + 1);
 	if (eval6(arg, &var2, evaluate, op == '.') == FAIL)
 	{
diff --git a/src/testdir/test_eval_stuff.vim b/src/testdir/test_eval_stuff.vim
index f4b3598..6d61ce9 100644
--- a/src/testdir/test_eval_stuff.vim
+++ b/src/testdir/test_eval_stuff.vim
@@ -94,3 +94,32 @@
   call assert_fails('let v:errmsg = []', 'E730:')
   let v:errmsg = ''
 endfunc
+
+func Test_string_concatenation()
+  call assert_equal('ab', 'a'.'b')
+  call assert_equal('ab', 'a' .'b')
+  call assert_equal('ab', 'a'. 'b')
+  call assert_equal('ab', 'a' . 'b')
+
+  call assert_equal('ab', 'a'..'b')
+  call assert_equal('ab', 'a' ..'b')
+  call assert_equal('ab', 'a'.. 'b')
+  call assert_equal('ab', 'a' .. 'b')
+
+  let a = 'a'
+  let b = 'b'
+  let a .= b
+  call assert_equal('ab', a)
+
+  let a = 'a'
+  let a.=b
+  call assert_equal('ab', a)
+
+  let a = 'a'
+  let a ..= b
+  call assert_equal('ab', a)
+
+  let a = 'a'
+  let a..=b
+  call assert_equal('ab', a)
+endfunc
diff --git a/src/version.c b/src/version.c
index 3805950..0b86e19 100644
--- a/src/version.c
+++ b/src/version.c
@@ -772,6 +772,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1114,
+/**/
     1113,
 /**/
     1112,