Skip to content

Commit 88d298a

Browse files
committed
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)
1 parent 84be8b6 commit 88d298a

File tree

7 files changed

+355
-10
lines changed

7 files changed

+355
-10
lines changed

src/ex_cmds.c

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,8 @@ do_move(linenr_T line1, linenr_T line2, linenr_T dest)
800800
linenr_T last_line; /* Last line in file after adding new text */
801801
#ifdef FEAT_FOLDING
802802
int isFolded;
803+
win_T *win;
804+
tabpage_T *tp;
803805

804806
/* Moving lines seems to corrupt the folds, delete folding info now
805807
* and recreate it when finished. Don't do this for manual folding, it
@@ -851,24 +853,34 @@ do_move(linenr_T line1, linenr_T line2, linenr_T dest)
851853
* their final destination at the new text position -- webb
852854
*/
853855
last_line = curbuf->b_ml.ml_line_count;
854-
mark_adjust(line1, line2, last_line - line2, 0L);
855-
changed_lines(last_line - num_lines + 1, 0, last_line + 1, num_lines);
856+
mark_adjust_nofold(line1, line2, last_line - line2, 0L);
856857
if (dest >= line2)
857858
{
858-
mark_adjust(line2 + 1, dest, -num_lines, 0L);
859+
mark_adjust_nofold(line2 + 1, dest, -num_lines, 0L);
860+
#ifdef FEAT_FOLDING
861+
FOR_ALL_TAB_WINDOWS(tp, win) {
862+
if (win->w_buffer == curbuf)
863+
foldMoveRange(&win->w_folds, line1, line2, dest);
864+
}
865+
#endif
859866
curbuf->b_op_start.lnum = dest - num_lines + 1;
860867
curbuf->b_op_end.lnum = dest;
861868
}
862869
else
863870
{
864-
mark_adjust(dest + 1, line1 - 1, num_lines, 0L);
871+
mark_adjust_nofold(dest + 1, line1 - 1, num_lines, 0L);
872+
#ifdef FEAT_FOLDING
873+
FOR_ALL_TAB_WINDOWS(tp, win) {
874+
if (win->w_buffer == curbuf)
875+
foldMoveRange(&win->w_folds, dest + 1, line1 - 1, line2);
876+
}
877+
#endif
865878
curbuf->b_op_start.lnum = dest + 1;
866879
curbuf->b_op_end.lnum = dest + num_lines;
867880
}
868881
curbuf->b_op_start.col = curbuf->b_op_end.col = 0;
869-
mark_adjust(last_line - num_lines + 1, last_line,
882+
mark_adjust_nofold(last_line - num_lines + 1, last_line,
870883
-(last_line - dest - extra), 0L);
871-
changed_lines(last_line - num_lines + 1, 0, last_line + 1, -extra);
872884

873885
/*
874886
* Now we delete the original text -- webb
@@ -907,9 +919,9 @@ do_move(linenr_T line1, linenr_T line2, linenr_T dest)
907919
changed_lines(dest + 1, 0, line1 + num_lines, 0L);
908920

909921
#ifdef FEAT_FOLDING
910-
/* recreate folds */
911-
if (isFolded)
912-
foldUpdateAll(curwin);
922+
/* recreate folds */
923+
if (isFolded)
924+
foldUpdateAll(curwin);
913925
#endif
914926

915927
return OK;

src/fold.c

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ static void deleteFoldMarkers(fold_T *fp, int recursive, linenr_T lnum_off);
6464
static void foldDelMarker(linenr_T lnum, char_u *marker, int markerlen);
6565
static void foldUpdateIEMS(win_T *wp, linenr_T top, linenr_T bot);
6666
static void parseMarker(win_T *wp);
67+
static void foldMoveRange_int(garray_T *gap, linenr_T line1, linenr_T line2, linenr_T dest);
6768

6869
static char *e_nofold = N_("E490: No fold found");
6970

@@ -1075,6 +1076,12 @@ foldAdjustCursor(void)
10751076
(void)hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL);
10761077
}
10771078

1079+
/* foldMoveRange() {{{2 */
1080+
void
1081+
foldMoveRange(garray_T *gap, linenr_T line1, linenr_T line2, linenr_T dest)
1082+
{
1083+
foldMoveRange_int(gap, line1, line2, dest);
1084+
}
10781085
/* Internal functions for "fold_T" {{{1 */
10791086
/* cloneFoldGrowArray() {{{2 */
10801087
/*
@@ -2968,6 +2975,182 @@ foldRemove(garray_T *gap, linenr_T top, linenr_T bot)
29682975
}
29692976
}
29702977

2978+
/* foldReverseOrder() {{{2 */
2979+
static void
2980+
foldReverseOrder(garray_T *gap, linenr_T start, linenr_T end)
2981+
{
2982+
fold_T *left, *right;
2983+
fold_T tmp;
2984+
2985+
for (; start < end; start++, end--)
2986+
{
2987+
left = (fold_T *)gap->ga_data + start;
2988+
right = (fold_T *)gap->ga_data + end;
2989+
tmp = *left;
2990+
*left = *right;
2991+
*right = tmp;
2992+
}
2993+
}
2994+
2995+
/* foldMoveRange_int() {{{2 */
2996+
/*
2997+
* Move folds within the inclusive range "line1" to "line2" to after "dest"
2998+
* requires "line1" <= "line2" <= "dest"
2999+
*
3000+
* There are the following situations for the first fold at or below line1 - 1.
3001+
* 1 2 3 4
3002+
* 1 2 3 4
3003+
* line1 2 3 4
3004+
* 2 3 4 5 6 7
3005+
* line2 3 4 5 6 7
3006+
* 3 4 6 7 8 9
3007+
* dest 4 7 8 9
3008+
* 4 7 8 10
3009+
* 4 7 8 10
3010+
*
3011+
* In the following descriptions, "moved" means moving in the buffer, *and* in
3012+
* the fold array.
3013+
* Meanwhile, "shifted" just means moving in the buffer.
3014+
* 1. not changed
3015+
* 2. truncated above line1
3016+
* 3. length reduced by line2 - line1, folds starting between the end of 3 and
3017+
* dest are truncated and shifted up
3018+
* 4. internal folds moved (from [line1, line2] to dest)
3019+
* 5. moved to dest.
3020+
* 6. truncated below line2 and moved.
3021+
* 7. length reduced by line2 - dest, folds starting between line2 and dest are
3022+
* removed, top is moved down by move_len.
3023+
* 8. truncated below dest and shifted up.
3024+
* 9. shifted up
3025+
* 10. not changed
3026+
*/
3027+
3028+
static void
3029+
truncate_fold(fold_T *fp, linenr_T end)
3030+
{
3031+
foldRemove(&fp->fd_nested, end - fp->fd_top, MAXLNUM);
3032+
fp->fd_len = end - fp->fd_top + 1;
3033+
}
3034+
3035+
#define fold_end(fp) ((fp)->fd_top + (fp)->fd_len - 1)
3036+
#define valid_fold(fp, gap) ((fp) < ((fold_T *)(gap)->ga_data + (gap)->ga_len))
3037+
#define fold_index(fp, gap) ((size_t)(fp - ((fold_T *)(gap)->ga_data)))
3038+
3039+
static void
3040+
foldMoveRange_int(garray_T *gap, linenr_T line1, linenr_T line2, linenr_T dest)
3041+
{
3042+
fold_T *fp;
3043+
linenr_T range_len = line2 - line1 + 1;
3044+
linenr_T move_len = dest - line2;
3045+
int at_start = foldFind(gap, line1 - 1, &fp);
3046+
size_t move_start = 0, move_end = 0, dest_index = 0;
3047+
3048+
if (at_start)
3049+
{
3050+
if (fold_end(fp) > dest)
3051+
{
3052+
/* Case 4
3053+
* don't have to change this fold, but have to move nested folds.
3054+
*/
3055+
foldMoveRange(&fp->fd_nested, line1 - fp->fd_top, line2 -
3056+
fp->fd_top, dest - fp->fd_top);
3057+
return;
3058+
}
3059+
else if (fold_end(fp) > line2)
3060+
{
3061+
/* Case 3
3062+
* Remove nested folds between line1 and line2 & reduce the
3063+
* length of fold by "range_len".
3064+
* Folds after this one must be dealt with.
3065+
*/
3066+
foldMarkAdjustRecurse(&fp->fd_nested, line1 - fp->fd_top, line2 -
3067+
fp->fd_top, MAXLNUM, -range_len);
3068+
fp->fd_len -= range_len;
3069+
}
3070+
else
3071+
/* Case 2 truncate fold, folds after this one must be dealt with. */
3072+
truncate_fold(fp, line1);
3073+
3074+
/* Look at the next fold, and treat that one as if it were the first
3075+
* after "line1" (because now it is). */
3076+
fp = fp + 1;
3077+
}
3078+
3079+
if (!valid_fold(fp, gap) || fp->fd_top > dest)
3080+
{
3081+
/* Case 10
3082+
* No folds after "line1" and before "dest"
3083+
*/
3084+
return;
3085+
}
3086+
else if (fp->fd_top > line2)
3087+
{
3088+
for (; valid_fold(fp, gap) && fold_end(fp) < dest; fp++)
3089+
/* Case 9. (for all case 9's) -- shift up. */
3090+
fp->fd_top -= range_len;
3091+
3092+
if (valid_fold(fp, gap) && fp->fd_top < dest)
3093+
{
3094+
/* Case 8. -- ensure truncated at dest, shift up */
3095+
truncate_fold(fp, dest);
3096+
fp->fd_top -= range_len;
3097+
}
3098+
return;
3099+
}
3100+
else if (fold_end(fp) > dest)
3101+
{
3102+
/* Case 7 -- remove nested folds and shrink */
3103+
foldMarkAdjustRecurse(&fp->fd_nested, line2 + 1 - fp->fd_top, dest -
3104+
fp->fd_top, MAXLNUM, -move_len);
3105+
fp->fd_len -= move_len;
3106+
fp->fd_top += move_len;
3107+
return;
3108+
}
3109+
3110+
/* Case 5 or 6
3111+
* changes rely on whether there are folds between the end of
3112+
* this fold and "dest".
3113+
*/
3114+
move_start = fold_index(fp, gap);
3115+
3116+
for (; valid_fold(fp, gap) && fp->fd_top <= dest; fp++)
3117+
{
3118+
if (fp->fd_top <= line2)
3119+
{
3120+
/* 1. 2. or 3. */
3121+
if (fold_end(fp) > line2)
3122+
/* 2. or 3., truncate before moving */
3123+
truncate_fold(fp, line2);
3124+
3125+
fp->fd_top += move_len;
3126+
continue;
3127+
}
3128+
3129+
/* Record index of the first fold after the moved range. */
3130+
if (move_end == 0)
3131+
move_end = fold_index(fp, gap);
3132+
3133+
if (fold_end(fp) > dest)
3134+
truncate_fold(fp, dest);
3135+
3136+
fp->fd_top -= range_len;
3137+
}
3138+
3139+
dest_index = fold_index(fp, gap);
3140+
3141+
/*
3142+
* All folds are now correct, but they are not necessarily in the correct
3143+
* order. We have to swap folds in the range [move_end, dest_index) with
3144+
* those in the range [move_start, move_end).
3145+
*/
3146+
foldReverseOrder(gap, move_start, dest_index - 1);
3147+
foldReverseOrder(gap, move_start, move_start + dest_index - move_end - 1);
3148+
foldReverseOrder(gap, move_start + dest_index - move_end, dest_index - 1);
3149+
}
3150+
#undef fold_end
3151+
#undef valid_fold
3152+
#undef fold_index
3153+
29713154
/* foldMerge() {{{2 */
29723155
/*
29733156
* Merge two adjacent folds (and the nested ones in them).

src/mark.c

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ static void cleanup_jumplist(void);
3737
#ifdef FEAT_VIMINFO
3838
static void write_one_filemark(FILE *fp, xfmark_T *fm, int c1, int c2);
3939
#endif
40+
static void mark_adjust_internal(linenr_T line1, linenr_T line2, long amount,
41+
long amount_after, int adjust_folds);
4042

4143
/*
4244
* Set named mark "c" at current cursor position.
@@ -1028,6 +1030,27 @@ mark_adjust(
10281030
linenr_T line2,
10291031
long amount,
10301032
long amount_after)
1033+
{
1034+
mark_adjust_internal(line1, line2, amount, amount_after, TRUE);
1035+
}
1036+
1037+
void
1038+
mark_adjust_nofold(
1039+
linenr_T line1,
1040+
linenr_T line2,
1041+
long amount,
1042+
long amount_after)
1043+
{
1044+
mark_adjust_internal(line1, line2, amount, amount_after, FALSE);
1045+
}
1046+
1047+
static void
1048+
mark_adjust_internal(
1049+
linenr_T line1,
1050+
linenr_T line2,
1051+
long amount,
1052+
long amount_after,
1053+
int adjust_folds UNUSED)
10311054
{
10321055
int i;
10331056
int fnum = curbuf->b_fnum;
@@ -1174,7 +1197,8 @@ mark_adjust(
11741197

11751198
#ifdef FEAT_FOLDING
11761199
/* adjust folds */
1177-
foldMarkAdjust(win, line1, line2, amount, amount_after);
1200+
if (adjust_folds)
1201+
foldMarkAdjust(win, line1, line2, amount, amount_after);
11781202
#endif
11791203
}
11801204
}

src/proto/fold.pro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ void foldInitWin(win_T *new_win);
3131
int find_wl_entry(win_T *win, linenr_T lnum);
3232
void foldAdjustVisual(void);
3333
void foldAdjustCursor(void);
34+
void foldMoveRange(garray_T *gap, linenr_T line1, linenr_T line2, linenr_T dest);
3435
void cloneFoldGrowArray(garray_T *from, garray_T *to);
3536
void deleteFoldRecurse(garray_T *gap);
3637
void foldMarkAdjust(win_T *wp, linenr_T line1, linenr_T line2, long amount, long amount_after);

src/proto/mark.pro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ void ex_jumps(exarg_T *eap);
1919
void ex_clearjumps(exarg_T *eap);
2020
void ex_changes(exarg_T *eap);
2121
void mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after);
22+
void mark_adjust_nofold(linenr_T line1, linenr_T line2, long amount, long amount_after);
2223
void mark_col_adjust(linenr_T lnum, colnr_T mincol, long lnum_amount, long col_amount);
2324
void copy_jumplist(win_T *from, win_T *to);
2425
void free_jumplist(win_T *wp);

0 commit comments

Comments
 (0)