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