mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	extmark: refiy "Decoration" abstraction
one very important thought
This commit is contained in:
		 Björn Linse
					Björn Linse
				
			
				
					committed by
					
						 Thomas Vigouroux
						Thomas Vigouroux
					
				
			
			
				
	
			
			
			 Thomas Vigouroux
						Thomas Vigouroux
					
				
			
						parent
						
							3acfefb63e
						
					
				
				
					commit
					54ce1010e8
				
			| @@ -1318,7 +1318,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer id, | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   id_num = extmark_set(buf, (uint64_t)ns_id, id_num, |   id_num = extmark_set(buf, (uint64_t)ns_id, id_num, | ||||||
|                        (int)line, (colnr_T)col, kExtmarkUndo); |                        (int)line, (colnr_T)col, | ||||||
|  |                        -1, -1, NULL, kExtmarkUndo); | ||||||
|  |  | ||||||
|   return (Integer)id_num; |   return (Integer)id_num; | ||||||
| } | } | ||||||
| @@ -1425,10 +1426,13 @@ Integer nvim_buf_add_highlight(Buffer buffer, | |||||||
|     end_line++; |     end_line++; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   extmark_add_decoration(buf, ns_id, hlg_id, |   Decoration *decor = xcalloc(1, sizeof(*decor)); | ||||||
|                          (int)line, (colnr_T)col_start, |   decor->hl_id = hlg_id; | ||||||
|                          end_line, (colnr_T)col_end, |  | ||||||
|                          VIRTTEXT_EMPTY); |   ns_id = extmark_set(buf, ns_id, 0, | ||||||
|  |                       (int)line, (colnr_T)col_start, | ||||||
|  |                       end_line, (colnr_T)col_end, | ||||||
|  |                       decor, kExtmarkUndo); | ||||||
|   return src_id; |   return src_id; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -1592,9 +1596,10 @@ Integer nvim_buf_set_virtual_text(Buffer buffer, | |||||||
|     return src_id; |     return src_id; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   extmark_add_decoration(buf, ns_id, 0, |   Decoration *decor = xcalloc(1, sizeof(*decor)); | ||||||
|                          (int)line, 0, -1, -1, |   decor->virt_text = virt_text; | ||||||
|                          virt_text); |  | ||||||
|  |   extmark_set(buf, ns_id, 0, (int)line, 0, -1, -1, decor, kExtmarkUndo); | ||||||
|   return src_id; |   return src_id; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -1695,9 +1700,14 @@ Integer nvim__buf_add_decoration(Buffer buffer, Integer ns_id, String hl_group, | |||||||
|     return 0; |     return 0; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   uint64_t mark_id = extmark_add_decoration(buf, (uint64_t)ns_id, hlg_id, |   Decoration *decor = xcalloc(1, sizeof(*decor)); | ||||||
|                                             (int)start_row, (colnr_T)start_col, |   decor->hl_id = hlg_id; | ||||||
|                                             (int)end_row, (colnr_T)end_col, vt); |   decor->virt_text = vt; | ||||||
|  |  | ||||||
|  |   uint64_t mark_id = extmark_set(buf, (uint64_t)ns_id, 0, | ||||||
|  |                                  (int)start_row, (colnr_T)start_col, | ||||||
|  |                                  (int)end_row, (colnr_T)end_col, decor, | ||||||
|  |                                  kExtmarkUndo); | ||||||
|   return (Integer)mark_id; |   return (Integer)mark_id; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,29 +1,24 @@ | |||||||
| // This is an open source non-commercial project. Dear PVS-Studio, please check | // This is an open source non-commercial project. Dear PVS-Studio, please check | ||||||
| // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com | // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com | ||||||
|  |  | ||||||
| // Implements extended marks for plugins. Each mark exists in a btree of | // Implements extended marks for plugins. Marks sit in a MarkTree | ||||||
| // lines containing btrees of columns. | // datastructure which provides both efficient mark insertations/lookups | ||||||
|  | // and adjustment to text changes. See marktree.c for more details. | ||||||
| // | // | ||||||
| // The btree provides efficient range lookups. |  | ||||||
| // A map of pointers to the marks is used for fast lookup by mark id. | // A map of pointers to the marks is used for fast lookup by mark id. | ||||||
| // | // | ||||||
| // Marks are moved by calls to extmark_splice. Additionally mark_adjust | // Marks are moved by calls to extmark_splice. Some standard interfaces | ||||||
| // might adjust extmarks to line inserts/deletes. | // mark_adjust and inserted_bytes already adjust marks, check if these are | ||||||
|  | // being used before adding extmark_splice calls! | ||||||
| // | // | ||||||
| // Undo/Redo of marks is implemented by storing the call arguments to | // Undo/Redo of marks is implemented by storing the call arguments to | ||||||
| // extmark_splice. The list of arguments is applied in extmark_apply_undo. | // extmark_splice. The list of arguments is applied in extmark_apply_undo. | ||||||
| // The only case where we have to copy extmarks is for the area being effected | // We have to copy extmark positions when the extmarks are within a | ||||||
| // by a delete. | // deleted/changed region. | ||||||
| // | // | ||||||
| // Marks live in namespaces that allow plugins/users to segregate marks | // Marks live in namespaces that allow plugins/users to segregate marks | ||||||
| // from other users. | // from other users. | ||||||
| // | // | ||||||
| // For possible ideas for efficency improvements see: |  | ||||||
| // http://blog.atom.io/2015/06/16/optimizing-an-important-atom-primitive.html |  | ||||||
| // TODO(bfredl): These ideas could be used for an enhanced btree, which |  | ||||||
| // wouldn't need separate line and column layers. |  | ||||||
| // Other implementations exist in gtk and tk toolkits. |  | ||||||
| // |  | ||||||
| // Deleting marks only happens when explicitly calling extmark_del, deleteing | // Deleting marks only happens when explicitly calling extmark_del, deleteing | ||||||
| // over a range of marks will only move the marks. Deleting on a mark will | // over a range of marks will only move the marks. Deleting on a mark will | ||||||
| // leave it in same position unless it is on the EOL of a line. | // leave it in same position unless it is on the EOL of a line. | ||||||
| @@ -71,7 +66,8 @@ static ExtmarkNs *buf_ns_ref(buf_T *buf, uint64_t ns_id, bool put) { | |||||||
| /// must not be used during iteration! | /// must not be used during iteration! | ||||||
| /// @returns the mark id | /// @returns the mark id | ||||||
| uint64_t extmark_set(buf_T *buf, uint64_t ns_id, uint64_t id, | uint64_t extmark_set(buf_T *buf, uint64_t ns_id, uint64_t id, | ||||||
|                      int row, colnr_T col, ExtmarkOp op) |                      int row, colnr_T col, int end_row, colnr_T end_col, | ||||||
|  |                      Decoration *decor, ExtmarkOp op) | ||||||
| { | { | ||||||
|   ExtmarkNs *ns = buf_ns_ref(buf, ns_id, true); |   ExtmarkNs *ns = buf_ns_ref(buf, ns_id, true); | ||||||
|   mtpos_t old_pos; |   mtpos_t old_pos; | ||||||
| @@ -82,7 +78,7 @@ uint64_t extmark_set(buf_T *buf, uint64_t ns_id, uint64_t id, | |||||||
|   } else { |   } else { | ||||||
|     uint64_t old_mark = map_get(uint64_t, uint64_t)(ns->map, id); |     uint64_t old_mark = map_get(uint64_t, uint64_t)(ns->map, id); | ||||||
|     if (old_mark) { |     if (old_mark) { | ||||||
|       if (old_mark & MARKTREE_PAIRED_FLAG) { |       if (old_mark & MARKTREE_PAIRED_FLAG || end_row > -1) { | ||||||
|         extmark_del(buf, ns_id, id); |         extmark_del(buf, ns_id, id); | ||||||
|       } else { |       } else { | ||||||
|         // TODO(bfredl): we need to do more if "revising" a decoration mark. |         // TODO(bfredl): we need to do more if "revising" a decoration mark. | ||||||
| @@ -90,7 +86,12 @@ uint64_t extmark_set(buf_T *buf, uint64_t ns_id, uint64_t id, | |||||||
|         old_pos = marktree_lookup(buf->b_marktree, old_mark, itr); |         old_pos = marktree_lookup(buf->b_marktree, old_mark, itr); | ||||||
|         assert(itr->node); |         assert(itr->node); | ||||||
|         if (old_pos.row == row && old_pos.col == col) { |         if (old_pos.row == row && old_pos.col == col) { | ||||||
|           map_del(uint64_t, ExtmarkItem)(buf->b_extmark_index, old_mark); |           ExtmarkItem it = map_del(uint64_t, ExtmarkItem)(buf->b_extmark_index, | ||||||
|  |                                                           old_mark); | ||||||
|  |           if (it.decor) { | ||||||
|  |             decoration_redraw(buf, row, row, it.decor); | ||||||
|  |             free_decoration(it.decor); | ||||||
|  |           } | ||||||
|           mark = marktree_revise(buf->b_marktree, itr); |           mark = marktree_revise(buf->b_marktree, itr); | ||||||
|           goto revised; |           goto revised; | ||||||
|         } |         } | ||||||
| @@ -101,11 +102,17 @@ uint64_t extmark_set(buf_T *buf, uint64_t ns_id, uint64_t id, | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   mark = marktree_put(buf->b_marktree, row, col, true); |   if (end_row > -1) { | ||||||
|  |     mark = marktree_put_pair(buf->b_marktree, | ||||||
|  |                              row, col, true, | ||||||
|  |                              end_row, end_col, false); | ||||||
|  |   } else { | ||||||
|  |     mark = marktree_put(buf->b_marktree, row, col, true); | ||||||
|  |   } | ||||||
|  |  | ||||||
| revised: | revised: | ||||||
|   map_put(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark, |   map_put(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark, | ||||||
|                                  (ExtmarkItem){ ns_id, id, 0, |                                  (ExtmarkItem){ ns_id, id, decor }); | ||||||
|                                                 KV_INITIAL_VALUE }); |  | ||||||
|   map_put(uint64_t, uint64_t)(ns->map, id, mark); |   map_put(uint64_t, uint64_t)(ns->map, id, mark); | ||||||
|  |  | ||||||
|   if (op != kExtmarkNoUndo) { |   if (op != kExtmarkNoUndo) { | ||||||
| @@ -114,6 +121,10 @@ revised: | |||||||
|     // adding new marks to old undo headers. |     // adding new marks to old undo headers. | ||||||
|     u_extmark_set(buf, mark, row, col); |     u_extmark_set(buf, mark, row, col); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   if (decor) { | ||||||
|  |     decoration_redraw(buf, row, end_row > -1 ? end_row : row, decor); | ||||||
|  |   } | ||||||
|   return id; |   return id; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -152,27 +163,23 @@ bool extmark_del(buf_T *buf, uint64_t ns_id, uint64_t id) | |||||||
|   assert(pos.row >= 0); |   assert(pos.row >= 0); | ||||||
|   marktree_del_itr(buf->b_marktree, itr, false); |   marktree_del_itr(buf->b_marktree, itr, false); | ||||||
|   ExtmarkItem item = map_get(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark); |   ExtmarkItem item = map_get(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark); | ||||||
|  |   mtpos_t pos2 = pos; | ||||||
|  |  | ||||||
|   if (mark & MARKTREE_PAIRED_FLAG) { |   if (mark & MARKTREE_PAIRED_FLAG) { | ||||||
|     mtpos_t pos2 = marktree_lookup(buf->b_marktree, |     pos2 = marktree_lookup(buf->b_marktree, mark|MARKTREE_END_FLAG, itr); | ||||||
|                                    mark|MARKTREE_END_FLAG, itr); |  | ||||||
|     assert(pos2.row >= 0); |     assert(pos2.row >= 0); | ||||||
|     marktree_del_itr(buf->b_marktree, itr, false); |     marktree_del_itr(buf->b_marktree, itr, false); | ||||||
|     if (item.hl_id && pos2.row >= pos.row) { |  | ||||||
|       redraw_buf_range_later(buf, pos.row+1, pos2.row+1); |  | ||||||
|     } |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   if (kv_size(item.virt_text)) { |   if (item.decor) { | ||||||
|     redraw_buf_line_later(buf, pos.row+1); |     decoration_redraw(buf, pos.row, pos2.row, item.decor); | ||||||
|  |     free_decoration(item.decor); | ||||||
|   } |   } | ||||||
|   clear_virttext(&item.virt_text); |  | ||||||
|  |  | ||||||
|   map_del(uint64_t, uint64_t)(ns->map, id); |   map_del(uint64_t, uint64_t)(ns->map, id); | ||||||
|   map_del(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark); |   map_del(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark); | ||||||
|  |  | ||||||
|   // TODO(bfredl): delete it from current undo header, opportunistically? |   // TODO(bfredl): delete it from current undo header, opportunistically? | ||||||
|  |  | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -202,9 +209,11 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   // the value is either zero or the lnum (row+1) if highlight was present. |   // the value is either zero or the lnum (row+1) if highlight was present. | ||||||
|   static Map(uint64_t, uint64_t) *delete_set = NULL; |   static Map(uint64_t, ssize_t) *delete_set = NULL; | ||||||
|  |   typedef struct { Decoration *decor; int row1; } DecorItem; | ||||||
|  |   static kvec_t(DecorItem) decors; | ||||||
|   if (delete_set == NULL) { |   if (delete_set == NULL) { | ||||||
|     delete_set = map_new(uint64_t, uint64_t)(); |     delete_set = map_new(uint64_t, ssize_t)(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   MarkTreeIter itr[1] = { 0 }; |   MarkTreeIter itr[1] = { 0 }; | ||||||
| @@ -216,14 +225,16 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, | |||||||
|         || (mark.row == u_row && mark.col > u_col)) { |         || (mark.row == u_row && mark.col > u_col)) { | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
|     uint64_t *del_status = map_ref(uint64_t, uint64_t)(delete_set, mark.id, |     ssize_t *del_status = map_ref(uint64_t, ssize_t)(delete_set, mark.id, | ||||||
|                                                        false); |                                                      false); | ||||||
|     if (del_status) { |     if (del_status) { | ||||||
|       marktree_del_itr(buf->b_marktree, itr, false); |       marktree_del_itr(buf->b_marktree, itr, false); | ||||||
|       map_del(uint64_t, uint64_t)(delete_set, mark.id); |       if (*del_status >= 0) {  // we had a decor_id | ||||||
|       if (*del_status > 0) { |         DecorItem it = kv_A(decors, *del_status); | ||||||
|         redraw_buf_range_later(buf, (linenr_T)(*del_status), mark.row+1); |         decoration_redraw(buf, it.row1, mark.row, it.decor); | ||||||
|  |         free_decoration(it.decor); | ||||||
|       } |       } | ||||||
|  |       map_del(uint64_t, ssize_t)(delete_set, mark.id); | ||||||
|       continue; |       continue; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -233,15 +244,21 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, | |||||||
|  |  | ||||||
|     assert(item.ns_id > 0 && item.mark_id > 0); |     assert(item.ns_id > 0 && item.mark_id > 0); | ||||||
|     if (item.mark_id > 0 && (item.ns_id == ns_id || all_ns)) { |     if (item.mark_id > 0 && (item.ns_id == ns_id || all_ns)) { | ||||||
|       if (kv_size(item.virt_text)) { |  | ||||||
|         redraw_buf_line_later(buf, mark.row+1); |  | ||||||
|       } |  | ||||||
|       clear_virttext(&item.virt_text); |  | ||||||
|       marks_cleared = true; |       marks_cleared = true; | ||||||
|       if (mark.id & MARKTREE_PAIRED_FLAG) { |       if (mark.id & MARKTREE_PAIRED_FLAG) { | ||||||
|         uint64_t other = mark.id ^ MARKTREE_END_FLAG; |         uint64_t other = mark.id ^ MARKTREE_END_FLAG; | ||||||
|         uint64_t status = item.hl_id ? ((uint64_t)mark.row+1) : 0; |         ssize_t decor_id = -1; | ||||||
|         map_put(uint64_t, uint64_t)(delete_set, other, status); |         if (item.decor) { | ||||||
|  |           // Save the decoration and the first pos. Clear the decoration | ||||||
|  |           // later when we know the full range. | ||||||
|  |           decor_id = (ssize_t)kv_size(decors); | ||||||
|  |           kv_push(decors, | ||||||
|  |                   ((DecorItem) { .decor = item.decor, .row1 = mark.row })); | ||||||
|  |         } | ||||||
|  |         map_put(uint64_t, ssize_t)(delete_set, other, decor_id); | ||||||
|  |       } else if (item.decor) { | ||||||
|  |         decoration_redraw(buf, mark.row, mark.row, item.decor); | ||||||
|  |         free_decoration(item.decor); | ||||||
|       } |       } | ||||||
|       ExtmarkNs *my_ns = all_ns ? buf_ns_ref(buf, item.ns_id, false) : ns; |       ExtmarkNs *my_ns = all_ns ? buf_ns_ref(buf, item.ns_id, false) : ns; | ||||||
|       map_del(uint64_t, uint64_t)(my_ns->map, item.mark_id); |       map_del(uint64_t, uint64_t)(my_ns->map, item.mark_id); | ||||||
| @@ -251,16 +268,20 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, | |||||||
|       marktree_itr_next(buf->b_marktree, itr); |       marktree_itr_next(buf->b_marktree, itr); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   uint64_t id, status; |   uint64_t id; | ||||||
|   map_foreach(delete_set, id, status, { |   ssize_t decor_id; | ||||||
|  |   map_foreach(delete_set, id, decor_id, { | ||||||
|     mtpos_t pos = marktree_lookup(buf->b_marktree, id, itr); |     mtpos_t pos = marktree_lookup(buf->b_marktree, id, itr); | ||||||
|     assert(itr->node); |     assert(itr->node); | ||||||
|     marktree_del_itr(buf->b_marktree, itr, false); |     marktree_del_itr(buf->b_marktree, itr, false); | ||||||
|     if (status > 0) { |     if (decor_id >= 0) { | ||||||
|       redraw_buf_range_later(buf, (linenr_T)status, pos.row+1); |       DecorItem it = kv_A(decors, decor_id); | ||||||
|  |       decoration_redraw(buf, it.row1, pos.row, it.decor); | ||||||
|  |       free_decoration(it.decor); | ||||||
|     } |     } | ||||||
|   }); |   }); | ||||||
|   map_clear(uint64_t, uint64_t)(delete_set); |   map_clear(uint64_t, ssize_t)(delete_set); | ||||||
|  |   kv_size(decors) = 0; | ||||||
|   return marks_cleared; |   return marks_cleared; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -352,7 +373,7 @@ void extmark_free_all(buf_T *buf) | |||||||
|  |  | ||||||
|   map_foreach(buf->b_extmark_index, id, item, { |   map_foreach(buf->b_extmark_index, id, item, { | ||||||
|     (void)id; |     (void)id; | ||||||
|     clear_virttext(&item.virt_text); |     free_decoration(item.decor); | ||||||
|   }); |   }); | ||||||
|   map_free(uint64_t, ExtmarkItem)(buf->b_extmark_index); |   map_free(uint64_t, ExtmarkItem)(buf->b_extmark_index); | ||||||
|   buf->b_extmark_index = NULL; |   buf->b_extmark_index = NULL; | ||||||
| @@ -642,50 +663,6 @@ uint64_t src2ns(Integer *src_id) | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Adds a decoration to a buffer. |  | ||||||
| /// |  | ||||||
| /// Unlike matchaddpos() highlights, these follow changes to the the buffer |  | ||||||
| /// texts. Decorations are represented internally and in the API as extmarks. |  | ||||||
| /// |  | ||||||
| /// @param buf The buffer to add decorations to |  | ||||||
| /// @param ns_id A valid namespace id. |  | ||||||
| /// @param hl_id Id of the highlight group to use (or zero) |  | ||||||
| /// @param start_row The line to highlight |  | ||||||
| /// @param start_col First column to highlight |  | ||||||
| /// @param end_row The line to highlight |  | ||||||
| /// @param end_col The last column to highlight |  | ||||||
| /// @param virt_text Virtual text (currently placed at the EOL of start_row) |  | ||||||
| /// @return The extmark id inside the namespace |  | ||||||
| uint64_t extmark_add_decoration(buf_T *buf, uint64_t ns_id, int hl_id, |  | ||||||
|                                 int start_row, colnr_T start_col, |  | ||||||
|                                 int end_row, colnr_T end_col, |  | ||||||
|                                 VirtText virt_text) |  | ||||||
| { |  | ||||||
|   ExtmarkNs *ns = buf_ns_ref(buf, ns_id, true); |  | ||||||
|   ExtmarkItem item; |  | ||||||
|   item.ns_id = ns_id; |  | ||||||
|   item.mark_id = ns->free_id++; |  | ||||||
|   item.hl_id = hl_id; |  | ||||||
|   item.virt_text = virt_text; |  | ||||||
|  |  | ||||||
|   uint64_t mark; |  | ||||||
|  |  | ||||||
|   if (end_row > -1) { |  | ||||||
|     mark = marktree_put_pair(buf->b_marktree, |  | ||||||
|                              start_row, start_col, true, |  | ||||||
|                              end_row, end_col, false); |  | ||||||
|   } else { |  | ||||||
|     mark = marktree_put(buf->b_marktree, start_row, start_col, true); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   map_put(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark, item); |  | ||||||
|   map_put(uint64_t, uint64_t)(ns->map, item.mark_id, mark); |  | ||||||
|  |  | ||||||
|   redraw_buf_range_later(buf, start_row+1, |  | ||||||
|                          (end_row >= 0 ? end_row : start_row) + 1); |  | ||||||
|   return item.mark_id; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /// Add highlighting to a buffer, bounded by two cursor positions, | /// Add highlighting to a buffer, bounded by two cursor positions, | ||||||
| /// with an offset. | /// with an offset. | ||||||
| /// | /// | ||||||
| @@ -729,10 +706,30 @@ void bufhl_add_hl_pos_offset(buf_T *buf, | |||||||
|       hl_start = pos_start.col + offset; |       hl_start = pos_start.col + offset; | ||||||
|       hl_end = pos_end.col + offset; |       hl_end = pos_end.col + offset; | ||||||
|     } |     } | ||||||
|     (void)extmark_add_decoration(buf, (uint64_t)src_id, hl_id, |     Decoration *decor = xcalloc(1, sizeof(*decor)); | ||||||
|                                  (int)lnum-1, hl_start, |     decor->hl_id = hl_id; | ||||||
|                                  (int)lnum-1+end_off, hl_end, |     (void)extmark_set(buf, (uint64_t)src_id, 0, | ||||||
|                                  VIRTTEXT_EMPTY); |                       (int)lnum-1, hl_start, (int)lnum-1+end_off, hl_end, | ||||||
|  |                       decor, kExtmarkUndo); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void decoration_redraw(buf_T *buf, int row1, int row2, Decoration *decor) | ||||||
|  | { | ||||||
|  |   if (decor->hl_id && row2 >= row1) { | ||||||
|  |     redraw_buf_range_later(buf, row1+1, row2+1); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (kv_size(decor->virt_text)) { | ||||||
|  |     redraw_buf_line_later(buf, row1+1); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void free_decoration(Decoration *decor) | ||||||
|  | { | ||||||
|  |   if (decor) { | ||||||
|  |     clear_virttext(&decor->virt_text); | ||||||
|  |     xfree(decor); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -757,8 +754,8 @@ VirtText *extmark_find_virttext(buf_T *buf, int row, uint64_t ns_id) | |||||||
|     ExtmarkItem *item = map_ref(uint64_t, ExtmarkItem)(buf->b_extmark_index, |     ExtmarkItem *item = map_ref(uint64_t, ExtmarkItem)(buf->b_extmark_index, | ||||||
|                                                        mark.id, false); |                                                        mark.id, false); | ||||||
|     if (item && (ns_id == 0 || ns_id == item->ns_id) |     if (item && (ns_id == 0 || ns_id == item->ns_id) | ||||||
|         && kv_size(item->virt_text)) { |         && item->decor && kv_size(item->decor->virt_text)) { | ||||||
|       return &item->virt_text; |       return &item->decor->virt_text; | ||||||
|     } |     } | ||||||
|     marktree_itr_next(buf->b_marktree, itr); |     marktree_itr_next(buf->b_marktree, itr); | ||||||
|   } |   } | ||||||
| @@ -787,7 +784,6 @@ bool decorations_redraw_start(buf_T *buf, int top_row, | |||||||
|     if (mark.row < 0) {  // || mark.row > end_row |     if (mark.row < 0) {  // || mark.row > end_row | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
|     // TODO(bfredl): dedicated flag for being a decoration? |  | ||||||
|     if ((mark.row < top_row && mark.id&MARKTREE_END_FLAG)) { |     if ((mark.row < top_row && mark.id&MARKTREE_END_FLAG)) { | ||||||
|       goto next_mark; |       goto next_mark; | ||||||
|     } |     } | ||||||
| @@ -797,25 +793,30 @@ bool decorations_redraw_start(buf_T *buf, int top_row, | |||||||
|     uint64_t start_id = mark.id & ~MARKTREE_END_FLAG; |     uint64_t start_id = mark.id & ~MARKTREE_END_FLAG; | ||||||
|     ExtmarkItem *item = map_ref(uint64_t, ExtmarkItem)(buf->b_extmark_index, |     ExtmarkItem *item = map_ref(uint64_t, ExtmarkItem)(buf->b_extmark_index, | ||||||
|                                                        start_id, false); |                                                        start_id, false); | ||||||
|  |     if (!item || !item->decor) { | ||||||
|  |     // TODO(bfredl): dedicated flag for being a decoration? | ||||||
|  |       goto next_mark; | ||||||
|  |     } | ||||||
|  |     Decoration *decor = item->decor; | ||||||
|  |  | ||||||
|     if ((!(mark.id&MARKTREE_END_FLAG) && altpos.row < top_row |     if ((!(mark.id&MARKTREE_END_FLAG) && altpos.row < top_row | ||||||
|          && item && !kv_size(item->virt_text)) |          && item && !kv_size(decor->virt_text)) | ||||||
|         || ((mark.id&MARKTREE_END_FLAG) && altpos.row >= top_row)) { |         || ((mark.id&MARKTREE_END_FLAG) && altpos.row >= top_row)) { | ||||||
|       goto next_mark; |       goto next_mark; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (item && (item->hl_id > 0 || kv_size(item->virt_text))) { |     int attr_id = decor->hl_id > 0 ? syn_id2attr(decor->hl_id) : 0; | ||||||
|       int attr_id = item->hl_id > 0 ? syn_id2attr(item->hl_id) : 0; |     VirtText *vt = kv_size(decor->virt_text) ? &decor->virt_text : NULL; | ||||||
|       VirtText *vt = kv_size(item->virt_text) ? &item->virt_text : NULL; |     HlRange range; | ||||||
|       HlRange range; |     if (mark.id&MARKTREE_END_FLAG) { | ||||||
|       if (mark.id&MARKTREE_END_FLAG) { |       range = (HlRange){ altpos.row, altpos.col, mark.row, mark.col, | ||||||
|         range = (HlRange){ altpos.row, altpos.col, mark.row, mark.col, |                          attr_id, vt }; | ||||||
|                            attr_id, vt }; |     } else { | ||||||
|       } else { |       range = (HlRange){ mark.row, mark.col, altpos.row, | ||||||
|         range = (HlRange){ mark.row, mark.col, altpos.row, |                          altpos.col, attr_id, vt }; | ||||||
|                            altpos.col, attr_id, vt }; |  | ||||||
|       } |  | ||||||
|       kv_push(state->active, range); |  | ||||||
|     } |     } | ||||||
|  |     kv_push(state->active, range); | ||||||
|  |  | ||||||
| next_mark: | next_mark: | ||||||
|     if (marktree_itr_node_done(state->itr)) { |     if (marktree_itr_node_done(state->itr)) { | ||||||
|       break; |       break; | ||||||
| @@ -860,21 +861,24 @@ int decorations_redraw_col(buf_T *buf, int col, DecorationRedrawState *state) | |||||||
|  |  | ||||||
|     ExtmarkItem *item = map_ref(uint64_t, ExtmarkItem)(buf->b_extmark_index, |     ExtmarkItem *item = map_ref(uint64_t, ExtmarkItem)(buf->b_extmark_index, | ||||||
|                                                        mark.id, false); |                                                        mark.id, false); | ||||||
|  |     if (!item || !item->decor) { | ||||||
|  |     // TODO(bfredl): dedicated flag for being a decoration? | ||||||
|  |       goto next_mark; | ||||||
|  |     } | ||||||
|  |     Decoration *decor = item->decor; | ||||||
|  |  | ||||||
|     if (endpos.row < mark.row |     if (endpos.row < mark.row | ||||||
|         || (endpos.row == mark.row && endpos.col <= mark.col)) { |         || (endpos.row == mark.row && endpos.col <= mark.col)) { | ||||||
|       if (item && !kv_size(item->virt_text)) { |       if (item && !kv_size(decor->virt_text)) { | ||||||
|         goto next_mark; |         goto next_mark; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (item && (item->hl_id > 0 || kv_size(item->virt_text))) { |     int attr_id = decor->hl_id > 0 ? syn_id2attr(decor->hl_id) : 0; | ||||||
|       int attr_id = item->hl_id > 0 ? syn_id2attr(item->hl_id) : 0; |     VirtText *vt = kv_size(decor->virt_text) ? &decor->virt_text : NULL; | ||||||
|       VirtText *vt = kv_size(item->virt_text) ? &item->virt_text : NULL; |     kv_push(state->active, ((HlRange){ mark.row, mark.col, | ||||||
|       kv_push(state->active, ((HlRange){ mark.row, mark.col, |                                        endpos.row, endpos.col, | ||||||
|                                          endpos.row, endpos.col, |                                        attr_id, vt })); | ||||||
|                                          attr_id, vt })); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| next_mark: | next_mark: | ||||||
|     marktree_itr_next(buf->b_marktree, state->itr); |     marktree_itr_next(buf->b_marktree, state->itr); | ||||||
|   | |||||||
| @@ -12,14 +12,21 @@ typedef struct { | |||||||
| typedef kvec_t(VirtTextChunk) VirtText; | typedef kvec_t(VirtTextChunk) VirtText; | ||||||
| #define VIRTTEXT_EMPTY ((VirtText)KV_INITIAL_VALUE) | #define VIRTTEXT_EMPTY ((VirtText)KV_INITIAL_VALUE) | ||||||
|  |  | ||||||
|  | typedef struct | ||||||
|  | { | ||||||
|  |   int hl_id;  // highlight group | ||||||
|  |   VirtText virt_text; | ||||||
|  |   // TODO(bfredl): style, signs, etc | ||||||
|  | } Decoration; | ||||||
|  |  | ||||||
| typedef struct | typedef struct | ||||||
| { | { | ||||||
|   uint64_t ns_id; |   uint64_t ns_id; | ||||||
|   uint64_t mark_id; |   uint64_t mark_id; | ||||||
|   int hl_id;  // highlight group |   // TODO(bfredl): a lot of small allocations. Should probably use | ||||||
|   // TODO(bfredl): virt_text is pretty larger than the rest, |   // kvec_t(Decoration) as an arena. Alternatively, store ns_id/mark_id | ||||||
|   // pointer indirection? |   // _inline_ in MarkTree and use the map only for decorations. | ||||||
|   VirtText virt_text; |   Decoration *decor; | ||||||
| } ExtmarkItem; | } ExtmarkItem; | ||||||
|  |  | ||||||
| typedef struct undo_object ExtmarkUndoObject; | typedef struct undo_object ExtmarkUndoObject; | ||||||
|   | |||||||
| @@ -184,7 +184,7 @@ MAP_IMPL(uint64_t, uint64_t, DEFAULT_INITIALIZER) | |||||||
| #define EXTMARK_NS_INITIALIZER { 0, 0 } | #define EXTMARK_NS_INITIALIZER { 0, 0 } | ||||||
| MAP_IMPL(uint64_t, ExtmarkNs, EXTMARK_NS_INITIALIZER) | MAP_IMPL(uint64_t, ExtmarkNs, EXTMARK_NS_INITIALIZER) | ||||||
| #define KVEC_INITIALIZER { .size = 0, .capacity = 0, .items = NULL } | #define KVEC_INITIALIZER { .size = 0, .capacity = 0, .items = NULL } | ||||||
| #define EXTMARK_ITEM_INITIALIZER { 0, 0, 0, KVEC_INITIALIZER } | #define EXTMARK_ITEM_INITIALIZER { 0, 0, NULL } | ||||||
| MAP_IMPL(uint64_t, ExtmarkItem, EXTMARK_ITEM_INITIALIZER) | MAP_IMPL(uint64_t, ExtmarkItem, EXTMARK_ITEM_INITIALIZER) | ||||||
| MAP_IMPL(handle_T, ptr_t, DEFAULT_INITIALIZER) | MAP_IMPL(handle_T, ptr_t, DEFAULT_INITIALIZER) | ||||||
| #define MSGPACK_HANDLER_INITIALIZER { .fn = NULL, .fast = false } | #define MSGPACK_HANDLER_INITIALIZER { .fn = NULL, .fast = false } | ||||||
|   | |||||||
| @@ -2554,6 +2554,7 @@ win_line ( | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   // If this line has a sign with line highlighting set line_attr. |   // If this line has a sign with line highlighting set line_attr. | ||||||
|  |   // TODO(bfredl, vigoux): this should not take priority over decorations! | ||||||
|   v = buf_getsigntype(wp->w_buffer, lnum, SIGN_LINEHL, 0, 1); |   v = buf_getsigntype(wp->w_buffer, lnum, SIGN_LINEHL, 0, 1); | ||||||
|   if (v != 0) { |   if (v != 0) { | ||||||
|     line_attr = sign_get_attr((int)v, SIGN_LINEHL); |     line_attr = sign_get_attr((int)v, SIGN_LINEHL); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user