aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/screen.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/screen.c')
-rw-r--r--src/nvim/screen.c460
1 files changed, 17 insertions, 443 deletions
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 2419a42a2a..c9a8faa8c6 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -16,6 +16,7 @@
#include "nvim/buffer.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/ex_getln.h"
#include "nvim/extmark.h"
@@ -34,6 +35,7 @@
#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/state.h"
+#include "nvim/statusline.h"
#include "nvim/ui_compositor.h"
#include "nvim/undo.h"
#include "nvim/window.h"
@@ -249,7 +251,7 @@ void rl_mirror(char_u *str)
}
/// Get the length of an item as it will be shown in the status line.
-static int status_match_len(expand_T *xp, char_u *s)
+static int wildmenu_match_len(expand_T *xp, char_u *s)
{
int len = 0;
@@ -262,7 +264,7 @@ static int status_match_len(expand_T *xp, char_u *s)
}
while (*s != NUL) {
- s += skip_status_match_char(xp, s);
+ s += skip_wildmenu_char(xp, s);
len += ptr2cells((char *)s);
MB_PTR_ADV(s);
}
@@ -270,29 +272,9 @@ static int status_match_len(expand_T *xp, char_u *s)
return len;
}
-/// Redraw all status lines at the bottom of frame "frp".
-void win_redraw_last_status(const frame_T *frp)
- FUNC_ATTR_NONNULL_ARG(1)
-{
- if (frp->fr_layout == FR_LEAF) {
- frp->fr_win->w_redr_status = true;
- } else if (frp->fr_layout == FR_ROW) {
- FOR_ALL_FRAMES(frp, frp->fr_child) {
- win_redraw_last_status(frp);
- }
- } else {
- assert(frp->fr_layout == FR_COL);
- frp = frp->fr_child;
- while (frp->fr_next != NULL) {
- frp = frp->fr_next;
- }
- win_redraw_last_status(frp);
- }
-}
-
-/// Return the number of characters that should be skipped in a status match.
+/// Return the number of characters that should be skipped in the wildmenu
/// These are backslashes used for escaping. Do show backslashes in help tags.
-static int skip_status_match_char(expand_T *xp, char_u *s)
+static int skip_wildmenu_char(expand_T *xp, char_u *s)
{
if ((rem_backslash(s) && xp->xp_context != EXPAND_HELP)
|| ((xp->xp_context == EXPAND_MENUS
@@ -300,6 +282,10 @@ static int skip_status_match_char(expand_T *xp, char_u *s)
&& (s[0] == '\t'
|| (s[0] == '\\' && s[1] != NUL)))) {
#ifndef BACKSLASH_IN_FILENAME
+ // TODO(bfredl): Why in the actual fuck are we special casing the
+ // shell variety deep in the redraw logic? Shell special snowflakiness
+ // should already be eliminated multiple layers before reaching the
+ // screen infracstructure.
if (xp->xp_shell && csh_like_shell() && s[1] == '\\' && s[2] == '!') {
return 2;
}
@@ -316,7 +302,7 @@ static int skip_status_match_char(expand_T *xp, char_u *s)
/// If inversion is possible we use it. Else '=' characters are used.
///
/// @param matches list of matches
-void win_redr_status_matches(expand_T *xp, int num_matches, char **matches, int match, int showtail)
+void redraw_wildmenu(expand_T *xp, int num_matches, char **matches, int match, int showtail)
{
#define L_MATCH(m) (showtail ? sm_gettail(matches[m], false) : matches[m])
int row;
@@ -347,7 +333,7 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char **matches, int
highlight = false;
}
// count 1 for the ending ">"
- clen = status_match_len(xp, (char_u *)L_MATCH(match)) + 3;
+ clen = wildmenu_match_len(xp, (char_u *)L_MATCH(match)) + 3;
if (match == 0) {
first_match = 0;
} else if (match < first_match) {
@@ -357,7 +343,7 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char **matches, int
} else {
// check if match fits on the screen
for (i = first_match; i < match; i++) {
- clen += status_match_len(xp, (char_u *)L_MATCH(i)) + 2;
+ clen += wildmenu_match_len(xp, (char_u *)L_MATCH(i)) + 2;
}
if (first_match > 0) {
clen += 2;
@@ -368,7 +354,7 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char **matches, int
// if showing the last match, we can add some on the left
clen = 2;
for (i = match; i < num_matches; i++) {
- clen += status_match_len(xp, (char_u *)L_MATCH(i)) + 2;
+ clen += wildmenu_match_len(xp, (char_u *)L_MATCH(i)) + 2;
if ((long)clen >= Columns) {
break;
}
@@ -380,7 +366,7 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char **matches, int
}
if (add_left) {
while (first_match > 0) {
- clen += status_match_len(xp, (char_u *)L_MATCH(first_match - 1)) + 2;
+ clen += wildmenu_match_len(xp, (char_u *)L_MATCH(first_match - 1)) + 2;
if ((long)clen >= Columns) {
break;
}
@@ -400,7 +386,7 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char **matches, int
clen = len;
i = first_match;
- while (clen + status_match_len(xp, (char_u *)L_MATCH(i)) + 2 < Columns) {
+ while (clen + wildmenu_match_len(xp, (char_u *)L_MATCH(i)) + 2 < Columns) {
if (i == match) {
selstart = buf + len;
selstart_col = clen;
@@ -417,7 +403,7 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char **matches, int
clen += l;
} else {
for (; *s != NUL; s++) {
- s += skip_status_match_char(xp, s);
+ s += skip_wildmenu_char(xp, s);
clen += ptr2cells((char *)s);
if ((l = utfc_ptr2len((char *)s)) > 1) {
STRNCPY(buf + len, s, l); // NOLINT(runtime/printf)
@@ -561,223 +547,6 @@ bool get_keymap_str(win_T *wp, char *fmt, char *buf, int len)
return buf[0] != NUL;
}
-/// Redraw the status line, window bar or ruler of window "wp".
-/// When "wp" is NULL redraw the tab pages line from 'tabline'.
-void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)
-{
- static bool entered = false;
- int attr;
- int curattr;
- int row;
- int col = 0;
- int maxwidth;
- int width;
- int n;
- int len;
- int fillchar;
- char buf[MAXPATHL];
- char_u *stl;
- char *p;
- stl_hlrec_t *hltab;
- StlClickRecord *tabtab;
- int use_sandbox = false;
- win_T *ewp;
- int p_crb_save;
- bool is_stl_global = global_stl_height() > 0;
-
- ScreenGrid *grid = &default_grid;
-
- // 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;
- use_sandbox = was_set_insecurely(wp, "tabline", 0);
- } else if (draw_winbar) {
- stl = (char_u *)((*wp->w_p_wbr != NUL) ? wp->w_p_wbr : p_wbr);
- row = -1; // row zero is first row of text
- col = 0;
- grid = &wp->w_grid;
- grid_adjust(&grid, &row, &col);
-
- if (row < 0) {
- return;
- }
-
- fillchar = wp->w_p_fcs_chars.wbr;
- attr = (wp == curwin) ? win_hl_attr(wp, HLF_WBR) : win_hl_attr(wp, HLF_WBRNC);
- maxwidth = wp->w_width_inner;
- use_sandbox = was_set_insecurely(wp, "winbar", 0);
-
- stl_clear_click_defs(wp->w_winbar_click_defs, (long)wp->w_winbar_click_defs_size);
- // Allocate / resize the click definitions array for winbar if needed.
- if (wp->w_winbar_height && wp->w_winbar_click_defs_size < (size_t)maxwidth) {
- xfree(wp->w_winbar_click_defs);
- wp->w_winbar_click_defs_size = (size_t)maxwidth;
- wp->w_winbar_click_defs = xcalloc(wp->w_winbar_click_defs_size, sizeof(StlClickRecord));
- }
- } else {
- row = is_stl_global ? (Rows - (int)p_ch - 1) : W_ENDROW(wp);
- fillchar = fillchar_status(&attr, wp);
- maxwidth = is_stl_global ? Columns : wp->w_width;
-
- stl_clear_click_defs(wp->w_status_click_defs, (long)wp->w_status_click_defs_size);
- // Allocate / resize the click definitions array for statusline if needed.
- if (wp->w_status_click_defs_size < (size_t)maxwidth) {
- xfree(wp->w_status_click_defs);
- wp->w_status_click_defs_size = (size_t)maxwidth;
- wp->w_status_click_defs = xcalloc(wp->w_status_click_defs_size, sizeof(StlClickRecord));
- }
-
- if (draw_ruler) {
- stl = p_ruf;
- // advance past any leading group spec - implicit in ru_col
- if (*stl == '%') {
- if (*++stl == '-') {
- stl++;
- }
- if (atoi((char *)stl)) {
- while (ascii_isdigit(*stl)) {
- stl++;
- }
- }
- if (*stl++ != '(') {
- stl = p_ruf;
- }
- }
- col = ru_col - (Columns - maxwidth);
- if (col < (maxwidth + 1) / 2) {
- col = (maxwidth + 1) / 2;
- }
- maxwidth = maxwidth - col;
- if (!wp->w_status_height && !is_stl_global) {
- grid = &msg_grid_adj;
- row = Rows - 1;
- maxwidth--; // writing in last column may cause scrolling
- fillchar = ' ';
- attr = HL_ATTR(HLF_MSG);
- }
-
- use_sandbox = was_set_insecurely(wp, "rulerformat", 0);
- } else {
- if (*wp->w_p_stl != NUL) {
- stl = wp->w_p_stl;
- } else {
- stl = p_stl;
- }
- use_sandbox = was_set_insecurely(wp, "statusline", *wp->w_p_stl == NUL ? 0 : OPT_LOCAL);
- }
-
- col += is_stl_global ? 0 : 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), (char *)stl, use_sandbox,
- fillchar, maxwidth, &hltab, &tabtab);
- xfree(stl);
- ewp->w_p_crb = p_crb_save;
-
- // Make all characters printable.
- p = transstr(buf, true);
- len = (int)STRLCPY(buf, p, sizeof(buf));
- len = (size_t)len < sizeof(buf) ? len : (int)sizeof(buf) - 1;
- xfree(p);
-
- // fill up with "fillchar"
- while (width < maxwidth && len < (int)sizeof(buf) - 1) {
- len += utf_char2bytes(fillchar, buf + len);
- width++;
- }
- buf[len] = NUL;
-
- // Draw each snippet with the specified highlighting.
- grid_puts_line_start(grid, row);
-
- curattr = attr;
- p = buf;
- for (n = 0; hltab[n].start != NULL; n++) {
- int textlen = (int)(hltab[n].start - p);
- grid_puts_len(grid, (char_u *)p, textlen, row, col, curattr);
- col += vim_strnsize((char_u *)p, textlen);
- p = hltab[n].start;
-
- if (hltab[n].userhl == 0) {
- curattr = attr;
- } else if (hltab[n].userhl < 0) {
- curattr = syn_id2attr(-hltab[n].userhl);
- } 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];
- }
- }
- // Make sure to use an empty string instead of p, if p is beyond buf + len.
- grid_puts(grid, p >= buf + len ? (char_u *)"" : (char_u *)p, row, col,
- curattr);
-
- grid_puts_line_flush(false);
-
- // Fill the tab_page_click_defs, w_status_click_defs or w_winbar_click_defs array for clicking
- // in the tab page line, status line or window bar
- StlClickDefinition *click_defs = (wp == NULL) ? tab_page_click_defs
- : draw_winbar ? wp->w_winbar_click_defs
- : wp->w_status_click_defs;
-
- if (click_defs == NULL) {
- goto theend;
- }
-
- col = 0;
- len = 0;
- p = buf;
- StlClickDefinition cur_click_def = {
- .type = kStlClickDisabled,
- };
- for (n = 0; tabtab[n].start != NULL; n++) {
- len += vim_strnsize((char_u *)p, (int)(tabtab[n].start - p));
- while (col < len) {
- click_defs[col++] = cur_click_def;
- }
- p = (char *)tabtab[n].start;
- cur_click_def = tabtab[n].def;
- if ((wp != NULL) && !(cur_click_def.type == kStlClickDisabled
- || cur_click_def.type == kStlClickFuncRun)) {
- // window bar and status line only support click functions
- cur_click_def.type = kStlClickDisabled;
- }
- }
- while (col < maxwidth) {
- click_defs[col++] = cur_click_def;
- }
-
-theend:
- entered = false;
-}
-
/// Prepare for 'hlsearch' highlighting.
void start_search_hl(void)
{
@@ -1378,32 +1147,6 @@ void get_trans_bufname(buf_T *buf)
trans_characters((char *)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;
- bool is_curwin = (wp == curwin);
- if (is_curwin) {
- *attr = win_hl_attr(wp, HLF_S);
- fill = wp->w_p_fcs_chars.stl;
- } else {
- *attr = win_hl_attr(wp, HLF_SNC);
- fill = wp->w_p_fcs_chars.stlnc;
- }
- // Use fill when there is highlighting, and highlighting of current
- // window differs, or the fillchars differ, or this is not the
- // current window
- if (*attr != 0 && ((win_hl_attr(wp, HLF_S) != win_hl_attr(wp, HLF_SNC)
- || !is_curwin || ONE_WINDOW)
- || (wp->w_p_fcs_chars.stl != wp->w_p_fcs_chars.stlnc))) {
- return fill;
- }
- if (is_curwin) {
- return '^';
- }
- return '=';
-}
-
/// Get the character to use in a separator between vertically split windows.
/// Get its attributes in "*attr".
int fillchar_vsep(win_T *wp, int *attr)
@@ -1433,175 +1176,6 @@ bool messaging(void)
return !(p_lz && char_avail() && !KeyTyped) && ui_has_messages();
}
-void win_redr_ruler(win_T *wp, bool always)
-{
- bool is_stl_global = global_stl_height() > 0;
- static bool did_show_ext_ruler = false;
-
- // If 'ruler' off, don't do anything
- if (!p_ru) {
- return;
- }
-
- // Check if cursor.lnum is valid, since win_redr_ruler() may be called
- // after deleting lines, before cursor.lnum is corrected.
- if (wp->w_cursor.lnum > wp->w_buffer->b_ml.ml_line_count) {
- return;
- }
-
- // Don't draw the ruler while doing insert-completion, it might overwrite
- // the (long) mode message.
- if (wp == lastwin && lastwin->w_status_height == 0 && !is_stl_global) {
- if (edit_submode != NULL) {
- return;
- }
- }
-
- if (*p_ruf && p_ch > 0 && !ui_has(kUIMessages)) {
- const int called_emsg_before = called_emsg;
- win_redr_custom(wp, false, true);
- if (called_emsg > called_emsg_before) {
- set_string_option_direct("rulerformat", -1, "", OPT_FREE, SID_ERROR);
- }
- return;
- }
-
- // Check if not in Insert mode and the line is empty (will show "0-1").
- int empty_line = false;
- if ((State & MODE_INSERT) == 0 && *ml_get_buf(wp->w_buffer, wp->w_cursor.lnum, false) == NUL) {
- empty_line = true;
- }
-
- // Only draw the ruler when something changed.
- validate_virtcol_win(wp);
- if (redraw_cmdline
- || always
- || wp->w_cursor.lnum != wp->w_ru_cursor.lnum
- || wp->w_cursor.col != wp->w_ru_cursor.col
- || wp->w_virtcol != wp->w_ru_virtcol
- || wp->w_cursor.coladd != wp->w_ru_cursor.coladd
- || wp->w_topline != wp->w_ru_topline
- || wp->w_buffer->b_ml.ml_line_count != wp->w_ru_line_count
- || wp->w_topfill != wp->w_ru_topfill
- || empty_line != wp->w_ru_empty) {
- int width;
- int row;
- int fillchar;
- int attr;
- int off;
- bool part_of_status = false;
-
- if (wp->w_status_height) {
- row = W_ENDROW(wp);
- fillchar = fillchar_status(&attr, wp);
- off = wp->w_wincol;
- width = wp->w_width;
- part_of_status = true;
- } else if (is_stl_global) {
- row = Rows - (int)p_ch - 1;
- fillchar = fillchar_status(&attr, wp);
- off = 0;
- width = Columns;
- part_of_status = true;
- } else {
- row = Rows - 1;
- fillchar = ' ';
- attr = HL_ATTR(HLF_MSG);
- width = Columns;
- off = 0;
- }
-
- if (!part_of_status && !ui_has_messages()) {
- return;
- }
-
- // In list mode virtcol needs to be recomputed
- colnr_T virtcol = wp->w_virtcol;
- if (wp->w_p_list && wp->w_p_lcs_chars.tab1 == NUL) {
- wp->w_p_list = false;
- getvvcol(wp, &wp->w_cursor, NULL, &virtcol, NULL);
- wp->w_p_list = true;
- }
-
-#define RULER_BUF_LEN 70
- char buffer[RULER_BUF_LEN];
-
- // Some sprintfs return the length, some return a pointer.
- // To avoid portability problems we use strlen() here.
- vim_snprintf(buffer, RULER_BUF_LEN, "%" PRId64 ",",
- (wp->w_buffer->b_ml.ml_flags &
- ML_EMPTY) ? (int64_t)0L : (int64_t)wp->w_cursor.lnum);
- size_t len = STRLEN(buffer);
- col_print(buffer + len, RULER_BUF_LEN - len,
- empty_line ? 0 : (int)wp->w_cursor.col + 1,
- (int)virtcol + 1);
-
- // Add a "50%" if there is room for it.
- // On the last line, don't print in the last column (scrolls the
- // screen up on some terminals).
- int i = (int)STRLEN(buffer);
- get_rel_pos(wp, buffer + i + 1, RULER_BUF_LEN - i - 1);
- int o = i + vim_strsize(buffer + i + 1);
- if (wp->w_status_height == 0 && !is_stl_global) { // can't use last char of screen
- o++;
- }
- int this_ru_col = ru_col - (Columns - width);
- if (this_ru_col < 0) {
- this_ru_col = 0;
- }
- // Never use more than half the window/screen width, leave the other half
- // for the filename.
- if (this_ru_col < (width + 1) / 2) {
- this_ru_col = (width + 1) / 2;
- }
- if (this_ru_col + o < width) {
- // Need at least 3 chars left for get_rel_pos() + NUL.
- while (this_ru_col + o < width && RULER_BUF_LEN > i + 4) {
- i += utf_char2bytes(fillchar, buffer + i);
- o++;
- }
- get_rel_pos(wp, buffer + i, RULER_BUF_LEN - i);
- }
-
- if (ui_has(kUIMessages) && !part_of_status) {
- MAXSIZE_TEMP_ARRAY(content, 1);
- MAXSIZE_TEMP_ARRAY(chunk, 2);
- ADD_C(chunk, INTEGER_OBJ(attr));
- ADD_C(chunk, STRING_OBJ(cstr_as_string((char *)buffer)));
- ADD_C(content, ARRAY_OBJ(chunk));
- ui_call_msg_ruler(content);
- did_show_ext_ruler = true;
- } else {
- if (did_show_ext_ruler) {
- ui_call_msg_ruler((Array)ARRAY_DICT_INIT);
- did_show_ext_ruler = false;
- }
- // Truncate at window boundary.
- o = 0;
- for (i = 0; buffer[i] != NUL; i += utfc_ptr2len(buffer + i)) {
- o += utf_ptr2cells(buffer + i);
- if (this_ru_col + o > width) {
- buffer[i] = NUL;
- break;
- }
- }
-
- ScreenGrid *grid = part_of_status ? &default_grid : &msg_grid_adj;
- grid_puts(grid, (char_u *)buffer, row, this_ru_col + off, attr);
- grid_fill(grid, row, row + 1,
- this_ru_col + off + (int)STRLEN(buffer), off + width, fillchar,
- fillchar, attr);
- }
-
- wp->w_ru_cursor = wp->w_cursor;
- wp->w_ru_virtcol = wp->w_virtcol;
- wp->w_ru_empty = (char)empty_line;
- wp->w_ru_topline = wp->w_topline;
- wp->w_ru_line_count = wp->w_buffer->b_ml.ml_line_count;
- wp->w_ru_topfill = wp->w_topfill;
- }
-}
-
#define COL_RULER 17 // columns needed by standard ruler
/// Compute columns for ruler and shown command. 'sc_col' is also used to