2859 lines · 65.5 KB · C
RawDownload
/* vi:set ts=8 sts=4 sw=4: * * 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. *//* * move.c: Functions for moving the cursor and scrolling text. * * There are two ways to move the cursor: * 1. Move the cursor directly, the text is scrolled to keep the cursor in the *    window. * 2. Scroll the text, the cursor is moved into the text visible in the *    window. * The 'scrolloff' option makes this a bit complicated. */ #include "vim.h" static void comp_botline __ARGS((win_T *wp));static int scrolljump_value __ARGS((void));static int check_top_offset __ARGS((void));static void curs_rows __ARGS((win_T *wp, int do_botline));static void validate_botline_win __ARGS((win_T *wp));static void validate_cheight __ARGS((void)); typedef struct{    linenr_T	    lnum;	/* line number */#ifdef FEAT_DIFF    int		    fill;	/* filler lines */#endif    int		    height;	/* height of added line */} lineoff_T; static void topline_back __ARGS((lineoff_T *lp));static void botline_forw __ARGS((lineoff_T *lp));#ifdef FEAT_DIFFstatic void botline_topline __ARGS((lineoff_T *lp));static void topline_botline __ARGS((lineoff_T *lp));static void max_topfill __ARGS((void));#endif /* * Compute wp->w_botline for the current wp->w_topline.  Can be called after * wp->w_topline changed. */    static voidcomp_botline(wp)    win_T	*wp;{    int		n;    linenr_T	lnum;    int		done;#ifdef FEAT_FOLDING    linenr_T    last;    int		folded;#endif     /*     * If w_cline_row is valid, start there.     * Otherwise have to start at w_topline.     */    check_cursor_moved(wp);    if (wp->w_valid & VALID_CROW)    {	lnum = wp->w_cursor.lnum;	done = wp->w_cline_row;    }    else    {	lnum = wp->w_topline;	done = 0;    }     for ( ; lnum <= wp->w_buffer->b_ml.ml_line_count; ++lnum)    {#ifdef FEAT_FOLDING	last = lnum;	folded = FALSE;	if (hasFoldingWin(wp, lnum, NULL, &last, TRUE, NULL))	{	    n = 1;	    folded = TRUE;	}	else#endif#ifdef FEAT_DIFF	    if (lnum == wp->w_topline)		n = plines_win_nofill(wp, lnum, TRUE) + wp->w_topfill;	    else#endif		n = plines_win(wp, lnum, TRUE);	if (#ifdef FEAT_FOLDING		lnum <= wp->w_cursor.lnum && last >= wp->w_cursor.lnum#else		lnum == wp->w_cursor.lnum#endif	   )	{	    wp->w_cline_row = done;	    wp->w_cline_height = n;#ifdef FEAT_FOLDING	    wp->w_cline_folded = folded;#endif	    wp->w_valid |= (VALID_CROW|VALID_CHEIGHT);	}	if (done + n > wp->w_height)	    break;	done += n;#ifdef FEAT_FOLDING	lnum = last;#endif    }     /* wp->w_botline is the line that is just below the window */    wp->w_botline = lnum;    wp->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;     set_empty_rows(wp, done);} /* * Update curwin->w_topline and redraw if necessary. * Used to update the screen before printing a message. */    voidupdate_topline_redraw(){    update_topline();    if (must_redraw)	update_screen(0);} /* * Update curwin->w_topline to move the cursor onto the screen. */    voidupdate_topline(){    long	line_count;    int		halfheight;    int		n;    linenr_T	old_topline;#ifdef FEAT_DIFF    int		old_topfill;#endif#ifdef FEAT_FOLDING    linenr_T	lnum;#endif    int		check_topline = FALSE;    int		check_botline = FALSE;#ifdef FEAT_MOUSE    int		save_so = p_so;#endif     if (!screen_valid(TRUE))	return;     check_cursor_moved(curwin);    if (curwin->w_valid & VALID_TOPLINE)	return; #ifdef FEAT_MOUSE    /* When dragging with the mouse, don't scroll that quickly */    if (mouse_dragging > 0)	p_so = mouse_dragging - 1;#endif     old_topline = curwin->w_topline;#ifdef FEAT_DIFF    old_topfill = curwin->w_topfill;#endif     /*     * If the buffer is empty, always set topline to 1.     */    if (bufempty())		/* special case - file is empty */    {	if (curwin->w_topline != 1)	    redraw_later(NOT_VALID);	curwin->w_topline = 1;#ifdef FEAT_DIFF	curwin->w_topfill = 0;#endif	curwin->w_botline = 2;	curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;#ifdef FEAT_SCROLLBIND	curwin->w_scbind_pos = 1;#endif    }     /*     * If the cursor is above or near the top of the window, scroll the window     * to show the line the cursor is in, with 'scrolloff' context.     */    else    {	if (curwin->w_topline > 1)	{	    /* If the cursor is above topline, scrolling is always needed.	     * If the cursor is far below topline and there is no folding,	     * scrolling down is never needed. */	    if (curwin->w_cursor.lnum < curwin->w_topline)		check_topline = TRUE;	    else if (check_top_offset())		check_topline = TRUE;	}#ifdef FEAT_DIFF	    /* Check if there are more filler lines than allowed. */	if (!check_topline && curwin->w_topfill > diff_check_fill(curwin,							   curwin->w_topline))	    check_topline = TRUE;#endif 	if (check_topline)	{	    halfheight = curwin->w_height / 2 - 1;	    if (halfheight < 2)		halfheight = 2; #ifdef FEAT_FOLDING	    if (hasAnyFolding(curwin))	    {		/* Count the number of logical lines between the cursor and		 * topline + p_so (approximation of how much will be		 * scrolled). */		n = 0;		for (lnum = curwin->w_cursor.lnum;				      lnum < curwin->w_topline + p_so; ++lnum)		{		    ++n;		    /* stop at end of file or when we know we are far off */		    if (lnum >= curbuf->b_ml.ml_line_count || n >= halfheight)			break;		    (void)hasFolding(lnum, NULL, &lnum);		}	    }	    else#endif		n = curwin->w_topline + p_so - curwin->w_cursor.lnum; 	    /* If we weren't very close to begin with, we scroll to put the	     * cursor in the middle of the window.  Otherwise put the cursor	     * near the top of the window. */	    if (n >= halfheight)		scroll_cursor_halfway(FALSE);	    else	    {		scroll_cursor_top(scrolljump_value(), FALSE);		check_botline = TRUE;	    }	} 	else	{#ifdef FEAT_FOLDING	    /* Make sure topline is the first line of a fold. */	    (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);#endif	    check_botline = TRUE;	}    }     /*     * If the cursor is below the bottom of the window, scroll the window     * to put the cursor on the window.     * When w_botline is invalid, recompute it first, to avoid a redraw later.     * If w_botline was approximated, we might need a redraw later in a few     * cases, but we don't want to spend (a lot of) time recomputing w_botline     * for every small change.     */    if (check_botline)    {	if (!(curwin->w_valid & VALID_BOTLINE_AP))	    validate_botline(); 	if (curwin->w_botline <= curbuf->b_ml.ml_line_count)	{	    if (curwin->w_cursor.lnum < curwin->w_botline		    && ((long)curwin->w_cursor.lnum					     >= (long)curwin->w_botline - p_so#ifdef FEAT_FOLDING			|| hasAnyFolding(curwin)#endif			))	    {		lineoff_T	loff; 		/* Cursor is above botline, check if there are 'scrolloff'		 * window lines below the cursor.  If not, need to scroll. */		n = curwin->w_empty_rows;		loff.lnum = curwin->w_cursor.lnum;#ifdef FEAT_FOLDING		/* In a fold go to its last line. */		(void)hasFolding(loff.lnum, NULL, &loff.lnum);#endif#ifdef FEAT_DIFF		loff.fill = 0;		n += curwin->w_filler_rows;#endif		loff.height = 0;		while (loff.lnum < curwin->w_botline#ifdef FEAT_DIFF			&& (loff.lnum + 1 < curwin->w_botline || loff.fill == 0)#endif			)		{		    n += loff.height;		    if (n >= p_so)			break;		    botline_forw(&loff);		}		if (n >= p_so)		    /* sufficient context, no need to scroll */		    check_botline = FALSE;	    }	    if (check_botline)	    {#ifdef FEAT_FOLDING		if (hasAnyFolding(curwin))		{		    /* Count the number of logical lines between the cursor and		     * botline - p_so (approximation of how much will be		     * scrolled). */		    line_count = 0;		    for (lnum = curwin->w_cursor.lnum;				     lnum >= curwin->w_botline - p_so; --lnum)		    {			++line_count;			/* stop at end of file or when we know we are far off */			if (lnum <= 0 || line_count > curwin->w_height + 1)			    break;			(void)hasFolding(lnum, &lnum, NULL);		    }		}		else#endif		    line_count = curwin->w_cursor.lnum - curwin->w_botline								   + 1 + p_so;		if (line_count <= curwin->w_height + 1)		    scroll_cursor_bot(scrolljump_value(), FALSE);		else		    scroll_cursor_halfway(FALSE);	    }	}    }    curwin->w_valid |= VALID_TOPLINE;     /*     * Need to redraw when topline changed.     */    if (curwin->w_topline != old_topline#ifdef FEAT_DIFF	    || curwin->w_topfill != old_topfill#endif	    )    {	dollar_vcol = 0;	if (curwin->w_skipcol != 0)	{	    curwin->w_skipcol = 0;	    redraw_later(NOT_VALID);	}	else	    redraw_later(VALID);	/* May need to set w_skipcol when cursor in w_topline. */	if (curwin->w_cursor.lnum == curwin->w_topline)	    validate_cursor();    } #ifdef FEAT_MOUSE    p_so = save_so;#endif} /* * Return the scrolljump value to use for the current window. * When 'scrolljump' is positive use it as-is. * When 'scrolljump' is negative use it as a percentage of the window height. */    static intscrolljump_value(){    if (p_sj >= 0)	return (int)p_sj;    return (curwin->w_height * -p_sj) / 100;} /* * Return TRUE when there are not 'scrolloff' lines above the cursor for the * current window. */    static intcheck_top_offset(){    lineoff_T	loff;    int		n;     if (curwin->w_cursor.lnum < curwin->w_topline + p_so#ifdef FEAT_FOLDING		    || hasAnyFolding(curwin)#endif	    )    {	loff.lnum = curwin->w_cursor.lnum;#ifdef FEAT_DIFF	loff.fill = 0;	n = curwin->w_topfill;	    /* always have this context */#else	n = 0;#endif	/* Count the visible screen lines above the cursor line. */	while (n < p_so)	{	    topline_back(&loff);	    /* Stop when included a line above the window. */	    if (loff.lnum < curwin->w_topline#ifdef FEAT_DIFF		    || (loff.lnum == curwin->w_topline && loff.fill > 0)#endif		    )		break;	    n += loff.height;	}	if (n < p_so)	    return TRUE;    }    return FALSE;}     voidupdate_curswant(){    if (curwin->w_set_curswant)    {	validate_virtcol();	curwin->w_curswant = curwin->w_virtcol;	curwin->w_set_curswant = FALSE;    }} /* * Check if the cursor has moved.  Set the w_valid flag accordingly. */    voidcheck_cursor_moved(wp)    win_T	*wp;{    if (wp->w_cursor.lnum != wp->w_valid_cursor.lnum)    {	wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL				     |VALID_CHEIGHT|VALID_CROW|VALID_TOPLINE);	wp->w_valid_cursor = wp->w_cursor;	wp->w_valid_leftcol = wp->w_leftcol;    }    else if (wp->w_cursor.col != wp->w_valid_cursor.col	     || wp->w_leftcol != wp->w_valid_leftcol#ifdef FEAT_VIRTUALEDIT	     || wp->w_cursor.coladd != wp->w_valid_cursor.coladd#endif	     )    {	wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL);	wp->w_valid_cursor.col = wp->w_cursor.col;	wp->w_valid_leftcol = wp->w_leftcol;#ifdef FEAT_VIRTUALEDIT	wp->w_valid_cursor.coladd = wp->w_cursor.coladd;#endif    }} /* * Call this function when some window settings have changed, which require * the cursor position, botline and topline to be recomputed and the window to * be redrawn.  E.g, when changing the 'wrap' option or folding. */    voidchanged_window_setting(){    changed_window_setting_win(curwin);}     voidchanged_window_setting_win(wp)    win_T	*wp;{    wp->w_lines_valid = 0;    changed_line_abv_curs_win(wp);    wp->w_valid &= ~(VALID_BOTLINE|VALID_BOTLINE_AP|VALID_TOPLINE);    redraw_win_later(wp, NOT_VALID);} /* * Set wp->w_topline to a certain number. */    void

Lines 1–500 of 2859.