summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorshadmansaleh <shadmansaleh3@gmail.com>2021-05-15 17:23:28 +0200
committerBram Moolenaar <Bram@vim.org>2021-05-15 17:23:28 +0200
commit30e3de21fc36153c5f7c9cf9db90bcc60dd67fb9 (patch)
tree009edc4fee70a74a78f74d680a58e0dd1b9ca5b3
parentd832c3c56ec7043322ad3e99897e312700fa1d3e (diff)
downloadvim-git-30e3de21fc36153c5f7c9cf9db90bcc60dd67fb9.tar.gz
patch 8.2.2854: custom statusline cannot contain % itemsv8.2.2854
Problem: Custom statusline cannot contain % items. Solution: Add "%{% expr %}". (closes #8190)
-rw-r--r--runtime/doc/options.txt12
-rw-r--r--src/buffer.c73
-rw-r--r--src/optionstr.c4
-rw-r--r--src/testdir/test_statusline.vim20
-rw-r--r--src/version.c2
5 files changed, 106 insertions, 5 deletions
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index efab64802..9cff98da7 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -7339,6 +7339,18 @@ A jump table for the options with a short description can be found at |Q_op|.
Note that there is no '%' before the closing '}'. The
expression cannot contain a '}' character, call a function to
work around that. See |stl-%{| below.
+ {% - 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.
+ The expression can contain the } character, the end of
+ expression is denoted by %}.
+ The For example: >
+ func! Stl_filename() abort
+ return "%t"
+ endfunc
+< `stl=%{Stl_filename()}` results in `"%t"`
+ `stl=%{%Stl_filename()%}` results in `"Name of current file"`
+ } - End of `{%` expression
( - Start of item group. Can be used for setting the width and
alignment of a section. Must be followed by %) somewhere.
) - End of item group. No width fields allowed.
diff --git a/src/buffer.c b/src/buffer.c
index f80d42494..d8b7ed4f2 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -27,6 +27,12 @@
#include "vim.h"
+
+#ifdef FEAT_EVAL
+// Determines how deeply nested %{} blocks will be evaluated in statusline.
+# define MAX_STL_EVAL_DEPTH 100
+#endif
+
static void enter_buffer(buf_T *buf);
static void buflist_getfpos(void);
static char_u *buflist_match(regmatch_T *rmp, buf_T *buf, int ignore_case);
@@ -4113,6 +4119,9 @@ build_stl_str_hl(
int group_end_userhl;
int group_start_userhl;
int groupdepth;
+#ifdef FEAT_EVAL
+ int evaldepth;
+#endif
int minwid;
int maxwid;
int zeropad;
@@ -4187,6 +4196,9 @@ build_stl_str_hl(
byteval = (*mb_ptr2char)(p + wp->w_cursor.col);
groupdepth = 0;
+#ifdef FEAT_EVAL
+ evaldepth = 0;
+#endif
p = out;
curitem = 0;
prevchar_isflag = TRUE;
@@ -4447,6 +4459,15 @@ build_stl_str_hl(
curitem++;
continue;
}
+#ifdef FEAT_EVAL
+ // Denotes end of expanded %{} block
+ if (*s == '}' && evaldepth > 0)
+ {
+ s++;
+ evaldepth--;
+ continue;
+ }
+#endif
if (vim_strchr(STL_ALL, *s) == NULL)
{
s++;
@@ -4482,16 +4503,27 @@ build_stl_str_hl(
break;
case STL_VIM_EXPR: // '{'
+ {
+#ifdef FEAT_EVAL
+ char_u *block_start = s - 1;
+#endif
+ int reevaluate = (*s == '%');
+
+ if (reevaluate)
+ s++;
itemisflag = TRUE;
t = p;
- while (*s != '}' && *s != NUL && p + 1 < out + outlen)
+ while ((*s != '}' || (reevaluate && s[-1] != '%'))
+ && *s != NUL && p + 1 < out + outlen)
*p++ = *s++;
if (*s != '}') // missing '}' or out of space
break;
s++;
- *p = 0;
+ if (reevaluate)
+ p[-1] = 0; // remove the % at the end of %{% expr %}
+ else
+ *p = 0;
p = t;
-
#ifdef FEAT_EVAL
vim_snprintf((char *)buf_tmp, sizeof(buf_tmp),
"%d", curbuf->b_fnum);
@@ -4525,9 +4557,42 @@ build_stl_str_hl(
itemisflag = FALSE;
}
}
+
+ // If the output of the expression needs to be evaluated
+ // replace the %{} block with the result of evaluation
+ if (reevaluate && str != NULL && *str != 0
+ && strchr((const char *)str, '%') != NULL
+ && evaldepth < MAX_STL_EVAL_DEPTH)
+ {
+ size_t parsed_usefmt = (size_t)(block_start - usefmt);
+ size_t str_length = strlen((const char *)str);
+ size_t fmt_length = strlen((const char *)s);
+ size_t new_fmt_len = parsed_usefmt
+ + str_length + fmt_length + 3;
+ char_u *new_fmt = (char_u *)alloc(new_fmt_len * sizeof(char_u));
+ char_u *new_fmt_p = new_fmt;
+
+ new_fmt_p = (char_u *)memcpy(new_fmt_p, usefmt, parsed_usefmt)
+ + parsed_usefmt;
+ new_fmt_p = (char_u *)memcpy(new_fmt_p , str, str_length)
+ + str_length;
+ new_fmt_p = (char_u *)memcpy(new_fmt_p, "%}", 2) + 2;
+ new_fmt_p = (char_u *)memcpy(new_fmt_p , s, fmt_length)
+ + fmt_length;
+ *new_fmt_p = 0;
+ new_fmt_p = NULL;
+
+ if (usefmt != fmt)
+ vim_free(usefmt);
+ VIM_CLEAR(str);
+ usefmt = new_fmt;
+ s = usefmt + parsed_usefmt;
+ evaldepth++;
+ continue;
+ }
#endif
break;
-
+ }
case STL_LINE:
num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY)
? 0L : (long)(wp->w_cursor.lnum);
diff --git a/src/optionstr.c b/src/optionstr.c
index 1ec8d05d6..f332294ed 100644
--- a/src/optionstr.c
+++ b/src/optionstr.c
@@ -618,8 +618,10 @@ check_stl_option(char_u *s)
}
if (*s == '{')
{
+ int reevaluate = (*s == '%');
+
s++;
- while (*s != '}' && *s)
+ while ((*s != '}' || (reevaluate && s[-1] != '%')) && *s)
s++;
if (*s != '}')
return N_("E540: Unclosed expression sequence");
diff --git a/src/testdir/test_statusline.vim b/src/testdir/test_statusline.vim
index ca28379f5..f3eea2e71 100644
--- a/src/testdir/test_statusline.vim
+++ b/src/testdir/test_statusline.vim
@@ -251,6 +251,26 @@ func Test_statusline()
call assert_match('^vimLineComment\s*$', s:get_statusline())
syntax off
+ "%{%expr%}: evaluates enxpressions present in result of expr
+ func! Inner_eval()
+ return '%n some other text'
+ endfunc
+ func! Outer_eval()
+ return 'some text %{%Inner_eval()%}'
+ endfunc
+ set statusline=%{%Outer_eval()%}
+ call assert_match('^some text ' . bufnr() . ' some other text\s*$', s:get_statusline())
+ delfunc Inner_eval
+ delfunc Outer_eval
+
+ "%{%expr%}: Doesn't get stuck in recursion
+ func! Recurse_eval()
+ return '%{%Recurse_eval()%}'
+ endfunc
+ set statusline=%{%Recurse_eval()%}
+ call assert_match('^%{%Recurse_eval()%}\s*$', s:get_statusline())
+ delfunc Recurse_eval
+
"%(: Start of item group.
set statusline=ab%(cd%q%)de
call assert_match('^abde\s*$', s:get_statusline())
diff --git a/src/version.c b/src/version.c
index d04ebfbf7..7635c2f47 100644
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 2854,
+/**/
2853,
/**/
2852,