mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	fix(extmarks): problems with folded virtual lines (#21930)
Problem:    When a folded line has virtual lines attached, the following
            problems occur:
              - The virtual lines are drawn empty.
              - The 'foldtext' line is drawn empty.
              - The cursor is drawn incorrectly.
Solution:   Check whether virtual lines belong to a folded line.
Fix #17027
Fix #19557
Fix #21837
Co-authored-by: zeertzjq <zeertzjq@outlook.com>
			
			
This commit is contained in:
		| @@ -7,6 +7,7 @@ | |||||||
| #include "nvim/decoration.h" | #include "nvim/decoration.h" | ||||||
| #include "nvim/drawscreen.h" | #include "nvim/drawscreen.h" | ||||||
| #include "nvim/extmark.h" | #include "nvim/extmark.h" | ||||||
|  | #include "nvim/fold.h" | ||||||
| #include "nvim/highlight.h" | #include "nvim/highlight.h" | ||||||
| #include "nvim/highlight_group.h" | #include "nvim/highlight_group.h" | ||||||
| #include "nvim/memory.h" | #include "nvim/memory.h" | ||||||
| @@ -550,7 +551,8 @@ void decor_add_ephemeral(int start_row, int start_col, int end_row, int end_col, | |||||||
|   decor_add(&decor_state, start_row, start_col, end_row, end_col, decor, true, ns_id, mark_id); |   decor_add(&decor_state, start_row, start_col, end_row, end_col, decor, true, ns_id, mark_id); | ||||||
| } | } | ||||||
|  |  | ||||||
| int decor_virt_lines(win_T *wp, linenr_T lnum, VirtLines *lines) | /// @param has_fold  whether line "lnum" has a fold, or kNone when not calculated yet | ||||||
|  | int decor_virt_lines(win_T *wp, linenr_T lnum, VirtLines *lines, TriState has_fold) | ||||||
| { | { | ||||||
|   buf_T *buf = wp->w_buffer; |   buf_T *buf = wp->w_buffer; | ||||||
|   if (!buf->b_virt_line_blocks) { |   if (!buf->b_virt_line_blocks) { | ||||||
| @@ -564,6 +566,10 @@ int decor_virt_lines(win_T *wp, linenr_T lnum, VirtLines *lines) | |||||||
|   int end_row = (int)lnum; |   int end_row = (int)lnum; | ||||||
|   MarkTreeIter itr[1] = { 0 }; |   MarkTreeIter itr[1] = { 0 }; | ||||||
|   marktree_itr_get(buf->b_marktree, row, 0,  itr); |   marktree_itr_get(buf->b_marktree, row, 0,  itr); | ||||||
|  |   bool below_fold = lnum > 1 && hasFoldingWin(wp, lnum - 1, NULL, NULL, true, NULL); | ||||||
|  |   if (has_fold == kNone) { | ||||||
|  |     has_fold = hasFoldingWin(wp, lnum, NULL, NULL, true, NULL); | ||||||
|  |   } | ||||||
|   while (true) { |   while (true) { | ||||||
|     mtkey_t mark = marktree_itr_current(itr); |     mtkey_t mark = marktree_itr_current(itr); | ||||||
|     if (mark.pos.row < 0 || mark.pos.row >= end_row) { |     if (mark.pos.row < 0 || mark.pos.row >= end_row) { | ||||||
| @@ -572,8 +578,9 @@ int decor_virt_lines(win_T *wp, linenr_T lnum, VirtLines *lines) | |||||||
|       goto next_mark; |       goto next_mark; | ||||||
|     } |     } | ||||||
|     bool above = mark.pos.row > (lnum - 2); |     bool above = mark.pos.row > (lnum - 2); | ||||||
|  |     bool has_fold_cur = above ? has_fold : below_fold; | ||||||
|     Decoration *decor = mark.decor_full; |     Decoration *decor = mark.decor_full; | ||||||
|     if (decor && decor->virt_lines_above == above) { |     if (!has_fold_cur && decor && decor->virt_lines_above == above) { | ||||||
|       virt_lines += (int)kv_size(decor->virt_lines); |       virt_lines += (int)kv_size(decor->virt_lines); | ||||||
|       if (lines) { |       if (lines) { | ||||||
|         kv_splice(*lines, decor->virt_lines); |         kv_splice(*lines, decor->virt_lines); | ||||||
|   | |||||||
| @@ -940,7 +940,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, | |||||||
|     area_highlighting = true; |     area_highlighting = true; | ||||||
|   } |   } | ||||||
|   VirtLines virt_lines = KV_INITIAL_VALUE; |   VirtLines virt_lines = KV_INITIAL_VALUE; | ||||||
|   int n_virt_lines = decor_virt_lines(wp, lnum, &virt_lines); |   int n_virt_lines = decor_virt_lines(wp, lnum, &virt_lines, has_fold); | ||||||
|   filler_lines += n_virt_lines; |   filler_lines += n_virt_lines; | ||||||
|   if (lnum == wp->w_topline) { |   if (lnum == wp->w_topline) { | ||||||
|     filler_lines = wp->w_topfill; |     filler_lines = wp->w_topfill; | ||||||
| @@ -1507,7 +1507,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, | |||||||
|         && has_fold |         && has_fold | ||||||
|         && col == win_col_offset |         && col == win_col_offset | ||||||
|         && n_extra == 0 |         && n_extra == 0 | ||||||
|         && row == startrow) { |         && row == startrow + filler_lines) { | ||||||
|       char_attr = win_hl_attr(wp, HLF_FL); |       char_attr = win_hl_attr(wp, HLF_FL); | ||||||
|  |  | ||||||
|       linenr_T lnume = lnum + foldinfo.fi_lines - 1; |       linenr_T lnume = lnum + foldinfo.fi_lines - 1; | ||||||
| @@ -1528,7 +1528,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, | |||||||
|         && has_fold |         && has_fold | ||||||
|         && col < grid->cols |         && col < grid->cols | ||||||
|         && n_extra == 0 |         && n_extra == 0 | ||||||
|         && row == startrow) { |         && row == startrow + filler_lines) { | ||||||
|       // fill rest of line with 'fold' |       // fill rest of line with 'fold' | ||||||
|       c_extra = wp->w_p_fcs_chars.fold; |       c_extra = wp->w_p_fcs_chars.fold; | ||||||
|       c_final = NUL; |       c_final = NUL; | ||||||
| @@ -1540,7 +1540,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, | |||||||
|         && has_fold |         && has_fold | ||||||
|         && col >= grid->cols |         && col >= grid->cols | ||||||
|         && n_extra != 0 |         && n_extra != 0 | ||||||
|         && row == startrow) { |         && row == startrow + filler_lines) { | ||||||
|       // Truncate the folding. |       // Truncate the folding. | ||||||
|       n_extra = 0; |       n_extra = 0; | ||||||
|     } |     } | ||||||
| @@ -2753,7 +2753,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, | |||||||
|     // At end of screen line and there is more to come: Display the line |     // At end of screen line and there is more to come: Display the line | ||||||
|     // so far.  If there is no more to display it is caught above. |     // so far.  If there is no more to display it is caught above. | ||||||
|     if ((wp->w_p_rl ? (col < 0) : (col >= grid->cols)) |     if ((wp->w_p_rl ? (col < 0) : (col >= grid->cols)) | ||||||
|         && foldinfo.fi_lines == 0 |         && (!has_fold || virt_line_offset >= 0) | ||||||
|         && (draw_state != WL_LINE |         && (draw_state != WL_LINE | ||||||
|             || *ptr != NUL |             || *ptr != NUL | ||||||
|             || filler_todo > 0 |             || filler_todo > 0 | ||||||
|   | |||||||
| @@ -1906,7 +1906,7 @@ static void win_update(win_T *wp, DecorProviders *providers) | |||||||
|       if (j > 0 && !wp->w_botfill && row < wp->w_grid.rows) { |       if (j > 0 && !wp->w_botfill && row < wp->w_grid.rows) { | ||||||
|         // Display filler text below last line. win_line() will check |         // Display filler text below last line. win_line() will check | ||||||
|         // for ml_line_count+1 and only draw filler lines |         // for ml_line_count+1 and only draw filler lines | ||||||
|         foldinfo_T info = FOLDINFO_INIT; |         foldinfo_T info = { 0 }; | ||||||
|         row = win_line(wp, wp->w_botline, row, wp->w_grid.rows, |         row = win_line(wp, wp->w_botline, row, wp->w_grid.rows, | ||||||
|                        false, false, info, &line_providers, &provider_err); |                        false, false, info, &line_providers, &provider_err); | ||||||
|       } |       } | ||||||
|   | |||||||
| @@ -20,8 +20,6 @@ typedef struct foldinfo { | |||||||
|   linenr_T fi_lines; |   linenr_T fi_lines; | ||||||
| } foldinfo_T; | } foldinfo_T; | ||||||
|  |  | ||||||
| #define FOLDINFO_INIT { 0, 0, 0, 0 } |  | ||||||
|  |  | ||||||
| EXTERN int disable_fold_update INIT(= 0); | EXTERN int disable_fold_update INIT(= 0); | ||||||
|  |  | ||||||
| #ifdef INCLUDE_GENERATED_DECLARATIONS | #ifdef INCLUDE_GENERATED_DECLARATIONS | ||||||
|   | |||||||
| @@ -51,7 +51,7 @@ int plines_win(win_T *wp, linenr_T lnum, bool winheight) | |||||||
| /// @return Number of filler lines above lnum | /// @return Number of filler lines above lnum | ||||||
| int win_get_fill(win_T *wp, linenr_T lnum) | int win_get_fill(win_T *wp, linenr_T lnum) | ||||||
| { | { | ||||||
|   int virt_lines = decor_virt_lines(wp, lnum, NULL); |   int virt_lines = decor_virt_lines(wp, lnum, NULL, kNone); | ||||||
|  |  | ||||||
|   // be quick when there are no filler lines |   // be quick when there are no filler lines | ||||||
|   if (diffopt_filler()) { |   if (diffopt_filler()) { | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ local expect = helpers.expect | |||||||
| local funcs = helpers.funcs | local funcs = helpers.funcs | ||||||
| local meths = helpers.meths | local meths = helpers.meths | ||||||
| local exec = helpers.exec | local exec = helpers.exec | ||||||
|  | local exec_lua = helpers.exec_lua | ||||||
| local assert_alive = helpers.assert_alive | local assert_alive = helpers.assert_alive | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -1852,6 +1853,128 @@ describe("folded lines", function() | |||||||
|         ]]) |         ]]) | ||||||
|       end |       end | ||||||
|     end) |     end) | ||||||
|  |  | ||||||
|  |     it('fold attached virtual lines are drawn correctly #21837', function() | ||||||
|  |       funcs.setline(1, 'line 1') | ||||||
|  |       funcs.setline(2, 'line 2') | ||||||
|  |       funcs.setline(3, 'line 3') | ||||||
|  |       funcs.setline(4, 'line 4') | ||||||
|  |       feed("zfj") | ||||||
|  |       exec_lua([[ | ||||||
|  |         local ns = vim.api.nvim_create_namespace("ns") | ||||||
|  |         vim.api.nvim_buf_set_extmark(0, ns, 0, 0, { virt_lines_above = true, virt_lines = {{{"virt_line above line 1", ""}}} }) | ||||||
|  |         vim.api.nvim_buf_set_extmark(0, ns, 1, 0, { virt_lines = {{{"virt_line below line 2", ""}}} }) | ||||||
|  |         vim.api.nvim_buf_set_extmark(0, ns, 2, 0, { virt_lines_above = true, virt_lines = {{{"virt_line above line 3", ""}}} }) | ||||||
|  |         vim.api.nvim_buf_set_extmark(0, ns, 3, 0, { virt_lines = {{{"virt_line below line 4", ""}}} }) | ||||||
|  |       ]]) | ||||||
|  |       if multigrid then | ||||||
|  |         screen:expect([[ | ||||||
|  |         ## grid 1 | ||||||
|  |           [2:---------------------------------------------]| | ||||||
|  |           [2:---------------------------------------------]| | ||||||
|  |           [2:---------------------------------------------]| | ||||||
|  |           [2:---------------------------------------------]| | ||||||
|  |           [2:---------------------------------------------]| | ||||||
|  |           [2:---------------------------------------------]| | ||||||
|  |           [2:---------------------------------------------]| | ||||||
|  |           [3:---------------------------------------------]| | ||||||
|  |         ## grid 2 | ||||||
|  |           {5:^+--  2 lines: line 1·························}| | ||||||
|  |           virt_line above line 3                       | | ||||||
|  |           line 3                                       | | ||||||
|  |           line 4                                       | | ||||||
|  |           virt_line below line 4                       | | ||||||
|  |           {1:~                                            }| | ||||||
|  |           {1:~                                            }| | ||||||
|  |         ## grid 3 | ||||||
|  |                                                        | | ||||||
|  |         ]]) | ||||||
|  |       else | ||||||
|  |         screen:expect([[ | ||||||
|  |           {5:^+--  2 lines: line 1·························}| | ||||||
|  |           virt_line above line 3                       | | ||||||
|  |           line 3                                       | | ||||||
|  |           line 4                                       | | ||||||
|  |           virt_line below line 4                       | | ||||||
|  |           {1:~                                            }| | ||||||
|  |           {1:~                                            }| | ||||||
|  |                                                        | | ||||||
|  |         ]]) | ||||||
|  |       end | ||||||
|  |  | ||||||
|  |       feed('jzfj') | ||||||
|  |       if multigrid then | ||||||
|  |         screen:expect([[ | ||||||
|  |         ## grid 1 | ||||||
|  |           [2:---------------------------------------------]| | ||||||
|  |           [2:---------------------------------------------]| | ||||||
|  |           [2:---------------------------------------------]| | ||||||
|  |           [2:---------------------------------------------]| | ||||||
|  |           [2:---------------------------------------------]| | ||||||
|  |           [2:---------------------------------------------]| | ||||||
|  |           [2:---------------------------------------------]| | ||||||
|  |           [3:---------------------------------------------]| | ||||||
|  |         ## grid 2 | ||||||
|  |           {5:+--  2 lines: line 1·························}| | ||||||
|  |           {5:^+--  2 lines: line 3·························}| | ||||||
|  |           {1:~                                            }| | ||||||
|  |           {1:~                                            }| | ||||||
|  |           {1:~                                            }| | ||||||
|  |           {1:~                                            }| | ||||||
|  |           {1:~                                            }| | ||||||
|  |         ## grid 3 | ||||||
|  |                                                        | | ||||||
|  |         ]]) | ||||||
|  |       else | ||||||
|  |         screen:expect([[ | ||||||
|  |           {5:+--  2 lines: line 1·························}| | ||||||
|  |           {5:^+--  2 lines: line 3·························}| | ||||||
|  |           {1:~                                            }| | ||||||
|  |           {1:~                                            }| | ||||||
|  |           {1:~                                            }| | ||||||
|  |           {1:~                                            }| | ||||||
|  |           {1:~                                            }| | ||||||
|  |                                                        | | ||||||
|  |         ]]) | ||||||
|  |       end | ||||||
|  |  | ||||||
|  |       feed('kzo<C-Y>') | ||||||
|  |       funcs.setline(5, 'line 5') | ||||||
|  |       if multigrid then | ||||||
|  |         screen:expect([[ | ||||||
|  |         ## grid 1 | ||||||
|  |           [2:---------------------------------------------]| | ||||||
|  |           [2:---------------------------------------------]| | ||||||
|  |           [2:---------------------------------------------]| | ||||||
|  |           [2:---------------------------------------------]| | ||||||
|  |           [2:---------------------------------------------]| | ||||||
|  |           [2:---------------------------------------------]| | ||||||
|  |           [2:---------------------------------------------]| | ||||||
|  |           [3:---------------------------------------------]| | ||||||
|  |         ## grid 2 | ||||||
|  |           virt_line above line 1                       | | ||||||
|  |           ^line 1                                       | | ||||||
|  |           line 2                                       | | ||||||
|  |           virt_line below line 2                       | | ||||||
|  |           {5:+--  2 lines: line 3·························}| | ||||||
|  |           line 5                                       | | ||||||
|  |           {1:~                                            }| | ||||||
|  |         ## grid 3 | ||||||
|  |                                                        | | ||||||
|  |         ]]) | ||||||
|  |       else | ||||||
|  |         screen:expect([[ | ||||||
|  |           virt_line above line 1                       | | ||||||
|  |           ^line 1                                       | | ||||||
|  |           line 2                                       | | ||||||
|  |           virt_line below line 2                       | | ||||||
|  |           {5:+--  2 lines: line 3·························}| | ||||||
|  |           line 5                                       | | ||||||
|  |           {1:~                                            }| | ||||||
|  |                                                        | | ||||||
|  |         ]]) | ||||||
|  |       end | ||||||
|  |     end) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   describe("with ext_multigrid", function() |   describe("with ext_multigrid", function() | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 luukvbaal
					luukvbaal