changeset 31936:82da100c0e45 v9.0.1300

patch 9.0.1300: 'statusline' only supports one "%=" item Commit: https://github.com/vim/vim/commit/3ec78f973fdaec2cea8e036ed38037b2fe40670b Author: Yegappan Lakshmanan <yegappan@yahoo.com> Date: Sat Feb 11 11:15:25 2023 +0000 patch 9.0.1300: 'statusline' only supports one "%=" item Problem: 'statusline' only supports one "%=" item. Solution: Add support for multiple "%=" items. (TJ DeVries, Yegappan Lakshmanan, closes #11970, closes #11965)
author Bram Moolenaar <Bram@vim.org>
date Sat, 11 Feb 2023 12:30:03 +0100
parents 46f369a1f0cb
children 65c19095296f
files runtime/doc/options.txt src/buffer.c src/option.h src/optionstr.c src/testdir/test_statusline.vim src/version.c
diffstat 6 files changed, 73 insertions(+), 25 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -7776,7 +7776,10 @@ A jump table for the options with a shor
 	      mark.  This information is used for mouse clicks.
 	< -   Where to truncate line if too long.  Default is at the start.
 	      No width fields allowed.
-	= -   Separation point between left and right aligned items.
+	= -   Separation point between alignment sections.  Each section will
+	      be separated by an equal number of spaces.  With one %= what
+	      comes after it will be right-aligned.  With two %= there is a
+	      middle part, with white space left and right of it.
 	      No width fields allowed.
 	# -   Set highlight group.  The name must follow and then a # again.
 	      Thus use %#HLname# for highlight group HLname.  The same
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -4179,7 +4179,7 @@ typedef struct
 	Normal,
 	Empty,
 	Group,
-	Middle,
+	Separate,
 	Highlight,
 	TabPage,
 	Trunc
@@ -4191,6 +4191,7 @@ 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;
+static int		*stl_separator_locations = NULL;
 
 /*
  * Build a string from the status line items in "fmt".
@@ -4200,7 +4201,7 @@ static stl_hlrec_T     *stl_tabtab = NUL
  * is "curwin".
  *
  * Items are drawn interspersed with the text that surrounds it
- * Specials: %-<wid>(xxx%) => group, %= => middle marker, %< => truncation
+ * Specials: %-<wid>(xxx%) => group, %= => separation marker, %< => truncation
  * Item: %-<minwid>.<maxwid><itemch> All but <itemch> are optional
  *
  * If maxwidth is not zero, the string will be filled at any middle marker
@@ -4282,6 +4283,8 @@ build_stl_str_hl(
 	// end of the list.
 	stl_hltab  = ALLOC_MULT(stl_hlrec_T, stl_items_len + 1);
 	stl_tabtab = ALLOC_MULT(stl_hlrec_T, stl_items_len + 1);
+
+	stl_separator_locations = ALLOC_MULT(int, stl_items_len);
     }
 
 #ifdef FEAT_EVAL
@@ -4350,19 +4353,20 @@ build_stl_str_hl(
 	if (curitem == (int)stl_items_len)
 	{
 	    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);
+
+	    stl_item_T *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);
+
+	    int *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,
+
+	    stl_hlrec_T	*new_hlrec = vim_realloc(stl_hltab,
 					  sizeof(stl_hlrec_T) * (new_len + 1));
 	    if (new_hlrec == NULL)
 		break;
@@ -4372,6 +4376,13 @@ build_stl_str_hl(
 	    if (new_hlrec == NULL)
 		break;
 	    stl_tabtab = new_hlrec;
+
+	    int *new_separator_locs = vim_realloc(stl_separator_locations,
+					    sizeof(int) * new_len);
+	    if (new_separator_locs == NULL)
+		break;
+	    stl_separator_locations = new_separator_locs;;
+
 	    stl_items_len = new_len;
 	}
 
@@ -4400,12 +4411,13 @@ build_stl_str_hl(
 	    prevchar_isflag = prevchar_isitem = FALSE;
 	    continue;
 	}
-	if (*s == STL_MIDDLEMARK)
+	// STL_SEPARATE: Separation between items, filled with white space.
+	if (*s == STL_SEPARATE)
 	{
 	    s++;
 	    if (groupdepth > 0)
 		continue;
-	    stl_items[curitem].stl_type = Middle;
+	    stl_items[curitem].stl_type = Separate;
 	    stl_items[curitem++].stl_start = p;
 	    continue;
 	}
@@ -5121,19 +5133,45 @@ build_stl_str_hl(
     }
     else if (width < maxwidth && STRLEN(out) + maxwidth - width + 1 < outlen)
     {
-	// Apply STL_MIDDLE if any
+	// Find how many separators there are, which we will use when
+	// figuring out how many groups there are.
+	int num_separators = 0;
+
 	for (l = 0; l < itemcnt; l++)
-	    if (stl_items[l].stl_type == Middle)
-		break;
-	if (l < itemcnt)
+	{
+	    if (stl_items[l].stl_type == Separate)
+	    {
+		// Create an array of the start location for each separator
+		// mark.
+		stl_separator_locations[num_separators] = l;
+		num_separators++;
+	    }
+	}
+
+	// If we have separated groups, then we deal with it now
+	if (num_separators)
 	{
-	    int middlelength = (maxwidth - width) * MB_CHAR2LEN(fillchar);
-	    p = stl_items[l].stl_start + middlelength;
-	    STRMOVE(p, stl_items[l].stl_start);
-	    for (s = stl_items[l].stl_start; s < p;)
-		MB_CHAR2BYTES(fillchar, s);
-	    for (l++; l < itemcnt; l++)
-		stl_items[l].stl_start += middlelength;
+	    int standard_spaces;
+	    int final_spaces;
+
+	    standard_spaces = (maxwidth - width) / num_separators;
+	    final_spaces = (maxwidth - width) -
+					standard_spaces * (num_separators - 1);
+	    for (l = 0; l < num_separators; l++)
+	    {
+		int dislocation = (l == (num_separators - 1)) ?
+					final_spaces : standard_spaces;
+		dislocation *= MB_CHAR2LEN(fillchar);
+		char_u *start = stl_items[stl_separator_locations[l]].stl_start;
+		char_u *seploc = start + dislocation;
+		STRMOVE(seploc, start);
+		for (s = start; s < seploc;)
+		    MB_CHAR2BYTES(fillchar, s);
+
+		for (int i = stl_separator_locations[l] + 1; i < itemcnt; i++)
+		    stl_items[i].stl_start += dislocation;
+	    }
+
 	    width = maxwidth;
 	}
     }
--- a/src/option.h
+++ b/src/option.h
@@ -347,7 +347,8 @@ typedef enum {
 #define STL_PAGENUM	'N'		// page number (when printing)
 #define STL_SHOWCMD	'S'		// 'showcmd' buffer
 #define STL_VIM_EXPR	'{'		// start of expression to substitute
-#define STL_MIDDLEMARK	'='		// separation between left and right
+#define STL_SEPARATE	'='		// separation between alignment
+					// sections
 #define STL_TRUNCMARK	'<'		// truncation mark if line is too long
 #define STL_USER_HL	'*'		// highlight from (User)1..9 or 0
 #define STL_HIGHLIGHT	'#'		// highlight name
--- a/src/optionstr.c
+++ b/src/optionstr.c
@@ -587,7 +587,7 @@ check_stl_option(char_u *s)
 	if (!*s)
 	    break;
 	s++;
-	if (*s == '%' || *s == STL_TRUNCMARK || *s == STL_MIDDLEMARK)
+	if (*s == '%' || *s == STL_TRUNCMARK || *s == STL_SEPARATE)
 	{
 	    s++;
 	    continue;
--- a/src/testdir/test_statusline.vim
+++ b/src/testdir/test_statusline.vim
@@ -231,6 +231,10 @@ func Test_statusline()
   " %=: Separation point between left and right aligned items.
   set statusline=foo%=bar
   call assert_match('^foo\s\+bar\s*$', s:get_statusline())
+  set statusline=foo%=bar%=baz
+  call assert_match('^foo\s\+bar\s\+baz\s*$', s:get_statusline())
+  set statusline=foo%=bar%=baz%=qux
+  call assert_match('^foo\s\+bar\s\+baz\s\+qux\s*$', s:get_statusline())
 
   " Test min/max width, leading zeroes, left/right justify.
   set statusline=%04B
--- a/src/version.c
+++ b/src/version.c
@@ -696,6 +696,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1300,
+/**/
     1299,
 /**/
     1298,