patch 8.2.0845: text properties crossing lines not handled correctly

Problem:    Text properties crossing lines not handled correctly.
Solution:   When joining lines merge text properties if possible.
            (Axel Forsman, closes #5839, closes #5683)
diff --git a/src/ops.c b/src/ops.c
index 9ab3555..ed16886 100644
--- a/src/ops.c
+++ b/src/ops.c
@@ -1887,6 +1887,7 @@
     char_u      *curr_start = NULL;
     char_u	*cend;
     char_u	*newp;
+    size_t	newp_len;
     char_u	*spaces;	// number of spaces inserted before a line
     int		endcurr1 = NUL;
     int		endcurr2 = NUL;
@@ -1900,8 +1901,8 @@
 				  && has_format_option(FO_REMOVE_COMS);
     int		prev_was_comment;
 #ifdef FEAT_PROP_POPUP
-    textprop_T	**prop_lines = NULL;
-    int		*prop_lengths = NULL;
+    int		propcount = 0;	// number of props over all joined lines
+    int		props_remaining;
 #endif
 
     if (save_undo && u_save((linenr_T)(curwin->w_cursor.lnum - 1),
@@ -1932,6 +1933,9 @@
     for (t = 0; t < count; ++t)
     {
 	curr = curr_start = ml_get((linenr_T)(curwin->w_cursor.lnum + t));
+#ifdef FEAT_PROP_POPUP
+	propcount += count_props((linenr_T) (curwin->w_cursor.lnum + t), t > 0);
+#endif
 	if (t == 0 && setmark && !cmdmod.lockmarks)
 	{
 	    // Set the '[ mark.
@@ -2014,7 +2018,11 @@
     col = sumsize - currsize - spaces[count - 1];
 
     // allocate the space for the new line
-    newp = alloc(sumsize + 1);
+    newp_len = sumsize + 1;
+#ifdef FEAT_PROP_POPUP
+    newp_len += propcount * sizeof(textprop_T);
+#endif
+    newp = alloc(newp_len);
     if (newp == NULL)
     {
 	ret = FAIL;
@@ -2023,20 +2031,6 @@
     cend = newp + sumsize;
     *cend = 0;
 
-#ifdef FEAT_PROP_POPUP
-    // We need to move properties of the lines that are going to be deleted to
-    // the new long one.
-    if (curbuf->b_has_textprop && !text_prop_frozen)
-    {
-	// Allocate an array to copy the text properties of joined lines into.
-	// And another array to store the number of properties in each line.
-	prop_lines = ALLOC_CLEAR_MULT(textprop_T *, count - 1);
-	prop_lengths = ALLOC_CLEAR_MULT(int, count - 1);
-	if (prop_lengths == NULL)
-	    VIM_CLEAR(prop_lines);
-    }
-#endif
-
     /*
      * Move affected lines to the new long one.
      * This loops backwards over the joined lines, including the original line.
@@ -2045,12 +2039,16 @@
      * column.  This is not Vi compatible, but Vi deletes the marks, thus that
      * should not really be a problem.
      */
+#ifdef FEAT_PROP_POPUP
+    props_remaining = propcount;
+#endif
     for (t = count - 1; ; --t)
     {
 	int spaces_removed;
 
 	cend -= currsize;
 	mch_memmove(cend, curr, (size_t)currsize);
+
 	if (spaces[t] > 0)
 	{
 	    cend -= spaces[t];
@@ -2063,15 +2061,14 @@
 
 	mark_col_adjust(curwin->w_cursor.lnum + t, (colnr_T)0, (linenr_T)-t,
 			 (long)(cend - newp - spaces_removed), spaces_removed);
-	if (t == 0)
-	    break;
 #ifdef FEAT_PROP_POPUP
-	if (prop_lines != NULL)
-	    adjust_props_for_join(curwin->w_cursor.lnum + t,
-				      prop_lines + t - 1, prop_lengths + t - 1,
-			 (long)(cend - newp - spaces_removed), spaces_removed);
+	prepend_joined_props(newp + sumsize + 1, propcount, &props_remaining,
+		curwin->w_cursor.lnum + t, t == count - 1,
+		(long)(cend - newp), spaces_removed);
 #endif
 
+	if (t == 0)
+	    break;
 	curr = curr_start = ml_get((linenr_T)(curwin->w_cursor.lnum + t - 1));
 	if (remove_comments)
 	    curr += comments[t - 1];
@@ -2080,13 +2077,7 @@
 	currsize = (int)STRLEN(curr);
     }
 
-#ifdef FEAT_PROP_POPUP
-    if (prop_lines != NULL)
-	join_prop_lines(curwin->w_cursor.lnum, newp,
-					      prop_lines, prop_lengths, count);
-    else
-#endif
-	ml_replace(curwin->w_cursor.lnum, newp, FALSE);
+    ml_replace_len(curwin->w_cursor.lnum, newp, newp_len, TRUE, FALSE);
 
     if (setmark && !cmdmod.lockmarks)
     {