# HG changeset patch # User Bram Moolenaar # Date 1603743306 -3600 # Node ID 92a100fc5e1790687285dc6ecf22759493d89eab # Parent 51ceeb95d655ced5d7c6012b7baa5a933865cd03 patch 8.2.1909: number of status line items is limited to 80 Commit: https://github.com/vim/vim/commit/8133cc6bf454eb90bb0868f7cf806fce5c0c9fe6 Author: Bram Moolenaar Date: Mon Oct 26 21:05:27 2020 +0100 patch 8.2.1909: number of status line items is limited to 80 Problem: Number of status line items is limited to 80. Solution: Dynamically allocate the arrays. (Rom Grk, closes https://github.com/vim/vim/issues/7181) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -7225,7 +7225,7 @@ A jump table for the options with a shor normal text. Each status line item is of the form: %-0{minwid}.{maxwid}{item} All fields except the {item} are optional. A single percent sign can - be given as "%%". Up to 80 items can be specified. *E541* + be given as "%%". When the option starts with "%!" then it is used as an expression, evaluated and the result is used as the option value. Example: > diff --git a/src/buffer.c b/src/buffer.c --- a/src/buffer.c +++ b/src/buffer.c @@ -673,7 +673,7 @@ aucmd_abort: buf->b_nwindows = nwindows; buf_freeall(buf, (del_buf ? BFA_DEL : 0) - + (wipe_buf ? BFA_WIPE : 0) + + (wipe_buf ? BFA_WIPE : 0) + (ignore_abort ? BFA_IGNORE_ABORT : 0)); // Autocommands may have deleted the buffer. @@ -4017,6 +4017,32 @@ free_titles(void) #endif // FEAT_TITLE #if defined(FEAT_STL_OPT) || defined(FEAT_GUI_TABLINE) || defined(PROTO) + +/* + * Used for building in the status line. + */ +typedef struct +{ + char_u *stl_start; + int stl_minwid; + int stl_maxwid; + enum { + Normal, + Empty, + Group, + Middle, + Highlight, + TabPage, + Trunc + } stl_type; +} stl_item_T; + +static size_t stl_items_len = 20; // Initial value, grows as needed. +static stl_item_T *stl_items = NULL; +static int *stl_groupitem = NULL; +static stl_hlrec_T *stl_hltab = NULL; +static stl_hlrec_T *stl_tabtab = NULL; + /* * Build a string from the status line items in "fmt". * Return length of string in screen cells. @@ -4040,8 +4066,8 @@ build_stl_str_hl( int use_sandbox UNUSED, // "fmt" was set insecurely, use sandbox int fillchar, int maxwidth, - struct stl_hlrec *hltab, // return: HL attributes (can be NULL) - struct stl_hlrec *tabtab) // return: tab page nrs (can be NULL) + stl_hlrec_T **hltab, // return: HL attributes (can be NULL) + stl_hlrec_T **tabtab) // return: tab page nrs (can be NULL) { linenr_T lnum; size_t len; @@ -4069,24 +4095,7 @@ build_stl_str_hl( int curitem; int group_end_userhl; int group_start_userhl; - int groupitem[STL_MAX_ITEM]; int groupdepth; - struct stl_item - { - char_u *start; - int minwid; - int maxwid; - enum - { - Normal, - Empty, - Group, - Middle, - Highlight, - TabPage, - Trunc - } type; - } item[STL_MAX_ITEM]; int minwid; int maxwid; int zeropad; @@ -4096,10 +4105,18 @@ build_stl_str_hl( char_u buf_tmp[TMPLEN]; char_u win_tmp[TMPLEN]; char_u *usefmt = fmt; - struct stl_hlrec *sp; + stl_hlrec_T *sp; int save_must_redraw = must_redraw; int save_redr_type = curwin->w_redr_type; + if (stl_items == NULL) + { + stl_items = ALLOC_MULT(stl_item_T, stl_items_len); + stl_groupitem = ALLOC_MULT(int, stl_items_len); + stl_hltab = ALLOC_MULT(stl_hlrec_T, stl_items_len); + stl_tabtab = ALLOC_MULT(stl_hlrec_T, stl_items_len); + } + #ifdef FEAT_EVAL /* * When the format starts with "%!" then evaluate it as an expression and @@ -4162,16 +4179,30 @@ build_stl_str_hl( prevchar_isitem = FALSE; for (s = usefmt; *s; ) { - if (curitem == STL_MAX_ITEM) + if (curitem == (int)stl_items_len) { - // There are too many items. Add the error code to the statusline - // to give the user a hint about what went wrong. - if (p + 6 < out + outlen) - { - mch_memmove(p, " E541", (size_t)5); - p += 5; - } - break; + size_t new_len = stl_items_len * 3 / 2; + stl_item_T *new_items; + int *new_groupitem; + stl_hlrec_T *new_hlrec; + + new_items = vim_realloc(stl_items, sizeof(stl_item_T) * new_len); + if (new_items == NULL) + break; + stl_items = new_items; + new_groupitem = vim_realloc(stl_groupitem, sizeof(int) * new_len); + if (new_groupitem == NULL) + break; + stl_groupitem = new_groupitem; + new_hlrec = vim_realloc(stl_hltab, sizeof(stl_hlrec_T) * new_len); + if (new_hlrec == NULL) + break; + stl_hltab = new_hlrec; + new_hlrec = vim_realloc(stl_tabtab, sizeof(stl_hlrec_T) * new_len); + if (new_hlrec == NULL) + break; + stl_tabtab = new_hlrec; + stl_items_len = new_len; } if (*s != NUL && *s != '%') @@ -4204,15 +4235,15 @@ build_stl_str_hl( s++; if (groupdepth > 0) continue; - item[curitem].type = Middle; - item[curitem++].start = p; + stl_items[curitem].stl_type = Middle; + stl_items[curitem++].stl_start = p; continue; } if (*s == STL_TRUNCMARK) { s++; - item[curitem].type = Trunc; - item[curitem++].start = p; + stl_items[curitem].stl_type = Trunc; + stl_items[curitem++].stl_start = p; continue; } if (*s == ')') @@ -4222,83 +4253,85 @@ build_stl_str_hl( continue; groupdepth--; - t = item[groupitem[groupdepth]].start; + t = stl_items[stl_groupitem[groupdepth]].stl_start; *p = NUL; l = vim_strsize(t); - if (curitem > groupitem[groupdepth] + 1 - && item[groupitem[groupdepth]].minwid == 0) + if (curitem > stl_groupitem[groupdepth] + 1 + && stl_items[stl_groupitem[groupdepth]].stl_minwid == 0) { // remove group if all items are empty and highlight group // doesn't change group_start_userhl = group_end_userhl = 0; - for (n = groupitem[groupdepth] - 1; n >= 0; n--) + for (n = stl_groupitem[groupdepth] - 1; n >= 0; n--) { - if (item[n].type == Highlight) + if (stl_items[n].stl_type == Highlight) { - group_start_userhl = group_end_userhl = item[n].minwid; + group_start_userhl = group_end_userhl = + stl_items[n].stl_minwid; break; } } - for (n = groupitem[groupdepth] + 1; n < curitem; n++) + for (n = stl_groupitem[groupdepth] + 1; n < curitem; n++) { - if (item[n].type == Normal) + if (stl_items[n].stl_type == Normal) break; - if (item[n].type == Highlight) - group_end_userhl = item[n].minwid; + if (stl_items[n].stl_type == Highlight) + group_end_userhl = stl_items[n].stl_minwid; } if (n == curitem && group_start_userhl == group_end_userhl) { // empty group p = t; l = 0; - for (n = groupitem[groupdepth] + 1; n < curitem; n++) + for (n = stl_groupitem[groupdepth] + 1; n < curitem; n++) { // do not use the highlighting from the removed group - if (item[n].type == Highlight) - item[n].type = Empty; + if (stl_items[n].stl_type == Highlight) + stl_items[n].stl_type = Empty; // adjust the start position of TabPage to the next // item position - if (item[n].type == TabPage) - item[n].start = p; + if (stl_items[n].stl_type == TabPage) + stl_items[n].stl_start = p; } } } - if (l > item[groupitem[groupdepth]].maxwid) + if (l > stl_items[stl_groupitem[groupdepth]].stl_maxwid) { // truncate, remove n bytes of text at the start if (has_mbyte) { // Find the first character that should be included. n = 0; - while (l >= item[groupitem[groupdepth]].maxwid) + while (l >= stl_items[stl_groupitem[groupdepth]].stl_maxwid) { l -= ptr2cells(t + n); n += (*mb_ptr2len)(t + n); } } else - n = (long)(p - t) - item[groupitem[groupdepth]].maxwid + 1; + n = (long)(p - t) - stl_items[stl_groupitem[groupdepth]] + .stl_maxwid + 1; *t = '<'; mch_memmove(t + 1, t + n, (size_t)(p - (t + n))); p = p - n + 1; // Fill up space left over by half a double-wide char. - while (++l < item[groupitem[groupdepth]].minwid) + while (++l < stl_items[stl_groupitem[groupdepth]].stl_minwid) *p++ = fillchar; // correct the start of the items for the truncation - for (l = groupitem[groupdepth] + 1; l < curitem; l++) + for (l = stl_groupitem[groupdepth] + 1; l < curitem; l++) { - item[l].start -= n; - if (item[l].start < t) - item[l].start = t; + stl_items[l].stl_start -= n; + if (stl_items[l].stl_start < t) + stl_items[l].stl_start = t; } } - else if (abs(item[groupitem[groupdepth]].minwid) > l) + else if (abs(stl_items[stl_groupitem[groupdepth]].stl_minwid) > l) { // fill - n = item[groupitem[groupdepth]].minwid; + n = stl_items[stl_groupitem[groupdepth]].stl_minwid; if (n < 0) { // fill by appending characters @@ -4314,8 +4347,8 @@ build_stl_str_hl( if (p + l >= out + outlen) l = (long)((out + outlen) - p - 1); p += l; - for (n = groupitem[groupdepth] + 1; n < curitem; n++) - item[n].start += l; + for (n = stl_groupitem[groupdepth] + 1; n < curitem; n++) + stl_items[n].stl_start += l; for ( ; l > 0; l--) *t++ = fillchar; } @@ -4344,9 +4377,9 @@ build_stl_str_hl( } if (*s == STL_USER_HL) { - item[curitem].type = Highlight; - item[curitem].start = p; - item[curitem].minwid = minwid > 9 ? 1 : minwid; + stl_items[curitem].stl_type = Highlight; + stl_items[curitem].stl_start = p; + stl_items[curitem].stl_minwid = minwid > 9 ? 1 : minwid; s++; curitem++; continue; @@ -4360,9 +4393,10 @@ build_stl_str_hl( // %X ends the close label, go back to the previously // define tab label nr. for (n = curitem - 1; n >= 0; --n) - if (item[n].type == TabPage && item[n].minwid >= 0) + if (stl_items[n].stl_type == TabPage + && stl_items[n].stl_minwid >= 0) { - minwid = item[n].minwid; + minwid = stl_items[n].stl_minwid; break; } } @@ -4370,9 +4404,9 @@ build_stl_str_hl( // close nrs are stored as negative values minwid = - minwid; } - item[curitem].type = TabPage; - item[curitem].start = p; - item[curitem].minwid = minwid; + stl_items[curitem].stl_type = TabPage; + stl_items[curitem].stl_start = p; + stl_items[curitem].stl_minwid = minwid; s++; curitem++; continue; @@ -4390,11 +4424,11 @@ build_stl_str_hl( minwid = (minwid > 50 ? 50 : minwid) * l; if (*s == '(') { - groupitem[groupdepth++] = curitem; - item[curitem].type = Group; - item[curitem].start = p; - item[curitem].minwid = minwid; - item[curitem].maxwid = maxwid; + stl_groupitem[groupdepth++] = curitem; + stl_items[curitem].stl_type = Group; + stl_items[curitem].stl_start = p; + stl_items[curitem].stl_minwid = minwid; + stl_items[curitem].stl_maxwid = maxwid; s++; curitem++; continue; @@ -4647,9 +4681,9 @@ build_stl_str_hl( ++s; if (*s == '#') { - item[curitem].type = Highlight; - item[curitem].start = p; - item[curitem].minwid = -syn_namen2id(t, (int)(s - t)); + stl_items[curitem].stl_type = Highlight; + stl_items[curitem].stl_start = p; + stl_items[curitem].stl_minwid = -syn_namen2id(t, (int)(s - t)); curitem++; } if (*s != NUL) @@ -4657,8 +4691,8 @@ build_stl_str_hl( continue; } - item[curitem].start = p; - item[curitem].type = Normal; + stl_items[curitem].stl_start = p; + stl_items[curitem].stl_type = Normal; if (str != NULL && *str) { t = str; @@ -4757,7 +4791,7 @@ build_stl_str_hl( p += STRLEN(p); } else - item[curitem].type = Empty; + stl_items[curitem].stl_type = Empty; if (opt == STL_VIM_EXPR) vim_free(str); @@ -4784,16 +4818,16 @@ build_stl_str_hl( else { for ( ; l < itemcnt; l++) - if (item[l].type == Trunc) + if (stl_items[l].stl_type == Trunc) { // Truncate at %< item. - s = item[l].start; + s = stl_items[l].stl_start; break; } if (l == itemcnt) { // No %< item, truncate first item. - s = item[0].start; + s = stl_items[0].stl_start; l = 0; } } @@ -4819,7 +4853,7 @@ build_stl_str_hl( else s = out + maxwidth - 1; for (l = 0; l < itemcnt; l++) - if (item[l].start > s) + if (stl_items[l].stl_start > s) break; itemcnt = l; *s++ = '>'; @@ -4853,10 +4887,10 @@ build_stl_str_hl( --n; // count the '<' for (; l < itemcnt; l++) { - if (item[l].start - n >= s) - item[l].start -= n; + if (stl_items[l].stl_start - n >= s) + stl_items[l].stl_start -= n; else - item[l].start = s; + stl_items[l].stl_start = s; } } width = maxwidth; @@ -4865,16 +4899,16 @@ build_stl_str_hl( { // Apply STL_MIDDLE if any for (l = 0; l < itemcnt; l++) - if (item[l].type == Middle) + if (stl_items[l].stl_type == Middle) break; if (l < itemcnt) { - p = item[l].start + maxwidth - width; - STRMOVE(p, item[l].start); - for (s = item[l].start; s < p; s++) + p = stl_items[l].stl_start + maxwidth - width; + STRMOVE(p, stl_items[l].stl_start); + for (s = stl_items[l].stl_start; s < p; s++) *s = fillchar; for (l++; l < itemcnt; l++) - item[l].start += maxwidth - width; + stl_items[l].stl_start += maxwidth - width; width = maxwidth; } } @@ -4882,13 +4916,14 @@ build_stl_str_hl( // Store the info about highlighting. if (hltab != NULL) { - sp = hltab; + *hltab = stl_hltab; + sp = stl_hltab; for (l = 0; l < itemcnt; l++) { - if (item[l].type == Highlight) + if (stl_items[l].stl_type == Highlight) { - sp->start = item[l].start; - sp->userhl = item[l].minwid; + sp->start = stl_items[l].stl_start; + sp->userhl = stl_items[l].stl_minwid; sp++; } } @@ -4899,13 +4934,14 @@ build_stl_str_hl( // Store the info about tab pages labels. if (tabtab != NULL) { - sp = tabtab; + *tabtab = stl_tabtab; + sp = stl_tabtab; for (l = 0; l < itemcnt; l++) { - if (item[l].type == TabPage) + if (stl_items[l].stl_type == TabPage) { - sp->start = item[l].start; - sp->userhl = item[l].minwid; + sp->start = stl_items[l].stl_start; + sp->userhl = stl_items[l].stl_minwid; sp++; } } @@ -5534,8 +5570,8 @@ bt_nofile(buf_T *buf) bt_dontwrite(buf_T *buf) { return buf != NULL && (buf->b_p_bt[0] == 'n' - || buf->b_p_bt[0] == 't' - || buf->b_p_bt[0] == 'p'); + || buf->b_p_bt[0] == 't' + || buf->b_p_bt[0] == 'p'); } #if defined(FEAT_QUICKFIX) || defined(PROTO) diff --git a/src/optionstr.c b/src/optionstr.c --- a/src/optionstr.c +++ b/src/optionstr.c @@ -571,11 +571,10 @@ valid_filetype(char_u *val) static char * check_stl_option(char_u *s) { - int itemcnt = 0; int groupdepth = 0; static char errbuf[80]; - while (*s && itemcnt < STL_MAX_ITEM) + while (*s) { // Check for valid keys after % sequences while (*s && *s != '%') @@ -583,8 +582,6 @@ check_stl_option(char_u *s) if (!*s) break; s++; - if (*s != '%' && *s != ')') - ++itemcnt; if (*s == '%' || *s == STL_TRUNCMARK || *s == STL_MIDDLEMARK) { s++; @@ -627,8 +624,6 @@ check_stl_option(char_u *s) return N_("E540: Unclosed expression sequence"); } } - if (itemcnt >= STL_MAX_ITEM) - return N_("E541: too many items"); if (groupdepth != 0) return N_("E542: unbalanced groups"); return NULL; diff --git a/src/proto/buffer.pro b/src/proto/buffer.pro --- a/src/proto/buffer.pro +++ b/src/proto/buffer.pro @@ -48,7 +48,7 @@ void col_print(char_u *buf, size_t bufle void maketitle(void); void resettitle(void); void free_titles(void); -int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use_sandbox, int fillchar, int maxwidth, struct stl_hlrec *hltab, struct stl_hlrec *tabtab); +int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use_sandbox, int fillchar, int maxwidth, stl_hlrec_T **hltab, stl_hlrec_T **tabtab); void get_rel_pos(win_T *wp, char_u *buf, int buflen); char_u *fix_fname(char_u *fname); void fname_expand(buf_T *buf, char_u **ffname, char_u **sfname); diff --git a/src/screen.c b/src/screen.c --- a/src/screen.c +++ b/src/screen.c @@ -1196,8 +1196,8 @@ win_redr_custom( char_u buf[MAXPATHL]; char_u *stl; char_u *p; - struct stl_hlrec hltab[STL_MAX_ITEM]; - struct stl_hlrec tabtab[STL_MAX_ITEM]; + stl_hlrec_T *hltab; + stl_hlrec_T *tabtab; int use_sandbox = FALSE; win_T *ewp; int p_crb_save; @@ -1287,7 +1287,7 @@ win_redr_custom( stl = vim_strsave(stl); width = build_stl_str_hl(ewp, buf, sizeof(buf), stl, use_sandbox, - fillchar, maxwidth, hltab, tabtab); + fillchar, maxwidth, &hltab, &tabtab); vim_free(stl); ewp->w_p_crb = p_crb_save; diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -1229,14 +1229,15 @@ struct mapblock #endif }; + /* * Used for highlighting in the status line. */ -struct stl_hlrec +typedef struct { char_u *start; int userhl; // 0: no HL, 1-9: User HL, < 0 for syn ID -}; +} stl_hlrec_T; /* diff --git a/src/testdir/test_options.vim b/src/testdir/test_options.vim --- a/src/testdir/test_options.vim +++ b/src/testdir/test_options.vim @@ -372,7 +372,6 @@ func Test_set_errors() call assert_fails('set commentstring=x', 'E537:') call assert_fails('set complete=x', 'E539:') call assert_fails('set statusline=%{', 'E540:') - call assert_fails('set statusline=' . repeat("%p", 81), 'E541:') call assert_fails('set statusline=%(', 'E542:') if has('cursorshape') " This invalid value for 'guicursor' used to cause Vim to crash. diff --git a/src/testdir/test_statusline.vim b/src/testdir/test_statusline.vim --- a/src/testdir/test_statusline.vim +++ b/src/testdir/test_statusline.vim @@ -376,6 +376,21 @@ func Test_statusline() delfunc GetNested delfunc GetStatusLine + " Test statusline works with 80+ items + function! StatusLabel() + redrawstatus + return '[label]' + endfunc + let statusline = '%{StatusLabel()}' + for i in range(150) + let statusline .= '%#TabLine' . (i % 2 == 0 ? 'Fill' : 'Sel') . '#' . string(i)[0] + endfor + let &statusline = statusline + redrawstatus + set statusline& + delfunc StatusLabel + + " Check statusline in current and non-current window " with the 'fillchars' option. set fillchars=stl:^,stlnc:=,vert:\|,fold:-,diff:- diff --git a/src/version.c b/src/version.c --- 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 */ /**/ + 1909, +/**/ 1908, /**/ 1907, diff --git a/src/vim.h b/src/vim.h --- a/src/vim.h +++ b/src/vim.h @@ -1699,7 +1699,6 @@ typedef unsigned short disptick_T; // di #endif #define SHOWCMD_COLS 10 // columns needed by shown command -#define STL_MAX_ITEM 80 // max nr of % in statusline typedef void *vim_acl_T; // dummy to pass an ACL to a function