1
0
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:
Bram Moolenaar
2017-03-14 21:53:58 +01:00
parent 84be8b6660
commit 88d298aed8
7 changed files with 355 additions and 10 deletions

View File

@@ -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).