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.