diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt index 00f550e7da..29e6151912 100644 --- a/runtime/doc/deprecated.txt +++ b/runtime/doc/deprecated.txt @@ -40,6 +40,11 @@ LUA • vim.F.npcall() Renamed to |vim.npcall()| • vim.F.nil_wrap() Use |vim.npcall()| instead +UI + +• |v:echospace| Its purpose to avoid a "Press ENTER" prompt + no longer applies with |ui2|. + LSP • vim.lsp.util.character_offset() Use to |vim.str_utfindex()| instead diff --git a/runtime/doc/message.txt b/runtime/doc/message.txt index 926b5275a0..d2740624de 100644 --- a/runtime/doc/message.txt +++ b/runtime/doc/message.txt @@ -843,7 +843,6 @@ To avoid overflow ("spill") of the |ui2| messages area: - Set 'cmdheight' to 2 or higher. - Add flags to 'shortmess'. - Reset 'showcmd' and/or 'ruler'. -- Make sure `:echo` text is within |v:echospace| screen cells. *more-prompt* *pager* > f/d/j: screen/page/line down, b/u/k: up, : stop paging diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index eed1f8deca..30e9caeddd 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -44,7 +44,11 @@ DIAGNOSTICS OPTIONS -• todo +• When |ui2| is enabled, since the ruler is no longer a left-aligned box of + fixed width, there is no longer an inconsistent special handling of item + groups in 'rulerformat' to configure that fixed width. An old-style left- + aligned fixed-width ruler can now be achieved by using item groups exactly + as they are documented, e.g. `%-20.20(...%)`. TREESITTER diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index b261a5ca59..3aaf667e5a 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -5168,6 +5168,13 @@ A jump table for the options with a short description can be found at |Q_op|. Example: >vim set rulerformat=%15(%c%V\ %p%%%) < + This looks like an item group, but there are some differences in this + particular case. Most notably, the width is fixed and not a minimum, + and the ruler is left-aligned, whereas the alignment of item groups is + configurable and right-aligned by default. + + When |ui2| is enabled, the ruler no longer has a fixed width and the + item group syntax has no special meaning for 'rulerformat'. *'runtimepath'* *'rtp'* *vimfiles* 'runtimepath' 'rtp' string (default "$XDG_CONFIG_HOME/nvim, diff --git a/runtime/doc/vvars.txt b/runtime/doc/vvars.txt index da5323523b..58d5c5d4dd 100644 --- a/runtime/doc/vvars.txt +++ b/runtime/doc/vvars.txt @@ -128,7 +128,7 @@ v:dying v:echospace Number of screen cells that can be used for an `:echo` message in the last screen line before causing the |hit-enter| prompt - (or "overflow" with |ui2|). + (no longer applicable when |ui2| is enabled). Depends on 'showcmd', 'ruler' and 'columns'. You need to check 'cmdheight' for whether there are full-width lines diff --git a/runtime/lua/vim/_core/ui2/messages.lua b/runtime/lua/vim/_core/ui2/messages.lua index aa3888635b..922c05c44b 100644 --- a/runtime/lua/vim/_core/ui2/messages.lua +++ b/runtime/lua/vim/_core/ui2/messages.lua @@ -126,7 +126,7 @@ local function set_virttext(type, tgt) }) local row = texth.end_row local col = fn.virtcol2col(win, row + 1, texth.end_vcol) - local scol = fn.screenpos(win, row + 1, col).col ---@type integer + local scol = fn.screenpos(win, row + 1, col).endcol ---@type integer if type ~= 'last' then -- Calculate at which column to place the virt_text such that it is at the end @@ -179,7 +179,8 @@ local function set_virttext(type, tgt) -- Crop text on last screen row and find byte offset to place mark at. local vcol = texth.end_vcol - (scol - M.cmd.last_col) - col = vcol <= 0 and 0 or fn.virtcol2col(win, row + 1, vcol) + col = vcol <= 0 and 0 or fn.virtcol2col(win, row + 1, vcol + 1) - 1 + scol = fn.screenpos(win, row + 1, col).endcol M.cmd.prev_msg = mode > 0 and '' or M.cmd.prev_msg M.virt.cmd = mode > 0 and { {}, {} } or M.virt.cmd api.nvim_buf_set_text(ui.bufs.cmd, row, col, row, -1, { mode > 0 and ' ' or '' }) @@ -389,6 +390,13 @@ function M.show_msg(tgt, kind, content, replace_last, append, id) M.virt[tgt][M.virt.idx.dupe][1] = dupe > 0 and { 0, ('(%d)'):format(dupe) } or nil M.virt[tgt][M.virt.idx.spill][1] = tgt == 'cmd' and M.virt.cmd[M.virt.idx.spill][1] or nil set_virttext(tgt --[[@as 'cmd'|'msg']], tgt) + if tgt == 'cmd' then + -- make sure repeated long messages are cropped to keep the ruler intact + -- (if this is not scheduled, it breaks messages during textlock) + vim.schedule(function() + set_virttext('last', 'cmd') + end) + end end -- Reset message state the next event loop iteration. @@ -462,6 +470,7 @@ function M.msg_show(kind, content, replace_last, _, append, id, trigger) M.cmd.last_emsg = (kind == 'emsg' or kind == 'wmsg') and os.time() or M.cmd.last_emsg -- Should clear the search count now, mark itself is cleared by invalidate. M.virt.last[M.virt.idx.search][1] = nil + M.virt.last[M.virt.idx.cmd] = {} end -- When message was emitted below an already expanded cmdline, move and route to pager. tgt = ui.cmd.expand > 0 and 'pager' or tgt diff --git a/runtime/lua/vim/_meta/options.gen.lua b/runtime/lua/vim/_meta/options.gen.lua index e7296cea5d..7b76ddbdc4 100644 --- a/runtime/lua/vim/_meta/options.gen.lua +++ b/runtime/lua/vim/_meta/options.gen.lua @@ -5380,6 +5380,13 @@ vim.go.ru = vim.go.ruler --- set rulerformat=%15(%c%V\ %p%%%) --- ``` --- +--- This looks like an item group, but there are some differences in this +--- particular case. Most notably, the width is fixed and not a minimum, +--- and the ruler is left-aligned, whereas the alignment of item groups is +--- configurable and right-aligned by default. +--- +--- When `ui2` is enabled, the ruler no longer has a fixed width and the +--- item group syntax has no special meaning for 'rulerformat'. --- --- @type string vim.o.rulerformat = "" diff --git a/runtime/lua/vim/_meta/vvars.gen.lua b/runtime/lua/vim/_meta/vvars.gen.lua index 3bc819b5a1..0154045f20 100644 --- a/runtime/lua/vim/_meta/vvars.gen.lua +++ b/runtime/lua/vim/_meta/vvars.gen.lua @@ -129,7 +129,7 @@ vim.v.dying = ... --- Number of screen cells that can be used for an `:echo` message --- in the last screen line before causing the `hit-enter` prompt ---- (or "overflow" with `ui2`). +--- (no longer applicable when `ui2` is enabled). --- --- Depends on 'showcmd', 'ruler' and 'columns'. You need to --- check 'cmdheight' for whether there are full-width lines diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c index 110577e081..0cc60a8fa8 100644 --- a/src/nvim/insexpand.c +++ b/src/nvim/insexpand.c @@ -5241,7 +5241,13 @@ void ins_compl_insert(bool move_cursor, bool insert_prefix) static void ins_compl_show_filename(void) { char *const lead = _("match in file"); - int space = sc_col - vim_strsize(lead) - 2; + + // In the case of ext_messages, `sc_col` is obsolete. Fortunately, long messages are no longer + // disruptive, so truncation could be skipped. But in this particular case, with default + // configuration `showmode cmdheight=1`, a multi-line message is shown partially, and a message + // that fits into one line is not shown at all. It's better to be consistent: it should not depend + // on the exact length of the message whether it is displayed at all. + int space = (ui_has(kUIMessages) ? Columns : sc_col) - vim_strsize(lead) - 2; if (space <= 0) { return; } diff --git a/src/nvim/message.c b/src/nvim/message.c index 1d3fcaa01d..8bc448a331 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -511,7 +511,12 @@ char *msg_strtrunc(const char *s, int force) room = (Rows - msg_row) * Columns - 1; } else { // Use up to 'showcmd' column. - room = (Rows - msg_row - 1) * Columns + sc_col - 1; + // In the case of ui2, we no longer need to avoid the "Press ENTER" prompt, but the message is + // still kept under 1 line to avoid glitches. For example, when a long search term is + // displayed that expands the cmdline, it will be immediately collapsed again when the search + // count is displayed, which leads to flickering on each hit. + int last_row = ui_has(kUIMessages) ? Columns : sc_col - 1; + room = (Rows - msg_row - 1) * Columns + last_row; } if (len > room && room > 0) { // may have up to 18 bytes per cell (6 per char, up to two diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 990b063152..d4ab86e50f 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -7072,6 +7072,13 @@ local options = { Example: >vim set rulerformat=%15(%c%V\ %p%%%) < + This looks like an item group, but there are some differences in this + particular case. Most notably, the width is fixed and not a minimum, + and the ruler is left-aligned, whereas the alignment of item groups is + configurable and right-aligned by default. + + When |ui2| is enabled, the ruler no longer has a fixed width and the + item group syntax has no special meaning for 'rulerformat'. ]=], full_name = 'rulerformat', modelineexpr = true, diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c index 3826f3a5b0..aef2f16eef 100644 --- a/src/nvim/optionstr.c +++ b/src/nvim/optionstr.c @@ -52,6 +52,7 @@ #include "nvim/tag.h" #include "nvim/terminal.h" #include "nvim/types_defs.h" +#include "nvim/ui.h" #include "nvim/vim_defs.h" #include "nvim/window.h" #include "nvim/winfloat.h" @@ -1917,7 +1918,7 @@ static const char *did_set_statustabline_rulerformat(optset_T *args, bool rulerf win_config_float(win, win->w_config); } - if (rulerformat && *s == '%') { + if (rulerformat && !ui_has(kUIMessages) && *s == '%') { // set ru_wid if 'ruf' starts with "%99(" if (*++s == '-') { // ignore a '-' s++; @@ -1932,8 +1933,8 @@ static const char *did_set_statustabline_rulerformat(optset_T *args, bool rulerf errmsg = check_stl_option(p_ruf); } } - } else if (rulerformat || s[0] != '%' || s[1] != '!') { - // check 'statusline', 'winbar', 'tabline' or 'statuscolumn' + } else if (s[0] != '%' || s[1] != '!') { + // check 'statusline', 'rulerformat', 'winbar', 'tabline' or 'statuscolumn' // only if it doesn't start with "%!" errmsg = check_stl_option(s); } diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index 574f16c405..b5d9f32e4d 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -308,7 +308,15 @@ static void win_redr_stl_expr(win_T *wp, bool draw_winbar, bool draw_ruler, bool wp->w_status_click_defs = stl_alloc_click_defs(wp->w_status_click_defs, maxwidth, &wp->w_status_click_defs_size); - if (draw_ruler) { + if (draw_ruler && ui_event) { + stl = p_ruf; + opt_idx = kOptRulerformat; + maxwidth = Columns / 2; + if (!in_status_line) { + fillchar = schar_from_ascii(' '); + group = HLF_MSG; + } + } else if (draw_ruler) { stl = p_ruf; opt_idx = kOptRulerformat; // advance past any leading group spec - implicit in ru_col @@ -549,8 +557,8 @@ void redraw_ruler(void) char rel_pos[RULER_BUF_LEN]; int rel_poslen = get_rel_pos(wp, rel_pos, RULER_BUF_LEN); int n1 = bufferlen + vim_strsize(rel_pos); - if (wp->w_status_height == 0 && !is_stl_global) { // can't use last char of screen - n1++; + if (wp->w_status_height == 0 && !is_stl_global && !ui_has(kUIMessages)) { + n1++; // can't use last char of screen } int this_ru_col = ru_col - (Columns - width); @@ -1464,9 +1472,8 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op } } - // Bound the minimum width at 50. // Make the number negative to denote left alignment of the item - minwid = (minwid > 50 ? 50 : minwid) * (left_align ? -1 : 1); + minwid *= left_align ? -1 : 1; // Denotes the start of a new group if (*fmt_p == '(') { diff --git a/src/nvim/vvars.lua b/src/nvim/vvars.lua index 0cca5124e3..d57c4b4596 100644 --- a/src/nvim/vvars.lua +++ b/src/nvim/vvars.lua @@ -145,7 +145,7 @@ M.vars = { desc = [=[ Number of screen cells that can be used for an `:echo` message in the last screen line before causing the |hit-enter| prompt - (or "overflow" with |ui2|). + (no longer applicable when |ui2| is enabled). Depends on 'showcmd', 'ruler' and 'columns'. You need to check 'cmdheight' for whether there are full-width lines diff --git a/test/functional/ui/messages2_spec.lua b/test/functional/ui/messages2_spec.lua index 7a611ebadb..30acb62925 100644 --- a/test/functional/ui/messages2_spec.lua +++ b/test/functional/ui/messages2_spec.lua @@ -62,7 +62,7 @@ describe('messages2', function() {3: }| ^foo | bar | - 1,1 All| + 1,1 All| ]]) -- Multiple messages in same event loop iteration are appended and shown in full. feed([[q:echo "foo" | echo "bar\nbaz\n"->repeat(&lines)]]) @@ -86,21 +86,21 @@ describe('messages2', function() screen:expect([[ ^ | {1:~ }|*12 - foo 0,0-1 All| + foo 0,0-1 All| ]]) command('echo "foo"') -- Ruler still positioned correctly after dupe message. screen:expect([[ ^ | {1:~ }|*12 - foo(1) 0,0-1 All| + foo(1) 0,0-1 All| ]]) command('echo "foo"') -- Dupe counter increases beyond 1 screen:expect([[ ^ | {1:~ }|*12 - foo(2) 0,0-1 All| + foo(2) 0,0-1 All| ]]) -- No error for ruler virt_text msg_row exceeding buffer length. command([[map Q echo "foo\nbar" ls]]) @@ -117,7 +117,7 @@ describe('messages2', function() screen:expect([[ ^ | {1:~ }|*12 - 0,0-1 All| + 0,0-1 All| ]]) -- g< shows messages from last command feed('g') @@ -128,20 +128,20 @@ describe('messages2', function() ^foo | bar | 1 %a "[No Name]" line 1 | - 1,1 All| + 1,1 All| ]]) -- edit_unputchar() does not clear already updated screen #34515. feed('qixdwi') screen:expect([[ {18:^"} | {1:~ }|*12 - ^R 1,1 All| + ^R 1,1 All| ]]) feed('-') screen:expect([[ ^x | {1:~ }|*12 - 1,1 All| + 1,1 All| ]]) -- Switching tabpage closes expanded cmdline #37659. command('tabnew | echo "foo\nbar"') @@ -158,7 +158,7 @@ describe('messages2', function() {5: + [No Name] }{24: [No Name] }{2: }{24:X}| ^x | {1:~ }|*11 - foo [+1] 1,1 All| + foo [+1] 1,1 All| ]]) -- Don't enter the pager in insert mode. command('tabonly | call nvim_echo([["foo\n"]]->repeat(&lines), 1, {}) | startinsert') @@ -174,7 +174,7 @@ describe('messages2', function() | ^x | {1:~ }|*11 - foo [+14] 2,1 All| + foo [+14] 2,1 All| ]]) feed('') -- First multiline message expands cmdline, additional message updates spill indicator. @@ -192,7 +192,7 @@ describe('messages2', function() screen:expect([[ ^foo | foo |*12 - 1,1 Top| + 1,1 Top| ]]) -- Changing 'laststatus' reveals the global statusline with a pager height -- exceeding the available lines: #38008. @@ -1092,20 +1092,129 @@ describe('messages2', function() end) it('search count is cleared', function() - command('set ruler shortmess-=S | call setline(1, ["foo", "bar"])') + command('set ruler showcmd shortmess-=S | call setline(1, ["foo", "bar"])') feed('/foo') - screen:expect([[ + local search_count = [[ {10:^foo} | bar | {1:~ }|*11 - /foo W [1/1] 1,1 All| - ]]) + /foo W [1/1] 1,1 All| + ]] + screen:expect(search_count) feed('j') screen:expect([[ {10:foo} | ^bar | {1:~ }|*11 - 2,1 All| + 2,1 All| + ]]) + feed('n') + screen:expect(search_count) + command('echo "-"->repeat(&columns)') + screen:expect([[ + {10:^foo} | + bar | + {1:~ }|*11 + -----------------------------------1,1 All| + ]]) + end) + + it('crops long messages to make place for ruler', function() + command('set noruler | echo "-"->repeat(&columns)') + screen:expect([[ + ^ | + {1:~ }|*12 + -----------------------------------------------------| + ]]) + feed('') + command('set ruler showcmd | echo "-"->repeat(&columns)') + screen:expect([[ + ^ | + {1:~ }|*12 + -----------------------------------0,0-1 All| + ]]) + feed('') + -- message with multibyte characters + command('echo "∙"->repeat(&columns)') + screen:expect([[ + ^ | + {1:~ }|*12 + ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙0,0-1 All| + ]]) + feed('') + -- message with multicell characters + command('echo "-".."🙂"->repeat(&columns/2-1)') + screen:expect([[ + ^ | + {1:~ }|*12 + -🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂0,0-1 All| + ]]) + feed('') + command('echo "🙂"->repeat(&columns/2)') + screen:expect([[ + ^ | + {1:~ }|*12 + 🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂 0,0-1 All| + ]]) + feed('') + -- when cmdheight>1, only the last line is cropped + command('set cmdheight=2 | redraw | echo "-"->repeat(2*&columns)') + screen:expect([[ + ^ | + {1:~ }|*11 + -----------------------------------------------------| + -----------------------------------0,0-1 All| + ]]) + feed('') + command('set cmdheight=2 showbreak=> | redraw | echo "-"->repeat(2*&columns-1)') + screen:expect([[ + ^ | + {1:~ }|*11 + -----------------------------------------------------| + {1:>}----------------------------------0,0-1 All| + ]]) + command('set showbreak&') + feed('') + -- ruler is not shown after dismissing expanded cmdline with key press -> no need to crop + command('set cmdheight=1 | redraw | echo "-"->repeat(2*&columns)') + feed('k') + screen:expect([[ + ^ | + {1:~ }|*12 + -----------------------------------------------------| + ]]) + feed('') + -- messages are deferred until showcmd is cleared -> no need to crop for showcmd + command('lua vim.defer_fn(function() vim.cmd[[echo "-"->repeat(&columns)]] end, 50)') + feed('g') + screen:expect([[ + ^ | + {1:~ }|*12 + g 0,0-1 All| + ]]) + screen:sleep(100) + feed('') + screen:expect([[ + ^ | + {1:~ }|*12 + -----------------------------------0,0-1 All| + ]]) + feed('') + -- message with repetition indicator keeps ruler intact + command('echo "-"->repeat(&columns)') + command('echo "-"->repeat(&columns)') + screen:expect([[ + ^ | + {1:~ }|*12 + --------------------------------(1)0,0-1 All| + ]]) + feed('') + -- when the ruler is configured smaller, there is more space for messages + command('set rulerformat=%l | redraw | echo "-"->repeat(&columns)') + screen:expect([[ + ^ | + {1:~ }|*12 + ----------------------------------------------------0| ]]) end) end) diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua index 89062b9291..7e3928fc35 100644 --- a/test/functional/ui/messages_spec.lua +++ b/test/functional/ui/messages_spec.lua @@ -941,7 +941,7 @@ describe('ui/ext_messages', function() ^ | {1:~ }|*4 ]], - ruler = { { '0,0-1 All', 9, 'MsgArea' } }, + ruler = { { '0,0-1 All', 9, 'MsgArea' } }, }) command('hi clear MsgArea') feed('i') @@ -951,7 +951,7 @@ describe('ui/ext_messages', function() {1:~ }|*4 ]], showmode = { { '-- INSERT --', 5, 'ModeMsg' } }, - ruler = { { '0,1 All', 'MsgArea' } }, + ruler = { { '0,1 All', 'MsgArea' } }, } feed('abcde12345') screen:expect { @@ -960,7 +960,7 @@ describe('ui/ext_messages', function() 1234^5 | {1:~ }|*3 ]], - ruler = { { '2,5 All', 'MsgArea' } }, + ruler = { { '2,5 All', 'MsgArea' } }, } feed('d') screen:expect { @@ -970,7 +970,7 @@ describe('ui/ext_messages', function() {1:~ }|*3 ]], showcmd = { { 'd' } }, - ruler = { { '2,5 All', 'MsgArea' } }, + ruler = { { '2,5 All', 'MsgArea' } }, } feed('^') screen:expect { @@ -979,7 +979,7 @@ describe('ui/ext_messages', function() ^12345 | {1:~ }|*3 ]], - ruler = { { '2,1 All', 'MsgArea' } }, + ruler = { { '2,1 All', 'MsgArea' } }, } feed('k2l') screen:expect({ @@ -990,7 +990,7 @@ describe('ui/ext_messages', function() ]], showmode = { { '-- VISUAL BLOCK --', 5, 'ModeMsg' } }, showcmd = { { '2x3' } }, - ruler = { { '1,3 All', 'MsgArea' } }, + ruler = { { '1,3 All', 'MsgArea' } }, }) feed('od') screen:expect { @@ -1000,7 +1000,7 @@ describe('ui/ext_messages', function() {1:~ }|*3 ]], showcmd = { { 'd' } }, - ruler = { { '2,1 All', 'MsgArea' } }, + ruler = { { '2,1 All', 'MsgArea' } }, } feed('i') screen:expect { @@ -1010,7 +1010,7 @@ describe('ui/ext_messages', function() {1:~ }|*3 ]], showcmd = { { 'di' } }, - ruler = { { '2,1 All', 'MsgArea' } }, + ruler = { { '2,1 All', 'MsgArea' } }, } feed('w') screen:expect { @@ -1019,7 +1019,7 @@ describe('ui/ext_messages', function() ^ | {1:~ }|*3 ]], - ruler = { { '2,0-1 All', 'MsgArea' } }, + ruler = { { '2,0-1 All', 'MsgArea' } }, } command('set rulerformat=Foo%#ErrorMsg#Bar') screen:expect({