3338 lines · 81.8 KB · C
RawDownload
/* vim:set ts=8 sts=4 sw=4: * vim600: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 "vim.h" #if defined(FEAT_FOLDING) || defined(PROTO) /* 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 __ARGS((win_T *wp));static int checkCloseRec __ARGS((garray_T *gap, linenr_T lnum, int level));static int foldFind __ARGS((garray_T *gap, linenr_T lnum, fold_T **fpp));static int foldLevelWin __ARGS((win_T *wp, linenr_T lnum));static void checkupdate __ARGS((win_T *wp));static void setFoldRepeat __ARGS((linenr_T lnum, long count, int open));static linenr_T setManualFold __ARGS((linenr_T lnum, int opening, int recurse, int *donep));static linenr_T setManualFoldWin __ARGS((win_T *wp, linenr_T lnum, int opening, int recurse, int *donep));static void foldOpenNested __ARGS((fold_T *fpr));static void deleteFoldEntry __ARGS((garray_T *gap, int idx, int recursive));static void foldMarkAdjustRecurse __ARGS((garray_T *gap, linenr_T line1, linenr_T line2, long amount, long amount_after));static int getDeepestNestingRecurse __ARGS((garray_T *gap));static int check_closed __ARGS((win_T *win, fold_T *fp, int *use_levelp, int level, int *maybe_smallp, linenr_T lnum_off));static void checkSmall __ARGS((win_T *wp, fold_T *fp, linenr_T lnum_off));static void setSmallMaybe __ARGS((garray_T *gap));static void foldCreateMarkers __ARGS((linenr_T start, linenr_T end));static void foldAddMarker __ARGS((linenr_T lnum, char_u *marker, int markerlen));static void deleteFoldMarkers __ARGS((fold_T *fp, int recursive, linenr_T lnum_off));static void foldDelMarker __ARGS((linenr_T lnum, char_u *marker, int markerlen));static void foldUpdateIEMS __ARGS((win_T *wp, linenr_T top, linenr_T bot));static void parseMarker __ARGS((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 */#if defined(FEAT_WINDOWS) || defined(PROTO)/* * Copy that folding state from window "wp_from" to window "wp_to". */    voidcopyFoldingState(wp_from, wp_to)    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);}#endif /* hasAnyFolding() {{{2 *//* * Return TRUE if there may be folded lines in the current window. */    inthasAnyFolding(win)    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). */    inthasFolding(lnum, firstp, lastp)    linenr_T	lnum;    linenr_T	*firstp;    linenr_T	*lastp;{    return hasFoldingWin(curwin, lnum, firstp, lastp, TRUE, NULL);} /* hasFoldingWin() {{{2 */    inthasFoldingWin(win, lnum, firstp, lastp, cache, infop)    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. */    intfoldLevel(lnum)    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. */    intlineFolded(win, lnum)    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. */    longfoldedCount(win, lnum, infop)    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" */    intfoldmethodIsManual(wp)    win_T	*wp;{    return (wp->w_p_fdm[3] == 'u');} /* foldmethodIsIndent() {{{2 *//* * Return TRUE if 'foldmethod' is "indent" */    intfoldmethodIsIndent(wp)    win_T	*wp;{    return (wp->w_p_fdm[0] == 'i');} /* foldmethodIsExpr() {{{2 *//* * Return TRUE if 'foldmethod' is "expr" */    intfoldmethodIsExpr(wp)    win_T	*wp;{    return (wp->w_p_fdm[1] == 'x');} /* foldmethodIsMarker() {{{2 *//* * Return TRUE if 'foldmethod' is "marker" */    intfoldmethodIsMarker(wp)    win_T	*wp;{    return (wp->w_p_fdm[2] == 'r');} /* foldmethodIsSyntax() {{{2 *//* * Return TRUE if 'foldmethod' is "syntax" */    intfoldmethodIsSyntax(wp)    win_T	*wp;{    return (wp->w_p_fdm[0] == 's');} /* foldmethodIsDiff() {{{2 *//* * Return TRUE if 'foldmethod' is "diff" */    intfoldmethodIsDiff(wp)    win_T	*wp;{    return (wp->w_p_fdm[0] == 'd');} /* closeFold() {{{2 *//* * Close fold for current window at line "lnum". * Repeat "count" times. */    voidcloseFold(lnum, count)    linenr_T	lnum;    long	count;{    setFoldRepeat(lnum, count, FALSE);} /* closeFoldRecurse() {{{2 *//* * Close fold for current window at line "lnum" recursively. */    voidcloseFoldRecurse(lnum)    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. */    voidopFoldRange(first, last, opening, recurse, had_visual)    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));#ifdef FEAT_VISUAL    /* Force a redraw to remove the Visual highlighting. */    if (had_visual)	redraw_curbuf_later(INVERTED);#endif} /* openFold() {{{2 *//* * Open fold for current window at line "lnum". * Repeat "count" times. */    voidopenFold(lnum, count)    linenr_T	lnum;    long	count;{    setFoldRepeat(lnum, count, TRUE);} /* openFoldRecurse() {{{2 *//* * Open fold for current window at line "lnum" recursively. */    voidopenFoldRecurse(lnum)    linenr_T	lnum;{    (void)setManualFold(lnum, TRUE, TRUE, NULL);} /* foldOpenCursor() {{{2 *//* * Open folds until the cursor line is not in a closed fold. */    voidfoldOpenCursor(){    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. */    voidnewFoldLevel(){    newFoldLevelWin(curwin); #ifdef FEAT_DIFF    if (foldmethodIsDiff(curwin) && curwin->w_p_scb)    {	win_T	    *wp; 	/*	 * Set the same foldlevel in other windows in diff mode.	 */

Lines 1–500 of 3338.