Skip to content

Commit 30e3de2

Browse files
shadmansalehbrammool
authored andcommitted
patch 8.2.2854: custom statusline cannot contain % items
Problem: Custom statusline cannot contain % items. Solution: Add "%{% expr %}". (closes #8190)
1 parent d832c3c commit 30e3de2

File tree

5 files changed

+106
-5
lines changed

5 files changed

+106
-5
lines changed

runtime/doc/options.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7339,6 +7339,18 @@ A jump table for the options with a short description can be found at |Q_op|.
73397339
Note that there is no '%' before the closing '}'. The
73407340
expression cannot contain a '}' character, call a function to
73417341
work around that. See |stl-%{| below.
7342+
{% - This is almost same as { except the result of the expression is
7343+
re-evaluated as a statusline format string. Thus if the
7344+
return value of expr contains % items they will get expanded.
7345+
The expression can contain the } character, the end of
7346+
expression is denoted by %}.
7347+
The For example: >
7348+
func! Stl_filename() abort
7349+
return "%t"
7350+
endfunc
7351+
< `stl=%{Stl_filename()}` results in `"%t"`
7352+
`stl=%{%Stl_filename()%}` results in `"Name of current file"`
7353+
} - End of `{%` expression
73427354
( - Start of item group. Can be used for setting the width and
73437355
alignment of a section. Must be followed by %) somewhere.
73447356
) - End of item group. No width fields allowed.

src/buffer.c

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@
2727

2828
#include "vim.h"
2929

30+
31+
#ifdef FEAT_EVAL
32+
// Determines how deeply nested %{} blocks will be evaluated in statusline.
33+
# define MAX_STL_EVAL_DEPTH 100
34+
#endif
35+
3036
static void enter_buffer(buf_T *buf);
3137
static void buflist_getfpos(void);
3238
static char_u *buflist_match(regmatch_T *rmp, buf_T *buf, int ignore_case);
@@ -4113,6 +4119,9 @@ build_stl_str_hl(
41134119
int group_end_userhl;
41144120
int group_start_userhl;
41154121
int groupdepth;
4122+
#ifdef FEAT_EVAL
4123+
int evaldepth;
4124+
#endif
41164125
int minwid;
41174126
int maxwid;
41184127
int zeropad;
@@ -4187,6 +4196,9 @@ build_stl_str_hl(
41874196
byteval = (*mb_ptr2char)(p + wp->w_cursor.col);
41884197

41894198
groupdepth = 0;
4199+
#ifdef FEAT_EVAL
4200+
evaldepth = 0;
4201+
#endif
41904202
p = out;
41914203
curitem = 0;
41924204
prevchar_isflag = TRUE;
@@ -4447,6 +4459,15 @@ build_stl_str_hl(
44474459
curitem++;
44484460
continue;
44494461
}
4462+
#ifdef FEAT_EVAL
4463+
// Denotes end of expanded %{} block
4464+
if (*s == '}' && evaldepth > 0)
4465+
{
4466+
s++;
4467+
evaldepth--;
4468+
continue;
4469+
}
4470+
#endif
44504471
if (vim_strchr(STL_ALL, *s) == NULL)
44514472
{
44524473
s++;
@@ -4482,16 +4503,27 @@ build_stl_str_hl(
44824503
break;
44834504

44844505
case STL_VIM_EXPR: // '{'
4506+
{
4507+
#ifdef FEAT_EVAL
4508+
char_u *block_start = s - 1;
4509+
#endif
4510+
int reevaluate = (*s == '%');
4511+
4512+
if (reevaluate)
4513+
s++;
44854514
itemisflag = TRUE;
44864515
t = p;
4487-
while (*s != '}' && *s != NUL && p + 1 < out + outlen)
4516+
while ((*s != '}' || (reevaluate && s[-1] != '%'))
4517+
&& *s != NUL && p + 1 < out + outlen)
44884518
*p++ = *s++;
44894519
if (*s != '}') // missing '}' or out of space
44904520
break;
44914521
s++;
4492-
*p = 0;
4522+
if (reevaluate)
4523+
p[-1] = 0; // remove the % at the end of %{% expr %}
4524+
else
4525+
*p = 0;
44934526
p = t;
4494-
44954527
#ifdef FEAT_EVAL
44964528
vim_snprintf((char *)buf_tmp, sizeof(buf_tmp),
44974529
"%d", curbuf->b_fnum);
@@ -4525,9 +4557,42 @@ build_stl_str_hl(
45254557
itemisflag = FALSE;
45264558
}
45274559
}
4560+
4561+
// If the output of the expression needs to be evaluated
4562+
// replace the %{} block with the result of evaluation
4563+
if (reevaluate && str != NULL && *str != 0
4564+
&& strchr((const char *)str, '%') != NULL
4565+
&& evaldepth < MAX_STL_EVAL_DEPTH)
4566+
{
4567+
size_t parsed_usefmt = (size_t)(block_start - usefmt);
4568+
size_t str_length = strlen((const char *)str);
4569+
size_t fmt_length = strlen((const char *)s);
4570+
size_t new_fmt_len = parsed_usefmt
4571+
+ str_length + fmt_length + 3;
4572+
char_u *new_fmt = (char_u *)alloc(new_fmt_len * sizeof(char_u));
4573+
char_u *new_fmt_p = new_fmt;
4574+
4575+
new_fmt_p = (char_u *)memcpy(new_fmt_p, usefmt, parsed_usefmt)
4576+
+ parsed_usefmt;
4577+
new_fmt_p = (char_u *)memcpy(new_fmt_p , str, str_length)
4578+
+ str_length;
4579+
new_fmt_p = (char_u *)memcpy(new_fmt_p, "%}", 2) + 2;
4580+
new_fmt_p = (char_u *)memcpy(new_fmt_p , s, fmt_length)
4581+
+ fmt_length;
4582+
*new_fmt_p = 0;
4583+
new_fmt_p = NULL;
4584+
4585+
if (usefmt != fmt)
4586+
vim_free(usefmt);
4587+
VIM_CLEAR(str);
4588+
usefmt = new_fmt;
4589+
s = usefmt + parsed_usefmt;
4590+
evaldepth++;
4591+
continue;
4592+
}
45284593
#endif
45294594
break;
4530-
4595+
}
45314596
case STL_LINE:
45324597
num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY)
45334598
? 0L : (long)(wp->w_cursor.lnum);

src/optionstr.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -618,8 +618,10 @@ check_stl_option(char_u *s)
618618
}
619619
if (*s == '{')
620620
{
621+
int reevaluate = (*s == '%');
622+
621623
s++;
622-
while (*s != '}' && *s)
624+
while ((*s != '}' || (reevaluate && s[-1] != '%')) && *s)
623625
s++;
624626
if (*s != '}')
625627
return N_("E540: Unclosed expression sequence");

src/testdir/test_statusline.vim

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,26 @@ func Test_statusline()
251251
call assert_match('^vimLineComment\s*$', s:get_statusline())
252252
syntax off
253253

254+
"%{%expr%}: evaluates enxpressions present in result of expr
255+
func! Inner_eval()
256+
return '%n some other text'
257+
endfunc
258+
func! Outer_eval()
259+
return 'some text %{%Inner_eval()%}'
260+
endfunc
261+
set statusline=%{%Outer_eval()%}
262+
call assert_match('^some text ' . bufnr() . ' some other text\s*$', s:get_statusline())
263+
delfunc Inner_eval
264+
delfunc Outer_eval
265+
266+
"%{%expr%}: Doesn't get stuck in recursion
267+
func! Recurse_eval()
268+
return '%{%Recurse_eval()%}'
269+
endfunc
270+
set statusline=%{%Recurse_eval()%}
271+
call assert_match('^%{%Recurse_eval()%}\s*$', s:get_statusline())
272+
delfunc Recurse_eval
273+
254274
"%(: Start of item group.
255275
set statusline=ab%(cd%q%)de
256276
call assert_match('^abde\s*$', s:get_statusline())

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,8 @@ static char *(features[]) =
750750

751751
static int included_patches[] =
752752
{ /* Add new patch number below this line */
753+
/**/
754+
2854,
753755
/**/
754756
2853,
755757
/**/

0 commit comments

Comments
 (0)