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)
This commit is contained in:
Sébastien Hoffmann
2026-07-20 21:35:16 +02:00
parent 9be130304d
commit ca07e505f9
12 changed files with 224 additions and 226 deletions

View File

@@ -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