1 /* vi:set ts=8 sts=4 sw=4 noet:
2  * vim600:fdm=marker fdl=1 fdc=3:
3  *
4  * VIM - Vi IMproved	by Bram Moolenaar
5  *
6  * Do ":help uganda"  in Vim to read copying and usage conditions.
7  * Do ":help credits" in Vim to see a list of people who contributed.
8  * See README.txt for an overview of the Vim source code.
9  */
10 
11 /*
12  * fold.c: code for folding
13  */
14 
15 #include "vim.h"
16 
17 #if defined(FEAT_FOLDING) || defined(PROTO)
18 
19 // local declarations. {{{1
20 // typedef fold_T {{{2
21 /*
22  * The toplevel folds for each window are stored in the w_folds growarray.
23  * Each toplevel fold can contain an array of second level folds in the
24  * fd_nested growarray.
25  * The info stored in both growarrays is the same: An array of fold_T.
26  */
27 typedef struct
28 {
29     linenr_T	fd_top;		// first line of fold; for nested fold
30 				// relative to parent
31     linenr_T	fd_len;		// number of lines in the fold
32     garray_T	fd_nested;	// array of nested folds
33     char	fd_flags;	// see below
34     char	fd_small;	// TRUE, FALSE or MAYBE: fold smaller than
35 				// 'foldminlines'; MAYBE applies to nested
36 				// folds too
37 } fold_T;
38 
39 #define FD_OPEN		0	// fold is open (nested ones can be closed)
40 #define FD_CLOSED	1	// fold is closed
41 #define FD_LEVEL	2	// depends on 'foldlevel' (nested folds too)
42 
43 #define MAX_LEVEL	20	// maximum fold depth
44 
45 // static functions {{{2
46 static void newFoldLevelWin(win_T *wp);
47 static int checkCloseRec(garray_T *gap, linenr_T lnum, int level);
48 static int foldFind(garray_T *gap, linenr_T lnum, fold_T **fpp);
49 static int foldLevelWin(win_T *wp, linenr_T lnum);
50 static void checkupdate(win_T *wp);
51 static void setFoldRepeat(linenr_T lnum, long count, int do_open);
52 static linenr_T setManualFold(linenr_T lnum, int opening, int recurse, int *donep);
53 static linenr_T setManualFoldWin(win_T *wp, linenr_T lnum, int opening, int recurse, int *donep);
54 static void foldOpenNested(fold_T *fpr);
55 static void deleteFoldEntry(garray_T *gap, int idx, int recursive);
56 static void foldMarkAdjustRecurse(garray_T *gap, linenr_T line1, linenr_T line2, long amount, long amount_after);
57 static int getDeepestNestingRecurse(garray_T *gap);
58 static int check_closed(win_T *win, fold_T *fp, int *use_levelp, int level, int *maybe_smallp, linenr_T lnum_off);
59 static void checkSmall(win_T *wp, fold_T *fp, linenr_T lnum_off);
60 static void setSmallMaybe(garray_T *gap);
61 static void foldCreateMarkers(linenr_T start, linenr_T end);
62 static void foldAddMarker(linenr_T lnum, char_u *marker, int markerlen);
63 static void deleteFoldMarkers(fold_T *fp, int recursive, linenr_T lnum_off);
64 static void foldDelMarker(linenr_T lnum, char_u *marker, int markerlen);
65 static void foldUpdateIEMS(win_T *wp, linenr_T top, linenr_T bot);
66 static void parseMarker(win_T *wp);
67 
68 static char *e_nofold = N_("E490: No fold found");
69 
70 /*
71  * While updating the folds lines between invalid_top and invalid_bot have an
72  * undefined fold level.  Only used for the window currently being updated.
73  */
74 static linenr_T invalid_top = (linenr_T)0;
75 static linenr_T invalid_bot = (linenr_T)0;
76 
77 /*
78  * When using 'foldexpr' we sometimes get the level of the next line, which
79  * calls foldlevel() to get the level of the current line, which hasn't been
80  * stored yet.  To get around this chicken-egg problem the level of the
81  * previous line is stored here when available.  prev_lnum is zero when the
82  * level is not available.
83  */
84 static linenr_T prev_lnum = 0;
85 static int prev_lnum_lvl = -1;
86 
87 // Flags used for "done" argument of setManualFold.
88 #define DONE_NOTHING	0
89 #define DONE_ACTION	1	// did close or open a fold
90 #define DONE_FOLD	2	// did find a fold
91 
92 static int foldstartmarkerlen;
93 static char_u *foldendmarker;
94 static int foldendmarkerlen;
95 
96 // Exported folding functions. {{{1
97 // copyFoldingState() {{{2
98 
99 /*
100  * Copy that folding state from window "wp_from" to window "wp_to".
101  */
102     void
copyFoldingState(win_T * wp_from,win_T * wp_to)103 copyFoldingState(win_T *wp_from, win_T *wp_to)
104 {
105     wp_to->w_fold_manual = wp_from->w_fold_manual;
106     wp_to->w_foldinvalid = wp_from->w_foldinvalid;
107     cloneFoldGrowArray(&wp_from->w_folds, &wp_to->w_folds);
108 }
109 
110 // hasAnyFolding() {{{2
111 /*
112  * Return TRUE if there may be folded lines in the current window.
113  */
114     int
hasAnyFolding(win_T * win)115 hasAnyFolding(win_T *win)
116 {
117     // very simple now, but can become more complex later
118     return (win->w_p_fen
119 	    && (!foldmethodIsManual(win) || win->w_folds.ga_len > 0));
120 }
121 
122 // hasFolding() {{{2
123 /*
124  * Return TRUE if line "lnum" in the current window is part of a closed
125  * fold.
126  * When returning TRUE, *firstp and *lastp are set to the first and last
127  * lnum of the sequence of folded lines (skipped when NULL).
128  */
129     int
hasFolding(linenr_T lnum,linenr_T * firstp,linenr_T * lastp)130 hasFolding(linenr_T lnum, linenr_T *firstp, linenr_T *lastp)
131 {
132     return hasFoldingWin(curwin, lnum, firstp, lastp, TRUE, NULL);
133 }
134 
135 // hasFoldingWin() {{{2
136     int
hasFoldingWin(win_T * win,linenr_T lnum,linenr_T * firstp,linenr_T * lastp,int cache,foldinfo_T * infop)137 hasFoldingWin(
138     win_T	*win,
139     linenr_T	lnum,
140     linenr_T	*firstp,
141     linenr_T	*lastp,
142     int		cache,		// when TRUE: use cached values of window
143     foldinfo_T	*infop)		// where to store fold info
144 {
145     int		had_folded = FALSE;
146     linenr_T	first = 0;
147     linenr_T	last = 0;
148     linenr_T	lnum_rel = lnum;
149     int		x;
150     fold_T	*fp;
151     int		level = 0;
152     int		use_level = FALSE;
153     int		maybe_small = FALSE;
154     garray_T	*gap;
155     int		low_level = 0;
156 
157     checkupdate(win);
158 
159     /*
160      * Return quickly when there is no folding at all in this window.
161      */
162     if (!hasAnyFolding(win))
163     {
164 	if (infop != NULL)
165 	    infop->fi_level = 0;
166 	return FALSE;
167     }
168 
169     if (cache)
170     {
171 	/*
172 	 * First look in cached info for displayed lines.  This is probably
173 	 * the fastest, but it can only be used if the entry is still valid.
174 	 */
175 	x = find_wl_entry(win, lnum);
176 	if (x >= 0)
177 	{
178 	    first = win->w_lines[x].wl_lnum;
179 	    last = win->w_lines[x].wl_lastlnum;
180 	    had_folded = win->w_lines[x].wl_folded;
181 	}
182     }
183 
184     if (first == 0)
185     {
186 	/*
187 	 * Recursively search for a fold that contains "lnum".
188 	 */
189 	gap = &win->w_folds;
190 	for (;;)
191 	{
192 	    if (!foldFind(gap, lnum_rel, &fp))
193 		break;
194 
195 	    // Remember lowest level of fold that starts in "lnum".
196 	    if (lnum_rel == fp->fd_top && low_level == 0)
197 		low_level = level + 1;
198 
199 	    first += fp->fd_top;
200 	    last += fp->fd_top;
201 
202 	    // is this fold closed?
203 	    had_folded = check_closed(win, fp, &use_level, level,
204 					       &maybe_small, lnum - lnum_rel);
205 	    if (had_folded)
206 	    {
207 		// Fold closed: Set last and quit loop.
208 		last += fp->fd_len - 1;
209 		break;
210 	    }
211 
212 	    // Fold found, but it's open: Check nested folds.  Line number is
213 	    // relative to containing fold.
214 	    gap = &fp->fd_nested;
215 	    lnum_rel -= fp->fd_top;
216 	    ++level;
217 	}
218     }
219 
220     if (!had_folded)
221     {
222 	if (infop != NULL)
223 	{
224 	    infop->fi_level = level;
225 	    infop->fi_lnum = lnum - lnum_rel;
226 	    infop->fi_low_level = low_level == 0 ? level : low_level;
227 	}
228 	return FALSE;
229     }
230 
231     if (last > win->w_buffer->b_ml.ml_line_count)
232 	last = win->w_buffer->b_ml.ml_line_count;
233     if (lastp != NULL)
234 	*lastp = last;
235     if (firstp != NULL)
236 	*firstp = first;
237     if (infop != NULL)
238     {
239 	infop->fi_level = level + 1;
240 	infop->fi_lnum = first;
241 	infop->fi_low_level = low_level == 0 ? level + 1 : low_level;
242     }
243     return TRUE;
244 }
245 
246 // foldLevel() {{{2
247 #ifdef FEAT_EVAL
248 /*
249  * Return fold level at line number "lnum" in the current window.
250  */
251     static int
foldLevel(linenr_T lnum)252 foldLevel(linenr_T lnum)
253 {
254     // While updating the folds lines between invalid_top and invalid_bot have
255     // an undefined fold level.  Otherwise update the folds first.
256     if (invalid_top == (linenr_T)0)
257 	checkupdate(curwin);
258     else if (lnum == prev_lnum && prev_lnum_lvl >= 0)
259 	return prev_lnum_lvl;
260     else if (lnum >= invalid_top && lnum <= invalid_bot)
261 	return -1;
262 
263     // Return quickly when there is no folding at all in this window.
264     if (!hasAnyFolding(curwin))
265 	return 0;
266 
267     return foldLevelWin(curwin, lnum);
268 }
269 #endif
270 
271 // lineFolded()	{{{2
272 /*
273  * Low level function to check if a line is folded.  Doesn't use any caching.
274  * Return TRUE if line is folded.
275  * Return FALSE if line is not folded.
276  * Return MAYBE if the line is folded when next to a folded line.
277  */
278     int
lineFolded(win_T * win,linenr_T lnum)279 lineFolded(win_T *win, linenr_T lnum)
280 {
281     return foldedCount(win, lnum, NULL) != 0;
282 }
283 
284 // foldedCount() {{{2
285 /*
286  * Count the number of lines that are folded at line number "lnum".
287  * Normally "lnum" is the first line of a possible fold, and the returned
288  * number is the number of lines in the fold.
289  * Doesn't use caching from the displayed window.
290  * Returns number of folded lines from "lnum", or 0 if line is not folded.
291  * When "infop" is not NULL, fills *infop with the fold level info.
292  */
293     long
foldedCount(win_T * win,linenr_T lnum,foldinfo_T * infop)294 foldedCount(win_T *win, linenr_T lnum, foldinfo_T *infop)
295 {
296     linenr_T	last;
297 
298     if (hasFoldingWin(win, lnum, NULL, &last, FALSE, infop))
299 	return (long)(last - lnum + 1);
300     return 0;
301 }
302 
303 // foldmethodIsManual() {{{2
304 /*
305  * Return TRUE if 'foldmethod' is "manual"
306  */
307     int
foldmethodIsManual(win_T * wp)308 foldmethodIsManual(win_T *wp)
309 {
310     return (wp->w_p_fdm[0] != NUL && wp->w_p_fdm[3] == 'u');
311 }
312 
313 // foldmethodIsIndent() {{{2
314 /*
315  * Return TRUE if 'foldmethod' is "indent"
316  */
317     int
foldmethodIsIndent(win_T * wp)318 foldmethodIsIndent(win_T *wp)
319 {
320     return (wp->w_p_fdm[0] == 'i');
321 }
322 
323 // foldmethodIsExpr() {{{2
324 /*
325  * Return TRUE if 'foldmethod' is "expr"
326  */
327     int
foldmethodIsExpr(win_T * wp)328 foldmethodIsExpr(win_T *wp)
329 {
330     return (wp->w_p_fdm[0] != NUL && wp->w_p_fdm[1] == 'x');
331 }
332 
333 // foldmethodIsMarker() {{{2
334 /*
335  * Return TRUE if 'foldmethod' is "marker"
336  */
337     int
foldmethodIsMarker(win_T * wp)338 foldmethodIsMarker(win_T *wp)
339 {
340     return (wp->w_p_fdm[0] != NUL && wp->w_p_fdm[2] == 'r');
341 }
342 
343 // foldmethodIsSyntax() {{{2
344 /*
345  * Return TRUE if 'foldmethod' is "syntax"
346  */
347     int
foldmethodIsSyntax(win_T * wp)348 foldmethodIsSyntax(win_T *wp)
349 {
350     return (wp->w_p_fdm[0] == 's');
351 }
352 
353 // foldmethodIsDiff() {{{2
354 /*
355  * Return TRUE if 'foldmethod' is "diff"
356  */
357     int
foldmethodIsDiff(win_T * wp)358 foldmethodIsDiff(win_T *wp)
359 {
360     return (wp->w_p_fdm[0] == 'd');
361 }
362 
363 // closeFold() {{{2
364 /*
365  * Close fold for current window at line "lnum".
366  * Repeat "count" times.
367  */
368     void
closeFold(linenr_T lnum,long count)369 closeFold(linenr_T lnum, long count)
370 {
371     setFoldRepeat(lnum, count, FALSE);
372 }
373 
374 // closeFoldRecurse() {{{2
375 /*
376  * Close fold for current window at line "lnum" recursively.
377  */
378     void
closeFoldRecurse(linenr_T lnum)379 closeFoldRecurse(linenr_T lnum)
380 {
381     (void)setManualFold(lnum, FALSE, TRUE, NULL);
382 }
383 
384 // opFoldRange() {{{2
385 /*
386  * Open or Close folds for current window in lines "first" to "last".
387  * Used for "zo", "zO", "zc" and "zC" in Visual mode.
388  */
389     void
opFoldRange(linenr_T first,linenr_T last,int opening,int recurse,int had_visual)390 opFoldRange(
391     linenr_T	first,
392     linenr_T	last,
393     int		opening,	// TRUE to open, FALSE to close
394     int		recurse,	// TRUE to do it recursively
395     int		had_visual)	// TRUE when Visual selection used
396 {
397     int		done = DONE_NOTHING;	// avoid error messages
398     linenr_T	lnum;
399     linenr_T	lnum_next;
400 
401     for (lnum = first; lnum <= last; lnum = lnum_next + 1)
402     {
403 	lnum_next = lnum;
404 	// Opening one level only: next fold to open is after the one going to
405 	// be opened.
406 	if (opening && !recurse)
407 	    (void)hasFolding(lnum, NULL, &lnum_next);
408 	(void)setManualFold(lnum, opening, recurse, &done);
409 	// Closing one level only: next line to close a fold is after just
410 	// closed fold.
411 	if (!opening && !recurse)
412 	    (void)hasFolding(lnum, NULL, &lnum_next);
413     }
414     if (done == DONE_NOTHING)
415 	emsg(_(e_nofold));
416     // Force a redraw to remove the Visual highlighting.
417     if (had_visual)
418 	redraw_curbuf_later(INVERTED);
419 }
420 
421 // openFold() {{{2
422 /*
423  * Open fold for current window at line "lnum".
424  * Repeat "count" times.
425  */
426     void
openFold(linenr_T lnum,long count)427 openFold(linenr_T lnum, long count)
428 {
429     setFoldRepeat(lnum, count, TRUE);
430 }
431 
432 // openFoldRecurse() {{{2
433 /*
434  * Open fold for current window at line "lnum" recursively.
435  */
436     void
openFoldRecurse(linenr_T lnum)437 openFoldRecurse(linenr_T lnum)
438 {
439     (void)setManualFold(lnum, TRUE, TRUE, NULL);
440 }
441 
442 // foldOpenCursor() {{{2
443 /*
444  * Open folds until the cursor line is not in a closed fold.
445  */
446     void
foldOpenCursor(void)447 foldOpenCursor(void)
448 {
449     int		done;
450 
451     checkupdate(curwin);
452     if (hasAnyFolding(curwin))
453 	for (;;)
454 	{
455 	    done = DONE_NOTHING;
456 	    (void)setManualFold(curwin->w_cursor.lnum, TRUE, FALSE, &done);
457 	    if (!(done & DONE_ACTION))
458 		break;
459 	}
460 }
461 
462 // newFoldLevel() {{{2
463 /*
464  * Set new foldlevel for current window.
465  */
466     void
newFoldLevel(void)467 newFoldLevel(void)
468 {
469     newFoldLevelWin(curwin);
470 
471 #ifdef FEAT_DIFF
472     if (foldmethodIsDiff(curwin) && curwin->w_p_scb)
473     {
474 	win_T	    *wp;
475 
476 	/*
477 	 * Set the same foldlevel in other windows in diff mode.
478 	 */
479 	FOR_ALL_WINDOWS(wp)
480 	{
481 	    if (wp != curwin && foldmethodIsDiff(wp) && wp->w_p_scb)
482 	    {
483 		wp->w_p_fdl = curwin->w_p_fdl;
484 		newFoldLevelWin(wp);
485 	    }
486 	}
487     }
488 #endif
489 }
490 
491     static void
newFoldLevelWin(win_T * wp)492 newFoldLevelWin(win_T *wp)
493 {
494     fold_T	*fp;
495     int		i;
496 
497     checkupdate(wp);
498     if (wp->w_fold_manual)
499     {
500 	// Set all flags for the first level of folds to FD_LEVEL.  Following
501 	// manual open/close will then change the flags to FD_OPEN or
502 	// FD_CLOSED for those folds that don't use 'foldlevel'.
503 	fp = (fold_T *)wp->w_folds.ga_data;
504 	for (i = 0; i < wp->w_folds.ga_len; ++i)
505 	    fp[i].fd_flags = FD_LEVEL;
506 	wp->w_fold_manual = FALSE;
507     }
508     changed_window_setting_win(wp);
509 }
510 
511 // foldCheckClose() {{{2
512 /*
513  * Apply 'foldlevel' to all folds that don't contain the cursor.
514  */
515     void
foldCheckClose(void)516 foldCheckClose(void)
517 {
518     if (*p_fcl != NUL)	// can only be "all" right now
519     {
520 	checkupdate(curwin);
521 	if (checkCloseRec(&curwin->w_folds, curwin->w_cursor.lnum,
522 							(int)curwin->w_p_fdl))
523 	    changed_window_setting();
524     }
525 }
526 
527 // checkCloseRec() {{{2
528     static int
checkCloseRec(garray_T * gap,linenr_T lnum,int level)529 checkCloseRec(garray_T *gap, linenr_T lnum, int level)
530 {
531     fold_T	*fp;
532     int		retval = FALSE;
533     int		i;
534 
535     fp = (fold_T *)gap->ga_data;
536     for (i = 0; i < gap->ga_len; ++i)
537     {
538 	// Only manually opened folds may need to be closed.
539 	if (fp[i].fd_flags == FD_OPEN)
540 	{
541 	    if (level <= 0 && (lnum < fp[i].fd_top
542 				      || lnum >= fp[i].fd_top + fp[i].fd_len))
543 	    {
544 		fp[i].fd_flags = FD_LEVEL;
545 		retval = TRUE;
546 	    }
547 	    else
548 		retval |= checkCloseRec(&fp[i].fd_nested, lnum - fp[i].fd_top,
549 								   level - 1);
550 	}
551     }
552     return retval;
553 }
554 
555 // foldCreateAllowed() {{{2
556 /*
557  * Return TRUE if it's allowed to manually create or delete a fold.
558  * Give an error message and return FALSE if not.
559  */
560     int
foldManualAllowed(int create)561 foldManualAllowed(int create)
562 {
563     if (foldmethodIsManual(curwin) || foldmethodIsMarker(curwin))
564 	return TRUE;
565     if (create)
566 	emsg(_("E350: Cannot create fold with current 'foldmethod'"));
567     else
568 	emsg(_("E351: Cannot delete fold with current 'foldmethod'"));
569     return FALSE;
570 }
571 
572 // foldCreate() {{{2
573 /*
574  * Create a fold from line "start" to line "end" (inclusive) in the current
575  * window.
576  */
577     void
foldCreate(linenr_T start,linenr_T end)578 foldCreate(linenr_T start, linenr_T end)
579 {
580     fold_T	*fp;
581     garray_T	*gap;
582     garray_T	fold_ga;
583     int		i, j;
584     int		cont;
585     int		use_level = FALSE;
586     int		closed = FALSE;
587     int		level = 0;
588     linenr_T	start_rel = start;
589     linenr_T	end_rel = end;
590 
591     if (start > end)
592     {
593 	// reverse the range
594 	end = start_rel;
595 	start = end_rel;
596 	start_rel = start;
597 	end_rel = end;
598     }
599 
600     // When 'foldmethod' is "marker" add markers, which creates the folds.
601     if (foldmethodIsMarker(curwin))
602     {
603 	foldCreateMarkers(start, end);
604 	return;
605     }
606 
607     checkupdate(curwin);
608 
609     // Find the place to insert the new fold.
610     gap = &curwin->w_folds;
611     if (gap->ga_len == 0)
612 	i = 0;
613     else
614     {
615 	for (;;)
616 	{
617 	    if (!foldFind(gap, start_rel, &fp))
618 		break;
619 	    if (fp->fd_top + fp->fd_len > end_rel)
620 	    {
621 		// New fold is completely inside this fold: Go one level
622 		// deeper.
623 		gap = &fp->fd_nested;
624 		start_rel -= fp->fd_top;
625 		end_rel -= fp->fd_top;
626 		if (use_level || fp->fd_flags == FD_LEVEL)
627 		{
628 		    use_level = TRUE;
629 		    if (level >= curwin->w_p_fdl)
630 			closed = TRUE;
631 		}
632 		else if (fp->fd_flags == FD_CLOSED)
633 		    closed = TRUE;
634 		++level;
635 	    }
636 	    else
637 	    {
638 		// This fold and new fold overlap: Insert here and move some
639 		// folds inside the new fold.
640 		break;
641 	    }
642 	}
643 	if (gap->ga_len == 0)
644 	    i = 0;
645 	else
646 	    i = (int)(fp - (fold_T *)gap->ga_data);
647     }
648 
649     if (ga_grow(gap, 1) == OK)
650     {
651 	fp = (fold_T *)gap->ga_data + i;
652 	ga_init2(&fold_ga, (int)sizeof(fold_T), 10);
653 
654 	// Count number of folds that will be contained in the new fold.
655 	for (cont = 0; i + cont < gap->ga_len; ++cont)
656 	    if (fp[cont].fd_top > end_rel)
657 		break;
658 	if (cont > 0 && ga_grow(&fold_ga, cont) == OK)
659 	{
660 	    // If the first fold starts before the new fold, let the new fold
661 	    // start there.  Otherwise the existing fold would change.
662 	    if (start_rel > fp->fd_top)
663 		start_rel = fp->fd_top;
664 
665 	    // When last contained fold isn't completely contained, adjust end
666 	    // of new fold.
667 	    if (end_rel < fp[cont - 1].fd_top + fp[cont - 1].fd_len - 1)
668 		end_rel = fp[cont - 1].fd_top + fp[cont - 1].fd_len - 1;
669 	    // Move contained folds to inside new fold.
670 	    mch_memmove(fold_ga.ga_data, fp, sizeof(fold_T) * cont);
671 	    fold_ga.ga_len += cont;
672 	    i += cont;
673 
674 	    // Adjust line numbers in contained folds to be relative to the
675 	    // new fold.
676 	    for (j = 0; j < cont; ++j)
677 		((fold_T *)fold_ga.ga_data)[j].fd_top -= start_rel;
678 	}
679 	// Move remaining entries to after the new fold.
680 	if (i < gap->ga_len)
681 	    mch_memmove(fp + 1, (fold_T *)gap->ga_data + i,
682 				     sizeof(fold_T) * (gap->ga_len - i));
683 	gap->ga_len = gap->ga_len + 1 - cont;
684 
685 	// insert new fold
686 	fp->fd_nested = fold_ga;
687 	fp->fd_top = start_rel;
688 	fp->fd_len = end_rel - start_rel + 1;
689 
690 	// We want the new fold to be closed.  If it would remain open because
691 	// of using 'foldlevel', need to adjust fd_flags of containing folds.
692 	if (use_level && !closed && level < curwin->w_p_fdl)
693 	    closeFold(start, 1L);
694 	if (!use_level)
695 	    curwin->w_fold_manual = TRUE;
696 	fp->fd_flags = FD_CLOSED;
697 	fp->fd_small = MAYBE;
698 
699 	// redraw
700 	changed_window_setting();
701     }
702 }
703 
704 // deleteFold() {{{2
705 /*
706  * Delete a fold at line "start" in the current window.
707  * When "end" is not 0, delete all folds from "start" to "end".
708  * When "recursive" is TRUE delete recursively.
709  */
710     void
deleteFold(linenr_T start,linenr_T end,int recursive,int had_visual)711 deleteFold(
712     linenr_T	start,
713     linenr_T	end,
714     int		recursive,
715     int		had_visual)	// TRUE when Visual selection used
716 {
717     garray_T	*gap;
718     fold_T	*fp;
719     garray_T	*found_ga;
720     fold_T	*found_fp = NULL;
721     linenr_T	found_off = 0;
722     int		use_level;
723     int		maybe_small = FALSE;
724     int		level = 0;
725     linenr_T	lnum = start;
726     linenr_T	lnum_off;
727     int		did_one = FALSE;
728     linenr_T	first_lnum = MAXLNUM;
729     linenr_T	last_lnum = 0;
730 
731     checkupdate(curwin);
732 
733     while (lnum <= end)
734     {
735 	// Find the deepest fold for "start".
736 	gap = &curwin->w_folds;
737 	found_ga = NULL;
738 	lnum_off = 0;
739 	use_level = FALSE;
740 	for (;;)
741 	{
742 	    if (!foldFind(gap, lnum - lnum_off, &fp))
743 		break;
744 	    // lnum is inside this fold, remember info
745 	    found_ga = gap;
746 	    found_fp = fp;
747 	    found_off = lnum_off;
748 
749 	    // if "lnum" is folded, don't check nesting
750 	    if (check_closed(curwin, fp, &use_level, level,
751 						      &maybe_small, lnum_off))
752 		break;
753 
754 	    // check nested folds
755 	    gap = &fp->fd_nested;
756 	    lnum_off += fp->fd_top;
757 	    ++level;
758 	}
759 	if (found_ga == NULL)
760 	{
761 	    ++lnum;
762 	}
763 	else
764 	{
765 	    lnum = found_fp->fd_top + found_fp->fd_len + found_off;
766 
767 	    if (foldmethodIsManual(curwin))
768 		deleteFoldEntry(found_ga,
769 		    (int)(found_fp - (fold_T *)found_ga->ga_data), recursive);
770 	    else
771 	    {
772 		if (first_lnum > found_fp->fd_top + found_off)
773 		    first_lnum = found_fp->fd_top + found_off;
774 		if (last_lnum < lnum)
775 		    last_lnum = lnum;
776 		if (!did_one)
777 		    parseMarker(curwin);
778 		deleteFoldMarkers(found_fp, recursive, found_off);
779 	    }
780 	    did_one = TRUE;
781 
782 	    // redraw window
783 	    changed_window_setting();
784 	}
785     }
786     if (!did_one)
787     {
788 	emsg(_(e_nofold));
789 	// Force a redraw to remove the Visual highlighting.
790 	if (had_visual)
791 	    redraw_curbuf_later(INVERTED);
792     }
793     else
794 	// Deleting markers may make cursor column invalid.
795 	check_cursor_col();
796 
797     if (last_lnum > 0)
798 	changed_lines(first_lnum, (colnr_T)0, last_lnum, 0L);
799 }
800 
801 // clearFolding() {{{2
802 /*
803  * Remove all folding for window "win".
804  */
805     void
clearFolding(win_T * win)806 clearFolding(win_T *win)
807 {
808     deleteFoldRecurse(&win->w_folds);
809     win->w_foldinvalid = FALSE;
810 }
811 
812 // foldUpdate() {{{2
813 /*
814  * Update folds for changes in the buffer of a window.
815  * Note that inserted/deleted lines must have already been taken care of by
816  * calling foldMarkAdjust().
817  * The changes in lines from top to bot (inclusive).
818  */
819     void
foldUpdate(win_T * wp,linenr_T top,linenr_T bot)820 foldUpdate(win_T *wp, linenr_T top, linenr_T bot)
821 {
822     fold_T	*fp;
823 
824     if (disable_fold_update > 0)
825 	return;
826 #ifdef FEAT_DIFF
827     if (need_diff_redraw)
828 	// will update later
829 	return;
830 #endif
831 
832     if (wp->w_folds.ga_len > 0)
833     {
834 	// Mark all folds from top to bot as maybe-small.
835 	(void)foldFind(&wp->w_folds, top, &fp);
836 	while (fp < (fold_T *)wp->w_folds.ga_data + wp->w_folds.ga_len
837 		&& fp->fd_top < bot)
838 	{
839 	    fp->fd_small = MAYBE;
840 	    ++fp;
841 	}
842     }
843 
844     if (foldmethodIsIndent(wp)
845 	    || foldmethodIsExpr(wp)
846 	    || foldmethodIsMarker(wp)
847 #ifdef FEAT_DIFF
848 	    || foldmethodIsDiff(wp)
849 #endif
850 	    || foldmethodIsSyntax(wp))
851     {
852 	int save_got_int = got_int;
853 
854 	// reset got_int here, otherwise it won't work
855 	got_int = FALSE;
856 	foldUpdateIEMS(wp, top, bot);
857 	got_int |= save_got_int;
858     }
859 }
860 
861 // foldUpdateAll() {{{2
862 /*
863  * Update all lines in a window for folding.
864  * Used when a fold setting changes or after reloading the buffer.
865  * The actual updating is postponed until fold info is used, to avoid doing
866  * every time a setting is changed or a syntax item is added.
867  */
868     void
foldUpdateAll(win_T * win)869 foldUpdateAll(win_T *win)
870 {
871     win->w_foldinvalid = TRUE;
872     redraw_win_later(win, NOT_VALID);
873 }
874 
875 // foldMoveTo() {{{2
876 /*
877  * If "updown" is FALSE: Move to the start or end of the fold.
878  * If "updown" is TRUE: move to fold at the same level.
879  * If not moved return FAIL.
880  */
881     int
foldMoveTo(int updown,int dir,long count)882 foldMoveTo(
883     int		updown,
884     int		dir,	    // FORWARD or BACKWARD
885     long	count)
886 {
887     long	n;
888     int		retval = FAIL;
889     linenr_T	lnum_off;
890     linenr_T	lnum_found;
891     linenr_T	lnum;
892     int		use_level;
893     int		maybe_small;
894     garray_T	*gap;
895     fold_T	*fp;
896     int		level;
897     int		last;
898 
899     checkupdate(curwin);
900 
901     // Repeat "count" times.
902     for (n = 0; n < count; ++n)
903     {
904 	// Find nested folds.  Stop when a fold is closed.  The deepest fold
905 	// that moves the cursor is used.
906 	lnum_off = 0;
907 	gap = &curwin->w_folds;
908 	if (gap->ga_len == 0)
909 	    break;
910 	use_level = FALSE;
911 	maybe_small = FALSE;
912 	lnum_found = curwin->w_cursor.lnum;
913 	level = 0;
914 	last = FALSE;
915 	for (;;)
916 	{
917 	    if (!foldFind(gap, curwin->w_cursor.lnum - lnum_off, &fp))
918 	    {
919 		if (!updown || gap->ga_len == 0)
920 		    break;
921 
922 		// When moving up, consider a fold above the cursor; when
923 		// moving down consider a fold below the cursor.
924 		if (dir == FORWARD)
925 		{
926 		    if (fp - (fold_T *)gap->ga_data >= gap->ga_len)
927 			break;
928 		    --fp;
929 		}
930 		else
931 		{
932 		    if (fp == (fold_T *)gap->ga_data)
933 			break;
934 		}
935 		// don't look for contained folds, they will always move
936 		// the cursor too far.
937 		last = TRUE;
938 	    }
939 
940 	    if (!last)
941 	    {
942 		// Check if this fold is closed.
943 		if (check_closed(curwin, fp, &use_level, level,
944 						      &maybe_small, lnum_off))
945 		    last = TRUE;
946 
947 		// "[z" and "]z" stop at closed fold
948 		if (last && !updown)
949 		    break;
950 	    }
951 
952 	    if (updown)
953 	    {
954 		if (dir == FORWARD)
955 		{
956 		    // to start of next fold if there is one
957 		    if (fp + 1 - (fold_T *)gap->ga_data < gap->ga_len)
958 		    {
959 			lnum = fp[1].fd_top + lnum_off;
960 			if (lnum > curwin->w_cursor.lnum)
961 			    lnum_found = lnum;
962 		    }
963 		}
964 		else
965 		{
966 		    // to end of previous fold if there is one
967 		    if (fp > (fold_T *)gap->ga_data)
968 		    {
969 			lnum = fp[-1].fd_top + lnum_off + fp[-1].fd_len - 1;
970 			if (lnum < curwin->w_cursor.lnum)
971 			    lnum_found = lnum;
972 		    }
973 		}
974 	    }
975 	    else
976 	    {
977 		// Open fold found, set cursor to its start/end and then check
978 		// nested folds.
979 		if (dir == FORWARD)
980 		{
981 		    lnum = fp->fd_top + lnum_off + fp->fd_len - 1;
982 		    if (lnum > curwin->w_cursor.lnum)
983 			lnum_found = lnum;
984 		}
985 		else
986 		{
987 		    lnum = fp->fd_top + lnum_off;
988 		    if (lnum < curwin->w_cursor.lnum)
989 			lnum_found = lnum;
990 		}
991 	    }
992 
993 	    if (last)
994 		break;
995 
996 	    // Check nested folds (if any).
997 	    gap = &fp->fd_nested;
998 	    lnum_off += fp->fd_top;
999 	    ++level;
1000 	}
1001 	if (lnum_found != curwin->w_cursor.lnum)
1002 	{
1003 	    if (retval == FAIL)
1004 		setpcmark();
1005 	    curwin->w_cursor.lnum = lnum_found;
1006 	    curwin->w_cursor.col = 0;
1007 	    retval = OK;
1008 	}
1009 	else
1010 	    break;
1011     }
1012 
1013     return retval;
1014 }
1015 
1016 // foldInitWin() {{{2
1017 /*
1018  * Init the fold info in a new window.
1019  */
1020     void
foldInitWin(win_T * new_win)1021 foldInitWin(win_T *new_win)
1022 {
1023     ga_init2(&new_win->w_folds, (int)sizeof(fold_T), 10);
1024 }
1025 
1026 // find_wl_entry() {{{2
1027 /*
1028  * Find an entry in the win->w_lines[] array for buffer line "lnum".
1029  * Only valid entries are considered (for entries where wl_valid is FALSE the
1030  * line number can be wrong).
1031  * Returns index of entry or -1 if not found.
1032  */
1033     int
find_wl_entry(win_T * win,linenr_T lnum)1034 find_wl_entry(win_T *win, linenr_T lnum)
1035 {
1036     int		i;
1037 
1038     for (i = 0; i < win->w_lines_valid; ++i)
1039 	if (win->w_lines[i].wl_valid)
1040 	{
1041 	    if (lnum < win->w_lines[i].wl_lnum)
1042 		return -1;
1043 	    if (lnum <= win->w_lines[i].wl_lastlnum)
1044 		return i;
1045 	}
1046     return -1;
1047 }
1048 
1049 // foldAdjustVisual() {{{2
1050 /*
1051  * Adjust the Visual area to include any fold at the start or end completely.
1052  */
1053     void
foldAdjustVisual(void)1054 foldAdjustVisual(void)
1055 {
1056     pos_T	*start, *end;
1057     char_u	*ptr;
1058 
1059     if (!VIsual_active || !hasAnyFolding(curwin))
1060 	return;
1061 
1062     if (LTOREQ_POS(VIsual, curwin->w_cursor))
1063     {
1064 	start = &VIsual;
1065 	end = &curwin->w_cursor;
1066     }
1067     else
1068     {
1069 	start = &curwin->w_cursor;
1070 	end = &VIsual;
1071     }
1072     if (hasFolding(start->lnum, &start->lnum, NULL))
1073 	start->col = 0;
1074     if (hasFolding(end->lnum, NULL, &end->lnum))
1075     {
1076 	ptr = ml_get(end->lnum);
1077 	end->col = (colnr_T)STRLEN(ptr);
1078 	if (end->col > 0 && *p_sel == 'o')
1079 	    --end->col;
1080 	// prevent cursor from moving on the trail byte
1081 	if (has_mbyte)
1082 	    mb_adjust_cursor();
1083     }
1084 }
1085 
1086 // cursor_foldstart() {{{2
1087 /*
1088  * Move the cursor to the first line of a closed fold.
1089  */
1090     void
foldAdjustCursor(void)1091 foldAdjustCursor(void)
1092 {
1093     (void)hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL);
1094 }
1095 
1096 // Internal functions for "fold_T" {{{1
1097 // cloneFoldGrowArray() {{{2
1098 /*
1099  * Will "clone" (i.e deep copy) a garray_T of folds.
1100  *
1101  * Return FAIL if the operation cannot be completed, otherwise OK.
1102  */
1103     void
cloneFoldGrowArray(garray_T * from,garray_T * to)1104 cloneFoldGrowArray(garray_T *from, garray_T *to)
1105 {
1106     int		i;
1107     fold_T	*from_p;
1108     fold_T	*to_p;
1109 
1110     ga_init2(to, from->ga_itemsize, from->ga_growsize);
1111     if (from->ga_len == 0 || ga_grow(to, from->ga_len) == FAIL)
1112 	return;
1113 
1114     from_p = (fold_T *)from->ga_data;
1115     to_p = (fold_T *)to->ga_data;
1116 
1117     for (i = 0; i < from->ga_len; i++)
1118     {
1119 	to_p->fd_top = from_p->fd_top;
1120 	to_p->fd_len = from_p->fd_len;
1121 	to_p->fd_flags = from_p->fd_flags;
1122 	to_p->fd_small = from_p->fd_small;
1123 	cloneFoldGrowArray(&from_p->fd_nested, &to_p->fd_nested);
1124 	++to->ga_len;
1125 	++from_p;
1126 	++to_p;
1127     }
1128 }
1129 
1130 // foldFind() {{{2
1131 /*
1132  * Search for line "lnum" in folds of growarray "gap".
1133  * Set *fpp to the fold struct for the fold that contains "lnum" or
1134  * the first fold below it (careful: it can be beyond the end of the array!).
1135  * Returns FALSE when there is no fold that contains "lnum".
1136  */
1137     static int
foldFind(garray_T * gap,linenr_T lnum,fold_T ** fpp)1138 foldFind(garray_T *gap, linenr_T lnum, fold_T **fpp)
1139 {
1140     linenr_T	low, high;
1141     fold_T	*fp;
1142     int		i;
1143 
1144     if (gap->ga_len == 0)
1145     {
1146 	*fpp = NULL;
1147 	return FALSE;
1148     }
1149 
1150     /*
1151      * Perform a binary search.
1152      * "low" is lowest index of possible match.
1153      * "high" is highest index of possible match.
1154      */
1155     fp = (fold_T *)gap->ga_data;
1156     low = 0;
1157     high = gap->ga_len - 1;
1158     while (low <= high)
1159     {
1160 	i = (low + high) / 2;
1161 	if (fp[i].fd_top > lnum)
1162 	    // fold below lnum, adjust high
1163 	    high = i - 1;
1164 	else if (fp[i].fd_top + fp[i].fd_len <= lnum)
1165 	    // fold above lnum, adjust low
1166 	    low = i + 1;
1167 	else
1168 	{
1169 	    // lnum is inside this fold
1170 	    *fpp = fp + i;
1171 	    return TRUE;
1172 	}
1173     }
1174     *fpp = fp + low;
1175     return FALSE;
1176 }
1177 
1178 // foldLevelWin() {{{2
1179 /*
1180  * Return fold level at line number "lnum" in window "wp".
1181  */
1182     static int
foldLevelWin(win_T * wp,linenr_T lnum)1183 foldLevelWin(win_T *wp, linenr_T lnum)
1184 {
1185     fold_T	*fp;
1186     linenr_T	lnum_rel = lnum;
1187     int		level =  0;
1188     garray_T	*gap;
1189 
1190     // Recursively search for a fold that contains "lnum".
1191     gap = &wp->w_folds;
1192     for (;;)
1193     {
1194 	if (!foldFind(gap, lnum_rel, &fp))
1195 	    break;
1196 	// Check nested folds.  Line number is relative to containing fold.
1197 	gap = &fp->fd_nested;
1198 	lnum_rel -= fp->fd_top;
1199 	++level;
1200     }
1201 
1202     return level;
1203 }
1204 
1205 // checkupdate() {{{2
1206 /*
1207  * Check if the folds in window "wp" are invalid and update them if needed.
1208  */
1209     static void
checkupdate(win_T * wp)1210 checkupdate(win_T *wp)
1211 {
1212     if (wp->w_foldinvalid)
1213     {
1214 	foldUpdate(wp, (linenr_T)1, (linenr_T)MAXLNUM); // will update all
1215 	wp->w_foldinvalid = FALSE;
1216     }
1217 }
1218 
1219 // setFoldRepeat() {{{2
1220 /*
1221  * Open or close fold for current window at line "lnum".
1222  * Repeat "count" times.
1223  */
1224     static void
setFoldRepeat(linenr_T lnum,long count,int do_open)1225 setFoldRepeat(linenr_T lnum, long count, int do_open)
1226 {
1227     int		done;
1228     long	n;
1229 
1230     for (n = 0; n < count; ++n)
1231     {
1232 	done = DONE_NOTHING;
1233 	(void)setManualFold(lnum, do_open, FALSE, &done);
1234 	if (!(done & DONE_ACTION))
1235 	{
1236 	    // Only give an error message when no fold could be opened.
1237 	    if (n == 0 && !(done & DONE_FOLD))
1238 		emsg(_(e_nofold));
1239 	    break;
1240 	}
1241     }
1242 }
1243 
1244 // setManualFold() {{{2
1245 /*
1246  * Open or close the fold in the current window which contains "lnum".
1247  * Also does this for other windows in diff mode when needed.
1248  */
1249     static linenr_T
setManualFold(linenr_T lnum,int opening,int recurse,int * donep)1250 setManualFold(
1251     linenr_T	lnum,
1252     int		opening,    // TRUE when opening, FALSE when closing
1253     int		recurse,    // TRUE when closing/opening recursive
1254     int		*donep)
1255 {
1256 #ifdef FEAT_DIFF
1257     if (foldmethodIsDiff(curwin) && curwin->w_p_scb)
1258     {
1259 	win_T	    *wp;
1260 	linenr_T    dlnum;
1261 
1262 	/*
1263 	 * Do the same operation in other windows in diff mode.  Calculate the
1264 	 * line number from the diffs.
1265 	 */
1266 	FOR_ALL_WINDOWS(wp)
1267 	{
1268 	    if (wp != curwin && foldmethodIsDiff(wp) && wp->w_p_scb)
1269 	    {
1270 		dlnum = diff_lnum_win(curwin->w_cursor.lnum, wp);
1271 		if (dlnum != 0)
1272 		    (void)setManualFoldWin(wp, dlnum, opening, recurse, NULL);
1273 	    }
1274 	}
1275     }
1276 #endif
1277 
1278     return setManualFoldWin(curwin, lnum, opening, recurse, donep);
1279 }
1280 
1281 // setManualFoldWin() {{{2
1282 /*
1283  * Open or close the fold in window "wp" which contains "lnum".
1284  * "donep", when not NULL, points to flag that is set to DONE_FOLD when some
1285  * fold was found and to DONE_ACTION when some fold was opened or closed.
1286  * When "donep" is NULL give an error message when no fold was found for
1287  * "lnum", but only if "wp" is "curwin".
1288  * Return the line number of the next line that could be closed.
1289  * It's only valid when "opening" is TRUE!
1290  */
1291     static linenr_T
setManualFoldWin(win_T * wp,linenr_T lnum,int opening,int recurse,int * donep)1292 setManualFoldWin(
1293     win_T	*wp,
1294     linenr_T	lnum,
1295     int		opening,    // TRUE when opening, FALSE when closing
1296     int		recurse,    // TRUE when closing/opening recursive
1297     int		*donep)
1298 {
1299     fold_T	*fp;
1300     fold_T	*fp2;
1301     fold_T	*found = NULL;
1302     int		j;
1303     int		level = 0;
1304     int		use_level = FALSE;
1305     int		found_fold = FALSE;
1306     garray_T	*gap;
1307     linenr_T	next = MAXLNUM;
1308     linenr_T	off = 0;
1309     int		done = 0;
1310 
1311     checkupdate(wp);
1312 
1313     /*
1314      * Find the fold, open or close it.
1315      */
1316     gap = &wp->w_folds;
1317     for (;;)
1318     {
1319 	if (!foldFind(gap, lnum, &fp))
1320 	{
1321 	    // If there is a following fold, continue there next time.
1322 	    if (fp != NULL && fp < (fold_T *)gap->ga_data + gap->ga_len)
1323 		next = fp->fd_top + off;
1324 	    break;
1325 	}
1326 
1327 	// lnum is inside this fold
1328 	found_fold = TRUE;
1329 
1330 	// If there is a following fold, continue there next time.
1331 	if (fp + 1 < (fold_T *)gap->ga_data + gap->ga_len)
1332 	    next = fp[1].fd_top + off;
1333 
1334 	// Change from level-dependent folding to manual.
1335 	if (use_level || fp->fd_flags == FD_LEVEL)
1336 	{
1337 	    use_level = TRUE;
1338 	    if (level >= wp->w_p_fdl)
1339 		fp->fd_flags = FD_CLOSED;
1340 	    else
1341 		fp->fd_flags = FD_OPEN;
1342 	    fp2 = (fold_T *)fp->fd_nested.ga_data;
1343 	    for (j = 0; j < fp->fd_nested.ga_len; ++j)
1344 		fp2[j].fd_flags = FD_LEVEL;
1345 	}
1346 
1347 	// Simple case: Close recursively means closing the fold.
1348 	if (!opening && recurse)
1349 	{
1350 	    if (fp->fd_flags != FD_CLOSED)
1351 	    {
1352 		done |= DONE_ACTION;
1353 		fp->fd_flags = FD_CLOSED;
1354 	    }
1355 	}
1356 	else if (fp->fd_flags == FD_CLOSED)
1357 	{
1358 	    // When opening, open topmost closed fold.
1359 	    if (opening)
1360 	    {
1361 		fp->fd_flags = FD_OPEN;
1362 		done |= DONE_ACTION;
1363 		if (recurse)
1364 		    foldOpenNested(fp);
1365 	    }
1366 	    break;
1367 	}
1368 
1369 	// fold is open, check nested folds
1370 	found = fp;
1371 	gap = &fp->fd_nested;
1372 	lnum -= fp->fd_top;
1373 	off += fp->fd_top;
1374 	++level;
1375     }
1376     if (found_fold)
1377     {
1378 	// When closing and not recurse, close deepest open fold.
1379 	if (!opening && found != NULL)
1380 	{
1381 	    found->fd_flags = FD_CLOSED;
1382 	    done |= DONE_ACTION;
1383 	}
1384 	wp->w_fold_manual = TRUE;
1385 	if (done & DONE_ACTION)
1386 	    changed_window_setting_win(wp);
1387 	done |= DONE_FOLD;
1388     }
1389     else if (donep == NULL && wp == curwin)
1390 	emsg(_(e_nofold));
1391 
1392     if (donep != NULL)
1393 	*donep |= done;
1394 
1395     return next;
1396 }
1397 
1398 // foldOpenNested() {{{2
1399 /*
1400  * Open all nested folds in fold "fpr" recursively.
1401  */
1402     static void
foldOpenNested(fold_T * fpr)1403 foldOpenNested(fold_T *fpr)
1404 {
1405     int		i;
1406     fold_T	*fp;
1407 
1408     fp = (fold_T *)fpr->fd_nested.ga_data;
1409     for (i = 0; i < fpr->fd_nested.ga_len; ++i)
1410     {
1411 	foldOpenNested(&fp[i]);
1412 	fp[i].fd_flags = FD_OPEN;
1413     }
1414 }
1415 
1416 // deleteFoldEntry() {{{2
1417 /*
1418  * Delete fold "idx" from growarray "gap".
1419  * When "recursive" is TRUE also delete all the folds contained in it.
1420  * When "recursive" is FALSE contained folds are moved one level up.
1421  */
1422     static void
deleteFoldEntry(garray_T * gap,int idx,int recursive)1423 deleteFoldEntry(garray_T *gap, int idx, int recursive)
1424 {
1425     fold_T	*fp;
1426     int		i;
1427     long	moved;
1428     fold_T	*nfp;
1429 
1430     fp = (fold_T *)gap->ga_data + idx;
1431     if (recursive || fp->fd_nested.ga_len == 0)
1432     {
1433 	// recursively delete the contained folds
1434 	deleteFoldRecurse(&fp->fd_nested);
1435 	--gap->ga_len;
1436 	if (idx < gap->ga_len)
1437 	    mch_memmove(fp, fp + 1, sizeof(fold_T) * (gap->ga_len - idx));
1438     }
1439     else
1440     {
1441 	// Move nested folds one level up, to overwrite the fold that is
1442 	// deleted.
1443 	moved = fp->fd_nested.ga_len;
1444 	if (ga_grow(gap, (int)(moved - 1)) == OK)
1445 	{
1446 	    // Get "fp" again, the array may have been reallocated.
1447 	    fp = (fold_T *)gap->ga_data + idx;
1448 
1449 	    // adjust fd_top and fd_flags for the moved folds
1450 	    nfp = (fold_T *)fp->fd_nested.ga_data;
1451 	    for (i = 0; i < moved; ++i)
1452 	    {
1453 		nfp[i].fd_top += fp->fd_top;
1454 		if (fp->fd_flags == FD_LEVEL)
1455 		    nfp[i].fd_flags = FD_LEVEL;
1456 		if (fp->fd_small == MAYBE)
1457 		    nfp[i].fd_small = MAYBE;
1458 	    }
1459 
1460 	    // move the existing folds down to make room
1461 	    if (idx + 1 < gap->ga_len)
1462 		mch_memmove(fp + moved, fp + 1,
1463 				  sizeof(fold_T) * (gap->ga_len - (idx + 1)));
1464 	    // move the contained folds one level up
1465 	    mch_memmove(fp, nfp, (size_t)(sizeof(fold_T) * moved));
1466 	    vim_free(nfp);
1467 	    gap->ga_len += moved - 1;
1468 	}
1469     }
1470 }
1471 
1472 // deleteFoldRecurse() {{{2
1473 /*
1474  * Delete nested folds in a fold.
1475  */
1476     void
deleteFoldRecurse(garray_T * gap)1477 deleteFoldRecurse(garray_T *gap)
1478 {
1479     int		i;
1480 
1481     for (i = 0; i < gap->ga_len; ++i)
1482 	deleteFoldRecurse(&(((fold_T *)(gap->ga_data))[i].fd_nested));
1483     ga_clear(gap);
1484 }
1485 
1486 // foldMarkAdjust() {{{2
1487 /*
1488  * Update line numbers of folds for inserted/deleted lines.
1489  */
1490     void
foldMarkAdjust(win_T * wp,linenr_T line1,linenr_T line2,long amount,long amount_after)1491 foldMarkAdjust(
1492     win_T	*wp,
1493     linenr_T	line1,
1494     linenr_T	line2,
1495     long	amount,
1496     long	amount_after)
1497 {
1498     // If deleting marks from line1 to line2, but not deleting all those
1499     // lines, set line2 so that only deleted lines have their folds removed.
1500     if (amount == MAXLNUM && line2 >= line1 && line2 - line1 >= -amount_after)
1501 	line2 = line1 - amount_after - 1;
1502     // If appending a line in Insert mode, it should be included in the fold
1503     // just above the line.
1504     if ((State & INSERT) && amount == (linenr_T)1 && line2 == MAXLNUM)
1505 	--line1;
1506     foldMarkAdjustRecurse(&wp->w_folds, line1, line2, amount, amount_after);
1507 }
1508 
1509 // foldMarkAdjustRecurse() {{{2
1510     static void
foldMarkAdjustRecurse(garray_T * gap,linenr_T line1,linenr_T line2,long amount,long amount_after)1511 foldMarkAdjustRecurse(
1512     garray_T	*gap,
1513     linenr_T	line1,
1514     linenr_T	line2,
1515     long	amount,
1516     long	amount_after)
1517 {
1518     fold_T	*fp;
1519     int		i;
1520     linenr_T	last;
1521     linenr_T	top;
1522 
1523     if (gap->ga_len == 0)
1524 	return;
1525 
1526     // In Insert mode an inserted line at the top of a fold is considered part
1527     // of the fold, otherwise it isn't.
1528     if ((State & INSERT) && amount == (linenr_T)1 && line2 == MAXLNUM)
1529 	top = line1 + 1;
1530     else
1531 	top = line1;
1532 
1533     // Find the fold containing or just below "line1".
1534     (void)foldFind(gap, line1, &fp);
1535 
1536     /*
1537      * Adjust all folds below "line1" that are affected.
1538      */
1539     for (i = (int)(fp - (fold_T *)gap->ga_data); i < gap->ga_len; ++i, ++fp)
1540     {
1541 	/*
1542 	 * Check for these situations:
1543 	 *	  1  2	3
1544 	 *	  1  2	3
1545 	 * line1     2	3  4  5
1546 	 *	     2	3  4  5
1547 	 *	     2	3  4  5
1548 	 * line2     2	3  4  5
1549 	 *		3     5  6
1550 	 *		3     5  6
1551 	 */
1552 
1553 	last = fp->fd_top + fp->fd_len - 1; // last line of fold
1554 
1555 	// 1. fold completely above line1: nothing to do
1556 	if (last < line1)
1557 	    continue;
1558 
1559 	// 6. fold below line2: only adjust for amount_after
1560 	if (fp->fd_top > line2)
1561 	{
1562 	    if (amount_after == 0)
1563 		break;
1564 	    fp->fd_top += amount_after;
1565 	}
1566 	else
1567 	{
1568 	    if (fp->fd_top >= top && last <= line2)
1569 	    {
1570 		// 4. fold completely contained in range
1571 		if (amount == MAXLNUM)
1572 		{
1573 		    // Deleting lines: delete the fold completely
1574 		    deleteFoldEntry(gap, i, TRUE);
1575 		    --i;    // adjust index for deletion
1576 		    --fp;
1577 		}
1578 		else
1579 		    fp->fd_top += amount;
1580 	    }
1581 	    else
1582 	    {
1583 		if (fp->fd_top < top)
1584 		{
1585 		    // 2 or 3: need to correct nested folds too
1586 		    foldMarkAdjustRecurse(&fp->fd_nested, line1 - fp->fd_top,
1587 				  line2 - fp->fd_top, amount, amount_after);
1588 		    if (last <= line2)
1589 		    {
1590 			// 2. fold contains line1, line2 is below fold
1591 			if (amount == MAXLNUM)
1592 			    fp->fd_len = line1 - fp->fd_top;
1593 			else
1594 			    fp->fd_len += amount;
1595 		    }
1596 		    else
1597 		    {
1598 			// 3. fold contains line1 and line2
1599 			fp->fd_len += amount_after;
1600 		    }
1601 		}
1602 		else
1603 		{
1604 		    // 5. fold is below line1 and contains line2; need to
1605 		    // correct nested folds too
1606 		    if (amount == MAXLNUM)
1607 		    {
1608 			foldMarkAdjustRecurse(&fp->fd_nested,
1609 				  line1 - fp->fd_top,
1610 				  line2 - fp->fd_top,
1611 				  amount,
1612 				  amount_after + (fp->fd_top - top));
1613 			fp->fd_len -= line2 - fp->fd_top + 1;
1614 			fp->fd_top = line1;
1615 		    }
1616 		    else
1617 		    {
1618 			foldMarkAdjustRecurse(&fp->fd_nested,
1619 				  line1 - fp->fd_top,
1620 				  line2 - fp->fd_top,
1621 				  amount,
1622 				  amount_after - amount);
1623 			fp->fd_len += amount_after - amount;
1624 			fp->fd_top += amount;
1625 		    }
1626 		}
1627 	    }
1628 	}
1629     }
1630 }
1631 
1632 // getDeepestNesting() {{{2
1633 /*
1634  * Get the lowest 'foldlevel' value that makes the deepest nested fold in the
1635  * current window open.
1636  */
1637     int
getDeepestNesting(void)1638 getDeepestNesting(void)
1639 {
1640     checkupdate(curwin);
1641     return getDeepestNestingRecurse(&curwin->w_folds);
1642 }
1643 
1644     static int
getDeepestNestingRecurse(garray_T * gap)1645 getDeepestNestingRecurse(garray_T *gap)
1646 {
1647     int		i;
1648     int		level;
1649     int		maxlevel = 0;
1650     fold_T	*fp;
1651 
1652     fp = (fold_T *)gap->ga_data;
1653     for (i = 0; i < gap->ga_len; ++i)
1654     {
1655 	level = getDeepestNestingRecurse(&fp[i].fd_nested) + 1;
1656 	if (level > maxlevel)
1657 	    maxlevel = level;
1658     }
1659 
1660     return maxlevel;
1661 }
1662 
1663 // check_closed() {{{2
1664 /*
1665  * Check if a fold is closed and update the info needed to check nested folds.
1666  */
1667     static int
check_closed(win_T * win,fold_T * fp,int * use_levelp,int level,int * maybe_smallp,linenr_T lnum_off)1668 check_closed(
1669     win_T	*win,
1670     fold_T	*fp,
1671     int		*use_levelp,	    // TRUE: outer fold had FD_LEVEL
1672     int		level,		    // folding depth
1673     int		*maybe_smallp,	    // TRUE: outer this had fd_small == MAYBE
1674     linenr_T	lnum_off)	    // line number offset for fp->fd_top
1675 {
1676     int		closed = FALSE;
1677 
1678     // Check if this fold is closed.  If the flag is FD_LEVEL this
1679     // fold and all folds it contains depend on 'foldlevel'.
1680     if (*use_levelp || fp->fd_flags == FD_LEVEL)
1681     {
1682 	*use_levelp = TRUE;
1683 	if (level >= win->w_p_fdl)
1684 	    closed = TRUE;
1685     }
1686     else if (fp->fd_flags == FD_CLOSED)
1687 	closed = TRUE;
1688 
1689     // Small fold isn't closed anyway.
1690     if (fp->fd_small == MAYBE)
1691 	*maybe_smallp = TRUE;
1692     if (closed)
1693     {
1694 	if (*maybe_smallp)
1695 	    fp->fd_small = MAYBE;
1696 	checkSmall(win, fp, lnum_off);
1697 	if (fp->fd_small == TRUE)
1698 	    closed = FALSE;
1699     }
1700     return closed;
1701 }
1702 
1703 // checkSmall() {{{2
1704 /*
1705  * Update fd_small field of fold "fp".
1706  */
1707     static void
checkSmall(win_T * wp,fold_T * fp,linenr_T lnum_off)1708 checkSmall(
1709     win_T	*wp,
1710     fold_T	*fp,
1711     linenr_T	lnum_off)	// offset for fp->fd_top
1712 {
1713     int		count;
1714     int		n;
1715 
1716     if (fp->fd_small == MAYBE)
1717     {
1718 	// Mark any nested folds to maybe-small
1719 	setSmallMaybe(&fp->fd_nested);
1720 
1721 	if (fp->fd_len > curwin->w_p_fml)
1722 	    fp->fd_small = FALSE;
1723 	else
1724 	{
1725 	    count = 0;
1726 	    for (n = 0; n < fp->fd_len; ++n)
1727 	    {
1728 		count += plines_win_nofold(wp, fp->fd_top + lnum_off + n);
1729 		if (count > curwin->w_p_fml)
1730 		{
1731 		    fp->fd_small = FALSE;
1732 		    return;
1733 		}
1734 	    }
1735 	    fp->fd_small = TRUE;
1736 	}
1737     }
1738 }
1739 
1740 // setSmallMaybe() {{{2
1741 /*
1742  * Set small flags in "gap" to MAYBE.
1743  */
1744     static void
setSmallMaybe(garray_T * gap)1745 setSmallMaybe(garray_T *gap)
1746 {
1747     int		i;
1748     fold_T	*fp;
1749 
1750     fp = (fold_T *)gap->ga_data;
1751     for (i = 0; i < gap->ga_len; ++i)
1752 	fp[i].fd_small = MAYBE;
1753 }
1754 
1755 // foldCreateMarkers() {{{2
1756 /*
1757  * Create a fold from line "start" to line "end" (inclusive) in the current
1758  * window by adding markers.
1759  */
1760     static void
foldCreateMarkers(linenr_T start,linenr_T end)1761 foldCreateMarkers(linenr_T start, linenr_T end)
1762 {
1763     if (!curbuf->b_p_ma)
1764     {
1765 	emsg(_(e_cannot_make_changes_modifiable_is_off));
1766 	return;
1767     }
1768     parseMarker(curwin);
1769 
1770     foldAddMarker(start, curwin->w_p_fmr, foldstartmarkerlen);
1771     foldAddMarker(end, foldendmarker, foldendmarkerlen);
1772 
1773     // Update both changes here, to avoid all folds after the start are
1774     // changed when the start marker is inserted and the end isn't.
1775     changed_lines(start, (colnr_T)0, end, 0L);
1776 }
1777 
1778 // foldAddMarker() {{{2
1779 /*
1780  * Add "marker[markerlen]" in 'commentstring' to line "lnum".
1781  */
1782     static void
foldAddMarker(linenr_T lnum,char_u * marker,int markerlen)1783 foldAddMarker(linenr_T lnum, char_u *marker, int markerlen)
1784 {
1785     char_u	*cms = curbuf->b_p_cms;
1786     char_u	*line;
1787     int		line_len;
1788     char_u	*newline;
1789     char_u	*p = (char_u *)strstr((char *)curbuf->b_p_cms, "%s");
1790     int		line_is_comment = FALSE;
1791 
1792     // Allocate a new line: old-line + 'cms'-start + marker + 'cms'-end
1793     line = ml_get(lnum);
1794     line_len = (int)STRLEN(line);
1795 
1796     if (u_save(lnum - 1, lnum + 1) == OK)
1797     {
1798 	// Check if the line ends with an unclosed comment
1799 	(void)skip_comment(line, FALSE, FALSE, &line_is_comment);
1800 	newline = alloc(line_len + markerlen + STRLEN(cms) + 1);
1801 	if (newline == NULL)
1802 	    return;
1803 	STRCPY(newline, line);
1804 	// Append the marker to the end of the line
1805 	if (p == NULL || line_is_comment)
1806 	    vim_strncpy(newline + line_len, marker, markerlen);
1807 	else
1808 	{
1809 	    STRCPY(newline + line_len, cms);
1810 	    STRNCPY(newline + line_len + (p - cms), marker, markerlen);
1811 	    STRCPY(newline + line_len + (p - cms) + markerlen, p + 2);
1812 	}
1813 
1814 	ml_replace(lnum, newline, FALSE);
1815     }
1816 }
1817 
1818 // deleteFoldMarkers() {{{2
1819 /*
1820  * Delete the markers for a fold, causing it to be deleted.
1821  */
1822     static void
deleteFoldMarkers(fold_T * fp,int recursive,linenr_T lnum_off)1823 deleteFoldMarkers(
1824     fold_T	*fp,
1825     int		recursive,
1826     linenr_T	lnum_off)	// offset for fp->fd_top
1827 {
1828     int		i;
1829 
1830     if (recursive)
1831 	for (i = 0; i < fp->fd_nested.ga_len; ++i)
1832 	    deleteFoldMarkers((fold_T *)fp->fd_nested.ga_data + i, TRUE,
1833 						       lnum_off + fp->fd_top);
1834     foldDelMarker(fp->fd_top + lnum_off, curwin->w_p_fmr, foldstartmarkerlen);
1835     foldDelMarker(fp->fd_top + lnum_off + fp->fd_len - 1,
1836 					     foldendmarker, foldendmarkerlen);
1837 }
1838 
1839 // foldDelMarker() {{{2
1840 /*
1841  * Delete marker "marker[markerlen]" at the end of line "lnum".
1842  * Delete 'commentstring' if it matches.
1843  * If the marker is not found, there is no error message.  Could be a missing
1844  * close-marker.
1845  */
1846     static void
foldDelMarker(linenr_T lnum,char_u * marker,int markerlen)1847 foldDelMarker(linenr_T lnum, char_u *marker, int markerlen)
1848 {
1849     char_u	*line;
1850     char_u	*newline;
1851     char_u	*p;
1852     int		len;
1853     char_u	*cms = curbuf->b_p_cms;
1854     char_u	*cms2;
1855 
1856     // end marker may be missing and fold extends below the last line
1857     if (lnum > curbuf->b_ml.ml_line_count)
1858 	return;
1859     line = ml_get(lnum);
1860     for (p = line; *p != NUL; ++p)
1861 	if (STRNCMP(p, marker, markerlen) == 0)
1862 	{
1863 	    // Found the marker, include a digit if it's there.
1864 	    len = markerlen;
1865 	    if (VIM_ISDIGIT(p[len]))
1866 		++len;
1867 	    if (*cms != NUL)
1868 	    {
1869 		// Also delete 'commentstring' if it matches.
1870 		cms2 = (char_u *)strstr((char *)cms, "%s");
1871 		if (p - line >= cms2 - cms
1872 			&& STRNCMP(p - (cms2 - cms), cms, cms2 - cms) == 0
1873 			&& STRNCMP(p + len, cms2 + 2, STRLEN(cms2 + 2)) == 0)
1874 		{
1875 		    p -= cms2 - cms;
1876 		    len += (int)STRLEN(cms) - 2;
1877 		}
1878 	    }
1879 	    if (u_save(lnum - 1, lnum + 1) == OK)
1880 	    {
1881 		// Make new line: text-before-marker + text-after-marker
1882 		newline = alloc(STRLEN(line) - len + 1);
1883 		if (newline != NULL)
1884 		{
1885 		    STRNCPY(newline, line, p - line);
1886 		    STRCPY(newline + (p - line), p + len);
1887 		    ml_replace(lnum, newline, FALSE);
1888 		}
1889 	    }
1890 	    break;
1891 	}
1892 }
1893 
1894 // get_foldtext() {{{2
1895 /*
1896  * Return the text for a closed fold at line "lnum", with last line "lnume".
1897  * When 'foldtext' isn't set puts the result in "buf[FOLD_TEXT_LEN]".
1898  * Otherwise the result is in allocated memory.
1899  */
1900     char_u *
get_foldtext(win_T * wp,linenr_T lnum,linenr_T lnume,foldinfo_T * foldinfo,char_u * buf)1901 get_foldtext(
1902     win_T	*wp,
1903     linenr_T	lnum,
1904     linenr_T	lnume,
1905     foldinfo_T	*foldinfo,
1906     char_u	*buf)
1907 {
1908     char_u	*text = NULL;
1909 #ifdef FEAT_EVAL
1910      // an error occurred when evaluating 'fdt' setting
1911     static int	    got_fdt_error = FALSE;
1912     int		    save_did_emsg = did_emsg;
1913     static win_T    *last_wp = NULL;
1914     static linenr_T last_lnum = 0;
1915 
1916     if (last_wp != wp || last_wp == NULL
1917 					|| last_lnum > lnum || last_lnum == 0)
1918 	// window changed, try evaluating foldtext setting once again
1919 	got_fdt_error = FALSE;
1920 
1921     if (!got_fdt_error)
1922 	// a previous error should not abort evaluating 'foldexpr'
1923 	did_emsg = FALSE;
1924 
1925     if (*wp->w_p_fdt != NUL)
1926     {
1927 	char_u	dashes[MAX_LEVEL + 2];
1928 	win_T	*save_curwin;
1929 	int	level;
1930 	char_u	*p;
1931 
1932 	// Set "v:foldstart" and "v:foldend".
1933 	set_vim_var_nr(VV_FOLDSTART, lnum);
1934 	set_vim_var_nr(VV_FOLDEND, lnume);
1935 
1936 	// Set "v:folddashes" to a string of "level" dashes.
1937 	// Set "v:foldlevel" to "level".
1938 	level = foldinfo->fi_level;
1939 	if (level > (int)sizeof(dashes) - 1)
1940 	    level = (int)sizeof(dashes) - 1;
1941 	vim_memset(dashes, '-', (size_t)level);
1942 	dashes[level] = NUL;
1943 	set_vim_var_string(VV_FOLDDASHES, dashes, -1);
1944 	set_vim_var_nr(VV_FOLDLEVEL, (long)level);
1945 
1946 	// skip evaluating foldtext on errors
1947 	if (!got_fdt_error)
1948 	{
1949 	    save_curwin = curwin;
1950 	    curwin = wp;
1951 	    curbuf = wp->w_buffer;
1952 
1953 	    ++emsg_silent; // handle exceptions, but don't display errors
1954 	    text = eval_to_string_safe(wp->w_p_fdt,
1955 			 was_set_insecurely((char_u *)"foldtext", OPT_LOCAL));
1956 	    --emsg_silent;
1957 
1958 	    if (text == NULL || did_emsg)
1959 		got_fdt_error = TRUE;
1960 
1961 	    curwin = save_curwin;
1962 	    curbuf = curwin->w_buffer;
1963 	}
1964 	last_lnum = lnum;
1965 	last_wp   = wp;
1966 	set_vim_var_string(VV_FOLDDASHES, NULL, -1);
1967 
1968 	if (!did_emsg && save_did_emsg)
1969 	    did_emsg = save_did_emsg;
1970 
1971 	if (text != NULL)
1972 	{
1973 	    // Replace unprintable characters, if there are any.  But
1974 	    // replace a TAB with a space.
1975 	    for (p = text; *p != NUL; ++p)
1976 	    {
1977 		int	len;
1978 
1979 		if (has_mbyte && (len = (*mb_ptr2len)(p)) > 1)
1980 		{
1981 		    if (!vim_isprintc((*mb_ptr2char)(p)))
1982 			break;
1983 		    p += len - 1;
1984 		}
1985 		else
1986 		    if (*p == TAB)
1987 			*p = ' ';
1988 		    else if (ptr2cells(p) > 1)
1989 			break;
1990 	    }
1991 	    if (*p != NUL)
1992 	    {
1993 		p = transstr(text);
1994 		vim_free(text);
1995 		text = p;
1996 	    }
1997 	}
1998     }
1999     if (text == NULL)
2000 #endif
2001     {
2002 	long count = (long)(lnume - lnum + 1);
2003 
2004 	vim_snprintf((char *)buf, FOLD_TEXT_LEN,
2005 		     NGETTEXT("+--%3ld line folded ",
2006 					       "+--%3ld lines folded ", count),
2007 		     count);
2008 	text = buf;
2009     }
2010     return text;
2011 }
2012 
2013 // foldtext_cleanup() {{{2
2014 #ifdef FEAT_EVAL
2015 /*
2016  * Remove 'foldmarker' and 'commentstring' from "str" (in-place).
2017  */
2018     static void
foldtext_cleanup(char_u * str)2019 foldtext_cleanup(char_u *str)
2020 {
2021     char_u	*cms_start;	// first part or the whole comment
2022     int		cms_slen = 0;	// length of cms_start
2023     char_u	*cms_end;	// last part of the comment or NULL
2024     int		cms_elen = 0;	// length of cms_end
2025     char_u	*s;
2026     char_u	*p;
2027     int		len;
2028     int		did1 = FALSE;
2029     int		did2 = FALSE;
2030 
2031     // Ignore leading and trailing white space in 'commentstring'.
2032     cms_start = skipwhite(curbuf->b_p_cms);
2033     cms_slen = (int)STRLEN(cms_start);
2034     while (cms_slen > 0 && VIM_ISWHITE(cms_start[cms_slen - 1]))
2035 	--cms_slen;
2036 
2037     // locate "%s" in 'commentstring', use the part before and after it.
2038     cms_end = (char_u *)strstr((char *)cms_start, "%s");
2039     if (cms_end != NULL)
2040     {
2041 	cms_elen = cms_slen - (int)(cms_end - cms_start);
2042 	cms_slen = (int)(cms_end - cms_start);
2043 
2044 	// exclude white space before "%s"
2045 	while (cms_slen > 0 && VIM_ISWHITE(cms_start[cms_slen - 1]))
2046 	    --cms_slen;
2047 
2048 	// skip "%s" and white space after it
2049 	s = skipwhite(cms_end + 2);
2050 	cms_elen -= (int)(s - cms_end);
2051 	cms_end = s;
2052     }
2053     parseMarker(curwin);
2054 
2055     for (s = str; *s != NUL; )
2056     {
2057 	len = 0;
2058 	if (STRNCMP(s, curwin->w_p_fmr, foldstartmarkerlen) == 0)
2059 	    len = foldstartmarkerlen;
2060 	else if (STRNCMP(s, foldendmarker, foldendmarkerlen) == 0)
2061 	    len = foldendmarkerlen;
2062 	if (len > 0)
2063 	{
2064 	    if (VIM_ISDIGIT(s[len]))
2065 		++len;
2066 
2067 	    // May remove 'commentstring' start.  Useful when it's a double
2068 	    // quote and we already removed a double quote.
2069 	    for (p = s; p > str && VIM_ISWHITE(p[-1]); --p)
2070 		;
2071 	    if (p >= str + cms_slen
2072 			   && STRNCMP(p - cms_slen, cms_start, cms_slen) == 0)
2073 	    {
2074 		len += (int)(s - p) + cms_slen;
2075 		s = p - cms_slen;
2076 	    }
2077 	}
2078 	else if (cms_end != NULL)
2079 	{
2080 	    if (!did1 && cms_slen > 0 && STRNCMP(s, cms_start, cms_slen) == 0)
2081 	    {
2082 		len = cms_slen;
2083 		did1 = TRUE;
2084 	    }
2085 	    else if (!did2 && cms_elen > 0
2086 					&& STRNCMP(s, cms_end, cms_elen) == 0)
2087 	    {
2088 		len = cms_elen;
2089 		did2 = TRUE;
2090 	    }
2091 	}
2092 	if (len != 0)
2093 	{
2094 	    while (VIM_ISWHITE(s[len]))
2095 		++len;
2096 	    STRMOVE(s, s + len);
2097 	}
2098 	else
2099 	{
2100 	    MB_PTR_ADV(s);
2101 	}
2102     }
2103 }
2104 #endif
2105 
2106 // Folding by indent, expr, marker and syntax. {{{1
2107 // Define "fline_T", passed to get fold level for a line. {{{2
2108 typedef struct
2109 {
2110     win_T	*wp;		// window
2111     linenr_T	lnum;		// current line number
2112     linenr_T	off;		// offset between lnum and real line number
2113     linenr_T	lnum_save;	// line nr used by foldUpdateIEMSRecurse()
2114     int		lvl;		// current level (-1 for undefined)
2115     int		lvl_next;	// level used for next line
2116     int		start;		// number of folds that are forced to start at
2117 				// this line.
2118     int		end;		// level of fold that is forced to end below
2119 				// this line
2120     int		had_end;	// level of fold that is forced to end above
2121 				// this line (copy of "end" of prev. line)
2122 } fline_T;
2123 
2124 // Flag is set when redrawing is needed.
2125 static int fold_changed;
2126 
2127 // Function declarations. {{{2
2128 static linenr_T foldUpdateIEMSRecurse(garray_T *gap, int level, linenr_T startlnum, fline_T *flp, void (*getlevel)(fline_T *), linenr_T bot, int topflags);
2129 static int foldInsert(garray_T *gap, int i);
2130 static void foldSplit(garray_T *gap, int i, linenr_T top, linenr_T bot);
2131 static void foldRemove(garray_T *gap, linenr_T top, linenr_T bot);
2132 static void foldMerge(fold_T *fp1, garray_T *gap, fold_T *fp2);
2133 static void foldlevelIndent(fline_T *flp);
2134 #ifdef FEAT_DIFF
2135 static void foldlevelDiff(fline_T *flp);
2136 #endif
2137 static void foldlevelExpr(fline_T *flp);
2138 static void foldlevelMarker(fline_T *flp);
2139 static void foldlevelSyntax(fline_T *flp);
2140 
2141 // foldUpdateIEMS() {{{2
2142 /*
2143  * Update the folding for window "wp", at least from lines "top" to "bot".
2144  * Return TRUE if any folds did change.
2145  */
2146     static void
foldUpdateIEMS(win_T * wp,linenr_T top,linenr_T bot)2147 foldUpdateIEMS(win_T *wp, linenr_T top, linenr_T bot)
2148 {
2149     linenr_T	start;
2150     linenr_T	end;
2151     fline_T	fline;
2152     void	(*getlevel)(fline_T *);
2153     int		level;
2154     fold_T	*fp;
2155 
2156     // Avoid problems when being called recursively.
2157     if (invalid_top != (linenr_T)0)
2158 	return;
2159 
2160     if (wp->w_foldinvalid)
2161     {
2162 	// Need to update all folds.
2163 	top = 1;
2164 	bot = wp->w_buffer->b_ml.ml_line_count;
2165 	wp->w_foldinvalid = FALSE;
2166 
2167 	// Mark all folds a maybe-small.
2168 	setSmallMaybe(&wp->w_folds);
2169     }
2170 
2171 #ifdef FEAT_DIFF
2172     // add the context for "diff" folding
2173     if (foldmethodIsDiff(wp))
2174     {
2175 	if (top > diff_context)
2176 	    top -= diff_context;
2177 	else
2178 	    top = 1;
2179 	bot += diff_context;
2180     }
2181 #endif
2182 
2183     // When deleting lines at the end of the buffer "top" can be past the end
2184     // of the buffer.
2185     if (top > wp->w_buffer->b_ml.ml_line_count)
2186 	top = wp->w_buffer->b_ml.ml_line_count;
2187 
2188     fold_changed = FALSE;
2189     fline.wp = wp;
2190     fline.off = 0;
2191     fline.lvl = 0;
2192     fline.lvl_next = -1;
2193     fline.start = 0;
2194     fline.end = MAX_LEVEL + 1;
2195     fline.had_end = MAX_LEVEL + 1;
2196 
2197     invalid_top = top;
2198     invalid_bot = bot;
2199 
2200     if (foldmethodIsMarker(wp))
2201     {
2202 	getlevel = foldlevelMarker;
2203 
2204 	// Init marker variables to speed up foldlevelMarker().
2205 	parseMarker(wp);
2206 
2207 	// Need to get the level of the line above top, it is used if there is
2208 	// no marker at the top.
2209 	if (top > 1)
2210 	{
2211 	    // Get the fold level at top - 1.
2212 	    level = foldLevelWin(wp, top - 1);
2213 
2214 	    // The fold may end just above the top, check for that.
2215 	    fline.lnum = top - 1;
2216 	    fline.lvl = level;
2217 	    getlevel(&fline);
2218 
2219 	    // If a fold started here, we already had the level, if it stops
2220 	    // here, we need to use lvl_next.  Could also start and end a fold
2221 	    // in the same line.
2222 	    if (fline.lvl > level)
2223 		fline.lvl = level - (fline.lvl - fline.lvl_next);
2224 	    else
2225 		fline.lvl = fline.lvl_next;
2226 	}
2227 	fline.lnum = top;
2228 	getlevel(&fline);
2229     }
2230     else
2231     {
2232 	fline.lnum = top;
2233 	if (foldmethodIsExpr(wp))
2234 	{
2235 	    getlevel = foldlevelExpr;
2236 	    // start one line back, because a "<1" may indicate the end of a
2237 	    // fold in the topline
2238 	    if (top > 1)
2239 		--fline.lnum;
2240 	}
2241 	else if (foldmethodIsSyntax(wp))
2242 	    getlevel = foldlevelSyntax;
2243 #ifdef FEAT_DIFF
2244 	else if (foldmethodIsDiff(wp))
2245 	    getlevel = foldlevelDiff;
2246 #endif
2247 	else
2248 	    getlevel = foldlevelIndent;
2249 
2250 	// Backup to a line for which the fold level is defined.  Since it's
2251 	// always defined for line one, we will stop there.
2252 	fline.lvl = -1;
2253 	for ( ; !got_int; --fline.lnum)
2254 	{
2255 	    // Reset lvl_next each time, because it will be set to a value for
2256 	    // the next line, but we search backwards here.
2257 	    fline.lvl_next = -1;
2258 	    getlevel(&fline);
2259 	    if (fline.lvl >= 0)
2260 		break;
2261 	}
2262     }
2263 
2264     /*
2265      * If folding is defined by the syntax, it is possible that a change in
2266      * one line will cause all sub-folds of the current fold to change (e.g.,
2267      * closing a C-style comment can cause folds in the subsequent lines to
2268      * appear). To take that into account we should adjust the value of "bot"
2269      * to point to the end of the current fold:
2270      */
2271     if (foldlevelSyntax == getlevel)
2272     {
2273 	garray_T *gap = &wp->w_folds;
2274 	fold_T	 *fpn = NULL;
2275 	int	  current_fdl = 0;
2276 	linenr_T  fold_start_lnum = 0;
2277 	linenr_T  lnum_rel = fline.lnum;
2278 
2279 	while (current_fdl < fline.lvl)
2280 	{
2281 	    if (!foldFind(gap, lnum_rel, &fpn))
2282 		break;
2283 	    ++current_fdl;
2284 
2285 	    fold_start_lnum += fpn->fd_top;
2286 	    gap = &fpn->fd_nested;
2287 	    lnum_rel -= fpn->fd_top;
2288 	}
2289 	if (fpn != NULL && current_fdl == fline.lvl)
2290 	{
2291 	    linenr_T fold_end_lnum = fold_start_lnum + fpn->fd_len;
2292 
2293 	    if (fold_end_lnum > bot)
2294 		bot = fold_end_lnum;
2295 	}
2296     }
2297 
2298     start = fline.lnum;
2299     end = bot;
2300     // Do at least one line.
2301     if (start > end && end < wp->w_buffer->b_ml.ml_line_count)
2302 	end = start;
2303     while (!got_int)
2304     {
2305 	// Always stop at the end of the file ("end" can be past the end of
2306 	// the file).
2307 	if (fline.lnum > wp->w_buffer->b_ml.ml_line_count)
2308 	    break;
2309 	if (fline.lnum > end)
2310 	{
2311 	    // For "marker", "expr"  and "syntax"  methods: If a change caused
2312 	    // a fold to be removed, we need to continue at least until where
2313 	    // it ended.
2314 	    if (getlevel != foldlevelMarker
2315 		    && getlevel != foldlevelSyntax
2316 		    && getlevel != foldlevelExpr)
2317 		break;
2318 	    if ((start <= end
2319 			&& foldFind(&wp->w_folds, end, &fp)
2320 			&& fp->fd_top + fp->fd_len - 1 > end)
2321 		    || (fline.lvl == 0
2322 			&& foldFind(&wp->w_folds, fline.lnum, &fp)
2323 			&& fp->fd_top < fline.lnum))
2324 		end = fp->fd_top + fp->fd_len - 1;
2325 	    else if (getlevel == foldlevelSyntax
2326 		    && foldLevelWin(wp, fline.lnum) != fline.lvl)
2327 		// For "syntax" method: Compare the foldlevel that the syntax
2328 		// tells us to the foldlevel from the existing folds.  If they
2329 		// don't match continue updating folds.
2330 		end = fline.lnum;
2331 	    else
2332 		break;
2333 	}
2334 
2335 	// A level 1 fold starts at a line with foldlevel > 0.
2336 	if (fline.lvl > 0)
2337 	{
2338 	    invalid_top = fline.lnum;
2339 	    invalid_bot = end;
2340 	    end = foldUpdateIEMSRecurse(&wp->w_folds,
2341 				   1, start, &fline, getlevel, end, FD_LEVEL);
2342 	    start = fline.lnum;
2343 	}
2344 	else
2345 	{
2346 	    if (fline.lnum == wp->w_buffer->b_ml.ml_line_count)
2347 		break;
2348 	    ++fline.lnum;
2349 	    fline.lvl = fline.lvl_next;
2350 	    getlevel(&fline);
2351 	}
2352     }
2353 
2354     // There can't be any folds from start until end now.
2355     foldRemove(&wp->w_folds, start, end);
2356 
2357     // If some fold changed, need to redraw and position cursor.
2358     if (fold_changed && wp->w_p_fen)
2359 	changed_window_setting_win(wp);
2360 
2361     // If we updated folds past "bot", need to redraw more lines.  Don't do
2362     // this in other situations, the changed lines will be redrawn anyway and
2363     // this method can cause the whole window to be updated.
2364     if (end != bot)
2365     {
2366 	if (wp->w_redraw_top == 0 || wp->w_redraw_top > top)
2367 	    wp->w_redraw_top = top;
2368 	if (wp->w_redraw_bot < end)
2369 	    wp->w_redraw_bot = end;
2370     }
2371 
2372     invalid_top = (linenr_T)0;
2373 }
2374 
2375 // foldUpdateIEMSRecurse() {{{2
2376 /*
2377  * Update a fold that starts at "flp->lnum".  At this line there is always a
2378  * valid foldlevel, and its level >= "level".
2379  * "flp" is valid for "flp->lnum" when called and it's valid when returning.
2380  * "flp->lnum" is set to the lnum just below the fold, if it ends before
2381  * "bot", it's "bot" plus one if the fold continues and it's bigger when using
2382  * the marker method and a text change made following folds to change.
2383  * When returning, "flp->lnum_save" is the line number that was used to get
2384  * the level when the level at "flp->lnum" is invalid.
2385  * Remove any folds from "startlnum" up to here at this level.
2386  * Recursively update nested folds.
2387  * Below line "bot" there are no changes in the text.
2388  * "flp->lnum", "flp->lnum_save" and "bot" are relative to the start of the
2389  * outer fold.
2390  * "flp->off" is the offset to the real line number in the buffer.
2391  *
2392  * All this would be a lot simpler if all folds in the range would be deleted
2393  * and then created again.  But we would lose all information about the
2394  * folds, even when making changes that don't affect the folding (e.g. "vj~").
2395  *
2396  * Returns bot, which may have been increased for lines that also need to be
2397  * updated as a result of a detected change in the fold.
2398  */
2399     static linenr_T
foldUpdateIEMSRecurse(garray_T * gap,int level,linenr_T startlnum,fline_T * flp,void (* getlevel)(fline_T *),linenr_T bot,int topflags)2400 foldUpdateIEMSRecurse(
2401     garray_T	*gap,
2402     int		level,
2403     linenr_T	startlnum,
2404     fline_T	*flp,
2405     void	(*getlevel)(fline_T *),
2406     linenr_T	bot,
2407     int		topflags)	// flags used by containing fold
2408 {
2409     linenr_T	ll;
2410     fold_T	*fp = NULL;
2411     fold_T	*fp2;
2412     int		lvl = level;
2413     linenr_T	startlnum2 = startlnum;
2414     linenr_T	firstlnum = flp->lnum;	// first lnum we got
2415     int		i;
2416     int		finish = FALSE;
2417     linenr_T	linecount = flp->wp->w_buffer->b_ml.ml_line_count - flp->off;
2418     int		concat;
2419 
2420     /*
2421      * If using the marker method, the start line is not the start of a fold
2422      * at the level we're dealing with and the level is non-zero, we must use
2423      * the previous fold.  But ignore a fold that starts at or below
2424      * startlnum, it must be deleted.
2425      */
2426     if (getlevel == foldlevelMarker && flp->start <= flp->lvl - level
2427 							      && flp->lvl > 0)
2428     {
2429 	(void)foldFind(gap, startlnum - 1, &fp);
2430 	if (fp != NULL && (fp >= ((fold_T *)gap->ga_data) + gap->ga_len
2431 						   || fp->fd_top >= startlnum))
2432 	    fp = NULL;
2433     }
2434 
2435     /*
2436      * Loop over all lines in this fold, or until "bot" is hit.
2437      * Handle nested folds inside of this fold.
2438      * "flp->lnum" is the current line.  When finding the end of the fold, it
2439      * is just below the end of the fold.
2440      * "*flp" contains the level of the line "flp->lnum" or a following one if
2441      * there are lines with an invalid fold level.  "flp->lnum_save" is the
2442      * line number that was used to get the fold level (below "flp->lnum" when
2443      * it has an invalid fold level).  When called the fold level is always
2444      * valid, thus "flp->lnum_save" is equal to "flp->lnum".
2445      */
2446     flp->lnum_save = flp->lnum;
2447     while (!got_int)
2448     {
2449 	// Updating folds can be slow, check for CTRL-C.
2450 	line_breakcheck();
2451 
2452 	// Set "lvl" to the level of line "flp->lnum".  When flp->start is set
2453 	// and after the first line of the fold, set the level to zero to
2454 	// force the fold to end.  Do the same when had_end is set: Previous
2455 	// line was marked as end of a fold.
2456 	lvl = flp->lvl;
2457 	if (lvl > MAX_LEVEL)
2458 	    lvl = MAX_LEVEL;
2459 	if (flp->lnum > firstlnum
2460 		&& (level > lvl - flp->start || level >= flp->had_end))
2461 	    lvl = 0;
2462 
2463 	if (flp->lnum > bot && !finish && fp != NULL)
2464 	{
2465 	    // For "marker" and "syntax" methods:
2466 	    // - If a change caused a nested fold to be removed, we need to
2467 	    //   delete it and continue at least until where it ended.
2468 	    // - If a change caused a nested fold to be created, or this fold
2469 	    //   to continue below its original end, need to finish this fold.
2470 	    if (getlevel != foldlevelMarker
2471 		    && getlevel != foldlevelExpr
2472 		    && getlevel != foldlevelSyntax)
2473 		break;
2474 	    i = 0;
2475 	    fp2 = fp;
2476 	    if (lvl >= level)
2477 	    {
2478 		// Compute how deep the folds currently are, if it's deeper
2479 		// than "lvl" then some must be deleted, need to update
2480 		// at least one nested fold.
2481 		ll = flp->lnum - fp->fd_top;
2482 		while (foldFind(&fp2->fd_nested, ll, &fp2))
2483 		{
2484 		    ++i;
2485 		    ll -= fp2->fd_top;
2486 		}
2487 	    }
2488 	    if (lvl < level + i)
2489 	    {
2490 		(void)foldFind(&fp->fd_nested, flp->lnum - fp->fd_top, &fp2);
2491 		if (fp2 != NULL)
2492 		    bot = fp2->fd_top + fp2->fd_len - 1 + fp->fd_top;
2493 	    }
2494 	    else if (fp->fd_top + fp->fd_len <= flp->lnum && lvl >= level)
2495 		finish = TRUE;
2496 	    else
2497 		break;
2498 	}
2499 
2500 	// At the start of the first nested fold and at the end of the current
2501 	// fold: check if existing folds at this level, before the current
2502 	// one, need to be deleted or truncated.
2503 	if (fp == NULL
2504 		&& (lvl != level
2505 		    || flp->lnum_save >= bot
2506 		    || flp->start != 0
2507 		    || flp->had_end <= MAX_LEVEL
2508 		    || flp->lnum == linecount))
2509 	{
2510 	    /*
2511 	     * Remove or update folds that have lines between startlnum and
2512 	     * firstlnum.
2513 	     */
2514 	    while (!got_int)
2515 	    {
2516 		// set concat to 1 if it's allowed to concatenated this fold
2517 		// with a previous one that touches it.
2518 		if (flp->start != 0 || flp->had_end <= MAX_LEVEL)
2519 		    concat = 0;
2520 		else
2521 		    concat = 1;
2522 
2523 		// Find an existing fold to re-use.  Preferably one that
2524 		// includes startlnum, otherwise one that ends just before
2525 		// startlnum or starts after it.
2526 		if (gap->ga_len > 0 && (foldFind(gap, startlnum, &fp)
2527 			|| (fp < ((fold_T *)gap->ga_data) + gap->ga_len
2528 			    && fp->fd_top <= firstlnum)
2529 			|| foldFind(gap, firstlnum - concat, &fp)
2530 			|| (fp < ((fold_T *)gap->ga_data) + gap->ga_len
2531 			    && ((lvl < level && fp->fd_top < flp->lnum)
2532 				|| (lvl >= level
2533 					   && fp->fd_top <= flp->lnum_save)))))
2534 		{
2535 		    if (fp->fd_top + fp->fd_len + concat > firstlnum)
2536 		    {
2537 			// Use existing fold for the new fold.  If it starts
2538 			// before where we started looking, extend it.  If it
2539 			// starts at another line, update nested folds to keep
2540 			// their position, compensating for the new fd_top.
2541 			if (fp->fd_top == firstlnum)
2542 			{
2543 			    // have found a fold beginning where we want
2544 			}
2545 			else if (fp->fd_top >= startlnum)
2546 			{
2547 			    if (fp->fd_top > firstlnum)
2548 				// like lines are inserted
2549 				foldMarkAdjustRecurse(&fp->fd_nested,
2550 					(linenr_T)0, (linenr_T)MAXLNUM,
2551 					(long)(fp->fd_top - firstlnum), 0L);
2552 			    else
2553 				// like lines are deleted
2554 				foldMarkAdjustRecurse(&fp->fd_nested,
2555 					(linenr_T)0,
2556 					(long)(firstlnum - fp->fd_top - 1),
2557 					(linenr_T)MAXLNUM,
2558 					(long)(fp->fd_top - firstlnum));
2559 			    fp->fd_len += fp->fd_top - firstlnum;
2560 			    fp->fd_top = firstlnum;
2561 			    fold_changed = TRUE;
2562 			}
2563 			else if ((flp->start != 0 && lvl == level)
2564 						     || firstlnum != startlnum)
2565 			{
2566 			    linenr_T breakstart;
2567 			    linenr_T breakend;
2568 
2569 			    /*
2570 			     * Before there was a fold spanning from above
2571 			     * startlnum to below firstlnum. This fold is valid
2572 			     * above startlnum (because we are not updating
2573 			     * that range), but there should now be a break in
2574 			     * it.
2575 			     * If the break is because we are now forced to
2576 			     * start a new fold at the level "level" at line
2577 			     * fline->lnum, then we need to split the fold at
2578 			     * fline->lnum.
2579 			     * If the break is because the range
2580 			     * [startlnum, firstlnum) is now at a lower indent
2581 			     * than "level", we need to split the fold in this
2582 			     * range.
2583 			     * Any splits have to be done recursively.
2584 			     */
2585 			    if (firstlnum != startlnum)
2586 			    {
2587 				breakstart = startlnum;
2588 				breakend = firstlnum;
2589 			    }
2590 			    else
2591 			    {
2592 				breakstart = flp->lnum;
2593 				breakend = flp->lnum;
2594 			    }
2595 			    foldRemove(&fp->fd_nested, breakstart - fp->fd_top,
2596 						      breakend - fp->fd_top);
2597 			    i = (int)(fp - (fold_T *)gap->ga_data);
2598 			    foldSplit(gap, i, breakstart, breakend - 1);
2599 			    fp = (fold_T *)gap->ga_data + i + 1;
2600 
2601 			    // If using the "marker" or "syntax" method, we
2602 			    // need to continue until the end of the fold is
2603 			    // found.
2604 			    if (getlevel == foldlevelMarker
2605 				    || getlevel == foldlevelExpr
2606 				    || getlevel == foldlevelSyntax)
2607 				finish = TRUE;
2608 			}
2609 
2610 			if (fp->fd_top == startlnum && concat)
2611 			{
2612 			    i = (int)(fp - (fold_T *)gap->ga_data);
2613 			    if (i != 0)
2614 			    {
2615 				fp2 = fp - 1;
2616 				if (fp2->fd_top + fp2->fd_len == fp->fd_top)
2617 				{
2618 				    foldMerge(fp2, gap, fp);
2619 				    fp = fp2;
2620 				}
2621 			    }
2622 			}
2623 			break;
2624 		    }
2625 		    if (fp->fd_top >= startlnum)
2626 		    {
2627 			// A fold that starts at or after startlnum and stops
2628 			// before the new fold must be deleted.  Continue
2629 			// looking for the next one.
2630 			deleteFoldEntry(gap,
2631 				     (int)(fp - (fold_T *)gap->ga_data), TRUE);
2632 		    }
2633 		    else
2634 		    {
2635 			// A fold has some lines above startlnum, truncate it
2636 			// to stop just above startlnum.
2637 			fp->fd_len = startlnum - fp->fd_top;
2638 			foldMarkAdjustRecurse(&fp->fd_nested,
2639 				(linenr_T)fp->fd_len, (linenr_T)MAXLNUM,
2640 						       (linenr_T)MAXLNUM, 0L);
2641 			fold_changed = TRUE;
2642 		    }
2643 		}
2644 		else
2645 		{
2646 		    // Insert new fold.  Careful: ga_data may be NULL and it
2647 		    // may change!
2648 		    if (gap->ga_len == 0)
2649 			i = 0;
2650 		    else
2651 			i = (int)(fp - (fold_T *)gap->ga_data);
2652 		    if (foldInsert(gap, i) != OK)
2653 			return bot;
2654 		    fp = (fold_T *)gap->ga_data + i;
2655 		    // The new fold continues until bot, unless we find the
2656 		    // end earlier.
2657 		    fp->fd_top = firstlnum;
2658 		    fp->fd_len = bot - firstlnum + 1;
2659 		    // When the containing fold is open, the new fold is open.
2660 		    // The new fold is closed if the fold above it is closed.
2661 		    // The first fold depends on the containing fold.
2662 		    if (topflags == FD_OPEN)
2663 		    {
2664 			flp->wp->w_fold_manual = TRUE;
2665 			fp->fd_flags = FD_OPEN;
2666 		    }
2667 		    else if (i <= 0)
2668 		    {
2669 			fp->fd_flags = topflags;
2670 			if (topflags != FD_LEVEL)
2671 			    flp->wp->w_fold_manual = TRUE;
2672 		    }
2673 		    else
2674 			fp->fd_flags = (fp - 1)->fd_flags;
2675 		    fp->fd_small = MAYBE;
2676 		    // If using the "marker", "expr" or "syntax" method, we
2677 		    // need to continue until the end of the fold is found.
2678 		    if (getlevel == foldlevelMarker
2679 			    || getlevel == foldlevelExpr
2680 			    || getlevel == foldlevelSyntax)
2681 			finish = TRUE;
2682 		    fold_changed = TRUE;
2683 		    break;
2684 		}
2685 	    }
2686 	}
2687 
2688 	if (lvl < level || flp->lnum > linecount)
2689 	{
2690 	    /*
2691 	     * Found a line with a lower foldlevel, this fold ends just above
2692 	     * "flp->lnum".
2693 	     */
2694 	    break;
2695 	}
2696 
2697 	/*
2698 	 * The fold includes the line "flp->lnum" and "flp->lnum_save".
2699 	 * Check "fp" for safety.
2700 	 */
2701 	if (lvl > level && fp != NULL)
2702 	{
2703 	    /*
2704 	     * There is a nested fold, handle it recursively.
2705 	     */
2706 	    // At least do one line (can happen when finish is TRUE).
2707 	    if (bot < flp->lnum)
2708 		bot = flp->lnum;
2709 
2710 	    // Line numbers in the nested fold are relative to the start of
2711 	    // this fold.
2712 	    flp->lnum = flp->lnum_save - fp->fd_top;
2713 	    flp->off += fp->fd_top;
2714 	    i = (int)(fp - (fold_T *)gap->ga_data);
2715 	    bot = foldUpdateIEMSRecurse(&fp->fd_nested, level + 1,
2716 				       startlnum2 - fp->fd_top, flp, getlevel,
2717 					      bot - fp->fd_top, fp->fd_flags);
2718 	    fp = (fold_T *)gap->ga_data + i;
2719 	    flp->lnum += fp->fd_top;
2720 	    flp->lnum_save += fp->fd_top;
2721 	    flp->off -= fp->fd_top;
2722 	    bot += fp->fd_top;
2723 	    startlnum2 = flp->lnum;
2724 
2725 	    // This fold may end at the same line, don't incr. flp->lnum.
2726 	}
2727 	else
2728 	{
2729 	    /*
2730 	     * Get the level of the next line, then continue the loop to check
2731 	     * if it ends there.
2732 	     * Skip over undefined lines, to find the foldlevel after it.
2733 	     * For the last line in the file the foldlevel is always valid.
2734 	     */
2735 	    flp->lnum = flp->lnum_save;
2736 	    ll = flp->lnum + 1;
2737 	    while (!got_int)
2738 	    {
2739 		// Make the previous level available to foldlevel().
2740 		prev_lnum = flp->lnum;
2741 		prev_lnum_lvl = flp->lvl;
2742 
2743 		if (++flp->lnum > linecount)
2744 		    break;
2745 		flp->lvl = flp->lvl_next;
2746 		getlevel(flp);
2747 		if (flp->lvl >= 0 || flp->had_end <= MAX_LEVEL)
2748 		    break;
2749 	    }
2750 	    prev_lnum = 0;
2751 	    if (flp->lnum > linecount)
2752 		break;
2753 
2754 	    // leave flp->lnum_save to lnum of the line that was used to get
2755 	    // the level, flp->lnum to the lnum of the next line.
2756 	    flp->lnum_save = flp->lnum;
2757 	    flp->lnum = ll;
2758 	}
2759     }
2760 
2761     if (fp == NULL)	// only happens when got_int is set
2762 	return bot;
2763 
2764     /*
2765      * Get here when:
2766      * lvl < level: the folds ends just above "flp->lnum"
2767      * lvl >= level: fold continues below "bot"
2768      */
2769 
2770     // Current fold at least extends until lnum.
2771     if (fp->fd_len < flp->lnum - fp->fd_top)
2772     {
2773 	fp->fd_len = flp->lnum - fp->fd_top;
2774 	fp->fd_small = MAYBE;
2775 	fold_changed = TRUE;
2776     }
2777     else if (fp->fd_top + fp->fd_len > linecount)
2778 	// running into the end of the buffer (deleted last line)
2779 	fp->fd_len = linecount - fp->fd_top + 1;
2780 
2781     // Delete contained folds from the end of the last one found until where
2782     // we stopped looking.
2783     foldRemove(&fp->fd_nested, startlnum2 - fp->fd_top,
2784 						  flp->lnum - 1 - fp->fd_top);
2785 
2786     if (lvl < level)
2787     {
2788 	// End of fold found, update the length when it got shorter.
2789 	if (fp->fd_len != flp->lnum - fp->fd_top)
2790 	{
2791 	    if (fp->fd_top + fp->fd_len - 1 > bot)
2792 	    {
2793 		// fold continued below bot
2794 		if (getlevel == foldlevelMarker
2795 			|| getlevel == foldlevelExpr
2796 			|| getlevel == foldlevelSyntax)
2797 		{
2798 		    // marker method: truncate the fold and make sure the
2799 		    // previously included lines are processed again
2800 		    bot = fp->fd_top + fp->fd_len - 1;
2801 		    fp->fd_len = flp->lnum - fp->fd_top;
2802 		}
2803 		else
2804 		{
2805 		    // indent or expr method: split fold to create a new one
2806 		    // below bot
2807 		    i = (int)(fp - (fold_T *)gap->ga_data);
2808 		    foldSplit(gap, i, flp->lnum, bot);
2809 		    fp = (fold_T *)gap->ga_data + i;
2810 		}
2811 	    }
2812 	    else
2813 		fp->fd_len = flp->lnum - fp->fd_top;
2814 	    fold_changed = TRUE;
2815 	}
2816     }
2817 
2818     // delete following folds that end before the current line
2819     for (;;)
2820     {
2821 	fp2 = fp + 1;
2822 	if (fp2 >= (fold_T *)gap->ga_data + gap->ga_len
2823 						  || fp2->fd_top > flp->lnum)
2824 	    break;
2825 	if (fp2->fd_top + fp2->fd_len > flp->lnum)
2826 	{
2827 	    if (fp2->fd_top < flp->lnum)
2828 	    {
2829 		// Make fold that includes lnum start at lnum.
2830 		foldMarkAdjustRecurse(&fp2->fd_nested,
2831 			(linenr_T)0, (long)(flp->lnum - fp2->fd_top - 1),
2832 			(linenr_T)MAXLNUM, (long)(fp2->fd_top - flp->lnum));
2833 		fp2->fd_len -= flp->lnum - fp2->fd_top;
2834 		fp2->fd_top = flp->lnum;
2835 		fold_changed = TRUE;
2836 	    }
2837 
2838 	    if (lvl >= level)
2839 	    {
2840 		// merge new fold with existing fold that follows
2841 		foldMerge(fp, gap, fp2);
2842 	    }
2843 	    break;
2844 	}
2845 	fold_changed = TRUE;
2846 	deleteFoldEntry(gap, (int)(fp2 - (fold_T *)gap->ga_data), TRUE);
2847     }
2848 
2849     // Need to redraw the lines we inspected, which might be further down than
2850     // was asked for.
2851     if (bot < flp->lnum - 1)
2852 	bot = flp->lnum - 1;
2853 
2854     return bot;
2855 }
2856 
2857 // foldInsert() {{{2
2858 /*
2859  * Insert a new fold in "gap" at position "i".
2860  * Returns OK for success, FAIL for failure.
2861  */
2862     static int
foldInsert(garray_T * gap,int i)2863 foldInsert(garray_T *gap, int i)
2864 {
2865     fold_T	*fp;
2866 
2867     if (ga_grow(gap, 1) != OK)
2868 	return FAIL;
2869     fp = (fold_T *)gap->ga_data + i;
2870     if (gap->ga_len > 0 && i < gap->ga_len)
2871 	mch_memmove(fp + 1, fp, sizeof(fold_T) * (gap->ga_len - i));
2872     ++gap->ga_len;
2873     ga_init2(&fp->fd_nested, (int)sizeof(fold_T), 10);
2874     return OK;
2875 }
2876 
2877 // foldSplit() {{{2
2878 /*
2879  * Split the "i"th fold in "gap", which starts before "top" and ends below
2880  * "bot" in two pieces, one ending above "top" and the other starting below
2881  * "bot".
2882  * The caller must first have taken care of any nested folds from "top" to
2883  * "bot"!
2884  */
2885     static void
foldSplit(garray_T * gap,int i,linenr_T top,linenr_T bot)2886 foldSplit(
2887     garray_T	*gap,
2888     int		i,
2889     linenr_T	top,
2890     linenr_T	bot)
2891 {
2892     fold_T	*fp;
2893     fold_T	*fp2;
2894     garray_T	*gap1;
2895     garray_T	*gap2;
2896     int		idx;
2897     int		len;
2898 
2899     // The fold continues below bot, need to split it.
2900     if (foldInsert(gap, i + 1) == FAIL)
2901 	return;
2902     fp = (fold_T *)gap->ga_data + i;
2903     fp[1].fd_top = bot + 1;
2904     fp[1].fd_len = fp->fd_len - (fp[1].fd_top - fp->fd_top);
2905     fp[1].fd_flags = fp->fd_flags;
2906     fp[1].fd_small = MAYBE;
2907     fp->fd_small = MAYBE;
2908 
2909     // Move nested folds below bot to new fold.  There can't be
2910     // any between top and bot, they have been removed by the caller.
2911     gap1 = &fp->fd_nested;
2912     gap2 = &fp[1].fd_nested;
2913     if (foldFind(gap1, bot + 1 - fp->fd_top, &fp2))
2914     {
2915 	len = (int)((fold_T *)gap1->ga_data + gap1->ga_len - fp2);
2916 	if (len > 0 && ga_grow(gap2, len) == OK)
2917 	{
2918 	    for (idx = 0; idx < len; ++idx)
2919 	    {
2920 		((fold_T *)gap2->ga_data)[idx] = fp2[idx];
2921 		((fold_T *)gap2->ga_data)[idx].fd_top
2922 						  -= fp[1].fd_top - fp->fd_top;
2923 	    }
2924 	    gap2->ga_len = len;
2925 	    gap1->ga_len -= len;
2926 	}
2927     }
2928     fp->fd_len = top - fp->fd_top;
2929     fold_changed = TRUE;
2930 }
2931 
2932 // foldRemove() {{{2
2933 /*
2934  * Remove folds within the range "top" to and including "bot".
2935  * Check for these situations:
2936  *      1  2  3
2937  *      1  2  3
2938  * top     2  3  4  5
2939  *	   2  3  4  5
2940  * bot	   2  3  4  5
2941  *	      3     5  6
2942  *	      3     5  6
2943  *
2944  * 1: not changed
2945  * 2: truncate to stop above "top"
2946  * 3: split in two parts, one stops above "top", other starts below "bot".
2947  * 4: deleted
2948  * 5: made to start below "bot".
2949  * 6: not changed
2950  */
2951     static void
foldRemove(garray_T * gap,linenr_T top,linenr_T bot)2952 foldRemove(garray_T *gap, linenr_T top, linenr_T bot)
2953 {
2954     fold_T	*fp = NULL;
2955 
2956     if (bot < top)
2957 	return;		// nothing to do
2958 
2959     while (gap->ga_len > 0)
2960     {
2961 	// Find fold that includes top or a following one.
2962 	if (foldFind(gap, top, &fp) && fp->fd_top < top)
2963 	{
2964 	    // 2: or 3: need to delete nested folds
2965 	    foldRemove(&fp->fd_nested, top - fp->fd_top, bot - fp->fd_top);
2966 	    if (fp->fd_top + fp->fd_len - 1 > bot)
2967 	    {
2968 		// 3: need to split it.
2969 		foldSplit(gap, (int)(fp - (fold_T *)gap->ga_data), top, bot);
2970 	    }
2971 	    else
2972 	    {
2973 		// 2: truncate fold at "top".
2974 		fp->fd_len = top - fp->fd_top;
2975 	    }
2976 	    fold_changed = TRUE;
2977 	    continue;
2978 	}
2979 	if (fp >= (fold_T *)(gap->ga_data) + gap->ga_len
2980 		|| fp->fd_top > bot)
2981 	{
2982 	    // 6: Found a fold below bot, can stop looking.
2983 	    break;
2984 	}
2985 	if (fp->fd_top >= top)
2986 	{
2987 	    // Found an entry below top.
2988 	    fold_changed = TRUE;
2989 	    if (fp->fd_top + fp->fd_len - 1 > bot)
2990 	    {
2991 		// 5: Make fold that includes bot start below bot.
2992 		foldMarkAdjustRecurse(&fp->fd_nested,
2993 			(linenr_T)0, (long)(bot - fp->fd_top),
2994 			(linenr_T)MAXLNUM, (long)(fp->fd_top - bot - 1));
2995 		fp->fd_len -= bot - fp->fd_top + 1;
2996 		fp->fd_top = bot + 1;
2997 		break;
2998 	    }
2999 
3000 	    // 4: Delete completely contained fold.
3001 	    deleteFoldEntry(gap, (int)(fp - (fold_T *)gap->ga_data), TRUE);
3002 	}
3003     }
3004 }
3005 
3006 // foldReverseOrder() {{{2
3007     static void
foldReverseOrder(garray_T * gap,linenr_T start_arg,linenr_T end_arg)3008 foldReverseOrder(garray_T *gap, linenr_T start_arg, linenr_T end_arg)
3009 {
3010     fold_T *left, *right;
3011     fold_T tmp;
3012     linenr_T start = start_arg;
3013     linenr_T end = end_arg;
3014 
3015     for (; start < end; start++, end--)
3016     {
3017 	left = (fold_T *)gap->ga_data + start;
3018 	right = (fold_T *)gap->ga_data + end;
3019 	tmp  = *left;
3020 	*left = *right;
3021 	*right = tmp;
3022     }
3023 }
3024 
3025 // foldMoveRange() {{{2
3026 /*
3027  * Move folds within the inclusive range "line1" to "line2" to after "dest"
3028  * requires "line1" <= "line2" <= "dest"
3029  *
3030  * There are the following situations for the first fold at or below line1 - 1.
3031  *       1  2  3  4
3032  *       1  2  3  4
3033  * line1    2  3  4
3034  *          2  3  4  5  6  7
3035  * line2       3  4  5  6  7
3036  *             3  4     6  7  8  9
3037  * dest           4        7  8  9
3038  *                4        7  8    10
3039  *                4        7  8    10
3040  *
3041  * In the following descriptions, "moved" means moving in the buffer, *and* in
3042  * the fold array.
3043  * Meanwhile, "shifted" just means moving in the buffer.
3044  * 1. not changed
3045  * 2. truncated above line1
3046  * 3. length reduced by  line2 - line1, folds starting between the end of 3 and
3047  *    dest are truncated and shifted up
3048  * 4. internal folds moved (from [line1, line2] to dest)
3049  * 5. moved to dest.
3050  * 6. truncated below line2 and moved.
3051  * 7. length reduced by line2 - dest, folds starting between line2 and dest are
3052  *    removed, top is moved down by move_len.
3053  * 8. truncated below dest and shifted up.
3054  * 9. shifted up
3055  * 10. not changed
3056  */
3057 
3058     static void
truncate_fold(fold_T * fp,linenr_T end)3059 truncate_fold(fold_T *fp, linenr_T end)
3060 {
3061     end += 1;
3062     foldRemove(&fp->fd_nested, end - fp->fd_top, MAXLNUM);
3063     fp->fd_len = end - fp->fd_top;
3064 }
3065 
3066 #define fold_end(fp) ((fp)->fd_top + (fp)->fd_len - 1)
3067 #define valid_fold(fp, gap) ((gap)->ga_len > 0 && (fp) < ((fold_T *)(gap)->ga_data + (gap)->ga_len))
3068 #define fold_index(fp, gap) ((size_t)(fp - ((fold_T *)(gap)->ga_data)))
3069 
3070     void
foldMoveRange(garray_T * gap,linenr_T line1,linenr_T line2,linenr_T dest)3071 foldMoveRange(garray_T *gap, linenr_T line1, linenr_T line2, linenr_T dest)
3072 {
3073     fold_T *fp;
3074     linenr_T range_len = line2 - line1 + 1;
3075     linenr_T move_len = dest - line2;
3076     int at_start = foldFind(gap, line1 - 1, &fp);
3077     size_t move_start = 0, move_end = 0, dest_index = 0;
3078 
3079     if (at_start)
3080     {
3081 	if (fold_end(fp) > dest)
3082 	{
3083 	    // Case 4
3084 	   // don't have to change this fold, but have to move nested folds.
3085 	    foldMoveRange(&fp->fd_nested, line1 - fp->fd_top, line2 -
3086 		    fp->fd_top, dest - fp->fd_top);
3087 	    return;
3088 	}
3089 	else if (fold_end(fp) > line2)
3090 	{
3091 	    // Case 3
3092 	    // Remove nested folds between line1 and line2 & reduce the
3093 	    // length of fold by "range_len".
3094 	    // Folds after this one must be dealt with.
3095 	    foldMarkAdjustRecurse(&fp->fd_nested, line1 - fp->fd_top, line2 -
3096 		    fp->fd_top, MAXLNUM, -range_len);
3097 	    fp->fd_len -= range_len;
3098 	}
3099 	else
3100 	    // Case 2 truncate fold, folds after this one must be dealt with.
3101 	    truncate_fold(fp, line1 - 1);
3102 
3103 	// Look at the next fold, and treat that one as if it were the first
3104 	// after  "line1" (because now it is).
3105 	fp = fp + 1;
3106     }
3107 
3108     if (!valid_fold(fp, gap) || fp->fd_top > dest)
3109     {
3110 	// Case 10
3111 	// No folds after "line1" and before "dest"
3112 	return;
3113     }
3114     else if (fp->fd_top > line2)
3115     {
3116 	for (; valid_fold(fp, gap) && fold_end(fp) <= dest; fp++)
3117 	// Case 9. (for all case 9's) -- shift up.
3118 	    fp->fd_top -= range_len;
3119 
3120 	if (valid_fold(fp, gap) && fp->fd_top <= dest)
3121 	{
3122 	    // Case 8. -- ensure truncated at dest, shift up
3123 	    truncate_fold(fp, dest);
3124 	    fp->fd_top -= range_len;
3125 	}
3126 	return;
3127     }
3128     else if (fold_end(fp) > dest)
3129     {
3130 	// Case 7 -- remove nested folds and shrink
3131 	foldMarkAdjustRecurse(&fp->fd_nested, line2 + 1 - fp->fd_top, dest -
3132 		fp->fd_top, MAXLNUM, -move_len);
3133 	fp->fd_len -= move_len;
3134 	fp->fd_top += move_len;
3135 	return;
3136     }
3137 
3138     // Case 5 or 6
3139     // changes rely on whether there are folds between the end of
3140     // this fold and "dest".
3141     move_start = fold_index(fp, gap);
3142 
3143     for (; valid_fold(fp, gap) && fp->fd_top <= dest; fp++)
3144     {
3145 	if (fp->fd_top <= line2)
3146 	{
3147 	    // 1. 2. or 3.
3148 	    if (fold_end(fp) > line2)
3149 		// 2. or 3., truncate before moving
3150 		truncate_fold(fp, line2);
3151 
3152 	    fp->fd_top += move_len;
3153 	    continue;
3154 	}
3155 
3156 	// Record index of the first fold after the moved range.
3157 	if (move_end == 0)
3158 	    move_end = fold_index(fp, gap);
3159 
3160 	if (fold_end(fp) > dest)
3161 	    truncate_fold(fp, dest);
3162 
3163 	fp->fd_top -= range_len;
3164     }
3165 
3166     dest_index = fold_index(fp, gap);
3167 
3168     /*
3169      * All folds are now correct, but not necessarily in the correct order.  We
3170      * must swap folds in the range [move_end, dest_index) with those in the
3171      * range [move_start, move_end).
3172      */
3173     if (move_end == 0)
3174 	// There are no folds after those moved, hence no folds have been moved
3175 	// out of order.
3176 	return;
3177     foldReverseOrder(gap, (linenr_T)move_start, (linenr_T)dest_index - 1);
3178     foldReverseOrder(gap, (linenr_T)move_start,
3179 			   (linenr_T)(move_start + dest_index - move_end - 1));
3180     foldReverseOrder(gap, (linenr_T)(move_start + dest_index - move_end),
3181 						   (linenr_T)(dest_index - 1));
3182 }
3183 #undef fold_end
3184 #undef valid_fold
3185 #undef fold_index
3186 
3187 // foldMerge() {{{2
3188 /*
3189  * Merge two adjacent folds (and the nested ones in them).
3190  * This only works correctly when the folds are really adjacent!  Thus "fp1"
3191  * must end just above "fp2".
3192  * The resulting fold is "fp1", nested folds are moved from "fp2" to "fp1".
3193  * Fold entry "fp2" in "gap" is deleted.
3194  */
3195     static void
foldMerge(fold_T * fp1,garray_T * gap,fold_T * fp2)3196 foldMerge(fold_T *fp1, garray_T *gap, fold_T *fp2)
3197 {
3198     fold_T	*fp3;
3199     fold_T	*fp4;
3200     int		idx;
3201     garray_T	*gap1 = &fp1->fd_nested;
3202     garray_T	*gap2 = &fp2->fd_nested;
3203 
3204     // If the last nested fold in fp1 touches the first nested fold in fp2,
3205     // merge them recursively.
3206     if (foldFind(gap1, fp1->fd_len - 1L, &fp3) && foldFind(gap2, 0L, &fp4))
3207 	foldMerge(fp3, gap2, fp4);
3208 
3209     // Move nested folds in fp2 to the end of fp1.
3210     if (gap2->ga_len > 0 && ga_grow(gap1, gap2->ga_len) == OK)
3211     {
3212 	for (idx = 0; idx < gap2->ga_len; ++idx)
3213 	{
3214 	    ((fold_T *)gap1->ga_data)[gap1->ga_len]
3215 					= ((fold_T *)gap2->ga_data)[idx];
3216 	    ((fold_T *)gap1->ga_data)[gap1->ga_len].fd_top += fp1->fd_len;
3217 	    ++gap1->ga_len;
3218 	}
3219 	gap2->ga_len = 0;
3220     }
3221 
3222     fp1->fd_len += fp2->fd_len;
3223     deleteFoldEntry(gap, (int)(fp2 - (fold_T *)gap->ga_data), TRUE);
3224     fold_changed = TRUE;
3225 }
3226 
3227 // foldlevelIndent() {{{2
3228 /*
3229  * Low level function to get the foldlevel for the "indent" method.
3230  * Doesn't use any caching.
3231  * Returns a level of -1 if the foldlevel depends on surrounding lines.
3232  */
3233     static void
foldlevelIndent(fline_T * flp)3234 foldlevelIndent(fline_T *flp)
3235 {
3236     char_u	*s;
3237     buf_T	*buf;
3238     linenr_T	lnum = flp->lnum + flp->off;
3239 
3240     buf = flp->wp->w_buffer;
3241     s = skipwhite(ml_get_buf(buf, lnum, FALSE));
3242 
3243     // empty line or lines starting with a character in 'foldignore': level
3244     // depends on surrounding lines
3245     if (*s == NUL || vim_strchr(flp->wp->w_p_fdi, *s) != NULL)
3246     {
3247 	// first and last line can't be undefined, use level 0
3248 	if (lnum == 1 || lnum == buf->b_ml.ml_line_count)
3249 	    flp->lvl = 0;
3250 	else
3251 	    flp->lvl = -1;
3252     }
3253     else
3254 	flp->lvl = get_indent_buf(buf, lnum) / get_sw_value(buf);
3255     if (flp->lvl > flp->wp->w_p_fdn)
3256     {
3257 	flp->lvl = flp->wp->w_p_fdn;
3258 	if (flp->lvl < 0)
3259 	    flp->lvl = 0;
3260     }
3261 }
3262 
3263 // foldlevelDiff() {{{2
3264 #ifdef FEAT_DIFF
3265 /*
3266  * Low level function to get the foldlevel for the "diff" method.
3267  * Doesn't use any caching.
3268  */
3269     static void
foldlevelDiff(fline_T * flp)3270 foldlevelDiff(fline_T *flp)
3271 {
3272     if (diff_infold(flp->wp, flp->lnum + flp->off))
3273 	flp->lvl = 1;
3274     else
3275 	flp->lvl = 0;
3276 }
3277 #endif
3278 
3279 // foldlevelExpr() {{{2
3280 /*
3281  * Low level function to get the foldlevel for the "expr" method.
3282  * Doesn't use any caching.
3283  * Returns a level of -1 if the foldlevel depends on surrounding lines.
3284  */
3285     static void
foldlevelExpr(fline_T * flp)3286 foldlevelExpr(fline_T *flp)
3287 {
3288 #ifndef FEAT_EVAL
3289     flp->start = FALSE;
3290     flp->lvl = 0;
3291 #else
3292     win_T	*win;
3293     int		n;
3294     int		c;
3295     linenr_T	lnum = flp->lnum + flp->off;
3296     int		save_keytyped;
3297 
3298     win = curwin;
3299     curwin = flp->wp;
3300     curbuf = flp->wp->w_buffer;
3301     set_vim_var_nr(VV_LNUM, lnum);
3302 
3303     flp->start = 0;
3304     flp->had_end = flp->end;
3305     flp->end = MAX_LEVEL + 1;
3306     if (lnum <= 1)
3307 	flp->lvl = 0;
3308 
3309     // KeyTyped may be reset to 0 when calling a function which invokes
3310     // do_cmdline().  To make 'foldopen' work correctly restore KeyTyped.
3311     save_keytyped = KeyTyped;
3312     n = eval_foldexpr(flp->wp->w_p_fde, &c);
3313     KeyTyped = save_keytyped;
3314 
3315     switch (c)
3316     {
3317 	// "a1", "a2", .. : add to the fold level
3318 	case 'a': if (flp->lvl >= 0)
3319 		  {
3320 		      flp->lvl += n;
3321 		      flp->lvl_next = flp->lvl;
3322 		  }
3323 		  flp->start = n;
3324 		  break;
3325 
3326 	// "s1", "s2", .. : subtract from the fold level
3327 	case 's': if (flp->lvl >= 0)
3328 		  {
3329 		      if (n > flp->lvl)
3330 			  flp->lvl_next = 0;
3331 		      else
3332 			  flp->lvl_next = flp->lvl - n;
3333 		      flp->end = flp->lvl_next + 1;
3334 		  }
3335 		  break;
3336 
3337 	// ">1", ">2", .. : start a fold with a certain level
3338 	case '>': flp->lvl = n;
3339 		  flp->lvl_next = n;
3340 		  flp->start = 1;
3341 		  break;
3342 
3343 	// "<1", "<2", .. : end a fold with a certain level
3344 	case '<': flp->lvl_next = n - 1;
3345 		  flp->end = n;
3346 		  break;
3347 
3348 	// "=": No change in level
3349 	case '=': flp->lvl_next = flp->lvl;
3350 		  break;
3351 
3352 	// "-1", "0", "1", ..: set fold level
3353 	default:  if (n < 0)
3354 		      // Use the current level for the next line, so that "a1"
3355 		      // will work there.
3356 		      flp->lvl_next = flp->lvl;
3357 		  else
3358 		      flp->lvl_next = n;
3359 		  flp->lvl = n;
3360 		  break;
3361     }
3362 
3363     // If the level is unknown for the first or the last line in the file, use
3364     // level 0.
3365     if (flp->lvl < 0)
3366     {
3367 	if (lnum <= 1)
3368 	{
3369 	    flp->lvl = 0;
3370 	    flp->lvl_next = 0;
3371 	}
3372 	if (lnum == curbuf->b_ml.ml_line_count)
3373 	    flp->lvl_next = 0;
3374     }
3375 
3376     curwin = win;
3377     curbuf = curwin->w_buffer;
3378 #endif
3379 }
3380 
3381 // parseMarker() {{{2
3382 /*
3383  * Parse 'foldmarker' and set "foldendmarker", "foldstartmarkerlen" and
3384  * "foldendmarkerlen".
3385  * Relies on the option value to have been checked for correctness already.
3386  */
3387     static void
parseMarker(win_T * wp)3388 parseMarker(win_T *wp)
3389 {
3390     foldendmarker = vim_strchr(wp->w_p_fmr, ',');
3391     foldstartmarkerlen = (int)(foldendmarker++ - wp->w_p_fmr);
3392     foldendmarkerlen = (int)STRLEN(foldendmarker);
3393 }
3394 
3395 // foldlevelMarker() {{{2
3396 /*
3397  * Low level function to get the foldlevel for the "marker" method.
3398  * "foldendmarker", "foldstartmarkerlen" and "foldendmarkerlen" must have been
3399  * set before calling this.
3400  * Requires that flp->lvl is set to the fold level of the previous line!
3401  * Careful: This means you can't call this function twice on the same line.
3402  * Doesn't use any caching.
3403  * Sets flp->start when a start marker was found.
3404  */
3405     static void
foldlevelMarker(fline_T * flp)3406 foldlevelMarker(fline_T *flp)
3407 {
3408     char_u	*startmarker;
3409     int		cstart;
3410     int		cend;
3411     int		start_lvl = flp->lvl;
3412     char_u	*s;
3413     int		n;
3414 
3415     // cache a few values for speed
3416     startmarker = flp->wp->w_p_fmr;
3417     cstart = *startmarker;
3418     ++startmarker;
3419     cend = *foldendmarker;
3420 
3421     // Default: no start found, next level is same as current level
3422     flp->start = 0;
3423     flp->lvl_next = flp->lvl;
3424 
3425     s = ml_get_buf(flp->wp->w_buffer, flp->lnum + flp->off, FALSE);
3426     while (*s)
3427     {
3428 	if (*s == cstart
3429 		  && STRNCMP(s + 1, startmarker, foldstartmarkerlen - 1) == 0)
3430 	{
3431 	    // found startmarker: set flp->lvl
3432 	    s += foldstartmarkerlen;
3433 	    if (VIM_ISDIGIT(*s))
3434 	    {
3435 		n = atoi((char *)s);
3436 		if (n > 0)
3437 		{
3438 		    flp->lvl = n;
3439 		    flp->lvl_next = n;
3440 		    if (n <= start_lvl)
3441 			flp->start = 1;
3442 		    else
3443 			flp->start = n - start_lvl;
3444 		}
3445 	    }
3446 	    else
3447 	    {
3448 		++flp->lvl;
3449 		++flp->lvl_next;
3450 		++flp->start;
3451 	    }
3452 	}
3453 	else if (*s == cend
3454 	      && STRNCMP(s + 1, foldendmarker + 1, foldendmarkerlen - 1) == 0)
3455 	{
3456 	    // found endmarker: set flp->lvl_next
3457 	    s += foldendmarkerlen;
3458 	    if (VIM_ISDIGIT(*s))
3459 	    {
3460 		n = atoi((char *)s);
3461 		if (n > 0)
3462 		{
3463 		    flp->lvl = n;
3464 		    flp->lvl_next = n - 1;
3465 		    // never start a fold with an end marker
3466 		    if (flp->lvl_next > start_lvl)
3467 			flp->lvl_next = start_lvl;
3468 		}
3469 	    }
3470 	    else
3471 		--flp->lvl_next;
3472 	}
3473 	else
3474 	    MB_PTR_ADV(s);
3475     }
3476 
3477     // The level can't go negative, must be missing a start marker.
3478     if (flp->lvl_next < 0)
3479 	flp->lvl_next = 0;
3480 }
3481 
3482 // foldlevelSyntax() {{{2
3483 /*
3484  * Low level function to get the foldlevel for the "syntax" method.
3485  * Doesn't use any caching.
3486  */
3487     static void
foldlevelSyntax(fline_T * flp)3488 foldlevelSyntax(fline_T *flp)
3489 {
3490 #ifndef FEAT_SYN_HL
3491     flp->start = 0;
3492     flp->lvl = 0;
3493 #else
3494     linenr_T	lnum = flp->lnum + flp->off;
3495     int		n;
3496 
3497     // Use the maximum fold level at the start of this line and the next.
3498     flp->lvl = syn_get_foldlevel(flp->wp, lnum);
3499     flp->start = 0;
3500     if (lnum < flp->wp->w_buffer->b_ml.ml_line_count)
3501     {
3502 	n = syn_get_foldlevel(flp->wp, lnum + 1);
3503 	if (n > flp->lvl)
3504 	{
3505 	    flp->start = n - flp->lvl;	// fold(s) start here
3506 	    flp->lvl = n;
3507 	}
3508     }
3509 #endif
3510 }
3511 
3512 // functions for storing the fold state in a View {{{1
3513 // put_folds() {{{2
3514 #if defined(FEAT_SESSION) || defined(PROTO)
3515 static int put_folds_recurse(FILE *fd, garray_T *gap, linenr_T off);
3516 static int put_foldopen_recurse(FILE *fd, win_T *wp, garray_T *gap, linenr_T off);
3517 static int put_fold_open_close(FILE *fd, fold_T *fp, linenr_T off);
3518 
3519 /*
3520  * Write commands to "fd" to restore the manual folds in window "wp".
3521  * Return FAIL if writing fails.
3522  */
3523     int
put_folds(FILE * fd,win_T * wp)3524 put_folds(FILE *fd, win_T *wp)
3525 {
3526     if (foldmethodIsManual(wp))
3527     {
3528 	if (put_line(fd, "silent! normal! zE") == FAIL
3529 		|| put_folds_recurse(fd, &wp->w_folds, (linenr_T)0) == FAIL
3530 		|| put_line(fd, "let &fdl = &fdl") == FAIL)
3531 	    return FAIL;
3532     }
3533 
3534     // If some folds are manually opened/closed, need to restore that.
3535     if (wp->w_fold_manual)
3536 	return put_foldopen_recurse(fd, wp, &wp->w_folds, (linenr_T)0);
3537 
3538     return OK;
3539 }
3540 
3541 // put_folds_recurse() {{{2
3542 /*
3543  * Write commands to "fd" to recreate manually created folds.
3544  * Returns FAIL when writing failed.
3545  */
3546     static int
put_folds_recurse(FILE * fd,garray_T * gap,linenr_T off)3547 put_folds_recurse(FILE *fd, garray_T *gap, linenr_T off)
3548 {
3549     int		i;
3550     fold_T	*fp;
3551 
3552     fp = (fold_T *)gap->ga_data;
3553     for (i = 0; i < gap->ga_len; i++)
3554     {
3555 	// Do nested folds first, they will be created closed.
3556 	if (put_folds_recurse(fd, &fp->fd_nested, off + fp->fd_top) == FAIL)
3557 	    return FAIL;
3558 	if (fprintf(fd, "%ld,%ldfold", fp->fd_top + off,
3559 					fp->fd_top + off + fp->fd_len - 1) < 0
3560 		|| put_eol(fd) == FAIL)
3561 	    return FAIL;
3562 	++fp;
3563     }
3564     return OK;
3565 }
3566 
3567 // put_foldopen_recurse() {{{2
3568 /*
3569  * Write commands to "fd" to open and close manually opened/closed folds.
3570  * Returns FAIL when writing failed.
3571  */
3572     static int
put_foldopen_recurse(FILE * fd,win_T * wp,garray_T * gap,linenr_T off)3573 put_foldopen_recurse(
3574     FILE	*fd,
3575     win_T	*wp,
3576     garray_T	*gap,
3577     linenr_T	off)
3578 {
3579     int		i;
3580     int		level;
3581     fold_T	*fp;
3582 
3583     fp = (fold_T *)gap->ga_data;
3584     for (i = 0; i < gap->ga_len; i++)
3585     {
3586 	if (fp->fd_flags != FD_LEVEL)
3587 	{
3588 	    if (fp->fd_nested.ga_len > 0)
3589 	    {
3590 		// open nested folds while this fold is open
3591 		if (fprintf(fd, "%ld", fp->fd_top + off) < 0
3592 			|| put_eol(fd) == FAIL
3593 			|| put_line(fd, "normal! zo") == FAIL)
3594 		    return FAIL;
3595 		if (put_foldopen_recurse(fd, wp, &fp->fd_nested,
3596 							     off + fp->fd_top)
3597 			== FAIL)
3598 		    return FAIL;
3599 		// close the parent when needed
3600 		if (fp->fd_flags == FD_CLOSED)
3601 		{
3602 		    if (put_fold_open_close(fd, fp, off) == FAIL)
3603 			return FAIL;
3604 		}
3605 	    }
3606 	    else
3607 	    {
3608 		// Open or close the leaf according to the window foldlevel.
3609 		// Do not close a leaf that is already closed, as it will close
3610 		// the parent.
3611 		level = foldLevelWin(wp, off + fp->fd_top);
3612 		if ((fp->fd_flags == FD_CLOSED && wp->w_p_fdl >= level)
3613 			|| (fp->fd_flags != FD_CLOSED && wp->w_p_fdl < level))
3614 		if (put_fold_open_close(fd, fp, off) == FAIL)
3615 		    return FAIL;
3616 	    }
3617 	}
3618 	++fp;
3619     }
3620 
3621     return OK;
3622 }
3623 
3624 // put_fold_open_close() {{{2
3625 /*
3626  * Write the open or close command to "fd".
3627  * Returns FAIL when writing failed.
3628  */
3629     static int
put_fold_open_close(FILE * fd,fold_T * fp,linenr_T off)3630 put_fold_open_close(FILE *fd, fold_T *fp, linenr_T off)
3631 {
3632     if (fprintf(fd, "%ld", fp->fd_top + off) < 0
3633 	    || put_eol(fd) == FAIL
3634 	    || fprintf(fd, "normal! z%c",
3635 			   fp->fd_flags == FD_CLOSED ? 'c' : 'o') < 0
3636 	    || put_eol(fd) == FAIL)
3637 	return FAIL;
3638 
3639     return OK;
3640 }
3641 #endif // FEAT_SESSION
3642 
3643 // }}}1
3644 #endif // defined(FEAT_FOLDING) || defined(PROTO)
3645 
3646 #if defined(FEAT_EVAL) || defined(PROTO)
3647 
3648 /*
3649  * "foldclosed()" and "foldclosedend()" functions
3650  */
3651     static void
foldclosed_both(typval_T * argvars UNUSED,typval_T * rettv,int end UNUSED)3652 foldclosed_both(
3653     typval_T	*argvars UNUSED,
3654     typval_T	*rettv,
3655     int		end UNUSED)
3656 {
3657 # ifdef FEAT_FOLDING
3658     linenr_T	lnum;
3659     linenr_T	first, last;
3660 
3661     if (in_vim9script() && check_for_lnum_arg(argvars, 0) == FAIL)
3662 	return;
3663 
3664     lnum = tv_get_lnum(argvars);
3665     if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count)
3666     {
3667 	if (hasFoldingWin(curwin, lnum, &first, &last, FALSE, NULL))
3668 	{
3669 	    if (end)
3670 		rettv->vval.v_number = (varnumber_T)last;
3671 	    else
3672 		rettv->vval.v_number = (varnumber_T)first;
3673 	    return;
3674 	}
3675     }
3676 #endif
3677     rettv->vval.v_number = -1;
3678 }
3679 
3680 /*
3681  * "foldclosed()" function
3682  */
3683     void
f_foldclosed(typval_T * argvars,typval_T * rettv)3684 f_foldclosed(typval_T *argvars, typval_T *rettv)
3685 {
3686     foldclosed_both(argvars, rettv, FALSE);
3687 }
3688 
3689 /*
3690  * "foldclosedend()" function
3691  */
3692     void
f_foldclosedend(typval_T * argvars,typval_T * rettv)3693 f_foldclosedend(typval_T *argvars, typval_T *rettv)
3694 {
3695     foldclosed_both(argvars, rettv, TRUE);
3696 }
3697 
3698 /*
3699  * "foldlevel()" function
3700  */
3701     void
f_foldlevel(typval_T * argvars UNUSED,typval_T * rettv UNUSED)3702 f_foldlevel(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
3703 {
3704 # ifdef FEAT_FOLDING
3705     linenr_T	lnum;
3706 
3707     if (in_vim9script() && check_for_lnum_arg(argvars, 0) == FAIL)
3708 	return;
3709 
3710     lnum = tv_get_lnum(argvars);
3711     if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count)
3712 	rettv->vval.v_number = foldLevel(lnum);
3713 # endif
3714 }
3715 
3716 /*
3717  * "foldtext()" function
3718  */
3719     void
f_foldtext(typval_T * argvars UNUSED,typval_T * rettv)3720 f_foldtext(typval_T *argvars UNUSED, typval_T *rettv)
3721 {
3722 # ifdef FEAT_FOLDING
3723     linenr_T	foldstart;
3724     linenr_T	foldend;
3725     char_u	*dashes;
3726     linenr_T	lnum;
3727     char_u	*s;
3728     char_u	*r;
3729     int		len;
3730     char	*txt;
3731     long	count;
3732 # endif
3733 
3734     rettv->v_type = VAR_STRING;
3735     rettv->vval.v_string = NULL;
3736 # ifdef FEAT_FOLDING
3737     foldstart = (linenr_T)get_vim_var_nr(VV_FOLDSTART);
3738     foldend = (linenr_T)get_vim_var_nr(VV_FOLDEND);
3739     dashes = get_vim_var_str(VV_FOLDDASHES);
3740     if (foldstart > 0 && foldend <= curbuf->b_ml.ml_line_count
3741 	    && dashes != NULL)
3742     {
3743 	// Find first non-empty line in the fold.
3744 	for (lnum = foldstart; lnum < foldend; ++lnum)
3745 	    if (!linewhite(lnum))
3746 		break;
3747 
3748 	// Find interesting text in this line.
3749 	s = skipwhite(ml_get(lnum));
3750 	// skip C comment-start
3751 	if (s[0] == '/' && (s[1] == '*' || s[1] == '/'))
3752 	{
3753 	    s = skipwhite(s + 2);
3754 	    if (*skipwhite(s) == NUL
3755 			    && lnum + 1 < (linenr_T)get_vim_var_nr(VV_FOLDEND))
3756 	    {
3757 		s = skipwhite(ml_get(lnum + 1));
3758 		if (*s == '*')
3759 		    s = skipwhite(s + 1);
3760 	    }
3761 	}
3762 	count = (long)(foldend - foldstart + 1);
3763 	txt = NGETTEXT("+-%s%3ld line: ", "+-%s%3ld lines: ", count);
3764 	r = alloc(STRLEN(txt)
3765 		    + STRLEN(dashes)	    // for %s
3766 		    + 20		    // for %3ld
3767 		    + STRLEN(s));	    // concatenated
3768 	if (r != NULL)
3769 	{
3770 	    sprintf((char *)r, txt, dashes, count);
3771 	    len = (int)STRLEN(r);
3772 	    STRCAT(r, s);
3773 	    // remove 'foldmarker' and 'commentstring'
3774 	    foldtext_cleanup(r + len);
3775 	    rettv->vval.v_string = r;
3776 	}
3777     }
3778 # endif
3779 }
3780 
3781 /*
3782  * "foldtextresult(lnum)" function
3783  */
3784     void
f_foldtextresult(typval_T * argvars UNUSED,typval_T * rettv)3785 f_foldtextresult(typval_T *argvars UNUSED, typval_T *rettv)
3786 {
3787 # ifdef FEAT_FOLDING
3788     linenr_T	lnum;
3789     char_u	*text;
3790     char_u	buf[FOLD_TEXT_LEN];
3791     foldinfo_T  foldinfo;
3792     int		fold_count;
3793     static int	entered = FALSE;
3794 # endif
3795 
3796     rettv->v_type = VAR_STRING;
3797     rettv->vval.v_string = NULL;
3798 
3799     if (in_vim9script() && check_for_lnum_arg(argvars, 0) == FAIL)
3800 	return;
3801 
3802 # ifdef FEAT_FOLDING
3803     if (entered)
3804 	return; // reject recursive use
3805     entered = TRUE;
3806 
3807     lnum = tv_get_lnum(argvars);
3808     // treat illegal types and illegal string values for {lnum} the same
3809     if (lnum < 0)
3810 	lnum = 0;
3811     fold_count = foldedCount(curwin, lnum, &foldinfo);
3812     if (fold_count > 0)
3813     {
3814 	text = get_foldtext(curwin, lnum, lnum + fold_count - 1,
3815 							       &foldinfo, buf);
3816 	if (text == buf)
3817 	    text = vim_strsave(text);
3818 	rettv->vval.v_string = text;
3819     }
3820 
3821     entered = FALSE;
3822 # endif
3823 }
3824 
3825 #endif // FEAT_EVAL
3826