aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShadman <shadmansaleh3@gmail.com>2021-05-15 23:10:41 +0600
committerGitHub <noreply@github.com>2021-05-15 13:10:41 -0400
commitd67dcaba02d76fe92ba818dde7b672fe6956a100 (patch)
treee03e0cc41d3744fdc57c20393508bf18a472b0b6
parent0cd14303162df99bbd796002a44588aface2bad8 (diff)
downloadrneovim-d67dcaba02d76fe92ba818dde7b672fe6956a100.tar.gz
rneovim-d67dcaba02d76fe92ba818dde7b672fe6956a100.tar.bz2
rneovim-d67dcaba02d76fe92ba818dde7b672fe6956a100.zip
vim-patch:8.2.2854: custom statusline cannot contain % items (#14558)
Problem: Custom statusline cannot contain % items. Solution: Add "%{% expr %}". (closes vim/vim#8190) https://github.com/vim/vim/commit/30e3de21fc36153c5f7c9cf9db90bcc60dd67fb9
-rw-r--r--runtime/doc/options.txt12
-rw-r--r--src/nvim/buffer.c61
-rw-r--r--src/nvim/option.c4
-rw-r--r--src/nvim/testdir/test_statusline.vim20
4 files changed, 94 insertions, 3 deletions
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 2527a91cc8..4a6ae0245b 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -5923,6 +5923,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/nvim/buffer.c b/src/nvim/buffer.c
index 5e700dea8a..6a50264e0f 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -85,6 +85,9 @@
# include "buffer.c.generated.h"
#endif
+// Determines how deeply nested %{} blocks will be evaluated in statusline.
+#define MAX_STL_EVAL_DEPTH 100
+
static char *msg_loclist = N_("[Location List]");
static char *msg_qflist = N_("[Quickfix List]");
static char *e_auabort = N_("E855: Autocommands caused command to abort");
@@ -3572,6 +3575,7 @@ int build_stl_str_hl(
}
int groupdepth = 0;
+ int evaldepth = 0;
int curitem = 0;
bool prevchar_isflag = true;
@@ -3909,6 +3913,13 @@ int build_stl_str_hl(
continue;
}
+ // Denotes end of expanded %{} block
+ if (*fmt_p == '}' && evaldepth > 0) {
+ fmt_p++;
+ evaldepth--;
+ continue;
+ }
+
// An invalid item was specified.
// Continue processing on the next character of the format string.
if (vim_strchr(STL_ALL, *fmt_p) == NULL) {
@@ -3950,18 +3961,30 @@ int build_stl_str_hl(
}
case STL_VIM_EXPR: // '{'
{
+ char_u *block_start = fmt_p - 1;
+ int reevaluate = (*fmt_p == '%');
itemisflag = true;
+ if (reevaluate) {
+ fmt_p++;
+ }
+
// Attempt to copy the expression to evaluate into
// the output buffer as a null-terminated string.
char_u *t = out_p;
- while (*fmt_p != '}' && *fmt_p != NUL && out_p < out_end_p)
+ while ((*fmt_p != '}' || (reevaluate && fmt_p[-1] != '%'))
+ && *fmt_p != NUL && out_p < out_end_p) {
*out_p++ = *fmt_p++;
+ }
if (*fmt_p != '}') { // missing '}' or out of space
break;
}
fmt_p++;
- *out_p = 0;
+ if (reevaluate) {
+ out_p[-1] = 0; // remove the % at the end of %{% expr %}
+ } else {
+ *out_p = 0;
+ }
// Move our position in the output buffer
// to the beginning of the expression
@@ -4007,6 +4030,40 @@ int 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 *)fmt_p);
+ size_t new_fmt_len = parsed_usefmt
+ + str_length + fmt_length + 3;
+ char_u *new_fmt = (char_u *)xmalloc(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 , fmt_p, fmt_length)
+ + fmt_length;
+ *new_fmt_p = 0;
+ new_fmt_p = NULL;
+
+ if (usefmt != fmt) {
+ xfree(usefmt);
+ }
+ XFREE_CLEAR(str);
+ usefmt = new_fmt;
+ fmt_p = usefmt + parsed_usefmt;
+ evaldepth++;
+ continue;
+ }
break;
}
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 6f28f37d2b..ad481af7fa 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -3641,9 +3641,11 @@ char_u *check_stl_option(char_u *s)
return illegal_char(errbuf, sizeof(errbuf), *s);
}
if (*s == '{') {
+ int reevaluate = (*s == '%');
s++;
- while (*s != '}' && *s)
+ while ((*s != '}' || (reevaluate && s[-1] != '%')) && *s) {
s++;
+ }
if (*s != '}') {
return (char_u *)N_("E540: Unclosed expression sequence");
}
diff --git a/src/nvim/testdir/test_statusline.vim b/src/nvim/testdir/test_statusline.vim
index f5b6446108..a3e4dcdd25 100644
--- a/src/nvim/testdir/test_statusline.vim
+++ b/src/nvim/testdir/test_statusline.vim
@@ -241,6 +241,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())