changeset 24043:15408ab5fed7 v8.2.2563

patch 8.2.2563: cannot use multibyte characters for folding in 'fillchars' Commit: https://github.com/vim/vim/commit/4fa1175765d55613302fc27d0f65e2c699452b6e Author: Bram Moolenaar <Bram@vim.org> Date: Wed Mar 3 13:26:02 2021 +0100 patch 8.2.2563: cannot use multibyte characters for folding in 'fillchars' Problem: Cannot use multibyte characters for folding in 'fillchars'. Solution: Port pull request 11568 to Vim. (Yegappan Lakshmanan, closes #7924)
author Bram Moolenaar <Bram@vim.org>
date Wed, 03 Mar 2021 13:30:02 +0100
parents 553271829e46
children fa681d4682be
files src/drawline.c src/drawscreen.c src/macros.h src/proto/screen.pro src/screen.c src/testdir/test_fold.vim src/testdir/test_profile.vim src/version.c
diffstat 8 files changed, 220 insertions(+), 54 deletions(-) [+]
line wrap: on
line diff
--- a/src/drawline.c
+++ b/src/drawline.c
@@ -1031,12 +1031,11 @@ win_line(
 		    // Draw the 'foldcolumn'.  Allocate a buffer, "extra" may
 		    // already be in use.
 		    vim_free(p_extra_free);
-		    p_extra_free = alloc(12 + 1);
-
+		    p_extra_free = alloc(MAX_MCO * fdc + 1);
 		    if (p_extra_free != NULL)
 		    {
-			fill_foldcolumn(p_extra_free, wp, FALSE, lnum);
-			n_extra = fdc;
+			n_extra = fill_foldcolumn(p_extra_free, wp,
+								  FALSE, lnum);
 			p_extra_free[n_extra] = NUL;
 			p_extra = p_extra_free;
 			c_extra = NUL;
--- a/src/drawscreen.c
+++ b/src/drawscreen.c
@@ -1044,7 +1044,9 @@ fold_line(
     linenr_T	lnum,
     int		row)
 {
-    char_u	buf[FOLD_TEXT_LEN];
+    // Max value of 'foldcolumn' is 12 and maximum number of bytes in a
+    // multi-byte character is MAX_MCO.
+    char_u	buf[MAX_MCO * 12 + 1];
     pos_T	*top, *bot;
     linenr_T	lnume = lnum + fold_count - 1;
     int		len;
@@ -1077,29 +1079,6 @@ fold_line(
     }
 #endif
 
-    // 2. Add the 'foldcolumn'
-    //    Reduce the width when there is not enough space.
-    fdc = compute_foldcolumn(wp, col);
-    if (fdc > 0)
-    {
-	fill_foldcolumn(buf, wp, TRUE, lnum);
-#ifdef FEAT_RIGHTLEFT
-	if (wp->w_p_rl)
-	{
-	    int		i;
-
-	    copy_text_attr(off + wp->w_width - fdc - col, buf, fdc,
-							     HL_ATTR(HLF_FC));
-	    // reverse the fold column
-	    for (i = 0; i < fdc; ++i)
-		ScreenLines[off + wp->w_width - i - 1 - col] = buf[i];
-	}
-	else
-#endif
-	    copy_text_attr(off + col, buf, fdc, HL_ATTR(HLF_FC));
-	col += fdc;
-    }
-
 #ifdef FEAT_RIGHTLEFT
 # define RL_MEMSET(p, v, l) \
     do { \
@@ -1118,6 +1097,53 @@ fold_line(
     } while (0)
 #endif
 
+    // 2. Add the 'foldcolumn'
+    //    Reduce the width when there is not enough space.
+    fdc = compute_foldcolumn(wp, col);
+    if (fdc > 0)
+    {
+	char_u	*p;
+	int	i;
+	int	idx;
+
+	fill_foldcolumn(buf, wp, TRUE, lnum);
+	p = buf;
+	for (i = 0; i < fdc; i++)
+	{
+	    int		ch;
+
+	    if (has_mbyte)
+		ch = mb_ptr2char_adv(&p);
+	    else
+		ch = *p++;
+#ifdef FEAT_RIGHTLEFT
+	    if (wp->w_p_rl)
+		idx = off + wp->w_width - i - 1 - col;
+	    else
+#endif
+		idx = off + col + i;
+	    if (enc_utf8)
+	    {
+		if (ch >= 0x80)
+		{
+		    ScreenLinesUC[idx] = ch;
+		    ScreenLinesC[0][idx] = 0;
+		    ScreenLines[idx] = 0x80;
+		}
+		else
+		{
+		    ScreenLines[idx] = ch;
+		    ScreenLinesUC[idx] = 0;
+		}
+	    }
+	    else
+		ScreenLines[idx] = ch;
+	}
+
+	RL_MEMSET(col, HL_ATTR(HLF_FC), fdc);
+	col += fdc;
+    }
+
     // Set all attributes of the 'number' or 'relativenumber' column and the
     // text
     RL_MEMSET(col, HL_ATTR(HLF_FL), wp->w_width - col);
--- a/src/macros.h
+++ b/src/macros.h
@@ -388,3 +388,10 @@
 
 // Inlined version of ga_grow().  Especially useful if "n" is a constant.
 #define GA_GROW(gap, n) (((gap)->ga_maxlen - (gap)->ga_len < n) ? ga_grow_inner((gap), (n)) : OK)
+
+#ifndef MIN
+# define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+#ifndef MAX
+# define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
--- a/src/proto/screen.pro
+++ b/src/proto/screen.pro
@@ -4,7 +4,7 @@ void conceal_check_cursor_line(void);
 int get_wcr_attr(win_T *wp);
 void win_draw_end(win_T *wp, int c1, int c2, int draw_margin, int row, int endrow, hlf_T hl);
 int compute_foldcolumn(win_T *wp, int col);
-void fill_foldcolumn(char_u *p, win_T *wp, int closed, linenr_T lnum);
+size_t fill_foldcolumn(char_u *p, win_T *wp, int closed, linenr_T lnum);
 int screen_get_current_line_off(void);
 void reset_screen_attr(void);
 void screen_line(int row, int coloff, int endcol, int clear_width, int flags);
--- a/src/screen.c
+++ b/src/screen.c
@@ -239,8 +239,11 @@ compute_foldcolumn(win_T *wp, int col)
 /*
  * Fill the foldcolumn at "p" for window "wp".
  * Only to be called when 'foldcolumn' > 0.
+ * Returns the number of bytes stored in 'p'. When non-multibyte characters are
+ * used for the fold column markers, this is equal to 'fdc' setting. Otherwise,
+ * this will be greater than 'fdc'.
  */
-    void
+    size_t
 fill_foldcolumn(
     char_u	*p,
     win_T	*wp,
@@ -252,39 +255,54 @@ fill_foldcolumn(
     int		first_level;
     int		empty;
     int		fdc = compute_foldcolumn(wp, 0);
+    size_t	byte_counter = 0;
+    int		symbol = 0;
+    int		len = 0;
 
     // Init to all spaces.
-    vim_memset(p, ' ', (size_t)fdc);
+    vim_memset(p, ' ', MAX_MCO * fdc + 1);
 
     level = win_foldinfo.fi_level;
-    if (level > 0)
+    empty = (fdc == 1) ? 0 : 1;
+
+    // If the column is too narrow, we start at the lowest level that
+    // fits and use numbers to indicated the depth.
+    first_level = level - fdc - closed + 1 + empty;
+    if (first_level < 1)
+	first_level = 1;
+
+    for (i = 0; i < MIN(fdc, level); i++)
     {
-	// If there is only one column put more info in it.
-	empty = (fdc == 1) ? 0 : 1;
-
-	// If the column is too narrow, we start at the lowest level that
-	// fits and use numbers to indicated the depth.
-	first_level = level - fdc - closed + 1 + empty;
-	if (first_level < 1)
-	    first_level = 1;
-
-	for (i = 0; i + empty < fdc; ++i)
+	if (win_foldinfo.fi_lnum == lnum
+		&& first_level + i >= win_foldinfo.fi_low_level)
+	    symbol = fill_foldopen;
+	else if (first_level == 1)
+	    symbol = fill_foldsep;
+	else if (first_level + i <= 9)
+	    symbol = '0' + first_level + i;
+	else
+	    symbol = '>';
+
+	len = utf_char2bytes(symbol, &p[byte_counter]);
+	byte_counter += len;
+	if (first_level + i >= level)
 	{
-	    if (win_foldinfo.fi_lnum == lnum
-			      && first_level + i >= win_foldinfo.fi_low_level)
-		p[i] = fill_foldopen;
-	    else if (first_level == 1)
-		p[i] = fill_foldsep;
-	    else if (first_level + i <= 9)
-		p[i] = '0' + first_level + i;
-	    else
-		p[i] = '>';
-	    if (first_level + i == level)
-		break;
+	    i++;
+	    break;
 	}
     }
+
     if (closed)
-	p[i >= fdc ? i - 1 : i] = fill_foldclosed;
+    {
+	if (symbol != 0)
+	    // rollback length
+	    byte_counter -= len;
+	symbol = fill_foldclosed;
+	len = utf_char2bytes(symbol, &p[byte_counter]);
+	byte_counter += len;
+    }
+
+    return MAX(byte_counter + (fdc - i), (size_t)fdc);
 }
 #endif // FEAT_FOLDING
 
--- a/src/testdir/test_fold.vim
+++ b/src/testdir/test_fold.vim
@@ -894,4 +894,116 @@ func Test_fold_relative_move()
   set fdm& sw& wrap& tw&
 endfunc
 
+" Test for using multibyte characters as 'foldopen', 'foldclose' and
+" 'foldsetp' items in 'fillchars'
+func s:mbyte_fillchar_tests(fo, fc, fs)
+  setlocal foldcolumn=3
+
+  normal zE
+  1,2fold
+  call assert_equal([a:fc .. '  +--  2 ', '   three  '],
+        \ ScreenLines([1, 2], 10))
+  1,2foldopen
+  call assert_equal([a:fo .. '  one ', a:fs .. '  two '],
+        \ ScreenLines([1, 2], 7))
+  1,2foldclose
+  redraw!
+  call assert_equal([a:fc .. '  +--  2 ', '   three  '],
+        \  ScreenLines([1, 2], 10))
+
+  " Two level fold
+  normal zE
+  2,3fold
+  1,4fold
+  call assert_equal([a:fc .. '  +--  4 ', '   five   '],
+        \ ScreenLines([1, 2], 10))
+  1,4foldopen
+  call assert_equal([a:fo .. '  one    ', a:fs .. a:fc .. ' +---  2'],
+        \ ScreenLines([1, 2], 10))
+  1,4foldopen
+  call assert_equal([a:fo .. '  one    ', a:fs .. a:fo .. ' two    ',
+        \ a:fs .. a:fs .. ' three  '], ScreenLines([1, 3], 10))
+  2,3foldclose
+  call assert_equal([a:fo .. '  one    ', a:fs .. a:fc .. ' +---  2'],
+        \ ScreenLines([1, 2], 10))
+  1,4foldclose
+  call assert_equal([a:fc .. '  +--  4 ', '   five   '],
+        \ ScreenLines([1, 2], 10))
+
+  " Three level fold
+  normal zE
+  3,4fold
+  2,5fold
+  1,6fold
+  call assert_equal([a:fc .. '  +--  6 '], ScreenLines(1, 10))
+  " open all the folds
+  normal zR
+  call assert_equal([
+        \ a:fo .. '  one    ',
+        \ a:fs .. a:fo .. ' two    ',
+        \ '2' .. a:fo .. ' three  ',
+        \ '23 four   ',
+        \ a:fs .. a:fs .. ' five   ',
+        \ a:fs .. '  six    ',
+        \ ], ScreenLines([1, 6], 10))
+  " close the innermost fold
+  3,4foldclose
+  call assert_equal([
+        \ a:fo .. '  one    ',
+        \ a:fs .. a:fo .. ' two    ',
+        \ a:fs .. a:fs .. a:fc .. '+----  ',
+        \ a:fs .. a:fs .. ' five   ',
+        \ a:fs .. '  six    ',
+        \ ], ScreenLines([1, 5], 10))
+  " close the next fold
+  2,5foldclose
+  call assert_equal([
+        \ a:fo .. '  one    ',
+        \ a:fs .. a:fc .. ' +---  4',
+        \ a:fs .. '  six    ',
+        \ ], ScreenLines([1, 3], 10))
+
+  " set the fold column size to 2
+  setlocal fdc=2
+  normal zR
+  call assert_equal([
+        \ a:fo .. ' one  ',
+        \ a:fo .. ' two  ',
+        \ a:fo .. ' three',
+        \ '3 four ',
+        \ '2 five ',
+        \ a:fs .. ' six  ',
+        \ ], ScreenLines([1, 6], 7))
+
+  " set the fold column size to 1
+  setlocal fdc=1
+  normal zR
+  call assert_equal([
+        \ a:fo .. 'one   ',
+        \ a:fo .. 'two   ',
+        \ a:fo .. 'three ',
+        \ '3four  ',
+        \ '2five  ',
+        \ a:fs .. 'six   ',
+        \ ], ScreenLines([1, 6], 7))
+
+  setlocal foldcolumn&
+endfunc
+
+func Test_foldcolumn_multibyte_char()
+  new
+  call setline(1, ['one', 'two', 'three', 'four', 'five', 'six'])
+  setlocal foldenable foldmethod=manual
+
+  " First test with the default setting
+  call s:mbyte_fillchar_tests('-', '+', '|')
+
+  " Use multi-byte characters
+  set fillchars+=foldopen:▾,foldsep:│,foldclose:▸
+  call s:mbyte_fillchar_tests('▾', '▸', '│')
+
+  bw!
+  set foldenable& fdc& fdm& fillchars&
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
--- a/src/testdir/test_profile.vim
+++ b/src/testdir/test_profile.vim
@@ -636,6 +636,8 @@ func Test_vim9_nested_call()
   call assert_match('FUNCTION  <SNR>\d\+_Two().*'
         \ .. '#Called 3 times.*'
         \ .. '#    3 \s*[0-9.]\+   total += nr', prof_lines)
+  call delete('Xprofile_nested.vim')
+  call delete('Xprofile_nested.log')
 endfunc
 
 
--- 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 */
 /**/
+    2563,
+/**/
     2562,
 /**/
     2561,