From dc21aa07f7bbe50780e0f18b32f357242bd7c6f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Hoffmann?= Date: Sat, 25 Jul 2026 14:48:14 +0200 Subject: [PATCH 1/5] fix(ruler): no window-local highlights for last line 'rulerformat' Fixes #38777 in case 'rulerformat' is set. See PR 38879. Original message: Problem: When the 'ruler' is in the last line of the screen, it takes local highlight definitions of the current window, tripping an assert (since c1648cf). Solution: Don't use window-local highlight definitions when the ruler is not part of a statusline. --- src/nvim/statusline.c | 2 +- test/functional/ui/statusline_spec.lua | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index 853f2e8a8a..51bafb4238 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -348,7 +348,7 @@ static void win_redr_stl_expr(win_T *wp, bool draw_winbar, bool draw_ruler, bool opt_scope = ((*wp->w_p_stl != NUL) ? OPT_LOCAL : 0); } - attr = win_hl_attr(wp, (int)group); + attr = draw_ruler && !in_status_line ? HL_ATTR(group) : win_hl_attr(wp, (int)group); if (!wp->w_floating && in_status_line && !is_stl_global) { col += wp->w_wincol; } diff --git a/test/functional/ui/statusline_spec.lua b/test/functional/ui/statusline_spec.lua index 0b676be6f9..c70015b78f 100644 --- a/test/functional/ui/statusline_spec.lua +++ b/test/functional/ui/statusline_spec.lua @@ -818,6 +818,15 @@ describe('statusline', function() {2:[No Name] 0,0-1 All}| 0,0-1 All | ]]) + command('set rulerformat=%17(%l,%c%V%=%P%)') + screen:expect([[ + | + {1:~}{15:^ }{1: }| + {1:~ }|*4 + {2:[No Name] 0,0-1 All}| + 0,0-1 All | + ]]) + command('set rulerformat&') api.nvim_win_close(win, true) screen:expect([[ ^ | From 81261804a8e335f9a97f0c4b2e8e70c59593ccca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Hoffmann?= Date: Sat, 1 Aug 2026 12:29:54 +0200 Subject: [PATCH 2/5] fix(ruler): always clear ruler when disabled Problem: the ruler is not cleared in the following circumstances: - ui1 is running - 'rulerformat' is configured - the default ruler was not previously visible in the last line, for example because 'rulerformat' is configured in init.lua - 'ruler' is disabled without using the command-line, for example via key-binding (entering the command-line would clear the ruler) The corresponding test case did not fail because 'rulerformat' was set while the default ruler was shown. Solution: use a dedicated variable for tracking whether the ui1 ruler was previously shown in the last line. `did_ruler_col` is now only used for setting `msg_col`, which is not implemented in the case where 'rulerformat' is configured. Reorder the test code to make the individual checks more independent from each other, and to reflect the future where 'rulerformat' will never be empty. Note that the check where 'rulerformat' was configured relied on the default ruler not being cleared and a stale "0," still being shown in front of the new ruler - this is also fixed with ui2. --- src/nvim/statusline.c | 6 ++++++ test/functional/ui/messages_spec.lua | 32 ++++++++++++++-------------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index 51bafb4238..01726a0a8f 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -482,6 +482,7 @@ void win_redr_winbar(win_T *wp) void redraw_ruler(void) { + static bool did_show_ruler = false; static int did_ruler_col = -1; win_T *wp = !curwin->w_config.hide && curwin->w_status_height == 0 ? curwin : lastwin_nofloating(NULL); @@ -495,8 +496,11 @@ void redraw_ruler(void) } else if (did_ruler_col > 0) { msg_col = did_ruler_col; msg_row = Rows - 1; + } + if (did_show_ruler && !ui_has(kUIMessages)) { msg_clr_eos(); } + did_show_ruler = false; did_ruler_col = -1; return; } @@ -516,6 +520,7 @@ void redraw_ruler(void) bool part_of_status = wp->w_status_height || is_stl_global; if (*p_ruf && (p_ch > 0 || (ui_has(kUIMessages) && !part_of_status))) { win_redr_stl_expr(wp, false, true, ui_has(kUIMessages)); + did_show_ruler = !ui_has(kUIMessages); return; } @@ -605,6 +610,7 @@ void redraw_ruler(void) } grid_line_start(&msg_grid_adj, Rows - 1); + did_show_ruler = true; did_ruler_col = off + this_ru_col; int w = grid_line_puts(did_ruler_col, buffer, -1, attr); grid_line_fill(did_ruler_col + w, off + width, fillchar, attr); diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua index e63068fe56..29119bda3f 100644 --- a/test/functional/ui/messages_spec.lua +++ b/test/functional/ui/messages_spec.lua @@ -1890,7 +1890,22 @@ describe('ui/builtin messages', function() end) it('supports ruler with laststatus=0', function() - command('set ruler laststatus=0') + command('set laststatus=0 ruler rulerformat=%-15(%c%V\\ %p%%%)') + screen:expect([[ + ^ | + {1:~ }|*5 + 0-1 100% | + ]]) + + -- Ruler is cleared when it is no longer drawn. + command('set noruler') + screen:expect([[ + ^ | + {1:~ }|*5 + | + ]]) + + command('set ruler rulerformat&') screen:expect([[ ^ | {1:~ }|*5 @@ -1903,21 +1918,6 @@ describe('ui/builtin messages', function() {1:~ }|*5 {101: 0,0-1 All }| ]]) - - command('set rulerformat=%15(%c%V\\ %p%%%)') - screen:expect([[ - ^ | - {1:~ }|*5 - {101: 0,0-1 100% }| - ]]) - - -- Ruler is cleared when it is no longer drawn. - command('set noruler') - screen:expect([[ - ^ | - {1:~ }|*5 - {101: }| - ]]) end) it('supports echo with CRLF line separators', function() From 259e6fa9ccb7354cb541cf77c64c0f8196cbe2ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Hoffmann?= Date: Sat, 1 Aug 2026 18:01:00 +0200 Subject: [PATCH 3/5] fix(ruler)!: consistent width in last line and statusline Problem: Traditionally, the ruler in the last line is one cell shorter than in the statusline, leaving the last cell of the screen blank. According to code comments, this is in order to prevent unwanted scrolling on "some" (unspecified, but presumably ancient) terminals. Berkeley vi is more specific in its `vs_modeline` function: dumb terminals with hardware scroll, SunOS 4.1.1 and Ultrix 4.2 curses. (n)curses still has a similar limitation in `(w)addstr`, but apparently only for historical reasons. Maintaining the different widths leads to awkward inconsistencies when the ruler is configured with 'rulerformat', except for the special case where it contains a top-level `%=`. Shifting the ruler in the last line to the left would be a solution, but the empty cell at the end doesn't seem to be relevant anymore. Solution: extend the ruler in the last line all the way to the right edge of the screen, just like in the statusline. The exact same amount of place will be available to the rest of the UI as before. BREAKING CHANGE: - the default ruler width is now 18 cells - the last cell of the screen is no longer empty Closes #41076 --- runtime/doc/news.txt | 6 ++++++ runtime/doc/options.txt | 2 +- runtime/lua/vim/_meta/options.gen.lua | 2 +- src/nvim/drawscreen.c | 4 ++-- src/nvim/options.lua | 2 +- src/nvim/statusline.c | 6 ------ test/functional/api/window_spec.lua | 4 ++-- test/functional/editor/completion_spec.lua | 2 +- .../swapfile_preserve_recover_spec.lua | 2 +- test/functional/legacy/cmdline_spec.lua | 2 +- test/functional/legacy/excmd_spec.lua | 4 ++-- test/functional/legacy/listlbr_utf8_spec.lua | 8 ++++---- test/functional/legacy/normal_spec.lua | 2 +- test/functional/legacy/window_cmd_spec.lua | 2 +- test/functional/terminal/tui_spec.lua | 4 ++-- test/functional/terminal/window_spec.lua | 6 +++--- test/functional/ui/float_spec.lua | 12 +++++------ test/functional/ui/messages_spec.lua | 6 +++--- test/functional/ui/multibyte_spec.lua | 20 +++++++++---------- test/functional/ui/screen_basic_spec.lua | 2 +- test/functional/ui/statusline_spec.lua | 16 +++++---------- 21 files changed, 54 insertions(+), 60 deletions(-) diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 0fd7f18e27..3afd74c260 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -163,6 +163,12 @@ TREESITTER • todo +UI + +• The bottom right cell of the screen is no longer left empty when the ruler + is shown. This was originally intended to prevent unwanted scrolling on + quirky terminals. + VIMSCRIPT • Removed: ctxget(), ctxpop(), ctxpush(), ctxset(), ctxsize(). Use diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 34a03b6fdb..47b36501db 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -5212,7 +5212,7 @@ A jump table for the options with a short description can be found at |Q_op|. The format of this option is like that of 'statusline'. This option cannot be set in a modeline when 'modelineexpr' is off. - The default ruler width is 17 characters. To make the ruler 15 + The default ruler width is 18 characters. To make the ruler 15 characters wide, put "%15(" at the start and "%)" at the end. Example: >vim set rulerformat=%15(%c%V\ %p%%%) diff --git a/runtime/lua/vim/_meta/options.gen.lua b/runtime/lua/vim/_meta/options.gen.lua index 898d57edc2..25f2085916 100644 --- a/runtime/lua/vim/_meta/options.gen.lua +++ b/runtime/lua/vim/_meta/options.gen.lua @@ -5403,7 +5403,7 @@ vim.go.ru = vim.go.ruler --- The format of this option is like that of 'statusline'. --- This option cannot be set in a modeline when 'modelineexpr' is off. --- ---- The default ruler width is 17 characters. To make the ruler 15 +--- The default ruler width is 18 characters. To make the ruler 15 --- characters wide, put "%15(" at the start and "%)" at the end. --- Example: --- diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c index a88790153c..0a0e8b6703 100644 --- a/src/nvim/drawscreen.c +++ b/src/nvim/drawscreen.c @@ -1125,7 +1125,7 @@ static void recording_mode(int hl_id) msg_puts_hl(s, hl_id, false); } -#define COL_RULER 17 // columns needed by standard ruler +#define COL_RULER 18 // 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. @@ -1138,7 +1138,7 @@ void comp_col(void) sc_col = 0; ru_col = 0; if (p_ru) { - ru_col = (ru_wid ? ru_wid : COL_RULER) + 1; + ru_col = (ru_wid ? ru_wid : COL_RULER); // no last status line, adjust sc_col if (!last_has_status) { sc_col = ru_col; diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 2b73f9c046..02920e5af6 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -7312,7 +7312,7 @@ local options = { The format of this option is like that of 'statusline'. This option cannot be set in a modeline when 'modelineexpr' is off. - The default ruler width is 17 characters. To make the ruler 15 + The default ruler width is 18 characters. To make the ruler 15 characters wide, put "%15(" at the start and "%)" at the end. Example: >vim set rulerformat=%15(%c%V\ %p%%%) diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index 01726a0a8f..2e27bfb0b7 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -338,7 +338,6 @@ static void win_redr_stl_expr(win_T *wp, bool draw_winbar, bool draw_ruler, bool if (!in_status_line) { row = Rows - 1; grid = grid_adjust(&msg_grid_adj, &row, &col); - maxwidth--; // writing in last column may cause scrolling fillchar = schar_from_ascii(' '); group = HLF_MSG; } @@ -557,14 +556,9 @@ void redraw_ruler(void) (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). 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 && !ui_has(kUIMessages)) { - n1++; // can't use last char of screen - } int this_ru_col = ru_col - (Columns - width); // Never use more than half the window/screen width, leave the other half diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua index 8d8113dff3..cd6892d07b 100644 --- a/test/functional/api/window_spec.lua +++ b/test/functional/api/window_spec.lua @@ -2337,7 +2337,7 @@ describe('API/win', function() screen:expect([[ │^ | ~ │~ |*4 - 0,0-1 All | + 0,0-1 All| {5:-- TERMINAL --} | ]]) screen:detach() @@ -2351,7 +2351,7 @@ describe('API/win', function() screen:expect([[ ^ │ | ~ │~ |*4 - 0,0-1 All | + 0,0-1 All| {5:-- TERMINAL --} | ]]) end) diff --git a/test/functional/editor/completion_spec.lua b/test/functional/editor/completion_spec.lua index 837453edca..86b5bca879 100644 --- a/test/functional/editor/completion_spec.lua +++ b/test/functional/editor/completion_spec.lua @@ -1507,7 +1507,7 @@ describe('completion', function() {12:hello }{1: }| {4:hullo }{1: }| {4:heee }{1: }| - {5:-- INSERT --} 4,6 All | + {5:-- INSERT --} 4,6 All| ]]) end) diff --git a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua index 383fe672ef..f9064e4b74 100644 --- a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua +++ b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua @@ -429,7 +429,7 @@ describe('swapfile detection', function() screen:expect([[ ^ | {1:~ }|*16 - {19:W325: Ignoring swapfile from Nvim process }0,0-1 All | + {19:W325: Ignoring swapfile from Nvim process }0,0-1 All| ]]) eq(('\n' .. msg_expected):rep(3):sub(2), n.exec_capture('messages')) command('bwipe!') diff --git a/test/functional/legacy/cmdline_spec.lua b/test/functional/legacy/cmdline_spec.lua index cc8c31bb7c..a54cd50b84 100644 --- a/test/functional/legacy/cmdline_spec.lua +++ b/test/functional/legacy/cmdline_spec.lua @@ -386,7 +386,7 @@ describe('cmdline', function() api.nvim_set_option_value('rulerformat', '%!TestRulerFn()', {}) screen:expect([[ ^ | - 10,20 30% | + 10,20 30%| ]]) end) diff --git a/test/functional/legacy/excmd_spec.lua b/test/functional/legacy/excmd_spec.lua index 5eaf495b63..fde106e53a 100644 --- a/test/functional/legacy/excmd_spec.lua +++ b/test/functional/legacy/excmd_spec.lua @@ -263,7 +263,7 @@ describe(':confirm command dialog', function() fooba^r | {1:~ }|*5 | - 1,6 All | + 1,6 All| ]]) eq('foo\n', read_file('Xconfirm_write_ro')) @@ -377,7 +377,7 @@ describe(':confirm command dialog', function() d | {1:~ }|*2 | - 1,1 All | + 1,1 All| ]]) eq('a\nb\nc\nd\n', read_file('Xwrite_partial')) os.remove('Xwrite_partial') diff --git a/test/functional/legacy/listlbr_utf8_spec.lua b/test/functional/legacy/listlbr_utf8_spec.lua index ef6187f13b..b2df33544b 100644 --- a/test/functional/legacy/listlbr_utf8_spec.lua +++ b/test/functional/legacy/listlbr_utf8_spec.lua @@ -249,7 +249,7 @@ describe('linebreak', function() {17:foo}^ │{1:~ }| xxxxxxxxxxxxxxxxxxxx│{1:~ }| {1:~ }│{1:~ }|*2 - {5:-- VISUAL BLOCK --} 2x4 2,4 All | + {5:-- VISUAL BLOCK --} 2x4 2,4 All| ]]) -- TAB as end char: 'linebreak' shouldn't break Visual block hl @@ -264,7 +264,7 @@ describe('linebreak', function() f{17:oo12345}bar | f^o{17:o }bar | {1:~ }|*2 - {5:-- VISUAL BLOCK --} 3x7 3,2 All | + {5:-- VISUAL BLOCK --} 3x7 3,2 All| ]]) feed(':setlocal linebreakgv') screen:expect_unchanged(true) @@ -281,7 +281,7 @@ describe('linebreak', function() f{17:oo123456}bar | f^o{17:o}bar | {1:~ }|*2 - {5:-- VISUAL BLOCK --} 3x8 3,2 All | + {5:-- VISUAL BLOCK --} 3x8 3,2 All| ]]) feed(':setlocal linebreakgv') screen:expect_unchanged(true) @@ -303,7 +303,7 @@ describe('linebreak', function() xx{17:xx}foo: {17:x}xxxxx | xx{17:xx}bar: ^xxxxxx | {1:~ }|*2 - {5:-- VISUAL BLOCK --} 3x8 3,5-10 All | + {5:-- VISUAL BLOCK --} 3x8 3,5-10 All| ]]) feed(':setlocal linebreakgv') screen:expect_unchanged(true) diff --git a/test/functional/legacy/normal_spec.lua b/test/functional/legacy/normal_spec.lua index 1f16bc7f4f..19aa76920a 100644 --- a/test/functional/legacy/normal_spec.lua +++ b/test/functional/legacy/normal_spec.lua @@ -158,7 +158,7 @@ describe('normal', function() ^4 | 5 | 6 | - 40 more lines 5,1 %8 | + 40 more lines 5,1 %8| ]]) end) end) diff --git a/test/functional/legacy/window_cmd_spec.lua b/test/functional/legacy/window_cmd_spec.lua index d4f714bd87..27e71a0db3 100644 --- a/test/functional/legacy/window_cmd_spec.lua +++ b/test/functional/legacy/window_cmd_spec.lua @@ -31,7 +31,7 @@ it('scrolling with laststatus=0 and :botright split', function() 98 | 99 | ^100 | - 100,1 Bot | + 100,1 Bot| ]]) end) diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 263c78057a..34029f40fc 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -821,7 +821,7 @@ describe('TUI :restart', function() screen:expect([[ ^ | ~ |*4 - 0,0-1 All | + 0,0-1 All| {5:-- TERMINAL --} | ]]) @@ -833,7 +833,7 @@ describe('TUI :restart', function() 0002;;Cc;0;BN;;;;;N;START OF TEXT;;;; | 0003;;Cc;0;BN;;;;;N;END OF TEXT;;;; | 0004;;Cc;0;BN;;;;;N;END OF TRANSMISSION;;| - TRIGGERED: 1 1,1 Top | + TRIGGERED: 1 1,1 Top| {5:-- TERMINAL --} | ]]) diff --git a/test/functional/terminal/window_spec.lua b/test/functional/terminal/window_spec.lua index 752def3c57..0421fc0737 100644 --- a/test/functional/terminal/window_spec.lua +++ b/test/functional/terminal/window_spec.lua @@ -423,7 +423,7 @@ describe(':terminal window', function() cool line 8 | cool line 9 | cool line 10 | - {5:-- TERMINAL --} 6,1 Bot | + {5:-- TERMINAL --} 6,1 Bot| ]]) command('call nvim_win_set_cursor(0, [1, 0])') screen:expect_unchanged() @@ -436,7 +436,7 @@ describe(':terminal window', function() cool line 8 | cool line 9 | cool line 10 | - {5:-- TERMINAL --} 7,5 Bot | + {5:-- TERMINAL --} 7,5 Bot| ]]) -- Check topline correct after leaving terminal mode. -- The new cursor position is one column left of the terminal's actual cursor position. @@ -448,7 +448,7 @@ describe(':terminal window', function() cool line 8 | cool line 9 | cool line 10 | - 7,4 Bot | + 7,4 Bot| ]]) end) diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua index ce59c22690..b57fa6f018 100644 --- a/test/functional/ui/float_spec.lua +++ b/test/functional/ui/float_spec.lua @@ -3578,7 +3578,7 @@ describe('float window', function() | {0:~ }|*5 ## grid 3 - 1,1 All | + 1,1 All| ## grid 4 {1:^aaa aab }| {1:abb acc }| @@ -3597,7 +3597,7 @@ describe('float window', function() {0:~ }{1:abb acc }{0: }| {0:~ }{2:~ }{0: }| {0:~ }|*3 - 1,1 All | + 1,1 All| ]], } end @@ -3613,7 +3613,7 @@ describe('float window', function() | {0:~ }|*5 ## grid 3 - 1,5 All | + 1,5 All| ## grid 4 {1:aaa ^aab }| {1:abb acc }| @@ -3632,7 +3632,7 @@ describe('float window', function() {0:~ }{1:abb acc }{0: }| {0:~ }{2:~ }{0: }| {0:~ }|*3 - 1,5 All | + 1,5 All| ]], } end @@ -3692,7 +3692,7 @@ describe('float window', function() | {0:~ }|*7 ## grid 3 - 0,0-1 All | + 0,0-1 All| ## grid 4 {1: }| {2:~ }|*2 @@ -3716,7 +3716,7 @@ describe('float window', function() {1: } {1:^ } | {2:~ }{0: }{2:~ }{0: }|*2 {0:~ }|*5 - 0,0-1 All | + 0,0-1 All| ]], } end diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua index 29119bda3f..604e7e0d36 100644 --- a/test/functional/ui/messages_spec.lua +++ b/test/functional/ui/messages_spec.lua @@ -1894,7 +1894,7 @@ describe('ui/builtin messages', function() screen:expect([[ ^ | {1:~ }|*5 - 0-1 100% | + 0-1 100% | ]]) -- Ruler is cleared when it is no longer drawn. @@ -1909,14 +1909,14 @@ describe('ui/builtin messages', function() screen:expect([[ ^ | {1:~ }|*5 - 0,0-1 All | + 0,0-1 All| ]]) command('hi MsgArea guibg=#333333') screen:expect([[ ^ | {1:~ }|*5 - {101: 0,0-1 All }| + {101: 0,0-1 All}| ]]) end) diff --git a/test/functional/ui/multibyte_spec.lua b/test/functional/ui/multibyte_spec.lua index 2f15be9d4b..5d265d724b 100644 --- a/test/functional/ui/multibyte_spec.lua +++ b/test/functional/ui/multibyte_spec.lua @@ -303,35 +303,35 @@ describe('multibyte rendering', function() screen:expect([[ ^🏳️‍⚧️ | {1:~ }|*4 - 1,1 All | + 1,1 All| ]]) feed('a word') screen:expect([[ 🏳️‍⚧️ wor^d | {1:~ }|*4 - 1,21-7 All | + 1,21-7 All| ]]) feed('0') screen:expect([[ ^🏳️‍⚧️ word | {1:~ }|*4 - 1,1 All | + 1,1 All| ]]) feed('l') screen:expect([[ 🏳️‍⚧️^ word | {1:~ }|*4 - 1,17-3 All | + 1,17-3 All| ]]) feed('h') screen:expect([[ ^🏳️‍⚧️ word | {1:~ }|*4 - 1,1 All | + 1,1 All| ]]) feed('o❤️ variant selected') @@ -339,7 +339,7 @@ describe('multibyte rendering', function() 🏳️‍⚧️ word | ❤️ variant selecte^d | {1:~ }|*3 - 2,23-19 All | + 2,23-19 All| ]]) feed('0') @@ -347,7 +347,7 @@ describe('multibyte rendering', function() 🏳️‍⚧️ word | ^❤️ variant selected | {1:~ }|*3 - 2,1 All | + 2,1 All| ]]) feed('l') @@ -355,7 +355,7 @@ describe('multibyte rendering', function() 🏳️‍⚧️ word | ❤️^ variant selected | {1:~ }|*3 - 2,7-3 All | + 2,7-3 All| ]]) feed('h') @@ -363,7 +363,7 @@ describe('multibyte rendering', function() 🏳️‍⚧️ word | ^❤️ variant selected | {1:~ }|*3 - 2,1 All | + 2,1 All| ]]) -- without selector: single width (note column 18 and not 19) @@ -373,7 +373,7 @@ describe('multibyte rendering', function() ❤️ variant selected | ❤ variant selecte^d | {1:~ }|*2 - 3,20-18 All | + 3,20-18 All| ]]) end) end) diff --git a/test/functional/ui/screen_basic_spec.lua b/test/functional/ui/screen_basic_spec.lua index 194d1fb1c7..79726786e5 100644 --- a/test/functional/ui/screen_basic_spec.lua +++ b/test/functional/ui/screen_basic_spec.lua @@ -366,7 +366,7 @@ local function screen_tests(linegrid) 0123^456 | 789 | {1:~ }|*11 - 1,5 All | + 1,5 All| ]]) end) end) diff --git a/test/functional/ui/statusline_spec.lua b/test/functional/ui/statusline_spec.lua index c70015b78f..d386f8bf52 100644 --- a/test/functional/ui/statusline_spec.lua +++ b/test/functional/ui/statusline_spec.lua @@ -346,7 +346,7 @@ describe('global statusline', function() screen:expect([[ ^ | {1:~ }|*14 - 0,0-1 All | + 0,0-1 All| ]]) command('set laststatus=3') @@ -402,7 +402,7 @@ describe('global statusline', function() {2:< 0,0-1 All <-1 All <}│{1:~ }| │{1:~ }| {1:~ }│{1:~ }|*4 - 0,0-1 All | + 0,0-1 All| ]]) command('set laststatus=3') @@ -816,16 +816,10 @@ describe('statusline', function() {1:~}{15:^ }{1: }| {1:~ }|*4 {2:[No Name] 0,0-1 All}| - 0,0-1 All | - ]]) - command('set rulerformat=%17(%l,%c%V%=%P%)') - screen:expect([[ - | - {1:~}{15:^ }{1: }| - {1:~ }|*4 - {2:[No Name] 0,0-1 All}| - 0,0-1 All | + 0,0-1 All| ]]) + command('set rulerformat=%18(%l,%c%V%=%P%)') + screen:expect_unchanged() command('set rulerformat&') api.nvim_win_close(win, true) screen:expect([[ From 9be130304d39e2223ec976c26e69b87b9ef7a0e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Hoffmann?= Date: Sat, 1 Aug 2026 13:33:52 +0200 Subject: [PATCH 4/5] refactor(statusline): simplify and remove dead code - Remove code that is never executed because the ruler is never rendered separately for a window with visible statusline or when `ls=3`, in other words: the variables `part_of_status` and `in_status_line` were always false for the ruler and badly named for the statusline itself. - Leave `maxwidth` unchanged after calling `stl_alloc_click_defs`. - Don't reuse the window's click definitions for the ruler. Even though it doesn't seem to be a bug because statusline and ruler are never shown at the same time for the same window, the ruler currently doesn't support clicks anyway. - Disentangle the different cases, in particular statusline- from ruler- specific code. Another example: `wp->w_wincol`, `wp->w_winrow`, and `wp->w_width` are always used together, but this was hard to see. --- src/nvim/statusline.c | 126 +++++++++++++++++++++--------------------- 1 file changed, 62 insertions(+), 64 deletions(-) diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index 2e27bfb0b7..c1579ab2b2 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -239,7 +239,7 @@ static void win_redr_stl_expr(win_T *wp, bool draw_winbar, bool draw_ruler, bool OptIndex opt_idx = kOptInvalid; int opt_scope = 0; stl_hlrec_t *hltab; - StlClickRecord *tabtab; + StlClickDefinition *click_defs; bool is_stl_global = global_stl_height() > 0; ScreenGrid *grid = wp && wp->w_floating && !is_stl_global ? &wp->w_grid_alloc : &default_grid; @@ -274,6 +274,7 @@ static void win_redr_stl_expr(win_T *wp, bool draw_winbar, bool draw_ruler, bool attr = HL_ATTR(group); maxwidth = Columns; opt_idx = kOptTabline; + click_defs = tab_page_click_defs; } else if (draw_winbar) { opt_idx = kOptWinbar; stl = ((*wp->w_p_wbr != NUL) ? wp->w_p_wbr : p_wbr); @@ -293,64 +294,66 @@ static void win_redr_stl_expr(win_T *wp, bool draw_winbar, bool draw_ruler, bool stl_clear_click_defs(wp->w_winbar_click_defs, wp->w_winbar_click_defs_size); wp->w_winbar_click_defs = stl_alloc_click_defs(wp->w_winbar_click_defs, maxwidth, &wp->w_winbar_click_defs_size); + click_defs = wp->w_winbar_click_defs; + } else if (draw_ruler && ui_event) { + stl = p_ruf; + opt_idx = kOptRulerformat; + maxwidth = Columns / 2; + fillchar = schar_from_ascii(' '); + group = HLF_MSG; + attr = HL_ATTR(group); + click_defs = NULL; + } else if (draw_ruler) { + stl = p_ruf; + opt_idx = kOptRulerformat; + // advance past any leading group spec - implicit in ru_col + if (*stl == '%') { + if (*++stl == '-') { + stl++; + } + if (atoi(stl)) { + while (ascii_isdigit(*stl)) { + stl++; + } + } + if (*stl++ != '(') { + stl = p_ruf; + } + } + row = Rows - 1; + col = MAX(ru_col, Columns / 2); + grid = grid_adjust(&msg_grid_adj, &row, &col); + maxwidth = Columns - col; + fillchar = schar_from_ascii(' '); + group = HLF_MSG; + attr = HL_ATTR(group); + click_defs = NULL; } else { - const bool in_status_line = wp->w_status_height != 0 || is_stl_global; - if (wp->w_floating && !is_stl_global && !draw_ruler) { + // statusline + stl = ((*wp->w_p_stl != NUL) ? wp->w_p_stl : p_stl); + opt_idx = kOptStatusline; + opt_scope = ((*wp->w_p_stl != NUL) ? OPT_LOCAL : 0); + if (is_stl_global) { + row = Rows - (int)p_ch - 1; + col = 0; + maxwidth = Columns; + } else if (wp->w_status_height == 0) { + goto theend; + } else if (wp->w_floating) { row = wp->w_winrow_off + wp->w_view_height; col = wp->w_wincol_off; maxwidth = wp->w_view_width; } else { - row = is_stl_global ? (Rows - (int)p_ch - 1) : W_ENDROW(wp); - maxwidth = in_status_line && !is_stl_global ? wp->w_width : Columns; + row = W_ENDROW(wp); + col = wp->w_wincol; + maxwidth = wp->w_width; } fillchar = fillchar_status(&group, wp); + attr = win_hl_attr(wp, (int)group); stl_clear_click_defs(wp->w_status_click_defs, wp->w_status_click_defs_size); wp->w_status_click_defs = stl_alloc_click_defs(wp->w_status_click_defs, maxwidth, &wp->w_status_click_defs_size); - - 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 - if (*stl == '%') { - if (*++stl == '-') { - stl++; - } - if (atoi(stl)) { - while (ascii_isdigit(*stl)) { - stl++; - } - } - if (*stl++ != '(') { - stl = p_ruf; - } - } - col = MAX(ru_col - (Columns - maxwidth), (maxwidth + 1) / 2); - maxwidth -= col; - if (!in_status_line) { - row = Rows - 1; - grid = grid_adjust(&msg_grid_adj, &row, &col); - fillchar = schar_from_ascii(' '); - group = HLF_MSG; - } - } else { - opt_idx = kOptStatusline; - stl = ((*wp->w_p_stl != NUL) ? wp->w_p_stl : p_stl); - opt_scope = ((*wp->w_p_stl != NUL) ? OPT_LOCAL : 0); - } - - attr = draw_ruler && !in_status_line ? HL_ATTR(group) : win_hl_attr(wp, (int)group); - if (!wp->w_floating && in_status_line && !is_stl_global) { - col += wp->w_wincol; - } + click_defs = wp->w_status_click_defs; } if (maxwidth <= 0) { @@ -366,8 +369,11 @@ static void win_redr_stl_expr(win_T *wp, bool draw_winbar, bool draw_ruler, bool // Make a copy, because the statusline may include a function call that // might change the option value and free the memory. stl = xstrdup(stl); + + StlClickRecord *tabtab = NULL; build_stl_str_hl(ewp, buf, sizeof(buf), stl, opt_idx, opt_scope, - fillchar, maxwidth, &hltab, NULL, &tabtab, NULL); + fillchar, maxwidth, &hltab, NULL, click_defs ? &tabtab : NULL, NULL); + stl_fill_click_defs(click_defs, tabtab, buf, maxwidth, wp == NULL); xfree(stl); ewp->w_p_crb = p_crb_save; @@ -442,14 +448,6 @@ static void win_redr_stl_expr(win_T *wp, bool draw_winbar, bool draw_ruler, bool grid_line_fill(col, maxcol, fillchar, curattr); grid_line_flush(); - // 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; - - stl_fill_click_defs(click_defs, tabtab, buf, maxwidth, wp == NULL); - theend: entered = false; @@ -488,7 +486,8 @@ void redraw_ruler(void) bool is_stl_global = global_stl_height() > 0; // Check if ruler should be drawn, clear if it was drawn before. - if (!p_ru || wp->w_status_height > 0 || is_stl_global || (p_ch == 0 && !ui_has(kUIMessages))) { + bool part_of_status = wp->w_status_height != 0 || is_stl_global; + if (!p_ru || part_of_status || (p_ch == 0 && !ui_has(kUIMessages))) { if (did_show_ext_ruler && ui_has(kUIMessages)) { ui_call_msg_ruler((Array)ARRAY_DICT_INIT); did_show_ext_ruler = false; @@ -516,8 +515,7 @@ void redraw_ruler(void) return; } - bool part_of_status = wp->w_status_height || is_stl_global; - if (*p_ruf && (p_ch > 0 || (ui_has(kUIMessages) && !part_of_status))) { + if (*p_ruf && (p_ch > 0 || ui_has(kUIMessages))) { win_redr_stl_expr(wp, false, true, ui_has(kUIMessages)); did_show_ruler = !ui_has(kUIMessages); return; @@ -526,8 +524,8 @@ void redraw_ruler(void) hlf_T group = HLF_MSG; int off = wp->w_status_height ? wp->w_wincol : 0; int width = wp->w_status_height ? wp->w_width : Columns; - schar_T fillchar = part_of_status ? fillchar_status(&group, wp) : schar_from_ascii(' '); - int attr = part_of_status ? win_hl_attr(wp, (int)group) : HL_ATTR(group); + schar_T fillchar = schar_from_ascii(' '); + int attr = HL_ATTR(group); // In list mode virtcol needs to be recomputed colnr_T virtcol = wp->w_virtcol; @@ -577,7 +575,7 @@ void redraw_ruler(void) } (void)bufferlen; - if (ui_has(kUIMessages) && !part_of_status) { + if (ui_has(kUIMessages)) { MAXSIZE_TEMP_ARRAY(content, 1); MAXSIZE_TEMP_ARRAY(chunk, 3); ADD_C(chunk, INTEGER_OBJ(attr)); From ca07e505f939e8ff6e70571ef130722bdc40b292 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Hoffmann?= Date: Mon, 20 Jul 2026 21:35:16 +0200 Subject: [PATCH 5/5] refactor(ruler)!: replace C implementation with 'rulerformat' expression Problem: The default 'ruler' is implemented in C instead of the 'statusline' DSL. Solution: Replace the C implementation with a default 'rulerformat' expression. This is a continuation of #1248 and #33036. Advantages: - configuration is more discoverable, the default being a useful example - users and plugins can augment the default - code reuse and less C code to maintain - ui2: due to the use of an item group with `minwid`, it can expand instead of truncating when the content gets too long, which is particularly useful for locales with long translations of Top/Bot/All Implementation details: As is the case for 'statusline', when trying to set 'rulerformat' to an empty string, the default expression is restored instead, mimicking how previously the default C implementation would have been activated. Just like before, `:set rulerformat=` and `:set rulerformat&` have the same effect, and the ruler is disabled with `:set noruler`. The default expression uses an item group with `%=`, unlike the fallback in the previous default statusline `%-14.(%l,%c%V%) %P`, because the total width and how it is configured is immediately clear without documentation, it is a more useful pattern in general that works when both sides have flexible width, and it also works for vim, which is useful for configuration sharing/reuse. A truncation marker `%<` is added at the end to mimic how at small screen widths, the scroll percentage would disappear first, so that the cursor position can remain fully visible. BREAKING CHANGES: - `&rulerformat` can no longer be set to an empty string - ui2: the default ruler is no longer of fixed width, but can expand - at very small screen widths (< 36 columns) - ui2: it will no longer try to shrink white-space before truncating - it truncates gradually from the right, whereas previously, the scroll percentage would disappear all at once - l10n can no longer add a space after the comma between line and column (this was only done for one language: Ukrainian) --- runtime/doc/news.txt | 5 +- runtime/doc/options.txt | 86 +++++++++++++-------- runtime/lua/vim/_meta/options.gen.lua | 95 ++++++++++++++--------- src/nvim/options.lua | 90 ++++++++++++--------- src/nvim/optionstr.c | 4 +- src/nvim/po/sv.po | 7 -- src/nvim/po/uk.po | 4 - src/nvim/statusline.c | 103 +------------------------ test/functional/api/window_spec.lua | 4 +- test/functional/testnvim.lua | 3 +- test/functional/ui/messages_spec.lua | 2 +- test/functional/ui/statusline_spec.lua | 47 ++++++++++- 12 files changed, 224 insertions(+), 226 deletions(-) diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 3afd74c260..28aedc495c 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -154,6 +154,8 @@ OPTIONS To restore the previous truncation from the left, add a `%<` to the start. The same applies to 'rulerformat', 'statuscolumn', 'tabline', 'winbar', 'titlestring', and 'iconstring'. +• 'rulerformat' can no longer be set empty, the new default expression will be + restored instead. PLUGINS @@ -229,7 +231,8 @@ BUILD DEFAULTS -• todo +• 'rulerformat' default is exposed as a statusline expression (previously it + was implemented as an internal C routine). DIAGNOSTICS diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 47b36501db..c8f1d868a8 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -5181,43 +5181,45 @@ A jump table for the options with a short description can be found at |Q_op|. *'ruler'* *'ru'* *'noruler'* *'noru'* 'ruler' 'ru' boolean (default on) global - Show the line and column number of the cursor position, separated by a - comma. When there is room, the relative position of the displayed - text in the file is shown on the far right: - Top first line is visible - Bot last line is visible - All first and last line are visible - 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. If a window doesn't have a status line and - 'cmdheight' is zero, 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'. + When on, show some buffer information in the ruler. Each window has + its own ruler. The ruler of the active window is shown in the last + line of the screen, unless 'cmdheight' is zero, or the window has a + status line, which (by default) contains the window's ruler. + + 'rulerformat' determines the contents of the ruler. + By default, the line and column number of the current cursor position + are shown, separated by a comma. 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, separated with a dash. For an empty line "0-1" is shown. For an empty buffer the line number will also be zero: "0,0-1". + Finally, the relative position of the displayed text in the file is + shown on the far right: + Top first line is visible + Bot last line is visible + All first and last line are visible + 45% relative position in the file + If you don't want to see the ruler all the time but want to know where you are, use "g CTRL-G" |g_CTRL-G|. + When 'ruler' is on, the output of |CTRL-G| doen't contain the current + line number of the cursor position. *'rulerformat'* *'ruf'* -'rulerformat' 'ruf' string (default "") +'rulerformat' 'ruf' string (default "%18(%l,%c%V%= %P%)%<") global - When this option is not empty, it determines the content of the ruler - string, as displayed for the 'ruler' option. + This option determines the content of the ruler string, as displayed + for the 'ruler' option. The format of this option is like that of 'statusline'. + Setting to empty (`:set rulerformat=`) sets the value to the default. This option cannot be set in a modeline when 'modelineexpr' is off. - The default ruler width is 18 characters. To make the ruler 15 - characters wide, put "%15(" at the start and "%)" at the end. - Example: >vim - set rulerformat=%15(%c%V\ %p%%%) -< - This looks like an item group, but there are some differences in this + When 'ruler' is on, the default 'statusline' includes 'rulerformat'. + + The default ruler width is 18 characters, which is configured with + what 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. @@ -6429,8 +6431,11 @@ A jump table for the options with a short description can be found at |Q_op|. would loop. When the result contains unprintable characters the result is unpredictable. - Note that the only effect of 'ruler' when this option is set (and - 'laststatus' is 2 or 3) is controlling the output of |CTRL-G|. + When 'ruler' is on, the default 'statusline' includes 'rulerformat'. + See note below. + Note that if 'statusline' is configured without including the ruler, + the only effect of 'ruler' when this option is set (and 'laststatus' + is 2 or 3) is controlling the output of |CTRL-G|. field meaning ~ - Left justify the item. The default is right justified @@ -6447,10 +6452,10 @@ A jump table for the options with a short description can be found at |Q_op|. Following is a description of the possible statusline items. The second character in "item" is the type: - N for number - S for string - F for flags as described below - - not applicable + "N" for number + "S" for string + "F" for flags as described below + "-" not applicable item meaning ~ f S Path to the file in the buffer, as typed or relative to current @@ -6504,6 +6509,8 @@ A jump table for the options with a short description can be found at |Q_op|. endfunc < `stl=%{Stl_filename()}` results in `"%t"` `stl=%{%Stl_filename()%}` results in `"Name of current file"` + The default status line uses this to include 'rulerformat', see + note below. %} - End of "{%" expression ( - Start of item group. Can be used for setting the width and alignment of a section. Must be followed by %) somewhere. @@ -6540,11 +6547,13 @@ A jump table for the options with a short description can be found at |Q_op|. is a bug that denotes that new mouse button recognition was added without modifying code that reacts on mouse clicks on this label. + Use |getmousepos()|.winid in the specified function to get the corresponding |window-ID| of the clicked item. < - Where to truncate line if too long. Default is at the first item. Truncation markers within item groups apply to the truncation of that group until its maxwid is reached. + In case of several competing truncation markers, the first wins. No width fields allowed. = - Separation point between alignment sections. Each section will be separated by an equal number of spaces. With one %= what @@ -6607,12 +6616,12 @@ A jump table for the options with a short description can be found at |Q_op|. edit your vimrc or whatever with "vim --clean" to get it right. Examples: - Emulate standard status line with 'ruler' set >vim - set statusline=%<%f\ %h%w%m%r%=%-14.(%l,%c%V%)\ %P + A simple version of the standard status line with 'ruler' set >vim + set statusline=%f\ %h%w%m%r%=\ %-14.(%l,%c%V%)\ %P < Similar, but add ASCII value of char under the cursor (like "ga") >vim - set statusline=%<%f%h%m%r%=%b\ 0x%B\ \ %l,%c%V\ %P + set statusline=%f%h%m%r%=\ %b\ 0x%B\ \ %l,%c%V\ %P < Display byte count and byte value, modified flag in red. >vim - set statusline=%<%f%=\ [%1*%M%*%n%R%H]\ %-19(%3l,%02c%03V%)%O'%02b' + set statusline=%f%=\ [%1*%M%*%n%R%H]\ %-19(%3l,%02c%03V%)%O'%02b' hi User1 term=inverse,bold cterm=inverse,bold ctermfg=red < Display a ,GZ flag if a compressed file is loaded >vim set statusline=...%r%{VarExists('b:gzflag','\ [GZ]')}%h... @@ -6625,6 +6634,17 @@ A jump table for the options with a short description can be found at |Q_op|. if exists(a:var) | return a:val | else | return '' | endif endfunction < + Note: By default, the status line is truncated from the left, and the + ruler from the right. But the status line can include the ruler. + To ensure that a top-level (i.e. not inside an item group) `%<` in + 'rulerformat' doesn't change the truncation of the status line, + - 'statusline' can be prepended with an explicit `%<`, which otherwise + would not be necessary. Example: >vim + set statusline=%<%f%=\ %{%&rulerformat%} +< - Or the ruler can be wrapped in an item group, which also collapses + any top-level `%=` in 'rulerformat' unless minwid is specified: >vim + set statusline=%f%=\ %(%{%&rulerformat%}%) +< *'suffixes'* *'su'* 'suffixes' 'su' string (default ".bak,~,.o,.h,.info,.swp,.obj") diff --git a/runtime/lua/vim/_meta/options.gen.lua b/runtime/lua/vim/_meta/options.gen.lua index 25f2085916..dd2d1a7468 100644 --- a/runtime/lua/vim/_meta/options.gen.lua +++ b/runtime/lua/vim/_meta/options.gen.lua @@ -5369,28 +5369,31 @@ vim.o.rlc = vim.o.rightleftcmd vim.wo.rightleftcmd = vim.o.rightleftcmd vim.wo.rlc = vim.wo.rightleftcmd ---- Show the line and column number of the cursor position, separated by a ---- comma. When there is room, the relative position of the displayed ---- text in the file is shown on the far right: ---- Top first line is visible ---- Bot last line is visible ---- All first and last line are visible ---- 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. If a window doesn't have a status line and ---- 'cmdheight' is zero, 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'. +--- When on, show some buffer information in the ruler. Each window has +--- its own ruler. The ruler of the active window is shown in the last +--- line of the screen, unless 'cmdheight' is zero, or the window has a +--- status line, which (by default) contains the window's ruler. +--- +--- 'rulerformat' determines the contents of the ruler. +--- By default, the line and column number of the current cursor position +--- are shown, separated by a comma. --- 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, --- separated with a dash. --- For an empty line "0-1" is shown. --- For an empty buffer the line number will also be zero: "0,0-1". +--- Finally, the relative position of the displayed text in the file is +--- shown on the far right: +--- Top first line is visible +--- Bot last line is visible +--- All first and last line are visible +--- 45% relative position in the file +--- --- If you don't want to see the ruler all the time but want to know where --- you are, use "g CTRL-G" `g_CTRL-G`. +--- When 'ruler' is on, the output of `CTRL-G` doen't contain the current +--- line number of the cursor position. --- --- @type boolean vim.o.ruler = true @@ -5398,20 +5401,16 @@ vim.o.ru = vim.o.ruler vim.go.ruler = vim.o.ruler vim.go.ru = vim.go.ruler ---- When this option is not empty, it determines the content of the ruler ---- string, as displayed for the 'ruler' option. +--- This option determines the content of the ruler string, as displayed +--- for the 'ruler' option. --- The format of this option is like that of 'statusline'. +--- Setting to empty (`:set rulerformat=`) sets the value to the default. --- This option cannot be set in a modeline when 'modelineexpr' is off. --- ---- The default ruler width is 18 characters. To make the ruler 15 ---- characters wide, put "%15(" at the start and "%)" at the end. ---- Example: +--- When 'ruler' is on, the default 'statusline' includes 'rulerformat'. --- ---- ```vim ---- set rulerformat=%15(%c%V\ %p%%%) ---- ``` ---- ---- This looks like an item group, but there are some differences in this +--- The default ruler width is 18 characters, which is configured with +--- what 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. @@ -5420,7 +5419,7 @@ vim.go.ru = vim.go.ruler --- item group syntax has no special meaning for 'rulerformat'. --- --- @type string -vim.o.rulerformat = "" +vim.o.rulerformat = "%18(%l,%c%V%= %P%)%<" vim.o.ruf = vim.o.rulerformat vim.go.rulerformat = vim.o.rulerformat vim.go.ruf = vim.go.rulerformat @@ -6808,8 +6807,11 @@ vim.wo.stc = vim.wo.statuscolumn --- would loop. When the result contains unprintable characters the --- result is unpredictable. --- ---- Note that the only effect of 'ruler' when this option is set (and ---- 'laststatus' is 2 or 3) is controlling the output of `CTRL-G`. +--- When 'ruler' is on, the default 'statusline' includes 'rulerformat'. +--- See note below. +--- Note that if 'statusline' is configured without including the ruler, +--- the only effect of 'ruler' when this option is set (and 'laststatus' +--- is 2 or 3) is controlling the output of `CTRL-G`. --- --- field meaning ~ --- - Left justify the item. The default is right justified @@ -6826,10 +6828,10 @@ vim.wo.stc = vim.wo.statuscolumn --- --- Following is a description of the possible statusline items. The --- second character in "item" is the type: ---- N for number ---- S for string ---- F for flags as described below ---- - not applicable +--- "N" for number +--- "S" for string +--- "F" for flags as described below +--- "-" not applicable --- --- item meaning ~ --- f S Path to the file in the buffer, as typed or relative to current @@ -6886,6 +6888,8 @@ vim.wo.stc = vim.wo.statuscolumn --- ``` --- `stl=%{Stl_filename()}` results in `"%t"` --- `stl=%{%Stl_filename()%}` results in `"Name of current file"` +--- The default status line uses this to include 'rulerformat', see +--- note below. --- %} - End of "{%" expression --- ( - Start of item group. Can be used for setting the width and --- alignment of a section. Must be followed by %) somewhere. @@ -6922,11 +6926,13 @@ vim.wo.stc = vim.wo.statuscolumn --- is a bug that denotes that new mouse button recognition was --- added without modifying code that reacts on mouse clicks on --- this label. +--- --- Use `getmousepos()`.winid in the specified function to get the --- corresponding `window-ID` of the clicked item. --- \< - Where to truncate line if too long. Default is at the first --- item. Truncation markers within item groups apply to the --- truncation of that group until its maxwid is reached. +--- In case of several competing truncation markers, the first wins. --- No width fields allowed. --- = - Separation point between alignment sections. Each section will --- be separated by an equal number of spaces. With one %= what @@ -6992,20 +6998,20 @@ vim.wo.stc = vim.wo.statuscolumn --- edit your vimrc or whatever with "vim --clean" to get it right. --- --- Examples: ---- Emulate standard status line with 'ruler' set +--- A simple version of the standard status line with 'ruler' set --- --- ```vim ---- set statusline=%<%f\ %h%w%m%r%=%-14.(%l,%c%V%)\ %P +--- set statusline=%f\ %h%w%m%r%=\ %-14.(%l,%c%V%)\ %P --- ``` --- Similar, but add ASCII value of char under the cursor (like "ga") --- --- ```vim ---- set statusline=%<%f%h%m%r%=%b\ 0x%B\ \ %l,%c%V\ %P +--- set statusline=%f%h%m%r%=\ %b\ 0x%B\ \ %l,%c%V\ %P --- ``` --- Display byte count and byte value, modified flag in red. --- --- ```vim ---- set statusline=%<%f%=\ [%1*%M%*%n%R%H]\ %-19(%3l,%02c%03V%)%O'%02b' +--- set statusline=%f%=\ [%1*%M%*%n%R%H]\ %-19(%3l,%02c%03V%)%O'%02b' --- hi User1 term=inverse,bold cterm=inverse,bold ctermfg=red --- ``` --- Display a ,GZ flag if a compressed file is loaded @@ -7031,9 +7037,26 @@ vim.wo.stc = vim.wo.statuscolumn --- endfunction --- ``` --- +--- Note: By default, the status line is truncated from the left, and the +--- ruler from the right. But the status line can include the ruler. +--- To ensure that a top-level (i.e. not inside an item group) `%<` in +--- 'rulerformat' doesn't change the truncation of the status line, +--- - 'statusline' can be prepended with an explicit `%<`, which otherwise +--- would not be necessary. Example: +--- +--- ```vim +--- set statusline=%<%f%=\ %{%&rulerformat%} +--- ``` +--- - Or the ruler can be wrapped in an item group, which also collapses +--- any top-level `%=` in 'rulerformat' unless minwid is specified: +--- +--- ```vim +--- set statusline=%f%=\ %(%{%&rulerformat%}%) +--- ``` +--- --- --- @type string -vim.o.statusline = "%<%f %h%w%m%r %{% v:lua.require('vim._core.util').term_exitcode() %}%=%{% luaeval('(package.loaded[''vim.ui''] and vim.api.nvim_get_current_win() == tonumber(vim.g.actual_curwin or -1) and vim.ui.progress_status()) or '''' ')%}%{% &showcmdloc == 'statusline' ? '%-10.S ' : '' %}%{% exists('b:keymap_name') ? '<'..b:keymap_name..'> ' : '' %}%{% &busy > 0 ? '◐ ' : '' %}%{% luaeval('(package.loaded[''vim.diagnostic''] and next(vim.diagnostic.count()) and vim.diagnostic.status() .. '' '') or '''' ') %}%{% &ruler ? ( &rulerformat == '' ? '%-14.(%l,%c%V%) %P' : &rulerformat ) : '' %}" +vim.o.statusline = "%<%f %h%w%m%r %{% v:lua.require('vim._core.util').term_exitcode() %}%=%{% luaeval('(package.loaded[''vim.ui''] and vim.api.nvim_get_current_win() == tonumber(vim.g.actual_curwin or -1) and vim.ui.progress_status()) or '''' ')%}%{% &showcmdloc == 'statusline' ? '%-10.S ' : '' %}%{% exists('b:keymap_name') ? '<'..b:keymap_name..'> ' : '' %}%{% &busy > 0 ? '◐ ' : '' %}%{% luaeval('(package.loaded[''vim.diagnostic''] and next(vim.diagnostic.count()) and vim.diagnostic.status() .. '' '') or '''' ') %}%{% &ruler ? &rulerformat : '' %}" vim.o.stl = vim.o.statusline vim.wo.statusline = vim.o.statusline vim.wo.stl = vim.wo.statusline diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 02920e5af6..bf487a6d59 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -7272,28 +7272,31 @@ local options = { abbreviation = 'ru', defaults = true, desc = [=[ - Show the line and column number of the cursor position, separated by a - comma. When there is room, the relative position of the displayed - text in the file is shown on the far right: - Top first line is visible - Bot last line is visible - All first and last line are visible - 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. If a window doesn't have a status line and - 'cmdheight' is zero, 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'. + When on, show some buffer information in the ruler. Each window has + its own ruler. The ruler of the active window is shown in the last + line of the screen, unless 'cmdheight' is zero, or the window has a + status line, which (by default) contains the window's ruler. + + 'rulerformat' determines the contents of the ruler. + By default, the line and column number of the current cursor position + are shown, separated by a comma. 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, separated with a dash. For an empty line "0-1" is shown. For an empty buffer the line number will also be zero: "0,0-1". + Finally, the relative position of the displayed text in the file is + shown on the far right: + Top first line is visible + Bot last line is visible + All first and last line are visible + 45% relative position in the file + If you don't want to see the ruler all the time but want to know where you are, use "g CTRL-G" |g_CTRL-G|. + When 'ruler' is on, the output of |CTRL-G| doen't contain the current + line number of the cursor position. ]=], full_name = 'ruler', redraw = { 'statuslines' }, @@ -7305,19 +7308,18 @@ local options = { { abbreviation = 'ruf', cb = 'did_set_rulerformat', - defaults = '', + defaults = '%18(%l,%c%V%= %P%)%<', desc = [=[ - When this option is not empty, it determines the content of the ruler - string, as displayed for the 'ruler' option. + This option determines the content of the ruler string, as displayed + for the 'ruler' option. The format of this option is like that of 'statusline'. + Setting to empty (`:set rulerformat=`) sets the value to the default. This option cannot be set in a modeline when 'modelineexpr' is off. - The default ruler width is 18 characters. To make the ruler 15 - characters wide, put "%15(" at the start and "%)" at the end. - Example: >vim - set rulerformat=%15(%c%V\ %p%%%) - < - This looks like an item group, but there are some differences in this + When 'ruler' is on, the default 'statusline' includes 'rulerformat'. + + The default ruler width is 18 characters, which is configured with + what 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. @@ -9086,7 +9088,7 @@ local options = { cb = 'did_set_statusline', defaults = { if_true = table.concat({ - '%<', + '%<', -- guards the default truncation from the left against a %< injected via rulerformat '%f %h%w%m%r ', "%{% v:lua.require('vim._core.util').term_exitcode() %}", '%=', @@ -9095,7 +9097,7 @@ local options = { "%{% exists('b:keymap_name') ? '<'..b:keymap_name..'> ' : '' %}", "%{% &busy > 0 ? '◐ ' : '' %}", "%{% luaeval('(package.loaded[''vim.diagnostic''] and next(vim.diagnostic.count()) and vim.diagnostic.status() .. '' '') or '''' ') %}", - "%{% &ruler ? ( &rulerformat == '' ? '%-14.(%l,%c%V%) %P' : &rulerformat ) : '' %}", + "%{% &ruler ? &rulerformat : '' %}", }), doc = 'is very long', }, @@ -9126,8 +9128,11 @@ local options = { would loop. When the result contains unprintable characters the result is unpredictable. - Note that the only effect of 'ruler' when this option is set (and - 'laststatus' is 2 or 3) is controlling the output of |CTRL-G|. + When 'ruler' is on, the default 'statusline' includes 'rulerformat'. + See note below. + Note that if 'statusline' is configured without including the ruler, + the only effect of 'ruler' when this option is set (and 'laststatus' + is 2 or 3) is controlling the output of |CTRL-G|. field meaning ~ - Left justify the item. The default is right justified @@ -9144,10 +9149,10 @@ local options = { Following is a description of the possible statusline items. The second character in "item" is the type: - N for number - S for string - F for flags as described below - - not applicable + "N" for number + "S" for string + "F" for flags as described below + "-" not applicable item meaning ~ f S Path to the file in the buffer, as typed or relative to current @@ -9201,6 +9206,8 @@ local options = { endfunc < `stl=%{Stl_filename()}` results in `"%t"` `stl=%{%Stl_filename()%}` results in `"Name of current file"` + The default status line uses this to include 'rulerformat', see + note below. %} - End of "{%" expression ( - Start of item group. Can be used for setting the width and alignment of a section. Must be followed by %) somewhere. @@ -9237,11 +9244,13 @@ local options = { is a bug that denotes that new mouse button recognition was added without modifying code that reacts on mouse clicks on this label. + Use |getmousepos()|.winid in the specified function to get the corresponding |window-ID| of the clicked item. \< - Where to truncate line if too long. Default is at the first item. Truncation markers within item groups apply to the truncation of that group until its maxwid is reached. + In case of several competing truncation markers, the first wins. No width fields allowed. = - Separation point between alignment sections. Each section will be separated by an equal number of spaces. With one %= what @@ -9304,12 +9313,12 @@ local options = { edit your vimrc or whatever with "vim --clean" to get it right. Examples: - Emulate standard status line with 'ruler' set >vim - set statusline=%<%f\ %h%w%m%r%=%-14.(%l,%c%V%)\ %P + A simple version of the standard status line with 'ruler' set >vim + set statusline=%f\ %h%w%m%r%=\ %-14.(%l,%c%V%)\ %P < Similar, but add ASCII value of char under the cursor (like "ga") >vim - set statusline=%<%f%h%m%r%=%b\ 0x%B\ \ %l,%c%V\ %P + set statusline=%f%h%m%r%=\ %b\ 0x%B\ \ %l,%c%V\ %P < Display byte count and byte value, modified flag in red. >vim - set statusline=%<%f%=\ [%1*%M%*%n%R%H]\ %-19(%3l,%02c%03V%)%O'%02b' + set statusline=%f%=\ [%1*%M%*%n%R%H]\ %-19(%3l,%02c%03V%)%O'%02b' hi User1 term=inverse,bold cterm=inverse,bold ctermfg=red < Display a ,GZ flag if a compressed file is loaded >vim set statusline=...%r%{VarExists('b:gzflag','\ [GZ]')}%h... @@ -9322,6 +9331,17 @@ local options = { if exists(a:var) | return a:val | else | return '' | endif endfunction < + Note: By default, the status line is truncated from the left, and the + ruler from the right. But the status line can include the ruler. + To ensure that a top-level (i.e. not inside an item group) `%<` in + 'rulerformat' doesn't change the truncation of the status line, + - 'statusline' can be prepended with an explicit `%<`, which otherwise + would not be necessary. Example: >vim + set statusline=%<%f%=\ %{%&rulerformat%} + < - Or the ruler can be wrapped in an item group, which also collapses + any top-level `%=` in 'rulerformat' unless minwid is specified: >vim + set statusline=%f%=\ %(%{%&rulerformat%}%) + < ]=], full_name = 'statusline', modelineexpr = true, diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c index fcb81ab8b5..a224d2290e 100644 --- a/src/nvim/optionstr.c +++ b/src/nvim/optionstr.c @@ -1838,8 +1838,8 @@ static const char *did_set_statustabline_rulerformat(optset_T *args, bool rulerf char *s = *varp; bool is_stl = args->os_idx == kOptStatusline; - // reset statusline to default when setting global option and empty string is being set - if (is_stl + // reset global statusline/rulerformat option to default when it is being set to an empty string + if ((is_stl || rulerformat) && ((args->os_flags & OPT_GLOBAL) || !(args->os_flags & OPT_LOCAL)) && s[0] == NUL) { xfree(*varp); diff --git a/src/nvim/po/sv.po b/src/nvim/po/sv.po index 4fc307d3e5..2989ff7f9e 100644 --- a/src/nvim/po/sv.po +++ b/src/nvim/po/sv.po @@ -460,13 +460,6 @@ msgstr "Katakana" msgid "Bopomofo" msgstr "Bopomofo" -#. row number, column number is appended -#. l10n: leave as-is unless a space after the comma is preferred -#. l10n: do not add any row/column label, due to the limited space -#, c-format -msgid "%ld," -msgstr "%ld," - msgid "" "\n" "\tLast set from " diff --git a/src/nvim/po/uk.po b/src/nvim/po/uk.po index fb5db5f7ce..2508dee750 100644 --- a/src/nvim/po/uk.po +++ b/src/nvim/po/uk.po @@ -5477,10 +5477,6 @@ msgstr "Замінити «%.*s» на:" msgid " < \"%.*s\"" msgstr " < «%.*s»" -#, c-format -msgid "%," -msgstr "%, " - msgid "[Help]" msgstr "[Допомога]" diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index c1579ab2b2..3a7f0cd0fd 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -480,7 +480,6 @@ void win_redr_winbar(win_T *wp) void redraw_ruler(void) { static bool did_show_ruler = false; - static int did_ruler_col = -1; win_T *wp = !curwin->w_config.hide && curwin->w_status_height == 0 ? curwin : lastwin_nofloating(NULL); bool is_stl_global = global_stl_height() > 0; @@ -491,15 +490,10 @@ void redraw_ruler(void) if (did_show_ext_ruler && ui_has(kUIMessages)) { ui_call_msg_ruler((Array)ARRAY_DICT_INIT); did_show_ext_ruler = false; - } else if (did_ruler_col > 0) { - msg_col = did_ruler_col; - msg_row = Rows - 1; - } - if (did_show_ruler && !ui_has(kUIMessages)) { + } else if (did_show_ruler && !ui_has(kUIMessages)) { msg_clr_eos(); } did_show_ruler = false; - did_ruler_col = -1; return; } @@ -515,99 +509,8 @@ void redraw_ruler(void) return; } - if (*p_ruf && (p_ch > 0 || ui_has(kUIMessages))) { - win_redr_stl_expr(wp, false, true, ui_has(kUIMessages)); - did_show_ruler = !ui_has(kUIMessages); - return; - } - - hlf_T group = HLF_MSG; - int off = wp->w_status_height ? wp->w_wincol : 0; - int width = wp->w_status_height ? wp->w_width : Columns; - schar_T fillchar = schar_from_ascii(' '); - int attr = HL_ATTR(group); - - // 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, 0); - wp->w_p_list = true; - } - - // Check if not in Insert mode and the line is empty (will show "0-1"). - int empty_line = (State & MODE_INSERT) == 0 - && *ml_get_buf(wp->w_buffer, wp->w_cursor.lnum) == NUL; - -#define RULER_BUF_LEN 70 - char buffer[RULER_BUF_LEN]; - - // row number, column number is appended - // l10n: leave as-is unless a space after the comma is preferred - // l10n: do not add any row/column label, due to the limited space - int bufferlen = vim_snprintf(buffer, RULER_BUF_LEN, _("%" PRId64 ","), - (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) - ? 0 - : (int64_t)wp->w_cursor.lnum); - bufferlen += col_print(buffer + bufferlen, RULER_BUF_LEN - (size_t)bufferlen, - empty_line ? 0 : (int)wp->w_cursor.col + 1, - (int)virtcol + 1); - - // Add a "50%" if there is room for it. - 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); - - int this_ru_col = ru_col - (Columns - width); - // Never use more than half the window/screen width, leave the other half - // for the filename. - int n2 = (width + 1) / 2; - this_ru_col = MAX(this_ru_col, n2); - if (this_ru_col + n1 < width) { - // need at least space for rel_pos + NUL - while (this_ru_col + n1 < width - && RULER_BUF_LEN > bufferlen + rel_poslen + 1) { // +1 for NUL - bufferlen += (int)schar_get(buffer + bufferlen, fillchar); - n1++; - } - bufferlen += vim_snprintf(buffer + bufferlen, RULER_BUF_LEN - (size_t)bufferlen, - "%s", rel_pos); - } - (void)bufferlen; - - if (ui_has(kUIMessages)) { - MAXSIZE_TEMP_ARRAY(content, 1); - MAXSIZE_TEMP_ARRAY(chunk, 3); - ADD_C(chunk, INTEGER_OBJ(attr)); - ADD_C(chunk, CSTR_AS_OBJ(buffer)); - ADD_C(chunk, INTEGER_OBJ(HLF_MSG)); - assert(attr == HL_ATTR(HLF_MSG)); - ADD_C(content, ARRAY_OBJ(chunk)); - ui_call_msg_ruler(content); - did_show_ext_ruler = true; - did_ruler_col = 1; - } else { - if (did_show_ext_ruler) { - ui_call_msg_ruler((Array)ARRAY_DICT_INIT); - did_show_ext_ruler = false; - } - // Truncate at window boundary. - for (n1 = 0, n2 = 0; buffer[n1] != NUL; n1 += utfc_ptr2len(buffer + n1)) { - n2 += utf_ptr2cells(buffer + n1); - if (this_ru_col + n2 > width) { - bufferlen = n1; - buffer[bufferlen] = NUL; - break; - } - } - - grid_line_start(&msg_grid_adj, Rows - 1); - did_show_ruler = true; - did_ruler_col = off + this_ru_col; - int w = grid_line_puts(did_ruler_col, buffer, -1, attr); - grid_line_fill(did_ruler_col + w, off + width, fillchar, attr); - grid_line_flush(); - } + win_redr_stl_expr(wp, false, true, ui_has(kUIMessages)); + did_show_ruler = !ui_has(kUIMessages); } /// Get the character to use in a status line. Get its attributes in "*attr". diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua index cd6892d07b..f79b8aad12 100644 --- a/test/functional/api/window_spec.lua +++ b/test/functional/api/window_spec.lua @@ -561,14 +561,14 @@ describe('API/win', function() api.nvim_set_option_value('statusline', 'window-status', { win = 0 }) eq('window-status', api.nvim_get_option_value('statusline', { win = 0 })) eq( - "%<%f %{%nvim_eval_statusline('%h%w%m%r', {'maxwidth': 30}).width > 0 ? '%h%w%m%r ' : ''%}%=%{% &showcmdloc == 'statusline' ? '%-10.S ' : '' %}%{% exists('b:keymap_name') ? '<'..b:keymap_name..'> ' : '' %}%{% &ruler ? ( &rulerformat == '' ? '%-14.(%l,%c%V%) %P' : &rulerformat ) : '' %}", + "%<%f %{%nvim_eval_statusline('%h%w%m%r', {'maxwidth': 30}).width > 0 ? '%h%w%m%r ' : ''%}%=%{% &showcmdloc == 'statusline' ? '%-10.S ' : '' %}%{% exists('b:keymap_name') ? '<'..b:keymap_name..'> ' : '' %}%{% &ruler ? &rulerformat : '' %}", api.nvim_get_option_value('statusline', { scope = 'global' }) ) command('set modified') command('enew') -- global-local: not preserved in new buffer -- confirm local value was not copied eq( - "%<%f %{%nvim_eval_statusline('%h%w%m%r', {'maxwidth': 30}).width > 0 ? '%h%w%m%r ' : ''%}%=%{% &showcmdloc == 'statusline' ? '%-10.S ' : '' %}%{% exists('b:keymap_name') ? '<'..b:keymap_name..'> ' : '' %}%{% &ruler ? ( &rulerformat == '' ? '%-14.(%l,%c%V%) %P' : &rulerformat ) : '' %}", + "%<%f %{%nvim_eval_statusline('%h%w%m%r', {'maxwidth': 30}).width > 0 ? '%h%w%m%r ' : ''%}%=%{% &showcmdloc == 'statusline' ? '%-10.S ' : '' %}%{% exists('b:keymap_name') ? '<'..b:keymap_name..'> ' : '' %}%{% &ruler ? &rulerformat : '' %}", api.nvim_get_option_value('statusline', { win = 0 }) ) eq('', eval('&l:statusline')) diff --git a/test/functional/testnvim.lua b/test/functional/testnvim.lua index 7ee91de5ad..b826601cee 100644 --- a/test/functional/testnvim.lua +++ b/test/functional/testnvim.lua @@ -27,7 +27,8 @@ M.nvim_set = ( 'set shortmess+=IS background=light noswapfile noautoindent startofline' .. ' laststatus=1 undodir=. directory=. viewdir=. backupdir=.' .. " belloff= wildoptions-=pum joinspaces noshowcmd noruler nomore redrawdebug=invalid shada=!,'100,<50,s10,h" - .. [[ statusline=%<%f\ %{%nvim_eval_statusline('%h%w%m%r',\ {'maxwidth':\ 30}).width\ >\ 0\ ?\ '%h%w%m%r\ '\ :\ ''%}%=%{%\ &showcmdloc\ ==\ 'statusline'\ ?\ '%-10.S\ '\ :\ ''\ %}%{%\ exists('b:keymap_name')\ ?\ '<'..b:keymap_name..'>\ '\ :\ ''\ %}%{%\ &ruler\ ?\ (\ &rulerformat\ ==\ ''\ ?\ '%-14.(%l,%c%V%)\ %P'\ :\ &rulerformat\ )\ :\ ''\ %}]] + .. [[ statusline=%<%f\ %{%nvim_eval_statusline('%h%w%m%r',\ {'maxwidth':\ 30}).width\ >\ 0\ ?\ '%h%w%m%r\ '\ :\ ''%}%=%{%\ &showcmdloc\ ==\ 'statusline'\ ?\ '%-10.S\ '\ :\ ''\ %}%{%\ exists('b:keymap_name')\ ?\ '<'..b:keymap_name..'>\ '\ :\ ''\ %}%{%\ &ruler\ ?\ &rulerformat\ :\ ''\ %}]] + .. ' rulerformat=%18(%l,%c%V%=%P%)' ) M.nvim_argv = { M.nvim_prog, diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua index 604e7e0d36..654475e8f3 100644 --- a/test/functional/ui/messages_spec.lua +++ b/test/functional/ui/messages_spec.lua @@ -936,7 +936,7 @@ describe('ui/ext_messages', function() end) it("supports 'showcmd' and 'ruler(format)'", function() - command('set showcmd ruler') + command('set showcmd ruler rulerformat=%12(%l,%c%V%=%P%)') command('hi link MsgArea ErrorMsg') screen:expect({ grid = [[ diff --git a/test/functional/ui/statusline_spec.lua b/test/functional/ui/statusline_spec.lua index d386f8bf52..509ac13fa4 100644 --- a/test/functional/ui/statusline_spec.lua +++ b/test/functional/ui/statusline_spec.lua @@ -818,9 +818,6 @@ describe('statusline', function() {2:[No Name] 0,0-1 All}| 0,0-1 All| ]]) - command('set rulerformat=%18(%l,%c%V%=%P%)') - screen:expect_unchanged() - command('set rulerformat&') api.nvim_win_close(win, true) screen:expect([[ ^ | @@ -1145,7 +1142,7 @@ describe('default statusline', function() "%{% exists('b:keymap_name') ? '<'..b:keymap_name..'> ' : '' %}", "%{% &busy > 0 ? '◐ ' : '' %}", "%{% luaeval('(package.loaded[''vim.diagnostic''] and next(vim.diagnostic.count()) and vim.diagnostic.status() .. '' '') or '''' ') %}", - "%{% &ruler ? ( &rulerformat == '' ? '%-14.(%l,%c%V%) %P' : &rulerformat ) : '' %}", + "%{% &ruler ? &rulerformat : '' %}", }) exec_lua("vim.o.statusline = ''") @@ -1298,6 +1295,48 @@ describe('default statusline', function() end) end) +describe('default rulerformat', function() + local screen + + before_each(function() + clear() + screen = Screen.new(60, 16) + command('set ruler') + end) + + it('setting rulerformat to empty string sets default rulerformat', function() + exec_lua("vim.o.rulerformat = 'hjkl'") + eq('hjkl', eval('&rulerformat')) + screen:expect([[ + ^ | + {1:~ }|*14 + hjkl | + ]]) + + local default_rulerformat = '%18(%l,%c%V%= %P%)%<' + + exec_lua("vim.o.rulerformat = ''") + eq(default_rulerformat, eval('&rulerformat')) + screen:expect([[ + ^ | + {1:~ }|*14 + 0,0-1 All| + ]]) + + -- Reset to default if there's an error. + command('set rulerformat=%{a%}') + eq(default_rulerformat, eval('&rulerformat')) + eq(default_rulerformat, eval('&g:rulerformat')) + eq(default_rulerformat, eval('&l:rulerformat')) + command('redrawstatus') -- like Vim, rulerformat isn't immediately redrawn after an error + screen:expect([[ + ^ | + {1:~ }|*14 + {9:E121: Undefined variable: a} 0,0-1 All| + ]]) + end) +end) + describe("'statusline' in floatwin", function() local screen before_each(function()