diff options
| author | Eliseo Martínez <eliseomarmol@gmail.com> | 2014-05-12 02:25:17 +0200 |
|---|---|---|
| committer | Eliseo Martínez <eliseomarmol@gmail.com> | 2014-05-15 20:46:01 +0200 |
| commit | da51dc9cf202772f60bd2da975dbef257bd9237c (patch) | |
| tree | 5c16b93238a153f55634e9323077f30c8133970c /src/nvim/fold.c | |
| parent | ffe61e5ba1721340ca51d56bae3ddaca415fb5bc (diff) | |
| download | rneovim-da51dc9cf202772f60bd2da975dbef257bd9237c.tar.gz rneovim-da51dc9cf202772f60bd2da975dbef257bd9237c.tar.bz2 rneovim-da51dc9cf202772f60bd2da975dbef257bd9237c.zip | |
Introduce nvim namespace: Move files.
Move files from src/ to src/nvim/.
- src/nvim/ becomes the new root dir for nvim executable sources.
- src/libnvim/ is planned to become root dir of the neovim library.
Diffstat (limited to 'src/nvim/fold.c')
| -rw-r--r-- | src/nvim/fold.c | 3033 |
1 files changed, 3033 insertions, 0 deletions
diff --git a/src/nvim/fold.c b/src/nvim/fold.c new file mode 100644 index 0000000000..143b35ea10 --- /dev/null +++ b/src/nvim/fold.c @@ -0,0 +1,3033 @@ +/* vim: set fdm=marker fdl=1 fdc=3 + * + * VIM - Vi IMproved by Bram Moolenaar + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + * See README.txt for an overview of the Vim source code. + */ + +/* + * fold.c: code for folding + */ + +#include <string.h> + +#include "vim.h" +#include "fold.h" +#include "charset.h" +#include "diff.h" +#include "eval.h" +#include "ex_docmd.h" +#include "indent.h" +#include "mark.h" +#include "memline.h" +#include "memory.h" +#include "message.h" +#include "misc1.h" +#include "misc2.h" +#include "garray.h" +#include "move.h" +#include "option.h" +#include "screen.h" +#include "syntax.h" +#include "undo.h" + +/* local declarations. {{{1 */ +/* typedef fold_T {{{2 */ +/* + * The toplevel folds for each window are stored in the w_folds growarray. + * Each toplevel fold can contain an array of second level folds in the + * fd_nested growarray. + * The info stored in both growarrays is the same: An array of fold_T. + */ +typedef struct { + linenr_T fd_top; /* first line of fold; for nested fold + * relative to parent */ + linenr_T fd_len; /* number of lines in the fold */ + garray_T fd_nested; /* array of nested folds */ + char fd_flags; /* see below */ + char fd_small; /* TRUE, FALSE or MAYBE: fold smaller than + 'foldminlines'; MAYBE applies to nested + folds too */ +} fold_T; + +#define FD_OPEN 0 /* fold is open (nested ones can be closed) */ +#define FD_CLOSED 1 /* fold is closed */ +#define FD_LEVEL 2 /* depends on 'foldlevel' (nested folds too) */ + +#define MAX_LEVEL 20 /* maximum fold depth */ + +/* static functions {{{2 */ +static void newFoldLevelWin(win_T *wp); +static int checkCloseRec(garray_T *gap, linenr_T lnum, int level); +static int foldFind(garray_T *gap, linenr_T lnum, fold_T **fpp); +static int foldLevelWin(win_T *wp, linenr_T lnum); +static void checkupdate(win_T *wp); +static void setFoldRepeat(linenr_T lnum, long count, int do_open); +static linenr_T setManualFold(linenr_T lnum, int opening, int recurse, + int *donep); +static linenr_T setManualFoldWin(win_T *wp, linenr_T lnum, int opening, + int recurse, + int *donep); +static void foldOpenNested(fold_T *fpr); +static void deleteFoldEntry(garray_T *gap, int idx, int recursive); +static void foldMarkAdjustRecurse(garray_T *gap, linenr_T line1, + linenr_T line2, long amount, + long amount_after); +static int getDeepestNestingRecurse(garray_T *gap); +static int check_closed(win_T *win, fold_T *fp, int *use_levelp, + int level, int *maybe_smallp, + linenr_T lnum_off); +static void checkSmall(win_T *wp, fold_T *fp, linenr_T lnum_off); +static void setSmallMaybe(garray_T *gap); +static void foldCreateMarkers(linenr_T start, linenr_T end); +static void foldAddMarker(linenr_T lnum, char_u *marker, int markerlen); +static void deleteFoldMarkers(fold_T *fp, int recursive, + linenr_T lnum_off); +static void foldDelMarker(linenr_T lnum, char_u *marker, int markerlen); +static void foldUpdateIEMS(win_T *wp, linenr_T top, linenr_T bot); +static void parseMarker(win_T *wp); + +static char *e_nofold = N_("E490: No fold found"); + +/* + * While updating the folds lines between invalid_top and invalid_bot have an + * undefined fold level. Only used for the window currently being updated. + */ +static linenr_T invalid_top = (linenr_T)0; +static linenr_T invalid_bot = (linenr_T)0; + +/* + * When using 'foldexpr' we sometimes get the level of the next line, which + * calls foldlevel() to get the level of the current line, which hasn't been + * stored yet. To get around this chicken-egg problem the level of the + * previous line is stored here when available. prev_lnum is zero when the + * level is not available. + */ +static linenr_T prev_lnum = 0; +static int prev_lnum_lvl = -1; + +/* Flags used for "done" argument of setManualFold. */ +#define DONE_NOTHING 0 +#define DONE_ACTION 1 /* did close or open a fold */ +#define DONE_FOLD 2 /* did find a fold */ + +static int foldstartmarkerlen; +static char_u *foldendmarker; +static int foldendmarkerlen; + +/* Exported folding functions. {{{1 */ +/* copyFoldingState() {{{2 */ +/* + * Copy that folding state from window "wp_from" to window "wp_to". + */ +void copyFoldingState(win_T *wp_from, win_T *wp_to) +{ + wp_to->w_fold_manual = wp_from->w_fold_manual; + wp_to->w_foldinvalid = wp_from->w_foldinvalid; + cloneFoldGrowArray(&wp_from->w_folds, &wp_to->w_folds); +} + +/* hasAnyFolding() {{{2 */ +/* + * Return TRUE if there may be folded lines in the current window. + */ +int hasAnyFolding(win_T *win) +{ + /* very simple now, but can become more complex later */ + return win->w_p_fen + && (!foldmethodIsManual(win) || win->w_folds.ga_len > 0); +} + +/* hasFolding() {{{2 */ +/* + * Return TRUE if line "lnum" in the current window is part of a closed + * fold. + * When returning TRUE, *firstp and *lastp are set to the first and last + * lnum of the sequence of folded lines (skipped when NULL). + */ +int hasFolding(linenr_T lnum, linenr_T *firstp, linenr_T *lastp) +{ + return hasFoldingWin(curwin, lnum, firstp, lastp, TRUE, NULL); +} + +/* hasFoldingWin() {{{2 */ +int +hasFoldingWin ( + win_T *win, + linenr_T lnum, + linenr_T *firstp, + linenr_T *lastp, + int cache, /* when TRUE: use cached values of window */ + foldinfo_T *infop /* where to store fold info */ +) +{ + int had_folded = FALSE; + linenr_T first = 0; + linenr_T last = 0; + linenr_T lnum_rel = lnum; + int x; + fold_T *fp; + int level = 0; + int use_level = FALSE; + int maybe_small = FALSE; + garray_T *gap; + int low_level = 0;; + + checkupdate(win); + /* + * Return quickly when there is no folding at all in this window. + */ + if (!hasAnyFolding(win)) { + if (infop != NULL) + infop->fi_level = 0; + return FALSE; + } + + if (cache) { + /* + * First look in cached info for displayed lines. This is probably + * the fastest, but it can only be used if the entry is still valid. + */ + x = find_wl_entry(win, lnum); + if (x >= 0) { + first = win->w_lines[x].wl_lnum; + last = win->w_lines[x].wl_lastlnum; + had_folded = win->w_lines[x].wl_folded; + } + } + + if (first == 0) { + /* + * Recursively search for a fold that contains "lnum". + */ + gap = &win->w_folds; + for (;; ) { + if (!foldFind(gap, lnum_rel, &fp)) + break; + + /* Remember lowest level of fold that starts in "lnum". */ + if (lnum_rel == fp->fd_top && low_level == 0) + low_level = level + 1; + + first += fp->fd_top; + last += fp->fd_top; + + /* is this fold closed? */ + had_folded = check_closed(win, fp, &use_level, level, + &maybe_small, lnum - lnum_rel); + if (had_folded) { + /* Fold closed: Set last and quit loop. */ + last += fp->fd_len - 1; + break; + } + + /* Fold found, but it's open: Check nested folds. Line number is + * relative to containing fold. */ + gap = &fp->fd_nested; + lnum_rel -= fp->fd_top; + ++level; + } + } + + if (!had_folded) { + if (infop != NULL) { + infop->fi_level = level; + infop->fi_lnum = lnum - lnum_rel; + infop->fi_low_level = low_level == 0 ? level : low_level; + } + return FALSE; + } + + if (lastp != NULL) + *lastp = last; + if (firstp != NULL) + *firstp = first; + if (infop != NULL) { + infop->fi_level = level + 1; + infop->fi_lnum = first; + infop->fi_low_level = low_level == 0 ? level + 1 : low_level; + } + return TRUE; +} + +/* foldLevel() {{{2 */ +/* + * Return fold level at line number "lnum" in the current window. + */ +int foldLevel(linenr_T lnum) +{ + /* While updating the folds lines between invalid_top and invalid_bot have + * an undefined fold level. Otherwise update the folds first. */ + if (invalid_top == (linenr_T)0) + checkupdate(curwin); + else if (lnum == prev_lnum && prev_lnum_lvl >= 0) + return prev_lnum_lvl; + else if (lnum >= invalid_top && lnum <= invalid_bot) + return -1; + + /* Return quickly when there is no folding at all in this window. */ + if (!hasAnyFolding(curwin)) + return 0; + + return foldLevelWin(curwin, lnum); +} + +/* lineFolded() {{{2 */ +/* + * Low level function to check if a line is folded. Doesn't use any caching. + * Return TRUE if line is folded. + * Return FALSE if line is not folded. + * Return MAYBE if the line is folded when next to a folded line. + */ +int lineFolded(win_T *win, linenr_T lnum) +{ + return foldedCount(win, lnum, NULL) != 0; +} + +/* foldedCount() {{{2 */ +/* + * Count the number of lines that are folded at line number "lnum". + * Normally "lnum" is the first line of a possible fold, and the returned + * number is the number of lines in the fold. + * Doesn't use caching from the displayed window. + * Returns number of folded lines from "lnum", or 0 if line is not folded. + * When "infop" is not NULL, fills *infop with the fold level info. + */ +long foldedCount(win_T *win, linenr_T lnum, foldinfo_T *infop) +{ + linenr_T last; + + if (hasFoldingWin(win, lnum, NULL, &last, FALSE, infop)) + return (long)(last - lnum + 1); + return 0; +} + +/* foldmethodIsManual() {{{2 */ +/* + * Return TRUE if 'foldmethod' is "manual" + */ +int foldmethodIsManual(win_T *wp) +{ + return wp->w_p_fdm[3] == 'u'; +} + +/* foldmethodIsIndent() {{{2 */ +/* + * Return TRUE if 'foldmethod' is "indent" + */ +int foldmethodIsIndent(win_T *wp) +{ + return wp->w_p_fdm[0] == 'i'; +} + +/* foldmethodIsExpr() {{{2 */ +/* + * Return TRUE if 'foldmethod' is "expr" + */ +int foldmethodIsExpr(win_T *wp) +{ + return wp->w_p_fdm[1] == 'x'; +} + +/* foldmethodIsMarker() {{{2 */ +/* + * Return TRUE if 'foldmethod' is "marker" + */ +int foldmethodIsMarker(win_T *wp) +{ + return wp->w_p_fdm[2] == 'r'; +} + +/* foldmethodIsSyntax() {{{2 */ +/* + * Return TRUE if 'foldmethod' is "syntax" + */ +int foldmethodIsSyntax(win_T *wp) +{ + return wp->w_p_fdm[0] == 's'; +} + +/* foldmethodIsDiff() {{{2 */ +/* + * Return TRUE if 'foldmethod' is "diff" + */ +int foldmethodIsDiff(win_T *wp) +{ + return wp->w_p_fdm[0] == 'd'; +} + +/* closeFold() {{{2 */ +/* + * Close fold for current window at line "lnum". + * Repeat "count" times. + */ +void closeFold(linenr_T lnum, long count) +{ + setFoldRepeat(lnum, count, FALSE); +} + +/* closeFoldRecurse() {{{2 */ +/* + * Close fold for current window at line "lnum" recursively. + */ +void closeFoldRecurse(linenr_T lnum) +{ + (void)setManualFold(lnum, FALSE, TRUE, NULL); +} + +/* opFoldRange() {{{2 */ +/* + * Open or Close folds for current window in lines "first" to "last". + * Used for "zo", "zO", "zc" and "zC" in Visual mode. + */ +void +opFoldRange ( + linenr_T first, + linenr_T last, + int opening, /* TRUE to open, FALSE to close */ + int recurse, /* TRUE to do it recursively */ + int had_visual /* TRUE when Visual selection used */ +) +{ + int done = DONE_NOTHING; /* avoid error messages */ + linenr_T lnum; + linenr_T lnum_next; + + for (lnum = first; lnum <= last; lnum = lnum_next + 1) { + lnum_next = lnum; + /* Opening one level only: next fold to open is after the one going to + * be opened. */ + if (opening && !recurse) + (void)hasFolding(lnum, NULL, &lnum_next); + (void)setManualFold(lnum, opening, recurse, &done); + /* Closing one level only: next line to close a fold is after just + * closed fold. */ + if (!opening && !recurse) + (void)hasFolding(lnum, NULL, &lnum_next); + } + if (done == DONE_NOTHING) + EMSG(_(e_nofold)); + /* Force a redraw to remove the Visual highlighting. */ + if (had_visual) + redraw_curbuf_later(INVERTED); +} + +/* openFold() {{{2 */ +/* + * Open fold for current window at line "lnum". + * Repeat "count" times. + */ +void openFold(linenr_T lnum, long count) +{ + setFoldRepeat(lnum, count, TRUE); +} + +/* openFoldRecurse() {{{2 */ +/* + * Open fold for current window at line "lnum" recursively. + */ +void openFoldRecurse(linenr_T lnum) +{ + (void)setManualFold(lnum, TRUE, TRUE, NULL); +} + +/* foldOpenCursor() {{{2 */ +/* + * Open folds until the cursor line is not in a closed fold. + */ +void foldOpenCursor(void) +{ + int done; + + checkupdate(curwin); + if (hasAnyFolding(curwin)) + for (;; ) { + done = DONE_NOTHING; + (void)setManualFold(curwin->w_cursor.lnum, TRUE, FALSE, &done); + if (!(done & DONE_ACTION)) + break; + } +} + +/* newFoldLevel() {{{2 */ +/* + * Set new foldlevel for current window. + */ +void newFoldLevel(void) +{ + newFoldLevelWin(curwin); + + if (foldmethodIsDiff(curwin) && curwin->w_p_scb) { + win_T *wp; + + /* + * Set the same foldlevel in other windows in diff mode. + */ + FOR_ALL_WINDOWS(wp) + { + if (wp != curwin && foldmethodIsDiff(wp) && wp->w_p_scb) { + wp->w_p_fdl = curwin->w_p_fdl; + newFoldLevelWin(wp); + } + } + } +} + +static void newFoldLevelWin(win_T *wp) +{ + fold_T *fp; + int i; + + checkupdate(wp); + if (wp->w_fold_manual) { + /* Set all flags for the first level of folds to FD_LEVEL. Following + * manual open/close will then change the flags to FD_OPEN or + * FD_CLOSED for those folds that don't use 'foldlevel'. */ + fp = (fold_T *)wp->w_folds.ga_data; + for (i = 0; i < wp->w_folds.ga_len; ++i) + fp[i].fd_flags = FD_LEVEL; + wp->w_fold_manual = FALSE; + } + changed_window_setting_win(wp); +} + +/* foldCheckClose() {{{2 */ +/* + * Apply 'foldlevel' to all folds that don't contain the cursor. + */ +void foldCheckClose(void) +{ + if (*p_fcl != NUL) { /* can only be "all" right now */ + checkupdate(curwin); + if (checkCloseRec(&curwin->w_folds, curwin->w_cursor.lnum, + (int)curwin->w_p_fdl)) + changed_window_setting(); + } +} + +/* checkCloseRec() {{{2 */ +static int checkCloseRec(garray_T *gap, linenr_T lnum, int level) +{ + fold_T *fp; + int retval = FALSE; + int i; + + fp = (fold_T *)gap->ga_data; + for (i = 0; i < gap->ga_len; ++i) { + /* Only manually opened folds may need to be closed. */ + if (fp[i].fd_flags == FD_OPEN) { + if (level <= 0 && (lnum < fp[i].fd_top + || lnum >= fp[i].fd_top + fp[i].fd_len)) { + fp[i].fd_flags = FD_LEVEL; + retval = TRUE; + } else + retval |= checkCloseRec(&fp[i].fd_nested, lnum - fp[i].fd_top, + level - 1); + } + } + return retval; +} + +/* foldCreateAllowed() {{{2 */ +/* + * Return TRUE if it's allowed to manually create or delete a fold. + * Give an error message and return FALSE if not. + */ +int foldManualAllowed(int create) +{ + if (foldmethodIsManual(curwin) || foldmethodIsMarker(curwin)) + return TRUE; + if (create) + EMSG(_("E350: Cannot create fold with current 'foldmethod'")); + else + EMSG(_("E351: Cannot delete fold with current 'foldmethod'")); + return FALSE; +} + +/* foldCreate() {{{2 */ +/* + * Create a fold from line "start" to line "end" (inclusive) in the current + * window. + */ +void foldCreate(linenr_T start, linenr_T end) +{ + fold_T *fp; + garray_T *gap; + garray_T fold_ga; + int i, j; + int cont; + int use_level = FALSE; + int closed = FALSE; + int level = 0; + linenr_T start_rel = start; + linenr_T end_rel = end; + + if (start > end) { + /* reverse the range */ + end = start_rel; + start = end_rel; + start_rel = start; + end_rel = end; + } + + /* When 'foldmethod' is "marker" add markers, which creates the folds. */ + if (foldmethodIsMarker(curwin)) { + foldCreateMarkers(start, end); + return; + } + + checkupdate(curwin); + + /* Find the place to insert the new fold. */ + gap = &curwin->w_folds; + for (;; ) { + if (!foldFind(gap, start_rel, &fp)) + break; + if (fp->fd_top + fp->fd_len > end_rel) { + /* New fold is completely inside this fold: Go one level deeper. */ + gap = &fp->fd_nested; + start_rel -= fp->fd_top; + end_rel -= fp->fd_top; + if (use_level || fp->fd_flags == FD_LEVEL) { + use_level = TRUE; + if (level >= curwin->w_p_fdl) + closed = TRUE; + } else if (fp->fd_flags == FD_CLOSED) + closed = TRUE; + ++level; + } else { + /* This fold and new fold overlap: Insert here and move some folds + * inside the new fold. */ + break; + } + } + + i = (int)(fp - (fold_T *)gap->ga_data); + ga_grow(gap, 1); + { + fp = (fold_T *)gap->ga_data + i; + ga_init(&fold_ga, (int)sizeof(fold_T), 10); + + /* Count number of folds that will be contained in the new fold. */ + for (cont = 0; i + cont < gap->ga_len; ++cont) + if (fp[cont].fd_top > end_rel) + break; + if (cont > 0) { + ga_grow(&fold_ga, cont); + /* If the first fold starts before the new fold, let the new fold + * start there. Otherwise the existing fold would change. */ + if (start_rel > fp->fd_top) + start_rel = fp->fd_top; + + /* When last contained fold isn't completely contained, adjust end + * of new fold. */ + if (end_rel < fp[cont - 1].fd_top + fp[cont - 1].fd_len - 1) + end_rel = fp[cont - 1].fd_top + fp[cont - 1].fd_len - 1; + /* Move contained folds to inside new fold. */ + memmove(fold_ga.ga_data, fp, sizeof(fold_T) * cont); + fold_ga.ga_len += cont; + i += cont; + + /* Adjust line numbers in contained folds to be relative to the + * new fold. */ + for (j = 0; j < cont; ++j) + ((fold_T *)fold_ga.ga_data)[j].fd_top -= start_rel; + } + /* Move remaining entries to after the new fold. */ + if (i < gap->ga_len) + memmove(fp + 1, (fold_T *)gap->ga_data + i, + sizeof(fold_T) * (gap->ga_len - i)); + gap->ga_len = gap->ga_len + 1 - cont; + + /* insert new fold */ + fp->fd_nested = fold_ga; + fp->fd_top = start_rel; + fp->fd_len = end_rel - start_rel + 1; + + /* We want the new fold to be closed. If it would remain open because + * of using 'foldlevel', need to adjust fd_flags of containing folds. + */ + if (use_level && !closed && level < curwin->w_p_fdl) + closeFold(start, 1L); + if (!use_level) + curwin->w_fold_manual = TRUE; + fp->fd_flags = FD_CLOSED; + fp->fd_small = MAYBE; + + /* redraw */ + changed_window_setting(); + } +} + + +/* deleteFold() {{{2 */ +/* + * Delete a fold at line "start" in the current window. + * When "end" is not 0, delete all folds from "start" to "end". + * When "recursive" is TRUE delete recursively. + */ +void +deleteFold ( + linenr_T start, + linenr_T end, + int recursive, + int had_visual /* TRUE when Visual selection used */ +) +{ + garray_T *gap; + fold_T *fp; + garray_T *found_ga; + fold_T *found_fp = NULL; + linenr_T found_off = 0; + int use_level; + int maybe_small = FALSE; + int level = 0; + linenr_T lnum = start; + linenr_T lnum_off; + int did_one = FALSE; + linenr_T first_lnum = MAXLNUM; + linenr_T last_lnum = 0; + + checkupdate(curwin); + + while (lnum <= end) { + /* Find the deepest fold for "start". */ + gap = &curwin->w_folds; + found_ga = NULL; + lnum_off = 0; + use_level = FALSE; + for (;; ) { + if (!foldFind(gap, lnum - lnum_off, &fp)) + break; + /* lnum is inside this fold, remember info */ + found_ga = gap; + found_fp = fp; + found_off = lnum_off; + + /* if "lnum" is folded, don't check nesting */ + if (check_closed(curwin, fp, &use_level, level, + &maybe_small, lnum_off)) + break; + + /* check nested folds */ + gap = &fp->fd_nested; + lnum_off += fp->fd_top; + ++level; + } + if (found_ga == NULL) { + ++lnum; + } else { + lnum = found_fp->fd_top + found_fp->fd_len + found_off; + + if (foldmethodIsManual(curwin)) + deleteFoldEntry(found_ga, + (int)(found_fp - (fold_T *)found_ga->ga_data), recursive); + else { + if (first_lnum > found_fp->fd_top + found_off) + first_lnum = found_fp->fd_top + found_off; + if (last_lnum < lnum) + last_lnum = lnum; + if (!did_one) + parseMarker(curwin); + deleteFoldMarkers(found_fp, recursive, found_off); + } + did_one = TRUE; + + /* redraw window */ + changed_window_setting(); + } + } + if (!did_one) { + EMSG(_(e_nofold)); + /* Force a redraw to remove the Visual highlighting. */ + if (had_visual) + redraw_curbuf_later(INVERTED); + } else + /* Deleting markers may make cursor column invalid. */ + check_cursor_col(); + + if (last_lnum > 0) + changed_lines(first_lnum, (colnr_T)0, last_lnum, 0L); +} + +/* clearFolding() {{{2 */ +/* + * Remove all folding for window "win". + */ +void clearFolding(win_T *win) +{ + deleteFoldRecurse(&win->w_folds); + win->w_foldinvalid = FALSE; +} + +/* foldUpdate() {{{2 */ +/* + * Update folds for changes in the buffer of a window. + * Note that inserted/deleted lines must have already been taken care of by + * calling foldMarkAdjust(). + * The changes in lines from top to bot (inclusive). + */ +void foldUpdate(win_T *wp, linenr_T top, linenr_T bot) +{ + fold_T *fp; + + /* Mark all folds from top to bot as maybe-small. */ + (void)foldFind(&curwin->w_folds, top, &fp); + while (fp < (fold_T *)curwin->w_folds.ga_data + curwin->w_folds.ga_len + && fp->fd_top < bot) { + fp->fd_small = MAYBE; + ++fp; + } + + if (foldmethodIsIndent(wp) + || foldmethodIsExpr(wp) + || foldmethodIsMarker(wp) + || foldmethodIsDiff(wp) + || foldmethodIsSyntax(wp)) { + int save_got_int = got_int; + + /* reset got_int here, otherwise it won't work */ + got_int = FALSE; + foldUpdateIEMS(wp, top, bot); + got_int |= save_got_int; + } +} + +/* foldUpdateAll() {{{2 */ +/* + * Update all lines in a window for folding. + * Used when a fold setting changes or after reloading the buffer. + * The actual updating is postponed until fold info is used, to avoid doing + * every time a setting is changed or a syntax item is added. + */ +void foldUpdateAll(win_T *win) +{ + win->w_foldinvalid = TRUE; + redraw_win_later(win, NOT_VALID); +} + +/* foldMoveTo() {{{2 */ +/* + * If "updown" is FALSE: Move to the start or end of the fold. + * If "updown" is TRUE: move to fold at the same level. + * If not moved return FAIL. + */ +int +foldMoveTo ( + int updown, + int dir, /* FORWARD or BACKWARD */ + long count +) +{ + long n; + int retval = FAIL; + linenr_T lnum_off; + linenr_T lnum_found; + linenr_T lnum; + int use_level; + int maybe_small; + garray_T *gap; + fold_T *fp; + int level; + int last; + + checkupdate(curwin); + + /* Repeat "count" times. */ + for (n = 0; n < count; ++n) { + /* Find nested folds. Stop when a fold is closed. The deepest fold + * that moves the cursor is used. */ + lnum_off = 0; + gap = &curwin->w_folds; + use_level = FALSE; + maybe_small = FALSE; + lnum_found = curwin->w_cursor.lnum; + level = 0; + last = FALSE; + for (;; ) { + if (!foldFind(gap, curwin->w_cursor.lnum - lnum_off, &fp)) { + if (!updown) + break; + + /* When moving up, consider a fold above the cursor; when + * moving down consider a fold below the cursor. */ + if (dir == FORWARD) { + if (fp - (fold_T *)gap->ga_data >= gap->ga_len) + break; + --fp; + } else { + if (fp == (fold_T *)gap->ga_data) + break; + } + /* don't look for contained folds, they will always move + * the cursor too far. */ + last = TRUE; + } + + if (!last) { + /* Check if this fold is closed. */ + if (check_closed(curwin, fp, &use_level, level, + &maybe_small, lnum_off)) + last = TRUE; + + /* "[z" and "]z" stop at closed fold */ + if (last && !updown) + break; + } + + if (updown) { + if (dir == FORWARD) { + /* to start of next fold if there is one */ + if (fp + 1 - (fold_T *)gap->ga_data < gap->ga_len) { + lnum = fp[1].fd_top + lnum_off; + if (lnum > curwin->w_cursor.lnum) + lnum_found = lnum; + } + } else { + /* to end of previous fold if there is one */ + if (fp > (fold_T *)gap->ga_data) { + lnum = fp[-1].fd_top + lnum_off + fp[-1].fd_len - 1; + if (lnum < curwin->w_cursor.lnum) + lnum_found = lnum; + } + } + } else { + /* Open fold found, set cursor to its start/end and then check + * nested folds. */ + if (dir == FORWARD) { + lnum = fp->fd_top + lnum_off + fp->fd_len - 1; + if (lnum > curwin->w_cursor.lnum) + lnum_found = lnum; + } else { + lnum = fp->fd_top + lnum_off; + if (lnum < curwin->w_cursor.lnum) + lnum_found = lnum; + } + } + + if (last) + break; + + /* Check nested folds (if any). */ + gap = &fp->fd_nested; + lnum_off += fp->fd_top; + ++level; + } + if (lnum_found != curwin->w_cursor.lnum) { + if (retval == FAIL) + setpcmark(); + curwin->w_cursor.lnum = lnum_found; + curwin->w_cursor.col = 0; + retval = OK; + } else + break; + } + + return retval; +} + +/* foldInitWin() {{{2 */ +/* + * Init the fold info in a new window. + */ +void foldInitWin(win_T *new_win) +{ + ga_init(&new_win->w_folds, (int)sizeof(fold_T), 10); +} + +/* find_wl_entry() {{{2 */ +/* + * Find an entry in the win->w_lines[] array for buffer line "lnum". + * Only valid entries are considered (for entries where wl_valid is FALSE the + * line number can be wrong). + * Returns index of entry or -1 if not found. + */ +int find_wl_entry(win_T *win, linenr_T lnum) +{ + int i; + + for (i = 0; i < win->w_lines_valid; ++i) + if (win->w_lines[i].wl_valid) { + if (lnum < win->w_lines[i].wl_lnum) + return -1; + if (lnum <= win->w_lines[i].wl_lastlnum) + return i; + } + return -1; +} + +/* foldAdjustVisual() {{{2 */ +/* + * Adjust the Visual area to include any fold at the start or end completely. + */ +void foldAdjustVisual(void) +{ + pos_T *start, *end; + char_u *ptr; + + if (!VIsual_active || !hasAnyFolding(curwin)) + return; + + if (ltoreq(VIsual, curwin->w_cursor)) { + start = &VIsual; + end = &curwin->w_cursor; + } else { + start = &curwin->w_cursor; + end = &VIsual; + } + if (hasFolding(start->lnum, &start->lnum, NULL)) + start->col = 0; + if (hasFolding(end->lnum, NULL, &end->lnum)) { + ptr = ml_get(end->lnum); + end->col = (colnr_T)STRLEN(ptr); + if (end->col > 0 && *p_sel == 'o') + --end->col; + /* prevent cursor from moving on the trail byte */ + if (has_mbyte) + mb_adjust_cursor(); + } +} + +/* cursor_foldstart() {{{2 */ +/* + * Move the cursor to the first line of a closed fold. + */ +void foldAdjustCursor(void) +{ + (void)hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL); +} + +/* Internal functions for "fold_T" {{{1 */ +/* cloneFoldGrowArray() {{{2 */ +/* + * Will "clone" (i.e deep copy) a garray_T of folds. + */ +void cloneFoldGrowArray(garray_T *from, garray_T *to) +{ + int i; + fold_T *from_p; + fold_T *to_p; + + ga_init(to, from->ga_itemsize, from->ga_growsize); + + if (from->ga_len == 0) + return; + + ga_grow(to, from->ga_len); + + from_p = (fold_T *)from->ga_data; + to_p = (fold_T *)to->ga_data; + + for (i = 0; i < from->ga_len; i++) { + to_p->fd_top = from_p->fd_top; + to_p->fd_len = from_p->fd_len; + to_p->fd_flags = from_p->fd_flags; + to_p->fd_small = from_p->fd_small; + cloneFoldGrowArray(&from_p->fd_nested, &to_p->fd_nested); + ++to->ga_len; + ++from_p; + ++to_p; + } +} + +/* foldFind() {{{2 */ +/* + * Search for line "lnum" in folds of growarray "gap". + * Set *fpp to the fold struct for the fold that contains "lnum" or + * the first fold below it (careful: it can be beyond the end of the array!). + * Returns FALSE when there is no fold that contains "lnum". + */ +static int foldFind(garray_T *gap, linenr_T lnum, fold_T **fpp) +{ + linenr_T low, high; + fold_T *fp; + int i; + + /* + * Perform a binary search. + * "low" is lowest index of possible match. + * "high" is highest index of possible match. + */ + fp = (fold_T *)gap->ga_data; + low = 0; + high = gap->ga_len - 1; + while (low <= high) { + i = (low + high) / 2; + if (fp[i].fd_top > lnum) + /* fold below lnum, adjust high */ + high = i - 1; + else if (fp[i].fd_top + fp[i].fd_len <= lnum) + /* fold above lnum, adjust low */ + low = i + 1; + else { + /* lnum is inside this fold */ + *fpp = fp + i; + return TRUE; + } + } + *fpp = fp + low; + return FALSE; +} + +/* foldLevelWin() {{{2 */ +/* + * Return fold level at line number "lnum" in window "wp". + */ +static int foldLevelWin(win_T *wp, linenr_T lnum) +{ + fold_T *fp; + linenr_T lnum_rel = lnum; + int level = 0; + garray_T *gap; + + /* Recursively search for a fold that contains "lnum". */ + gap = &wp->w_folds; + for (;; ) { + if (!foldFind(gap, lnum_rel, &fp)) + break; + /* Check nested folds. Line number is relative to containing fold. */ + gap = &fp->fd_nested; + lnum_rel -= fp->fd_top; + ++level; + } + + return level; +} + +/* checkupdate() {{{2 */ +/* + * Check if the folds in window "wp" are invalid and update them if needed. + */ +static void checkupdate(win_T *wp) +{ + if (wp->w_foldinvalid) { + foldUpdate(wp, (linenr_T)1, (linenr_T)MAXLNUM); /* will update all */ + wp->w_foldinvalid = FALSE; + } +} + +/* setFoldRepeat() {{{2 */ +/* + * Open or close fold for current window at line "lnum". + * Repeat "count" times. + */ +static void setFoldRepeat(linenr_T lnum, long count, int do_open) +{ + int done; + long n; + + for (n = 0; n < count; ++n) { + done = DONE_NOTHING; + (void)setManualFold(lnum, do_open, FALSE, &done); + if (!(done & DONE_ACTION)) { + /* Only give an error message when no fold could be opened. */ + if (n == 0 && !(done & DONE_FOLD)) + EMSG(_(e_nofold)); + break; + } + } +} + +/* setManualFold() {{{2 */ +/* + * Open or close the fold in the current window which contains "lnum". + * Also does this for other windows in diff mode when needed. |