aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/drawscreen.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/drawscreen.c')
-rw-r--r--src/nvim/drawscreen.c301
1 files changed, 168 insertions, 133 deletions
diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c
index 15a7294496..04c342e068 100644
--- a/src/nvim/drawscreen.c
+++ b/src/nvim/drawscreen.c
@@ -59,28 +59,47 @@
#include <stdbool.h>
#include <string.h>
+#include "klib/kvec.h"
+#include "nvim/api/private/defs.h"
+#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
#include "nvim/cmdexpand.h"
+#include "nvim/decoration.h"
+#include "nvim/decoration_provider.h"
#include "nvim/diff.h"
+#include "nvim/drawline.h"
#include "nvim/drawscreen.h"
#include "nvim/ex_getln.h"
+#include "nvim/extmark_defs.h"
+#include "nvim/fold.h"
+#include "nvim/globals.h"
#include "nvim/grid.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
#include "nvim/insexpand.h"
#include "nvim/match.h"
+#include "nvim/mbyte.h"
+#include "nvim/memline.h"
+#include "nvim/message.h"
#include "nvim/move.h"
+#include "nvim/normal.h"
#include "nvim/option.h"
#include "nvim/plines.h"
#include "nvim/popupmenu.h"
+#include "nvim/pos.h"
#include "nvim/profile.h"
#include "nvim/regexp.h"
+#include "nvim/screen.h"
#include "nvim/statusline.h"
#include "nvim/syntax.h"
+#include "nvim/terminal.h"
+#include "nvim/types.h"
+#include "nvim/ui.h"
#include "nvim/ui_compositor.h"
-#include "nvim/undo.h"
#include "nvim/version.h"
+#include "nvim/vim.h"
#include "nvim/window.h"
/// corner value flags for hsep_connected and vsep_connected
@@ -107,12 +126,14 @@ static char *provider_err = NULL;
void conceal_check_cursor_line(void)
{
bool should_conceal = conceal_cursor_line(curwin);
- if (curwin->w_p_cole > 0 && (conceal_cursor_used != should_conceal)) {
- redrawWinline(curwin, curwin->w_cursor.lnum);
- // Need to recompute cursor column, e.g., when starting Visual mode
- // without concealing.
- curs_columns(curwin, true);
+ if (curwin->w_p_cole <= 0 || conceal_cursor_used == should_conceal) {
+ return;
}
+
+ redrawWinline(curwin, curwin->w_cursor.lnum);
+ // Need to recompute cursor column, e.g., when starting Visual mode
+ // without concealing.
+ curs_columns(curwin, true);
}
/// Resize default_grid to Rows and Columns.
@@ -125,6 +146,8 @@ void conceal_check_cursor_line(void)
/// default_grid.Columns to access items in default_grid.chars[]. Use Rows
/// and Columns for positioning text etc. where the final size of the screen is
/// needed.
+///
+/// @return whether resizing has been done
bool default_grid_alloc(void)
{
static bool resizing = false;
@@ -161,14 +184,10 @@ bool default_grid_alloc(void)
// size is wrong.
grid_alloc(&default_grid, Rows, Columns, true, true);
- StlClickDefinition *new_tab_page_click_defs =
- xcalloc((size_t)Columns, sizeof(*new_tab_page_click_defs));
stl_clear_click_defs(tab_page_click_defs, tab_page_click_defs_size);
- xfree(tab_page_click_defs);
-
- tab_page_click_defs = new_tab_page_click_defs;
- tab_page_click_defs_size = Columns;
+ tab_page_click_defs = stl_alloc_click_defs(tab_page_click_defs, Columns,
+ &tab_page_click_defs_size);
default_grid.comp_height = Rows;
default_grid.comp_width = Columns;
@@ -185,14 +204,12 @@ void screenclear(void)
{
check_for_delay(false);
- int i;
-
if (starting == NO_SCREEN || default_grid.chars == NULL) {
return;
}
// blank out the default grid
- for (i = 0; i < default_grid.rows; i++) {
+ for (int i = 0; i < default_grid.rows; i++) {
grid_clear_line(&default_grid, default_grid.line_offset[i],
default_grid.cols, true);
default_grid.line_wraps[i] = false;
@@ -266,17 +283,28 @@ void screen_resize(int width, int height)
p_lines = Rows;
p_columns = Columns;
- // was invoked recursively from a VimResized autocmd, handled as a loop below
- if (resizing_autocmd) {
- return;
- }
+ ui_call_grid_resize(1, width, height);
int retry_count = 0;
resizing_autocmd = true;
- bool retry_resize = true;
- while (retry_resize) {
- retry_resize = default_grid_alloc();
+ // In rare cases, autocommands may have altered Rows or Columns,
+ // so retry to check if we need to allocate the screen again.
+ while (default_grid_alloc()) {
+ // win_new_screensize will recompute floats position, but tell the
+ // compositor to not redraw them yet
+ ui_comp_set_screen_valid(false);
+ if (msg_grid.chars) {
+ msg_grid_invalid = true;
+ }
+
+ RedrawingDisabled++;
+
+ win_new_screensize(); // fit the windows in the new sized screen
+
+ comp_col(); // recompute columns for shown command and ruler
+
+ RedrawingDisabled--;
// Do not apply autocommands more than 3 times to avoid an endless loop
// in case applying autocommands always changes Rows or Columns.
@@ -284,33 +312,10 @@ void screen_resize(int width, int height)
break;
}
- if (retry_resize) {
- // In rare cases, autocommands may have altered Rows or Columns,
- // retry to check if we need to allocate the screen again.
- apply_autocmds(EVENT_VIMRESIZED, NULL, NULL, false, curbuf);
- }
+ apply_autocmds(EVENT_VIMRESIZED, NULL, NULL, false, curbuf);
}
resizing_autocmd = false;
-
- ui_call_grid_resize(1, width, height);
-
- // win_new_screensize will recompute floats position, but tell the
- // compositor to not redraw them yet
- ui_comp_set_screen_valid(false);
- if (msg_grid.chars) {
- msg_grid_invalid = true;
- }
-
- // Note that the window sizes are updated before reallocating the arrays,
- // thus we must not redraw here!
- RedrawingDisabled++;
-
- win_new_screensize(); // fit the windows in the new sized screen
-
- comp_col(); // recompute columns for shown command and ruler
-
- RedrawingDisabled--;
redraw_all_later(UPD_CLEAR);
if (starting != NO_SCREEN) {
@@ -465,6 +470,7 @@ int update_screen(void)
}
msg_scrolled = 0;
msg_scrolled_at_flush = 0;
+ msg_grid_scroll_discount = 0;
need_wait_return = false;
}
@@ -518,7 +524,7 @@ int update_screen(void)
// TODO(bfredl): special casing curwin here is SÅ JÄVLA BULL.
// Either this should be done for all windows or not at all.
if (curwin->w_redr_type < UPD_NOT_VALID
- && curwin->w_nrwidth != ((curwin->w_p_nu || curwin->w_p_rnu)
+ && curwin->w_nrwidth != ((curwin->w_p_nu || curwin->w_p_rnu || *curwin->w_p_stc)
? number_width(curwin) : 0)) {
curwin->w_redr_type = UPD_NOT_VALID;
}
@@ -626,6 +632,20 @@ int update_screen(void)
return OK;
}
+static void win_border_redr_title(win_T *wp, ScreenGrid *grid, int col)
+{
+ VirtText title_chunks = wp->w_float_config.title_chunks;
+
+ for (size_t i = 0; i < title_chunks.size; i++) {
+ char *text = title_chunks.items[i].text;
+ int cell = (int)mb_string2cells(text);
+ int hl_id = title_chunks.items[i].hl_id;
+ int attr = hl_id ? syn_id2attr(hl_id) : 0;
+ grid_puts(grid, text, 0, col, attr);
+ col += cell;
+ }
+}
+
static void win_redr_border(win_T *wp)
{
wp->w_redr_border = false;
@@ -646,9 +666,24 @@ static void win_redr_border(win_T *wp)
if (adj[3]) {
grid_put_schar(grid, 0, 0, chars[0], attrs[0]);
}
+
for (int i = 0; i < icol; i++) {
grid_put_schar(grid, 0, i + adj[3], chars[1], attrs[1]);
}
+
+ if (wp->w_float_config.title) {
+ int title_col = 0;
+ int title_width = wp->w_float_config.title_width;
+ AlignTextPos title_pos = wp->w_float_config.title_pos;
+
+ if (title_pos == kAlignCenter) {
+ title_col = (icol - title_width) / 2 + 1;
+ } else {
+ title_col = title_pos == kAlignLeft ? 1 : icol - title_width + 1;
+ }
+
+ win_border_redr_title(wp, grid, title_col);
+ }
if (adj[1]) {
grid_put_schar(grid, 0, icol + adj[3], chars[2], attrs[2]);
}
@@ -804,29 +839,29 @@ static bool vsep_connected(win_T *wp, WindowCorner corner)
/// Draw the vertical separator right of window "wp"
static void draw_vsep_win(win_T *wp)
{
- int hl;
- int c;
-
- if (wp->w_vsep_width) {
- // draw the vertical separator right of this window
- c = fillchar_vsep(wp, &hl);
- grid_fill(&default_grid, wp->w_winrow, W_ENDROW(wp),
- W_ENDCOL(wp), W_ENDCOL(wp) + 1, c, ' ', hl);
+ if (!wp->w_vsep_width) {
+ return;
}
+
+ // draw the vertical separator right of this window
+ int hl;
+ int c = fillchar_vsep(wp, &hl);
+ grid_fill(&default_grid, wp->w_winrow, W_ENDROW(wp),
+ W_ENDCOL(wp), W_ENDCOL(wp) + 1, c, ' ', hl);
}
/// Draw the horizontal separator below window "wp"
static void draw_hsep_win(win_T *wp)
{
- int hl;
- int c;
-
- if (wp->w_hsep_height) {
- // draw the horizontal separator below this window
- c = fillchar_hsep(wp, &hl);
- grid_fill(&default_grid, W_ENDROW(wp), W_ENDROW(wp) + 1,
- wp->w_wincol, W_ENDCOL(wp), c, c, hl);
+ if (!wp->w_hsep_height) {
+ return;
}
+
+ // draw the horizontal separator below this window
+ int hl;
+ int c = fillchar_hsep(wp, &hl);
+ grid_fill(&default_grid, W_ENDROW(wp), W_ENDROW(wp) + 1,
+ wp->w_wincol, W_ENDCOL(wp), c, c, hl);
}
/// Get the separator connector for specified window corner of window "wp"
@@ -927,11 +962,6 @@ static void draw_sep_connectors_win(win_T *wp)
/// bot: from bot_start to last row (when scrolled up)
static void win_update(win_T *wp, DecorProviders *providers)
{
- bool called_decor_providers = false;
-win_update_start:
- ;
- buf_T *buf = wp->w_buffer;
- int type;
int top_end = 0; // Below last row of the top area that needs
// updating. 0 when no top area updating.
int mid_start = 999; // first row of the mid area that needs
@@ -946,28 +976,20 @@ win_update_start:
int bot_scroll_start = 999; // first line that needs to be redrawn due to
// scrolling. only used for EOB
- int row; // current window row to display
- linenr_T lnum; // current buffer lnum to display
- int idx; // current index in w_lines[]
- int srow; // starting row of the current line
-
- bool eof = false; // if true, we hit the end of the file
- bool didline = false; // if true, we finished the last line
- int i;
- long j;
static bool recursive = false; // being called recursively
- const linenr_T old_botline = wp->w_botline;
+
// Remember what happened to the previous line.
-#define DID_NONE 1 // didn't update a line
-#define DID_LINE 2 // updated a normal line
-#define DID_FOLD 3 // updated a folded line
- int did_update = DID_NONE;
+ enum {
+ DID_NONE = 1, // didn't update a line
+ DID_LINE = 2, // updated a normal line
+ DID_FOLD = 3, // updated a folded line
+ } did_update = DID_NONE;
+
linenr_T syntax_last_parsed = 0; // last parsed text line
linenr_T mod_top = 0;
linenr_T mod_bot = 0;
- int save_got_int;
- type = wp->w_redr_type;
+ int type = wp->w_redr_type;
if (type >= UPD_NOT_VALID) {
// TODO(bfredl): should only be implied for CLEAR, not NOT_VALID!
@@ -994,16 +1016,32 @@ win_update_start:
return;
}
+ buf_T *buf = wp->w_buffer;
+
+ // reset got_int, otherwise regexp won't work
+ int save_got_int = got_int;
+ got_int = 0;
+ // Set the time limit to 'redrawtime'.
+ proftime_T syntax_tm = profile_setlimit(p_rdt);
+ syn_set_timeout(&syntax_tm);
+
+ win_extmark_arr.size = 0;
+
+ decor_redraw_reset(buf, &decor_state);
+
+ DecorProviders line_providers;
+ decor_providers_invoke_win(wp, providers, &line_providers, &provider_err);
+
redraw_win_signcol(wp);
init_search_hl(wp, &screen_search_hl);
// Force redraw when width of 'number' or 'relativenumber' column
// changes.
- i = (wp->w_p_nu || wp->w_p_rnu) ? number_width(wp) : 0;
- if (wp->w_nrwidth != i) {
+ int nrwidth = (wp->w_p_nu || wp->w_p_rnu || *wp->w_p_stc) ? number_width(wp) : 0;
+ if (wp->w_nrwidth != nrwidth) {
type = UPD_NOT_VALID;
- wp->w_nrwidth = i;
+ wp->w_nrwidth = nrwidth;
if (buf->terminal) {
terminal_check_size(buf->terminal);
@@ -1075,7 +1113,7 @@ win_update_start:
// to this line. If there is no valid entry, use MAXLNUM.
lnumt = wp->w_topline;
lnumb = MAXLNUM;
- for (i = 0; i < wp->w_lines_valid; i++) {
+ for (int i = 0; i < wp->w_lines_valid; i++) {
if (wp->w_lines[i].wl_valid) {
if (wp->w_lines[i].wl_lastlnum < mod_top) {
lnumt = wp->w_lines[i].wl_lastlnum + 1;
@@ -1130,8 +1168,8 @@ win_update_start:
// When only displaying the lines at the top, set top_end. Used when
// window has scrolled down for msg_scrolled.
if (type == UPD_REDRAW_TOP) {
- j = 0;
- for (i = 0; i < wp->w_lines_valid; i++) {
+ long j = 0;
+ for (int i = 0; i < wp->w_lines_valid; i++) {
j += wp->w_lines[i].wl_size;
if (j >= wp->w_upd_rows) {
top_end = (int)j;
@@ -1167,6 +1205,7 @@ win_update_start:
|| (wp->w_topline == wp->w_lines[0].wl_lnum
&& wp->w_topfill > wp->w_old_topfill))) {
// New topline is above old topline: May scroll down.
+ long j;
if (hasAnyFolding(wp)) {
linenr_T ln;
@@ -1184,7 +1223,7 @@ win_update_start:
j = wp->w_lines[0].wl_lnum - wp->w_topline;
}
if (j < wp->w_grid.rows - 2) { // not too far off
- i = plines_m_win(wp, wp->w_topline, wp->w_lines[0].wl_lnum - 1);
+ int i = plines_m_win(wp, wp->w_topline, wp->w_lines[0].wl_lnum - 1);
// insert extra lines for previously invisible filler lines
if (wp->w_lines[0].wl_lnum != wp->w_topline) {
i += win_get_fill(wp, wp->w_lines[0].wl_lnum) - wp->w_old_topfill;
@@ -1206,6 +1245,7 @@ win_update_start:
if ((wp->w_lines_valid += (linenr_T)j) > wp->w_grid.rows) {
wp->w_lines_valid = wp->w_grid.rows;
}
+ int idx;
for (idx = wp->w_lines_valid; idx - j >= 0; idx--) {
wp->w_lines[idx] = wp->w_lines[idx - j];
}
@@ -1225,9 +1265,9 @@ win_update_start:
// needs updating.
// try to find wp->w_topline in wp->w_lines[].wl_lnum
- j = -1;
- row = 0;
- for (i = 0; i < wp->w_lines_valid; i++) {
+ long j = -1;
+ int row = 0;
+ for (int i = 0; i < wp->w_lines_valid; i++) {
if (wp->w_lines[i].wl_valid
&& wp->w_lines[i].wl_lnum == wp->w_topline) {
j = i;
@@ -1263,7 +1303,7 @@ win_update_start:
// upwards, to compensate for the deleted lines. Set
// bot_start to the first row that needs redrawing.
bot_start = 0;
- idx = 0;
+ int idx = 0;
for (;;) {
wp->w_lines[idx] = wp->w_lines[j];
// stop at line that didn't fit, unless it is still
@@ -1460,9 +1500,9 @@ win_update_start:
// above the Visual area and reset wl_valid, do count these for
// mid_end (in srow).
if (mid_start > 0) {
- lnum = wp->w_topline;
- idx = 0;
- srow = 0;
+ linenr_T lnum = wp->w_topline;
+ int idx = 0;
+ int srow = 0;
if (scrolled_down) {
mid_start = top_end;
} else {
@@ -1508,38 +1548,18 @@ win_update_start:
wp->w_old_visual_col = 0;
}
- // reset got_int, otherwise regexp won't work
- save_got_int = got_int;
- got_int = 0;
- // Set the time limit to 'redrawtime'.
- proftime_T syntax_tm = profile_setlimit(p_rdt);
- syn_set_timeout(&syntax_tm);
-
- // Update all the window rows.
- idx = 0; // first entry in w_lines[].wl_size
- row = 0;
- srow = 0;
- lnum = wp->w_topline; // first line shown in window
-
- win_extmark_arr.size = 0;
-
- decor_redraw_reset(buf, &decor_state);
-
- DecorProviders line_providers;
- decor_providers_invoke_win(wp, providers, &line_providers, &provider_err);
- (void)win_signcol_count(wp); // check if provider changed signcol width
- if (must_redraw != 0) {
- must_redraw = 0;
- if (!called_decor_providers) {
- called_decor_providers = true;
- goto win_update_start;
- }
- }
-
bool cursorline_standout = win_cursorline_standout(wp);
win_check_ns_hl(wp);
+ // Update all the window rows.
+ int idx = 0; // first entry in w_lines[].wl_size
+ int row = 0; // current window row to display
+ int srow = 0; // starting row of the current line
+ linenr_T lnum = wp->w_topline; // first line shown in window
+
+ bool eof = false; // if true, we hit the end of the file
+ bool didline = false; // if true, we finished the last line
for (;;) {
// stop updating when reached the end of the window (check for _past_
// the end of the window is at the end of the loop)
@@ -1603,6 +1623,7 @@ win_update_start:
int new_rows = 0;
int xtra_rows;
linenr_T l;
+ int i;
// Count the old number of window rows, using w_lines[], which
// should still contain the sizes for the lines as they are
@@ -1637,7 +1658,7 @@ win_update_start:
} else {
// Able to count old number of rows: Count new window
// rows, and may insert/delete lines
- j = idx;
+ long j = idx;
for (l = lnum; l < mod_bot; l++) {
if (hasFoldingWin(wp, l, NULL, &l, true, NULL)) {
new_rows++;
@@ -1753,7 +1774,7 @@ win_update_start:
// Let the syntax stuff know we skipped a few lines.
if (syntax_last_parsed != 0 && syntax_last_parsed + 1 < lnum
&& syntax_present(wp)) {
- syntax_end_parsing(syntax_last_parsed + 1);
+ syntax_end_parsing(wp, syntax_last_parsed + 1);
}
// Display one line
@@ -1808,6 +1829,18 @@ win_update_start:
did_update = DID_NONE;
}
+ // 'statuscolumn' width has changed or errored, start from the top.
+ if (wp->w_redr_statuscol) {
+ wp->w_redr_statuscol = false;
+ idx = 0;
+ row = 0;
+ lnum = wp->w_topline;
+ wp->w_lines_valid = 0;
+ wp->w_valid &= ~VALID_WCOL;
+ decor_providers_invoke_win(wp, providers, &line_providers, &provider_err);
+ continue;
+ }
+
if (lnum > buf->b_ml.ml_line_count) {
eof = true;
break;
@@ -1827,9 +1860,11 @@ win_update_start:
// Let the syntax stuff know we stop parsing here.
if (syntax_last_parsed != 0 && syntax_present(wp)) {
- syntax_end_parsing(syntax_last_parsed + 1);
+ syntax_end_parsing(wp, syntax_last_parsed + 1);
}
+ const linenr_T old_botline = wp->w_botline;
+
// If we didn't hit the end of the file, and we didn't finish the last
// line we were working on, then the line didn't fit.
wp->w_empty_rows = 0;
@@ -1873,11 +1908,11 @@ win_update_start:
} else {
if (eof) { // we hit the end of the file
wp->w_botline = buf->b_ml.ml_line_count + 1;
- j = win_get_fill(wp, wp->w_botline);
+ long j = win_get_fill(wp, wp->w_botline);
if (j > 0 && !wp->w_botfill && row < wp->w_grid.rows) {
// Display filler text below last line. win_line() will check
// for ml_line_count+1 and only draw filler lines
- foldinfo_T info = FOLDINFO_INIT;
+ foldinfo_T info = { 0 };
row = win_line(wp, wp->w_botline, row, wp->w_grid.rows,
false, false, info, &line_providers, &provider_err);
}
@@ -1943,11 +1978,11 @@ win_update_start:
update_topline(curwin); // may invalidate w_botline again
if (must_redraw != 0) {
// Don't update for changes in buffer again.
- i = curbuf->b_mod_set;
+ int mod_set = curbuf->b_mod_set;
curbuf->b_mod_set = false;
win_update(curwin, providers);
must_redraw = 0;
- curbuf->b_mod_set = i;
+ curbuf->b_mod_set = mod_set;
}
recursive = false;
}