forked from aniani/vim
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)
This commit is contained in:
183
src/fold.c
183
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).
|
||||
|
Reference in New Issue
Block a user