mirror of
https://github.com/vim/vim
synced 2025-03-26 03:35:11 +01:00
Problem: wrong drawing in GUI with setcellwidth() (after v9.1.0344) Solution: move gui.in_use condition (h-east) closes: #14600 Signed-off-by: h-east <h.east.727@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
5050 lines
126 KiB
C
5050 lines
126 KiB
C
/* vi:set ts=8 sts=4 sw=4 noet:
|
|
*
|
|
* VIM - Vi IMproved by Bram Moolenaar
|
|
*
|
|
* Do ":help uganda" in Vim to read copying and usage conditions.
|
|
* Do ":help credits" in Vim to see a list of people who contributed.
|
|
* See README.txt for an overview of the Vim source code.
|
|
*/
|
|
|
|
/*
|
|
* screen.c: Lower level code for displaying on the screen.
|
|
*
|
|
* Output to the screen (console, terminal emulator or GUI window) is minimized
|
|
* by remembering what is already on the screen, and only updating the parts
|
|
* that changed.
|
|
*
|
|
* ScreenLines[off] Contains a copy of the whole screen, as it is currently
|
|
* displayed (excluding text written by external commands).
|
|
* ScreenAttrs[off] Contains the associated attributes.
|
|
* ScreenCols[off] Contains the virtual columns in the line. -1 means not
|
|
* available or before buffer text.
|
|
*
|
|
* LineOffset[row] Contains the offset into ScreenLines*[], ScreenAttrs[]
|
|
* and ScreenCols[] for each line.
|
|
* LineWraps[row] Flag for each line whether it wraps to the next line.
|
|
*
|
|
* For double-byte characters, two consecutive bytes in ScreenLines[] can form
|
|
* one character which occupies two display cells.
|
|
* For UTF-8 a multi-byte character is converted to Unicode and stored in
|
|
* ScreenLinesUC[]. ScreenLines[] contains the first byte only. For an ASCII
|
|
* character without composing chars ScreenLinesUC[] will be 0 and
|
|
* ScreenLinesC[][] is not used. When the character occupies two display
|
|
* cells the next byte in ScreenLines[] is 0.
|
|
* ScreenLinesC[][] contain up to 'maxcombine' composing characters
|
|
* (drawn on top of the first character). There is 0 after the last one used.
|
|
* ScreenLines2[] is only used for euc-jp to store the second byte if the
|
|
* first byte is 0x8e (single-width character).
|
|
*
|
|
* The screen_*() functions write to the screen and handle updating
|
|
* ScreenLines[].
|
|
*/
|
|
|
|
#include "vim.h"
|
|
|
|
/*
|
|
* The attributes that are actually active for writing to the screen.
|
|
*/
|
|
static int screen_attr = 0;
|
|
|
|
static void screen_char_2(unsigned off, int row, int col);
|
|
static int screenclear2(int doclear);
|
|
static void lineclear(unsigned off, int width, int attr);
|
|
static void lineinvalid(unsigned off, int width);
|
|
static int win_do_lines(win_T *wp, int row, int line_count, int mayclear, int del, int clear_attr);
|
|
static void win_rest_invalid(win_T *wp);
|
|
static void msg_pos_mode(void);
|
|
static void recording_mode(int attr);
|
|
|
|
// Ugly global: overrule attribute used by screen_char()
|
|
static int screen_char_attr = 0;
|
|
|
|
#if defined(FEAT_CONCEAL) || defined(PROTO)
|
|
/*
|
|
* Return TRUE if the cursor line in window "wp" may be concealed, according
|
|
* to the 'concealcursor' option.
|
|
*/
|
|
int
|
|
conceal_cursor_line(win_T *wp)
|
|
{
|
|
int c;
|
|
|
|
if (*wp->w_p_cocu == NUL)
|
|
return FALSE;
|
|
if (get_real_state() & MODE_VISUAL)
|
|
c = 'v';
|
|
else if (State & MODE_INSERT)
|
|
c = 'i';
|
|
else if (State & MODE_NORMAL)
|
|
c = 'n';
|
|
else if (State & MODE_CMDLINE)
|
|
c = 'c';
|
|
else
|
|
return FALSE;
|
|
return vim_strchr(wp->w_p_cocu, c) != NULL;
|
|
}
|
|
|
|
/*
|
|
* Check if the cursor line needs to be redrawn because of 'concealcursor'.
|
|
* To be called after changing the state, "was_concealed" is the value of
|
|
* "conceal_cursor_line()" before the change.
|
|
* "
|
|
*/
|
|
void
|
|
conceal_check_cursor_line(int was_concealed)
|
|
{
|
|
if (curwin->w_p_cole <= 0 || conceal_cursor_line(curwin) == was_concealed)
|
|
return;
|
|
|
|
int wcol = curwin->w_wcol;
|
|
|
|
need_cursor_line_redraw = TRUE;
|
|
// Need to recompute cursor column, e.g., when starting Visual mode
|
|
// without concealing.
|
|
curs_columns(TRUE);
|
|
|
|
// When concealing now w_wcol will be computed wrong, keep the previous
|
|
// value, it will be updated in win_line().
|
|
if (!was_concealed)
|
|
curwin->w_wcol = wcol;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Get 'wincolor' attribute for window "wp". If not set and "wp" is a popup
|
|
* window then get the "Pmenu" highlight attribute.
|
|
*/
|
|
int
|
|
get_wcr_attr(win_T *wp)
|
|
{
|
|
int wcr_attr = 0;
|
|
|
|
if (*wp->w_p_wcr != NUL)
|
|
wcr_attr = syn_name2attr(wp->w_p_wcr);
|
|
#ifdef FEAT_PROP_POPUP
|
|
else if (WIN_IS_POPUP(wp))
|
|
{
|
|
if (wp->w_popup_flags & POPF_INFO)
|
|
wcr_attr = HL_ATTR(HLF_PSI); // PmenuSel
|
|
else
|
|
wcr_attr = HL_ATTR(HLF_PNI); // Pmenu
|
|
}
|
|
#endif
|
|
return wcr_attr;
|
|
}
|
|
|
|
/*
|
|
* Call screen_fill() with the columns adjusted for 'rightleft' if needed.
|
|
* Return the new offset.
|
|
*/
|
|
static int
|
|
screen_fill_end(
|
|
win_T *wp,
|
|
int c1,
|
|
int c2,
|
|
int off,
|
|
int width,
|
|
int row,
|
|
int endrow,
|
|
int attr)
|
|
{
|
|
int nn = off + width;
|
|
|
|
if (nn > wp->w_width)
|
|
nn = wp->w_width;
|
|
#ifdef FEAT_RIGHTLEFT
|
|
if (wp->w_p_rl)
|
|
{
|
|
screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + endrow,
|
|
W_ENDCOL(wp) - nn, (int)W_ENDCOL(wp) - off,
|
|
c1, c2, attr);
|
|
}
|
|
else
|
|
#endif
|
|
screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + endrow,
|
|
wp->w_wincol + off, (int)wp->w_wincol + nn,
|
|
c1, c2, attr);
|
|
return nn;
|
|
}
|
|
|
|
/*
|
|
* Clear lines near the end the window and mark the unused lines with "c1".
|
|
* use "c2" as the filler character.
|
|
* When "draw_margin" is TRUE then draw the sign, fold and number columns.
|
|
*/
|
|
void
|
|
win_draw_end(
|
|
win_T *wp,
|
|
int c1,
|
|
int c2,
|
|
int draw_margin,
|
|
int row,
|
|
int endrow,
|
|
hlf_T hl)
|
|
{
|
|
int n = 0;
|
|
int attr = HL_ATTR(hl);
|
|
int wcr_attr = get_wcr_attr(wp);
|
|
|
|
attr = hl_combine_attr(wcr_attr, attr);
|
|
|
|
if (draw_margin)
|
|
{
|
|
#ifdef FEAT_FOLDING
|
|
int fdc = compute_foldcolumn(wp, 0);
|
|
|
|
if (fdc > 0)
|
|
// draw the fold column
|
|
n = screen_fill_end(wp, ' ', ' ', n, fdc,
|
|
row, endrow, hl_combine_attr(wcr_attr, HL_ATTR(HLF_FC)));
|
|
#endif
|
|
#ifdef FEAT_SIGNS
|
|
if (signcolumn_on(wp))
|
|
// draw the sign column
|
|
n = screen_fill_end(wp, ' ', ' ', n, 2,
|
|
row, endrow, hl_combine_attr(wcr_attr, HL_ATTR(HLF_SC)));
|
|
#endif
|
|
if ((wp->w_p_nu || wp->w_p_rnu)
|
|
&& vim_strchr(p_cpo, CPO_NUMCOL) == NULL)
|
|
// draw the number column
|
|
n = screen_fill_end(wp, ' ', ' ', n, number_width(wp) + 1,
|
|
row, endrow, hl_combine_attr(wcr_attr, HL_ATTR(HLF_N)));
|
|
}
|
|
|
|
#ifdef FEAT_RIGHTLEFT
|
|
if (wp->w_p_rl)
|
|
{
|
|
screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + endrow,
|
|
wp->w_wincol, W_ENDCOL(wp) - 1 - n,
|
|
c2, c2, attr);
|
|
screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + endrow,
|
|
W_ENDCOL(wp) - 1 - n, W_ENDCOL(wp) - n,
|
|
c1, c2, attr);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + endrow,
|
|
wp->w_wincol + n, (int)W_ENDCOL(wp),
|
|
c1, c2, attr);
|
|
}
|
|
|
|
set_empty_rows(wp, row);
|
|
}
|
|
|
|
#if defined(FEAT_FOLDING) || defined(PROTO)
|
|
/*
|
|
* Compute the width of the foldcolumn. Based on 'foldcolumn' and how much
|
|
* space is available for window "wp", minus "col".
|
|
*/
|
|
int
|
|
compute_foldcolumn(win_T *wp, int col)
|
|
{
|
|
int fdc = wp->w_p_fdc;
|
|
int wmw = wp == curwin && p_wmw == 0 ? 1 : p_wmw;
|
|
int wwidth = wp->w_width;
|
|
|
|
if (fdc > wwidth - (col + wmw))
|
|
fdc = wwidth - (col + wmw);
|
|
return fdc;
|
|
}
|
|
|
|
/*
|
|
* Fill the foldcolumn at "p" for window "wp".
|
|
* Only to be called when 'foldcolumn' > 0.
|
|
* Returns the number of bytes stored in 'p'. When non-multibyte characters are
|
|
* used for the fold column markers, this is equal to 'fdc' setting. Otherwise,
|
|
* this will be greater than 'fdc'.
|
|
*/
|
|
size_t
|
|
fill_foldcolumn(
|
|
char_u *p,
|
|
win_T *wp,
|
|
int closed, // TRUE of FALSE
|
|
linenr_T lnum) // current line number
|
|
{
|
|
int i = 0;
|
|
int level;
|
|
int first_level;
|
|
int empty;
|
|
int fdc = compute_foldcolumn(wp, 0);
|
|
size_t byte_counter = 0;
|
|
int symbol = 0;
|
|
int len = 0;
|
|
|
|
// Init to all spaces.
|
|
vim_memset(p, ' ', MAX_MCO * fdc + 1);
|
|
|
|
level = win_foldinfo.fi_level;
|
|
empty = (fdc == 1) ? 0 : 1;
|
|
|
|
// If the column is too narrow, we start at the lowest level that
|
|
// fits and use numbers to indicate the depth.
|
|
first_level = level - fdc - closed + 1 + empty;
|
|
if (first_level < 1)
|
|
first_level = 1;
|
|
|
|
for (i = 0; i < MIN(fdc, level); i++)
|
|
{
|
|
if (win_foldinfo.fi_lnum == lnum
|
|
&& first_level + i >= win_foldinfo.fi_low_level)
|
|
symbol = wp->w_fill_chars.foldopen;
|
|
else if (first_level == 1)
|
|
symbol = wp->w_fill_chars.foldsep;
|
|
else if (first_level + i <= 9)
|
|
symbol = '0' + first_level + i;
|
|
else
|
|
symbol = '>';
|
|
|
|
len = utf_char2bytes(symbol, &p[byte_counter]);
|
|
byte_counter += len;
|
|
if (first_level + i >= level)
|
|
{
|
|
i++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (closed)
|
|
{
|
|
if (symbol != 0)
|
|
{
|
|
// rollback length and the character
|
|
byte_counter -= len;
|
|
if (len > 1)
|
|
// for a multibyte character, erase all the bytes
|
|
vim_memset(p + byte_counter, ' ', len);
|
|
}
|
|
symbol = wp->w_fill_chars.foldclosed;
|
|
len = utf_char2bytes(symbol, &p[byte_counter]);
|
|
byte_counter += len;
|
|
}
|
|
|
|
return MAX(byte_counter + (fdc - i), (size_t)fdc);
|
|
}
|
|
#endif // FEAT_FOLDING
|
|
|
|
/*
|
|
* Return if the composing characters at "off_from" and "off_to" differ.
|
|
* Only to be used when ScreenLinesUC[off_from] != 0.
|
|
*/
|
|
static int
|
|
comp_char_differs(int off_from, int off_to)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < Screen_mco; ++i)
|
|
{
|
|
if (ScreenLinesC[i][off_from] != ScreenLinesC[i][off_to])
|
|
return TRUE;
|
|
if (ScreenLinesC[i][off_from] == 0)
|
|
break;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Check whether the given character needs redrawing:
|
|
* - the (first byte of the) character is different
|
|
* - the attributes are different
|
|
* - the character is multi-byte and the next byte is different
|
|
* - the character is two cells wide and the second cell differs.
|
|
*/
|
|
static int
|
|
char_needs_redraw(int off_from, int off_to, int cols)
|
|
{
|
|
if (cols > 0
|
|
&& ((ScreenLines[off_from] != ScreenLines[off_to]
|
|
|| ScreenAttrs[off_from] != ScreenAttrs[off_to])
|
|
|| (enc_dbcs != 0
|
|
&& MB_BYTE2LEN(ScreenLines[off_from]) > 1
|
|
&& (enc_dbcs == DBCS_JPNU && ScreenLines[off_from] == 0x8e
|
|
? ScreenLines2[off_from] != ScreenLines2[off_to]
|
|
: (cols > 1 && ScreenLines[off_from + 1]
|
|
!= ScreenLines[off_to + 1])))
|
|
|| (enc_utf8
|
|
&& (ScreenLinesUC[off_from] != ScreenLinesUC[off_to]
|
|
|| (ScreenLinesUC[off_from] != 0
|
|
&& comp_char_differs(off_from, off_to))
|
|
|| ((*mb_off2cells)(off_from, off_from + cols) > 1
|
|
&& ScreenLines[off_from + 1]
|
|
!= ScreenLines[off_to + 1])))))
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
#if defined(FEAT_TERMINAL) || defined(PROTO)
|
|
/*
|
|
* Return the index in ScreenLines[] for the current screen line.
|
|
*/
|
|
int
|
|
screen_get_current_line_off(void)
|
|
{
|
|
return (int)(current_ScreenLine - ScreenLines);
|
|
}
|
|
#endif
|
|
|
|
#ifdef FEAT_PROP_POPUP
|
|
/*
|
|
* Return TRUE if this position has a higher level popup or this cell is
|
|
* transparent in the current popup.
|
|
*/
|
|
static int
|
|
blocked_by_popup(int row, int col)
|
|
{
|
|
int off;
|
|
|
|
if (!popup_visible)
|
|
return FALSE;
|
|
off = row * screen_Columns + col;
|
|
return popup_mask[off] > screen_zindex || popup_transparent[off];
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Reset the highlighting. Used before clearing the screen.
|
|
*/
|
|
void
|
|
reset_screen_attr(void)
|
|
{
|
|
#ifdef FEAT_GUI
|
|
if (gui.in_use)
|
|
// Use a code that will reset gui.highlight_mask in
|
|
// gui_stop_highlight().
|
|
screen_attr = HL_ALL + 1;
|
|
else
|
|
#endif
|
|
// Use attributes that is very unlikely to appear in text.
|
|
screen_attr = HL_BOLD | HL_UNDERLINE | HL_INVERSE | HL_STRIKETHROUGH;
|
|
}
|
|
|
|
/*
|
|
* Return TRUE if the character at "row" / "col" is under the popup menu and it
|
|
* will be redrawn soon or it is under another popup.
|
|
*/
|
|
static int
|
|
skip_for_popup(int row, int col)
|
|
{
|
|
// Popup windows with zindex higher than POPUPMENU_ZINDEX go on top.
|
|
if (pum_under_menu(row, col, TRUE)
|
|
#ifdef FEAT_PROP_POPUP
|
|
&& screen_zindex <= POPUPMENU_ZINDEX
|
|
#endif
|
|
)
|
|
return TRUE;
|
|
#ifdef FEAT_PROP_POPUP
|
|
if (blocked_by_popup(row, col))
|
|
return TRUE;
|
|
#endif
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Move one "cooked" screen line to the screen, but only the characters that
|
|
* have actually changed. Handle insert/delete character.
|
|
* "coloff" gives the first column on the screen for this line.
|
|
* "endcol" gives the columns where valid characters are.
|
|
* "clear_width" is the width of the window. It's > 0 if the rest of the line
|
|
* needs to be cleared, negative otherwise.
|
|
* "flags" can have bits:
|
|
* SLF_POPUP popup window
|
|
* SLF_RIGHTLEFT rightleft window:
|
|
* When TRUE and "clear_width" > 0, clear columns 0 to "endcol"
|
|
* When FALSE and "clear_width" > 0, clear columns "endcol" to "clear_width"
|
|
* SLF_INC_VCOL:
|
|
* When FALSE, use "last_vcol" for ScreenCols[] of the columns to clear.
|
|
* When TRUE, use an increasing sequence starting from "last_vcol + 1" for
|
|
* ScreenCols[] of the columns to clear.
|
|
*/
|
|
void
|
|
screen_line(
|
|
win_T *wp,
|
|
int row,
|
|
int coloff,
|
|
int endcol,
|
|
int clear_width,
|
|
colnr_T last_vcol,
|
|
int flags UNUSED)
|
|
{
|
|
unsigned off_from;
|
|
unsigned off_to;
|
|
unsigned max_off_from;
|
|
unsigned max_off_to;
|
|
int col = 0;
|
|
int hl;
|
|
int force = FALSE; // force update rest of the line
|
|
int redraw_this // bool: does character need redraw?
|
|
#ifdef FEAT_GUI
|
|
= TRUE // For GUI when while-loop empty
|
|
#endif
|
|
;
|
|
int redraw_next; // redraw_this for next character
|
|
#ifdef FEAT_GUI_MSWIN
|
|
int changed_this; // TRUE if character changed
|
|
int changed_next; // TRUE if next character changed
|
|
#endif
|
|
int clear_next = FALSE;
|
|
int char_cells; // 1: normal char
|
|
// 2: occupies two display cells
|
|
|
|
// Check for illegal row and col, just in case.
|
|
if (row >= Rows)
|
|
row = Rows - 1;
|
|
if (endcol > Columns)
|
|
endcol = Columns;
|
|
|
|
# ifdef FEAT_CLIPBOARD
|
|
clip_may_clear_selection(row, row);
|
|
# endif
|
|
|
|
off_from = (unsigned)(current_ScreenLine - ScreenLines);
|
|
off_to = LineOffset[row] + coloff;
|
|
max_off_from = off_from + screen_Columns;
|
|
max_off_to = LineOffset[row] + screen_Columns;
|
|
|
|
#ifdef FEAT_RIGHTLEFT
|
|
if (flags & SLF_RIGHTLEFT)
|
|
{
|
|
// Clear rest first, because it's left of the text.
|
|
if (clear_width > 0)
|
|
{
|
|
int clear_start = col;
|
|
|
|
while (col <= endcol && ScreenLines[off_to] == ' '
|
|
&& ScreenAttrs[off_to] == 0
|
|
&& (!enc_utf8 || ScreenLinesUC[off_to] == 0))
|
|
{
|
|
++off_to;
|
|
++col;
|
|
}
|
|
if (col <= endcol)
|
|
screen_fill(row, row + 1, col + coloff,
|
|
endcol + coloff + 1, ' ', ' ', 0);
|
|
|
|
for (int i = endcol; i >= clear_start; i--)
|
|
ScreenCols[off_to + (i - col)] =
|
|
(flags & SLF_INC_VCOL) ? ++last_vcol : last_vcol;
|
|
}
|
|
col = endcol + 1;
|
|
off_to = LineOffset[row] + col + coloff;
|
|
off_from += col;
|
|
endcol = (clear_width > 0 ? clear_width : -clear_width);
|
|
}
|
|
#endif // FEAT_RIGHTLEFT
|
|
|
|
#ifdef FEAT_PROP_POPUP
|
|
// First char of a popup window may go on top of the right half of a
|
|
// double-wide character. Clear the left half to avoid it getting the popup
|
|
// window background color.
|
|
if (coloff > 0 && enc_utf8
|
|
&& ScreenLines[off_to] == 0
|
|
&& ScreenLinesUC[off_to - 1] != 0
|
|
&& (*mb_char2cells)(ScreenLinesUC[off_to - 1]) > 1)
|
|
{
|
|
ScreenLines[off_to - 1] = ' ';
|
|
ScreenLinesUC[off_to - 1] = 0;
|
|
screen_char(off_to - 1, row, col + coloff - 1);
|
|
}
|
|
#endif
|
|
|
|
redraw_next = char_needs_redraw(off_from, off_to, endcol - col);
|
|
#ifdef FEAT_GUI_MSWIN
|
|
changed_next = redraw_next;
|
|
#endif
|
|
|
|
while (col < endcol)
|
|
{
|
|
if (has_mbyte && (col + 1 < endcol))
|
|
char_cells = (*mb_off2cells)(off_from, max_off_from);
|
|
else
|
|
char_cells = 1;
|
|
|
|
redraw_this = redraw_next;
|
|
redraw_next = force || char_needs_redraw(off_from + char_cells,
|
|
off_to + char_cells, endcol - col - char_cells);
|
|
|
|
#ifdef FEAT_GUI
|
|
# ifdef FEAT_GUI_MSWIN
|
|
changed_this = changed_next;
|
|
changed_next = redraw_next;
|
|
# endif
|
|
// If the next character was bold, then redraw the current character to
|
|
// remove any pixels that might have spilt over into us. This only
|
|
// happens in the GUI.
|
|
// With MS-Windows antialiasing may also cause pixels to spill over
|
|
// from a previous character, no matter attributes, always redraw if a
|
|
// character changed.
|
|
if (redraw_next && gui.in_use)
|
|
{
|
|
# ifndef FEAT_GUI_MSWIN
|
|
hl = ScreenAttrs[off_to + char_cells];
|
|
if (hl > HL_ALL)
|
|
hl = syn_attr2attr(hl);
|
|
if (hl & HL_BOLD)
|
|
# endif
|
|
redraw_this = TRUE;
|
|
}
|
|
#endif
|
|
// Do not redraw if under the popup menu.
|
|
if (redraw_this && skip_for_popup(row, col + coloff))
|
|
redraw_this = FALSE;
|
|
|
|
if (redraw_this)
|
|
{
|
|
/*
|
|
* 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 its highlighting. Need
|
|
* to clear the rest of the line, and force redrawing it
|
|
* completely.
|
|
*/
|
|
if ( p_wiv
|
|
&& !force
|
|
#ifdef FEAT_GUI
|
|
&& !gui.in_use
|
|
#endif
|
|
&& ScreenAttrs[off_to] != 0
|
|
&& ScreenAttrs[off_from] != ScreenAttrs[off_to])
|
|
{
|
|
/*
|
|
* Need to remove highlighting attributes here.
|
|
*/
|
|
windgoto(row, col + coloff);
|
|
out_str(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
|
|
redraw_next = TRUE; // or else next char would miss out
|
|
|
|
/*
|
|
* If the previous character was highlighted, need to stop
|
|
* highlighting at this character.
|
|
*/
|
|
if (col + coloff > 0 && ScreenAttrs[off_to - 1] != 0)
|
|
{
|
|
screen_attr = ScreenAttrs[off_to - 1];
|
|
term_windgoto(row, col + coloff);
|
|
screen_stop_highlight();
|
|
}
|
|
else
|
|
screen_attr = 0; // highlighting has stopped
|
|
}
|
|
if (enc_dbcs != 0)
|
|
{
|
|
// Check if overwriting a double-byte with a single-byte or
|
|
// the other way around requires another character to be
|
|
// redrawn. For UTF-8 this isn't needed, because comparing
|
|
// ScreenLinesUC[] is sufficient.
|
|
if (char_cells == 1
|
|
&& col + 1 < endcol
|
|
&& (*mb_off2cells)(off_to, max_off_to) > 1)
|
|
{
|
|
// Writing a single-cell character over a double-cell
|
|
// character: need to redraw the next cell.
|
|
ScreenLines[off_to + 1] = 0;
|
|
redraw_next = TRUE;
|
|
}
|
|
else if (char_cells == 2
|
|
&& col + 2 < endcol
|
|
&& (*mb_off2cells)(off_to, max_off_to) == 1
|
|
&& (*mb_off2cells)(off_to + 1, max_off_to) > 1)
|
|
{
|
|
// Writing the second half of a double-cell character over
|
|
// a double-cell character: need to redraw the second
|
|
// cell.
|
|
ScreenLines[off_to + 2] = 0;
|
|
redraw_next = TRUE;
|
|
}
|
|
|
|
if (enc_dbcs == DBCS_JPNU)
|
|
ScreenLines2[off_to] = ScreenLines2[off_from];
|
|
}
|
|
// When writing a single-width character over a double-width
|
|
// character and at the end of the redrawn text, need to clear out
|
|
// the right half of the old character.
|
|
// Also required when writing the right half of a double-width
|
|
// char over the left half of an existing one.
|
|
if (has_mbyte && col + char_cells == endcol
|
|
&& ((char_cells == 1
|
|
&& (*mb_off2cells)(off_to, max_off_to) > 1)
|
|
|| (char_cells == 2
|
|
&& (*mb_off2cells)(off_to, max_off_to) == 1
|
|
&& (*mb_off2cells)(off_to + 1, max_off_to) > 1)))
|
|
clear_next = TRUE;
|
|
|
|
ScreenLines[off_to] = ScreenLines[off_from];
|
|
if (enc_utf8)
|
|
{
|
|
ScreenLinesUC[off_to] = ScreenLinesUC[off_from];
|
|
if (ScreenLinesUC[off_from] != 0)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < Screen_mco; ++i)
|
|
ScreenLinesC[i][off_to] = ScreenLinesC[i][off_from];
|
|
}
|
|
}
|
|
if (char_cells == 2)
|
|
ScreenLines[off_to + 1] = ScreenLines[off_from + 1];
|
|
|
|
#if defined(FEAT_GUI) || defined(UNIX)
|
|
// The bold trick makes a single column of pixels appear in the
|
|
// next character. When a bold character is removed, the next
|
|
// character should be redrawn too. This happens for our own GUI
|
|
// and for some xterms.
|
|
if (
|
|
# ifdef FEAT_GUI
|
|
gui.in_use
|
|
# endif
|
|
# if defined(FEAT_GUI) && defined(UNIX)
|
|
||
|
|
# endif
|
|
# ifdef UNIX
|
|
term_is_xterm
|
|
# endif
|
|
)
|
|
{
|
|
hl = ScreenAttrs[off_to];
|
|
if (hl > HL_ALL)
|
|
hl = syn_attr2attr(hl);
|
|
if (hl & HL_BOLD)
|
|
redraw_next = TRUE;
|
|
}
|
|
#endif
|
|
#ifdef FEAT_GUI_MSWIN
|
|
// MS-Windows antialiasing may spill over to the next character,
|
|
// redraw that one if this one changed, no matter attributes.
|
|
if (gui.in_use && changed_this)
|
|
redraw_next = TRUE;
|
|
#endif
|
|
ScreenAttrs[off_to] = ScreenAttrs[off_from];
|
|
|
|
// For simplicity set the attributes of second half of a
|
|
// double-wide character equal to the first half.
|
|
if (char_cells == 2)
|
|
ScreenAttrs[off_to + 1] = ScreenAttrs[off_from];
|
|
|
|
if (enc_dbcs != 0 && char_cells == 2)
|
|
screen_char_2(off_to, row, col + coloff);
|
|
else
|
|
screen_char(off_to, row, col + coloff);
|
|
}
|
|
else if ( p_wiv
|
|
#ifdef FEAT_GUI
|
|
&& !gui.in_use
|
|
#endif
|
|
&& col + coloff > 0)
|
|
{
|
|
if (ScreenAttrs[off_to] == ScreenAttrs[off_to - 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 != 0)
|
|
screen_stop_highlight();
|
|
}
|
|
|
|
ScreenCols[off_to] = ScreenCols[off_from];
|
|
if (char_cells == 2)
|
|
ScreenCols[off_to + 1] = ScreenCols[off_from + 1];
|
|
|
|
off_to += char_cells;
|
|
off_from += char_cells;
|
|
col += char_cells;
|
|
}
|
|
|
|
if (clear_next && !skip_for_popup(row, col + coloff))
|
|
{
|
|
// Clear the second half of a double-wide character of which the left
|
|
// half was overwritten with a single-wide character.
|
|
ScreenLines[off_to] = ' ';
|
|
if (enc_utf8)
|
|
ScreenLinesUC[off_to] = 0;
|
|
screen_char(off_to, row, col + coloff);
|
|
}
|
|
|
|
if (clear_width > 0
|
|
#ifdef FEAT_RIGHTLEFT
|
|
&& !(flags & SLF_RIGHTLEFT)
|
|
#endif
|
|
)
|
|
{
|
|
#ifdef FEAT_GUI
|
|
int startCol = col;
|
|
#endif
|
|
|
|
// blank out the rest of the line
|
|
while (col < clear_width && ScreenLines[off_to] == ' '
|
|
&& ScreenAttrs[off_to] == 0
|
|
&& (!enc_utf8 || ScreenLinesUC[off_to] == 0))
|
|
{
|
|
ScreenCols[off_to] =
|
|
(flags & SLF_INC_VCOL) ? ++last_vcol : last_vcol;
|
|
++off_to;
|
|
++col;
|
|
}
|
|
if (col < clear_width)
|
|
{
|
|
#ifdef FEAT_GUI
|
|
/*
|
|
* In the GUI, clearing the rest of the line may leave pixels
|
|
* behind if the first character cleared was bold. Some bold
|
|
* fonts spill over the left. In this case we redraw the previous
|
|
* character too. If we didn't skip any blanks above, then we
|
|
* only redraw if the character wasn't already redrawn anyway.
|
|
*/
|
|
if (gui.in_use && (col > startCol || !redraw_this))
|
|
{
|
|
hl = ScreenAttrs[off_to];
|
|
if (hl > HL_ALL || (hl & HL_BOLD))
|
|
{
|
|
int prev_cells = 1;
|
|
|
|
if (enc_utf8)
|
|
// for utf-8, ScreenLines[char_offset + 1] == 0 means
|
|
// that its width is 2.
|
|
prev_cells = ScreenLines[off_to - 1] == 0 ? 2 : 1;
|
|
else if (enc_dbcs != 0)
|
|
{
|
|
// find previous character by counting from first
|
|
// column and get its width.
|
|
unsigned off = LineOffset[row];
|
|
unsigned max_off = LineOffset[row] + screen_Columns;
|
|
|
|
while (off < off_to)
|
|
{
|
|
prev_cells = (*mb_off2cells)(off, max_off);
|
|
off += prev_cells;
|
|
}
|
|
}
|
|
|
|
if (!skip_for_popup(row, col + coloff - prev_cells))
|
|
{
|
|
if (enc_dbcs != 0 && prev_cells > 1)
|
|
screen_char_2(off_to - prev_cells, row,
|
|
col + coloff - prev_cells);
|
|
else
|
|
screen_char(off_to - prev_cells, row,
|
|
col + coloff - prev_cells);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
screen_fill(row, row + 1, col + coloff, clear_width + coloff,
|
|
' ', ' ', 0);
|
|
while (col < clear_width)
|
|
{
|
|
ScreenCols[off_to++]
|
|
= (flags & SLF_INC_VCOL) ? ++last_vcol : last_vcol;
|
|
++col;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (clear_width > 0
|
|
#ifdef FEAT_PROP_POPUP
|
|
&& !(flags & SLF_POPUP) // no separator for popup window
|
|
#endif
|
|
)
|
|
{
|
|
// For a window that has a right neighbor, draw the separator char
|
|
// right of the window contents. But not on top of a popup window.
|
|
if (coloff + col < Columns)
|
|
{
|
|
if (!skip_for_popup(row, col + coloff))
|
|
{
|
|
int c;
|
|
|
|
c = fillchar_vsep(&hl, wp);
|
|
if (ScreenLines[off_to] != (schar_T)c
|
|
|| (enc_utf8 && (int)ScreenLinesUC[off_to]
|
|
!= (c >= 0x80 ? c : 0))
|
|
|| ScreenAttrs[off_to] != hl)
|
|
{
|
|
ScreenLines[off_to] = c;
|
|
ScreenAttrs[off_to] = hl;
|
|
if (enc_utf8)
|
|
{
|
|
if (c >= 0x80)
|
|
{
|
|
ScreenLinesUC[off_to] = c;
|
|
ScreenLinesC[0][off_to] = 0;
|
|
}
|
|
else
|
|
ScreenLinesUC[off_to] = 0;
|
|
}
|
|
screen_char(off_to, row, col + coloff);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
LineWraps[row] = FALSE;
|
|
}
|
|
}
|
|
|
|
#if defined(FEAT_RIGHTLEFT) || defined(PROTO)
|
|
/*
|
|
* Mirror text "str" for right-left displaying.
|
|
* Only works for single-byte characters (e.g., numbers).
|
|
*/
|
|
void
|
|
rl_mirror(char_u *str)
|
|
{
|
|
char_u *p1, *p2;
|
|
int t;
|
|
|
|
for (p1 = str, p2 = str + STRLEN(str) - 1; p1 < p2; ++p1, --p2)
|
|
{
|
|
t = *p1;
|
|
*p1 = *p2;
|
|
*p2 = t;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Draw the verticap separator right of window "wp" starting with line "row".
|
|
*/
|
|
void
|
|
draw_vsep_win(win_T *wp, int row)
|
|
{
|
|
int hl;
|
|
int c;
|
|
|
|
if (!wp->w_vsep_width)
|
|
return;
|
|
|
|
// draw the vertical separator right of this window
|
|
c = fillchar_vsep(&hl, wp);
|
|
screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + wp->w_height,
|
|
W_ENDCOL(wp), W_ENDCOL(wp) + 1,
|
|
c, ' ', hl);
|
|
}
|
|
|
|
/*
|
|
* Return TRUE if the status line of window "wp" is connected to the status
|
|
* line of the window right of it. If not, then it's a vertical separator.
|
|
* Only call if (wp->w_vsep_width != 0).
|
|
*/
|
|
int
|
|
stl_connected(win_T *wp)
|
|
{
|
|
frame_T *fr;
|
|
|
|
fr = wp->w_frame;
|
|
while (fr->fr_parent != NULL)
|
|
{
|
|
if (fr->fr_parent->fr_layout == FR_COL)
|
|
{
|
|
if (fr->fr_next != NULL)
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if (fr->fr_next != NULL)
|
|
return TRUE;
|
|
}
|
|
fr = fr->fr_parent;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
* Get the value to show for the language mappings, active 'keymap'.
|
|
*/
|
|
int
|
|
get_keymap_str(
|
|
win_T *wp,
|
|
char_u *fmt, // format string containing one %s item
|
|
char_u *buf, // buffer for the result
|
|
int len) // length of buffer
|
|
{
|
|
char_u *p;
|
|
|
|
if (wp->w_buffer->b_p_iminsert != B_IMODE_LMAP)
|
|
return FALSE;
|
|
|
|
#ifdef FEAT_EVAL
|
|
buf_T *old_curbuf = curbuf;
|
|
win_T *old_curwin = curwin;
|
|
char_u *s;
|
|
|
|
curbuf = wp->w_buffer;
|
|
curwin = wp;
|
|
STRCPY(buf, "b:keymap_name"); // must be writable
|
|
++emsg_skip;
|
|
s = p = eval_to_string(buf, FALSE, FALSE);
|
|
--emsg_skip;
|
|
curbuf = old_curbuf;
|
|
curwin = old_curwin;
|
|
if (p == NULL || *p == NUL)
|
|
#endif
|
|
{
|
|
#ifdef FEAT_KEYMAP
|
|
if (wp->w_buffer->b_kmap_state & KEYMAP_LOADED)
|
|
p = wp->w_buffer->b_p_keymap;
|
|
else
|
|
#endif
|
|
p = (char_u *)"lang";
|
|
}
|
|
if (vim_snprintf((char *)buf, len, (char *)fmt, p) > len - 1)
|
|
buf[0] = NUL;
|
|
#ifdef FEAT_EVAL
|
|
vim_free(s);
|
|
#endif
|
|
return buf[0] != NUL;
|
|
}
|
|
|
|
#if defined(FEAT_STL_OPT) || defined(PROTO)
|
|
/*
|
|
* Redraw the status line or ruler of window "wp".
|
|
* When "wp" is NULL redraw the tab pages line from 'tabline'.
|
|
*/
|
|
void
|
|
win_redr_custom(
|
|
win_T *wp,
|
|
int draw_ruler) // TRUE or FALSE
|
|
{
|
|
static int entered = FALSE;
|
|
int attr;
|
|
int curattr;
|
|
int row;
|
|
int col = 0;
|
|
int maxwidth;
|
|
int width;
|
|
int n;
|
|
int len;
|
|
int fillchar;
|
|
char_u buf[MAXPATHL];
|
|
char_u *stl;
|
|
char_u *p;
|
|
char_u *opt_name;
|
|
int opt_scope = 0;
|
|
stl_hlrec_T *hltab;
|
|
stl_hlrec_T *tabtab;
|
|
win_T *ewp;
|
|
int p_crb_save;
|
|
|
|
// There is a tiny chance that this gets called recursively: When
|
|
// redrawing a status line triggers redrawing the ruler or tabline.
|
|
// Avoid trouble by not allowing recursion.
|
|
if (entered)
|
|
return;
|
|
entered = TRUE;
|
|
|
|
// setup environment for the task at hand
|
|
if (wp == NULL)
|
|
{
|
|
// Use 'tabline'. Always at the first line of the screen.
|
|
stl = p_tal;
|
|
row = 0;
|
|
fillchar = ' ';
|
|
attr = HL_ATTR(HLF_TPF);
|
|
maxwidth = Columns;
|
|
opt_name = (char_u *)"tabline";
|
|
}
|
|
else
|
|
{
|
|
row = statusline_row(wp);
|
|
fillchar = fillchar_status(&attr, wp);
|
|
int in_status_line = wp->w_status_height != 0;
|
|
maxwidth = in_status_line ? wp->w_width : Columns;
|
|
|
|
if (draw_ruler)
|
|
{
|
|
stl = p_ruf;
|
|
opt_name = (char_u *)"rulerformat";
|
|
// advance past any leading group spec - implicit in ru_col
|
|
if (*stl == '%')
|
|
{
|
|
if (*++stl == '-')
|
|
stl++;
|
|
if (atoi((char *)stl))
|
|
while (VIM_ISDIGIT(*stl))
|
|
stl++;
|
|
if (*stl++ != '(')
|
|
stl = p_ruf;
|
|
}
|
|
col = ru_col - (Columns - maxwidth);
|
|
if (col < (maxwidth + 1) / 2)
|
|
col = (maxwidth + 1) / 2;
|
|
maxwidth -= col;
|
|
if (!in_status_line)
|
|
{
|
|
row = Rows - 1;
|
|
--maxwidth; // writing in last column may cause scrolling
|
|
fillchar = ' ';
|
|
attr = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
opt_name = (char_u *)"statusline";
|
|
if (*wp->w_p_stl != NUL)
|
|
{
|
|
stl = wp->w_p_stl;
|
|
opt_scope = OPT_LOCAL;
|
|
}
|
|
else
|
|
stl = p_stl;
|
|
}
|
|
|
|
if (in_status_line)
|
|
col += wp->w_wincol;
|
|
}
|
|
|
|
if (maxwidth <= 0)
|
|
goto theend;
|
|
|
|
// Temporarily reset 'cursorbind', we don't want a side effect from moving
|
|
// the cursor away and back.
|
|
ewp = wp == NULL ? curwin : wp;
|
|
p_crb_save = ewp->w_p_crb;
|
|
ewp->w_p_crb = FALSE;
|
|
|
|
// Make a copy, because the statusline may include a function call that
|
|
// might change the option value and free the memory.
|
|
stl = vim_strsave(stl);
|
|
width = build_stl_str_hl(ewp, buf, sizeof(buf),
|
|
stl, opt_name, opt_scope,
|
|
fillchar, maxwidth, &hltab, &tabtab);
|
|
vim_free(stl);
|
|
ewp->w_p_crb = p_crb_save;
|
|
|
|
// Make all characters printable.
|
|
p = transstr(buf);
|
|
if (p != NULL)
|
|
{
|
|
vim_strncpy(buf, p, sizeof(buf) - 1);
|
|
vim_free(p);
|
|
}
|
|
|
|
// fill up with "fillchar"
|
|
len = (int)STRLEN(buf);
|
|
while (width < maxwidth && len < (int)sizeof(buf) - 1)
|
|
{
|
|
len += (*mb_char2bytes)(fillchar, buf + len);
|
|
++width;
|
|
}
|
|
buf[len] = NUL;
|
|
|
|
/*
|
|
* Draw each snippet with the specified highlighting.
|
|
*/
|
|
curattr = attr;
|
|
p = buf;
|
|
for (n = 0; hltab[n].start != NULL; n++)
|
|
{
|
|
len = (int)(hltab[n].start - p);
|
|
screen_puts_len(p, len, row, col, curattr);
|
|
col += vim_strnsize(p, len);
|
|
p = hltab[n].start;
|
|
|
|
if (hltab[n].userhl == 0)
|
|
curattr = attr;
|
|
else if (hltab[n].userhl < 0)
|
|
curattr = syn_id2attr(-hltab[n].userhl);
|
|
#ifdef FEAT_TERMINAL
|
|
else if (wp != NULL && wp != curwin && bt_terminal(wp->w_buffer)
|
|
&& wp->w_status_height != 0)
|
|
curattr = highlight_stltermnc[hltab[n].userhl - 1];
|
|
else if (wp != NULL && bt_terminal(wp->w_buffer)
|
|
&& wp->w_status_height != 0)
|
|
curattr = highlight_stlterm[hltab[n].userhl - 1];
|
|
#endif
|
|
else if (wp != NULL && wp != curwin && wp->w_status_height != 0)
|
|
curattr = highlight_stlnc[hltab[n].userhl - 1];
|
|
else
|
|
curattr = highlight_user[hltab[n].userhl - 1];
|
|
}
|
|
screen_puts(p, row, col, curattr);
|
|
|
|
if (wp == NULL)
|
|
{
|
|
// Fill the TabPageIdxs[] array for clicking in the tab pagesline.
|
|
col = 0;
|
|
len = 0;
|
|
p = buf;
|
|
fillchar = 0;
|
|
for (n = 0; tabtab[n].start != NULL; n++)
|
|
{
|
|
len += vim_strnsize(p, (int)(tabtab[n].start - p));
|
|
while (col < len)
|
|
TabPageIdxs[col++] = fillchar;
|
|
p = tabtab[n].start;
|
|
fillchar = tabtab[n].userhl;
|
|
}
|
|
while (col < Columns)
|
|
TabPageIdxs[col++] = fillchar;
|
|
}
|
|
|
|
theend:
|
|
entered = FALSE;
|
|
}
|
|
|
|
#endif // FEAT_STL_OPT
|
|
|
|
/*
|
|
* Output a single character directly to the screen and update ScreenLines.
|
|
*/
|
|
void
|
|
screen_putchar(int c, int row, int col, int attr)
|
|
{
|
|
char_u buf[MB_MAXBYTES + 1];
|
|
|
|
if (has_mbyte)
|
|
buf[(*mb_char2bytes)(c, buf)] = NUL;
|
|
else
|
|
{
|
|
buf[0] = c;
|
|
buf[1] = NUL;
|
|
}
|
|
screen_puts(buf, row, col, attr);
|
|
}
|
|
|
|
/*
|
|
* Get a single character directly from ScreenLines into "bytes", which must
|
|
* have a size of "MB_MAXBYTES + 1".
|
|
* If "attrp" is not NULL, return the character's attribute in "*attrp".
|
|
*/
|
|
void
|
|
screen_getbytes(int row, int col, char_u *bytes, int *attrp)
|
|
{
|
|
unsigned off;
|
|
|
|
// safety check
|
|
if (ScreenLines == NULL || row >= screen_Rows || col >= screen_Columns)
|
|
return;
|
|
|
|
off = LineOffset[row] + col;
|
|
if (attrp != NULL)
|
|
*attrp = ScreenAttrs[off];
|
|
bytes[0] = ScreenLines[off];
|
|
bytes[1] = NUL;
|
|
|
|
if (enc_utf8 && ScreenLinesUC[off] != 0)
|
|
bytes[utfc_char2bytes(off, bytes)] = NUL;
|
|
else if (enc_dbcs == DBCS_JPNU && ScreenLines[off] == 0x8e)
|
|
{
|
|
bytes[0] = ScreenLines[off];
|
|
bytes[1] = ScreenLines2[off];
|
|
bytes[2] = NUL;
|
|
}
|
|
else if (enc_dbcs && MB_BYTE2LEN(bytes[0]) > 1)
|
|
{
|
|
bytes[1] = ScreenLines[off + 1];
|
|
bytes[2] = NUL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Return TRUE if composing characters for screen posn "off" differs from
|
|
* composing characters in "u8cc".
|
|
* Only to be used when ScreenLinesUC[off] != 0.
|
|
*/
|
|
static int
|
|
screen_comp_differs(int off, int *u8cc)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < Screen_mco; ++i)
|
|
{
|
|
if (ScreenLinesC[i][off] != (u8char_T)u8cc[i])
|
|
return TRUE;
|
|
if (u8cc[i] == 0)
|
|
break;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Put string '*text' on the screen at position 'row' and 'col', with
|
|
* attributes 'attr', and update ScreenLines[] and ScreenAttrs[].
|
|
* Note: only outputs within one row, message is truncated at screen boundary!
|
|
* Note: if ScreenLines[], row and/or col is invalid, nothing is done.
|
|
*/
|
|
void
|
|
screen_puts(
|
|
char_u *text,
|
|
int row,
|
|
int col,
|
|
int attr)
|
|
{
|
|
screen_puts_len(text, -1, row, col, attr);
|
|
}
|
|
|
|
/*
|
|
* Like screen_puts(), but output "text[len]". When "len" is -1 output up to
|
|
* a NUL.
|
|
*/
|
|
void
|
|
screen_puts_len(
|
|
char_u *text,
|
|
int textlen,
|
|
int row,
|
|
int col,
|
|
int attr_arg)
|
|
{
|
|
int attr = attr_arg;
|
|
unsigned off;
|
|
char_u *ptr = text;
|
|
int len = textlen;
|
|
int c;
|
|
unsigned max_off;
|
|
int mbyte_blen = 1;
|
|
int mbyte_cells = 1;
|
|
int u8c = 0;
|
|
int u8cc[MAX_MCO];
|
|
int clear_next_cell = FALSE;
|
|
#ifdef FEAT_ARABIC
|
|
int prev_c = 0; // previous Arabic character
|
|
int pc, nc, nc1;
|
|
int pcc[MAX_MCO];
|
|
#endif
|
|
int force_redraw_this;
|
|
int force_redraw_next = FALSE;
|
|
int need_redraw;
|
|
|
|
// Safety check. The check for negative row and column is to fix issue
|
|
// #4102. TODO: find out why row/col could be negative.
|
|
if (ScreenLines == NULL
|
|
|| row >= screen_Rows || row < 0
|
|
|| col >= screen_Columns || col < 0)
|
|
return;
|
|
off = LineOffset[row] + col;
|
|
|
|
// When drawing over the right half of a double-wide char clear out the
|
|
// left half. Only needed in a terminal.
|
|
if (has_mbyte && col > 0 && col < screen_Columns
|
|
#ifdef FEAT_GUI
|
|
&& !gui.in_use
|
|
#endif
|
|
&& mb_fix_col(col, row) != col)
|
|
{
|
|
if (!skip_for_popup(row, col - 1))
|
|
{
|
|
ScreenLines[off - 1] = ' ';
|
|
ScreenAttrs[off - 1] = 0;
|
|
if (enc_utf8)
|
|
{
|
|
ScreenLinesUC[off - 1] = 0;
|
|
ScreenLinesC[0][off - 1] = 0;
|
|
}
|
|
// redraw the previous cell, make it empty
|
|
screen_char(off - 1, row, col - 1);
|
|
}
|
|
// force the cell at "col" to be redrawn
|
|
force_redraw_next = TRUE;
|
|
}
|
|
|
|
max_off = LineOffset[row] + screen_Columns;
|
|
while (col < screen_Columns
|
|
&& (len < 0 || (int)(ptr - text) < len)
|
|
&& *ptr != NUL)
|
|
{
|
|
c = *ptr;
|
|
// check if this is the first byte of a multibyte
|
|
if (has_mbyte)
|
|
{
|
|
mbyte_blen = enc_utf8 && len > 0
|
|
? utfc_ptr2len_len(ptr, (int)((text + len) - ptr))
|
|
: (*mb_ptr2len)(ptr);
|
|
if (enc_dbcs == DBCS_JPNU && c == 0x8e)
|
|
mbyte_cells = 1;
|
|
else if (enc_dbcs != 0)
|
|
mbyte_cells = mbyte_blen;
|
|
else // enc_utf8
|
|
{
|
|
u8c = len >= 0
|
|
? utfc_ptr2char_len(ptr, u8cc, (int)((text + len) - ptr))
|
|
: utfc_ptr2char(ptr, u8cc);
|
|
mbyte_cells = utf_char2cells(u8c);
|
|
#ifdef FEAT_ARABIC
|
|
if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c))
|
|
{
|
|
// Do Arabic shaping.
|
|
if (len >= 0 && (int)(ptr - text) + mbyte_blen >= len)
|
|
{
|
|
// Past end of string to be displayed.
|
|
nc = NUL;
|
|
nc1 = NUL;
|
|
}
|
|
else
|
|
{
|
|
nc = len >= 0
|
|
? utfc_ptr2char_len(ptr + mbyte_blen, pcc,
|
|
(int)((text + len) - ptr - mbyte_blen))
|
|
: utfc_ptr2char(ptr + mbyte_blen, pcc);
|
|
nc1 = pcc[0];
|
|
}
|
|
pc = prev_c;
|
|
prev_c = u8c;
|
|
u8c = arabic_shape(u8c, &c, &u8cc[0], nc, nc1, pc);
|
|
}
|
|
else
|
|
prev_c = u8c;
|
|
#endif
|
|
if (col + mbyte_cells > screen_Columns)
|
|
{
|
|
// Only 1 cell left, but character requires 2 cells:
|
|
// display a '>' in the last column to avoid wrapping.
|
|
c = '>';
|
|
mbyte_cells = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
force_redraw_this = force_redraw_next;
|
|
force_redraw_next = FALSE;
|
|
|
|
need_redraw = ScreenLines[off] != c
|
|
|| (mbyte_cells == 2
|
|
&& ScreenLines[off + 1] != (enc_dbcs ? ptr[1] : 0))
|
|
|| (enc_dbcs == DBCS_JPNU
|
|
&& c == 0x8e
|
|
&& ScreenLines2[off] != ptr[1])
|
|
|| (enc_utf8
|
|
&& (ScreenLinesUC[off] !=
|
|
(u8char_T)(c < 0x80 && u8cc[0] == 0 ? 0 : u8c)
|
|
|| (ScreenLinesUC[off] != 0
|
|
&& screen_comp_differs(off, u8cc))))
|
|
|| ScreenAttrs[off] != attr
|
|
|| exmode_active;
|
|
|
|
if ((need_redraw || force_redraw_this) && !skip_for_popup(row, col))
|
|
{
|
|
#if defined(FEAT_GUI) || defined(UNIX)
|
|
// The bold trick makes a single row of pixels appear in the next
|
|
// character. When a bold character is removed, the next
|
|
// character should be redrawn too. This happens for our own GUI
|
|
// and for some xterms.
|
|
if (need_redraw && ScreenLines[off] != ' ' && (
|
|
# ifdef FEAT_GUI
|
|
gui.in_use
|
|
# endif
|
|
# if defined(FEAT_GUI) && defined(UNIX)
|
|
||
|
|
# endif
|
|
# ifdef UNIX
|
|
term_is_xterm
|
|
# endif
|
|
))
|
|
{
|
|
int n = ScreenAttrs[off];
|
|
|
|
if (n > HL_ALL)
|
|
n = syn_attr2attr(n);
|
|
if (n & HL_BOLD)
|
|
force_redraw_next = TRUE;
|
|
}
|
|
#endif
|
|
// When at the end of the text and overwriting a two-cell
|
|
// character with a one-cell character, need to clear the next
|
|
// cell. Also when overwriting the left half of a two-cell char
|
|
// with the right half of a two-cell char. Do this only once
|
|
// (mb_off2cells() may return 2 on the right half).
|
|
if (clear_next_cell)
|
|
clear_next_cell = FALSE;
|
|
else if (has_mbyte
|
|
&& (len < 0 ? ptr[mbyte_blen] == NUL
|
|
: ptr + mbyte_blen >= text + len)
|
|
&& ((mbyte_cells == 1 && (*mb_off2cells)(off, max_off) > 1)
|
|
|| (mbyte_cells == 2
|
|
&& (*mb_off2cells)(off, max_off) == 1
|
|
&& (*mb_off2cells)(off + 1, max_off) > 1)))
|
|
clear_next_cell = TRUE;
|
|
|
|
// Make sure we never leave a second byte of a double-byte behind,
|
|
// it confuses mb_off2cells().
|
|
if (enc_dbcs
|
|
&& ((mbyte_cells == 1 && (*mb_off2cells)(off, max_off) > 1)
|
|
|| (mbyte_cells == 2
|
|
&& (*mb_off2cells)(off, max_off) == 1
|
|
&& (*mb_off2cells)(off + 1, max_off) > 1)))
|
|
ScreenLines[off + mbyte_blen] = 0;
|
|
ScreenLines[off] = c;
|
|
ScreenAttrs[off] = attr;
|
|
ScreenCols[off] = -1;
|
|
if (enc_utf8)
|
|
{
|
|
if (c < 0x80 && u8cc[0] == 0)
|
|
ScreenLinesUC[off] = 0;
|
|
else
|
|
{
|
|
int i;
|
|
|
|
ScreenLinesUC[off] = u8c;
|
|
for (i = 0; i < Screen_mco; ++i)
|
|
{
|
|
ScreenLinesC[i][off] = u8cc[i];
|
|
if (u8cc[i] == 0)
|
|
break;
|
|
}
|
|
}
|
|
if (mbyte_cells == 2)
|
|
{
|
|
ScreenLines[off + 1] = 0;
|
|
ScreenAttrs[off + 1] = attr;
|
|
ScreenCols[off + 1] = -1;
|
|
}
|
|
screen_char(off, row, col);
|
|
}
|
|
else if (mbyte_cells == 2)
|
|
{
|
|
ScreenLines[off + 1] = ptr[1];
|
|
ScreenAttrs[off + 1] = attr;
|
|
ScreenCols[off + 1] = -1;
|
|
screen_char_2(off, row, col);
|
|
}
|
|
else if (enc_dbcs == DBCS_JPNU && c == 0x8e)
|
|
{
|
|
ScreenLines2[off] = ptr[1];
|
|
screen_char(off, row, col);
|
|
}
|
|
else
|
|
screen_char(off, row, col);
|
|
}
|
|
if (has_mbyte)
|
|
{
|
|
off += mbyte_cells;
|
|
col += mbyte_cells;
|
|
ptr += mbyte_blen;
|
|
if (clear_next_cell)
|
|
{
|
|
// This only happens at the end, display one space next.
|
|
// Keep the attribute from before.
|
|
ptr = (char_u *)" ";
|
|
len = -1;
|
|
attr = ScreenAttrs[off];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
++off;
|
|
++col;
|
|
++ptr;
|
|
}
|
|
}
|
|
|
|
// If we detected the next character needs to be redrawn, but the text
|
|
// doesn't extend up to there, update the character here.
|
|
if (force_redraw_next && col < screen_Columns && !skip_for_popup(row, col))
|
|
{
|
|
if (enc_dbcs != 0 && dbcs_off2cells(off, max_off) > 1)
|
|
screen_char_2(off, row, col);
|
|
else
|
|
screen_char(off, row, col);
|
|
}
|
|
}
|
|
|
|
#if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
|
|
/*
|
|
* Prepare for 'hlsearch' highlighting.
|
|
*/
|
|
void
|
|
start_search_hl(void)
|
|
{
|
|
if (!p_hls || no_hlsearch)
|
|
return;
|
|
|
|
end_search_hl(); // just in case it wasn't called before
|
|
last_pat_prog(&screen_search_hl.rm);
|
|
screen_search_hl.attr = HL_ATTR(HLF_L);
|
|
}
|
|
|
|
/*
|
|
* Clean up for 'hlsearch' highlighting.
|
|
*/
|
|
void
|
|
end_search_hl(void)
|
|
{
|
|
if (screen_search_hl.rm.regprog == NULL)
|
|
return;
|
|
|
|
vim_regfree(screen_search_hl.rm.regprog);
|
|
screen_search_hl.rm.regprog = NULL;
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
screen_start_highlight(int attr)
|
|
{
|
|
attrentry_T *aep = NULL;
|
|
|
|
screen_attr = attr;
|
|
if (!full_screen
|
|
#ifdef MSWIN
|
|
|| !termcap_active
|
|
#endif
|
|
)
|
|
return;
|
|
|
|
#ifdef FEAT_GUI
|
|
if (gui.in_use)
|
|
{
|
|
char buf[20];
|
|
|
|
// The GUI handles this internally.
|
|
sprintf(buf, "\033|%dh", attr);
|
|
OUT_STR(buf);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (attr > HL_ALL) // special HL attr.
|
|
{
|
|
if (IS_CTERM)
|
|
aep = syn_cterm_attr2entry(attr);
|
|
else
|
|
aep = syn_term_attr2entry(attr);
|
|
if (aep == NULL) // did ":syntax clear"
|
|
attr = 0;
|
|
else
|
|
attr = aep->ae_attr;
|
|
}
|
|
#if defined(FEAT_VTP) && defined(FEAT_TERMGUICOLORS)
|
|
if (use_vtp())
|
|
{
|
|
guicolor_T defguifg, defguibg;
|
|
int defctermfg, defctermbg;
|
|
|
|
// If FG and BG are unset, the color is undefined when
|
|
// BOLD+INVERSE. Use Normal as the default value.
|
|
get_default_console_color(&defctermfg, &defctermbg, &defguifg,
|
|
&defguibg);
|
|
|
|
if (p_tgc)
|
|
{
|
|
if (aep == NULL || COLOR_INVALID(aep->ae_u.cterm.fg_rgb))
|
|
term_fg_rgb_color(defguifg);
|
|
if (aep == NULL || COLOR_INVALID(aep->ae_u.cterm.bg_rgb))
|
|
term_bg_rgb_color(defguibg);
|
|
}
|
|
else if (t_colors >= 256)
|
|
{
|
|
if (aep == NULL || aep->ae_u.cterm.fg_color == 0)
|
|
term_fg_color(defctermfg);
|
|
if (aep == NULL || aep->ae_u.cterm.bg_color == 0)
|
|
term_bg_color(defctermbg);
|
|
}
|
|
}
|
|
#endif
|
|
if ((attr & HL_BOLD) && *T_MD != NUL) // bold
|
|
out_str(T_MD);
|
|
else if (aep != NULL && cterm_normal_fg_bold && (
|
|
#ifdef FEAT_TERMGUICOLORS
|
|
p_tgc && aep->ae_u.cterm.fg_rgb != CTERMCOLOR
|
|
? aep->ae_u.cterm.fg_rgb != INVALCOLOR
|
|
:
|
|
#endif
|
|
t_colors > 1 && aep->ae_u.cterm.fg_color))
|
|
// If the Normal FG color has BOLD attribute and the new HL
|
|
// has a FG color defined, clear BOLD.
|
|
out_str(T_ME);
|
|
if ((attr & HL_STANDOUT) && *T_SO != NUL) // standout
|
|
out_str(T_SO);
|
|
if ((attr & HL_UNDERCURL) && *T_UCS != NUL) // undercurl
|
|
out_str(T_UCS);
|
|
if ((attr & HL_UNDERDOUBLE) && *T_USS != NUL) // double underline
|
|
out_str(T_USS);
|
|
if ((attr & HL_UNDERDOTTED) && *T_DS != NUL) // dotted underline
|
|
out_str(T_DS);
|
|
if ((attr & HL_UNDERDASHED) && *T_CDS != NUL) // dashed underline
|
|
out_str(T_CDS);
|
|
if (((attr & HL_UNDERLINE) // underline or undercurl, etc.
|
|
|| ((attr & HL_UNDERCURL) && *T_UCS == NUL)
|
|
|| ((attr & HL_UNDERDOUBLE) && *T_USS == NUL)
|
|
|| ((attr & HL_UNDERDOTTED) && *T_DS == NUL)
|
|
|| ((attr & HL_UNDERDASHED) && *T_CDS == NUL))
|
|
&& *T_US != NUL)
|
|
out_str(T_US);
|
|
if ((attr & HL_ITALIC) && *T_CZH != NUL) // italic
|
|
out_str(T_CZH);
|
|
if ((attr & HL_INVERSE) && *T_MR != NUL) // inverse (reverse)
|
|
out_str(T_MR);
|
|
if ((attr & HL_STRIKETHROUGH) && *T_STS != NUL) // strike
|
|
out_str(T_STS);
|
|
|
|
/*
|
|
* Output the color or start string after bold etc., in case the
|
|
* bold etc. override the color setting.
|
|
*/
|
|
if (aep != NULL)
|
|
{
|
|
if (aep->ae_u.cterm.font > 0 && aep->ae_u.cterm.font < 12)
|
|
term_font(aep->ae_u.cterm.font);
|
|
#ifdef FEAT_TERMGUICOLORS
|
|
// When 'termguicolors' is set but fg or bg is unset,
|
|
// fall back to the cterm colors. This helps for SpellBad,
|
|
// where the GUI uses a red undercurl.
|
|
if (p_tgc && aep->ae_u.cterm.fg_rgb != CTERMCOLOR)
|
|
{
|
|
if (aep->ae_u.cterm.fg_rgb != INVALCOLOR)
|
|
term_fg_rgb_color(aep->ae_u.cterm.fg_rgb);
|
|
}
|
|
else
|
|
#endif
|
|
if (t_colors > 1)
|
|
{
|
|
if (aep->ae_u.cterm.fg_color)
|
|
term_fg_color(aep->ae_u.cterm.fg_color - 1);
|
|
}
|
|
#ifdef FEAT_TERMGUICOLORS
|
|
if (p_tgc && aep->ae_u.cterm.bg_rgb != CTERMCOLOR)
|
|
{
|
|
if (aep->ae_u.cterm.bg_rgb != INVALCOLOR)
|
|
term_bg_rgb_color(aep->ae_u.cterm.bg_rgb);
|
|
}
|
|
else
|
|
#endif
|
|
if (t_colors > 1)
|
|
{
|
|
if (aep->ae_u.cterm.bg_color)
|
|
term_bg_color(aep->ae_u.cterm.bg_color - 1);
|
|
}
|
|
#ifdef FEAT_TERMGUICOLORS
|
|
if (p_tgc && aep->ae_u.cterm.ul_rgb != CTERMCOLOR)
|
|
{
|
|
if (aep->ae_u.cterm.ul_rgb != INVALCOLOR)
|
|
term_ul_rgb_color(aep->ae_u.cterm.ul_rgb);
|
|
}
|
|
else
|
|
#endif
|
|
if (t_colors > 1)
|
|
{
|
|
if (aep->ae_u.cterm.ul_color)
|
|
term_ul_color(aep->ae_u.cterm.ul_color - 1);
|
|
}
|
|
|
|
if (!IS_CTERM)
|
|
{
|
|
if (aep->ae_u.term.start != NULL)
|
|
out_str(aep->ae_u.term.start);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
screen_stop_highlight(void)
|
|
{
|
|
int do_ME = FALSE; // output T_ME code
|
|
#if defined(FEAT_VTP) && defined(FEAT_TERMGUICOLORS)
|
|
int do_ME_fg = FALSE, do_ME_bg = FALSE;
|
|
#endif
|
|
|
|
if (screen_attr != 0
|
|
#ifdef MSWIN
|
|
&& termcap_active
|
|
#endif
|
|
)
|
|
{
|
|
#ifdef FEAT_GUI
|
|
if (gui.in_use)
|
|
{
|
|
char buf[20];
|
|
|
|
// use internal GUI code
|
|
sprintf(buf, "\033|%dH", screen_attr);
|
|
OUT_STR(buf);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
int is_under;
|
|
|
|
if (screen_attr > HL_ALL) // special HL attr.
|
|
{
|
|
attrentry_T *aep;
|
|
|
|
if (IS_CTERM)
|
|
{
|
|
/*
|
|
* Assume that t_me restores the original colors!
|
|
*/
|
|
aep = syn_cterm_attr2entry(screen_attr);
|
|
if (aep != NULL && ((
|
|
#ifdef FEAT_TERMGUICOLORS
|
|
p_tgc && aep->ae_u.cterm.fg_rgb != CTERMCOLOR
|
|
? aep->ae_u.cterm.fg_rgb != INVALCOLOR
|
|
# ifdef FEAT_VTP
|
|
? !(do_ME_fg = TRUE) : (do_ME_fg = FALSE)
|
|
# endif
|
|
:
|
|
#endif
|
|
aep->ae_u.cterm.fg_color) || (
|
|
#ifdef FEAT_TERMGUICOLORS
|
|
p_tgc && aep->ae_u.cterm.bg_rgb != CTERMCOLOR
|
|
? aep->ae_u.cterm.bg_rgb != INVALCOLOR
|
|
# ifdef FEAT_VTP
|
|
? !(do_ME_bg = TRUE) : (do_ME_bg = FALSE)
|
|
# endif
|
|
:
|
|
#endif
|
|
aep->ae_u.cterm.bg_color)))
|
|
do_ME = TRUE;
|
|
#if defined(FEAT_VTP) && defined(FEAT_TERMGUICOLORS)
|
|
if (use_vtp())
|
|
{
|
|
if (do_ME_fg && do_ME_bg)
|
|
do_ME = TRUE;
|
|
|
|
// FG and BG cannot be separated in T_ME, which is not
|
|
// efficient.
|
|
if (!do_ME && do_ME_fg)
|
|
out_str((char_u *)"\033|39m"); // restore FG
|
|
if (!do_ME && do_ME_bg)
|
|
out_str((char_u *)"\033|49m"); // restore BG
|
|
}
|
|
else
|
|
{
|
|
// Process FG and BG at once.
|
|
if (!do_ME)
|
|
do_ME = do_ME_fg | do_ME_bg;
|
|
}
|
|
#endif
|
|
}
|
|
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
|
|
out_str(aep->ae_u.term.stop);
|
|
}
|
|
}
|
|
if (aep == NULL) // did ":syntax clear"
|
|
screen_attr = 0;
|
|
else
|
|
screen_attr = aep->ae_attr;
|
|
}
|
|
|
|
/*
|
|
* 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
|
|
out_str(T_SE);
|
|
}
|
|
is_under = (screen_attr & (HL_UNDERCURL
|
|
| HL_UNDERDOUBLE | HL_UNDERDOTTED | HL_UNDERDASHED));
|
|
if (is_under && *T_UCE != NUL)
|
|
{
|
|
if (STRCMP(T_UCE, T_ME) == 0)
|
|
do_ME = TRUE;
|
|
else
|
|
out_str(T_UCE);
|
|
}
|
|
if ((screen_attr & HL_UNDERLINE) || (is_under && *T_UCE == NUL))
|
|
{
|
|
if (STRCMP(T_UE, T_ME) == 0)
|
|
do_ME = TRUE;
|
|
else
|
|
out_str(T_UE);
|
|
}
|
|
if (screen_attr & HL_ITALIC)
|
|
{
|
|
if (STRCMP(T_CZR, T_ME) == 0)
|
|
do_ME = TRUE;
|
|
else
|
|
out_str(T_CZR);
|
|
}
|
|
if (screen_attr & HL_STRIKETHROUGH)
|
|
{
|
|
if (STRCMP(T_STE, T_ME) == 0)
|
|
do_ME = TRUE;
|
|
else
|
|
out_str(T_STE);
|
|
}
|
|
if (do_ME || (screen_attr & (HL_BOLD | HL_INVERSE)))
|
|
out_str(T_ME);
|
|
|
|
#ifdef FEAT_TERMGUICOLORS
|
|
if (p_tgc)
|
|
{
|
|
if (cterm_normal_fg_gui_color != INVALCOLOR)
|
|
term_fg_rgb_color(cterm_normal_fg_gui_color);
|
|
if (cterm_normal_bg_gui_color != INVALCOLOR)
|
|
term_bg_rgb_color(cterm_normal_bg_gui_color);
|
|
if (cterm_normal_ul_gui_color != INVALCOLOR)
|
|
term_ul_rgb_color(cterm_normal_ul_gui_color);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (t_colors > 1)
|
|
{
|
|
// set Normal cterm colors
|
|
if (cterm_normal_fg_color != 0)
|
|
term_fg_color(cterm_normal_fg_color - 1);
|
|
if (cterm_normal_bg_color != 0)
|
|
term_bg_color(cterm_normal_bg_color - 1);
|
|
if (cterm_normal_ul_color != 0)
|
|
term_ul_color(cterm_normal_ul_color - 1);
|
|
if (cterm_normal_fg_bold)
|
|
out_str(T_MD);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
screen_attr = 0;
|
|
}
|
|
|
|
/*
|
|
* Reset the colors for a cterm. Used when leaving Vim.
|
|
* The machine specific code may override this again.
|
|
*/
|
|
void
|
|
reset_cterm_colors(void)
|
|
{
|
|
if (!IS_CTERM)
|
|
return;
|
|
|
|
// set Normal cterm colors
|
|
#ifdef FEAT_TERMGUICOLORS
|
|
if (p_tgc ? (cterm_normal_fg_gui_color != INVALCOLOR
|
|
|| cterm_normal_bg_gui_color != INVALCOLOR)
|
|
: (cterm_normal_fg_color > 0 || cterm_normal_bg_color > 0))
|
|
#else
|
|
if (cterm_normal_fg_color > 0 || cterm_normal_bg_color > 0)
|
|
#endif
|
|
{
|
|
out_str(T_OP);
|
|
screen_attr = -1;
|
|
}
|
|
if (cterm_normal_fg_bold)
|
|
{
|
|
out_str(T_ME);
|
|
screen_attr = -1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Put character ScreenLines["off"] on the screen at position "row" and "col",
|
|
* using the attributes from ScreenAttrs["off"].
|
|
*/
|
|
void
|
|
screen_char(unsigned off, int row, int col)
|
|
{
|
|
int attr;
|
|
|
|
// Check for illegal values, just in case (could happen just after
|
|
// resizing).
|
|
if (row >= screen_Rows || col >= screen_Columns)
|
|
return;
|
|
|
|
// Outputting a character in the last cell on the screen may scroll the
|
|
// screen up. Only do it when the "xn" termcap property is set, otherwise
|
|
// mark the character invalid (update it when scrolled up).
|
|
if (*T_XN == NUL
|
|
&& row == screen_Rows - 1 && col == screen_Columns - 1
|
|
#ifdef FEAT_RIGHTLEFT
|
|
// account for first command-line character in rightleft mode
|
|
&& !cmdmsg_rl
|
|
#endif
|
|
)
|
|
{
|
|
ScreenAttrs[off] = (sattr_T)-1;
|
|
ScreenCols[off] = -1;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Stop highlighting first, so it's easier to move the cursor.
|
|
*/
|
|
if (screen_char_attr != 0)
|
|
attr = screen_char_attr;
|
|
else
|
|
attr = ScreenAttrs[off];
|
|
if (screen_attr != attr)
|
|
screen_stop_highlight();
|
|
|
|
windgoto(row, col);
|
|
|
|
if (screen_attr != attr)
|
|
screen_start_highlight(attr);
|
|
|
|
if (enc_utf8 && ScreenLinesUC[off] != 0)
|
|
{
|
|
char_u buf[MB_MAXBYTES + 1];
|
|
|
|
if (
|
|
#ifdef FEAT_GUI
|
|
!gui.in_use &&
|
|
#endif
|
|
get_cellwidth(ScreenLinesUC[off]) > 1
|
|
)
|
|
{
|
|
// If the width is set to 2 with setcellwidths()
|
|
// clear the two screen cells. If the character is actually
|
|
// single width it won't change the second cell.
|
|
out_str((char_u *)" ");
|
|
term_windgoto(row, col);
|
|
screen_cur_col = 9999;
|
|
}
|
|
else if (utf_ambiguous_width(ScreenLinesUC[off]))
|
|
{
|
|
if (*p_ambw == 'd'
|
|
#ifdef FEAT_GUI
|
|
&& !gui.in_use
|
|
#endif
|
|
)
|
|
{
|
|
// Clear the two screen cells. If the character is actually
|
|
// single width it won't change the second cell.
|
|
out_str((char_u *)" ");
|
|
term_windgoto(row, col);
|
|
}
|
|
// not sure where the cursor is after drawing the ambiguous width
|
|
// character
|
|
screen_cur_col = 9999;
|
|
}
|
|
else if (utf_char2cells(ScreenLinesUC[off]) > 1)
|
|
++screen_cur_col;
|
|
|
|
// Convert the UTF-8 character to bytes and write it.
|
|
buf[utfc_char2bytes(off, buf)] = NUL;
|
|
out_str(buf);
|
|
}
|
|
else
|
|
{
|
|
out_flush_check();
|
|
out_char(ScreenLines[off]);
|
|
// double-byte character in single-width cell
|
|
if (enc_dbcs == DBCS_JPNU && ScreenLines[off] == 0x8e)
|
|
out_char(ScreenLines2[off]);
|
|
}
|
|
|
|
screen_cur_col++;
|
|
}
|
|
|
|
/*
|
|
* Used for enc_dbcs only: Put one double-wide character at ScreenLines["off"]
|
|
* on the screen at position 'row' and 'col'.
|
|
* The attributes of the first byte is used for all. This is required to
|
|
* output the two bytes of a double-byte character with nothing in between.
|
|
*/
|
|
static void
|
|
screen_char_2(unsigned off, int row, int col)
|
|
{
|
|
// Check for illegal values (could be wrong when screen was resized).
|
|
if (off + 1 >= (unsigned)(screen_Rows * screen_Columns))
|
|
return;
|
|
|
|
// Outputting the last character on the screen may scroll the screen up.
|
|
// Don't to it! Mark the character invalid (update it when scrolled up)
|
|
if (row == screen_Rows - 1 && col >= screen_Columns - 2)
|
|
{
|
|
ScreenAttrs[off] = (sattr_T)-1;
|
|
ScreenCols[off] = -1;
|
|
return;
|
|
}
|
|
|
|
// Output the first byte normally (positions the cursor), then write the
|
|
// second byte directly.
|
|
screen_char(off, row, col);
|
|
out_char(ScreenLines[off + 1]);
|
|
++screen_cur_col;
|
|
}
|
|
|
|
/*
|
|
* Draw a rectangle of the screen, inverted when "invert" is TRUE.
|
|
* This uses the contents of ScreenLines[] and doesn't change it.
|
|
*/
|
|
void
|
|
screen_draw_rectangle(
|
|
int row,
|
|
int col,
|
|
int height,
|
|
int width,
|
|
int invert)
|
|
{
|
|
int r, c;
|
|
int off;
|
|
int max_off;
|
|
|
|
// Can't use ScreenLines unless initialized
|
|
if (ScreenLines == NULL)
|
|
return;
|
|
|
|
if (invert)
|
|
screen_char_attr = HL_INVERSE;
|
|
for (r = row; r < row + height; ++r)
|
|
{
|
|
off = LineOffset[r];
|
|
max_off = off + screen_Columns;
|
|
for (c = col; c < col + width; ++c)
|
|
{
|
|
if (enc_dbcs != 0 && dbcs_off2cells(off + c, max_off) > 1)
|
|
{
|
|
if (!skip_for_popup(r, c))
|
|
screen_char_2(off + c, r, c);
|
|
++c;
|
|
}
|
|
else
|
|
{
|
|
if (!skip_for_popup(r, c))
|
|
screen_char(off + c, r, c);
|
|
if (utf_off2cells(off + c, max_off) > 1)
|
|
++c;
|
|
}
|
|
}
|
|
}
|
|
screen_char_attr = 0;
|
|
}
|
|
|
|
/*
|
|
* Redraw the characters for a vertically split window.
|
|
*/
|
|
static void
|
|
redraw_block(int row, int end, win_T *wp)
|
|
{
|
|
int col;
|
|
int width;
|
|
|
|
# ifdef FEAT_CLIPBOARD
|
|
clip_may_clear_selection(row, end - 1);
|
|
# endif
|
|
|
|
if (wp == NULL)
|
|
{
|
|
col = 0;
|
|
width = Columns;
|
|
}
|
|
else
|
|
{
|
|
col = wp->w_wincol;
|
|
width = wp->w_width;
|
|
}
|
|
screen_draw_rectangle(row, col, end - row, width, FALSE);
|
|
}
|
|
|
|
void
|
|
space_to_screenline(int off, int attr)
|
|
{
|
|
ScreenLines[off] = ' ';
|
|
ScreenAttrs[off] = attr;
|
|
ScreenCols[off] = -1;
|
|
if (enc_utf8)
|
|
ScreenLinesUC[off] = 0;
|
|
}
|
|
|
|
/*
|
|
* Fill the screen from "start_row" to "end_row" (exclusive), from "start_col"
|
|
* to "end_col" (exclusive) with character "c1" in first column followed by
|
|
* "c2" in the other columns. Use attributes "attr".
|
|
*/
|
|
void
|
|
screen_fill(
|
|
int start_row,
|
|
int end_row,
|
|
int start_col,
|
|
int end_col,
|
|
int c1,
|
|
int c2,
|
|
int attr)
|
|
{
|
|
int row;
|
|
int col;
|
|
int off;
|
|
int end_off;
|
|
int did_delete;
|
|
int c;
|
|
int norm_term;
|
|
#if defined(FEAT_GUI) || defined(UNIX)
|
|
int force_next = FALSE;
|
|
#endif
|
|
|
|
if (end_row > screen_Rows) // safety check
|
|
end_row = screen_Rows;
|
|
if (end_col > screen_Columns) // safety check
|
|
end_col = screen_Columns;
|
|
if (ScreenLines == NULL
|
|
|| start_row >= end_row
|
|
|| start_col >= end_col) // nothing to do
|
|
return;
|
|
|
|
// it's a "normal" terminal when not in a GUI or cterm
|
|
norm_term = (
|
|
#ifdef FEAT_GUI
|
|
!gui.in_use &&
|
|
#endif
|
|
!IS_CTERM);
|
|
for (row = start_row; row < end_row; ++row)
|
|
{
|
|
if (has_mbyte
|
|
#ifdef FEAT_GUI
|
|
&& !gui.in_use
|
|
#endif
|
|
)
|
|
{
|
|
// When drawing over the right half of a double-wide char clear
|
|
// out the left half. When drawing over the left half of a
|
|
// double wide-char clear out the right half. Only needed in a
|
|
// terminal.
|
|
if (start_col > 0 && mb_fix_col(start_col, row) != start_col)
|
|
screen_puts_len((char_u *)" ", 1, row, start_col - 1, 0);
|
|
if (end_col < screen_Columns && mb_fix_col(end_col, row) != end_col)
|
|
screen_puts_len((char_u *)" ", 1, row, end_col, 0);
|
|
}
|
|
/*
|
|
* Try to use delete-line termcap code, when no attributes or in a
|
|
* "normal" terminal, where a bold/italic space is just a
|
|
* space.
|
|
*/
|
|
did_delete = FALSE;
|
|
if (c2 == ' '
|
|
&& end_col == Columns
|
|
&& can_clear(T_CE)
|
|
&& (attr == 0
|
|
|| (norm_term
|
|
&& attr <= HL_ALL
|
|
&& ((attr & ~(HL_BOLD | HL_ITALIC)) == 0))))
|
|
{
|
|
/*
|
|
* check if we really need to clear something
|
|
*/
|
|
col = start_col;
|
|
if (c1 != ' ') // don't clear first char
|
|
++col;
|
|
|
|
off = LineOffset[row] + col;
|
|
end_off = LineOffset[row] + end_col;
|
|
|
|
// skip blanks (used often, keep it fast!)
|
|
if (enc_utf8)
|
|
while (off < end_off && ScreenLines[off] == ' '
|
|
&& ScreenAttrs[off] == 0 && ScreenLinesUC[off] == 0)
|
|
++off;
|
|
else
|
|
while (off < end_off && ScreenLines[off] == ' '
|
|
&& ScreenAttrs[off] == 0)
|
|
++off;
|
|
if (off < end_off) // something to be cleared
|
|
{
|
|
col = off - LineOffset[row];
|
|
screen_stop_highlight();
|
|
term_windgoto(row, col);// clear rest of this screen line
|
|
out_str(T_CE);
|
|
screen_start(); // don't know where cursor is now
|
|
col = end_col - col;
|
|
while (col--) // clear chars in ScreenLines
|
|
{
|
|
space_to_screenline(off, 0);
|
|
++off;
|
|
}
|
|
}
|
|
did_delete = TRUE; // the chars are cleared now
|
|
}
|
|
|
|
off = LineOffset[row] + start_col;
|
|
c = c1;
|
|
for (col = start_col; col < end_col; ++col)
|
|
{
|
|
if ((ScreenLines[off] != c
|
|
|| (enc_utf8 && (int)ScreenLinesUC[off]
|
|
!= (c >= 0x80 ? c : 0))
|
|
|| ScreenAttrs[off] != attr
|
|
|| must_redraw == UPD_CLEAR // screen clear pending
|
|
#if defined(FEAT_GUI) || defined(UNIX)
|
|
|| force_next
|
|
#endif
|
|
)
|
|
// Skip if under a(nother) popup.
|
|
&& !skip_for_popup(row, col))
|
|
{
|
|
#if defined(FEAT_GUI) || defined(UNIX)
|
|
// The bold trick may make a single row of pixels appear in
|
|
// the next character. When a bold character is removed, the
|
|
// next character should be redrawn too. This happens for our
|
|
// own GUI and for some xterms.
|
|
if (
|
|
# ifdef FEAT_GUI
|
|
gui.in_use
|
|
# endif
|
|
# if defined(FEAT_GUI) && defined(UNIX)
|
|
||
|
|
# endif
|
|
# ifdef UNIX
|
|
term_is_xterm
|
|
# endif
|
|
)
|
|
{
|
|
if (ScreenLines[off] != ' '
|
|
&& (ScreenAttrs[off] > HL_ALL
|
|
|| ScreenAttrs[off] & HL_BOLD))
|
|
force_next = TRUE;
|
|
else
|
|
force_next = FALSE;
|
|
}
|
|
#endif // FEAT_GUI || defined(UNIX)
|
|
ScreenLines[off] = c;
|
|
if (enc_utf8)
|
|
{
|
|
if (c >= 0x80)
|
|
{
|
|
ScreenLinesUC[off] = c;
|
|
ScreenLinesC[0][off] = 0;
|
|
}
|
|
else
|
|
ScreenLinesUC[off] = 0;
|
|
}
|
|
ScreenAttrs[off] = attr;
|
|
if (!did_delete || c != ' ')
|
|
screen_char(off, row, col);
|
|
}
|
|
ScreenCols[off] = -1;
|
|
++off;
|
|
if (col == start_col)
|
|
{
|
|
if (did_delete)
|
|
break;
|
|
c = c2;
|
|
}
|
|
}
|
|
if (end_col == Columns)
|
|
LineWraps[row] = FALSE;
|
|
if (row == Rows - 1) // overwritten the command line
|
|
{
|
|
redraw_cmdline = TRUE;
|
|
if (start_col == 0 && end_col == Columns
|
|
&& c1 == ' ' && c2 == ' ' && attr == 0)
|
|
clear_cmdline = FALSE; // command line has been cleared
|
|
if (start_col == 0)
|
|
mode_displayed = FALSE; // mode cleared or overwritten
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check if there should be a delay. Used before clearing or redrawing the
|
|
* screen or the command line.
|
|
*/
|
|
void
|
|
check_for_delay(int check_msg_scroll)
|
|
{
|
|
if ((emsg_on_display || (check_msg_scroll && msg_scroll))
|
|
&& !did_wait_return
|
|
&& emsg_silent == 0
|
|
&& !in_assert_fails)
|
|
{
|
|
out_flush();
|
|
ui_delay(1006L, TRUE);
|
|
emsg_on_display = FALSE;
|
|
if (check_msg_scroll)
|
|
msg_scroll = FALSE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Init TabPageIdxs[] to zero: Clicking outside of tabs has no effect.
|
|
*/
|
|
static void
|
|
clear_TabPageIdxs(void)
|
|
{
|
|
int scol;
|
|
|
|
for (scol = 0; scol < Columns; ++scol)
|
|
TabPageIdxs[scol] = 0;
|
|
}
|
|
|
|
/*
|
|
* screen_valid - allocate screen buffers if size changed
|
|
* If "doclear" 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(int doclear)
|
|
{
|
|
screenalloc(doclear); // allocate screen buffers if size changed
|
|
return (ScreenLines != NULL);
|
|
}
|
|
|
|
/*
|
|
* Resize the shell to Rows and Columns.
|
|
* Allocate ScreenLines[] and associated items.
|
|
*
|
|
* There may be some time between setting Rows and Columns and (re)allocating
|
|
* ScreenLines[]. This happens when starting up and when (manually) changing
|
|
* the shell size. Always use screen_Rows and screen_Columns to access items
|
|
* in ScreenLines[]. Use Rows and Columns for positioning text etc. where the
|
|
* final size of the shell is needed.
|
|
*/
|
|
void
|
|
screenalloc(int doclear)
|
|
{
|
|
int new_row, old_row;
|
|
#ifdef FEAT_GUI
|
|
int old_Rows;
|
|
#endif
|
|
win_T *wp;
|
|
int outofmem = FALSE;
|
|
int len;
|
|
schar_T *new_ScreenLines;
|
|
u8char_T *new_ScreenLinesUC = NULL;
|
|
u8char_T *new_ScreenLinesC[MAX_MCO];
|
|
schar_T *new_ScreenLines2 = NULL;
|
|
sattr_T *new_ScreenAttrs;
|
|
colnr_T *new_ScreenCols;
|
|
unsigned *new_LineOffset;
|
|
char_u *new_LineWraps;
|
|
short *new_TabPageIdxs;
|
|
#ifdef FEAT_PROP_POPUP
|
|
short *new_popup_mask;
|
|
short *new_popup_mask_next;
|
|
char *new_popup_transparent;
|
|
#endif
|
|
tabpage_T *tp;
|
|
static int entered = FALSE; // avoid recursiveness
|
|
static int done_outofmem_msg = FALSE; // did outofmem message
|
|
int retry_count = 0;
|
|
int found_null;
|
|
|
|
retry:
|
|
/*
|
|
* 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 ((ScreenLines != NULL
|
|
&& Rows == screen_Rows
|
|
&& Columns == screen_Columns
|
|
&& enc_utf8 == (ScreenLinesUC != NULL)
|
|
&& (enc_dbcs == DBCS_JPNU) == (ScreenLines2 != NULL)
|
|
&& p_mco == Screen_mco)
|
|
|| Rows == 0
|
|
|| Columns == 0
|
|
|| (!full_screen && ScreenLines == 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;
|
|
|
|
/*
|
|
* Note that the window sizes are updated before reallocating the arrays,
|
|
* thus we must not redraw here!
|
|
*/
|
|
++RedrawingDisabled;
|
|
|
|
win_new_shellsize(); // fit the windows in the new sized shell
|
|
|
|
#ifdef FEAT_GUI_HAIKU
|
|
vim_lock_screen(); // be safe, put it here
|
|
#endif
|
|
|
|
comp_col(); // recompute columns for shown command and ruler
|
|
|
|
/*
|
|
* We're changing the size of the screen.
|
|
* - Allocate new arrays for ScreenLines and ScreenAttrs.
|
|
* - 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 ScreenLines NULL, so we don't do anything!
|
|
* Continuing with the old ScreenLines may result in a crash, because the
|
|
* size is wrong.
|
|
*/
|
|
FOR_ALL_TAB_WINDOWS(tp, wp)
|
|
win_free_lsize(wp);
|
|
for (int i = 0; i < AUCMD_WIN_COUNT; ++i)
|
|
if (aucmd_win[i].auc_win != NULL)
|
|
win_free_lsize(aucmd_win[i].auc_win);
|
|
#ifdef FEAT_PROP_POPUP
|
|
// global popup windows
|
|
FOR_ALL_POPUPWINS(wp)
|
|
win_free_lsize(wp);
|
|
// tab-local popup windows
|
|
FOR_ALL_TABPAGES(tp)
|
|
FOR_ALL_POPUPWINS_IN_TAB(tp, wp)
|
|
win_free_lsize(wp);
|
|
#endif
|
|
|
|
new_ScreenLines = LALLOC_MULT(schar_T, (Rows + 1) * Columns);
|
|
vim_memset(new_ScreenLinesC, 0, sizeof(u8char_T *) * MAX_MCO);
|
|
if (enc_utf8)
|
|
{
|
|
new_ScreenLinesUC = LALLOC_MULT(u8char_T, (Rows + 1) * Columns);
|
|
for (int i = 0; i < p_mco; ++i)
|
|
new_ScreenLinesC[i] = LALLOC_CLEAR_MULT(u8char_T,
|
|
(Rows + 1) * Columns);
|
|
}
|
|
if (enc_dbcs == DBCS_JPNU)
|
|
new_ScreenLines2 = LALLOC_MULT(schar_T, (Rows + 1) * Columns);
|
|
new_ScreenAttrs = LALLOC_MULT(sattr_T, (Rows + 1) * Columns);
|
|
// Clear ScreenCols to avoid a warning for uninitialized memory in
|
|
// jump_to_mouse().
|
|
new_ScreenCols = LALLOC_CLEAR_MULT(colnr_T, (Rows + 1) * Columns);
|
|
new_LineOffset = LALLOC_MULT(unsigned, Rows);
|
|
new_LineWraps = LALLOC_MULT(char_u, Rows);
|
|
new_TabPageIdxs = LALLOC_MULT(short, Columns);
|
|
#ifdef FEAT_PROP_POPUP
|
|
new_popup_mask = LALLOC_MULT(short, Rows * Columns);
|
|
new_popup_mask_next = LALLOC_MULT(short, Rows * Columns);
|
|
new_popup_transparent = LALLOC_MULT(char, Rows * Columns);
|
|
#endif
|
|
|
|
FOR_ALL_TAB_WINDOWS(tp, wp)
|
|
{
|
|
if (win_alloc_lines(wp) == FAIL)
|
|
{
|
|
outofmem = TRUE;
|
|
goto give_up;
|
|
}
|
|
}
|
|
for (int i = 0; i < AUCMD_WIN_COUNT; ++i)
|
|
if (aucmd_win[i].auc_win != NULL
|
|
&& aucmd_win[i].auc_win->w_lines == NULL
|
|
&& win_alloc_lines(aucmd_win[i].auc_win) == FAIL)
|
|
{
|
|
outofmem = TRUE;
|
|
break;
|
|
}
|
|
#ifdef FEAT_PROP_POPUP
|
|
// global popup windows
|
|
FOR_ALL_POPUPWINS(wp)
|
|
if (win_alloc_lines(wp) == FAIL)
|
|
{
|
|
outofmem = TRUE;
|
|
goto give_up;
|
|
}
|
|
// tab-local popup windows
|
|
FOR_ALL_TABPAGES(tp)
|
|
FOR_ALL_POPUPWINS_IN_TAB(tp, wp)
|
|
if (win_alloc_lines(wp) == FAIL)
|
|
{
|
|
outofmem = TRUE;
|
|
goto give_up;
|
|
}
|
|
#endif
|
|
|
|
give_up:
|
|
found_null = FALSE;
|
|
for (int i = 0; i < p_mco; ++i)
|
|
if (new_ScreenLinesC[i] == NULL)
|
|
{
|
|
found_null = TRUE;
|
|
break;
|
|
}
|
|
if (new_ScreenLines == NULL
|
|
|| (enc_utf8 && (new_ScreenLinesUC == NULL || found_null))
|
|
|| (enc_dbcs == DBCS_JPNU && new_ScreenLines2 == NULL)
|
|
|| new_ScreenAttrs == NULL
|
|
|| new_ScreenCols == NULL
|
|
|| new_LineOffset == NULL
|
|
|| new_LineWraps == NULL
|
|
|| new_TabPageIdxs == NULL
|
|
#ifdef FEAT_PROP_POPUP
|
|
|| new_popup_mask == NULL
|
|
|| new_popup_mask_next == NULL
|
|
|| new_popup_transparent == NULL
|
|
#endif
|
|
|| outofmem)
|
|
{
|
|
if (ScreenLines != NULL || !done_outofmem_msg)
|
|
{
|
|
// guess the size
|
|
do_outofmem_msg((long_u)((Rows + 1) * Columns));
|
|
|
|
// Remember we did this to avoid getting outofmem messages over
|
|
// and over again.
|
|
done_outofmem_msg = TRUE;
|
|
}
|
|
VIM_CLEAR(new_ScreenLines);
|
|
VIM_CLEAR(new_ScreenLinesUC);
|
|
for (int i = 0; i < p_mco; ++i)
|
|
VIM_CLEAR(new_ScreenLinesC[i]);
|
|
VIM_CLEAR(new_ScreenLines2);
|
|
VIM_CLEAR(new_ScreenAttrs);
|
|
VIM_CLEAR(new_ScreenCols);
|
|
VIM_CLEAR(new_LineOffset);
|
|
VIM_CLEAR(new_LineWraps);
|
|
VIM_CLEAR(new_TabPageIdxs);
|
|
#ifdef FEAT_PROP_POPUP
|
|
VIM_CLEAR(new_popup_mask);
|
|
VIM_CLEAR(new_popup_mask_next);
|
|
VIM_CLEAR(new_popup_transparent);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
done_outofmem_msg = FALSE;
|
|
|
|
for (new_row = 0; new_row < Rows; ++new_row)
|
|
{
|
|
new_LineOffset[new_row] = new_row * Columns;
|
|
new_LineWraps[new_row] = FALSE;
|
|
|
|
(void)vim_memset(new_ScreenLines + new_row * Columns,
|
|
' ', (size_t)Columns * sizeof(schar_T));
|
|
if (enc_utf8)
|
|
{
|
|
(void)vim_memset(new_ScreenLinesUC + new_row * Columns,
|
|
0, (size_t)Columns * sizeof(u8char_T));
|
|
for (int i = 0; i < p_mco; ++i)
|
|
(void)vim_memset(new_ScreenLinesC[i]
|
|
+ new_row * Columns,
|
|
0, (size_t)Columns * sizeof(u8char_T));
|
|
}
|
|
if (enc_dbcs == DBCS_JPNU)
|
|
(void)vim_memset(new_ScreenLines2 + new_row * Columns,
|
|
0, (size_t)Columns * sizeof(schar_T));
|
|
(void)vim_memset(new_ScreenAttrs + new_row * Columns,
|
|
0, (size_t)Columns * sizeof(sattr_T));
|
|
(void)vim_memset(new_ScreenCols + new_row * Columns,
|
|
0, (size_t)Columns * sizeof(colnr_T));
|
|
|
|
/*
|
|
* 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 (!doclear)
|
|
{
|
|
old_row = new_row + (screen_Rows - Rows);
|
|
if (old_row >= 0 && ScreenLines != NULL)
|
|
{
|
|
if (screen_Columns < Columns)
|
|
len = screen_Columns;
|
|
else
|
|
len = Columns;
|
|
// When switching to utf-8 don't copy characters, they
|
|
// may be invalid now. Also when p_mco changes.
|
|
if (!(enc_utf8 && ScreenLinesUC == NULL)
|
|
&& p_mco == Screen_mco)
|
|
mch_memmove(new_ScreenLines + new_LineOffset[new_row],
|
|
ScreenLines + LineOffset[old_row],
|
|
(size_t)len * sizeof(schar_T));
|
|
if (enc_utf8 && ScreenLinesUC != NULL
|
|
&& p_mco == Screen_mco)
|
|
{
|
|
mch_memmove(new_ScreenLinesUC + new_LineOffset[new_row],
|
|
ScreenLinesUC + LineOffset[old_row],
|
|
(size_t)len * sizeof(u8char_T));
|
|
for (int i = 0; i < p_mco; ++i)
|
|
mch_memmove(new_ScreenLinesC[i]
|
|
+ new_LineOffset[new_row],
|
|
ScreenLinesC[i] + LineOffset[old_row],
|
|
(size_t)len * sizeof(u8char_T));
|
|
}
|
|
if (enc_dbcs == DBCS_JPNU && ScreenLines2 != NULL)
|
|
mch_memmove(new_ScreenLines2 + new_LineOffset[new_row],
|
|
ScreenLines2 + LineOffset[old_row],
|
|
(size_t)len * sizeof(schar_T));
|
|
mch_memmove(new_ScreenAttrs + new_LineOffset[new_row],
|
|
ScreenAttrs + LineOffset[old_row],
|
|
(size_t)len * sizeof(sattr_T));
|
|
mch_memmove(new_ScreenCols + new_LineOffset[new_row],
|
|
ScreenAttrs + LineOffset[old_row],
|
|
(size_t)len * sizeof(colnr_T));
|
|
}
|
|
}
|
|
}
|
|
// Use the last line of the screen for the current line.
|
|
current_ScreenLine = new_ScreenLines + Rows * Columns;
|
|
|
|
#ifdef FEAT_PROP_POPUP
|
|
vim_memset(new_popup_mask, 0, Rows * Columns * sizeof(short));
|
|
vim_memset(new_popup_transparent, 0, Rows * Columns * sizeof(char));
|
|
#endif
|
|
}
|
|
|
|
free_screenlines();
|
|
|
|
// NOTE: this may result in all pointers to become NULL.
|
|
ScreenLines = new_ScreenLines;
|
|
ScreenLinesUC = new_ScreenLinesUC;
|
|
for (int i = 0; i < p_mco; ++i)
|
|
ScreenLinesC[i] = new_ScreenLinesC[i];
|
|
Screen_mco = p_mco;
|
|
ScreenLines2 = new_ScreenLines2;
|
|
ScreenAttrs = new_ScreenAttrs;
|
|
ScreenCols = new_ScreenCols;
|
|
LineOffset = new_LineOffset;
|
|
LineWraps = new_LineWraps;
|
|
TabPageIdxs = new_TabPageIdxs;
|
|
#ifdef FEAT_PROP_POPUP
|
|
popup_mask = new_popup_mask;
|
|
popup_mask_next = new_popup_mask_next;
|
|
popup_transparent = new_popup_transparent;
|
|
popup_mask_refresh = TRUE;
|
|
#endif
|
|
|
|
// It's important that screen_Rows and screen_Columns reflect the actual
|
|
// size of ScreenLines[]. Set them before calling anything.
|
|
#ifdef FEAT_GUI
|
|
old_Rows = screen_Rows;
|
|
#endif
|
|
screen_Rows = Rows;
|
|
screen_Columns = Columns;
|
|
|
|
set_must_redraw(UPD_CLEAR); // need to clear the screen later
|
|
if (doclear)
|
|
screenclear2(TRUE);
|
|
#ifdef FEAT_GUI
|
|
else if (gui.in_use
|
|
&& !gui.starting
|
|
&& ScreenLines != NULL
|
|
&& old_Rows != Rows)
|
|
{
|
|
gui_redraw_block(0, 0, (int)Rows - 1, (int)Columns - 1, 0);
|
|
|
|
// 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 > old_Rows) // Rows got bigger
|
|
msg_row += Rows - old_Rows; // put cursor in same place
|
|
if (msg_col >= Columns) // Columns got smaller
|
|
msg_col = Columns - 1; // put cursor at last column
|
|
}
|
|
#endif
|
|
clear_TabPageIdxs();
|
|
|
|
#ifdef FEAT_GUI_HAIKU
|
|
vim_unlock_screen();
|
|
#endif
|
|
|
|
entered = FALSE;
|
|
if (RedrawingDisabled > 0)
|
|
--RedrawingDisabled;
|
|
|
|
/*
|
|
* Do not apply autocommands more than 3 times to avoid an endless loop
|
|
* in case applying autocommands always changes Rows or Columns.
|
|
*/
|
|
if (starting == 0 && ++retry_count <= 3)
|
|
{
|
|
apply_autocmds(EVENT_VIMRESIZED, NULL, NULL, FALSE, curbuf);
|
|
// In rare cases, autocommands may have altered Rows or Columns,
|
|
// jump back to check if we need to allocate the screen again.
|
|
goto retry;
|
|
}
|
|
}
|
|
|
|
void
|
|
free_screenlines(void)
|
|
{
|
|
int i;
|
|
|
|
VIM_CLEAR(ScreenLinesUC);
|
|
for (i = 0; i < Screen_mco; ++i)
|
|
VIM_CLEAR(ScreenLinesC[i]);
|
|
VIM_CLEAR(ScreenLines2);
|
|
VIM_CLEAR(ScreenLines);
|
|
VIM_CLEAR(ScreenAttrs);
|
|
VIM_CLEAR(ScreenCols);
|
|
VIM_CLEAR(LineOffset);
|
|
VIM_CLEAR(LineWraps);
|
|
VIM_CLEAR(TabPageIdxs);
|
|
#ifdef FEAT_PROP_POPUP
|
|
VIM_CLEAR(popup_mask);
|
|
VIM_CLEAR(popup_mask_next);
|
|
VIM_CLEAR(popup_transparent);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Clear the screen.
|
|
* May delay if there is something the user should read.
|
|
* Allocated the screen for resizing if needed.
|
|
* Returns TRUE when the screen was actually cleared, FALSE if all display
|
|
* cells were marked for updating.
|
|
*/
|
|
int
|
|
screenclear(void)
|
|
{
|
|
check_for_delay(FALSE);
|
|
screenalloc(FALSE); // allocate screen buffers if size changed
|
|
return screenclear2(TRUE); // clear the screen
|
|
}
|
|
|
|
/*
|
|
* Do not clear the screen but mark everything for redraw.
|
|
*/
|
|
void
|
|
redraw_as_cleared(void)
|
|
{
|
|
screenclear2(FALSE);
|
|
}
|
|
|
|
static int
|
|
screenclear2(int doclear)
|
|
{
|
|
int i;
|
|
int did_clear = FALSE;
|
|
|
|
if (starting == NO_SCREEN || ScreenLines == NULL
|
|
#ifdef FEAT_GUI
|
|
|| (gui.in_use && gui.starting)
|
|
#endif
|
|
)
|
|
return FALSE;
|
|
|
|
#ifdef FEAT_GUI
|
|
if (!gui.in_use)
|
|
#endif
|
|
screen_attr = -1; // force setting the Normal colors
|
|
screen_stop_highlight(); // don't want highlighting here
|
|
|
|
#ifdef FEAT_CLIPBOARD
|
|
// disable selection without redrawing it
|
|
clip_scroll_selection(9999);
|
|
#endif
|
|
|
|
// blank out ScreenLines
|
|
for (i = 0; i < Rows; ++i)
|
|
{
|
|
lineclear(LineOffset[i], (int)Columns, 0);
|
|
LineWraps[i] = FALSE;
|
|
}
|
|
|
|
if (doclear && can_clear(T_CL))
|
|
{
|
|
out_str(T_CL); // clear the display
|
|
did_clear = TRUE;
|
|
clear_cmdline = FALSE;
|
|
mode_displayed = FALSE;
|
|
}
|
|
else
|
|
{
|
|
// can't clear the screen, mark all chars with invalid attributes
|
|
for (i = 0; i < Rows; ++i)
|
|
lineinvalid(LineOffset[i], (int)Columns);
|
|
clear_cmdline = TRUE;
|
|
}
|
|
|
|
screen_cleared = TRUE; // can use contents of ScreenLines now
|
|
|
|
win_rest_invalid(firstwin); // redraw all regular windows
|
|
redraw_cmdline = TRUE;
|
|
redraw_tabline = TRUE;
|
|
if (must_redraw == UPD_CLEAR) // no need to clear again
|
|
must_redraw = UPD_NOT_VALID;
|
|
msg_scrolled = 0; // compute_cmdrow() uses this
|
|
compute_cmdrow();
|
|
#ifdef FEAT_PROP_POPUP
|
|
popup_redraw_all(); // redraw all popup windows
|
|
#endif
|
|
msg_row = cmdline_row; // put cursor on last line for messages
|
|
msg_col = 0;
|
|
screen_start(); // don't know where cursor is now
|
|
msg_didany = FALSE;
|
|
msg_didout = FALSE;
|
|
|
|
return did_clear;
|
|
}
|
|
|
|
/*
|
|
* Clear one line in ScreenLines.
|
|
*/
|
|
static void
|
|
lineclear(unsigned off, int width, int attr)
|
|
{
|
|
(void)vim_memset(ScreenLines + off, ' ', (size_t)width * sizeof(schar_T));
|
|
if (enc_utf8)
|
|
(void)vim_memset(ScreenLinesUC + off, 0,
|
|
(size_t)width * sizeof(u8char_T));
|
|
(void)vim_memset(ScreenAttrs + off, attr, (size_t)width * sizeof(sattr_T));
|
|
(void)vim_memset(ScreenCols + off, -1, (size_t)width * sizeof(colnr_T));
|
|
}
|
|
|
|
/*
|
|
* Mark one line in ScreenLines invalid by setting the attributes to an
|
|
* invalid value.
|
|
*/
|
|
static void
|
|
lineinvalid(unsigned off, int width)
|
|
{
|
|
(void)vim_memset(ScreenAttrs + off, -1, (size_t)width * sizeof(sattr_T));
|
|
(void)vim_memset(ScreenCols + off, -1, (size_t)width * sizeof(colnr_T));
|
|
}
|
|
|
|
/*
|
|
* To be called when characters were sent to the terminal directly, outputting
|
|
* test on "screen_lnum".
|
|
*/
|
|
void
|
|
line_was_clobbered(int screen_lnum)
|
|
{
|
|
lineinvalid(LineOffset[screen_lnum], (int)Columns);
|
|
}
|
|
|
|
/*
|
|
* Copy part of a Screenline for vertically split window "wp".
|
|
*/
|
|
static void
|
|
linecopy(int to, int from, win_T *wp)
|
|
{
|
|
unsigned off_to = LineOffset[to] + wp->w_wincol;
|
|
unsigned off_from = LineOffset[from] + wp->w_wincol;
|
|
|
|
mch_memmove(ScreenLines + off_to, ScreenLines + off_from,
|
|
wp->w_width * sizeof(schar_T));
|
|
if (enc_utf8)
|
|
{
|
|
int i;
|
|
|
|
mch_memmove(ScreenLinesUC + off_to, ScreenLinesUC + off_from,
|
|
wp->w_width * sizeof(u8char_T));
|
|
for (i = 0; i < p_mco; ++i)
|
|
mch_memmove(ScreenLinesC[i] + off_to, ScreenLinesC[i] + off_from,
|
|
wp->w_width * sizeof(u8char_T));
|
|
}
|
|
if (enc_dbcs == DBCS_JPNU)
|
|
mch_memmove(ScreenLines2 + off_to, ScreenLines2 + off_from,
|
|
wp->w_width * sizeof(schar_T));
|
|
mch_memmove(ScreenAttrs + off_to, ScreenAttrs + off_from,
|
|
wp->w_width * sizeof(sattr_T));
|
|
mch_memmove(ScreenCols + off_to, ScreenCols + off_from,
|
|
wp->w_width * sizeof(colnr_T));
|
|
}
|
|
|
|
/*
|
|
* Return TRUE if clearing with term string "p" would work.
|
|
* It can't work when the string is empty or it won't set the right background.
|
|
* Don't clear to end-of-line when there are popups, it may cause flicker.
|
|
*/
|
|
int
|
|
can_clear(char_u *p)
|
|
{
|
|
return (*p != NUL && (t_colors <= 1
|
|
#ifdef FEAT_GUI
|
|
|| gui.in_use
|
|
#endif
|
|
#ifdef FEAT_TERMGUICOLORS
|
|
|| (p_tgc && cterm_normal_bg_gui_color == INVALCOLOR)
|
|
|| (!p_tgc && cterm_normal_bg_color == 0)
|
|
#else
|
|
|| cterm_normal_bg_color == 0
|
|
#endif
|
|
|| *T_UT != NUL)
|
|
#ifdef FEAT_PROP_POPUP
|
|
&& !(p == T_CE && popup_visible)
|
|
#endif
|
|
);
|
|
}
|
|
|
|
/*
|
|
* 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(void)
|
|
{
|
|
screen_cur_row = screen_cur_col = 9999;
|
|
}
|
|
|
|
/*
|
|
* Move the cursor to position "row","col" in the screen.
|
|
* This tries to find the most efficient way to move, minimizing the number of
|
|
* characters sent to the terminal.
|
|
*/
|
|
void
|
|
windgoto(int row, int col)
|
|
{
|
|
sattr_T *p;
|
|
int i;
|
|
int plan;
|
|
int cost;
|
|
int wouldbe_col;
|
|
int noinvcurs;
|
|
char_u *bs;
|
|
int goto_cost;
|
|
int attr;
|
|
|
|
#define GOTO_COST 7 // assume a term_windgoto() takes about 7 chars
|
|
#define HIGHL_COST 5 // assume unhighlight takes 5 chars
|
|
|
|
#define PLAN_LE 1
|
|
#define PLAN_CR 2
|
|
#define PLAN_NL 3
|
|
#define PLAN_WRITE 4
|
|
// Can't use ScreenLines unless initialized
|
|
if (ScreenLines == NULL)
|
|
return;
|
|
if (col == screen_cur_col && row == screen_cur_row)
|
|
return;
|
|
|
|
// Check for valid position.
|
|
if (row < 0) // window without text lines?
|
|
row = 0;
|
|
if (row >= screen_Rows)
|
|
row = screen_Rows - 1;
|
|
if (col >= screen_Columns)
|
|
col = screen_Columns - 1;
|
|
|
|
// 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 T_LE 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 with T_ND or by writing chars.
|
|
*
|
|
* 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 attributes 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 T_LE.
|
|
*/
|
|
bs = NULL; // init for GCC
|
|
attr = screen_attr;
|
|
if (row == screen_cur_row && col < screen_cur_col)
|
|
{
|
|
// "le" is preferred over "bc", because "bc" is obsolete
|
|
if (*T_LE)
|
|
bs = T_LE; // "cursor left"
|
|
else
|
|
bs = T_BC; // "backspace character (old)
|
|
if (*bs)
|
|
cost = (screen_cur_col - col) * (int)STRLEN(bs);
|
|
else
|
|
cost = 999;
|
|
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_LE;
|
|
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. Also avoid UTF-8 characters.
|
|
*/
|
|
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 = ScreenAttrs + LineOffset[row] + wouldbe_col;
|
|
while (i && *p++ == attr)
|
|
--i;
|
|
if (i != 0)
|
|
{
|
|
/*
|
|
* Try if it works when highlighting is stopped here.
|
|
*/
|
|
if (*--p == 0)
|
|
{
|
|
cost += noinvcurs;
|
|
while (i && *p++ == 0)
|
|
--i;
|
|
}
|
|
if (i != 0)
|
|
cost = 999; // different attributes, don't do it
|
|
}
|
|
if (enc_utf8)
|
|
{
|
|
// Don't use an UTF-8 char for positioning, it's slow.
|
|
for (i = wouldbe_col; i < col; ++i)
|
|
if (ScreenLinesUC[LineOffset[row] + i] != 0)
|
|
{
|
|
cost = 999;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* We can do it without term_windgoto()!
|
|
*/
|
|
if (cost < goto_cost)
|
|
{
|
|
if (plan == PLAN_LE)
|
|
{
|
|
if (noinvcurs)
|
|
screen_stop_highlight();
|
|
while (screen_cur_col > col)
|
|
{
|
|
out_str(bs);
|
|
--screen_cur_col;
|
|
}
|
|
}
|
|
else if (plan == PLAN_CR)
|
|
{
|
|
if (noinvcurs)
|
|
screen_stop_highlight();
|
|
out_char('\r');
|
|
screen_cur_col = 0;
|
|
}
|
|
else if (plan == PLAN_NL)
|
|
{
|
|
if (noinvcurs)
|
|
screen_stop_highlight();
|
|
while (screen_cur_row < row)
|
|
{
|
|
out_char('\n');
|
|
++screen_cur_row;
|
|
}
|
|
screen_cur_col = 0;
|
|
}
|
|
|
|
i = col - screen_cur_col;
|
|
if (i > 0)
|
|
{
|
|
/*
|
|
* Use cursor-right if it's one character only. Avoids
|
|
* removing a line of pixels from the last bold char, when
|
|
* using the bold trick in the GUI.
|
|
*/
|
|
if (T_ND[0] != NUL && T_ND[1] == NUL)
|
|
{
|
|
while (i-- > 0)
|
|
out_char(*T_ND);
|
|
}
|
|
else
|
|
{
|
|
int off;
|
|
|
|
off = LineOffset[row] + screen_cur_col;
|
|
while (i-- > 0)
|
|
{
|
|
if (ScreenAttrs[off] != screen_attr)
|
|
screen_stop_highlight();
|
|
out_flush_check();
|
|
out_char(ScreenLines[off]);
|
|
if (enc_dbcs == DBCS_JPNU
|
|
&& ScreenLines[off] == 0x8e)
|
|
out_char(ScreenLines2[off]);
|
|
++off;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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 its position in the current window.
|
|
*/
|
|
void
|
|
setcursor(void)
|
|
{
|
|
setcursor_mayforce(FALSE);
|
|
}
|
|
|
|
/*
|
|
* Set cursor to its position in the current window.
|
|
* When "force" is TRUE also when not redrawing.
|
|
*/
|
|
void
|
|
setcursor_mayforce(int force)
|
|
{
|
|
if (force || redrawing())
|
|
{
|
|
validate_cursor();
|
|
windgoto(W_WINROW(curwin) + curwin->w_wrow,
|
|
curwin->w_wincol + (
|
|
#ifdef FEAT_RIGHTLEFT
|
|
// With 'rightleft' set and the cursor on a double-wide
|
|
// character, position it on the leftmost column.
|
|
curwin->w_p_rl ? ((int)curwin->w_width - curwin->w_wcol
|
|
- ((has_mbyte
|
|
&& (*mb_ptr2cells)(ml_get_cursor()) == 2
|
|
&& vim_isprintc(gchar_cursor())) ? 2 : 1)) :
|
|
#endif
|
|
curwin->w_wcol));
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Insert 'line_count' lines at 'row' in window 'wp'.
|
|
* If 'invalid' is TRUE the wp->w_lines[].wl_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(
|
|
win_T *wp,
|
|
int row,
|
|
int line_count,
|
|
int invalid,
|
|
int mayclear)
|
|
{
|
|
int did_delete;
|
|
int nextrow;
|
|
int lastrow;
|
|
int retval;
|
|
|
|
if (invalid)
|
|
wp->w_lines_valid = 0;
|
|
|
|
// with only a few lines it's not worth the effort
|
|
if (wp->w_height < 5)
|
|
return FAIL;
|
|
|
|
// with the popup menu visible this might not work correctly
|
|
if (pum_visible())
|
|
return FAIL;
|
|
|
|
if (line_count > wp->w_height - row)
|
|
line_count = wp->w_height - row;
|
|
|
|
retval = win_do_lines(wp, row, line_count, mayclear, FALSE, 0);
|
|
if (retval != MAYBE)
|
|
return retval;
|
|
|
|
/*
|
|
* 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 != NULL || wp->w_status_height)
|
|
{
|
|
if (screen_del_lines(0, W_WINROW(wp) + wp->w_height - line_count,
|
|
line_count, (int)Rows, FALSE, 0, NULL) == 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 = W_WINROW(wp) + wp->w_height + wp->w_status_height;
|
|
lastrow = nextrow + line_count;
|
|
if (lastrow > Rows)
|
|
lastrow = Rows;
|
|
screen_fill(nextrow - line_count, lastrow - line_count,
|
|
wp->w_wincol, (int)W_ENDCOL(wp),
|
|
' ', ' ', 0);
|
|
}
|
|
|
|
if (screen_ins_lines(0, W_WINROW(wp) + row, line_count, (int)Rows, 0, NULL)
|
|
== FAIL)
|
|
{
|
|
// deletion will have messed up other windows
|
|
if (did_delete)
|
|
{
|
|
wp->w_redr_status = TRUE;
|
|
win_rest_invalid(W_NEXT(wp));
|
|
}
|
|
return FAIL;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/*
|
|
* Delete "line_count" window lines at "row" in window "wp".
|
|
* If "invalid" is TRUE curwin->w_lines[] 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(
|
|
win_T *wp,
|
|
int row,
|
|
int line_count,
|
|
int invalid,
|
|
int mayclear,
|
|
int clear_attr) // for clearing lines
|
|
{
|
|
int retval;
|
|
|
|
if (invalid)
|
|
wp->w_lines_valid = 0;
|
|
|
|
if (line_count > wp->w_height - row)
|
|
line_count = wp->w_height - row;
|
|
|
|
retval = win_do_lines(wp, row, line_count, mayclear, TRUE, clear_attr);
|
|
if (retval != MAYBE)
|
|
return retval;
|
|
|
|
if (screen_del_lines(0, W_WINROW(wp) + row, line_count,
|
|
(int)Rows, FALSE, clear_attr, NULL) == 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, W_WINROW(wp) + wp->w_height - line_count,
|
|
line_count, (int)Rows, clear_attr, NULL) == 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;
|
|
}
|
|
|
|
/*
|
|
* Common code for win_ins_lines() and win_del_lines().
|
|
* Returns OK or FAIL when the work has been done.
|
|
* Returns MAYBE when not finished yet.
|
|
*/
|
|
static int
|
|
win_do_lines(
|
|
win_T *wp,
|
|
int row,
|
|
int line_count,
|
|
int mayclear,
|
|
int del,
|
|
int clear_attr)
|
|
{
|
|
int retval;
|
|
|
|
if (!redrawing() || line_count <= 0)
|
|
return FAIL;
|
|
|
|
// When inserting lines would result in loss of command output, just redraw
|
|
// the lines.
|
|
if (no_win_do_lines_ins && !del)
|
|
return FAIL;
|
|
|
|
// only a few lines left: redraw is faster
|
|
if (mayclear && Rows - line_count < 5 && wp->w_width == Columns)
|
|
{
|
|
if (!no_win_do_lines_ins)
|
|
screenclear(); // will set wp->w_lines_valid to 0
|
|
return FAIL;
|
|
}
|
|
|
|
#ifdef FEAT_PROP_POPUP
|
|
// this doesn't work when there are popups visible
|
|
if (popup_visible)
|
|
return FAIL;
|
|
#endif
|
|
|
|
// Delete all remaining lines
|
|
if (row + line_count >= wp->w_height)
|
|
{
|
|
screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + wp->w_height,
|
|
wp->w_wincol, (int)W_ENDCOL(wp),
|
|
' ', ' ', 0);
|
|
return OK;
|
|
}
|
|
|
|
/*
|
|
* When scrolling, the message on the command line should be cleared,
|
|
* otherwise it will stay there forever.
|
|
* Don't do this when avoiding to insert lines.
|
|
*/
|
|
if (!no_win_do_lines_ins)
|
|
clear_cmdline = TRUE;
|
|
|
|
/*
|
|
* If the terminal can set a scroll region, use that.
|
|
* Always do this in a vertically split window. This will redraw from
|
|
* ScreenLines[] when t_CV isn't defined. That's faster than using
|
|
* win_line().
|
|
* Don't use a scroll region when we are going to redraw the text, writing
|
|
* a character in the lower right corner of the scroll region may cause a
|
|
* scroll-up .
|
|
*/
|
|
if (scroll_region || wp->w_width != Columns)
|
|
{
|
|
if (scroll_region && (wp->w_width == Columns || *T_CSV != NUL))
|
|
scroll_region_set(wp, row);
|
|
if (del)
|
|
retval = screen_del_lines(W_WINROW(wp) + row, 0, line_count,
|
|
wp->w_height - row, FALSE, clear_attr, wp);
|
|
else
|
|
retval = screen_ins_lines(W_WINROW(wp) + row, 0, line_count,
|
|
wp->w_height - row, clear_attr, wp);
|
|
if (scroll_region && (wp->w_width == Columns || *T_CSV != NUL))
|
|
scroll_region_reset();
|
|
return retval;
|
|
}
|
|
|
|
if (wp->w_next != NULL && p_tf) // don't delete/insert on fast terminal
|
|
return FAIL;
|
|
|
|
return MAYBE;
|
|
}
|
|
|
|
/*
|
|
* window 'wp' and everything after it is messed up, mark it for redraw
|
|
*/
|
|
static void
|
|
win_rest_invalid(win_T *wp)
|
|
{
|
|
while (wp != NULL)
|
|
{
|
|
redraw_win_later(wp, UPD_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
|
|
#define USE_REDRAW 9
|
|
|
|
/*
|
|
* insert lines on the screen and update ScreenLines[]
|
|
* "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.
|
|
*/
|
|
int
|
|
screen_ins_lines(
|
|
int off,
|
|
int row,
|
|
int line_count,
|
|
int end,
|
|
int clear_attr,
|
|
win_T *wp) // NULL or window to use width from
|
|
{
|
|
int i;
|
|
int j;
|
|
unsigned temp;
|
|
int cursor_row;
|
|
int cursor_col = 0;
|
|
int type;
|
|
int result_empty;
|
|
int can_ce = can_clear(T_CE);
|
|
|
|
/*
|
|
* FAIL if
|
|
* - there is no valid screen
|
|
* - the line count is less than one
|
|
* - the line count is more than 'ttyscroll'
|
|
* - "end" is more than "Rows" (safety check, should not happen)
|
|
* - redrawing for a callback and there is a modeless selection
|
|
* - there is a popup window
|
|
*/
|
|
if (!screen_valid(TRUE)
|
|
|| line_count <= 0 || line_count > p_ttyscroll
|
|
|| end > Rows
|
|
#ifdef FEAT_CLIPBOARD
|
|
|| (clip_star.state != SELECT_CLEARED
|
|
&& redrawing_for_callback > 0)
|
|
#endif
|
|
#ifdef FEAT_PROP_POPUP
|
|
|| popup_visible
|
|
#endif
|
|
)
|
|
return FAIL;
|
|
|
|
/*
|
|
* There are seven ways to insert lines:
|
|
* 0. When in a vertically split window and t_CV isn't set, redraw the
|
|
* characters from ScreenLines[].
|
|
* 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.
|
|
* 8. redraw the characters from ScreenLines[].
|
|
*
|
|
* 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 (wp != NULL && wp->w_width != Columns && *T_CSV == NUL)
|
|
{
|
|
// Avoid that lines are first cleared here and then redrawn, which
|
|
// results in many characters updated twice. This happens with CTRL-F
|
|
// in a vertically split window. With line-by-line scrolling
|
|
// USE_REDRAW should be faster.
|
|
if (line_count > 3)
|
|
return FAIL;
|
|
type = USE_REDRAW;
|
|
}
|
|
else if (can_clear(T_CD) && 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 || !can_ce))
|
|
type = USE_T_CDL;
|
|
else if (*T_AL != NUL)
|
|
type = USE_T_AL;
|
|
else if (can_ce && 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 || can_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, 0, wp);
|
|
|
|
/*
|
|
* 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, 0, wp);
|
|
|
|
#ifdef FEAT_CLIPBOARD
|
|
// Remove a modeless selection when inserting lines halfway the screen
|
|
// or not the full width of the screen.
|
|
if (off + row > 0 || (wp != NULL && wp->w_width != Columns))
|
|
clip_clear_selection(&clip_star);
|
|
else
|
|
clip_scroll_selection(-line_count);
|
|
#endif
|
|
|
|
#ifdef FEAT_GUI_HAIKU
|
|
vim_lock_screen();
|
|
#endif
|
|
|
|
#ifdef FEAT_GUI
|
|
// Don't update the GUI cursor here, ScreenLines[] is invalid until the
|
|
// scrolling is actually carried out.
|
|
gui_dont_update_cursor(row + off <= gui.cursor_row);
|
|
#endif
|
|
|
|
if (wp != NULL && wp->w_wincol != 0 && *T_CSV != NUL && *T_CCS == NUL)
|
|
cursor_col = wp->w_wincol;
|
|
|
|
if (*T_CCS != NUL) // cursor relative to region
|
|
cursor_row = row;
|
|
else
|
|
cursor_row = row + off;
|
|
|
|
/*
|
|
* Shift LineOffset[] line_count down to reflect the inserted lines.
|
|
* Clear the inserted lines in ScreenLines[].
|
|
*/
|
|
row += off;
|
|
end += off;
|
|
for (i = 0; i < line_count; ++i)
|
|
{
|
|
if (wp != NULL && wp->w_width != Columns)
|
|
{
|
|
// need to copy part of a line
|
|
j = end - 1 - i;
|
|
while ((j -= line_count) >= row)
|
|
linecopy(j + line_count, j, wp);
|
|
j += line_count;
|
|
if (can_clear((char_u *)" "))
|
|
lineclear(LineOffset[j] + wp->w_wincol, wp->w_width,
|
|
clear_attr);
|
|
else
|
|
lineinvalid(LineOffset[j] + wp->w_wincol, wp->w_width);
|
|
LineWraps[j] = FALSE;
|
|
}
|
|
else
|
|
{
|
|
j = end - 1 - i;
|
|
temp = LineOffset[j];
|
|
while ((j -= line_count) >= row)
|
|
{
|
|
LineOffset[j + line_count] = LineOffset[j];
|
|
LineWraps[j + line_count] = LineWraps[j];
|
|
}
|
|
LineOffset[j + line_count] = temp;
|
|
LineWraps[j + line_count] = FALSE;
|
|
if (can_clear((char_u *)" "))
|
|
lineclear(temp, (int)Columns, clear_attr);
|
|
else
|
|
lineinvalid(temp, (int)Columns);
|
|
}
|
|
}
|
|
|
|
#ifdef FEAT_GUI_HAIKU
|
|
vim_unlock_screen();
|
|
#endif
|
|
|
|
screen_stop_highlight();
|
|
windgoto(cursor_row, cursor_col);
|
|
if (clear_attr != 0)
|
|
screen_start_highlight(clear_attr);
|
|
|
|
// redraw the characters
|
|
if (type == USE_REDRAW)
|
|
redraw_block(row, end, wp);
|
|
else 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, cursor_col);
|
|
out_str(T_AL);
|
|
}
|
|
else // type == USE_T_SR
|
|
out_str(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, cursor_col);
|
|
out_str(T_CE);
|
|
screen_start(); // don't know where cursor is now
|
|
}
|
|
}
|
|
|
|
#ifdef FEAT_GUI
|
|
gui_can_update_cursor();
|
|
if (gui.in_use)
|
|
out_flush(); // always flush after a scroll
|
|
#endif
|
|
return OK;
|
|
}
|
|
|
|
/*
|
|
* Delete lines on the screen and update ScreenLines[].
|
|
* "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(
|
|
int off,
|
|
int row,
|
|
int line_count,
|
|
int end,
|
|
int force, // even when line_count > p_ttyscroll
|
|
int clear_attr, // used for clearing lines
|
|
win_T *wp) // NULL or window to use width from
|
|
{
|
|
int j;
|
|
int i;
|
|
unsigned temp;
|
|
int cursor_row;
|
|
int cursor_col = 0;
|
|
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'
|
|
* - "end" is more than "Rows" (safety check, should not happen)
|
|
* - redrawing for a callback and there is a modeless selection
|
|
*/
|
|
if (!screen_valid(TRUE)
|
|
|| line_count <= 0
|
|
|| (!force && line_count > p_ttyscroll)
|
|
|| end > Rows
|
|
#ifdef FEAT_CLIPBOARD
|
|
|| (clip_star.state != SELECT_CLEARED && redrawing_for_callback > 0)
|
|
#endif
|
|
)
|
|
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 || can_clear(T_CE));
|
|
|
|
/*
|
|
* There are six ways to delete lines:
|
|
* 0. When in a vertically split window and t_CV isn't set, redraw the
|
|
* characters from ScreenLines[].
|
|
* 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.
|
|
* 6. redraw the characters from ScreenLines[].
|
|
*/
|
|
if (wp != NULL && wp->w_width != Columns && *T_CSV == NUL)
|
|
{
|
|
// Avoid that lines are first cleared here and then redrawn, which
|
|
// results in many characters updated twice. This happens with CTRL-F
|
|
// in a vertically split window. With line-by-line scrolling
|
|
// USE_REDRAW should be faster.
|
|
if (line_count > 3)
|
|
return FAIL;
|
|
type = USE_REDRAW;
|
|
}
|
|
else if (can_clear(T_CD) && result_empty)
|
|
type = USE_T_CD;
|
|
else if (row == 0 && (
|
|
#ifndef AMIGA
|
|
// On the Amiga, somehow '\n' on the last line doesn't always scroll
|
|
// up, so use delete-line command
|
|
line_count == 1 ||
|
|
#endif
|
|
*T_CDL == NUL))
|
|
type = USE_NL;
|
|
else if (*T_CDL != NUL && line_count > 1 && can_delete)
|
|
type = USE_T_CDL;
|
|
else if (can_clear(T_CE) && result_empty
|
|
&& (wp == NULL || wp->w_width == Columns))
|
|
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;
|
|
|
|
#ifdef FEAT_CLIPBOARD
|
|
// Remove a modeless selection when deleting lines halfway the screen or
|
|
// not the full width of the screen.
|
|
if (off + row > 0 || (wp != NULL && wp->w_width != Columns))
|
|
clip_clear_selection(&clip_star);
|
|
else
|
|
clip_scroll_selection(line_count);
|
|
#endif
|
|
|
|
#ifdef FEAT_GUI_HAIKU
|
|
vim_lock_screen();
|
|
#endif
|
|
|
|
#ifdef FEAT_GUI
|
|
// Don't update the GUI cursor here, ScreenLines[] is invalid until the
|
|
// scrolling is actually carried out.
|
|
gui_dont_update_cursor(gui.cursor_row >= row + off
|
|
&& gui.cursor_row < end + off);
|
|
#endif
|
|
|
|
if (wp != NULL && wp->w_wincol != 0 && *T_CSV != NUL && *T_CCS == NUL)
|
|
cursor_col = wp->w_wincol;
|
|
|
|
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 LineOffset[] line_count up to reflect the deleted lines.
|
|
* Clear the inserted lines in ScreenLines[].
|
|
*/
|
|
row += off;
|
|
end += off;
|
|
for (i = 0; i < line_count; ++i)
|
|
{
|
|
if (wp != NULL && wp->w_width != Columns)
|
|
{
|
|
// need to copy part of a line
|
|
j = row + i;
|
|
while ((j += line_count) <= end - 1)
|
|
linecopy(j - line_count, j, wp);
|
|
j -= line_count;
|
|
if (can_clear((char_u *)" "))
|
|
lineclear(LineOffset[j] + wp->w_wincol, wp->w_width,
|
|
clear_attr);
|
|
else
|
|
lineinvalid(LineOffset[j] + wp->w_wincol, wp->w_width);
|
|
LineWraps[j] = FALSE;
|
|
}
|
|
else
|
|
{
|
|
// whole width, moving the line pointers is faster
|
|
j = row + i;
|
|
temp = LineOffset[j];
|
|
while ((j += line_count) <= end - 1)
|
|
{
|
|
LineOffset[j - line_count] = LineOffset[j];
|
|
LineWraps[j - line_count] = LineWraps[j];
|
|
}
|
|
LineOffset[j - line_count] = temp;
|
|
LineWraps[j - line_count] = FALSE;
|
|
if (can_clear((char_u *)" "))
|
|
lineclear(temp, (int)Columns, clear_attr);
|
|
else
|
|
lineinvalid(temp, (int)Columns);
|
|
}
|
|
}
|
|
|
|
#ifdef FEAT_GUI_HAIKU
|
|
vim_unlock_screen();
|
|
#endif
|
|
|
|
if (screen_attr != clear_attr)
|
|
screen_stop_highlight();
|
|
if (clear_attr != 0)
|
|
screen_start_highlight(clear_attr);
|
|
|
|
// redraw the characters
|
|
if (type == USE_REDRAW)
|
|
redraw_block(row, end, wp);
|
|
else if (type == USE_T_CD) // delete the lines
|
|
{
|
|
windgoto(cursor_row, cursor_col);
|
|
out_str(T_CD);
|
|
screen_start(); // don't know where cursor is now
|
|
}
|
|
else if (type == USE_T_CDL)
|
|
{
|
|
windgoto(cursor_row, cursor_col);
|
|
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, cursor_col);
|
|
for (i = line_count; --i >= 0; )
|
|
out_char('\n'); // cursor will remain on same line
|
|
}
|
|
else
|
|
{
|
|
for (i = line_count; --i >= 0; )
|
|
{
|
|
if (type == USE_T_DL)
|
|
{
|
|
windgoto(cursor_row, cursor_col);
|
|
out_str(T_DL); // delete a line
|
|
}
|
|
else // type == USE_T_CE
|
|
{
|
|
windgoto(cursor_row + i, cursor_col);
|
|
out_str(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, cursor_col);
|
|
out_str(T_CE); // erase a line
|
|
screen_start(); // don't know where cursor is now
|
|
}
|
|
}
|
|
|
|
#ifdef FEAT_GUI
|
|
gui_can_update_cursor();
|
|
if (gui.in_use)
|
|
out_flush(); // always flush after a scroll
|
|
#endif
|
|
|
|
return OK;
|
|
}
|
|
|
|
/*
|
|
* Return TRUE when postponing displaying the mode message: when not redrawing
|
|
* or inside a mapping.
|
|
*/
|
|
int
|
|
skip_showmode(void)
|
|
{
|
|
// Call char_avail() only when we are going to show something, because it
|
|
// takes a bit of time. redrawing() may also call char_avail().
|
|
if (global_busy
|
|
|| msg_silent != 0
|
|
|| !redrawing()
|
|
|| (char_avail() && !KeyTyped))
|
|
{
|
|
redraw_mode = TRUE; // show mode later
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
* If redraw_mode is TRUE show or clear the mode.
|
|
* Return the length of the message (0 if no message).
|
|
*/
|
|
int
|
|
showmode(void)
|
|
{
|
|
int need_clear;
|
|
int length = 0;
|
|
int do_mode;
|
|
int attr;
|
|
int nwr_save;
|
|
int sub_attr;
|
|
|
|
do_mode = p_smd && msg_silent == 0
|
|
&& ((State & MODE_INSERT)
|
|
|| restart_edit != NUL
|
|
|| VIsual_active);
|
|
if (do_mode || reg_recording != 0)
|
|
{
|
|
if (skip_showmode())
|
|
return 0; // show mode later
|
|
|
|
nwr_save = need_wait_return;
|
|
|
|
// wait a bit before overwriting an important message
|
|
check_for_delay(FALSE);
|
|
|
|
// if the cmdline is more than one line high, erase top lines
|
|
need_clear = clear_cmdline;
|
|
if (clear_cmdline && cmdline_row < Rows - 1)
|
|
msg_clr_cmdline(); // will reset clear_cmdline
|
|
|
|
// Position on the last line in the window, column 0
|
|
msg_pos_mode();
|
|
cursor_off();
|
|
attr = HL_ATTR(HLF_CM); // Highlight mode
|
|
if (do_mode)
|
|
{
|
|
msg_puts_attr("--", attr);
|
|
#if defined(FEAT_XIM)
|
|
if (
|
|
# ifdef FEAT_GUI_GTK
|
|
preedit_get_status()
|
|
# else
|
|
im_get_status()
|
|
# endif
|
|
)
|
|
# ifdef FEAT_GUI_GTK // most of the time, it's not XIM being used
|
|
msg_puts_attr(" IM", attr);
|
|
# else
|
|
msg_puts_attr(" XIM", attr);
|
|
# endif
|
|
#endif
|
|
// CTRL-X in Insert mode
|
|
if (edit_submode != NULL && !shortmess(SHM_COMPLETIONMENU))
|
|
{
|
|
// These messages can get long, avoid a wrap in a narrow
|
|
// window. Prefer showing edit_submode_extra.
|
|
length = (Rows - msg_row) * Columns - 3;
|
|
if (edit_submode_extra != NULL)
|
|
length -= vim_strsize(edit_submode_extra);
|
|
if (length > 0)
|
|
{
|
|
if (edit_submode_pre != NULL)
|
|
length -= vim_strsize(edit_submode_pre);
|
|
if (length - vim_strsize(edit_submode) > 0)
|
|
{
|
|
if (edit_submode_pre != NULL)
|
|
msg_puts_attr((char *)edit_submode_pre, attr);
|
|
msg_puts_attr((char *)edit_submode, attr);
|
|
}
|
|
if (edit_submode_extra != NULL)
|
|
{
|
|
msg_puts_attr(" ", attr); // add a space in between
|
|
if ((int)edit_submode_highl < (int)HLF_COUNT)
|
|
sub_attr = HL_ATTR(edit_submode_highl);
|
|
else
|
|
sub_attr = attr;
|
|
msg_puts_attr((char *)edit_submode_extra, sub_attr);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (State & VREPLACE_FLAG)
|
|
msg_puts_attr(_(" VREPLACE"), attr);
|
|
else if (State & REPLACE_FLAG)
|
|
msg_puts_attr(_(" REPLACE"), attr);
|
|
else if (State & MODE_INSERT)
|
|
{
|
|
#ifdef FEAT_RIGHTLEFT
|
|
if (p_ri)
|
|
msg_puts_attr(_(" REVERSE"), attr);
|
|
#endif
|
|
msg_puts_attr(_(" INSERT"), attr);
|
|
}
|
|
else if (restart_edit == 'I' || restart_edit == 'i' ||
|
|
restart_edit == 'a' || restart_edit == 'A')
|
|
msg_puts_attr(_(" (insert)"), attr);
|
|
else if (restart_edit == 'R')
|
|
msg_puts_attr(_(" (replace)"), attr);
|
|
else if (restart_edit == 'V')
|
|
msg_puts_attr(_(" (vreplace)"), attr);
|
|
#ifdef FEAT_RIGHTLEFT
|
|
if (p_hkmap)
|
|
msg_puts_attr(_(" Hebrew"), attr);
|
|
#endif
|
|
#ifdef FEAT_KEYMAP
|
|
if (State & MODE_LANGMAP)
|
|
{
|
|
# ifdef FEAT_ARABIC
|
|
if (curwin->w_p_arab)
|
|
msg_puts_attr(_(" Arabic"), attr);
|
|
else
|
|
# endif
|
|
if (get_keymap_str(curwin, (char_u *)" (%s)",
|
|
NameBuff, MAXPATHL))
|
|
msg_puts_attr((char *)NameBuff, attr);
|
|
}
|
|
#endif
|
|
if ((State & MODE_INSERT) && p_paste)
|
|
msg_puts_attr(_(" (paste)"), attr);
|
|
|
|
if (VIsual_active)
|
|
{
|
|
char *p;
|
|
|
|
// Don't concatenate separate words to avoid translation
|
|
// problems.
|
|
switch ((VIsual_select ? 4 : 0)
|
|
+ (VIsual_mode == Ctrl_V) * 2
|
|
+ (VIsual_mode == 'V'))
|
|
{
|
|
case 0: p = N_(" VISUAL"); break;
|
|
case 1: p = N_(" VISUAL LINE"); break;
|
|
case 2: p = N_(" VISUAL BLOCK"); break;
|
|
case 4: p = N_(" SELECT"); break;
|
|
case 5: p = N_(" SELECT LINE"); break;
|
|
default: p = N_(" SELECT BLOCK"); break;
|
|
}
|
|
msg_puts_attr(_(p), attr);
|
|
}
|
|
msg_puts_attr(" --", attr);
|
|
}
|
|
|
|
need_clear = TRUE;
|
|
}
|
|
if (reg_recording != 0
|
|
&& edit_submode == NULL) // otherwise it gets too long
|
|
{
|
|
recording_mode(attr);
|
|
need_clear = TRUE;
|
|
}
|
|
|
|
mode_displayed = TRUE;
|
|
if (need_clear || clear_cmdline || redraw_mode)
|
|
msg_clr_eos();
|
|
msg_didout = FALSE; // overwrite this message
|
|
length = msg_col;
|
|
msg_col = 0;
|
|
need_wait_return = nwr_save; // never ask for hit-return for this
|
|
}
|
|
else if (clear_cmdline && msg_silent == 0)
|
|
// Clear the whole command line. Will reset "clear_cmdline".
|
|
msg_clr_cmdline();
|
|
else if (redraw_mode)
|
|
{
|
|
msg_pos_mode();
|
|
msg_clr_eos();
|
|
}
|
|
|
|
// In Visual mode the size of the selected area must be redrawn.
|
|
if (VIsual_active)
|
|
clear_showcmd();
|
|
|
|
// If the last window has no status line, the ruler is after the mode
|
|
// message and must be redrawn
|
|
if (redrawing() && lastwin->w_status_height == 0)
|
|
win_redr_ruler(lastwin, TRUE, FALSE);
|
|
|
|
redraw_cmdline = FALSE;
|
|
redraw_mode = FALSE;
|
|
clear_cmdline = FALSE;
|
|
|
|
return length;
|
|
}
|
|
|
|
/*
|
|
* Position for a mode message.
|
|
*/
|
|
static void
|
|
msg_pos_mode(void)
|
|
{
|
|
msg_col = 0;
|
|
msg_row = Rows - 1;
|
|
}
|
|
|
|
/*
|
|
* Delete mode message. Used when ESC is typed which is expected to end
|
|
* Insert mode (but Insert mode didn't end yet!).
|
|
* Caller should check "mode_displayed".
|
|
*/
|
|
void
|
|
unshowmode(int force)
|
|
{
|
|
/*
|
|
* Don't delete it right now, when not redrawing or inside a mapping.
|
|
*/
|
|
if (!redrawing() || (!force && char_avail() && !KeyTyped))
|
|
redraw_cmdline = TRUE; // delete mode later
|
|
else
|
|
clearmode();
|
|
}
|
|
|
|
/*
|
|
* Clear the mode message.
|
|
*/
|
|
void
|
|
clearmode(void)
|
|
{
|
|
int save_msg_row = msg_row;
|
|
int save_msg_col = msg_col;
|
|
|
|
msg_pos_mode();
|
|
if (reg_recording != 0)
|
|
recording_mode(HL_ATTR(HLF_CM));
|
|
msg_clr_eos();
|
|
|
|
msg_col = save_msg_col;
|
|
msg_row = save_msg_row;
|
|
}
|
|
|
|
static void
|
|
recording_mode(int attr)
|
|
{
|
|
msg_puts_attr(_("recording"), attr);
|
|
if (shortmess(SHM_RECORDING))
|
|
return;
|
|
|
|
char s[4];
|
|
|
|
sprintf(s, " @%c", reg_recording);
|
|
msg_puts_attr(s, attr);
|
|
}
|
|
|
|
/*
|
|
* Draw the tab pages line at the top of the Vim window.
|
|
*/
|
|
void
|
|
draw_tabline(void)
|
|
{
|
|
int tabcount = 0;
|
|
tabpage_T *tp;
|
|
int tabwidth;
|
|
int col = 0;
|
|
int scol = 0;
|
|
int attr;
|
|
win_T *wp;
|
|
win_T *cwp;
|
|
int wincount;
|
|
int modified;
|
|
int c;
|
|
int len;
|
|
int attr_sel = HL_ATTR(HLF_TPS);
|
|
int attr_nosel = HL_ATTR(HLF_TP);
|
|
int attr_fill = HL_ATTR(HLF_TPF);
|
|
char_u *p;
|
|
int room;
|
|
int use_sep_chars = (t_colors < 8
|
|
#ifdef FEAT_GUI
|
|
&& !gui.in_use
|
|
#endif
|
|
#ifdef FEAT_TERMGUICOLORS
|
|
&& !p_tgc
|
|
#endif
|
|
);
|
|
|
|
if (ScreenLines == NULL)
|
|
return;
|
|
redraw_tabline = FALSE;
|
|
|
|
#ifdef FEAT_GUI_TABLINE
|
|
// Take care of a GUI tabline.
|
|
if (gui_use_tabline())
|
|
{
|
|
gui_update_tabline();
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (tabline_height() < 1)
|
|
return;
|
|
|
|
#if defined(FEAT_STL_OPT)
|
|
clear_TabPageIdxs();
|
|
|
|
// Use the 'tabline' option if it's set.
|
|
if (*p_tal != NUL)
|
|
win_redr_custom(NULL, FALSE);
|
|
else
|
|
#endif
|
|
{
|
|
FOR_ALL_TABPAGES(tp)
|
|
++tabcount;
|
|
|
|
tabwidth = (Columns - 1 + tabcount / 2) / tabcount;
|
|
if (tabwidth < 6)
|
|
tabwidth = 6;
|
|
|
|
attr = attr_nosel;
|
|
tabcount = 0;
|
|
for (tp = first_tabpage; tp != NULL && col < Columns - 4;
|
|
tp = tp->tp_next)
|
|
{
|
|
scol = col;
|
|
|
|
if (tp->tp_topframe == topframe)
|
|
attr = attr_sel;
|
|
if (use_sep_chars && col > 0)
|
|
screen_putchar('|', 0, col++, attr);
|
|
|
|
if (tp->tp_topframe != topframe)
|
|
attr = attr_nosel;
|
|
|
|
screen_putchar(' ', 0, col++, attr);
|
|
|
|
if (tp == curtab)
|
|
{
|
|
cwp = curwin;
|
|
wp = firstwin;
|
|
}
|
|
else
|
|
{
|
|
cwp = tp->tp_curwin;
|
|
wp = tp->tp_firstwin;
|
|
}
|
|
|
|
modified = FALSE;
|
|
for (wincount = 0; wp != NULL; wp = wp->w_next, ++wincount)
|
|
if (bufIsChanged(wp->w_buffer))
|
|
modified = TRUE;
|
|
if (modified || wincount > 1)
|
|
{
|
|
if (wincount > 1)
|
|
{
|
|
vim_snprintf((char *)NameBuff, MAXPATHL, "%d", wincount);
|
|
len = (int)STRLEN(NameBuff);
|
|
if (col + len >= Columns - 3)
|
|
break;
|
|
screen_puts_len(NameBuff, len, 0, col,
|
|
#if defined(FEAT_SYN_HL)
|
|
hl_combine_attr(attr, HL_ATTR(HLF_T))
|
|
#else
|
|
attr
|
|
#endif
|
|
);
|
|
col += len;
|
|
}
|
|
if (modified)
|
|
screen_puts_len((char_u *)"+", 1, 0, col++, attr);
|
|
screen_putchar(' ', 0, col++, attr);
|
|
}
|
|
|
|
room = scol - col + tabwidth - 1;
|
|
if (room > 0)
|
|
{
|
|
// Get buffer name in NameBuff[]
|
|
get_trans_bufname(cwp->w_buffer);
|
|
shorten_dir(NameBuff);
|
|
len = vim_strsize(NameBuff);
|
|
p = NameBuff;
|
|
if (has_mbyte)
|
|
while (len > room)
|
|
{
|
|
len -= ptr2cells(p);
|
|
MB_PTR_ADV(p);
|
|
}
|
|
else if (len > room)
|
|
{
|
|
p += len - room;
|
|
len = room;
|
|
}
|
|
if (len > Columns - col - 1)
|
|
len = Columns - col - 1;
|
|
|
|
screen_puts_len(p, (int)STRLEN(p), 0, col, attr);
|
|
col += len;
|
|
}
|
|
screen_putchar(' ', 0, col++, attr);
|
|
|
|
// Store the tab page number in TabPageIdxs[], so that
|
|
// jump_to_mouse() knows where each one is.
|
|
++tabcount;
|
|
while (scol < col)
|
|
TabPageIdxs[scol++] = tabcount;
|
|
}
|
|
|
|
if (use_sep_chars)
|
|
c = '_';
|
|
else
|
|
c = ' ';
|
|
screen_fill(0, 1, col, (int)Columns, c, c, attr_fill);
|
|
|
|
// Draw the 'showcmd' information if 'showcmdloc' == "tabline".
|
|
if (p_sc && *p_sloc == 't')
|
|
{
|
|
int width = MIN(10, (int)Columns - col - (tabcount > 1) * 3);
|
|
|
|
if (width > 0)
|
|
screen_puts_len(showcmd_buf, width, 0, (int)Columns
|
|
- width - (tabcount > 1) * 2, attr_nosel);
|
|
}
|
|
|
|
// Put an "X" for closing the current tab if there are several.
|
|
if (tabcount > 1)
|
|
{
|
|
screen_putchar('X', 0, (int)Columns - 1, attr_nosel);
|
|
TabPageIdxs[Columns - 1] = -999;
|
|
}
|
|
}
|
|
|
|
// Reset the flag here again, in case evaluating 'tabline' causes it to be
|
|
// set.
|
|
redraw_tabline = FALSE;
|
|
}
|
|
|
|
/*
|
|
* Get buffer name for "buf" into NameBuff[].
|
|
* Takes care of special buffer names and translates special characters.
|
|
*/
|
|
void
|
|
get_trans_bufname(buf_T *buf)
|
|
{
|
|
if (buf_spname(buf) != NULL)
|
|
vim_strncpy(NameBuff, buf_spname(buf), MAXPATHL - 1);
|
|
else
|
|
home_replace(buf, buf->b_fname, NameBuff, MAXPATHL, TRUE);
|
|
trans_characters(NameBuff, MAXPATHL);
|
|
}
|
|
|
|
/*
|
|
* Get the character to use in a status line. Get its attributes in "*attr".
|
|
*/
|
|
int
|
|
fillchar_status(int *attr, win_T *wp)
|
|
{
|
|
int fill;
|
|
|
|
#ifdef FEAT_TERMINAL
|
|
if (bt_terminal(wp->w_buffer))
|
|
{
|
|
if (wp == curwin)
|
|
{
|
|
*attr = HL_ATTR(HLF_ST);
|
|
fill = wp->w_fill_chars.stl;
|
|
}
|
|
else
|
|
{
|
|
*attr = HL_ATTR(HLF_STNC);
|
|
fill = wp->w_fill_chars.stlnc;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
if (wp == curwin)
|
|
{
|
|
*attr = HL_ATTR(HLF_S);
|
|
fill = wp->w_fill_chars.stl;
|
|
}
|
|
else
|
|
{
|
|
*attr = HL_ATTR(HLF_SNC);
|
|
fill = wp->w_fill_chars.stlnc;
|
|
}
|
|
return fill;
|
|
}
|
|
|
|
/*
|
|
* Get the character to use in a separator between vertically split windows.
|
|
* Get its attributes in "*attr".
|
|
*/
|
|
int
|
|
fillchar_vsep(int *attr, win_T *wp)
|
|
{
|
|
*attr = HL_ATTR(HLF_C);
|
|
if (*attr == 0 && wp->w_fill_chars.vert == ' ')
|
|
return '|';
|
|
else
|
|
return wp->w_fill_chars.vert;
|
|
}
|
|
|
|
/*
|
|
* Return TRUE if redrawing should currently be done.
|
|
*/
|
|
int
|
|
redrawing(void)
|
|
{
|
|
#ifdef FEAT_EVAL
|
|
if (disable_redraw_for_testing)
|
|
return 0;
|
|
else
|
|
#endif
|
|
return ((RedrawingDisabled == 0
|
|
#ifdef FEAT_EVAL
|
|
|| ignore_redraw_flag_for_testing
|
|
#endif
|
|
) && !(p_lz && char_avail() && !KeyTyped && !do_redraw));
|
|
}
|
|
|
|
/*
|
|
* Return TRUE if printing messages should currently be done.
|
|
*/
|
|
int
|
|
messaging(void)
|
|
{
|
|
return (!(p_lz && char_avail() && !KeyTyped));
|
|
}
|
|
|
|
/*
|
|
* Compute columns for ruler and shown command. 'sc_col' is also used to
|
|
* decide what the maximum length of a message on the status line can be.
|
|
* If there is a status line for the last window, 'sc_col' is independent
|
|
* of 'ru_col'.
|
|
*/
|
|
|
|
#define COL_RULER 17 // columns needed by standard ruler
|
|
|
|
void
|
|
comp_col(void)
|
|
{
|
|
int last_has_status = last_stl_height(FALSE) > 0;
|
|
|
|
sc_col = 0;
|
|
ru_col = 0;
|
|
if (p_ru)
|
|
{
|
|
#ifdef FEAT_STL_OPT
|
|
ru_col = (ru_wid ? ru_wid : COL_RULER) + 1;
|
|
#else
|
|
ru_col = COL_RULER + 1;
|
|
#endif
|
|
// no last status line, adjust sc_col
|
|
if (!last_has_status)
|
|
sc_col = ru_col;
|
|
}
|
|
if (p_sc && *p_sloc == 'l')
|
|
{
|
|
sc_col += SHOWCMD_COLS;
|
|
if (!p_ru || last_has_status) // no need for separating space
|
|
++sc_col;
|
|
}
|
|
sc_col = Columns - sc_col;
|
|
ru_col = Columns - ru_col;
|
|
if (sc_col <= 0) // screen too narrow, will become a mess
|
|
sc_col = 1;
|
|
if (ru_col <= 0)
|
|
ru_col = 1;
|
|
#ifdef FEAT_EVAL
|
|
set_vim_var_nr(VV_ECHOSPACE, sc_col - 1);
|
|
#endif
|
|
}
|
|
|
|
#if defined(FEAT_LINEBREAK) || defined(PROTO)
|
|
/*
|
|
* Return the width of the 'number' and 'relativenumber' column.
|
|
* Caller may need to check if 'number' or 'relativenumber' is set.
|
|
* Otherwise it depends on 'numberwidth' and the line count.
|
|
*/
|
|
int
|
|
number_width(win_T *wp)
|
|
{
|
|
int n;
|
|
linenr_T lnum;
|
|
|
|
if (wp->w_p_rnu && !wp->w_p_nu)
|
|
// cursor line shows "0"
|
|
lnum = wp->w_height;
|
|
else
|
|
// cursor line shows absolute line number
|
|
lnum = wp->w_buffer->b_ml.ml_line_count;
|
|
|
|
if (lnum == wp->w_nrwidth_line_count && wp->w_nuw_cached == wp->w_p_nuw)
|
|
return wp->w_nrwidth_width;
|
|
wp->w_nrwidth_line_count = lnum;
|
|
|
|
n = 0;
|
|
do
|
|
{
|
|
lnum /= 10;
|
|
++n;
|
|
} while (lnum > 0);
|
|
|
|
// 'numberwidth' gives the minimal width plus one
|
|
if (n < wp->w_p_nuw - 1)
|
|
n = wp->w_p_nuw - 1;
|
|
|
|
# ifdef FEAT_SIGNS
|
|
// If 'signcolumn' is set to 'number' and there is a sign to display, then
|
|
// the minimal width for the number column is 2.
|
|
if (n < 2 && get_first_valid_sign(wp) != NULL
|
|
&& (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u'))
|
|
n = 2;
|
|
# endif
|
|
|
|
wp->w_nrwidth_width = n;
|
|
wp->w_nuw_cached = wp->w_p_nuw;
|
|
return n;
|
|
}
|
|
#endif
|
|
|
|
#if defined(FEAT_EVAL) || defined(PROTO)
|
|
/*
|
|
* Return the current cursor column. This is the actual position on the
|
|
* screen. First column is 0.
|
|
*/
|
|
int
|
|
screen_screencol(void)
|
|
{
|
|
return screen_cur_col;
|
|
}
|
|
|
|
/*
|
|
* Return the current cursor row. This is the actual position on the screen.
|
|
* First row is 0.
|
|
*/
|
|
int
|
|
screen_screenrow(void)
|
|
{
|
|
return screen_cur_row;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Calls mb_ptr2char_adv(p) and returns the character.
|
|
* If "p" starts with "\x", "\u" or "\U" the hex or unicode value is used.
|
|
*/
|
|
static int
|
|
get_encoded_char_adv(char_u **p)
|
|
{
|
|
char_u *s = *p;
|
|
|
|
if (s[0] == '\\' && (s[1] == 'x' || s[1] == 'u' || s[1] == 'U'))
|
|
{
|
|
varnumber_T num = 0;
|
|
int bytes;
|
|
int n;
|
|
|
|
for (bytes = s[1] == 'x' ? 1 : s[1] == 'u' ? 2 : 4; bytes > 0; --bytes)
|
|
{
|
|
*p += 2;
|
|
n = hexhex2nr(*p);
|
|
if (n < 0)
|
|
return 0;
|
|
num = num * 256 + n;
|
|
}
|
|
*p += 2;
|
|
return num;
|
|
}
|
|
return mb_ptr2char_adv(p);
|
|
}
|
|
|
|
struct charstab
|
|
{
|
|
int *cp;
|
|
char *name;
|
|
};
|
|
static fill_chars_T fill_chars;
|
|
static struct charstab filltab[] =
|
|
{
|
|
{&fill_chars.stl, "stl"},
|
|
{&fill_chars.stlnc, "stlnc"},
|
|
{&fill_chars.vert, "vert"},
|
|
{&fill_chars.fold, "fold"},
|
|
{&fill_chars.foldopen, "foldopen"},
|
|
{&fill_chars.foldclosed, "foldclose"},
|
|
{&fill_chars.foldsep, "foldsep"},
|
|
{&fill_chars.diff, "diff"},
|
|
{&fill_chars.eob, "eob"},
|
|
{&fill_chars.lastline, "lastline"},
|
|
};
|
|
static lcs_chars_T lcs_chars;
|
|
static struct charstab lcstab[] =
|
|
{
|
|
{&lcs_chars.eol, "eol"},
|
|
{&lcs_chars.ext, "extends"},
|
|
{&lcs_chars.nbsp, "nbsp"},
|
|
{&lcs_chars.prec, "precedes"},
|
|
{&lcs_chars.space, "space"},
|
|
{&lcs_chars.tab2, "tab"},
|
|
{&lcs_chars.trail, "trail"},
|
|
{&lcs_chars.lead, "lead"},
|
|
#ifdef FEAT_CONCEAL
|
|
{&lcs_chars.conceal, "conceal"},
|
|
#else
|
|
{NULL, "conceal"},
|
|
#endif
|
|
{NULL, "multispace"},
|
|
{NULL, "leadmultispace"},
|
|
};
|
|
|
|
static char *
|
|
field_value_err(char *errbuf, size_t errbuflen, char *fmt, char *field)
|
|
{
|
|
if (errbuf == NULL)
|
|
return "";
|
|
vim_snprintf(errbuf, errbuflen, _(fmt), field);
|
|
return errbuf;
|
|
}
|
|
|
|
/*
|
|
* Handle setting 'listchars' or 'fillchars'.
|
|
* "value" points to either the global or the window-local value.
|
|
* "is_listchars" is TRUE for "listchars" and FALSE for "fillchars".
|
|
* When "apply" is FALSE do not store the flags, only check for errors.
|
|
* Assume monocell characters.
|
|
* Returns error message, NULL if it's OK.
|
|
*/
|
|
static char *
|
|
set_chars_option(win_T *wp, char_u *value, int is_listchars, int apply,
|
|
char *errbuf, size_t errbuflen)
|
|
{
|
|
int round, i, len, entries;
|
|
char_u *p, *s;
|
|
int c1 = 0, c2 = 0, c3 = 0;
|
|
char_u *last_multispace = NULL; // Last occurrence of "multispace:"
|
|
char_u *last_lmultispace = NULL; // Last occurrence of "leadmultispace:"
|
|
int multispace_len = 0; // Length of lcs-multispace string
|
|
int lead_multispace_len = 0; // Length of lcs-leadmultispace string
|
|
|
|
struct charstab *tab;
|
|
|
|
if (is_listchars)
|
|
{
|
|
tab = lcstab;
|
|
CLEAR_FIELD(lcs_chars);
|
|
entries = ARRAY_LENGTH(lcstab);
|
|
if (wp->w_p_lcs[0] == NUL)
|
|
value = p_lcs; // local value is empty, use the global value
|
|
}
|
|
else
|
|
{
|
|
tab = filltab;
|
|
entries = ARRAY_LENGTH(filltab);
|
|
if (wp->w_p_fcs[0] == NUL)
|
|
value = p_fcs; // local value is empty, us the global value
|
|
}
|
|
|
|
// first round: check for valid value, second round: assign values
|
|
for (round = 0; round <= (apply ? 1 : 0); ++round)
|
|
{
|
|
if (round > 0)
|
|
{
|
|
// After checking that the value is valid: set defaults.
|
|
if (is_listchars)
|
|
{
|
|
for (i = 0; i < entries; ++i)
|
|
if (tab[i].cp != NULL)
|
|
*(tab[i].cp) = NUL;
|
|
lcs_chars.tab1 = NUL;
|
|
lcs_chars.tab3 = NUL;
|
|
|
|
if (multispace_len > 0)
|
|
{
|
|
lcs_chars.multispace = ALLOC_MULT(int, multispace_len + 1);
|
|
if (lcs_chars.multispace != NULL)
|
|
lcs_chars.multispace[multispace_len] = NUL;
|
|
}
|
|
else
|
|
lcs_chars.multispace = NULL;
|
|
|
|
if (lead_multispace_len > 0)
|
|
{
|
|
lcs_chars.leadmultispace =
|
|
ALLOC_MULT(int, lead_multispace_len + 1);
|
|
lcs_chars.leadmultispace[lead_multispace_len] = NUL;
|
|
}
|
|
else
|
|
lcs_chars.leadmultispace = NULL;
|
|
}
|
|
else
|
|
{
|
|
fill_chars.stl = ' ';
|
|
fill_chars.stlnc = ' ';
|
|
fill_chars.vert = ' ';
|
|
fill_chars.fold = '-';
|
|
fill_chars.foldopen = '-';
|
|
fill_chars.foldclosed = '+';
|
|
fill_chars.foldsep = '|';
|
|
fill_chars.diff = '-';
|
|
fill_chars.eob = '~';
|
|
fill_chars.lastline = '@';
|
|
}
|
|
}
|
|
p = value;
|
|
while (*p)
|
|
{
|
|
for (i = 0; i < entries; ++i)
|
|
{
|
|
len = (int)STRLEN(tab[i].name);
|
|
if (!(STRNCMP(p, tab[i].name, len) == 0 && p[len] == ':'))
|
|
continue;
|
|
|
|
if (is_listchars && strcmp(tab[i].name, "multispace") == 0)
|
|
{
|
|
s = p + len + 1;
|
|
if (round == 0)
|
|
{
|
|
// Get length of lcs-multispace string in first round
|
|
last_multispace = p;
|
|
multispace_len = 0;
|
|
while (*s != NUL && *s != ',')
|
|
{
|
|
c1 = get_encoded_char_adv(&s);
|
|
if (char2cells(c1) > 1)
|
|
return field_value_err(errbuf, errbuflen,
|
|
e_wrong_character_width_for_field_str,
|
|
tab[i].name);
|
|
++multispace_len;
|
|
}
|
|
if (multispace_len == 0)
|
|
// lcs-multispace cannot be an empty string
|
|
return field_value_err(errbuf, errbuflen,
|
|
e_wrong_number_of_characters_for_field_str,
|
|
tab[i].name);
|
|
p = s;
|
|
}
|
|
else
|
|
{
|
|
int multispace_pos = 0;
|
|
|
|
while (*s != NUL && *s != ',')
|
|
{
|
|
c1 = get_encoded_char_adv(&s);
|
|
if (p == last_multispace)
|
|
lcs_chars.multispace[multispace_pos++] = c1;
|
|
}
|
|
p = s;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (is_listchars && strcmp(tab[i].name, "leadmultispace") == 0)
|
|
{
|
|
s = p + len + 1;
|
|
if (round == 0)
|
|
{
|
|
// get length of lcs-leadmultispace string in first
|
|
// round
|
|
last_lmultispace = p;
|
|
lead_multispace_len = 0;
|
|
while (*s != NUL && *s != ',')
|
|
{
|
|
c1 = get_encoded_char_adv(&s);
|
|
if (char2cells(c1) > 1)
|
|
return field_value_err(errbuf, errbuflen,
|
|
e_wrong_character_width_for_field_str,
|
|
tab[i].name);
|
|
++lead_multispace_len;
|
|
}
|
|
if (lead_multispace_len == 0)
|
|
// lcs-leadmultispace cannot be an empty string
|
|
return field_value_err(errbuf, errbuflen,
|
|
e_wrong_number_of_characters_for_field_str,
|
|
tab[i].name);
|
|
p = s;
|
|
}
|
|
else
|
|
{
|
|
int multispace_pos = 0;
|
|
|
|
while (*s != NUL && *s != ',')
|
|
{
|
|
c1 = get_encoded_char_adv(&s);
|
|
if (p == last_lmultispace)
|
|
lcs_chars.leadmultispace[multispace_pos++] = c1;
|
|
}
|
|
p = s;
|
|
}
|
|
break;
|
|
}
|
|
|
|
c2 = c3 = 0;
|
|
s = p + len + 1;
|
|
if (*s == NUL)
|
|
return field_value_err(errbuf, errbuflen,
|
|
e_wrong_number_of_characters_for_field_str,
|
|
tab[i].name);
|
|
c1 = get_encoded_char_adv(&s);
|
|
if (char2cells(c1) > 1)
|
|
return field_value_err(errbuf, errbuflen,
|
|
e_wrong_character_width_for_field_str,
|
|
tab[i].name);
|
|
if (tab[i].cp == &lcs_chars.tab2)
|
|
{
|
|
if (*s == NUL)
|
|
return field_value_err(errbuf, errbuflen,
|
|
e_wrong_number_of_characters_for_field_str,
|
|
tab[i].name);
|
|
c2 = get_encoded_char_adv(&s);
|
|
if (char2cells(c2) > 1)
|
|
return field_value_err(errbuf, errbuflen,
|
|
e_wrong_character_width_for_field_str,
|
|
tab[i].name);
|
|
if (!(*s == ',' || *s == NUL))
|
|
{
|
|
c3 = get_encoded_char_adv(&s);
|
|
if (char2cells(c3) > 1)
|
|
return field_value_err(errbuf, errbuflen,
|
|
e_wrong_character_width_for_field_str,
|
|
tab[i].name);
|
|
}
|
|
}
|
|
|
|
if (*s == ',' || *s == NUL)
|
|
{
|
|
if (round > 0)
|
|
{
|
|
if (tab[i].cp == &lcs_chars.tab2)
|
|
{
|
|
lcs_chars.tab1 = c1;
|
|
lcs_chars.tab2 = c2;
|
|
lcs_chars.tab3 = c3;
|
|
}
|
|
else if (tab[i].cp != NULL)
|
|
*(tab[i].cp) = c1;
|
|
|
|
}
|
|
p = s;
|
|
break;
|
|
}
|
|
else
|
|
return field_value_err(errbuf, errbuflen,
|
|
e_wrong_number_of_characters_for_field_str,
|
|
tab[i].name);
|
|
}
|
|
|
|
if (i == entries)
|
|
return e_invalid_argument;
|
|
|
|
if (*p == ',')
|
|
++p;
|
|
}
|
|
}
|
|
|
|
if (apply)
|
|
{
|
|
if (is_listchars)
|
|
{
|
|
vim_free(wp->w_lcs_chars.multispace);
|
|
vim_free(wp->w_lcs_chars.leadmultispace);
|
|
wp->w_lcs_chars = lcs_chars;
|
|
}
|
|
else
|
|
{
|
|
wp->w_fill_chars = fill_chars;
|
|
}
|
|
}
|
|
|
|
return NULL; // no error
|
|
}
|
|
|
|
/*
|
|
* Handle the new value of 'fillchars'.
|
|
*/
|
|
char *
|
|
set_fillchars_option(win_T *wp, char_u *val, int apply, char *errbuf,
|
|
size_t errbuflen)
|
|
{
|
|
return set_chars_option(wp, val, FALSE, apply, errbuf, errbuflen);
|
|
}
|
|
|
|
/*
|
|
* Handle the new value of 'listchars'.
|
|
*/
|
|
char *
|
|
set_listchars_option(win_T *wp, char_u *val, int apply, char *errbuf,
|
|
size_t errbuflen)
|
|
{
|
|
return set_chars_option(wp, val, TRUE, apply, errbuf, errbuflen);
|
|
}
|
|
|
|
/*
|
|
* Function given to ExpandGeneric() to obtain possible arguments of the
|
|
* 'fillchars' option.
|
|
*/
|
|
char_u *
|
|
get_fillchars_name(expand_T *xp UNUSED, int idx)
|
|
{
|
|
if (idx >= (int)(sizeof(filltab) / sizeof(filltab[0])))
|
|
return NULL;
|
|
|
|
return (char_u*)filltab[idx].name;
|
|
}
|
|
|
|
/*
|
|
* Function given to ExpandGeneric() to obtain possible arguments of the
|
|
* 'listchars' option.
|
|
*/
|
|
char_u *
|
|
get_listchars_name(expand_T *xp UNUSED, int idx)
|
|
{
|
|
if (idx >= (int)(sizeof(lcstab) / sizeof(lcstab[0])))
|
|
return NULL;
|
|
|
|
return (char_u*)lcstab[idx].name;
|
|
}
|
|
|
|
/*
|
|
* Check all global and local values of 'listchars' and 'fillchars'.
|
|
* Return an untranslated error messages if any of them is invalid, NULL
|
|
* otherwise.
|
|
*/
|
|
char *
|
|
check_chars_options(void)
|
|
{
|
|
tabpage_T *tp;
|
|
win_T *wp;
|
|
|
|
if (set_listchars_option(curwin, p_lcs, FALSE, NULL, 0) != NULL)
|
|
return e_conflicts_with_value_of_listchars;
|
|
if (set_fillchars_option(curwin, p_fcs, FALSE, NULL, 0) != NULL)
|
|
return e_conflicts_with_value_of_fillchars;
|
|
FOR_ALL_TAB_WINDOWS(tp, wp)
|
|
{
|
|
if (set_listchars_option(wp, wp->w_p_lcs, FALSE, NULL, 0) != NULL)
|
|
return e_conflicts_with_value_of_listchars;
|
|
if (set_fillchars_option(wp, wp->w_p_fcs, FALSE, NULL, 0) != NULL)
|
|
return e_conflicts_with_value_of_fillchars;
|
|
}
|
|
return NULL;
|
|
}
|