diff options
author | Shadman <shadmansaleh3@gmail.com> | 2021-05-15 23:10:41 +0600 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-05-15 13:10:41 -0400 |
commit | d67dcaba02d76fe92ba818dde7b672fe6956a100 (patch) | |
tree | e03e0cc41d3744fdc57c20393508bf18a472b0b6 | |
parent | 0cd14303162df99bbd796002a44588aface2bad8 (diff) | |
download | rneovim-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.txt | 12 | ||||
-rw-r--r-- | src/nvim/buffer.c | 61 | ||||
-rw-r--r-- | src/nvim/option.c | 4 | ||||
-rw-r--r-- | src/nvim/testdir/test_statusline.vim | 20 |
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()) |