To: vim_dev@googlegroups.com Subject: Patch 8.2.2854 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.2854 Problem: Custom statusline cannot contain % items. Solution: Add "%{% expr %}". (closes #8190) Files: runtime/doc/options.txt, src/buffer.c, src/optionstr.c, src/testdir/test_statusline.vim *** ../vim-8.2.2853/runtime/doc/options.txt 2021-05-06 18:46:31.039085745 +0200 --- runtime/doc/options.txt 2021-05-15 16:54:06.138186893 +0200 *************** *** 7334,7339 **** --- 7339,7356 ---- 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. *** ../vim-8.2.2853/src/buffer.c 2021-03-04 21:55:54.601235264 +0100 --- src/buffer.c 2021-05-15 16:59:14.820874600 +0200 *************** *** 27,32 **** --- 27,38 ---- #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,4118 **** --- 4119,4127 ---- int group_end_userhl; int group_start_userhl; int groupdepth; + #ifdef FEAT_EVAL + int evaldepth; + #endif int minwid; int maxwid; int zeropad; *************** *** 4187,4192 **** --- 4196,4204 ---- byteval = (*mb_ptr2char)(p + wp->w_cursor.col); groupdepth = 0; + #ifdef FEAT_EVAL + evaldepth = 0; + #endif p = out; curitem = 0; prevchar_isflag = TRUE; *************** *** 4447,4452 **** --- 4459,4473 ---- 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,4497 **** break; case STL_VIM_EXPR: // '{' itemisflag = TRUE; t = p; ! while (*s != '}' && *s != NUL && p + 1 < out + outlen) *p++ = *s++; if (*s != '}') // missing '}' or out of space break; s++; ! *p = 0; p = t; - #ifdef FEAT_EVAL vim_snprintf((char *)buf_tmp, sizeof(buf_tmp), "%d", curbuf->b_fnum); --- 4503,4529 ---- 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 != '}' || (reevaluate && s[-1] != '%')) ! && *s != NUL && p + 1 < out + outlen) *p++ = *s++; if (*s != '}') // missing '}' or out of space break; s++; ! 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,4533 **** itemisflag = FALSE; } } #endif break; ! case STL_LINE: num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) ? 0L : (long)(wp->w_cursor.lnum); --- 4557,4598 ---- 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); *** ../vim-8.2.2853/src/optionstr.c 2021-05-03 20:40:35.263818664 +0200 --- src/optionstr.c 2021-05-15 17:00:55.640446428 +0200 *************** *** 618,625 **** } if (*s == '{') { s++; ! while (*s != '}' && *s) s++; if (*s != '}') return N_("E540: Unclosed expression sequence"); --- 618,627 ---- } if (*s == '{') { + int reevaluate = (*s == '%'); + s++; ! while ((*s != '}' || (reevaluate && s[-1] != '%')) && *s) s++; if (*s != '}') return N_("E540: Unclosed expression sequence"); *** ../vim-8.2.2853/src/testdir/test_statusline.vim 2021-04-08 18:27:49.525472168 +0200 --- src/testdir/test_statusline.vim 2021-05-15 17:20:46.167510458 +0200 *************** *** 251,256 **** --- 251,276 ---- 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()) *** ../vim-8.2.2853/src/version.c 2021-05-15 15:09:02.144699051 +0200 --- src/version.c 2021-05-15 17:21:18.095377858 +0200 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 2854, /**/ -- This is an airconditioned room, do not open Windows. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///