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 }