summaryrefslogtreecommitdiff
path: root/src/fold.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/fold.c')
-rw-r--r--src/fold.c183
1 files changed, 183 insertions, 0 deletions
diff --git a/src/fold.c b/src/fold.c
index 826ad5c8d..6af71783b 100644
--- a/src/fold.c
+++ b/src/fold.c
@@ -64,6 +64,7 @@ static void deleteFoldMarkers(fold_T *fp, int recursive, linenr_T lnum_off);
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, linenr_T bot)
}
}
+/* 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).