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