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] 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()