diff src/fold.c @ 11140:6b26e044b6f5 v8.0.0457

patch 8.0.0457: using :move messes up manual folds commit https://github.com/vim/vim/commit/88d298aed8682eac872ebfe40df3112a6acd83e8 Author: Bram Moolenaar <Bram@vim.org> Date: Tue Mar 14 21:53:58 2017 +0100 patch 8.0.0457: using :move messes up manual folds Problem: Using :move messes up manual folds. Solution: Split adjusting marks and folds. Add foldMoveRange(). (neovim patch #6221)
author Christian Brabandt <cb@256bit.org>
date Tue, 14 Mar 2017 22:00:05 +0100
parents 8d9ecf09183a
children 87779062d706
line wrap: on
line diff
--- a/src/fold.c
+++ b/src/fold.c
@@ -64,6 +64,7 @@ static void deleteFoldMarkers(fold_T *fp
 static void foldDelMarker(linenr_T lnum, char_u *marker, int markerlen);
 static void foldUpdateIEMS(win_T *wp, linenr_T top, linenr_T bot);
 static void parseMarker(win_T *wp);
+static void foldMoveRange_int(garray_T *gap, linenr_T line1, linenr_T line2, linenr_T dest);
 
 static char *e_nofold = N_("E490: No fold found");
 
@@ -1075,6 +1076,12 @@ foldAdjustCursor(void)
     (void)hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL);
 }
 
+/* foldMoveRange() {{{2 */
+    void
+foldMoveRange(garray_T *gap, linenr_T line1, linenr_T line2, linenr_T dest)
+{
+    foldMoveRange_int(gap, line1, line2, dest);
+}
 /* Internal functions for "fold_T" {{{1 */
 /* cloneFoldGrowArray() {{{2 */
 /*
@@ -2968,6 +2975,182 @@ foldRemove(garray_T *gap, linenr_T top, 
     }
 }
 
+/* foldReverseOrder() {{{2 */
+    static void
+foldReverseOrder(garray_T *gap, linenr_T start, linenr_T end)
+{
+    fold_T *left, *right;
+    fold_T tmp;
+
+    for (; start < end; start++, end--)
+    {
+	left = (fold_T *)gap->ga_data + start;
+	right = (fold_T *)gap->ga_data + end;
+	tmp  = *left;
+	*left = *right;
+	*right = tmp;
+    }
+}
+
+/* foldMoveRange_int() {{{2 */
+/*
+ * Move folds within the inclusive range "line1" to "line2" to after "dest"
+ * requires "line1" <= "line2" <= "dest"
+ *
+ * There are the following situations for the first fold at or below line1 - 1.
+ *       1  2  3  4
+ *       1  2  3  4
+ * line1    2  3  4
+ *          2  3  4  5  6  7
+ * line2       3  4  5  6  7
+ *             3  4     6  7  8  9
+ * dest           4        7  8  9
+ *                4        7  8    10
+ *                4        7  8    10
+ *
+ * In the following descriptions, "moved" means moving in the buffer, *and* in
+ * the fold array.
+ * Meanwhile, "shifted" just means moving in the buffer.
+ * 1. not changed
+ * 2. truncated above line1
+ * 3. length reduced by  line2 - line1, folds starting between the end of 3 and
+ *    dest are truncated and shifted up
+ * 4. internal folds moved (from [line1, line2] to dest)
+ * 5. moved to dest.
+ * 6. truncated below line2 and moved.
+ * 7. length reduced by line2 - dest, folds starting between line2 and dest are
+ *    removed, top is moved down by move_len.
+ * 8. truncated below dest and shifted up.
+ * 9. shifted up
+ * 10. not changed
+ */
+
+    static void
+truncate_fold(fold_T *fp, linenr_T end)
+{
+    foldRemove(&fp->fd_nested, end - fp->fd_top, MAXLNUM);
+    fp->fd_len = end - fp->fd_top + 1;
+}
+
+#define fold_end(fp) ((fp)->fd_top + (fp)->fd_len - 1)
+#define valid_fold(fp, gap) ((fp) < ((fold_T *)(gap)->ga_data + (gap)->ga_len))
+#define fold_index(fp, gap) ((size_t)(fp - ((fold_T *)(gap)->ga_data)))
+
+    static void
+foldMoveRange_int(garray_T *gap, linenr_T line1, linenr_T line2, linenr_T dest)
+{
+    fold_T *fp;
+    linenr_T range_len = line2 - line1 + 1;
+    linenr_T move_len = dest - line2;
+    int at_start = foldFind(gap, line1 - 1, &fp);
+    size_t move_start = 0, move_end = 0, dest_index = 0;
+
+    if (at_start)
+    {
+	if (fold_end(fp) > dest)
+	{
+	    /* Case 4
+	    * don't have to change this fold, but have to move nested folds.
+	    */
+	    foldMoveRange(&fp->fd_nested, line1 - fp->fd_top, line2 -
+		    fp->fd_top, dest - fp->fd_top);
+	    return;
+	}
+	else if (fold_end(fp) > line2)
+	{
+	    /* Case 3
+	     * Remove nested folds between line1 and line2 & reduce the
+	     * length of fold by "range_len".
+	     * Folds after this one must be dealt with.
+	     */
+	    foldMarkAdjustRecurse(&fp->fd_nested, line1 - fp->fd_top, line2 -
+		    fp->fd_top, MAXLNUM, -range_len);
+	    fp->fd_len -= range_len;
+	}
+	else
+	    /* Case 2 truncate fold, folds after this one must be dealt with. */
+	    truncate_fold(fp, line1);
+
+	/* Look at the next fold, and treat that one as if it were the first
+	 * after  "line1" (because now it is). */
+	fp = fp + 1;
+    }
+
+    if (!valid_fold(fp, gap) || fp->fd_top > dest)
+    {
+	/* Case 10
+	 * No folds after "line1" and before "dest"
+	 */
+	return;
+    }
+    else if (fp->fd_top > line2)
+    {
+	for (; valid_fold(fp, gap) && fold_end(fp) < dest; fp++)
+	/* Case 9. (for all case 9's) -- shift up. */
+	    fp->fd_top -= range_len;
+
+	if (valid_fold(fp, gap) && fp->fd_top < dest)
+	{
+	    /* Case 8. -- ensure truncated at dest, shift up */
+	    truncate_fold(fp, dest);
+	    fp->fd_top -= range_len;
+	}
+	return;
+    }
+    else if (fold_end(fp) > dest)
+    {
+	/* Case 7 -- remove nested folds and shrink */
+	foldMarkAdjustRecurse(&fp->fd_nested, line2 + 1 - fp->fd_top, dest -
+		fp->fd_top, MAXLNUM, -move_len);
+	fp->fd_len -= move_len;
+	fp->fd_top += move_len;
+	return;
+    }
+
+    /* Case 5 or 6
+     * changes rely on whether there are folds between the end of 
+     * this fold and "dest".
+     */
+    move_start = fold_index(fp, gap);
+
+    for (; valid_fold(fp, gap) && fp->fd_top <= dest; fp++)
+    {
+	if (fp->fd_top <= line2)
+	{
+	    /* 1. 2. or 3. */
+	    if (fold_end(fp) > line2)
+		/* 2. or 3., truncate before moving */
+		truncate_fold(fp, line2);
+
+	    fp->fd_top += move_len;
+	    continue;
+	}
+
+	/* Record index of the first fold after the moved range. */
+	if (move_end == 0)
+	    move_end = fold_index(fp, gap);
+
+	if (fold_end(fp) > dest)
+	    truncate_fold(fp, dest);
+
+	fp->fd_top -= range_len;
+    }
+
+    dest_index = fold_index(fp, gap);
+
+    /*
+     * All folds are now correct, but they are not necessarily in the correct
+     * order. We have to swap folds in the range [move_end, dest_index) with
+     * those in the range [move_start, move_end).
+     */
+    foldReverseOrder(gap, move_start, dest_index - 1);
+    foldReverseOrder(gap, move_start, move_start + dest_index - move_end - 1);
+    foldReverseOrder(gap, move_start + dest_index - move_end, dest_index - 1);
+}
+#undef fold_end
+#undef valid_fold
+#undef fold_index
+
 /* foldMerge() {{{2 */
 /*
  * Merge two adjacent folds (and the nested ones in them).