aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/drawscreen.c
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2023-11-30 20:35:25 +0000
committerJosh Rahm <joshuarahm@gmail.com>2023-11-30 20:35:25 +0000
commit1b7b916b7631ddf73c38e3a0070d64e4636cb2f3 (patch)
treecd08258054db80bb9a11b1061bb091c70b76926a /src/nvim/drawscreen.c
parenteaa89c11d0f8aefbb512de769c6c82f61a8baca3 (diff)
parent4a8bf24ac690004aedf5540fa440e788459e5e34 (diff)
downloadrneovim-aucmd_textputpost.tar.gz
rneovim-aucmd_textputpost.tar.bz2
rneovim-aucmd_textputpost.zip
Merge remote-tracking branch 'upstream/master' into aucmd_textputpostaucmd_textputpost
Diffstat (limited to 'src/nvim/drawscreen.c')
-rw-r--r--src/nvim/drawscreen.c1031
1 files changed, 863 insertions, 168 deletions
diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c
index 04c342e068..6cc623cb72 100644
--- a/src/nvim/drawscreen.c
+++ b/src/nvim/drawscreen.c
@@ -1,6 +1,3 @@
-// This is an open source non-commercial project. Dear PVS-Studio, please check
-// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
-
// drawscreen.c: Code for updating all the windows on the screen.
// This is the top level, drawline.c is the middle and grid.c/screen.c the lower level.
@@ -56,24 +53,32 @@
#include <assert.h>
#include <inttypes.h>
+#include <limits.h>
#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include "klib/kvec.h"
#include "nvim/api/private/defs.h"
-#include "nvim/ascii.h"
+#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
#include "nvim/cmdexpand.h"
+#include "nvim/cursor.h"
#include "nvim/decoration.h"
#include "nvim/decoration_provider.h"
#include "nvim/diff.h"
+#include "nvim/digraph.h"
#include "nvim/drawline.h"
#include "nvim/drawscreen.h"
+#include "nvim/eval.h"
#include "nvim/ex_getln.h"
-#include "nvim/extmark_defs.h"
#include "nvim/fold.h"
+#include "nvim/func_attr.h"
+#include "nvim/getchar.h"
+#include "nvim/gettext.h"
#include "nvim/globals.h"
#include "nvim/grid.h"
#include "nvim/highlight.h"
@@ -86,20 +91,25 @@
#include "nvim/move.h"
#include "nvim/normal.h"
#include "nvim/option.h"
+#include "nvim/option_vars.h"
#include "nvim/plines.h"
#include "nvim/popupmenu.h"
-#include "nvim/pos.h"
+#include "nvim/pos_defs.h"
#include "nvim/profile.h"
#include "nvim/regexp.h"
-#include "nvim/screen.h"
+#include "nvim/search.h"
+#include "nvim/sign_defs.h"
+#include "nvim/spell.h"
+#include "nvim/state.h"
#include "nvim/statusline.h"
+#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/terminal.h"
-#include "nvim/types.h"
+#include "nvim/types_defs.h"
#include "nvim/ui.h"
#include "nvim/ui_compositor.h"
#include "nvim/version.h"
-#include "nvim/vim.h"
+#include "nvim/vim_defs.h"
#include "nvim/window.h"
/// corner value flags for hsep_connected and vsep_connected
@@ -118,8 +128,6 @@ static bool redraw_popupmenu = false;
static bool msg_grid_invalid = false;
static bool resizing_autocmd = false;
-static char *provider_err = NULL;
-
/// Check if the cursor line needs to be redrawn because of 'concealcursor'.
///
/// When cursor is moved at the same time, both lines will be redrawn regardless.
@@ -202,7 +210,7 @@ bool default_grid_alloc(void)
void screenclear(void)
{
- check_for_delay(false);
+ msg_check_for_delay(false);
if (starting == NO_SCREEN || default_grid.chars == NULL) {
return;
@@ -212,7 +220,6 @@ void screenclear(void)
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;
}
ui_call_grid_clear(1); // clear the display
@@ -318,11 +325,15 @@ void screen_resize(int width, int height)
resizing_autocmd = false;
redraw_all_later(UPD_CLEAR);
+ if (State != MODE_ASKMORE && State != MODE_EXTERNCMD && State != MODE_CONFIRM) {
+ screenclear();
+ }
+
if (starting != NO_SCREEN) {
maketitle();
changed_line_abv_curs();
- invalidate_botline();
+ invalidate_botline(curwin);
// We only redraw when it's needed:
// - While at the more prompt or executing an external command, don't
@@ -370,6 +381,32 @@ void screen_resize(int width, int height)
resizing_screen = false;
}
+/// Check if the new Nvim application "screen" dimensions are valid.
+/// Correct it if it's too small or way too big.
+void check_screensize(void)
+{
+ // Limit Rows and Columns to avoid an overflow in Rows * Columns.
+ if (Rows < min_rows()) {
+ // need room for one window and command line
+ Rows = min_rows();
+ } else if (Rows > 1000) {
+ Rows = 1000;
+ }
+
+ if (Columns < MIN_COLUMNS) {
+ Columns = MIN_COLUMNS;
+ } else if (Columns > 10000) {
+ Columns = 10000;
+ }
+}
+
+/// Return true if redrawing should currently be done.
+bool redrawing(void)
+{
+ return !RedrawingDisabled
+ && !(p_lz && char_avail() && !KeyTyped && !do_redraw);
+}
+
/// Redraw the parts of the screen that is marked for redraw.
///
/// Most code shouldn't call this directly, rather use redraw_later() and
@@ -411,6 +448,15 @@ int update_screen(void)
display_tick++; // let syntax code know we're in a next round of
// display updating
+ // glyph cache full, very rare
+ if (schar_cache_clear_if_full()) {
+ // must use CLEAR, as the contents of screen buffers cannot be
+ // compared to their previous state here.
+ // TODO(bfredl): if start to cache schar_T values in places (like fcs/lcs)
+ // we need to revalidate these here as well!
+ type = MAX(type, UPD_CLEAR);
+ }
+
// Tricky: vim code can reset msg_scrolled behind our back, so need
// separate bookkeeping for now.
if (msg_did_scroll) {
@@ -430,7 +476,7 @@ int update_screen(void)
// non-displayed part of msg_grid is considered invalid.
for (int i = 0; i < MIN(msg_scrollsize(), msg_grid.rows); i++) {
grid_clear_line(&msg_grid, msg_grid.line_offset[i],
- msg_grid.cols, false);
+ msg_grid.cols, i < p_ch);
}
}
msg_grid.throttled = false;
@@ -507,7 +553,7 @@ int update_screen(void)
ui_comp_set_screen_valid(true);
DecorProviders providers;
- decor_providers_start(&providers, &provider_err);
+ decor_providers_start(&providers);
// "start" callback could have changed highlights for global elements
if (win_check_ns_hl(NULL)) {
@@ -516,7 +562,7 @@ int update_screen(void)
}
if (clear_cmdline) { // going to clear cmdline (done below)
- check_for_delay(false);
+ msg_check_for_delay(false);
}
// Force redraw when width of 'number' or 'relativenumber' column
@@ -540,9 +586,9 @@ int update_screen(void)
draw_tabline();
}
- // Correct stored syntax highlighting info for changes in each displayed
- // buffer. Each buffer must only be done once.
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ // Correct stored syntax highlighting info for changes in each displayed
+ // buffer. Each buffer must only be done once.
update_window_hl(wp, type >= UPD_NOT_VALID || hl_changed);
buf_T *buf = wp->w_buffer;
@@ -554,10 +600,19 @@ int update_screen(void)
}
if (buf->b_mod_tick_decor < display_tick) {
- decor_providers_invoke_buf(buf, &providers, &provider_err);
+ decor_providers_invoke_buf(buf, &providers);
buf->b_mod_tick_decor = display_tick;
}
}
+
+ // Reset 'statuscolumn' if there is no dedicated signcolumn but it is invalid.
+ if (*wp->w_p_stc != NUL && wp->w_minscwidth <= SCL_NO
+ && (wp->w_buffer->b_signcols.invalid_bot || !wp->w_buffer->b_signcols.sentinel)) {
+ wp->w_nrwidth_line_count = 0;
+ wp->w_valid &= ~VALID_WCOL;
+ wp->w_redr_type = UPD_NOT_VALID;
+ wp->w_buffer->b_signcols.invalid_bot = 0;
+ }
}
// Go from top to bottom through the windows, redrawing the ones that need it.
@@ -624,7 +679,7 @@ int update_screen(void)
}
did_intro = true;
- decor_providers_invoke_end(&providers, &provider_err);
+ decor_providers_invoke_end(&providers);
kvi_destroy(providers);
// either cmdline is cleared, not drawn or mode is last drawn
@@ -632,20 +687,56 @@ int update_screen(void)
return OK;
}
-static void win_border_redr_title(win_T *wp, ScreenGrid *grid, int col)
+/// 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);
+ // Set the time limit to 'redrawtime'.
+ screen_search_hl.tm = profile_setlimit(p_rdt);
+}
+
+/// Clean up for 'hlsearch' highlighting.
+void end_search_hl(void)
{
- VirtText title_chunks = wp->w_float_config.title_chunks;
+ if (screen_search_hl.rm.regprog == NULL) {
+ return;
+ }
- 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;
+ vim_regfree(screen_search_hl.rm.regprog);
+ screen_search_hl.rm.regprog = NULL;
+}
+
+static void win_redr_bordertext(win_T *wp, VirtText vt, int col)
+{
+ for (size_t i = 0; i < kv_size(vt);) {
+ int attr = 0;
+ char *text = next_virt_text_chunk(vt, &i, &attr);
+ if (text == NULL) {
+ break;
+ }
+ attr = hl_apply_winblend(wp, attr);
+ col += grid_line_puts(col, text, -1, attr);
}
}
+int win_get_bordertext_col(int total_col, int text_width, AlignTextPos align)
+{
+ switch (align) {
+ case kAlignLeft:
+ return 1;
+ case kAlignCenter:
+ return MAX((total_col - text_width) / 2 + 1, 1);
+ case kAlignRight:
+ return MAX(total_col - text_width + 1, 1);
+ }
+ UNREACHABLE;
+}
+
static void win_redr_border(win_T *wp)
{
wp->w_redr_border = false;
@@ -655,102 +746,459 @@ static void win_redr_border(win_T *wp)
ScreenGrid *grid = &wp->w_grid_alloc;
- schar_T *chars = wp->w_float_config.border_chars;
+ schar_T chars[8];
+ for (int i = 0; i < 8; i++) {
+ chars[i] = schar_from_str(wp->w_float_config.border_chars[i]);
+ }
int *attrs = wp->w_float_config.border_attr;
int *adj = wp->w_border_adj;
- int irow = wp->w_height_inner + wp->w_winbar_height, icol = wp->w_width_inner;
+ int irow = wp->w_height_inner + wp->w_winbar_height;
+ int icol = wp->w_width_inner;
if (adj[0]) {
- grid_puts_line_start(grid, 0);
+ grid_line_start(grid, 0);
if (adj[3]) {
- grid_put_schar(grid, 0, 0, chars[0], attrs[0]);
+ grid_line_put_schar(0, chars[0], attrs[0]);
}
for (int i = 0; i < icol; i++) {
- grid_put_schar(grid, 0, i + adj[3], chars[1], attrs[1]);
+ grid_line_put_schar(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);
+ int title_col = win_get_bordertext_col(icol, wp->w_float_config.title_width,
+ wp->w_float_config.title_pos);
+ win_redr_bordertext(wp, wp->w_float_config.title_chunks, title_col);
}
if (adj[1]) {
- grid_put_schar(grid, 0, icol + adj[3], chars[2], attrs[2]);
+ grid_line_put_schar(icol + adj[3], chars[2], attrs[2]);
}
- grid_puts_line_flush(false);
+ grid_line_flush();
}
for (int i = 0; i < irow; i++) {
if (adj[3]) {
- grid_puts_line_start(grid, i + adj[0]);
- grid_put_schar(grid, i + adj[0], 0, chars[7], attrs[7]);
- grid_puts_line_flush(false);
+ grid_line_start(grid, i + adj[0]);
+ grid_line_put_schar(0, chars[7], attrs[7]);
+ grid_line_flush();
}
if (adj[1]) {
- int ic = (i == 0 && !adj[0] && chars[2][0]) ? 2 : 3;
- grid_puts_line_start(grid, i + adj[0]);
- grid_put_schar(grid, i + adj[0], icol + adj[3], chars[ic], attrs[ic]);
- grid_puts_line_flush(false);
+ int ic = (i == 0 && !adj[0] && chars[2]) ? 2 : 3;
+ grid_line_start(grid, i + adj[0]);
+ grid_line_put_schar(icol + adj[3], chars[ic], attrs[ic]);
+ grid_line_flush();
}
}
if (adj[2]) {
- grid_puts_line_start(grid, irow + adj[0]);
+ grid_line_start(grid, irow + adj[0]);
if (adj[3]) {
- grid_put_schar(grid, irow + adj[0], 0, chars[6], attrs[6]);
+ grid_line_put_schar(0, chars[6], attrs[6]);
}
+
for (int i = 0; i < icol; i++) {
- int ic = (i == 0 && !adj[3] && chars[6][0]) ? 6 : 5;
- grid_put_schar(grid, irow + adj[0], i + adj[3], chars[ic], attrs[ic]);
+ int ic = (i == 0 && !adj[3] && chars[6]) ? 6 : 5;
+ grid_line_put_schar(i + adj[3], chars[ic], attrs[ic]);
+ }
+
+ if (wp->w_float_config.footer) {
+ int footer_col = win_get_bordertext_col(icol, wp->w_float_config.footer_width,
+ wp->w_float_config.footer_pos);
+ win_redr_bordertext(wp, wp->w_float_config.footer_chunks, footer_col);
}
if (adj[1]) {
- grid_put_schar(grid, irow + adj[0], icol + adj[3], chars[4], attrs[4]);
+ grid_line_put_schar(icol + adj[3], chars[4], attrs[4]);
+ }
+ grid_line_flush();
+ }
+}
+
+/// Set cursor to its position in the current window.
+void setcursor(void)
+{
+ setcursor_mayforce(false);
+}
+
+/// Set cursor to its position in the current window.
+/// @param force when true, also when not redrawing.
+void setcursor_mayforce(bool force)
+{
+ if (force || redrawing()) {
+ validate_cursor();
+
+ ScreenGrid *grid = &curwin->w_grid;
+ int row = curwin->w_wrow;
+ int col = curwin->w_wcol;
+ if (curwin->w_p_rl) {
+ // With 'rightleft' set and the cursor on a double-wide character,
+ // position it on the leftmost column.
+ col = curwin->w_width_inner - curwin->w_wcol
+ - ((utf_ptr2cells(get_cursor_pos_ptr()) == 2
+ && vim_isprintc(gchar_cursor())) ? 2 : 1);
}
- grid_puts_line_flush(false);
+
+ grid_adjust(&grid, &row, &col);
+ ui_grid_cursor_goto(grid->handle, row, col);
}
}
/// Show current cursor info in ruler and various other places
///
/// @param always if false, only show ruler if position has changed.
-void show_cursor_info(bool always)
+void show_cursor_info_later(bool force)
{
- if (!always && !redrawing()) {
- return;
+ int state = get_real_state();
+ int empty_line = (State & MODE_INSERT) == 0
+ && *ml_get_buf(curwin->w_buffer, curwin->w_cursor.lnum) == NUL;
+
+ // Only draw when something changed.
+ validate_virtcol_win(curwin);
+ if (force
+ || curwin->w_cursor.lnum != curwin->w_stl_cursor.lnum
+ || curwin->w_cursor.col != curwin->w_stl_cursor.col
+ || curwin->w_virtcol != curwin->w_stl_virtcol
+ || curwin->w_cursor.coladd != curwin->w_stl_cursor.coladd
+ || curwin->w_topline != curwin->w_stl_topline
+ || curwin->w_buffer->b_ml.ml_line_count != curwin->w_stl_line_count
+ || curwin->w_topfill != curwin->w_stl_topfill
+ || empty_line != curwin->w_stl_empty
+ || reg_recording != curwin->w_stl_recording
+ || state != curwin->w_stl_state
+ || (VIsual_active && VIsual_mode != curwin->w_stl_visual_mode)) {
+ if (curwin->w_status_height || global_stl_height()) {
+ curwin->w_redr_status = true;
+ } else {
+ redraw_cmdline = true;
+ }
+
+ if (*p_wbr != NUL || *curwin->w_p_wbr != NUL) {
+ curwin->w_redr_status = true;
+ }
+
+ if ((p_icon && (stl_syntax & STL_IN_ICON))
+ || (p_title && (stl_syntax & STL_IN_TITLE))) {
+ need_maketitle = true;
+ }
+ }
+
+ curwin->w_stl_cursor = curwin->w_cursor;
+ curwin->w_stl_virtcol = curwin->w_virtcol;
+ curwin->w_stl_empty = (char)empty_line;
+ curwin->w_stl_topline = curwin->w_topline;
+ curwin->w_stl_line_count = curwin->w_buffer->b_ml.ml_line_count;
+ curwin->w_stl_topfill = curwin->w_topfill;
+ curwin->w_stl_recording = reg_recording;
+ curwin->w_stl_state = state;
+ if (VIsual_active) {
+ curwin->w_stl_visual_mode = VIsual_mode;
}
+}
+
+/// @return true when postponing displaying the mode message: when not redrawing
+/// or inside a mapping.
+bool 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 length = 0;
+
+ if (ui_has(kUIMessages) && clear_cmdline) {
+ msg_ext_clear(true);
+ }
+
+ // don't make non-flushed message part of the showmode
+ msg_ext_ui_flush();
+
+ msg_grid_validate();
+
+ int do_mode = ((p_smd && msg_silent == 0)
+ && ((State & MODE_TERMINAL)
+ || (State & MODE_INSERT)
+ || restart_edit != NUL
+ || VIsual_active));
+
+ bool can_show_mode = (p_ch != 0 || ui_has(kUIMessages));
+ if ((do_mode || reg_recording != 0) && can_show_mode) {
+ int sub_attr;
+ if (skip_showmode()) {
+ return 0; // show mode later
+ }
+
+ bool nwr_save = need_wait_return;
+
+ // wait a bit before overwriting an important message
+ msg_check_for_delay(false);
+
+ // if the cmdline is more than one line high, erase top lines
+ bool 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();
+ int attr = HL_ATTR(HLF_CM); // Highlight mode
+
+ // When the screen is too narrow to show the entire mode message,
+ // avoid scrolling and truncate instead.
+ msg_no_more = true;
+ int save_lines_left = lines_left;
+ lines_left = 0;
+
+ if (do_mode) {
+ msg_puts_attr("--", attr);
+ // 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. With external messages there
+ // is no imposed limit.
+ if (ui_has(kUIMessages)) {
+ length = INT_MAX;
+ } else {
+ 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(edit_submode_pre, attr);
+ }
+ msg_puts_attr(edit_submode, attr);
+ }
+ if (edit_submode_extra != NULL) {
+ msg_puts_attr(" ", attr); // Add a space in between.
+ if ((int)edit_submode_highl < HLF_COUNT) {
+ sub_attr = win_hl_attr(curwin, (int)edit_submode_highl);
+ } else {
+ sub_attr = attr;
+ }
+ msg_puts_attr(edit_submode_extra, sub_attr);
+ }
+ }
+ } else {
+ if (State & MODE_TERMINAL) {
+ msg_puts_attr(_(" TERMINAL"), 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) {
+ if (p_ri) {
+ msg_puts_attr(_(" REVERSE"), attr);
+ }
+ msg_puts_attr(_(" INSERT"), attr);
+ } else if (restart_edit == 'I' || restart_edit == 'i'
+ || restart_edit == 'a' || restart_edit == 'A') {
+ if (curbuf->terminal) {
+ msg_puts_attr(_(" (terminal)"), attr);
+ } else {
+ 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);
+ }
+ if (State & MODE_LANGMAP) {
+ if (curwin->w_p_arab) {
+ msg_puts_attr(_(" Arabic"), attr);
+ } else if (get_keymap_str(curwin, " (%s)",
+ NameBuff, MAXPATHL)) {
+ msg_puts_attr(NameBuff, attr);
+ }
+ }
+ 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;
+ msg_no_more = false;
+ lines_left = save_lines_left;
+ 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();
+ }
+
+ // NB: also handles clearing the showmode if it was empty or disabled
+ msg_ext_flush_showmode();
+
+ // In Visual mode the size of the selected area must be redrawn.
+ if (VIsual_active) {
+ clear_showcmd();
+ }
+
+ // If the current or last window has no status line and global statusline is disabled,
+ // the ruler is after the mode message and must be redrawn
+ win_T *ruler_win = curwin->w_status_height == 0 ? curwin : lastwin_nofloating();
+ if (redrawing() && ruler_win->w_status_height == 0 && global_stl_height() == 0
+ && !(p_ch == 0 && !ui_has(kUIMessages))) {
+ grid_line_start(&msg_grid_adj, Rows - 1);
+ win_redr_ruler(ruler_win);
+ grid_line_flush();
+ }
+
+ 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;
+}
- win_check_ns_hl(curwin);
- if ((*p_stl != NUL || *curwin->w_p_stl != NUL)
- && (curwin->w_status_height || global_stl_height())) {
- redraw_custom_statusline(curwin);
+/// 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(bool 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 {
- win_redr_ruler(curwin, always);
+ clearmode();
}
- if (*p_wbr != NUL || *curwin->w_p_wbr != NUL) {
- win_redr_winbar(curwin);
+}
+
+// Clear the mode message.
+void clearmode(void)
+{
+ const int save_msg_row = msg_row;
+ const int save_msg_col = msg_col;
+
+ msg_ext_ui_flush();
+ msg_pos_mode();
+ if (reg_recording != 0) {
+ recording_mode(HL_ATTR(HLF_CM));
}
+ msg_clr_eos();
+ msg_ext_flush_showmode();
- if (need_maketitle
- || (p_icon && (stl_syntax & STL_IN_ICON))
- || (p_title && (stl_syntax & STL_IN_TITLE))) {
- maketitle();
+ 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;
}
- win_check_ns_hl(NULL);
- // Redraw the tab pages line if needed.
- if (redraw_tabline) {
- draw_tabline();
+ char s[4];
+ snprintf(s, ARRAY_SIZE(s), " @%c", reg_recording);
+ msg_puts_attr(s, attr);
+}
+
+#define COL_RULER 17 // columns needed by standard ruler
+
+/// 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'.
+void comp_col(void)
+{
+ bool last_has_status = last_stl_height(false) > 0;
+
+ sc_col = 0;
+ ru_col = 0;
+ if (p_ru) {
+ ru_col = (ru_wid ? ru_wid : COL_RULER) + 1;
+ // no last status line, adjust sc_col
+ if (!last_has_status) {
+ sc_col = ru_col;
+ }
+ }
+ if (p_sc) {
+ sc_col += SHOWCMD_COLS;
+ if (!p_ru || last_has_status) { // no need for separating space
+ sc_col++;
+ }
+ }
+ assert(sc_col >= 0
+ && INT_MIN + sc_col <= Columns);
+ sc_col = Columns - sc_col;
+ assert(ru_col >= 0
+ && INT_MIN + ru_col <= Columns);
+ 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;
+ }
+ set_vim_var_nr(VV_ECHOSPACE, sc_col - 1);
}
static void redraw_win_signcol(win_T *wp)
@@ -762,6 +1210,7 @@ static void redraw_win_signcol(win_T *wp)
wp->w_scwidth = win_signcol_count(wp);
if (wp->w_scwidth != scwidth) {
changed_line_abv_curs_win(wp);
+ redraw_later(wp, UPD_NOT_VALID);
}
}
@@ -844,8 +1293,8 @@ static void draw_vsep_win(win_T *wp)
}
// draw the vertical separator right of this window
- int hl;
- int c = fillchar_vsep(wp, &hl);
+ int hl = win_hl_attr(wp, HLF_C);
+ int c = wp->w_p_fcs_chars.vert;
grid_fill(&default_grid, wp->w_winrow, W_ENDROW(wp),
W_ENDCOL(wp), W_ENDCOL(wp) + 1, c, ' ', hl);
}
@@ -858,30 +1307,32 @@ static void draw_hsep_win(win_T *wp)
}
// draw the horizontal separator below this window
- int hl;
- int c = fillchar_hsep(wp, &hl);
+ int hl = win_hl_attr(wp, HLF_C);
+ int c = wp->w_p_fcs_chars.horiz;
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"
-static int get_corner_sep_connector(win_T *wp, WindowCorner corner)
+static schar_T get_corner_sep_connector(win_T *wp, WindowCorner corner)
{
// It's impossible for windows to be connected neither vertically nor horizontally
// So if they're not vertically connected, assume they're horizontally connected
+ int c;
if (vsep_connected(wp, corner)) {
if (hsep_connected(wp, corner)) {
- return wp->w_p_fcs_chars.verthoriz;
+ c = wp->w_p_fcs_chars.verthoriz;
} else if (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT) {
- return wp->w_p_fcs_chars.vertright;
+ c = wp->w_p_fcs_chars.vertright;
} else {
- return wp->w_p_fcs_chars.vertleft;
+ c = wp->w_p_fcs_chars.vertleft;
}
} else if (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT) {
- return wp->w_p_fcs_chars.horizdown;
+ c = wp->w_p_fcs_chars.horizdown;
} else {
- return wp->w_p_fcs_chars.horizup;
+ c = wp->w_p_fcs_chars.horizup;
}
+ return schar_from_char(c);
}
/// Draw separator connecting characters on the corners of window "wp"
@@ -917,21 +1368,31 @@ static void draw_sep_connectors_win(win_T *wp)
win_at_left = frp->fr_parent == NULL;
// Draw the appropriate separator connector in every corner where drawing them is necessary
- if (!(win_at_top || win_at_left)) {
- grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_TOP_LEFT),
- wp->w_winrow - 1, wp->w_wincol - 1, hl);
- }
- if (!(win_at_top || win_at_right)) {
- grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_TOP_RIGHT),
- wp->w_winrow - 1, W_ENDCOL(wp), hl);
- }
- if (!(win_at_bottom || win_at_left)) {
- grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_BOTTOM_LEFT),
- W_ENDROW(wp), wp->w_wincol - 1, hl);
- }
- if (!(win_at_bottom || win_at_right)) {
- grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_BOTTOM_RIGHT),
- W_ENDROW(wp), W_ENDCOL(wp), hl);
+ // Make sure not to send cursor position updates to ui.
+ bool top_left = !(win_at_top || win_at_left);
+ bool top_right = !(win_at_top || win_at_right);
+ bool bot_left = !(win_at_bottom || win_at_left);
+ bool bot_right = !(win_at_bottom || win_at_right);
+
+ if (top_left) {
+ grid_line_start(&default_grid, wp->w_winrow - 1);
+ grid_line_put_schar(wp->w_wincol - 1, get_corner_sep_connector(wp, WC_TOP_LEFT), hl);
+ grid_line_flush();
+ }
+ if (top_right) {
+ grid_line_start(&default_grid, wp->w_winrow - 1);
+ grid_line_put_schar(W_ENDCOL(wp), get_corner_sep_connector(wp, WC_TOP_RIGHT), hl);
+ grid_line_flush();
+ }
+ if (bot_left) {
+ grid_line_start(&default_grid, W_ENDROW(wp));
+ grid_line_put_schar(wp->w_wincol - 1, get_corner_sep_connector(wp, WC_BOTTOM_LEFT), hl);
+ grid_line_flush();
+ }
+ if (bot_right) {
+ grid_line_start(&default_grid, W_ENDROW(wp));
+ grid_line_put_schar(W_ENDCOL(wp), get_corner_sep_connector(wp, WC_BOTTOM_RIGHT), hl);
+ grid_line_flush();
}
}
@@ -992,9 +1453,7 @@ static void win_update(win_T *wp, DecorProviders *providers)
int type = wp->w_redr_type;
if (type >= UPD_NOT_VALID) {
- // TODO(bfredl): should only be implied for CLEAR, not NOT_VALID!
wp->w_redr_status = true;
-
wp->w_lines_valid = 0;
}
@@ -1027,25 +1486,41 @@ static void win_update(win_T *wp, DecorProviders *providers)
win_extmark_arr.size = 0;
- decor_redraw_reset(buf, &decor_state);
+ decor_redraw_reset(wp, &decor_state);
DecorProviders line_providers;
- decor_providers_invoke_win(wp, providers, &line_providers, &provider_err);
+ decor_providers_invoke_win(wp, providers, &line_providers);
redraw_win_signcol(wp);
init_search_hl(wp, &screen_search_hl);
- // Force redraw when width of 'number' or 'relativenumber' column
- // changes.
- 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 = nrwidth;
-
- if (buf->terminal) {
- terminal_check_size(buf->terminal);
+ // Make sure skipcol is valid, it depends on various options and the window
+ // width.
+ if (wp->w_skipcol > 0) {
+ int w = 0;
+ int width1 = wp->w_width_inner - win_col_off(wp);
+ int width2 = width1 + win_col_off2(wp);
+ int add = width1;
+
+ while (w < wp->w_skipcol) {
+ if (w > 0) {
+ add = width2;
+ }
+ w += add;
+ }
+ if (w != wp->w_skipcol) {
+ // always round down, the higher value may not be valid
+ wp->w_skipcol = w - add;
}
+ }
+
+ const int nrwidth_before = wp->w_nrwidth;
+ int nrwidth_new = (wp->w_p_nu || wp->w_p_rnu || *wp->w_p_stc) ? number_width(wp) : 0;
+ // Force redraw when width of 'number' or 'relativenumber' column changes.
+ if (wp->w_nrwidth != nrwidth_new) {
+ type = UPD_NOT_VALID;
+ wp->w_nrwidth = nrwidth_new;
} else if (buf->b_mod_set
&& buf->b_mod_xlines != 0
&& wp->w_redraw_top != 0) {
@@ -1099,8 +1574,6 @@ static void win_update(win_T *wp, DecorProviders *providers)
}
}
if (mod_top != 0 && hasAnyFolding(wp)) {
- linenr_T lnumt, lnumb;
-
// A change in a line can cause lines above it to become folded or
// unfolded. Find the top most buffer line that may be affected.
// If the line was previously folded and displayed, get the first
@@ -1111,8 +1584,8 @@ static void win_update(win_T *wp, DecorProviders *providers)
// the line below it. If there is no valid entry, use w_topline.
// Find the first valid w_lines[] entry below mod_bot. Set lnumb
// to this line. If there is no valid entry, use MAXLNUM.
- lnumt = wp->w_topline;
- lnumb = MAXLNUM;
+ linenr_T lnumt = wp->w_topline;
+ linenr_T lnumb = MAXLNUM;
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) {
@@ -1168,11 +1641,11 @@ static void win_update(win_T *wp, DecorProviders *providers)
// 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) {
- long j = 0;
+ int 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;
+ top_end = j;
break;
}
}
@@ -1205,7 +1678,7 @@ static void win_update(win_T *wp, DecorProviders *providers)
|| (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.
-