# HG changeset patch # User Bram Moolenaar # Date 1614774602 -3600 # Node ID 15408ab5fed7eb3017b7ff6298ee4925c8ad2d7c # Parent 553271829e461dfd8d19327ad43b68023e5e0075 patch 8.2.2563: cannot use multibyte characters for folding in 'fillchars' Commit: https://github.com/vim/vim/commit/4fa1175765d55613302fc27d0f65e2c699452b6e Author: Bram Moolenaar 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) diff --git a/src/drawline.c b/src/drawline.c --- 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; diff --git a/src/drawscreen.c b/src/drawscreen.c --- 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); diff --git a/src/macros.h b/src/macros.h --- 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 diff --git a/src/proto/screen.pro b/src/proto/screen.pro --- 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); diff --git a/src/screen.c b/src/screen.c --- 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 diff --git a/src/testdir/test_fold.vim b/src/testdir/test_fold.vim --- 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 diff --git a/src/testdir/test_profile.vim b/src/testdir/test_profile.vim --- 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 \d\+_Two().*' \ .. '#Called 3 times.*' \ .. '# 3 \s*[0-9.]\+ total += nr', prof_lines) + call delete('Xprofile_nested.vim') + call delete('Xprofile_nested.log') endfunc 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 */ /**/ + 2563, +/**/ 2562, /**/ 2561,