From d214c24129f0f63c8c1739d655595565a6d4d059 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 26 May 2026 09:04:24 +0800 Subject: [PATCH] vim-patch:9.2.0538: Cannot keep leading whitespace in %{} statusline expr (#40007) Problem: A leading space in the result of a %{} item is sometimes stripped, and an all-digit result is converted to a number. Solution: Add %0{} atom which inserts the expression result verbatim (glepnir) fixes: vim/vim#3898 closes: vim/vim#20315 https://github.com/vim/vim/commit/e8d7a40b98ce4062834399f097216213a0088ff3 Co-authored-by: glepnir --- runtime/doc/news.txt | 1 + runtime/doc/options.txt | 5 ++++- runtime/lua/vim/_meta/options.gen.lua | 5 ++++- src/nvim/options.lua | 5 ++++- src/nvim/statusline.c | 5 +++-- test/old/testdir/test_statusline.vim | 10 ++++++++++ 6 files changed, 26 insertions(+), 5 deletions(-) diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index a13e4ab872..024e30ca06 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -215,6 +215,7 @@ OPTIONS • 'ttyfast' can be disabled during startup by setting |$NVIM_NOTTYFAST|. • 'scrolloffpad' allows vertically centering cursor at the end of file. • 'shortmess' flag |shm-u| silences undo/redo messages. +• 'statusline' supports |stl-%0{| to insert the expression result verbatim. • 'winpinned' prevents window from closing unless specifically targeted. PERFORMANCE diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index f52fb73768..87772522c2 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -6420,7 +6420,8 @@ A jump table for the options with a short description can be found at |Q_op|. { NF Evaluate expression between "%{" and "}" and substitute result. Note that there is no "%" before the closing "}". The expression cannot contain a "}" character, call a function to - work around that. See |stl-%{| below. + work around that. See |stl-%{| below. Use "%0{" to insert the + result verbatim. `{%` - This is almost same as "{" except the result of the expression is re-evaluated as a statusline format string. Thus if the return value of expr contains "%" items they will get expanded. @@ -6523,6 +6524,8 @@ A jump table for the options with a short description can be found at |Q_op|. A result of all digits is regarded a number for display purposes. Otherwise the result is taken as flag text and applied to the rules described above. + *stl-%0{* + With %0{ neither applies: the result is inserted as a literal string. Watch out for errors in expressions. They may render Vim unusable! If you are stuck, hold down ':' or 'Q' to get a prompt, then quit and diff --git a/runtime/lua/vim/_meta/options.gen.lua b/runtime/lua/vim/_meta/options.gen.lua index 48ad6cbaf5..fefc7d7eb7 100644 --- a/runtime/lua/vim/_meta/options.gen.lua +++ b/runtime/lua/vim/_meta/options.gen.lua @@ -6817,7 +6817,8 @@ vim.wo.stc = vim.wo.statuscolumn --- { NF Evaluate expression between "%{" and "}" and substitute result. --- Note that there is no "%" before the closing "}". The --- expression cannot contain a "}" character, call a function to ---- work around that. See `stl-%{` below. +--- work around that. See `stl-%{` below. Use "%0{" to insert the +--- result verbatim. --- `{%` - This is almost same as "{" except the result of the expression is --- re-evaluated as a statusline format string. Thus if the --- return value of expr contains "%" items they will get expanded. @@ -6926,6 +6927,8 @@ vim.wo.stc = vim.wo.statuscolumn --- A result of all digits is regarded a number for display purposes. --- Otherwise the result is taken as flag text and applied to the rules --- described above. +--- *stl-%0{* +--- With %0{ neither applies: the result is inserted as a literal string. --- --- Watch out for errors in expressions. They may render Vim unusable! --- If you are stuck, hold down ':' or 'Q' to get a prompt, then quit and diff --git a/src/nvim/options.lua b/src/nvim/options.lua index c1072183f2..7a74739ce0 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -8879,7 +8879,8 @@ local options = { { NF Evaluate expression between "%{" and "}" and substitute result. Note that there is no "%" before the closing "}". The expression cannot contain a "}" character, call a function to - work around that. See |stl-%{| below. + work around that. See |stl-%{| below. Use "%0{" to insert the + result verbatim. `{%` - This is almost same as "{" except the result of the expression is re-evaluated as a statusline format string. Thus if the return value of expr contains "%" items they will get expanded. @@ -8982,6 +8983,8 @@ local options = { A result of all digits is regarded a number for display purposes. Otherwise the result is taken as flag text and applied to the rules described above. + *stl-%0{* + With %0{ neither applies: the result is inserted as a literal string. Watch out for errors in expressions. They may render Vim unusable! If you are stuck, hold down ':' or 'Q' to get a prompt, then quit and diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index f85f24dfcb..72bc5aa963 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -1360,11 +1360,12 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op { char *block_start = fmt_p - 1; bool reevaluate = (*fmt_p == '%'); - itemisflag = true; if (reevaluate) { fmt_p++; } + // %0{} keeps the result verbatim + itemisflag = zeropad ? false : true; // Attempt to copy the expression to evaluate into // the output buffer as a null-terminated string. @@ -1420,7 +1421,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op // Check if the evaluated result is a number. // If so, convert the number to an int and free the string. - if (str != NULL && *str != NUL) { + if (!zeropad && str != NULL && *str != NUL) { if (*skipdigits(str) == NUL) { num = atoi(str); XFREE_CLEAR(str); diff --git a/test/old/testdir/test_statusline.vim b/test/old/testdir/test_statusline.vim index d50fa2edc6..75b5d4be91 100644 --- a/test/old/testdir/test_statusline.vim +++ b/test/old/testdir/test_statusline.vim @@ -286,6 +286,16 @@ func Test_statusline() call assert_match('^vimLineComment\s*$', s:get_statusline()) syntax off + " %0{: result of expression is inserted verbatim + set statusline=%{'\ x'} + call assert_match('^x\s*$', s:get_statusline()) + set statusline=%0{'\ x'} + call assert_match('^ x\s*$', s:get_statusline()) + set statusline=%{'000'} + call assert_match('^0\s*$', s:get_statusline()) + set statusline=%0{'000'} + call assert_match('^000\s*$', s:get_statusline()) + "%{%expr%}: evaluates expressions present in result of expr func! Inner_eval() return '%n some other text'