]> granicus.if.org Git - vim/commitdiff
patch 8.2.2854: custom statusline cannot contain % items v8.2.2854
authorshadmansaleh <shadmansaleh3@gmail.com>
Sat, 15 May 2021 15:23:28 +0000 (17:23 +0200)
committerBram Moolenaar <Bram@vim.org>
Sat, 15 May 2021 15:23:28 +0000 (17:23 +0200)
Problem:    Custom statusline cannot contain % items.
Solution:   Add "%{% expr %}". (closes #8190)

runtime/doc/options.txt
src/buffer.c
src/optionstr.c
src/testdir/test_statusline.vim
src/version.c

index efab64802e5c9551bef73c74d30557114cdba9f6..9cff98da74f212f58df7ccd88738f86052010539 100644 (file)
@@ -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.
index f80d42494386a4375cfc2e335e40478cad341d98..d8b7ed4f23764a2eaa35711d9bc6ddd63a0544bc 100644 (file)
 
 #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);
index 1ec8d05d6c1003805ed5e2754fbf5138fd0b4101..f332294ed83714605dcae23bc8fe4d6552f7d4b7 100644 (file)
@@ -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");
index ca28379f5dd5c84d7978b0ff698fb227f05c3655..f3eea2e71eb132c1eb75f4460b138698743a40c5 100644 (file)
@@ -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())
index d04ebfbf7ddb950a3f46e7f536e6c8e1562782f9..7635c2f470f63e74e7f15020ba15b47b2fcf2d39 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2854,
 /**/
     2853,
 /**/