This is screen.c in view mode; [Download] [Up]
/* vi:set ts=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.
*/
/*
* screen.c: code for displaying on the screen
*/
#include "vim.h"
#include "globals.h"
#include "proto.h"
#include "option.h"
/*
* The attributes that are actually active for writing to the screen.
*/
static int screen_attr = 0;
#ifdef RIGHTLEFT
static int rightleft = 0; /* set to 1 for right to left in screen_fill */
#endif
/*
* Positioning the cursor is reduced by remembering the last position.
* Mostly used by windgoto() and screen_char().
*/
static int screen_cur_row, screen_cur_col; /* last known cursor position */
/*
* When highlighting matches for the last use search pattern, search_hl_prog
* points to the regexp program for it, and search_hl_attr contains the
* attributes to be used.
*/
vim_regexp *search_hl_prog = NULL;
int search_hl_attr;
/*
* Flags for w_valid.
* These are suppose to be used only in screen.c. From other files, use the
* functions that set or reset them.
*
* VALID_BOTLINE VALID_BOTLINE_AP
* on on w_botline valid
* off on w_botline approximated
* off off w_botline not valid
* on off not possible
*/
#define VALID_WROW 0x01 /* w_wrow (window row) is valid */
#define VALID_WCOL 0x02 /* w_wcol (window col) is valid */
#define VALID_VIRTCOL 0x04 /* w_virtcol (file col) is valid */
#define VALID_CHEIGHT 0x08 /* w_cline_height is valid */
#define VALID_CROW 0x10 /* w_cline_row is valid */
#define VALID_BOTLINE 0x20 /* w_botine and w_empty_rows are valid */
#define VALID_BOTLINE_AP 0x40 /* w_botine is approximated */
/*
* Buffer for one screen line.
*/
static char_u *current_LinePointer;
static void win_update __ARGS((WIN *wp));
static int win_line __ARGS((WIN *, linenr_t, int, int, int attr));
#ifdef RIGHTLEFT
static void screen_line __ARGS((int row, int endcol, int clear_rest, int rightleft));
#define SCREEN_LINE(r, e, c, rl) screen_line((r), (e), (c), (rl))
#else
static void screen_line __ARGS((int row, int endcol, int clear_rest));
#define SCREEN_LINE(r, e, c, rl) screen_line((r), (e), (c))
#endif
static void start_search_hl __ARGS((void));
static void screen_start_highlight __ARGS((int attr));
static void comp_botline __ARGS((void));
static void screen_char __ARGS((char_u *, int, int));
static void screenclear2 __ARGS((void));
static void lineclear __ARGS((char_u *p));
static void check_cursor_moved __ARGS((WIN *wp));
static void curs_rows __ARGS((int do_botline));
static void validate_virtcol_win __ARGS((WIN *wp));
static int screen_ins_lines __ARGS((int, int, int, int));
static int highlight_status __ARGS((int *attr));
static void intro_message __ARGS((void));
/*
* update_screenline() - like update_screen() but only for cursor line
*
* Must only be called when something in the cursor line has changed (e.g.
* character inserted or deleted).
*
* Check if the size of the cursor line has changed since the last screen
* update. If it did change, lines below the cursor will move up or down and
* we need to call the routine update_screen() to examine the entire screen.
*/
void
update_screenline()
{
int row;
int old_cline_height;
if (!screen_valid(TRUE))
return;
if (must_redraw) /* must redraw whole screen */
{
update_screen(must_redraw);
return;
}
if (!redrawing())
{
must_redraw = NOT_VALID; /* remember to update later */
return;
}
/*
* If the screen has scrolled, or some lines after the cursor line have
* been invalidated, call update_screen().
*/
if (curwin->w_lsize_valid <= curwin->w_cursor.lnum - curwin->w_topline ||
curwin->w_lsize_lnum[0] != curwin->w_topline)
{
update_screen(VALID_TO_CURSCHAR);
return;
}
/*
* Check if the cursor line is still at the same position. Be aware of
* the cursor having moved around, w_cline_row may be invalid, use the
* values from w_lsize[] by calling curs_rows().
*/
check_cursor_moved(curwin);
if (!(curwin->w_valid & VALID_CROW))
curs_rows(FALSE);
old_cline_height = curwin->w_cline_height;
if (p_hls)
start_search_hl();
/*
* w_virtcol needs to be valid.
*/
validate_virtcol();
cursor_off();
row = win_line(curwin, curwin->w_cursor.lnum,
curwin->w_cline_row, curwin->w_height, highlight_attr[HLF_V]);
display_hint = HINT_NONE;
if (search_hl_prog != NULL)
{
vim_free(search_hl_prog);
search_hl_prog = NULL;
}
if (row == curwin->w_height + 1) /* line too long for window */
{
if (curwin->w_topline < curwin->w_cursor.lnum)
{
/*
* Window needs to be scrolled up to show the cursor line.
* We know w_botline was valid before the change, so it should now
* be one less. This removes the need to recompute w_botline in
* update_topline().
*/
--curwin->w_botline;
curwin->w_valid |= VALID_BOTLINE_AP;
}
update_topline();
update_screen(VALID_TO_CURSCHAR);
}
else if (!dollar_vcol)
{
/*
* If the cursor line changed size, delete or insert screen lines and
* redraw the rest of the window.
*/
if (old_cline_height != curwin->w_cline_height)
{
if (curwin->w_cline_height < old_cline_height)
win_del_lines(curwin, row,
old_cline_height - curwin->w_cline_height, FALSE, TRUE);
else
win_ins_lines(curwin,
curwin->w_cline_row + curwin->w_cline_height,
curwin->w_cline_height - old_cline_height, FALSE, TRUE);
update_screen(VALID_TO_CURSCHAR);
}
#ifdef SYNTAX_HL
/*
* If syntax lost its sync, have to redraw the following lines.
*/
else if (syntax_present(curbuf) && row < cmdline_row &&
syntax_check_changed(curwin->w_cursor.lnum + 1))
update_screen(VALID_TO_CURSCHAR);
#endif
else if (clear_cmdline || redraw_cmdline)
showmode(); /* clear cmdline, show mode and ruler */
}
}
/*
* Redraw the screen later, with UpdateScreen(type).
* Set must_redraw only of not already set to a higher value.
* e.g. if must_redraw is CLEAR, type == NOT_VALID will do nothing.
*/
void
redraw_later(type)
int type;
{
if (must_redraw < type)
must_redraw = type;
}
/*
* Mark all windows to be udpated later.
*/
void
redraw_all_later(type)
int type;
{
WIN *wp;
for (wp = firstwin; wp; wp = wp->w_next)
if (wp->w_redr_type < type)
wp->w_redr_type = type;
redraw_later(type);
}
/*
* Mark all windows that are editing the current buffer to be udpated later.
*/
void
redraw_curbuf_later(type)
int type;
{
WIN *wp;
for (wp = firstwin; wp; wp = wp->w_next)
if (wp->w_redr_type < type && wp->w_buffer == curbuf)
wp->w_redr_type = type;
redraw_later(type);
}
/*
* update all windows that are editing the current buffer
*/
void
update_curbuf(type)
int type;
{
redraw_curbuf_later(type);
update_screen(type);
}
/*
* update_screen()
*
* Based on the current value of curwin->w_topline, transfer a screenfull
* of stuff from Filemem to NextScreen, and update curwin->w_botline.
*/
void
update_screen(type)
int type;
{
WIN *wp;
static int did_intro = FALSE;
if (!screen_valid(TRUE))
return;
dollar_vcol = 0;
if (must_redraw)
{
if (type < must_redraw) /* use maximal type */
type = must_redraw;
must_redraw = 0;
}
if (curwin->w_lsize_valid == 0 && type < NOT_VALID)
type = NOT_VALID;
if (!redrawing())
{
must_redraw = type; /* remember type for next time */
curwin->w_redr_type = type;
curwin->w_lsize_valid = 0; /* don't use w_lsize[] now */
return;
}
/*
* if the screen was scrolled up when displaying a message, scroll it down
*/
if (msg_scrolled)
{
clear_cmdline = TRUE;
if (msg_scrolled > Rows - 5) /* clearing is faster */
type = CLEAR;
else if (type != CLEAR)
{
if (screen_ins_lines(0, 0, msg_scrolled, (int)Rows) == FAIL)
type = CLEAR;
win_rest_invalid(firstwin); /* should do only first/last few */
}
msg_scrolled = 0;
need_wait_return = FALSE;
}
/*
* reset cmdline_row now (may have been changed temporarily)
*/
compute_cmdrow();
if (type == CLEAR) /* first clear screen */
{
screenclear(); /* will reset clear_cmdline */
type = NOT_VALID;
}
if (clear_cmdline) /* first clear cmdline */
{
check_for_delay(FALSE);
msg_row = cmdline_row;
msg_col = 0;
msg_clr_eos(); /* will reset clear_cmdline */
}
/*
* Only start redrawing if there is really something to do.
*/
if (!((type == VALID && curwin->w_topline == curwin->w_lsize_lnum[0]) ||
(type == INVERTED &&
curwin->w_old_cursor_lnum == curwin->w_cursor.lnum &&
(curwin->w_valid & VALID_VIRTCOL) &&
curwin->w_old_curswant == curwin->w_curswant)))
{
/*
* go from top to bottom through the windows, redrawing the ones that
* need it
*/
curwin->w_redr_type = type;
cursor_off();
if (p_hls)
start_search_hl();
for (wp = firstwin; wp; wp = wp->w_next)
{
if (wp->w_redr_type)
win_update(wp);
if (wp->w_redr_status)
win_redr_status(wp);
}
if (search_hl_prog != NULL)
{
vim_free(search_hl_prog);
search_hl_prog = NULL;
}
}
if (redraw_cmdline)
showmode();
display_hint = HINT_NONE;
/* May put up an introductory message when not editing a file */
if (!did_intro && bufempty() && curbuf->b_fname == NULL)
intro_message();
did_intro = TRUE;
}
#ifdef USE_GUI
/*
* Update a single window, its status line and maybe the command line msg.
* Used for the GUI scrollbar.
*/
void
updateWindow(wp)
WIN *wp;
{
cursor_off();
if (p_hls)
start_search_hl();
win_update(wp);
if (wp->w_redr_status)
win_redr_status(wp);
if (redraw_cmdline)
showmode();
if (search_hl_prog != NULL)
{
vim_free(search_hl_prog);
search_hl_prog = NULL;
}
}
#endif
/*
* Update all windows for the current buffer, except curwin.
* Used after modifying text, to update the other windows on the same buffer.
*/
void
update_other_win()
{
WIN *wp;
int first = TRUE;
for (wp = firstwin; wp; wp = wp->w_next)
if (wp != curwin && wp->w_buffer == curbuf)
{
if (first)
{
cursor_off();
if (p_hls)
start_search_hl();
first = FALSE;
}
wp->w_redr_type = NOT_VALID;
/*
* don't do the actual redraw if wait_return() has just been
* called and the user typed a ":"
*/
if (!skip_redraw)
win_update(wp);
}
if (search_hl_prog != NULL)
{
vim_free(search_hl_prog);
search_hl_prog = NULL;
}
}
/*
* Update a single window.
*
* This may cause the windows below it also to be redrawn.
*/
static void
win_update(wp)
WIN *wp;
{
int type = wp->w_redr_type;
int row;
int endrow;
linenr_t lnum;
linenr_t lastline = 0; /* only valid if endrow != Rows -1 */
int done; /* if TRUE, we hit the end of the file */
int didline; /* if TRUE, we finished the last line */
int srow = 0; /* starting row of the current line */
int idx;
int i;
long j;
int attr;
static int recursive = FALSE; /* being called recursively */
int old_botline = wp->w_botline;
int must_start_top = FALSE; /* update must start at top row */
int must_end_bot = FALSE; /* update must end at bottom row */
if (type == NOT_VALID)
{
wp->w_redr_status = TRUE;
wp->w_lsize_valid = 0;
}
idx = 0;
row = 0;
lnum = wp->w_topline;
validate_virtcol_win(wp);
/* The number of rows shown is w_height. */
/* The default last row is the status/command line. */
endrow = wp->w_height;
/*
* If there are no changes on the screen, handle two special cases:
* 1: we are off the top of the screen by a few lines: scroll down
* 2: wp->w_topline is below wp->w_lsize_lnum[0]: may scroll up
*/
if (type == VALID || type == VALID_TO_CURSCHAR ||
type == VALID_BEF_CURSCHAR || type == INVERTED)
{
if (wp->w_topline < wp->w_lsize_lnum[0]) /* may scroll down */
{
j = wp->w_lsize_lnum[0] - wp->w_topline;
if (j < wp->w_height - 2) /* not too far off */
{
lastline = wp->w_lsize_lnum[0] - 1;
i = plines_m_win(wp, wp->w_topline, lastline);
if (i < wp->w_height - 2) /* less than a screen off */
{
/*
* Try to insert the correct number of lines.
* If not the last window, delete the lines at the bottom.
* win_ins_lines may fail.
*/
if (win_ins_lines(wp, 0, i, FALSE, wp == firstwin) == OK &&
wp->w_lsize_valid)
{
must_start_top = TRUE;
endrow = i;
if ((wp->w_lsize_valid += j) > wp->w_height)
wp->w_lsize_valid = wp->w_height;
for (idx = wp->w_lsize_valid; idx - j >= 0; idx--)
{
wp->w_lsize_lnum[idx] = wp->w_lsize_lnum[idx - j];
wp->w_lsize[idx] = wp->w_lsize[idx - j];
}
idx = 0;
}
}
else if (lastwin == firstwin)
{
screenclear(); /* far off: clearing the screen is faster */
must_start_top = TRUE;
must_end_bot = TRUE;
}
}
else if (lastwin == firstwin)
{
screenclear(); /* far off: clearing the screen is faster */
must_start_top = TRUE;
must_end_bot = TRUE;
}
}
else /* may scroll up */
{
j = -1;
/* try to find wp->w_topline in wp->w_lsize_lnum[] */
for (i = 0; i < wp->w_lsize_valid; i++)
{
if (wp->w_lsize_lnum[i] == wp->w_topline)
{
j = i;
break;
}
row += wp->w_lsize[i];
}
if (j == -1) /* wp->w_topline is not in wp->w_lsize_lnum */
{
row = 0; /* start at the first row */
if (lastwin == firstwin)
{
screenclear(); /* far off: clearing the screen is faster */
must_start_top = TRUE;
must_end_bot = TRUE;
}
}
else
{
/*
* Try to delete the correct number of lines.
* wp->w_topline is at wp->w_lsize_lnum[i].
*/
if (row && win_del_lines(wp, 0, row, FALSE, wp == firstwin)
== OK)
must_end_bot = TRUE;
if ((row == 0 || must_end_bot) && wp->w_lsize_valid)
{
/*
* Skip the lines (below the deleted lines) that are still
* valid and don't need redrawing. Copy their info
* upwards, to compensate for the deleted lines. Leave
* row and lnum on the first line that needs redrawing.
*/
srow = row;
row = 0;
for (;;)
{
if ((type == VALID_TO_CURSCHAR &&
lnum == wp->w_cursor.lnum) ||
(type == VALID_BEF_CURSCHAR &&
lnum == wp->w_cursor.lnum - 1))
break;
if (row + srow + (int)wp->w_lsize[j] > wp->w_height)
break;
wp->w_lsize[idx] = wp->w_lsize[j];
wp->w_lsize_lnum[idx] = lnum++;
row += wp->w_lsize[idx++];
if ((int)++j >= wp->w_lsize_valid)
break;
}
wp->w_lsize_valid = idx;
}
else
row = 0; /* update all lines */
}
}
if (endrow == wp->w_height && idx == 0) /* no scrolling */
wp->w_lsize_valid = 0;
}
done = didline = FALSE;
if (VIsual_active) /* check if we are updating the inverted part */
{
linenr_t from, to;
/*
* Find the line numbers that need to be updated: The lines between
* the old cursor position and the current cursor position. Also
* check if the Visual position changed.
*/
if (wp->w_cursor.lnum < wp->w_old_cursor_lnum)
{
from = wp->w_cursor.lnum;
to = wp->w_old_cursor_lnum;
}
else
{
from = wp->w_old_cursor_lnum;
to = wp->w_cursor.lnum;
}
if (VIsual.lnum != wp->w_old_visual_lnum)
{
if (wp->w_old_visual_lnum < from)
from = wp->w_old_visual_lnum;
if (wp->w_old_visual_lnum > to)
to = wp->w_old_visual_lnum;
if (VIsual.lnum < from)
from = VIsual.lnum;
if (VIsual.lnum > to)
to = VIsual.lnum;
}
/*
* If in block mode and changed column or wp->w_curswant: update all
* lines.
* First compute the actual start and end column.
*/
if (VIsual_mode == Ctrl('V'))
{
colnr_t from1, from2, to1, to2;
getvcol(wp, &VIsual, &from1, NULL, &to1);
getvcol(wp, &wp->w_cursor, &from2, NULL, &to2);
if (from2 < from1)
from1 = from2;
if (to2 > to1)
to1 = to2;
++to1;
if (wp->w_curswant == MAXCOL)
to1 = MAXCOL;
if (from1 != wp->w_old_cursor_fcol || to1 != wp->w_old_cursor_lcol)
{
if (from > VIsual.lnum)
from = VIsual.lnum;
if (to < VIsual.lnum)
to = VIsual.lnum;
}
wp->w_old_cursor_fcol = from1;
wp->w_old_cursor_lcol = to1;
}
/*
* There is no need to update lines above the top of the window.
* If "must_start_top" is set, always start at w_topline.
*/
if (from < wp->w_topline || must_start_top)
from = wp->w_topline;
/*
* If we know the value of w_botline, use it to restrict the update to
* the lines that are visible in the window.
*/
if (wp->w_valid & VALID_BOTLINE)
{
if (from >= wp->w_botline)
from = wp->w_botline - 1;
if (to >= wp->w_botline)
to = wp->w_botline - 1;
}
/*
* Find the minimal part to be updated.
*
* If "lnum" is past "from" already, start at w_topline (can
* happen when scrolling).
*/
if (lnum > from)
{
lnum = wp->w_topline;
idx = 0;
row = 0;
}
while (lnum < from && idx < wp->w_lsize_valid) /* find start */
{
row += wp->w_lsize[idx++];
++lnum;
}
if (!must_end_bot && !must_start_top)
{
srow = row;
for (j = idx; j < wp->w_lsize_valid; ++j) /* find end */
{
if (wp->w_lsize_lnum[j] == to + 1)
{
endrow = srow;
break;
}
srow += wp->w_lsize[j];
}
}
else
{
/* Redraw until the end, otherwise we could miss something when
* doing CTRL-U. */
endrow = wp->w_height;
}
wp->w_old_cursor_lnum = curwin->w_cursor.lnum;
wp->w_old_visual_lnum = VIsual.lnum;
wp->w_old_curswant = wp->w_curswant;
}
else
{
wp->w_old_cursor_lnum = 0;
wp->w_old_visual_lnum = 0;
}
attr = highlight_attr[HLF_V];
/*
* Update the screen rows from "row" to "endrow".
* Start at line "lnum" which is at wp->w_lsize_lnum[idx].
*/
for (;;)
{
if (row == endrow)
{
didline = TRUE;
break;
}
if (lnum > wp->w_buffer->b_ml.ml_line_count)
{
done = TRUE; /* hit the end of the file */
break;
}
srow = row;
row = win_line(wp, lnum, srow, endrow, attr);
if (row > endrow) /* past end of screen */
{
/* we may need the size of that too long line later on */
wp->w_lsize[idx] = plines_win(wp, lnum);
wp->w_lsize_lnum[idx++] = lnum;
break;
}
wp->w_lsize[idx] = row - srow;
wp->w_lsize_lnum[idx++] = lnum;
if (++lnum > wp->w_buffer->b_ml.ml_line_count)
{
done = TRUE;
break;
}
}
if (idx > wp->w_lsize_valid)
wp->w_lsize_valid = idx;
/* Do we have to do off the top of the screen processing ? */
if (endrow != wp->w_height)
{
row = 0;
for (idx = 0; idx < wp->w_lsize_valid && row < wp->w_height; idx++)
row += wp->w_lsize[idx];
if (row < wp->w_height)
{
done = TRUE;
}
else if (row > wp->w_height) /* Need to blank out the last line */
{
lnum = wp->w_lsize_lnum[idx - 1];
srow = row - wp->w_lsize[idx - 1];
didline = FALSE;
}
else
{
lnum = wp->w_lsize_lnum[idx - 1] + 1;
didline = TRUE;
}
}
wp->w_empty_rows = 0;
/*
* If we didn't hit the end of the file, and we didn't finish the last
* line we were working on, then the line didn't fit.
*/
if (!done && !didline)
{
if (lnum == wp->w_topline)
{
/*
* Single line that does not fit!
* Fill last line with '@' characters.
*/
screen_fill(wp->w_winpos + wp->w_height - 1,
wp->w_winpos + wp->w_height, 0, (int)Columns, '@', '@',
highlight_attr[HLF_AT]);
wp->w_botline = lnum + 1;
}
else
{
/*
* Clear the rest of the screen and mark the unused lines.
*/
#ifdef RIGHTLEFT
if (wp->w_p_rl)
rightleft = 1;
#endif
screen_fill(wp->w_winpos + srow,
wp->w_winpos + wp->w_height, 0, (int)Columns, '@', ' ',
highlight_attr[HLF_AT]);
#ifdef RIGHTLEFT
rightleft = 0;
#endif
wp->w_botline = lnum;
wp->w_empty_rows = wp->w_height - srow;
}
}
else
{
/* make sure the rest of the screen is blank */
/* put '~'s on rows that aren't part of the file. */
#ifdef RIGHTLEFT
if (wp->w_p_rl)
rightleft = 1;
#endif
screen_fill(wp->w_winpos + row,
wp->w_winpos + wp->w_height, 0, (int)Columns, '~', ' ',
highlight_attr[HLF_AT]);
#ifdef RIGHTLEFT
rightleft = 0;
#endif
wp->w_empty_rows = wp->w_height - row;
if (done) /* we hit the end of the file */
wp->w_botline = wp->w_buffer->b_ml.ml_line_count + 1;
else
wp->w_botline = lnum;
}
/*
* There is a trick with w_botline. If we invalidate it on each change
* that might modify it, this will cause a lot of expensive calls to
* plines() in update_topline() each time. Therefore the value of
* w_botline is often approximated, and this value is used to compute the
* value of w_topline. If the value of w_botline was wrong, check that
* the value of w_topline is correct (cursor is on the visible part of the
* text). If it's not, we need to redraw again. Mostly this just means
* scrolling up a few lines, so it doesn't look too bad. Only do this for
* the current window (where changes are relevant).
*/
wp->w_valid |= VALID_BOTLINE;
wp->w_redr_type = 0;
if (wp == curwin && wp->w_botline != old_botline && !recursive)
{
recursive = TRUE;
update_topline(); /* may invalidate w_botline again */
if (must_redraw)
{
win_update(wp);
must_redraw = 0;
}
recursive = FALSE;
}
}
/*
* Display line "lnum" of window 'wp' on the screen.
* Start at row "startrow", stop when "endrow" is reached.
* wp->w_virtcol needs to be valid.
*
* Return the number of last row the line occupies.
*/
static int
win_line(wp, lnum, startrow, endrow, attr)
WIN *wp;
linenr_t lnum;
int startrow;
int endrow;
int attr; /* attributes for area highlighting */
{
char_u *screenp;
int c;
int col; /* visual column on screen */
long vcol; /* visual column for tabs */
#ifdef SYNTAX_HL
int rcol; /* real column in the line */
#endif
int row; /* row in the window, excl w_winpos */
int screen_row; /* row on the screen, incl w_winpos */
char_u *ptr;
#ifdef SYNTAX_HL
char_u *line;
#endif
char_u extra[16]; /* "%ld" must fit in here */
char_u *p_extra;
char_u *showbreak = NULL;
int n_extra;
int n_spaces = 0;
int fromcol, tocol; /* start/end of inverting */
int noinvcur = FALSE; /* don't invert the cursor */
FPOS *top, *bot;
int area_highlighting; /* Visual or incsearch highlighting in
this line */
int area_attr = 0; /* attributes desired by highlighting */
int search_attr = 0; /* attributes sesired by 'searchhl' */
#ifdef SYNTAX_HL
int syntax_attr = 0; /* attributes desired by syntax */
int has_syntax = FALSE; /* this buffer has syntax highl. */
#endif
int has_syn_or_lbr; /* has syntax or linebreak */
int char_attr; /* attributes for next character */
char_u *search_hl_start = NULL;
char_u *search_hl_end = NULL;
if (startrow > endrow) /* past the end already! */
return startrow;
row = startrow;
screen_row = row + wp->w_winpos;
/*
* To speed up the loop below, set has_syn_or_lbr when there is linebreak
* and/or syntax processing to be done.
*/
has_syn_or_lbr = wp->w_p_lbr;
#ifdef SYNTAX_HL
if (syntax_present(wp->w_buffer))
{
syntax_start(wp, lnum);
has_syntax = TRUE;
has_syn_or_lbr = TRUE;
}
#endif
col = 0;
vcol = 0;
fromcol = -10;
tocol = MAXCOL;
area_highlighting = FALSE;
char_attr = 0;
/*
* handle visual active in this window
*/
if (VIsual_active && wp->w_buffer == curwin->w_buffer)
{
/* Visual is after curwin->w_cursor */
if (ltoreq(curwin->w_cursor, VIsual))
{
top = &curwin->w_cursor;
bot = &VIsual;
}
else /* Visual is before curwin->w_cursor */
{
top = &VIsual;
bot = &curwin->w_cursor;
}
if (VIsual_mode == Ctrl('V')) /* block mode */
{
if (lnum >= top->lnum && lnum <= bot->lnum)
{
fromcol = wp->w_old_cursor_fcol;
tocol = wp->w_old_cursor_lcol;
}
}
else /* non-block mode */
{
if (lnum > top->lnum && lnum <= bot->lnum)
fromcol = 0;
else if (lnum == top->lnum)
getvcol(wp, top, (colnr_t *)&fromcol, NULL, NULL);
if (lnum == bot->lnum)
{
getvcol(wp, bot, NULL, NULL, (colnr_t *)&tocol);
++tocol;
}
if (VIsual_mode == 'V') /* linewise */
{
if (fromcol > 0)
fromcol = 0;
tocol = MAXCOL;
}
}
/* if the cursor can't be switched off, don't invert the
* character where the cursor is */
#ifndef MSDOS
if (!highlight_match && *T_VI == NUL &&
lnum == curwin->w_cursor.lnum && wp == curwin)
noinvcur = TRUE;
#endif
if (tocol <= (int)wp->w_leftcol) /* inverting is left of screen */
fromcol = 0;
/* start of invert is left of screen */
else if (fromcol >= 0 && fromcol < (int)wp->w_leftcol)
fromcol = wp->w_leftcol;
/* if inverting in this line, can't optimize cursor positioning */
if (fromcol >= 0)
area_highlighting = TRUE;
}
/*
* handle incremental search position highlighting
*/
else if (highlight_match && wp == curwin && search_match_len)
{
if (lnum == curwin->w_cursor.lnum)
{
getvcol(curwin, &(curwin->w_cursor),
(colnr_t *)&fromcol, NULL, NULL);
curwin->w_cursor.col += search_match_len;
getvcol(curwin, &(curwin->w_cursor),
(colnr_t *)&tocol, NULL, NULL);
curwin->w_cursor.col -= search_match_len;
area_highlighting = TRUE;
if (fromcol == tocol) /* do at least one character */
tocol = fromcol + 1; /* happens when past end of line */
}
}
ptr = ml_get_buf(wp->w_buffer, lnum, FALSE);
#ifdef SYNTAX_HL
line = ptr;
rcol = 0;
#endif
/*
* Handle highlighting the last used search pattern.
*/
if (search_hl_prog != NULL)
{
if (vim_regexec(search_hl_prog, ptr, TRUE))
{
search_hl_start = search_hl_prog->startp[0];
search_hl_end = search_hl_prog->endp[0];
area_highlighting = TRUE;
}
}
if (!wp->w_p_wrap) /* advance to first character to be displayed */
{
while ((colnr_t)vcol < wp->w_leftcol && *ptr)
{
vcol += win_chartabsize(wp, *ptr++, (colnr_t)vcol);
#ifdef SYNTAX_HL
++rcol;
#endif
}
if ((colnr_t)vcol > wp->w_leftcol)
{
n_spaces = vcol - wp->w_leftcol; /* begin with some spaces */
vcol = wp->w_leftcol;
}
}
screenp = current_LinePointer;
#ifdef RIGHTLEFT
if (wp->w_p_rl)
{
col = Columns - 1; /* col follows screenp here */
screenp += Columns - 1;
}
#endif
if (wp->w_p_nu)
{
#ifdef RIGHTLEFT
if (wp->w_p_rl) /* reverse line numbers */
{
char_u *c1, *c2, t;
sprintf((char *)extra, " %-7ld", (long)lnum);
for (c1 = extra, c2 = extra + STRLEN(extra) - 1; c1 < c2;
c1++, c2--)
{
t = *c1;
*c1 = *c2;
*c2 = t;
}
}
else
#endif
sprintf((char *)extra, "%7ld ", (long)lnum);
p_extra = extra;
n_extra = 8;
vcol -= 8; /* so vcol is 0 when line number has been printed */
}
else
{
p_extra = NULL;
n_extra = 0;
}
for (;;)
{
if (area_highlighting) /* Visual or match highlighting in this line */
{
if (((vcol == fromcol && !(noinvcur &&
(colnr_t)vcol == wp->w_virtcol)) ||
(noinvcur && (colnr_t)vcol == wp->w_virtcol + 1 &&
vcol >= fromcol)) && vcol < tocol)
area_attr = attr; /* start highlighting */
else if (area_attr && (vcol == tocol ||
(noinvcur && (colnr_t)vcol == wp->w_virtcol)))
area_attr = 0; /* stop highlighting */
/*
* Check for start/end of search pattern match.
* After end, check for start/end of next match.
* When another match, have to check for start again.
* Watch out for matching an empty string!
*/
if (!n_extra && !n_spaces)
{
for (;;)
{
if (ptr == search_hl_start)
search_attr = search_hl_attr;
if (ptr == search_hl_end)
{
search_attr = 0;
if (vim_regexec(search_hl_prog, ptr, TRUE))
{
search_hl_start = search_hl_prog->startp[0];
search_hl_end = search_hl_prog->endp[0];
if (search_hl_start != search_hl_end)
continue;
++search_hl_end; /* try again after empty match */
}
}
break;
}
}
if (area_attr)
char_attr = area_attr;
#ifdef SYNTAX_HL
else if (!search_attr && has_syntax)
char_attr = syntax_attr;
#endif
else
char_attr = search_attr;
}
/* Get the next character to put on the screen. */
/*
* if 'showbreak' is set it contains the characters to put at the
* start of each broken line
*/
if (
#ifdef RIGHTLEFT
(wp->w_p_rl ? col == -1 : col == Columns)
#else
col == Columns
#endif
&& (*ptr != NUL || (wp->w_p_list && n_extra == 0) ||
(n_extra && *p_extra) || n_spaces) &&
vcol != 0 && STRLEN(p_sbr) != 0)
showbreak = p_sbr;
if (showbreak != NULL)
{
c = *showbreak++;
if (*showbreak == NUL)
showbreak = NULL;
}
/*
* The 'extra' array contains the extra stuff that is inserted to
* represent special characters (non-printable stuff).
*/
else if (n_extra)
{
c = *p_extra++;
n_extra--;
}
else if (n_spaces)
{
c = ' ';
n_spaces--;
}
else
{
c = *ptr++;
if (has_syn_or_lbr)
{
#ifdef SYNTAX_HL
if (has_syntax)
{
syntax_attr = get_syntax_attr(rcol++, line);
if (!area_attr && !search_attr)
char_attr = syntax_attr;
}
#endif
/*
* Found last space before word: check for line break
*/
if (wp->w_p_lbr && isbreak(c) && !isbreak(*ptr) &&
!wp->w_p_list)
{
n_spaces = win_lbr_chartabsize(wp, ptr - 1,
(colnr_t)vcol, NULL) - 1;
if (vim_iswhite(c))
c = ' ';
}
}
/*
* Handling of non-printable characters.
*/
if (!safe_isprintchar(c))
{
/*
* when getting a character from the file, we may have to turn
* it into something else on the way to putting it into
* 'NextScreen'.
*/
if (c == TAB && !wp->w_p_list)
{
/* tab amount depends on current column */
n_spaces = (int)wp->w_buffer->b_p_ts -
vcol % (int)wp->w_buffer->b_p_ts - 1;
c = ' ';
}
else if (c == NUL && wp->w_p_list)
{
p_extra = (char_u *)"";
n_extra = 1;
c = '$';
--ptr; /* put it back at the NUL */
}
else if (c != NUL)
{
p_extra = transchar(c);
n_extra = charsize(c) - 1;
c = *p_extra++;
}
}
}
/*
* At end of the text line.
*/
if (c == NUL)
{
if (area_attr)
{
/* invert at least one char, used for Visual and empty line or
* highlight match at end of line. If it's beyond the last
* char on the screen, just overwrite that one (tricky!) */
if (vcol == fromcol)
{
#ifdef RIGHTLEFT
if (wp->w_p_rl)
{
if (col < 0)
{
++screenp;
++col;
}
}
else
#endif
{
if (col >= Columns)
{
--screenp;
--col;
}
}
*screenp = ' ';
*(screenp + Columns) = char_attr;
#ifdef RIGHTLEFT
if (wp->w_p_rl)
{
--screenp;
--col;
}
else
#endif
{
++screenp;
++col;
}
}
}
SCREEN_LINE(screen_row, col, TRUE, wp->w_p_rl);
row++;
/*
* Update w_cline_height if we can (saves a call to plines()
* later).
*/
if (wp == curwin && lnum == curwin->w_cursor.lnum)
{
curwin->w_cline_row = startrow;
curwin->w_cline_height = row - startrow;
curwin->w_valid |= (VALID_CHEIGHT|VALID_CROW);
}
break;
}
/*
* At end of screen line.
*/
if (
#ifdef RIGHTLEFT
wp->w_p_rl ? (col < 0) :
#endif
(col >= Columns)
)
{
SCREEN_LINE(screen_row, col, TRUE, wp->w_p_rl);
col = 0;
++row;
++screen_row;
if (!wp->w_p_wrap)
break;
if (row == endrow) /* line got too long for screen */
{
++row;
break;
}
screenp = current_LinePointer;
#ifdef RIGHTLEFT
if (wp->w_p_rl)
{
col = Columns - 1; /* col is not used if breaking! */
screenp += Columns - 1;
}
#endif
}
/*
* Store the character.
*/
*screenp = c;
*(screenp + Columns) = char_attr;
#ifdef RIGHTLEFT
if (wp->w_p_rl)
{
--screenp;
--col;
}
else
#endif
{
++screenp;
++col;
}
++vcol;
/* stop before '$' of change command */
if (dollar_vcol && wp == curwin && vcol >= (long)wp->w_virtcol)
{
SCREEN_LINE(screen_row, col, FALSE, wp->w_p_rl);
break;
}
}
return (row);
}
/*
* Move one "cooked" screen line to the screen, but only the characters that
* have actually changed. Handle insert/delete character.
* 'endcol' gives the columns where valid characters are (wp->w_p_rl matters).
* 'clear_rest' is TRUE if the rest of the line needs to be cleared.
*/
static void
screen_line(row, endcol, clear_rest
#ifdef RIGHTLEFT
, rightleft
#endif
)
int row;
int endcol;
int clear_rest;
#ifdef RIGHTLEFT
int rightleft;
#endif
{
char_u *screenp_from;
char_u *screenp_to;
int col = 0;
int force = FALSE; /* force update rest of the line */
screenp_from = current_LinePointer;
screenp_to = LinePointers[row];
#ifdef RIGHTLEFT
if (rightleft)
while (col <= endcol)
{
if (clear_rest && (*screenp_to != ' ' ||
*(screenp_to + Columns) != 0))
screen_char(screenp_to, row, col);
++col;
++screenp_to;
++screenp_from;
}
while (wp->w_p_rl ? (col < Columns) : (col < endcol))
#else
while (col < endcol)
#endif
{
if (force || *screenp_from != *screenp_to ||
*(screenp_from + Columns) != *(screenp_to + Columns))
{
#if 0 /* TODO */
/*
* Special trick to make copy/paste of wrapped lines work with
* xterm/screen:
* If the first column is to be written, write the preceding
* char twice. This will work with all terminal types
* (regardless of the xn,am settings).
* Only do this on a fast tty.
*/
if (col == 0 && p_tf && row > startrow &&
LinePointers[screen_row - 1][Columns - 1 + Columns] ==
char_attr)
{
if (screen_cur_row != screen_row - 1 ||
screen_cur_col != Columns)
screen_char(LinePointers[screen_row - 1] + Columns - 1,
screen_row - 1, (int)(Columns - 1));
screen_char(LinePointers[screen_row - 1] + Columns - 1,
screen_row - 1, (int)Columns);
screen_start();
}
#endif
/*
* Special handling when 'xs' termcap flag set (hpterm):
* Attributes for characters are stored at the position where the
* cursor is when writing the highlighting code. The
* start-highlighting code must be written with the cursor on the
* first highlighted character. The stop-highlighting code must
* be written with the cursor just after the last highlighted
* character.
* Overwriting a character doesn't remove it's highlighting. Need
* to clear the rest of the line, and force redrawing it
* completely.
*/
if (p_wiv && !force && *(screenp_to + Columns) &&
*(screenp_from + Columns) != *(screenp_to + Columns))
{
/*
* Need to remove highlighting attributes here.
*/
windgoto(row, col);
outstr(T_CE); /* clear rest of this screen line */
screen_start(); /* don't know where cursor is now */
force = TRUE; /* force redraw of rest of the line */
/*
* If the previous character was highlighted, need to stop
* highlighting at this character.
*/
if (col > 0 && *(screenp_to + Columns - 1))
{
screen_attr = *(screenp_to + Columns - 1);
term_windgoto(row, col);
screen_stop_highlight();
}
else
screen_attr = 0; /* highlighting has stopped */
}
*screenp_to = *screenp_from;
*(screenp_to + Columns) = *(screenp_from + Columns);
screen_char(screenp_to, row, col);
}
else if (p_wiv && col > 0)
{
if (*(screenp_to + Columns) == *(screenp_to + Columns - 1))
{
/*
* Don't output stop-highlight when moving the cursor, it will
* stop the highlighting when it should continue.
*/
screen_attr = 0;
}
else if (screen_attr)
{
screen_stop_highlight();
}
}
++screenp_to;
++screenp_from;
++col;
}
if (clear_rest
#ifdef RIGHTLEFT
&& !wp->w_p_rl
#endif
)
{
/* blank out the rest of the line */
while (col < Columns && *screenp_to == ' ' &&
*(screenp_to + Columns) == 0)
{
++screenp_to;
++col;
}
if (col < Columns)
screen_fill(row, row + 1, col, (int)Columns, ' ', ' ', 0);
}
}
/*
* mark all status lines for redraw; used after first :cd
*/
void
status_redraw_all()
{
WIN *wp;
for (wp = firstwin; wp; wp = wp->w_next)
wp->w_redr_status = TRUE;
update_screen(NOT_VALID);
}
/*
* Redraw the status line of window wp.
*
* If inversion is possible we use it. Else '=' characters are used.
*/
void
win_redr_status(wp)
WIN *wp;
{
int row;
char_u *p;
int len;
int fillchar;
int attr;
if (wp->w_status_height) /* if there is a status line */
{
fillchar = highlight_status(&attr);
p = wp->w_buffer->b_fname;
if (p == NULL)
STRCPY(NameBuff, "[No File]");
else
{
home_replace(wp->w_buffer, p, NameBuff, MAXPATHL);
trans_characters(NameBuff, MAXPATHL);
}
p = NameBuff;
len = STRLEN(p);
if (wp->w_buffer->b_help || wp->w_buffer->b_changed ||
wp->w_buffer->b_p_ro)
*(p + len++) = ' ';
if (wp->w_buffer->b_help)
{
STRCPY(p + len, "[help]");
len += 6;
}
if (wp->w_buffer->b_changed)
{
STRCPY(p + len, "[+]");
len += 3;
}
if (wp->w_buffer->b_p_ro)
{
STRCPY(p + len, "[RO]");
len += 4;
}
if (len > ru_col - 1)
{
p += len - (ru_col - 1);
*p = '<';
len = ru_col - 1;
}
row = wp->w_winpos + wp->w_height;
screen_puts(p, row, 0, attr);
screen_fill(row, row + 1, len, ru_col, fillchar, fillchar, attr);
win_redr_ruler(wp, TRUE);
}
else /* no status line, can only be last window */
redraw_cmdline = TRUE;
wp->w_redr_status = FALSE;
}
/*
* Output a single character directly to the screen and update NextScreen.
*/
void
screen_putchar(c, row, col, attr)
int c;
int row, col;
int attr;
{
char_u buf[2];
buf[0] = c;
buf[1] = NUL;
screen_puts(buf, row, col, attr);
}
/*
* Put string '*text' on the screen at position 'row' and 'col', with
* attributes 'attr', and update NextScreen.
* Note: only outputs within one row, message is truncated at screen boundary!
* Note: if NextScreen, row and/or col is invalid, nothing is done.
*/
void
screen_puts(text, row, col, attr)
char_u *text;
int row;
int col;
int attr;
{
char_u *screenp;
if (NextScreen != NULL && row < Rows) /* safety check */
{
screenp = LinePointers[row] + col;
while (*text && col < Columns)
{
if (*screenp != *text || *(screenp + Columns) != attr ||
exmode_active)
{
*screenp = *text;
*(screenp + Columns) = attr;
screen_char(screenp, row, col);
}
++screenp;
++col;
++text;
}
}
}
/*
* Prepare for 'searchhl' highlighting.
*/
static void
start_search_hl()
{
search_hl_prog = last_pat_prog();
search_hl_attr = highlight_attr[HLF_L];
}
/*
* Reset cursor position. Use whenever cursor was moved because of outputting
* something directly to the screen (shell commands) or a terminal control
* code.
*/
void
screen_start()
{
screen_cur_row = screen_cur_col = 9999;
}
/*
* Note that the cursor has gone down to the next line, column 0.
* Used for Ex mode.
*/
void
screen_down()
{
screen_cur_col = 0;
if (screen_cur_row < Rows - 1)
++screen_cur_row;
}
static void
screen_start_highlight(attr)
int attr;
{
screen_attr = attr;
if (full_screen
#ifdef WIN32
&& termcap_active
#endif
)
{
#ifdef USE_GUI
if (gui.in_use)
{
char buf[20];
sprintf(buf, "\033|%dh", attr); /* internal GUI code */
OUTSTR(buf);
}
else
#endif
{
#ifdef SYNTAX_HL
if (attr > HL_ALL) /* special HL attr. */
{
struct attr_entry *aep = NULL;
if (*T_CCO != NUL)
{
aep = syn_cterm_attr2entry(attr);
if (aep != NULL)
{
if (aep->ae_u.cterm.fg_color)
term_fg_color(aep->ae_u.cterm.fg_color - 1);
if (aep->ae_u.cterm.bg_color)
term_bg_color(aep->ae_u.cterm.bg_color - 1);
}
}
else
{
aep = syn_term_attr2entry(attr);
if (aep != NULL && aep->ae_u.term.start != NULL)
outstr(aep->ae_u.term.start);
}
if (aep == NULL) /* did ":syntax clear" */
attr = 0;
else
attr = aep->ae_attr;
}
#endif
if ((attr & HL_BOLD) && T_MD != NULL) /* bold */
outstr(T_MD);
if ((attr & HL_STANDOUT) && T_SO != NULL) /* standout */
outstr(T_SO);
if ((attr & HL_UNDERLINE) && T_US != NULL) /* underline */
outstr(T_US);
if ((attr & HL_ITALIC) && T_CZH != NULL) /* italic */
outstr(T_CZH);
if ((attr & HL_INVERSE) && T_MR != NULL) /* inverse (reverse) */
outstr(T_MR);
}
}
}
void
screen_stop_highlight()
{
int do_ME = FALSE; /* output T_ME code */
if (screen_attr
#ifdef WIN32
&& termcap_active
#endif
)
{
#ifdef USE_GUI
if (gui.in_use)
{
char buf[20];
sprintf(buf, "\033|%dH", screen_attr); /* internal GUI code */
OUTSTR(buf);
}
else
#endif
{
#ifdef SYNTAX_HL
if (screen_attr > HL_ALL) /* special HL attr. */
{
struct attr_entry *aep = NULL;
if (*T_CCO != NUL)
{
/*
* Assume that t_me restores the original colors!
*/
aep = syn_cterm_attr2entry(screen_attr);
if (aep != NULL && (aep->ae_u.cterm.fg_color ||
aep->ae_u.cterm.bg_color))
do_ME = TRUE;
}
else
{
aep = syn_term_attr2entry(screen_attr);
if (aep != NULL && aep->ae_u.term.stop != NULL)
{
if (STRCMP(aep->ae_u.term.stop, T_ME) == 0)
do_ME = TRUE;
else
outstr(aep->ae_u.term.stop);
}
}
if (aep == NULL) /* did ":syntax clear" */
screen_attr = 0;
else
screen_attr = aep->ae_attr;
}
#endif
/*
* Often all ending-codes are equal to T_ME. Avoid outputting the
* same sequence several times.
*/
if (screen_attr & HL_STANDOUT)
{
if (STRCMP(T_SE, T_ME) == 0)
do_ME = TRUE;
else
outstr(T_SE);
}
if (screen_attr & HL_UNDERLINE)
{
if (STRCMP(T_UE, T_ME) == 0)
do_ME = TRUE;
else
outstr(T_UE);
}
if (screen_attr & HL_ITALIC)
{
if (STRCMP(T_CZR, T_ME) == 0)
do_ME = TRUE;
else
outstr(T_CZR);
}
if (do_ME || (screen_attr & HL_BOLD) || (screen_attr & HL_INVERSE))
outstr(T_ME);
}
}
screen_attr = 0;
}
/*
* put character '*p' on the screen at position 'row' and 'col'
*/
static void
screen_char(p, row, col)
char_u *p;
int row;
int col;
{
/*
* Outputting the last character on the screen may scrollup the screen.
* Don't to it!
*/
if (col == Columns - 1 && row == Rows - 1)
return;
/*
* Stop highlighting first, so it's easier to move the cursor.
*/
if (screen_attr != *(p + Columns))
screen_stop_highlight();
windgoto(row, col);
if (screen_attr != *(p + Columns))
screen_start_highlight(*(p + Columns));
outchar(*p);
screen_cur_col++;
}
/*
* Fill the screen from 'start_row' to 'end_row', from 'start_col' to 'end_col'
* with character 'c1' in first column followed by 'c2' in the other columns.
* Use attributes 'attr'.
*/
void
screen_fill(start_row, end_row, start_col, end_col, c1, c2, attr)
int start_row, end_row;
int start_col, end_col;
int c1, c2;
int attr;
{
int row;
int col;
char_u *screenp;
char_u *attrp;
int did_delete;
int c;
if (end_row > Rows) /* safety check */
end_row = Rows;
if (end_col > Columns) /* safety check */
end_col = Columns;
if (NextScreen == NULL ||
start_row >= end_row || start_col >= end_col) /* nothing to do */
return;
for (row = start_row; row < end_row; ++row)
{
/* try to use delete-line termcap code */
did_delete = FALSE;
if (attr == 0 && c2 == ' ' && end_col == Columns && *T_CE != NUL
#ifdef RIGHTLEFT
&& !rightleft
#endif
)
{
/*
* check if we really need to clear something
*/
col = start_col;
screenp = LinePointers[row] + start_col;
if (c1 != ' ') /* don't clear first char */
{
++col;
++screenp;
}
/* skip blanks (used often, keep it fast!) */
attrp = screenp + Columns;
while (col < end_col && *screenp == ' ' && *attrp == 0)
{
++col;
++screenp;
++attrp;
}
if (col < end_col) /* something to be cleared */
{
screen_stop_highlight();
term_windgoto(row, col);/* clear rest of this screen line */
outstr(T_CE);
screen_start(); /* don't know where cursor is now */
col = end_col - col;
while (col--) /* clear chars in NextScreen */
{
*attrp++ = 0;
*screenp++ = ' ';
}
}
did_delete = TRUE; /* the chars are cleared now */
}
screenp = LinePointers[row] +
#ifdef RIGHTLEFT
(rightleft ? (int)Columns - 1 - start_col : start_col);
#else
start_col;
#endif
c = c1;
for (col = start_col; col < end_col; ++col)
{
if (*screenp != c || *(screenp + Columns) != attr)
{
*screenp = c;
*(screenp + Columns) = attr;
if (!did_delete || c != ' ')
screen_char(screenp, row,
#ifdef RIGHTLEFT
rightleft ? Columns - 1 - col :
#endif
col);
}
#ifdef RIGHTLEFT
if (rightleft)
--screenp;
else
#endif
++screenp;
if (col == start_col)
{
if (did_delete)
break;
c = c2;
}
}
if (row == Rows - 1) /* overwritten the command line */
{
redraw_cmdline = TRUE;
if (c1 == ' ' && c2 == ' ')
clear_cmdline = FALSE; /* command line has been cleared */
}
}
}
/*
* compute wp->w_botline. Can be called after wp->w_topline changed.
*/
static void
comp_botline()
{
int n;
linenr_t lnum;
int done;
/*
* If w_cline_row is valid, start there.
* Otherwise have to start at w_topline.
*/
check_cursor_moved(curwin);
if (curwin->w_valid & VALID_CROW)
{
lnum = curwin->w_cursor.lnum;
done = curwin->w_cline_row;
}
else
{
lnum = curwin->w_topline;
done = 0;
}
for ( ; lnum <= curwin->w_buffer->b_ml.ml_line_count; ++lnum)
{
n = plines(lnum);
if (lnum == curwin->w_cursor.lnum)
{
curwin->w_cline_row = done;
curwin->w_cline_height = n;
curwin->w_valid |= (VALID_CROW|VALID_CHEIGHT);
}
if (done + n > curwin->w_height)
break;
done += n;
}
/* curwin->w_botline is the line that is just below the window */
curwin->w_botline = lnum;
curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
/*
* Also set curwin->w_empty_rows, otherwise scroll_cursor_bot() won't work
*/
if (done == 0)
curwin->w_empty_rows = 0; /* single line that doesn't fit */
else
curwin->w_empty_rows = curwin->w_height - done;
}
void
screenalloc(clear)
int clear;
{
int new_row, old_row;
WIN *wp;
int outofmem = FALSE;
int len;
char_u *new_NextScreen;
char_u **new_LinePointers;
static int entered = FALSE; /* avoid recursiveness */
/*
* Allocation of the screen buffers is done only when the size changes
* and when Rows and Columns have been set and we have started doing full
* screen stuff.
*/
if ((NextScreen != NULL &&
Rows == screen_Rows && Columns == screen_Columns) ||
Rows == 0 || Columns == 0 || (!full_screen && NextScreen == NULL))
return;
/*
* It's possible that we produce an out-of-memory message below, which
* will cause this function to be called again. To break the loop, just
* return here.
*/
if (entered)
return;
entered = TRUE;
comp_col(); /* recompute columns for shown command and ruler */
/*
* We're changing the size of the screen.
* - Allocate new arrays for NextScreen.
* - Move lines from the old arrays into the new arrays, clear extra
* lines (unless the screen is going to be cleared).
* - Free the old arrays.
*
* If anything fails, make NextScreen NULL, so we don't do anything!
* Continuing with the old NextScreen may result in a crash, because the
* size is wrong.
*/
for (wp = firstwin; wp; wp = wp->w_next)
win_free_lsize(wp);
new_NextScreen = (char_u *)malloc((size_t) ((Rows + 1) * Columns * 2));
new_LinePointers = (char_u **)malloc(sizeof(char_u *) * Rows);
for (wp = firstwin; wp; wp = wp->w_next)
{
if (win_alloc_lsize(wp) == FAIL)
{
outofmem = TRUE;
break;
}
}
if (new_NextScreen == NULL || new_LinePointers == NULL || outofmem)
{
do_outofmem_msg();
vim_free(new_NextScreen);
new_NextScreen = NULL;
vim_free(new_LinePointers);
new_LinePointers = NULL;
}
else
{
for (new_row = 0; new_row < Rows; ++new_row)
{
new_LinePointers[new_row] = new_NextScreen + new_row * Columns * 2;
/*
* If the screen is not going to be cleared, copy as much as
* possible from the old screen to the new one and clear the rest
* (used when resizing the window at the "--more--" prompt or when
* executing an external command, for the GUI).
*/
if (!clear)
{
lineclear(new_LinePointers[new_row]);
old_row = new_row + (screen_Rows - Rows);
if (old_row >= 0)
{
if (screen_Columns < Columns)
len = screen_Columns;
else
len = Columns;
vim_memmove(new_LinePointers[new_row],
LinePointers[old_row], (size_t)len);
vim_memmove(new_LinePointers[new_row] + Columns,
LinePointers[old_row] + screen_Columns,
(size_t)len);
}
}
}
current_LinePointer = new_NextScreen + Rows * Columns * 2;
}
vim_free(NextScreen);
vim_free(LinePointers);
NextScreen = new_NextScreen;
LinePointers = new_LinePointers;
must_redraw = CLEAR; /* need to clear the screen later */
if (clear)
screenclear2();
#ifdef USE_GUI
else if (gui.in_use && NextScreen != NULL && Rows != screen_Rows)
{
gui_redraw_block(0, 0, Rows - 1, Columns - 1);
/*
* Adjust the position of the cursor, for when executing an external
* command.
*/
if (msg_row >= Rows) /* Rows got smaller */
msg_row = Rows - 1; /* put cursor at last row */
else if (Rows > screen_Rows) /* Rows got bigger */
msg_row += Rows - screen_Rows; /* put cursor in same place */
if (msg_col >= Columns) /* Columns got smaller */
msg_col = Columns - 1; /* put cursor at last column */
}
#endif
screen_Rows = Rows;
screen_Columns = Columns;
entered = FALSE;
}
void
screenclear()
{
check_for_delay(FALSE);
screenalloc(FALSE); /* allocate screen buffers if size changed */
screenclear2(); /* clear the screen */
}
static void
screenclear2()
{
int i;
if (starting || NextScreen == NULL)
return;
screen_stop_highlight(); /* don't want highlighting here */
outstr(T_CL); /* clear the display */
/* blank out NextScreen */
for (i = 0; i < Rows; ++i)
lineclear(LinePointers[i]);
screen_cleared = TRUE; /* can use contents of NextScreen now */
win_rest_invalid(firstwin);
clear_cmdline = FALSE;
redraw_cmdline = TRUE;
if (must_redraw == CLEAR) /* no need to clear again */
must_redraw = NOT_VALID;
compute_cmdrow();
msg_pos((int)Rows - 1, 0); /* put cursor on last line for messages */
screen_start(); /* don't know where cursor is now */
msg_scrolled = 0; /* can't scroll back */
msg_didany = FALSE;
msg_didout = FALSE;
}
/*
* Clear one line in NextScreen.
*/
static void
lineclear(p)
char_u *p;
{
(void)vim_memset(p, ' ', (size_t)Columns);
(void)vim_memset(p + Columns, 0, (size_t)Columns);
}
/*
* Update curwin->w_topline and redraw if necessary.
*/
void
update_topline_redraw()
{
update_topline();
if (must_redraw)
update_screen(must_redraw);
}
/*
* Update curwin->w_topline to move the cursor onto the screen.
*/
void
update_topline()
{
long line_count;
int temp;
linenr_t old_topline;
if (!screen_valid(TRUE))
return;
old_topline = curwin->w_topline;
/*
* 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;
curwin->w_botline = 2;
curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
}
/*
* If the cursor is above the top of the window, scroll the window to put
* it at the top of the window.
* If we weren't very close to begin with, we scroll to put the cursor in
* the middle of the window.
*/
else if (curwin->w_cursor.lnum < curwin->w_topline + p_so &&
curwin->w_topline > 1)
{
temp = curwin->w_height / 2 - 1;
if (temp < 2)
temp = 2;
/* not very close, put cursor halfway screen */
if (curwin->w_topline + p_so - curwin->w_cursor.lnum >= temp)
scroll_cursor_halfway(FALSE);
else
scroll_cursor_top((int)p_sj, FALSE);
}
/*
* If the cursor is below the bottom of the window, scroll the window
* to put the cursor on the window. If the cursor is less than a
* windowheight down compute the number of lines at the top which have
* the same or more rows than the rows of the lines below the bottom.
* 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.
*/
else
{
if (!(curwin->w_valid & VALID_BOTLINE_AP))
validate_botline();
if ((long)curwin->w_cursor.lnum >= (long)curwin->w_botline - p_so &&
curwin->w_botline <= curbuf->b_ml.ml_line_count)
{
line_count = curwin->w_cursor.lnum - curwin->w_botline + 1 + p_so;
if (line_count <= curwin->w_height + 1)
scroll_cursor_bot((int)p_sj, FALSE);
else
scroll_cursor_halfway(FALSE);
}
}
/*
* Need to redraw when topline changed.
*/
if (curwin->w_topline != old_topline)
redraw_later(VALID);
}
void
update_curswant()
{
if (curwin->w_set_curswant)
{
validate_virtcol();
curwin->w_curswant = curwin->w_virtcol;
curwin->w_set_curswant = FALSE;
}
}
void
windgoto(row, col)
int row;
int col;
{
char_u *p;
int i;
int plan;
int cost;
int wouldbe_col;
int noinvcurs;
char_u *bs;
int goto_cost;
int attr;
#define GOTO_COST 7 /* asssume a term_windgoto() takes about 7 chars */
#define HIGHL_COST 5 /* assume unhighlight takes 5 chars */
#define PLAN_BS 1
#define PLAN_CR 2
#define PLAN_NL 3
#define PLAN_WRITE 4
if (col != screen_cur_col || row != screen_cur_row)
{
/* check if no cursor movement is allowed in highlight mode */
if (screen_attr && *T_MS == NUL)
noinvcurs = HIGHL_COST;
else
noinvcurs = 0;
goto_cost = GOTO_COST + noinvcurs;
/*
* Plan how to do the positioning:
* 1. Use CR to move it to column 0, same row.
* 2. Use BS to move it a few columns to the left.
* 3. Use NL to move a few lines down, column 0.
* 4. Move a few columns to the right by writing characters.
*
* Don't do this if the cursor went beyond the last column, the cursor
* position is unknown then (some terminals wrap, some don't )
*
* First check if the highlighting attibutes allow us to write
* characters to move the cursor to the right.
*/
if (row >= screen_cur_row && screen_cur_col < Columns)
{
/*
* If the cursor is in the same row, bigger col, we can use CR
* or backspace.
*/
bs = NULL; /* init for GCC */
attr = screen_attr;
if (row == screen_cur_row && col < screen_cur_col)
{
if (*T_BC)
bs = T_BC;
else
bs = (char_u *)"\b";
cost = (screen_cur_col - col) * STRLEN(bs);
if (col + 1 < cost) /* using CR is less characters */
{
plan = PLAN_CR;
wouldbe_col = 0;
cost = 1; /* CR is just one character */
}
else
{
plan = PLAN_BS;
wouldbe_col = col;
}
if (noinvcurs) /* will stop highlighting */
{
cost += noinvcurs;
attr = 0;
}
}
/*
* If the cursor is above where we want to be, we can use CR LF.
*/
else if (row > screen_cur_row)
{
plan = PLAN_NL;
wouldbe_col = 0;
cost = (row - screen_cur_row) * 2; /* CR LF */
if (noinvcurs) /* will stop highlighting */
{
cost += noinvcurs;
attr = 0;
}
}
/*
* If the cursor is in the same row, smaller col, just use write.
*/
else
{
plan = PLAN_WRITE;
wouldbe_col = screen_cur_col;
cost = 0;
}
/*
* Check if any characters that need to be written have the
* correct attributes.
*/
i = col - wouldbe_col;
if (i > 0)
cost += i;
if (cost < goto_cost && i > 0)
{
/*
* Check if the attributes are correct without additionally
* stopping highlighting.
*/
p = LinePointers[row] + wouldbe_col + Columns;
while (i && *p++ == attr)
--i;
if (i)
{
/*
* Try if it works when highlighting is stopped here.
*/
if (*--p == 0)
{
cost += noinvcurs;
while (i && *p++ == 0)
--i;
}
if (i)
cost = 999; /* different attributes, don't do it */
}
}
/*
* We can do it without term_windgoto()!
*/
if (cost < goto_cost)
{
if (plan == PLAN_BS)
{
if (noinvcurs)
screen_stop_highlight();
while (screen_cur_col > col)
{
outstr(bs);
--screen_cur_col;
}
}
else if (plan == PLAN_CR)
{
if (noinvcurs)
screen_stop_highlight();
outchar('\r');
screen_cur_col = 0;
}
else if (plan == PLAN_NL)
{
if (noinvcurs)
screen_stop_highlight();
while (screen_cur_row < row)
{
outchar('\n');
++screen_cur_row;
}
screen_cur_col = 0;
}
i = col - screen_cur_col;
if (i > 0)
{
p = LinePointers[row] + screen_cur_col;
while (i)
{
if (*(p + Columns) != screen_attr)
screen_stop_highlight();
outchar(*p++);
--i;
}
}
}
}
else
cost = 999;
if (cost >= goto_cost)
{
if (noinvcurs)
screen_stop_highlight();
if (row == screen_cur_row && (col > screen_cur_col) &&
*T_CRI != NUL)
term_cursor_right(col - screen_cur_col);
else
term_windgoto(row, col);
}
screen_cur_row = row;
screen_cur_col = col;
}
}
/*
* Set cursor to current position.
*/
void
setcursor()
{
if (redrawing())
{
validate_cursor();
windgoto(curwin->w_winpos + curwin->w_wrow,
#ifdef RIGHTLEFT
curwin->w_p_rl ? (int)Columns - 1 - curwin->w_wcol :
#endif
curwin->w_wcol);
}
}
/*
* Recompute topline to put the cursor at the top of the window.
* Scroll at least "min_scroll" lines.
* If "always" is TRUE, always set topline (for "zt").
*/
void
scroll_cursor_top(min_scroll, always)
int min_scroll;
int always;
{
int scrolled = 0;
int extra = 0;
int used;
int i;
int sline; /* screen line for cursor */
int old_topline = curwin->w_topline;
/*
* Decrease topline until:
* - it has become 1
* - (part of) the cursor line is moved off the screen or
* - moved at least 'scrolljump' lines and
* - at least 'scrolloff' lines above and below the cursor
*/
validate_cheight();
used = curwin->w_cline_height;
for (sline = 1; sline < curwin->w_cursor.lnum; ++sline)
{
i = plines(curwin->w_cursor.lnum - sline);
used += i;
extra += i;
if (extra <= p_so &&
curwin->w_cursor.lnum + sline < curbuf->b_ml.ml_line_count)
used += plines(curwin->w_cursor.lnum + sline);
if (used > curwin->w_height)
break;
if (curwin->w_cursor.lnum - sline < curwin->w_topline)
scrolled += i;
/*
* If scrolling is needed, scroll at least 'sj' lines.
*/
if ((curwin->w_cursor.lnum - (sline - 1) >= curwin->w_topline ||
scrolled >= min_scroll) && extra > p_so)
break;
}
/*
* If we don't have enough space, put cursor in the middle.
* This makes sure we get the same position when using "k" and "j"
* in a small window.
*/
if (used > curwin->w_height)
scroll_cursor_halfway(FALSE);
else
{
/*
* If "always" is FALSE, only adjust topline to a lower value, higher
* value may happen with wrapping lines
*/
if (curwin->w_cursor.lnum - (sline - 1) < curwin->w_topline || always)
curwin->w_topline = curwin->w_cursor.lnum - (sline - 1);
if (curwin->w_topline > curwin->w_cursor.lnum)
curwin->w_topline = curwin->w_cursor.lnum;
if (curwin->w_topline != old_topline)
curwin->w_valid &=
~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
}
}
/*
* Recompute topline to put the cursor at the bottom of the window.
* Scroll at least "min_scroll" lines.
* If "set_topbot" is TRUE, set topline and botline first (for "zb").
* This is messy stuff!!!
*/
void
scroll_cursor_bot(min_scroll, set_topbot)
int min_scroll;
int set_topbot;
{
int used;
int scrolled = 0;
int extra = 0;
int sline; /* screen line for cursor from bottom */
int i;
linenr_t lnum;
linenr_t line_count;
linenr_t old_topline = curwin->w_topline;
linenr_t old_botline = curwin->w_botline;
linenr_t old_valid = curwin->w_valid;
int old_empty_rows = curwin->w_empty_rows;
linenr_t cln; /* Cursor Line Number */
cln = curwin->w_cursor.lnum;
if (set_topbot)
{
used = 0;
curwin->w_botline = cln + 1;
for (curwin->w_topline = curwin->w_botline;
curwin->w_topline != 1;
--curwin->w_topline)
{
i = plines(curwin->w_topline - 1);
if (used + i > curwin->w_height)
break;
used += i;
}
curwin->w_empty_rows = curwin->w_height - used;
curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
if (curwin->w_topline != old_topline)
curwin->w_valid &= ~(VALID_WROW|VALID_CROW);
}
else
validate_botline();
validate_cheight();
used = curwin->w_cline_height;
if (cln >= curwin->w_botline)
{
scrolled = used;
if (cln == curwin->w_botline)
scrolled -= curwin->w_empty_rows;
}
/*
* Stop counting lines to scroll when
* - hitting start of the file
* - scrolled nothing or at least 'sj' lines
* - at least 'so' lines below the cursor
* - lines between botline and cursor have been counted
*/
for (sline = 1; sline < cln; ++sline)
{
if ((((scrolled <= 0 || scrolled >= min_scroll) && extra >= p_so) ||
cln + sline > curbuf->b_ml.ml_line_count) &&
cln - sline < curwin->w_botline)
break;
i = plines(cln - sline);
used += i;
if (used > curwin->w_height)
break;
if (cln - sline >= curwin->w_botline)
{
scrolled += i;
if (cln - sline == curwin->w_botline)
scrolled -= curwin->w_empty_rows;
}
if (cln + sline <= curbuf->b_ml.ml_line_count)
{
i = plines(cln + sline);
used += i;
if (used > curwin->w_height)
break;
if (extra < p_so || scrolled < min_scroll)
{
extra += i;
if (cln + sline >= curwin->w_botline)
{
scrolled += i;
if (cln + sline == curwin->w_botline)
scrolled -= curwin->w_empty_rows;
}
}
}
}
/* curwin->w_empty_rows is larger, no need to scroll */
if (scrolled <= 0)
line_count = 0;
/* more than a screenfull, don't scroll but redraw */
else if (used > curwin->w_height)
line_count = used;
/* scroll minimal number of lines */
else
{
for (i = 0, lnum = curwin->w_topline;
i < scrolled && lnum < curwin->w_botline; ++lnum)
i += plines(lnum);
if (i >= scrolled) /* it's possible to scroll */
line_count = lnum - curwin->w_topline;
else /* below curwin->w_botline, don't scroll */
line_count = 9999;
}
/*
* Scroll up if the cursor is off the bottom of the screen a bit.
* Otherwise put it at 1/2 of the screen.
*/
if (line_count >= curwin->w_height && line_count > min_scroll)
scroll_cursor_halfway(FALSE);
else
scrollup(line_count);
/*
* If topline didn't change we need to restore w_botline and w_empty_rows
* (we changed them).
* If topline did change, update_screen() will set botline.
*/
if (curwin->w_topline == old_topline && set_topbot)
{
curwin->w_botline = old_botline;
curwin->w_empty_rows = old_empty_rows;
curwin->w_valid = old_valid;
}
}
/*
* Recompute topline to put the cursor halfway the window
* If "atend" is TRUE, also put it halfway at the end of the file.
*/
void
scroll_cursor_halfway(atend)
int atend;
{
int above = 0;
linenr_t topline;
int below = 0;
linenr_t botline;
int used;
int i;
linenr_t cln; /* Cursor Line Number */
topline = botline = cln = curwin->w_cursor.lnum;
used = plines(cln);
while (topline > 1)
{
if (below <= above) /* add a line below the cursor */
{
if (botline + 1 <= curbuf->b_ml.ml_line_count)
{
i = plines(botline + 1);
used += i;
if (used > curwin->w_height)
break;
below += i;
++botline;
}
else
{
++below; /* count a "~" line */
if (atend)
++used;
}
}
if (below > above) /* add a line above the cursor */
{
i = plines(topline - 1);
used += i;
if (used > curwin->w_height)
break;
above += i;
--topline;
}
}
curwin->w_topline = topline;
curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
}
/*
* Correct the cursor position so that it is in a part of the screen at least
* 'so' lines from the top and bottom, if possible.
* If not possible, put it at the same position as scroll_cursor_halfway().
* When called topline must be valid!
*/
void
cursor_correct()
{
int above = 0; /* screen lines above topline */
linenr_t topline;
int below = 0; /* screen lines below botline */
linenr_t botline;
int above_wanted, below_wanted;
linenr_t cln; /* Cursor Line Number */
int max_off;
/*
* How many lines we would like to have above/below the cursor depends on
* whether the first/last line of the file is on screen.
*/
above_wanted = p_so;
below_wanted = p_so;
if (curwin->w_topline == 1)
{
above_wanted = 0;
max_off = curwin->w_height / 2;
if (below_wanted > max_off)
below_wanted = max_off;
}
validate_botline();
if (curwin->w_botline == curbuf->b_ml.ml_line_count + 1)
{
below_wanted = 0;
max_off = (curwin->w_height - 1) / 2;
if (above_wanted > max_off)
above_wanted = max_off;
}
/*
* If there are sufficient file-lines above and below the cursor, we can
* return now.
*/
cln = curwin->w_cursor.lnum;
if (cln >= curwin->w_topline + above_wanted &&
cln < curwin->w_botline - below_wanted)
return;
/*
* Narrow down the area where the cursor can be put by taking lines from
* the top and the bottom until:
* - the desired context lines are found
* - the lines from the top is past the lines from the bottom
*/
topline = curwin->w_topline;
botline = curwin->w_botline - 1;
while ((above < above_wanted || below < below_wanted) && topline < botline)
{
if (below < below_wanted && (below <= above || above >= above_wanted))
{
below += plines(botline);
--botline;
}
if (above < above_wanted && (above < below || below >= below_wanted))
{
above += plines(topline);
++topline;
}
}
if (topline == botline || botline == 0)
curwin->w_cursor.lnum = topline;
else if (topline > botline)
curwin->w_cursor.lnum = botline;
else
{
if (cln < topline && curwin->w_topline > 1)
{
curwin->w_cursor.lnum = topline;
curwin->w_valid &=
~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW);
}
if (cln > botline && curwin->w_botline <= curbuf->b_ml.ml_line_count)
{
curwin->w_cursor.lnum = botline;
curwin->w_valid &=
~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW);
}
}
}
/*
* Check if the cursor has moved. Set the w_valid flag accordingly.
*/
static void
check_cursor_moved(wp)
WIN *wp;
{
if (wp->w_cursor.lnum != wp->w_valid_cursor.lnum)
{
wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL
|VALID_CHEIGHT|VALID_CROW);
wp->w_valid_cursor = wp->w_cursor;
}
else if (wp->w_cursor.col != wp->w_valid_cursor.col)
{
wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL);
wp->w_valid_cursor.col = wp->w_cursor.col;
}
}
/*
* Call this function when the length of the cursor line (in screen
* characters) has changed, and the change is before the cursor.
* Need to take care of w_botline separately!
*/
void
changed_cline_bef_curs()
{
curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL|VALID_CHEIGHT);
}
/*
* Call this function when the length of the cursor line (in screen
* characters) has changed, and the position of the cursor doesn't change.
* Need to take care of w_botline separately!
*/
void
changed_cline_aft_curs()
{
curwin->w_valid &= ~VALID_CHEIGHT;
}
/*
* Call this function when the length of a line (in screen characters) above
* the cursor have changed.
* Need to take care of w_botline separately!
*/
void
changed_line_abv_curs()
{
curwin->w_valid &=
~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL|VALID_CROW|VALID_CHEIGHT);
}
/*
* Set wp->w_topline to a certain number.
*/
void
set_topline(wp, lnum)
WIN *wp;
linenr_t lnum;
{
/* Approximate the value of w_botline */
wp->w_botline += lnum - wp->w_topline;
wp->w_topline = lnum;
wp->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
}
/*
* Make sure the value of curwin->w_botline is valid.
*/
void
validate_botline()
{
if (!(curwin->w_valid & VALID_BOTLINE))
comp_botline();
}
/*
* Mark curwin->w_botline as invalid (because of some change in the buffer).
*/
void
invalidate_botline()
{
curwin->w_valid &= ~(VALID_BOTLINE|VALID_BOTLINE_AP);
}
void
invalidate_botline_win(wp)
WIN *wp;
{
wp->w_valid &= ~(VALID_BOTLINE|VALID_BOTLINE_AP);
}
/*
* Mark curwin->w_botline as approximated (because of some small change in the
* buffer).
*/
void
approximate_botline()
{
curwin->w_valid &= ~VALID_BOTLINE;
}
/*
* Return TRUE if curwin->w_botline is valid.
*/
int
botline_valid()
{
return (curwin->w_valid & VALID_BOTLINE);
}
/*
* Return TRUE if curwin->w_botline is valid or approximated.
*/
int
botline_approximated()
{
return (curwin->w_valid & VALID_BOTLINE_AP);
}
/*
* Return TRUE if curwin->w_wrow and curwin->w_wcol are valid.
*/
int
cursor_valid()
{
check_cursor_moved(curwin);
return ((curwin->w_valid & (VALID_WROW|VALID_WCOL)) ==
(VALID_WROW|VALID_WCOL));
}
/*
* Validate cursor position. Makes sure w_wrow and w_wcol are valid.
* w_topline must be valid, you may need to call update_topline() first!
*/
void
validate_cursor()
{
check_cursor_moved(curwin);
if ((curwin->w_valid & (VALID_WCOL|VALID_WROW)) != (VALID_WCOL|VALID_WROW))
curs_columns(TRUE);
}
/*
* validate w_cline_row.
*/
void
validate_cline_row()
{
/*
* First make sure that w_topline is valid (after moving the cursor).
*/
update_topline();
check_cursor_moved(curwin);
if (!(curwin->w_valid & VALID_CROW))
curs_rows(FALSE);
}
/*
* Check if w_cline_row and w_cline_height are valid, or can be made valid.
* Can be called when topline and botline have not been updated.
* Used to decide to redraw or keep the window update.
*
* Return OK if w_cline_row is valid.
*/
int
may_validate_crow()
{
if (curwin->w_cursor.lnum < curwin->w_topline ||
curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count ||
!(curwin->w_valid & (VALID_BOTLINE|VALID_BOTLINE_AP)) ||
curwin->w_cursor.lnum >= curwin->w_botline)
return FAIL;
check_cursor_moved(curwin);
if ((curwin->w_valid & (VALID_CROW|VALID_CHEIGHT)) !=
(VALID_CROW|VALID_CHEIGHT))
curs_rows(TRUE);
return OK;
}
/*
* Compute curwin->w_cline_row and curwin->w_cline_height.
* Must only be called when w_topine is valid!
*
* Returns OK when cursor is in the window, FAIL when it isn't.
*/
static void
curs_rows(do_botline)
int do_botline; /* also compute w_botline */
{
linenr_t lnum;
int i;
int lsize_invalid;
/* Check if curwin->w_lsize[] is invalid */
lsize_invalid = (!redrawing() || curwin->w_lsize_valid == 0 ||
curwin->w_lsize_lnum[0] != curwin->w_topline);
i = 0;
curwin->w_cline_row = 0;
for (lnum = curwin->w_topline; lnum < curwin->w_cursor.lnum; ++lnum)
if (lsize_invalid)
curwin->w_cline_row += plines(lnum);
else
curwin->w_cline_row += curwin->w_lsize[i++];
check_cursor_moved(curwin);
if (!(curwin->w_valid & VALID_CHEIGHT))
{
if (lsize_invalid)
curwin->w_cline_height = plines(lnum);
/* Check for a line that is too long to fit on the last screen line. */
else if (i > curwin->w_lsize_valid)
curwin->w_cline_height = 0;
else
curwin->w_cline_height = curwin->w_lsize[i];
}
curwin->w_valid |= VALID_CROW|VALID_CHEIGHT;
/* validate botline too, if update_screen doesn't do it */
if (do_botline && lsize_invalid)
validate_botline();
}
/*
* Validate curwin->w_virtcol only.
*/
void
validate_virtcol()
{
validate_virtcol_win(curwin);
}
/*
* Validate wp->w_virtcol only.
*/
static void
validate_virtcol_win(wp)
WIN *wp;
{
check_cursor_moved(wp);
if (!(wp->w_valid & VALID_VIRTCOL))
{
getvcol(wp, &wp->w_cursor, NULL, &(wp->w_virtcol), NULL);
wp->w_valid |= VALID_VIRTCOL;
}
}
/*
* Validate curwin->w_cline_height only.
*/
void
validate_cheight()
{
check_cursor_moved(curwin);
if (!(curwin->w_valid & VALID_CHEIGHT))
{
curwin->w_cline_height = plines(curwin->w_cursor.lnum);
curwin->w_valid |= VALID_CHEIGHT;
}
}
/*
* validate w_wcol and w_virtcol only. Only for when 'wrap' on!
*/
void
validate_cursor_col()
{
validate_virtcol();
if (!(curwin->w_valid & VALID_WCOL))
{
curwin->w_wcol = curwin->w_virtcol;
if (curwin->w_p_nu)
curwin->w_wcol += 8;
/* long line wrapping, adjust curwin->w_wrow */
if (curwin->w_p_wrap && curwin->w_wcol >= Columns)
curwin->w_wcol = curwin->w_wcol % Columns;
curwin->w_valid |= VALID_WCOL;
}
}
/*
* compute curwin->w_wcol and curwin->w_virtcol.
* Also updates curwin->w_wrow and curwin->w_cline_row.
*/
void
curs_columns(scroll)
int scroll; /* when TRUE, may scroll horizontally */
{
int diff;
int extra;
int new_leftcol;
colnr_t startcol;
colnr_t endcol;
/*
* First make sure that w_topline is valid (after moving the cursor).
*/
update_topline();
/*
* Next make sure that w_cline_row is valid.
*/
if (!(curwin->w_valid & VALID_CROW))
curs_rows(FALSE);
/*
* Compute the number of virtual columns.
*/
getvcol(curwin, &curwin->w_cursor,
&startcol, &(curwin->w_virtcol), &endcol);
/* remove '$' from change command when cursor moves onto it */
if (startcol > dollar_vcol)
dollar_vcol = 0;
curwin->w_wcol = curwin->w_virtcol;
if (curwin->w_p_nu)
{
curwin->w_wcol += 8;
endcol += 8;
}
/*
* Now compute w_wrow, counting screen lines from w_cline_row.
*/
curwin->w_wrow = curwin->w_cline_row;
if (curwin->w_p_wrap) /* long line wrapping, adjust curwin->w_wrow */
{
while (curwin->w_wcol >= Columns)
{
curwin->w_wcol -= Columns;
curwin->w_wrow++;
}
}
else if (scroll) /* no line wrapping, compute curwin->w_leftcol if
* scrolling is on. If scrolling is off,
* curwin->w_leftcol is assumed to be 0 */
{
/* If Cursor is left of the screen, scroll rightwards */
/* If Cursor is right of the screen, scroll leftwards */
if ((extra = (int)startcol - (int)curwin->w_leftcol) < 0 ||
(extra = (int)endcol - (int)(curwin->w_leftcol + Columns) + 1) > 0)
{
if (extra < 0)
diff = -extra;
else
diff = extra;
/* far off, put cursor in middle of window */
if (p_ss == 0 || diff >= Columns / 2)
new_leftcol = curwin->w_wcol - Columns / 2;
else
{
if (diff < p_ss)
diff = p_ss;
if (extra < 0)
new_leftcol = curwin->w_leftcol - diff;
else
new_leftcol = curwin->w_leftcol + diff;
}
if (new_leftcol < 0)
curwin->w_leftcol = 0;
else
curwin->w_leftcol = new_leftcol;
/* screen has to be redrawn with new curwin->w_leftcol */
redraw_later(NOT_VALID);
}
curwin->w_wcol -= curwin->w_leftcol;
}
else if (curwin->w_wcol > (int)curwin->w_leftcol)
curwin->w_wcol -= curwin->w_leftcol;
else
curwin->w_wcol = 0;
/* Cursor past end of screen */
/* happens with line that does not fit on screen */
if (curwin->w_wrow > curwin->w_height - 1)
curwin->w_wrow = curwin->w_height - 1;
curwin->w_valid |= VALID_WCOL|VALID_WROW;
}
/*
* Scroll up 'line_count' lines.
*/
void
scrolldown(line_count)
long line_count;
{
long done = 0; /* total # of physical lines done */
int wrow;
int moved = FALSE;
validate_cursor(); /* w_wrow needs to be valid */
while (line_count--)
{
if (curwin->w_topline == 1)
break;
done += plines(--curwin->w_topline);
--curwin->w_botline; /* approximate w_botline */
curwin->w_valid &= ~VALID_BOTLINE;
}
curwin->w_wrow += done; /* keep w_wrow updated */
curwin->w_cline_row += done; /* keep w_cline_row updated */
/*
* Compute the row number of the last row of the cursor line
* and move it onto the screen.
*/
wrow = curwin->w_wrow;
if (curwin->w_p_wrap)
{
validate_virtcol();
validate_cheight();
wrow += curwin->w_cline_height - 1 - curwin->w_virtcol / Columns;
}
while (wrow >= curwin->w_height - p_so && curwin->w_cursor.lnum > 1)
{
wrow -= plines(curwin->w_cursor.lnum--);
curwin->w_valid &=
~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL);
moved = TRUE;
}
if (moved)
coladvance(curwin->w_curswant);
}
void
scrollup(line_count)
long line_count;
{
curwin->w_topline += line_count;
curwin->w_botline += line_count; /* approximate w_botline */
curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
if (curwin->w_topline > curbuf->b_ml.ml_line_count)
curwin->w_topline = curbuf->b_ml.ml_line_count;
if (curwin->w_cursor.lnum < curwin->w_topline)
{
curwin->w_cursor.lnum = curwin->w_topline;
curwin->w_valid &=
~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL);
coladvance(curwin->w_curswant);
}
}
/*
* Scroll the screen one line down, but don't do it if it would move the
* cursor off the screen.
*/
void
scrolldown_clamp()
{
int end_row;
if (curwin->w_topline == 1)
return;
validate_cursor(); /* w_wrow needs to be valid */
/*
* Compute the row number of the last row of the cursor line
* and make sure it doesn't go off the screen. Make sure the cursor
* doesn't go past 'scrolloff' lines from the screen end.
*/
end_row = curwin->w_wrow + plines(curwin->w_topline - 1);
if (curwin->w_p_wrap)
{
validate_cheight();
validate_virtcol();
end_row += curwin->w_cline_height - 1 - curwin->w_virtcol / Columns;
}
if (end_row < curwin->w_height - p_so)
{
--curwin->w_topline;
--curwin->w_botline; /* approximate w_botline */
curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
}
}
/*
* Scroll the screen one line up, but don't do it if it would move the cursor
* off the screen.
*/
void
scrollup_clamp()
{
int start_row;
if (curwin->w_topline == curbuf->b_ml.ml_line_count)
return;
validate_cursor(); /* w_wrow needs to be valid */
/*
* Compute the row number of the first row of the cursor line
* and make sure it doesn't go off the screen. Make sure the cursor
* doesn't go before 'scrolloff' lines from the screen start.
*/
start_row = curwin->w_wrow - plines(curwin->w_topline);
if (curwin->w_p_wrap)
{
validate_virtcol();
start_row -= curwin->w_virtcol / Columns;
}
if (start_row >= p_so)
{
++curwin->w_topline;
++curwin->w_botline; /* approximate w_botline */
curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
}
}
/*
* insert 'line_count' lines at 'row' in window 'wp'
* if 'invalid' is TRUE the wp->w_lsize_lnum[] is invalidated.
* if 'mayclear' is TRUE the screen will be cleared if it is faster than
* scrolling.
* Returns FAIL if the lines are not inserted, OK for success.
*/
int
win_ins_lines(wp, row, line_count, invalid, mayclear)
WIN *wp;
int row;
int line_count;
int invalid;
int mayclear;
{
int did_delete;
int nextrow;
int lastrow;
int retval;
if (invalid)
wp->w_lsize_valid = 0;
if (!redrawing() || line_count <= 0 || wp->w_height < 5)
return FAIL;
if (line_count > wp->w_height - row)
line_count = wp->w_height - row;
if (mayclear && Rows - line_count < 5) /* only a few lines left: redraw is faster */
{
screenclear(); /* will set wp->w_lsize_valid to 0 */
return FAIL;
}
/*
* Delete all remaining lines
*/
if (row + line_count >= wp->w_height)
{
screen_fill(wp->w_winpos + row, wp->w_winpos + wp->w_height,
0, (int)Columns, ' ', ' ', 0);
return OK;
}
/*
* when scrolling, the message on the command line should be cleared,
* otherwise it will stay there forever.
*/
clear_cmdline = TRUE;
/*
* if the terminal can set a scroll region, use that
*/
if (scroll_region)
{
scroll_region_set(wp, row);
retval = screen_ins_lines(wp->w_winpos + row, 0, line_count,
wp->w_height - row);
scroll_region_reset();
return retval;
}
if (wp->w_next && p_tf) /* don't delete/insert on fast terminal */
return FAIL;
/*
* If there is a next window or a status line, we first try to delete the
* lines at the bottom to avoid messing what is after the window.
* If this fails and there are following windows, don't do anything to avoid
* messing up those windows, better just redraw.
*/
did_delete = FALSE;
if (wp->w_next || wp->w_status_height)
{
if (screen_del_lines(0, wp->w_winpos + wp->w_height - line_count,
line_count, (int)Rows, FALSE) == OK)
did_delete = TRUE;
else if (wp->w_next)
return FAIL;
}
/*
* if no lines deleted, blank the lines that will end up below the window
*/
if (!did_delete)
{
wp->w_redr_status = TRUE;
redraw_cmdline = TRUE;
nextrow = wp->w_winpos + wp->w_height + wp->w_status_height;
lastrow = nextrow + line_count;
if (lastrow > Rows)
lastrow = Rows;
screen_fill(nextrow - line_count, lastrow - line_count,
0, (int)Columns, ' ', ' ', 0);
}
if (screen_ins_lines(0, wp->w_winpos + row, line_count, (int)Rows) == FAIL)
{
/* deletion will have messed up other windows */
if (did_delete)
{
wp->w_redr_status = TRUE;
win_rest_invalid(wp->w_next);
}
return FAIL;
}
return OK;
}
/*
* delete 'line_count' lines at 'row' in window 'wp'
* If 'invalid' is TRUE curwin->w_lsize_lnum[] is invalidated.
* If 'mayclear' is TRUE the screen will be cleared if it is faster than
* scrolling
* Return OK for success, FAIL if the lines are not deleted.
*/
int
win_del_lines(wp, row, line_count, invalid, mayclear)
WIN *wp;
int row;
int line_count;
int invalid;
int mayclear;
{
int retval;
if (invalid)
wp->w_lsize_valid = 0;
if (!redrawing() || line_count <= 0)
return FAIL;
if (line_count > wp->w_height - row)
line_count = wp->w_height - row;
/* only a few lines left: redraw is faster */
if (mayclear && Rows - line_count < 5)
{
screenclear(); /* will set wp->w_lsize_valid to 0 */
return FAIL;
}
/*
* Delete all remaining lines
*/
if (row + line_count >= wp->w_height)
{
screen_fill(wp->w_winpos + row, wp->w_winpos + wp->w_height,
0, (int)Columns, ' ', ' ', 0);
return OK;
}
/*
* when scrolling, the message on the command line should be cleared,
* otherwise it will stay there forever.
*/
clear_cmdline = TRUE;
/*
* if the terminal can set a scroll region, use that
*/
if (scroll_region)
{
scroll_region_set(wp, row);
retval = screen_del_lines(wp->w_winpos + row, 0, line_count,
wp->w_height - row, FALSE);
scroll_region_reset();
return retval;
}
if (wp->w_next && p_tf) /* don't delete/insert on fast terminal */
return FAIL;
if (screen_del_lines(0, wp->w_winpos + row, line_count,
(int)Rows, FALSE) == FAIL)
return FAIL;
/*
* If there are windows or status lines below, try to put them at the
* correct place. If we can't do that, they have to be redrawn.
*/
if (wp->w_next || wp->w_status_height || cmdline_row < Rows - 1)
{
if (screen_ins_lines(0, wp->w_winpos + wp->w_height - line_count,
line_count, (int)Rows) == FAIL)
{
wp->w_redr_status = TRUE;
win_rest_invalid(wp->w_next);
}
}
/*
* If this is the last window and there is no status line, redraw the
* command line later.
*/
else
redraw_cmdline = TRUE;
return OK;
}
/*
* window 'wp' and everything after it is messed up, mark it for redraw
*/
void
win_rest_invalid(wp)
WIN *wp;
{
while (wp)
{
wp->w_lsize_valid = 0;
wp->w_redr_type = NOT_VALID;
wp->w_redr_status = TRUE;
wp = wp->w_next;
}
redraw_cmdline = TRUE;
}
/*
* The rest of the routines in this file perform screen manipulations. The
* given operation is performed physically on the screen. The corresponding
* change is also made to the internal screen image. In this way, the editor
* anticipates the effect of editing changes on the appearance of the screen.
* That way, when we call screenupdate a complete redraw isn't usually
* necessary. Another advantage is that we can keep adding code to anticipate
* screen changes, and in the meantime, everything still works.
*/
/*
* types for inserting or deleting lines
*/
#define USE_T_CAL 1
#define USE_T_CDL 2
#define USE_T_AL 3
#define USE_T_CE 4
#define USE_T_DL 5
#define USE_T_SR 6
#define USE_NL 7
#define USE_T_CD 8
/*
* insert lines on the screen and update NextScreen
* 'end' is the line after the scrolled part. Normally it is Rows.
* When scrolling region used 'off' is the offset from the top for the region.
* 'row' and 'end' are relative to the start of the region.
*
* return FAIL for failure, OK for success.
*/
static int
screen_ins_lines(off, row, line_count, end)
int off;
int row;
int line_count;
int end;
{
int i;
int j;
char_u *temp;
int cursor_row;
int type;
int result_empty;
/*
* FAIL if
* - there is no valid screen
* - the screen has to be redrawn completely
* - the line count is less than one
* - the line count is more than 'ttyscroll'
*/
if (!screen_valid(TRUE) || line_count <= 0 || line_count > p_ttyscroll)
return FAIL;
/*
* There are seven ways to insert lines:
* 1. Use T_CD (clear to end of display) if it exists and the result of
* the insert is just empty lines
* 2. Use T_CAL (insert multiple lines) if it exists and T_AL is not
* present or line_count > 1. It looks better if we do all the inserts
* at once.
* 3. Use T_CDL (delete multiple lines) if it exists and the result of the
* insert is just empty lines and T_CE is not present or line_count >
* 1.
* 4. Use T_AL (insert line) if it exists.
* 5. Use T_CE (erase line) if it exists and the result of the insert is
* just empty lines.
* 6. Use T_DL (delete line) if it exists and the result of the insert is
* just empty lines.
* 7. Use T_SR (scroll reverse) if it exists and inserting at row 0 and
* the 'da' flag is not set or we have clear line capability.
*
* Careful: In a hpterm scroll reverse doesn't work as expected, it moves
* the scrollbar for the window. It does have insert line, use that if it
* exists.
*/
result_empty = (row + line_count >= end);
if (*T_CD != NUL && result_empty)
type = USE_T_CD;
else if (*T_CAL != NUL && (line_count > 1 || *T_AL == NUL))
type = USE_T_CAL;
else if (*T_CDL != NUL && result_empty && (line_count > 1 || *T_CE == NUL))
type = USE_T_CDL;
else if (*T_AL != NUL)
type = USE_T_AL;
else if (*T_CE != NUL && result_empty)
type = USE_T_CE;
else if (*T_DL != NUL && result_empty)
type = USE_T_DL;
else if (*T_SR != NUL && row == 0 && (*T_DA == NUL || *T_CE))
type = USE_T_SR;
else
return FAIL;
/*
* For clearing the lines screen_del_lines is used. This will also take
* care of t_db if necessary.
*/
if (type == USE_T_CD || type == USE_T_CDL ||
type == USE_T_CE || type == USE_T_DL)
return screen_del_lines(off, row, line_count, end, FALSE);
/*
* If text is retained below the screen, first clear or delete as many
* lines at the bottom of the window as are about to be inserted so that
* the deleted lines won't later surface during a screen_del_lines.
*/
if (*T_DB)
screen_del_lines(off, end - line_count, line_count, end, FALSE);
if (*T_CCS != NUL) /* cursor relative to region */
cursor_row = row;
else
cursor_row = row + off;
/*
* Shift LinePointers line_count down to reflect the inserted lines.
* Clear the inserted lines in NextScreen.
*/
row += off;
end += off;
for (i = 0; i < line_count; ++i)
{
j = end - 1 - i;
temp = LinePointers[j];
while ((j -= line_count) >= row)
LinePointers[j + line_count] = LinePointers[j];
LinePointers[j + line_count] = temp;
lineclear(temp);
}
screen_stop_highlight();
windgoto(cursor_row, 0);
if (type == USE_T_CAL)
{
term_append_lines(line_count);
screen_start(); /* don't know where cursor is now */
}
else
{
for (i = 0; i < line_count; i++)
{
if (type == USE_T_AL)
{
if (i && cursor_row != 0)
windgoto(cursor_row, 0);
outstr(T_AL);
}
else /* type == USE_T_SR */
outstr(T_SR);
screen_start(); /* don't know where cursor is now */
}
}
/*
* With scroll-reverse and 'da' flag set we need to clear the lines that
* have been scrolled down into the region.
*/
if (type == USE_T_SR && *T_DA)
{
for (i = 0; i < line_count; ++i)
{
windgoto(off + i, 0);
outstr(T_CE);
screen_start(); /* don't know where cursor is now */
}
}
return OK;
}
/*
* delete lines on the screen and update NextScreen
* 'end' is the line after the scrolled part. Normally it is Rows.
* When scrolling region used 'off' is the offset from the top for the region.
* 'row' and 'end' are relative to the start of the region.
*
* Return OK for success, FAIL if the lines are not deleted.
*/
int
screen_del_lines(off, row, line_count, end, force)
int off;
int row;
int line_count;
int end;
int force; /* even when line_count > p_ttyscroll */
{
int j;
int i;
char_u *temp;
int cursor_row;
int cursor_end;
int result_empty; /* result is empty until end of region */
int can_delete; /* deleting line codes can be used */
int type;
/*
* FAIL if
* - there is no valid screen
* - the screen has to be redrawn completely
* - the line count is less than one
* - the line count is more than 'ttyscroll'
*/
if (!screen_valid(TRUE) || line_count <= 0 ||
(!force && line_count > p_ttyscroll))
return FAIL;
/*
* Check if the rest of the current region will become empty.
*/
result_empty = row + line_count >= end;
/*
* We can delete lines only when 'db' flag not set or when 'ce' option
* available.
*/
can_delete = (*T_DB == NUL || *T_CE);
/*
* There are four ways to delete lines:
* 1. Use T_CD if it exists and the result is empty.
* 2. Use newlines if row == 0 and count == 1 or T_CDL does not exist.
* 3. Use T_CDL (delete multiple lines) if it exists and line_count > 1 or
* none of the other ways work.
* 4. Use T_CE (erase line) if the result is empty.
* 5. Use T_DL (delete line) if it exists.
*/
if (*T_CD != NUL && result_empty)
type = USE_T_CD;
#ifdef BEBOX
/*
* USE_NL does not seem to work in Terminal of DR8 so we set T_DB="" in
* its internal termcap... this works okay for tests which test *T_DB !=
* NUL. It has the disadvantage that the user cannot use any :set t_*
* command to get T_DB (back) to empty_option, only :set term=... will do
* the trick...
* Anyway, this hack will hopefully go away with the next OS release.
* (Olaf Seibert)
*/
else if (row == 0 && T_DB == empty_option
&& (line_count == 1 || *T_CDL == NUL))
#else
else if (row == 0 && (line_count == 1 || *T_CDL == NUL))
#endif
type = USE_NL;
else if (*T_CDL != NUL && line_count > 1 && can_delete)
type = USE_T_CDL;
else if (*T_CE != NUL && result_empty)
type = USE_T_CE;
else if (*T_DL != NUL && can_delete)
type = USE_T_DL;
else if (*T_CDL != NUL && can_delete)
type = USE_T_CDL;
else
return FAIL;
if (*T_CCS != NUL) /* cursor relative to region */
{
cursor_row = row;
cursor_end = end;
}
else
{
cursor_row = row + off;
cursor_end = end + off;
}
/*
* Now shift LinePointers line_count up to reflect the deleted lines.
* Clear the inserted lines in NextScreen.
*/
row += off;
end += off;
for (i = 0; i < line_count; ++i)
{
j = row + i;
temp = LinePointers[j];
while ((j += line_count) <= end - 1)
LinePointers[j - line_count] = LinePointers[j];
LinePointers[j - line_count] = temp;
lineclear(temp);
}
/* delete the lines */
screen_stop_highlight();
if (type == USE_T_CD)
{
windgoto(cursor_row, 0);
outstr(T_CD);
screen_start(); /* don't know where cursor is now */
}
else if (type == USE_T_CDL)
{
windgoto(cursor_row, 0);
term_delete_lines(line_count);
screen_start(); /* don't know where cursor is now */
}
/*
* Deleting lines at top of the screen or scroll region: Just scroll
* the whole screen (scroll region) up by outputting newlines on the
* last line.
*/
else if (type == USE_NL)
{
windgoto(cursor_end - 1, 0);
for (i = line_count; --i >= 0; )
outchar('\n'); /* cursor will remain on same line */
}
else
{
for (i = line_count; --i >= 0; )
{
if (type == USE_T_DL)
{
windgoto(cursor_row, 0);
outstr(T_DL); /* delete a line */
}
else /* type == USE_T_CE */
{
windgoto(cursor_row + i, 0);
outstr(T_CE); /* erase a line */
}
screen_start(); /* don't know where cursor is now */
}
}
/*
* If the 'db' flag is set, we need to clear the lines that have been
* scrolled up at the bottom of the region.
*/
if (*T_DB && (type == USE_T_DL || type == USE_T_CDL))
{
for (i = line_count; i > 0; --i)
{
windgoto(cursor_end - i, 0);
outstr(T_CE); /* erase a line */
screen_start(); /* don't know where cursor is now */
}
}
return OK;
}
/*
* show the current mode and ruler
*
* If clear_cmdline is TRUE, clear the rest of the cmdline.
* If clear_cmdline is FALSE there may be a message there that needs to be
* cleared only if a mode is shown.
* Return the length of the message (0 if no message).
*/
int
showmode()
{
int need_clear = FALSE;
int length = 0;
int do_mode;
int attr;
#ifdef INSERT_EXPAND
int sub_attr;
#endif
do_mode = (p_smd && ((State & INSERT) || restart_edit || VIsual_active));
if (do_mode || Recording)
{
/*
* Don't show mode right now, when not redrawing or inside a mapping.
* Call char_avail() only when we are going to show something, because
* it takes a bit of time.
*/
if (!redrawing() || (char_avail() && !KeyTyped))
{
redraw_cmdline = TRUE; /* show mode later */
return 0;
}
check_for_delay(FALSE);
msg_didout = FALSE; /* never scroll up */
msg_col = 0;
gotocmdline(FALSE);
attr = highlight_attr[HLF_CM]; /* Highlight mode */
if (do_mode)
{
MSG_PUTS_ATTR("--", attr);
#ifdef INSERT_EXPAND
if (edit_submode != NULL) /* CTRL-X in Insert mode */
{
msg_puts_attr(edit_submode, attr);
if (edit_submode_extra != NULL)
{
MSG_PUTS_ATTR(" ", attr); /* add a space in between */
if (edit_submode_highl >= 0)
sub_attr = highlight_attr[edit_submode_highl];
else
sub_attr = attr;
msg_puts_attr(edit_submode_extra, sub_attr);
}
}
else
#endif
{
if (State == INSERT)
{
#ifdef RIGHTLEFT
if (p_ri)
MSG_PUTS_ATTR(" REVERSE", attr);
#endif
MSG_PUTS_ATTR(" INSERT", attr);
}
else if (State == REPLACE)
MSG_PUTS_ATTR(" REPLACE", attr);
else if (restart_edit == 'I')
MSG_PUTS_ATTR(" (insert)", attr);
else if (restart_edit == 'R')
MSG_PUTS_ATTR(" (replace)", attr);
#ifdef RIGHTLEFT
if (p_hkmap)
MSG_PUTS_ATTR(" Hebrew", attr);
#endif
if ((State & INSERT) && p_paste)
MSG_PUTS_ATTR(" (paste)", attr);
if (VIsual_active)
{
MSG_PUTS_ATTR(" VISUAL", attr);
if (VIsual_mode == Ctrl('V'))
MSG_PUTS_ATTR(" BLOCK", attr);
else if (VIsual_mode == 'V')
MSG_PUTS_ATTR(" LINE", attr);
}
}
MSG_PUTS_ATTR(" --", attr);
need_clear = TRUE;
}
if (Recording)
{
MSG_PUTS_ATTR("recording", attr);
need_clear = TRUE;
}
if (need_clear || clear_cmdline)
msg_clr_eos();
msg_didout = FALSE; /* overwrite this message */
length = msg_col;
msg_col = 0;
}
else if (clear_cmdline) /* just clear anything */
{
msg_row = cmdline_row;
msg_col = 0;
msg_clr_eos(); /* will reset clear_cmdline */
}
win_redr_ruler(lastwin, TRUE);
redraw_cmdline = FALSE;
return length;
}
/*
* delete mode message
*/
void
unshowmode()
{
/*
* Don't delete it right now, when not redrawing or insided a mapping.
*/
if (!redrawing() || (char_avail() && !KeyTyped))
redraw_cmdline = TRUE; /* delete mode later */
else if (Recording)
MSG("recording");
else
MSG("");
}
static int
highlight_status(attr)
int *attr;
{
*attr = highlight_attr[HLF_S];
if (*attr) /* can use highlighting */
return ' ';
return '=';
}
/*
* if ruler option is set: show current cursor position
* if always is FALSE, only print if position has changed
*/
void
showruler(always)
int always;
{
win_redr_ruler(curwin, always);
}
void
win_redr_ruler(wp, always)
WIN *wp;
int always;
{
static linenr_t old_lnum = 0;
static colnr_t old_col = 0;
char_u buffer[30];
int row;
int fillchar;
int attr;
int empty_line = FALSE;
if (!p_ru) /* 'ruler' off, don't do anything */
return;
/*
* Check if cursor.lnum is valid, since win_redr_ruler() may be called
* after deleting lines, before cursor.lnum is corrected.
*/
if (wp->w_cursor.lnum > wp->w_buffer->b_ml.ml_line_count)
return;
/*
* Need to update on an empty line always, since we don't know if there
* was a character previously (changing column "1" to "0-1").
*/
if (*ml_get_buf(wp->w_buffer, wp->w_cursor.lnum, FALSE) == NUL)
empty_line = TRUE;
/* Note: don't update w_virtcol, assume that it's correct when the ruler
* needs to be redrawn */
if ((redraw_cmdline || always || empty_line ||
wp->w_cursor.lnum != old_lnum || wp->w_virtcol != old_col))
{
cursor_off();
if (wp->w_status_height)
{
row = wp->w_winpos + wp->w_height;
fillchar = highlight_status(&attr);
}
else
{
row = Rows - 1;
fillchar = ' ';
attr = 0;
}
/*
* Some sprintfs return the length, some return a pointer.
* To avoid portability problems we use strlen() here.
*/
sprintf((char *)buffer, "%ld,",
(wp->w_buffer->b_ml.ml_flags & ML_EMPTY)
? 0L
: (long)(wp->w_cursor.lnum));
col_print(buffer + STRLEN(buffer),
!(State & INSERT) && empty_line
? 0
: (int)wp->w_cursor.col + 1,
(int)wp->w_virtcol + 1);
screen_puts(buffer, row, ru_col, attr);
screen_fill(row, row + 1, ru_col + (int)STRLEN(buffer),
(int)Columns, fillchar, fillchar, attr);
old_lnum = wp->w_cursor.lnum;
old_col = wp->w_virtcol;
}
}
/*
* Check if there should be a delay. Used before clearing or redrawing the
* screen or the command line.
*/
void
check_for_delay(check_msg_scroll)
int check_msg_scroll;
{
if (emsg_on_display || (check_msg_scroll && msg_scroll)
#ifdef SLEEP_IN_EMSG
|| need_sleep
#endif
)
{
ui_delay(1000L, TRUE);
emsg_on_display = FALSE;
if (check_msg_scroll)
msg_scroll = FALSE;
#ifdef SLEEP_IN_EMSG
need_sleep = FALSE;
#endif
}
}
/*
* screen_valid - allocate screen buffers if size changed
* If "clear" is TRUE: clear screen if it has been resized.
* Returns TRUE if there is a valid screen to write to.
* Returns FALSE when starting up and screen not initialized yet.
*/
int
screen_valid(clear)
int clear;
{
screenalloc(clear); /* allocate screen buffers if size changed */
return (NextScreen != NULL);
}
#ifdef USE_MOUSE
/*
* Move the cursor to the specified row and column on the screen.
* Change current window if neccesary. Returns an integer with the
* CURSOR_MOVED bit set if the cursor has moved or unset otherwise.
*
* If flags has MOUSE_FOCUS, then the current window will not be changed, and
* if the mouse is outside the window then the text will scroll, or if the
* mouse was previously on a status line, then the status line may be dragged.
*
* If flags has MOUSE_MAY_VIS, then VIsual mode will be started before the
* cursor is moved unless the cursor was on a status line. Ignoring the
* CURSOR_MOVED bit, this function returns one of IN_UNKNOWN, IN_BUFFER, or
* IN_STATUS_LINE depending on where the cursor was clicked.
*
* If flags has MOUSE_DID_MOVE, nothing is done if the mouse didn't move since
* the last call.
*
* If flags has MOUSE_SETPOS, nothing is done, only the current position is
* remembered.
*/
int
jump_to_mouse(flags, inclusive)
int flags;
int *inclusive; /* used for inclusive operator, can be NULL */
{
static int on_status_line = 0; /* #lines below bottom of window */
static int prev_row = -1;
static int prev_col = -1;
WIN *wp, *old_curwin;
FPOS old_cursor;
int count;
int first;
int row = mouse_row;
int col = mouse_col;
mouse_past_bottom = FALSE;
mouse_past_eol = FALSE;
if ((flags & MOUSE_DID_MOVE) && prev_row == mouse_row &&
prev_col == mouse_col)
return IN_BUFFER; /* mouse pointer didn't move */
prev_row = mouse_row;
prev_col = mouse_col;
if ((flags & MOUSE_SETPOS))
return IN_BUFFER; /* mouse pointer didn't move */
old_curwin = curwin;
old_cursor = curwin->w_cursor;
if (!(flags & MOUSE_FOCUS))
{
if (row < 0 || col < 0) /* check if it makes sense */
return IN_UNKNOWN;
/* find the window where the row is in */
for (wp = firstwin; wp->w_next; wp = wp->w_next)
if (row < wp->w_next->w_winpos)
break;
/*
* winpos and height may change in win_enter()!
*/
row -= wp->w_winpos;
if (row >= wp->w_height) /* In (or below) status line */
on_status_line = row - wp->w_height + 1;
else
on_status_line = 0;
win_enter(wp, TRUE); /* can make wp invalid! */
if (on_status_line) /* In (or below) status line */
{
/* Don't use start_arrow() if we're in the same window */
if (curwin == old_curwin)
return IN_STATUS_LINE;
else
return IN_STATUS_LINE | CURSOR_MOVED;
}
curwin->w_cursor.lnum = curwin->w_topline;
}
else if (on_status_line)
{
/* Drag the status line */
count = row - curwin->w_winpos - curwin->w_height + 1 - on_status_line;
win_drag_status_line(count);
return IN_STATUS_LINE; /* Cursor didn't move */
}
else /* keep_window_focus must be TRUE */
{
row -= curwin->w_winpos;
/*
* When clicking beyond the end of the window, scroll the screen.
* Scroll by however many rows outside the window we are.
*/
if (row < 0)
{
count = 0;
for (first = TRUE; curwin->w_topline > 1; --curwin->w_topline)
{
count += plines(curwin->w_topline - 1);
if (!first && count > -row)
break;
first = FALSE;
}
curwin->w_valid &=
~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
redraw_later(VALID);
row = 0;
}
else if (row >= curwin->w_height)
{
count = 0;
for (first = TRUE; curwin->w_topline < curbuf->b_ml.ml_line_count;
++curwin->w_topline)
{
count += plines(curwin->w_topline);
if (!first && count > row - curwin->w_height + 1)
break;
first = FALSE;
}
redraw_later(VALID);
curwin->w_valid &=
~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
row = curwin->w_height - 1;
}
curwin->w_cursor.lnum = curwin->w_topline;
}
#ifdef RIGHTLEFT
if (curwin->w_p_rl)
col = Columns - 1 - col;
#endif
if (curwin->w_p_wrap) /* lines wrap */
{
while (row)
{
count = plines(curwin->w_cursor.lnum);
if (count > row)
{
col += row * Columns;
break;
}
if (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count)
{
mouse_past_bottom = TRUE;
break;
}
row -= count;
++curwin->w_cursor.lnum;
}
}
else /* lines don't wrap */
{
curwin->w_cursor.lnum += row;
if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
{
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
mouse_past_bottom = TRUE;
}
col += curwin->w_leftcol;
}
if (curwin->w_p_nu) /* skip number in front of the line */
if ((col -= 8) < 0)
col = 0;
curwin->w_curswant = col;
curwin->w_set_curswant = FALSE; /* May still have been TRUE */
if (coladvance(col) == FAIL) /* Mouse click beyond end of line */
{
if (inclusive != NULL)
*inclusive = TRUE;
mouse_past_eol = TRUE;
}
else if (inclusive != NULL)
*inclusive = FALSE;
if ((flags & MOUSE_MAY_VIS) && !VIsual_active)
{
check_visual_highlight();
VIsual = old_cursor;
VIsual_active = TRUE;
#ifdef USE_MOUSE
setmouse();
#endif
if (p_smd)
redraw_cmdline = TRUE; /* show visual mode later */
}
if (curwin == old_curwin && curwin->w_cursor.lnum == old_cursor.lnum &&
curwin->w_cursor.col == old_cursor.col)
return IN_BUFFER; /* Cursor has not moved */
return IN_BUFFER | CURSOR_MOVED; /* Cursor has moved */
}
#endif /* USE_MOUSE */
/*
* Return TRUE if redrawing should currently be done.
*/
int
redrawing()
{
return (!RedrawingDisabled && !(p_lz && char_avail() && !KeyTyped));
}
/*
* Return TRUE if printing messages should currently be done.
*/
int
messaging()
{
return (!(p_lz && char_avail() && !KeyTyped));
}
/*
* move screen 'count' pages up or down and update screen
*
* return FAIL for failure, OK otherwise
*/
int
onepage(dir, count)
int dir;
long count;
{
linenr_t lp;
long n;
int off;
int retval = OK;
if (curbuf->b_ml.ml_line_count == 1) /* nothing to do */
{
beep_flush();
return FAIL;
}
for ( ; count > 0; --count)
{
validate_botline();
/*
* It's an error to move a page up when the first line is already on
* the screen. It's an error to move a page down when the last line
* is on the screen and the topline is 'scrolloff' lines from the
* last line.
*/
if (dir == FORWARD
? ((curwin->w_topline >= curbuf->b_ml.ml_line_count - p_so) &&
curwin->w_botline > curbuf->b_ml.ml_line_count)
: (curwin->w_topline == 1))
{
beep_flush();
retval = FAIL;
break;
}
if (dir == FORWARD)
{
/* at end of file */
if (curwin->w_botline > curbuf->b_ml.ml_line_count)
{
curwin->w_topline = curbuf->b_ml.ml_line_count;
curwin->w_valid &= ~(VALID_WROW|VALID_CROW);
}
else
{
/*
* When there are three or less lines on the screen, move them
* all to above the screen.
*/
if (curwin->w_botline - curwin->w_topline <= 3)
off = 0;
/*
* Make sure at least w_botline gets onto the screen, also
* when 'scrolloff' is non-zero and with very long lines.
*/
else if (plines(curwin->w_botline) +
plines(curwin->w_botline - 1) +
plines(curwin->w_botline - 2) >= curwin->w_height - 2)
off = 0;
else
off = 2;
curwin->w_topline = curwin->w_botline - off;
curwin->w_cursor.lnum = curwin->w_topline;
curwin->w_valid &= ~(VALID_WCOL|VALID_CHEIGHT|VALID_WROW|
VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
}
}
else /* dir == BACKWARDS */
{
lp = curwin->w_topline;
/*
* If the first two lines on the screen are not too big, we keep
* them on the screen.
*/
if ((n = plines(lp)) > curwin->w_height / 2)
--lp;
else if (lp < curbuf->b_ml.ml_line_count &&
n + plines(lp + 1) < curwin->w_height / 2)
++lp;
curwin->w_cursor.lnum = lp;
n = 0;
while (n <= curwin->w_height && lp >= 1)
{
n += plines(lp);
--lp;
}
if (n <= curwin->w_height) /* at begin of file */
{
curwin->w_topline = 1;
curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
}
else if (lp >= curwin->w_topline - 2) /* very long lines */
{
--curwin->w_topline;
comp_botline();
curwin->w_cursor.lnum = curwin->w_botline - 1;
curwin->w_valid &= ~(VALID_WCOL|VALID_CHEIGHT|
VALID_WROW|VALID_CROW);
}
else
{
curwin->w_topline = lp + 2;
curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
}
}
}
cursor_correct();
beginline(MAYBE);
curwin->w_valid &= ~(VALID_WCOL|VALID_WROW|VALID_VIRTCOL);
/*
* Avoid the screen jumping up and down when 'scrolloff' is non-zero.
*/
if (dir == FORWARD && curwin->w_cursor.lnum < curwin->w_topline + p_so)
scroll_cursor_top(1, FALSE);
update_screen(VALID);
return retval;
}
/* #define KEEP_SCREEN_LINE */
void
halfpage(flag, Prenum)
int flag;
linenr_t Prenum;
{
long scrolled = 0;
int i;
int n;
int room;
if (Prenum)
curwin->w_p_scroll = (Prenum > curwin->w_height) ?
curwin->w_height : Prenum;
n = (curwin->w_p_scroll <= curwin->w_height) ?
curwin->w_p_scroll : curwin->w_height;
validate_botline();
room = curwin->w_empty_rows;
if (flag) /* scroll down */
{
while (n > 0 && curwin->w_botline <= curbuf->b_ml.ml_line_count)
{
i = plines(curwin->w_topline);
n -= i;
if (n < 0 && scrolled)
break;
++curwin->w_topline;
curwin->w_valid &= ~(VALID_CROW|VALID_WROW);
scrolled += i;
/*
* Correct w_botline for changed w_topline.
*/
room += i;
do
{
i = plines(curwin->w_botline);
if (i > room)
break;
++curwin->w_botline;
room -= i;
} while (curwin->w_botline <= curbuf->b_ml.ml_line_count);
#ifndef KEEP_SCREEN_LINE
if (curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
{
++curwin->w_cursor.lnum;
curwin->w_valid &= ~(VALID_VIRTCOL|VALID_CHEIGHT|VALID_WCOL);
}
#endif
}
#ifndef KEEP_SCREEN_LINE
/*
* When hit bottom of the file: move cursor down.
*/
if (n > 0)
{
curwin->w_cursor.lnum += n;
if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
}
#else
/* try to put the cursor in the same screen line */
while ((curwin->w_cursor.lnum < curwin->w_topline || scrolled > 0)
&& curwin->w_cursor.lnum < curwin->w_botline - 1)
{
scrolled -= plines(curwin->w_cursor.lnum);
if (scrolled < 0 && curwin->w_cursor.lnum >= curwin->w_topline)
break;
++curwin->w_cursor.lnum;
}
#endif
}
else /* scroll up */
{
while (n > 0 && curwin->w_topline > 1)
{
i = plines(curwin->w_topline - 1);
n -= i;
if (n < 0 && scrolled)
break;
scrolled += i;
--curwin->w_topline;
curwin->w_valid &= ~(VALID_CROW|VALID_WROW|
VALID_BOTLINE|VALID_BOTLINE_AP);
#ifndef KEEP_SCREEN_LINE
if (curwin->w_cursor.lnum > 1)
{
--curwin->w_cursor.lnum;
curwin->w_valid &= ~(VALID_VIRTCOL|VALID_CHEIGHT|VALID_WCOL);
}
#endif
}
#ifndef KEEP_SCREEN_LINE
/*
* When hit top of the file: move cursor up.
*/
if (n > 0)
{
if (curwin->w_cursor.lnum > (linenr_t)n)
curwin->w_cursor.lnum -= n;
else
curwin->w_cursor.lnum = 1;
}
#else
/* try to put the cursor in the same screen line */
scrolled += n; /* move cursor when topline is 1 */
while (curwin->w_cursor.lnum > curwin->w_topline &&
(scrolled > 0 || curwin->w_cursor.lnum >= curwin->w_botline))
{
scrolled -= plines(curwin->w_cursor.lnum - 1);
if (scrolled < 0 && curwin->w_cursor.lnum < curwin->w_botline)
break;
--curwin->w_cursor.lnum;
}
#endif
}
cursor_correct();
beginline(MAYBE);
update_screen(VALID);
}
/*
* Give an introductory message about Vim.
* Only used when starting Vim on an empty file, without a filename.
*/
static void
intro_message()
{
int i;
int row;
int col;
static char *(lines[]) =
{
"VIM - Vi IMproved",
"",
"version ",
"by Bram Moolenaar",
"",
"type :help<Enter> or <F1> for on-line help",
"",
"Vim is freely distributable",
"type :help uganda<Enter> if you like Vim",
"",
"type :q<Enter> to exit"
};
row = (Rows - sizeof(lines) / sizeof(char *)) / 2;
if (row > 2)
{
for (i = 0; i < (int)(sizeof(lines) / sizeof(char *)); ++i)
{
col = strlen(lines[i]);
if (i == 2)
col += strlen(mediumVersion);
col = (Columns - col) / 2;
if (col < 0)
col = 0;
screen_puts((char_u *)lines[i], row, col, 0);
if (i == 2)
screen_puts((char_u *)mediumVersion, row, col + 8, 0);
++row;
}
}
}
void
do_intro()
{
screenclear();
intro_message();
wait_return(TRUE);
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.