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