neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

fold.c (101436B)


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