diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index c9f978471f9b15..5d227a702c8929 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1320,6 +1320,9 @@ A jump table for the options with a short description can be found at |Q_op|. The value of this option is stored with the tab page, so that each tab page can have a different value. + When 'cmdheight' is zero, it disables echo area and all outputs need + |hit-enter| prompt. + *'cmdwinheight'* *'cwh'* 'cmdwinheight' 'cwh' number (default 7) global @@ -4817,9 +4820,11 @@ A jump table for the options with a short description can be found at |Q_op|. 45% relative position in the file If 'rulerformat' is set, it will determine the contents of the ruler. Each window has its own ruler. If a window has a status line, the - ruler is shown there. Otherwise it is shown in the last line of the - screen. If the statusline is given by 'statusline' (i.e. not empty), - this option takes precedence over 'ruler' and 'rulerformat' + ruler is shown there. If a window doesn't have a status line and + 'cmdheight' is 0, the ruler is not shown. Otherwise it is shown in + the last line of the screen. If the statusline is given by + 'statusline' (i.e. not empty), this option takes precedence over + 'ruler' and 'rulerformat'. If the number of characters displayed is different from the number of bytes in the text (e.g., for a TAB or a multibyte character), both the text column (byte number) and the screen column are shown, @@ -5525,6 +5530,7 @@ A jump table for the options with a short description can be found at |Q_op|. global Show (partial) command in the last line of the screen. Set this option off if your terminal is slow. + The option is disabled if 'cmdheight' is 0. In Visual mode the size of the selected area is shown: - When selecting characters within a line, the number of characters. If the number of bytes is different it is also displayed: "2-6" @@ -5571,6 +5577,7 @@ A jump table for the options with a short description can be found at |Q_op|. global If in Insert, Replace or Visual mode put a message on the last line. The |hl-ModeMsg| highlight group determines the highlighting. + The option is disabled if 'cmdheight' is 0. *'showtabline'* *'stal'* 'showtabline' 'stal' number (default 1) diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 94ce1ec4955f2b..46cc4b5c7ecd6f 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -108,11 +108,9 @@ typedef enum { kCmdRedrawAll, } CmdRedraw; -/* - * Variables shared between getcmdline(), redrawcmdline() and others. - * These need to be saved when using CTRL-R |, that's why they are in a - * structure. - */ +// Variables shared between getcmdline(), redrawcmdline() and others. +// These need to be saved when using CTRL-R |, that's why they are in a +// structure. struct cmdline_info { char_u *cmdbuff; // pointer to command line buffer int cmdbufflen; // length of cmdbuff @@ -139,6 +137,7 @@ struct cmdline_info { bool special_shift; ///< shift of last putcmdline char CmdRedraw redraw_state; ///< needed redraw for external cmdline }; + /// Last value of prompt_id, incremented when doing new prompt static unsigned last_prompt_id = 0; @@ -689,6 +688,14 @@ static void finish_incsearch_highlighting(int gotesc, incsearch_state_T *s, bool /// @param init_ccline clear ccline first static uint8_t *command_line_enter(int firstc, long count, int indent, bool init_ccline) { + bool cmdheight0 = p_ch < 1 && !ui_has(kUIMessages) && vpeekc() == NUL; + + if (cmdheight0) { + // If cmdheight is 0, cmdheight must be set to 1 when we enter command line. + set_option_value("ch", 1L, NULL, 0); + redraw_statuslines(); + } + // can be invoked recursively, identify each level static int cmdline_level = 0; cmdline_level++; @@ -976,6 +983,11 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init ccline.cmdbuff = NULL; } + if (cmdheight0) { + // Restore cmdheight + set_option_value("ch", 0L, NULL, 0); + } + return p; } @@ -2670,6 +2682,12 @@ char *getcmdline_prompt(const char firstc, const char *const prompt, const int a return ret; } +// Return current cmdline prompt +char_u *get_cmdprompt(void) +{ + return ccline.cmdprompt; +} + /* * Return TRUE when the text must not be changed and we can't switch to * another window or buffer. Used when editing the command line etc. @@ -3777,7 +3795,7 @@ void redrawcmd(void) msg_no_more = TRUE; draw_cmdline(0, ccline.cmdlen); msg_clr_eos(); - msg_no_more = FALSE; + msg_no_more = false; ccline.cmdspos = cmd_screencol(ccline.cmdpos); diff --git a/src/nvim/message.c b/src/nvim/message.c index f250f6de5885b9..23ceb0ba4c9daa 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -18,6 +18,7 @@ #include "nvim/eval.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" +#include "nvim/ex_getln.h" #include "nvim/fileio.h" #include "nvim/func_attr.h" #include "nvim/garray.h" @@ -163,6 +164,7 @@ void msg_grid_validate(void) { grid_assign_handle(&msg_grid); bool should_alloc = msg_use_grid(); + int max_rows = Rows - p_ch; if (should_alloc && (msg_grid.rows != Rows || msg_grid.cols != Columns || !msg_grid.chars)) { // TODO(bfredl): eventually should be set to "invalid". I e all callers @@ -174,7 +176,7 @@ void msg_grid_validate(void) msg_grid.dirty_col = xcalloc(Rows, sizeof(*msg_grid.dirty_col)); // Tricky: allow resize while pager is active - int pos = msg_scrolled ? msg_grid_pos : Rows - p_ch; + int pos = msg_scrolled ? msg_grid_pos : max_rows; ui_comp_put_grid(&msg_grid, pos, 0, msg_grid.rows, msg_grid.cols, false, true); ui_call_grid_resize(msg_grid.handle, msg_grid.cols, msg_grid.rows); @@ -184,7 +186,7 @@ void msg_grid_validate(void) msg_grid.focusable = false; msg_grid_adj.target = &msg_grid; if (!msg_scrolled) { - msg_grid_set_pos(Rows - p_ch, false); + msg_grid_set_pos(max_rows, false); } } else if (!should_alloc && msg_grid.chars) { ui_comp_remove_grid(&msg_grid); @@ -195,8 +197,8 @@ void msg_grid_validate(void) msg_grid_adj.row_offset = 0; msg_grid_adj.target = &default_grid; redraw_cmdline = true; - } else if (msg_grid.chars && !msg_scrolled && msg_grid_pos != Rows - p_ch) { - msg_grid_set_pos(Rows - p_ch, false); + } else if (msg_grid.chars && !msg_scrolled && msg_grid_pos != max_rows) { + msg_grid_set_pos(max_rows, false); } if (msg_grid.chars && cmdline_row < msg_grid_pos) { @@ -1386,7 +1388,9 @@ void msg_start(void) need_fileinfo = false; } - if (need_clr_eos) { + bool no_msg_area = !ui_has(kUIMessages) && p_ch < 1; + + if (need_clr_eos || (no_msg_area && redrawing_cmdline)) { // Halfway an ":echo" command and getting an (error) message: clear // any text from the command. need_clr_eos = false; @@ -1395,10 +1399,11 @@ void msg_start(void) if (!msg_scroll && full_screen) { // overwrite last message msg_row = cmdline_row; - msg_col = - cmdmsg_rl ? Columns - 1 : - 0; - } else if (msg_didout) { // start message on next line + msg_col = cmdmsg_rl ? Columns - 1 : 0; + if (no_msg_area && get_cmdprompt() == NULL) { + msg_row -= 1; + } + } else if (msg_didout || no_msg_area) { // start message on next line msg_putchar('\n'); did_return = true; cmdline_row = msg_row; @@ -3055,7 +3060,7 @@ void repeat_message(void) /// Skip this when ":silent" was used, no need to clear for redirection. void msg_clr_eos(void) { - if (msg_silent == 0) { + if (msg_silent == 0 && p_ch > 0) { msg_clr_eos_force(); } } @@ -3077,10 +3082,11 @@ void msg_clr_eos_force(void) msg_row = msg_grid_pos; } - grid_fill(&msg_grid_adj, msg_row, msg_row + 1, msg_startcol, msg_endcol, ' ', - ' ', HL_ATTR(HLF_MSG)); - grid_fill(&msg_grid_adj, msg_row + 1, Rows, 0, Columns, ' ', ' ', - HL_ATTR(HLF_MSG)); + grid_fill(&msg_grid_adj, msg_row, msg_row + 1, msg_startcol, msg_endcol, + ' ', ' ', HL_ATTR(HLF_MSG)); + if (p_ch > 0) { + grid_fill(&msg_grid_adj, msg_row + 1, Rows, 0, Columns, ' ', ' ', HL_ATTR(HLF_MSG)); + } redraw_cmdline = true; // overwritten the command line if (msg_row < Rows - 1 || msg_col == (cmdmsg_rl ? Columns : 0)) { diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 3603e11f5da53b..87a9e1e7657284 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -2747,6 +2747,10 @@ void pop_showcmd(void) static void display_showcmd(void) { + if (p_ch < 1 && !ui_has(kUIMessages)) { + return; + } + int len; len = (int)STRLEN(showcmd_buf); showcmd_is_clear = (len == 0); diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 7dcf4c433eaec9..0f202b2fd3800d 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -6041,6 +6041,10 @@ void cursor_pos_info(dict_T *dict) // Don't shorten this message, the user asked for it. p = p_shm; p_shm = (char_u *)""; + if (p_ch < 1) { + msg_start(); + msg_scroll = true; + } msg((char *)IObuff); p_shm = p; } diff --git a/src/nvim/option.c b/src/nvim/option.c index 35736fd5eb1109..ca9c7468cf4758 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -4365,7 +4365,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, errmsg = e_positive; } } else if (pp == &p_ch) { - int minval = ui_has(kUIMessages) ? 0 : 1; + int minval = 0; if (value < minval) { errmsg = e_positive; } diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 4142409ee02b87..2ee7cd44f7a8b0 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -6132,6 +6132,10 @@ void unshowmode(bool force) // Clear the mode message. void clearmode(void) { + if (p_ch <= 0 && !ui_has(kUIMessages)) { + return; + } + const int save_msg_row = msg_row; const int save_msg_col = msg_col; @@ -6500,7 +6504,6 @@ static void win_redr_ruler(win_T *wp, bool always) if (*p_ruf) { int save_called_emsg = called_emsg; - called_emsg = false; win_redr_custom(wp, false, true); if (called_emsg) { @@ -6558,6 +6561,10 @@ static void win_redr_ruler(win_T *wp, bool always) off = 0; } + if (!part_of_status && p_ch < 1 && !ui_has(kUIMessages)) { + 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) { @@ -6770,7 +6777,7 @@ void screen_resize(int width, int height) Columns = width; check_shellsize(); int max_p_ch = Rows - min_rows() + 1; - if (!ui_has(kUIMessages) && p_ch > max_p_ch) { + if (!ui_has(kUIMessages) && p_ch > 0 && p_ch > max_p_ch) { p_ch = max_p_ch ? max_p_ch : 1; } height = Rows; diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim index 798122dc5d380b..0fe5fc59eb5351 100644 --- a/src/nvim/testdir/test_window_cmd.vim +++ b/src/nvim/testdir/test_window_cmd.vim @@ -887,6 +887,7 @@ func Test_floatwin_splitmove() endfunc func Test_window_resize() + throw 'Skipped: Nvim supports cmdheight=0' " Vertical :resize (absolute, relative, min and max size). vsplit vert resize 8 @@ -1028,9 +1029,14 @@ func Test_win_move_statusline() call assert_equal(h0, winheight(0)) call assert_equal(1, &cmdheight) endfor + " Nvim supports cmdheight=0 + set cmdheight=0 call assert_true(win_move_statusline(0, 1)) - call assert_equal(h0, winheight(0)) - call assert_equal(1, &cmdheight) + "call assert_equal(h0, winheight(0)) + "call assert_equal(1, &cmdheight) + call assert_equal(h0 + 1, winheight(0)) + call assert_equal(0, &cmdheight) + set cmdheight& " check win_move_statusline from bottom window on top window ID let id = win_getid(1) for offset in range(5) diff --git a/src/nvim/window.c b/src/nvim/window.c index e09a7cd97e95f0..e08db2261bb075 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -907,7 +907,7 @@ void ui_ext_win_position(win_T *wp) int comp_col = (int)col - (east ? wp->w_width_outer : 0); comp_row += grid->comp_row; comp_col += grid->comp_col; - comp_row = MAX(MIN(comp_row, Rows - wp->w_height_outer - 1), 0); + comp_row = MAX(MIN(comp_row, Rows - wp->w_height_outer - (p_ch > 0 ? 1 : 0)), 0); comp_col = MAX(MIN(comp_col, Columns - wp->w_width_outer), 0); wp->w_winrow = comp_row; wp->w_wincol = comp_col; @@ -1144,6 +1144,9 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) if (flags & WSP_ROOM) { needed += p_wh - wmh1 + oldwin->w_winbar_height; } + if (p_ch < 1) { + needed += 1; // Adjust for cmdheight=0. + } if (flags & (WSP_BOT | WSP_TOP)) { minheight = frame_minheight(topframe, NOWIN) + need_status; available = topframe->fr_height; @@ -5501,7 +5504,7 @@ void win_setheight_win(int height, win_T *win) } } cmdline_row = row; - p_ch = MAX(Rows - cmdline_row, ui_has(kUIMessages) ? 0 : 1); + p_ch = MAX(Rows - cmdline_row, 0); curtab->tp_ch_used = p_ch; msg_row = row; msg_col = 0; @@ -5949,9 +5952,7 @@ void win_drag_status_line(win_T *dragwin, int offset) up = false; // Only dragging the last status line can reduce p_ch. room = Rows - cmdline_row; - if (curfr->fr_next == NULL) { - room -= 1; - } else { + if (curfr->fr_next != NULL) { room -= p_ch + global_stl_height(); } if (room < 0) { @@ -6008,7 +6009,7 @@ void win_drag_status_line(win_T *dragwin, int offset) clear_cmdline = true; } cmdline_row = row; - p_ch = MAX(Rows - cmdline_row, ui_has(kUIMessages) ? 0 : 1); + p_ch = MAX(Rows - cmdline_row, 0); curtab->tp_ch_used = p_ch; redraw_all_later(SOME_VALID); showmode(); diff --git a/test/functional/options/num_options_spec.lua b/test/functional/options/num_options_spec.lua index 4754c14f5b501f..f343e2da754c83 100644 --- a/test/functional/options/num_options_spec.lua +++ b/test/functional/options/num_options_spec.lua @@ -54,7 +54,7 @@ describe(':set validation', function() should_fail('iminsert', 3, 'E474') should_fail('imsearch', 3, 'E474') should_fail('titlelen', -1, 'E487') - should_fail('cmdheight', 0, 'E487') + should_fail('cmdheight', -1, 'E487') should_fail('updatecount', -1, 'E487') should_fail('textwidth', -1, 'E487') should_fail('tabstop', 0, 'E487') diff --git a/test/functional/ui/cmdline_spec.lua b/test/functional/ui/cmdline_spec.lua index 3b273fd2294104..0aee34d367a6a3 100644 --- a/test/functional/ui/cmdline_spec.lua +++ b/test/functional/ui/cmdline_spec.lua @@ -5,6 +5,8 @@ local source = helpers.source local command = helpers.command local assert_alive = helpers.assert_alive local uname = helpers.uname +local eval = helpers.eval +local eq = helpers.eq local function new_screen(opt) local screen = Screen.new(25, 5) @@ -858,3 +860,156 @@ describe("cmdline height", function() assert_alive() end) end) + +describe('cmdheight=0', function() + local screen + before_each(function() + clear() + screen = Screen.new(25, 5) + screen:attach() + end) + + it("with cmdheight=1 noruler laststatus=2", function() + command("set cmdheight=1 noruler laststatus=2") + screen:expect{grid=[[ + ^ | + ~ | + ~ | + [No Name] | + | + ]]} + end) + + it("with cmdheight=0 noruler laststatus=2", function() + command("set cmdheight=0 noruler laststatus=2") + screen:expect{grid=[[ + ^ | + ~ | + ~ | + ~ | + [No Name] | + ]]} + end) + + it("with cmdheight=0 ruler laststatus=0", function() + command("set cmdheight=0 ruler laststatus=0") + screen:expect{grid=[[ + ^ | + ~ | + ~ | + ~ | + ~ | + ]]} + end) + + it("with cmdheight=0 ruler laststatus=0", function() + command("set cmdheight=0 noruler laststatus=0 showmode") + feed('i') + screen:expect{grid=[[ + ^ | + ~ | + ~ | + ~ | + ~ | + ]], showmode={}} + feed('') + eq(0, eval('&cmdheight')) + end) + + it("with showmode", function() + command("set cmdheight=1 noruler laststatus=0 showmode") + feed('i') + screen:expect{grid=[[ + ^ | + ~ | + ~ | + ~ | + -- INSERT -- | + ]]} + feed('') + eq(1, eval('&cmdheight')) + end) + + it("when using command line", function() + command("set cmdheight=0 noruler laststatus=0") + feed(':') + screen:expect{grid=[[ + | + ~ | + ~ | + ~ | + :^ | + ]]} + eq(1, eval('&cmdheight')) + feed('') + screen:expect{grid=[[ + ^ | + ~ | + ~ | + ~ | + ~ | + ]], showmode={}} + eq(0, eval('&cmdheight')) + end) + + it("when using input()", function() + command("set cmdheight=0 noruler laststatus=0") + feed(':call input("foo >")') + screen:expect{grid=[[ + | + ~ | + ~ | + ~ | + foo >^ | + ]]} + eq(1, eval('&cmdheight')) + feed('') + screen:expect{grid=[[ + ^ | + ~ | + ~ | + ~ | + ~ | + ]], showmode={}} + eq(0, eval('&cmdheight')) + end) + + it("with winbar and splits", function() + command("set cmdheight=0 noruler laststatus=3 winbar=foo") + feed(':split') + screen:expect{grid=[[ + foo | + | + E36: Not enough room | + Press ENTER or type comma| + nd to continue^ | + ]]} + feed('') + screen:expect{grid=[[ + foo | + ^ | + ~ | + ~ | + [No Name] | + ]]} + feed(':') + screen:expect{grid=[[ + foo | + | + ~ | + [No Name] | + :^ | + ]]} + feed('') + screen:expect{grid=[[ + foo | + ^ | + ~ | + ~ | + [No Name] | + ]], showmode={}} + eq(0, eval('&cmdheight')) + + assert_alive() + end) +end) diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua index 7c789a9e585cc9..e9c0e1b2a15f32 100644 --- a/test/functional/ui/messages_spec.lua +++ b/test/functional/ui/messages_spec.lua @@ -737,7 +737,6 @@ describe('ui/ext_messages', function() ]]) eq(0, eval('&cmdheight')) - -- normally this would be an error feed(':set cmdheight=0') screen:expect{grid=[[ ^ | diff --git a/test/functional/ui/statusline_spec.lua b/test/functional/ui/statusline_spec.lua index 3e1b284856cd09..2733b9dd4a8dd8 100644 --- a/test/functional/ui/statusline_spec.lua +++ b/test/functional/ui/statusline_spec.lua @@ -313,5 +313,9 @@ describe('global statusline', function() eq(2, meths.get_option('cmdheight')) meths.input_mouse('left', 'drag', '', 0, 14, 10) eq(1, meths.get_option('cmdheight')) + meths.input_mouse('left', 'drag', '', 0, 15, 10) + eq(0, meths.get_option('cmdheight')) + meths.input_mouse('left', 'drag', '', 0, 14, 10) + eq(1, meths.get_option('cmdheight')) end) end)