mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	buffer: add support for virtual text annotations
This commit is contained in:
		| @@ -967,6 +967,85 @@ void nvim_buf_clear_highlight(Buffer buffer, | |||||||
|   bufhl_clear_line_range(buf, (int)src_id, (int)line_start+1, (int)line_end); |   bufhl_clear_line_range(buf, (int)src_id, (int)line_start+1, (int)line_end); | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /// Set the virtual text (annotation) for a buffer line. | ||||||
|  | /// | ||||||
|  | /// By default (and currently the only option) the text will be placed after | ||||||
|  | /// the buffer text. Virtual text will never cause reflow, rather virtual | ||||||
|  | /// text will be truncated at the end of the screen line. The virtual text will | ||||||
|  | /// begin after one cell to the right of the ordinary text, this will contain | ||||||
|  | /// the |lcs-eol| char if set, otherwise just be a space. | ||||||
|  | /// | ||||||
|  | /// @param buffer     Buffer handle | ||||||
|  | /// @param src_id     Source group to use or 0 to use a new group, | ||||||
|  | ///                   or -1 for a ungrouped annotation | ||||||
|  | /// @param line       Line to annotate with virtual text (zero-indexed) | ||||||
|  | /// @param chunks     A list of [text, hl_group] arrays, each representing a | ||||||
|  | ///                   text chunk with specified highlight. `hl_group` element | ||||||
|  | ///                   can be omitted for no highlight. | ||||||
|  | /// @param opts       Optional parameters. Currently not used. | ||||||
|  | /// @param[out] err   Error details, if any | ||||||
|  | /// @return The src_id that was used | ||||||
|  | Integer nvim_buf_set_virtual_text(Buffer buffer, | ||||||
|  |                                   Integer src_id, | ||||||
|  |                                   Integer line, | ||||||
|  |                                   Array chunks, | ||||||
|  |                                   Dictionary opts, | ||||||
|  |                                   Error *err) | ||||||
|  |   FUNC_API_SINCE(5) | ||||||
|  | { | ||||||
|  |   buf_T *buf = find_buffer_by_handle(buffer, err); | ||||||
|  |   if (!buf) { | ||||||
|  |     return 0; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (line < 0 || line >= MAXLNUM) { | ||||||
|  |     api_set_error(err, kErrorTypeValidation, "Line number outside range"); | ||||||
|  |     return 0; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (opts.size > 0) { | ||||||
|  |     api_set_error(err, kErrorTypeValidation, "opts dict isn't empty"); | ||||||
|  |     return 0; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   VirtText virt_text = KV_INITIAL_VALUE; | ||||||
|  |   for (size_t i = 0; i < chunks.size; i++) { | ||||||
|  |     if (chunks.items[i].type != kObjectTypeArray) { | ||||||
|  |       api_set_error(err, kErrorTypeValidation, "Chunk is not an array"); | ||||||
|  |       goto free_exit; | ||||||
|  |     } | ||||||
|  |     Array chunk = chunks.items[i].data.array; | ||||||
|  |     if (chunk.size == 0 || chunk.size > 2 | ||||||
|  |         || chunk.items[0].type != kObjectTypeString | ||||||
|  |         || (chunk.size == 2 && chunk.items[1].type != kObjectTypeString)) { | ||||||
|  |       api_set_error(err, kErrorTypeValidation, | ||||||
|  |                     "Chunk is not an array with one or two strings"); | ||||||
|  |       goto free_exit; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     String str = chunk.items[0].data.string; | ||||||
|  |     char *text = xstrdup(str.size > 0 ? str.data : ""); | ||||||
|  |  | ||||||
|  |     int hl_id = 0; | ||||||
|  |     if (chunk.size == 2) { | ||||||
|  |       String hl = chunk.items[1].data.string; | ||||||
|  |       if (hl.size > 0) { | ||||||
|  |         hl_id = syn_check_group((char_u *)hl.data, (int)hl.size); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     kv_push(virt_text, ((VirtTextChunk){ .text = text, .hl_id = hl_id })); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   src_id = bufhl_add_virt_text(buf, (int)src_id, (linenr_T)line+1, | ||||||
|  |                                virt_text); | ||||||
|  |   return src_id; | ||||||
|  |  | ||||||
|  | free_exit: | ||||||
|  |   kv_destroy(virt_text); | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
| // Check if deleting lines made the cursor position invalid. | // Check if deleting lines made the cursor position invalid. | ||||||
| // Changed the lines from "lo" to "hi" and added "extra" lines (negative if | // Changed the lines from "lo" to "hi" and added "extra" lines (negative if | ||||||
| // deleted). | // deleted). | ||||||
|   | |||||||
| @@ -5375,6 +5375,45 @@ void bufhl_add_hl_pos_offset(buf_T *buf, | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | int bufhl_add_virt_text(buf_T *buf, | ||||||
|  |                         int src_id, | ||||||
|  |                         linenr_T lnum, | ||||||
|  |                         VirtText virt_text) | ||||||
|  | { | ||||||
|  |   static int next_src_id = 1; | ||||||
|  |   if (src_id == 0) { | ||||||
|  |     src_id = next_src_id++; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   BufhlLine *lineinfo = bufhl_tree_ref(&buf->b_bufhl_info, lnum, true); | ||||||
|  |  | ||||||
|  |   bufhl_clear_virttext(&lineinfo->virt_text); | ||||||
|  |   if (kv_size(virt_text) > 0) { | ||||||
|  |     lineinfo->virt_text_src = src_id; | ||||||
|  |     lineinfo->virt_text = virt_text; | ||||||
|  |   } else { | ||||||
|  |     lineinfo->virt_text_src = 0; | ||||||
|  |     // currently not needed, but allow a future caller with | ||||||
|  |     // 0 size and non-zero capacity | ||||||
|  |     kv_destroy(virt_text); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (0 < lnum && lnum <= buf->b_ml.ml_line_count) { | ||||||
|  |     changed_lines_buf(buf, lnum, lnum+1, 0); | ||||||
|  |     redraw_buf_later(buf, VALID); | ||||||
|  |   } | ||||||
|  |   return src_id; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void bufhl_clear_virttext(VirtText *text) | ||||||
|  | { | ||||||
|  |   for (size_t i = 0; i < kv_size(*text); i++) { | ||||||
|  |     xfree(kv_A(*text, i).text); | ||||||
|  |   } | ||||||
|  |   kv_destroy(*text); | ||||||
|  |   *text = (VirtText)KV_INITIAL_VALUE; | ||||||
|  | } | ||||||
|  |  | ||||||
| /// Clear bufhl highlights from a given source group and range of lines. | /// Clear bufhl highlights from a given source group and range of lines. | ||||||
| /// | /// | ||||||
| /// @param buf The buffer to remove highlights from | /// @param buf The buffer to remove highlights from | ||||||
| @@ -5430,6 +5469,7 @@ void bufhl_clear_line_range(buf_T *buf, | |||||||
| static BufhlLineStatus bufhl_clear_line(BufhlLine *lineinfo, int src_id, | static BufhlLineStatus bufhl_clear_line(BufhlLine *lineinfo, int src_id, | ||||||
|                                         linenr_T lnum) |                                         linenr_T lnum) | ||||||
| { | { | ||||||
|  |   BufhlLineStatus changed = kBLSUnchanged; | ||||||
|   size_t oldsize = kv_size(lineinfo->items); |   size_t oldsize = kv_size(lineinfo->items); | ||||||
|   if (src_id < 0) { |   if (src_id < 0) { | ||||||
|     kv_size(lineinfo->items) = 0; |     kv_size(lineinfo->items) = 0; | ||||||
| @@ -5445,14 +5485,25 @@ static BufhlLineStatus bufhl_clear_line(BufhlLine *lineinfo, int src_id, | |||||||
|     } |     } | ||||||
|     kv_size(lineinfo->items) = newidx; |     kv_size(lineinfo->items) = newidx; | ||||||
|   } |   } | ||||||
|  |   if (kv_size(lineinfo->items) != oldsize) { | ||||||
|  |     changed = kBLSChanged; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   if (kv_size(lineinfo->items) == 0) { |   if (kv_size(lineinfo->virt_text) != 0 | ||||||
|  |       && (src_id < 0 || src_id == lineinfo->virt_text_src)) { | ||||||
|  |     bufhl_clear_virttext(&lineinfo->virt_text); | ||||||
|  |     lineinfo->virt_text_src = 0; | ||||||
|  |     changed = kBLSChanged; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (kv_size(lineinfo->items) == 0 && kv_size(lineinfo->virt_text) == 0) { | ||||||
|     kv_destroy(lineinfo->items); |     kv_destroy(lineinfo->items); | ||||||
|     return kBLSDeleted; |     return kBLSDeleted; | ||||||
|   } |   } | ||||||
|   return kv_size(lineinfo->items) != oldsize ? kBLSChanged : kBLSUnchanged; |   return changed; | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| /// Remove all highlights and free the highlight data | /// Remove all highlights and free the highlight data | ||||||
| void bufhl_clear_all(buf_T *buf) | void bufhl_clear_all(buf_T *buf) | ||||||
| { | { | ||||||
| @@ -5527,8 +5578,8 @@ bool bufhl_start_line(buf_T *buf, linenr_T lnum, BufhlLineInfo *info) | |||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|   info->valid_to = -1; |   info->valid_to = -1; | ||||||
|   info->entries = lineinfo->items; |   info->line = lineinfo; | ||||||
|   return kv_size(info->entries) > 0; |   return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| /// get highlighting at column col | /// get highlighting at column col | ||||||
| @@ -5548,8 +5599,8 @@ int bufhl_get_attr(BufhlLineInfo *info, colnr_T col) | |||||||
|   } |   } | ||||||
|   int attr = 0; |   int attr = 0; | ||||||
|   info->valid_to = MAXCOL; |   info->valid_to = MAXCOL; | ||||||
|   for (size_t i = 0; i < kv_size(info->entries); i++) { |   for (size_t i = 0; i < kv_size(info->line->items); i++) { | ||||||
|     BufhlItem entry = kv_A(info->entries, i); |     BufhlItem entry = kv_A(info->line->items, i); | ||||||
|     if (entry.start <= col && col <= entry.stop) { |     if (entry.start <= col && col <= entry.stop) { | ||||||
|       int entry_attr = syn_id2attr(entry.hl_id); |       int entry_attr = syn_id2attr(entry.hl_id); | ||||||
|       attr = hl_combine_attr(attr, entry_attr); |       attr = hl_combine_attr(attr, entry_attr); | ||||||
|   | |||||||
| @@ -14,16 +14,23 @@ typedef struct { | |||||||
|   colnr_T stop;  // last column to highlight |   colnr_T stop;  // last column to highlight | ||||||
| } BufhlItem; | } BufhlItem; | ||||||
|  |  | ||||||
| typedef kvec_t(BufhlItem) BufhlItemVec; | typedef struct { | ||||||
|  |   char *text; | ||||||
|  |   int hl_id; | ||||||
|  | } VirtTextChunk; | ||||||
|  |  | ||||||
|  | typedef kvec_t(VirtTextChunk) VirtText; | ||||||
|  |  | ||||||
| typedef struct { | typedef struct { | ||||||
|   linenr_T line; |   linenr_T line; | ||||||
|   BufhlItemVec items; |   kvec_t(BufhlItem) items; | ||||||
|  |   int virt_text_src; | ||||||
|  |   VirtText virt_text; | ||||||
| } BufhlLine; | } BufhlLine; | ||||||
| #define BUFHLLINE_INIT(l) { l, KV_INITIAL_VALUE } | #define BUFHLLINE_INIT(l) { l, KV_INITIAL_VALUE, 0,  KV_INITIAL_VALUE } | ||||||
|  |  | ||||||
| typedef struct { | typedef struct { | ||||||
|   BufhlItemVec entries; |   BufhlLine *line; | ||||||
|   int current; |   int current; | ||||||
|   colnr_T valid_to; |   colnr_T valid_to; | ||||||
| } BufhlLineInfo; | } BufhlLineInfo; | ||||||
|   | |||||||
| @@ -132,6 +132,15 @@ static schar_T  *current_ScreenLine; | |||||||
| StlClickDefinition *tab_page_click_defs = NULL; | StlClickDefinition *tab_page_click_defs = NULL; | ||||||
| long tab_page_click_defs_size = 0; | long tab_page_click_defs_size = 0; | ||||||
|  |  | ||||||
|  | // for line_putchar. Contains the state that needs to be remembered from | ||||||
|  | // putting one character to the next. | ||||||
|  | typedef struct { | ||||||
|  |   const char_u *p; | ||||||
|  |   int prev_c;  // previous Arabic character | ||||||
|  |   int prev_c1;  // first composing char for prev_c | ||||||
|  | } LineState; | ||||||
|  | #define LINE_STATE(p) { p, 0, 0 } | ||||||
|  |  | ||||||
| #ifdef INCLUDE_GENERATED_DECLARATIONS | #ifdef INCLUDE_GENERATED_DECLARATIONS | ||||||
| # include "screen.c.generated.h" | # include "screen.c.generated.h" | ||||||
| #endif | #endif | ||||||
| @@ -1731,6 +1740,56 @@ static int compute_foldcolumn(win_T *wp, int col) | |||||||
|   return fdc; |   return fdc; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /// Put a single char from an UTF-8 buffer into a line buffer. | ||||||
|  | /// | ||||||
|  | /// Handles composing chars and arabic shaping state. | ||||||
|  | static int line_putchar(LineState *s, schar_T *dest, int maxcells, bool rl) | ||||||
|  | { | ||||||
|  |   const char_u *p = s->p; | ||||||
|  |   int cells = utf_ptr2cells(p); | ||||||
|  |   int c_len = utfc_ptr2len(p); | ||||||
|  |   int u8c, u8cc[MAX_MCO]; | ||||||
|  |   if (cells > maxcells) { | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  |   u8c = utfc_ptr2char(p, u8cc); | ||||||
|  |   if (*p < 0x80 && u8cc[0] == 0) { | ||||||
|  |     schar_from_ascii(dest[0], *p); | ||||||
|  |     s->prev_c = u8c; | ||||||
|  |   } else { | ||||||
|  |     if (p_arshape && !p_tbidi && arabic_char(u8c)) { | ||||||
|  |       // Do Arabic shaping. | ||||||
|  |       int pc, pc1, nc; | ||||||
|  |       int pcc[MAX_MCO]; | ||||||
|  |       int firstbyte = *p; | ||||||
|  |  | ||||||
|  |       // The idea of what is the previous and next | ||||||
|  |       // character depends on 'rightleft'. | ||||||
|  |       if (rl) { | ||||||
|  |         pc = s->prev_c; | ||||||
|  |         pc1 = s->prev_c1; | ||||||
|  |         nc = utf_ptr2char(p + c_len); | ||||||
|  |         s->prev_c1 = u8cc[0]; | ||||||
|  |       } else { | ||||||
|  |         pc = utfc_ptr2char(p + c_len, pcc); | ||||||
|  |         nc = s->prev_c; | ||||||
|  |         pc1 = pcc[0]; | ||||||
|  |       } | ||||||
|  |       s->prev_c = u8c; | ||||||
|  |  | ||||||
|  |       u8c = arabic_shape(u8c, &firstbyte, &u8cc[0], pc, pc1, nc); | ||||||
|  |     } else { | ||||||
|  |       s->prev_c = u8c; | ||||||
|  |     } | ||||||
|  |     schar_from_cc(dest[0], u8c, u8cc); | ||||||
|  |   } | ||||||
|  |   if (cells > 1) { | ||||||
|  |     dest[1][0] = 0; | ||||||
|  |   } | ||||||
|  |   s->p += c_len; | ||||||
|  |   return cells; | ||||||
|  | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * Display one folded line. |  * Display one folded line. | ||||||
|  */ |  */ | ||||||
| @@ -1863,13 +1922,7 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T | |||||||
|    *    Right-left text is put in columns 0 - number-col, normal text is put |    *    Right-left text is put in columns 0 - number-col, normal text is put | ||||||
|    *    in columns number-col - window-width. |    *    in columns number-col - window-width. | ||||||
|    */ |    */ | ||||||
|   int cells; |  | ||||||
|   int u8c, u8cc[MAX_MCO]; |  | ||||||
|   int idx; |   int idx; | ||||||
|   int c_len; |  | ||||||
|   char_u  *p; |  | ||||||
|   int prev_c = 0;  // previous Arabic character |  | ||||||
|   int prev_c1 = 0;  // first composing char for prev_c |  | ||||||
|  |  | ||||||
|   if (wp->w_p_rl) { |   if (wp->w_p_rl) { | ||||||
|     idx = off; |     idx = off; | ||||||
| @@ -1877,50 +1930,20 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T | |||||||
|     idx = off + col; |     idx = off + col; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Store multibyte characters in ScreenLines[] et al. correctly. |   LineState s = LINE_STATE(text); | ||||||
|   for (p = text; *p != NUL; ) { |  | ||||||
|     cells = utf_ptr2cells(p); |   while (*s.p != NUL) { | ||||||
|     c_len = utfc_ptr2len(p); |     // TODO(bfredl): cargo-culted from the old Vim code: | ||||||
|     if (col + cells > wp->w_width - (wp->w_p_rl ? col : 0)) { |     // if(col + cells > wp->w_width - (wp->w_p_rl ? col : 0)) { break; } | ||||||
|  |     // This is obvious wrong. If Vim ever fixes this, solve for "cells" again | ||||||
|  |     // in the correct condition. | ||||||
|  |     int maxcells = wp->w_width - col - (wp->w_p_rl ? col : 0); | ||||||
|  |     int cells = line_putchar(&s, &ScreenLines[idx], maxcells, wp->w_p_rl); | ||||||
|  |     if (cells == -1) { | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
|     u8c = utfc_ptr2char(p, u8cc); |  | ||||||
|     if (*p < 0x80 && u8cc[0] == 0) { |  | ||||||
|       schar_from_ascii(ScreenLines[idx], *p); |  | ||||||
|       prev_c = u8c; |  | ||||||
|     } else { |  | ||||||
|       if (p_arshape && !p_tbidi && arabic_char(u8c)) { |  | ||||||
|         // Do Arabic shaping. |  | ||||||
|         int pc, pc1, nc; |  | ||||||
|         int pcc[MAX_MCO]; |  | ||||||
|         int firstbyte = *p; |  | ||||||
|  |  | ||||||
|         // The idea of what is the previous and next |  | ||||||
|         // character depends on 'rightleft'. |  | ||||||
|         if (wp->w_p_rl) { |  | ||||||
|           pc = prev_c; |  | ||||||
|           pc1 = prev_c1; |  | ||||||
|           nc = utf_ptr2char(p + c_len); |  | ||||||
|           prev_c1 = u8cc[0]; |  | ||||||
|         } else { |  | ||||||
|           pc = utfc_ptr2char(p + c_len, pcc); |  | ||||||
|           nc = prev_c; |  | ||||||
|           pc1 = pcc[0]; |  | ||||||
|         } |  | ||||||
|         prev_c = u8c; |  | ||||||
|  |  | ||||||
|         u8c = arabic_shape(u8c, &firstbyte, &u8cc[0], pc, pc1, nc); |  | ||||||
|       } else { |  | ||||||
|         prev_c = u8c; |  | ||||||
|       } |  | ||||||
|       schar_from_cc(ScreenLines[idx], u8c, u8cc); |  | ||||||
|     } |  | ||||||
|     if (cells > 1) { |  | ||||||
|       ScreenLines[idx + 1][0] = 0; |  | ||||||
|     } |  | ||||||
|     col += cells; |     col += cells; | ||||||
|     idx += cells; |     idx += cells; | ||||||
|     p += c_len; |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   /* Fill the rest of the line with the fold filler */ |   /* Fill the rest of the line with the fold filler */ | ||||||
| @@ -2215,9 +2238,9 @@ win_line ( | |||||||
|   int did_line_attr = 0; |   int did_line_attr = 0; | ||||||
|  |  | ||||||
|   bool search_attr_from_match = false;  // if search_attr is from :match |   bool search_attr_from_match = false;  // if search_attr is from :match | ||||||
|   bool has_bufhl = false;               // this buffer has highlight matches |  | ||||||
|   int bufhl_attr = 0;                   // attributes desired by bufhl |  | ||||||
|   BufhlLineInfo bufhl_info;             // bufhl data for this line |   BufhlLineInfo bufhl_info;             // bufhl data for this line | ||||||
|  |   bool has_bufhl = false;               // this buffer has highlight matches | ||||||
|  |   bool do_virttext = false;             // draw virtual text for this line | ||||||
|  |  | ||||||
|   /* draw_state: items that are drawn in sequence: */ |   /* draw_state: items that are drawn in sequence: */ | ||||||
| #define WL_START        0               /* nothing done yet */ | #define WL_START        0               /* nothing done yet */ | ||||||
| @@ -2279,9 +2302,14 @@ win_line ( | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (bufhl_start_line(wp->w_buffer, lnum, &bufhl_info)) { |     if (bufhl_start_line(wp->w_buffer, lnum, &bufhl_info)) { | ||||||
|  |       if (kv_size(bufhl_info.line->items)) { | ||||||
|         has_bufhl = true; |         has_bufhl = true; | ||||||
|         extra_check = true; |         extra_check = true; | ||||||
|       } |       } | ||||||
|  |       if (kv_size(bufhl_info.line->virt_text)) { | ||||||
|  |         do_virttext = true; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // Check for columns to display for 'colorcolumn'. |     // Check for columns to display for 'colorcolumn'. | ||||||
|     color_cols = wp->w_buffer->terminal ? NULL : wp->w_p_cc_cols; |     color_cols = wp->w_buffer->terminal ? NULL : wp->w_p_cc_cols; | ||||||
| @@ -3429,7 +3457,7 @@ win_line ( | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (has_bufhl && v > 0) { |         if (has_bufhl && v > 0) { | ||||||
|           bufhl_attr = bufhl_get_attr(&bufhl_info, (colnr_T)v); |           int bufhl_attr = bufhl_get_attr(&bufhl_info, (colnr_T)v); | ||||||
|           if (bufhl_attr != 0) { |           if (bufhl_attr != 0) { | ||||||
|             if (!attr_pri) { |             if (!attr_pri) { | ||||||
|               char_attr = hl_combine_attr(char_attr, bufhl_attr); |               char_attr = hl_combine_attr(char_attr, bufhl_attr); | ||||||
| @@ -3949,40 +3977,87 @@ win_line ( | |||||||
|             && (int)wp->w_virtcol < |             && (int)wp->w_virtcol < | ||||||
|             wp->w_width * (row - startrow + 1) + v |             wp->w_width * (row - startrow + 1) + v | ||||||
|             && lnum != wp->w_cursor.lnum) |             && lnum != wp->w_cursor.lnum) | ||||||
|            || draw_color_col) |            || draw_color_col || do_virttext) | ||||||
|           && !wp->w_p_rl |           && !wp->w_p_rl) { | ||||||
|           ) { |  | ||||||
|         int rightmost_vcol = 0; |         int rightmost_vcol = 0; | ||||||
|         int i; |         int i; | ||||||
|  |  | ||||||
|         if (wp->w_p_cuc) |         VirtText virt_text = do_virttext ? bufhl_info.line->virt_text | ||||||
|  |                                         : (VirtText)KV_INITIAL_VALUE; | ||||||
|  |         size_t virt_pos = 0; | ||||||
|  |         LineState s = LINE_STATE((char_u *)""); | ||||||
|  |         int virt_attr = 0; | ||||||
|  |  | ||||||
|  |         // Make sure alignment is the same regardless | ||||||
|  |         // if listchars=eol:X is used or not. | ||||||
|  |         bool delay_virttext = lcs_eol <= 0; | ||||||
|  |  | ||||||
|  |         if (wp->w_p_cuc) { | ||||||
|           rightmost_vcol = wp->w_virtcol; |           rightmost_vcol = wp->w_virtcol; | ||||||
|         if (draw_color_col) |         } | ||||||
|           /* determine rightmost colorcolumn to possibly draw */ |  | ||||||
|           for (i = 0; color_cols[i] >= 0; ++i) |         if (draw_color_col) { | ||||||
|             if (rightmost_vcol < color_cols[i]) |           // determine rightmost colorcolumn to possibly draw | ||||||
|  |           for (i = 0; color_cols[i] >= 0; i++) { | ||||||
|  |             if (rightmost_vcol < color_cols[i]) { | ||||||
|               rightmost_vcol = color_cols[i]; |               rightmost_vcol = color_cols[i]; | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |  | ||||||
|         int cuc_attr = win_hl_attr(wp, HLF_CUC); |         int cuc_attr = win_hl_attr(wp, HLF_CUC); | ||||||
|         int mc_attr = win_hl_attr(wp, HLF_MC); |         int mc_attr = win_hl_attr(wp, HLF_MC); | ||||||
|  |  | ||||||
|         while (col < wp->w_width) { |         while (col < wp->w_width) { | ||||||
|  |           int cells = -1; | ||||||
|  |           if (do_virttext && !delay_virttext) { | ||||||
|  |             if (*s.p == NUL) { | ||||||
|  |               if (virt_pos < virt_text.size) { | ||||||
|  |                 s.p = (char_u *)kv_A(virt_text, virt_pos).text; | ||||||
|  |                 int hl_id = kv_A(virt_text, virt_pos).hl_id; | ||||||
|  |                 virt_attr = hl_id > 0 ? syn_id2attr(hl_id) : 0; | ||||||
|  |                 virt_pos++; | ||||||
|  |               } else { | ||||||
|  |                do_virttext = false; | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  |             if (*s.p != NUL) { | ||||||
|  |               cells = line_putchar(&s, &ScreenLines[off], wp->w_width - col, | ||||||
|  |                                    false); | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |           delay_virttext = false; | ||||||
|  |  | ||||||
|  |           if (cells == -1) { | ||||||
|             schar_from_ascii(ScreenLines[off], ' '); |             schar_from_ascii(ScreenLines[off], ' '); | ||||||
|           col++; |             cells = 1; | ||||||
|  |           } | ||||||
|  |           col += cells; | ||||||
|           if (draw_color_col) { |           if (draw_color_col) { | ||||||
|             draw_color_col = advance_color_col(VCOL_HLC, &color_cols); |             draw_color_col = advance_color_col(VCOL_HLC, &color_cols); | ||||||
|           } |           } | ||||||
|  |  | ||||||
|  |           int attr = 0; | ||||||
|           if (wp->w_p_cuc && VCOL_HLC == (long)wp->w_virtcol) { |           if (wp->w_p_cuc && VCOL_HLC == (long)wp->w_virtcol) { | ||||||
|             ScreenAttrs[off++] = cuc_attr; |             attr = cuc_attr; | ||||||
|           } else if (draw_color_col && VCOL_HLC == *color_cols) { |           } else if (draw_color_col && VCOL_HLC == *color_cols) { | ||||||
|             ScreenAttrs[off++] = mc_attr; |             attr = mc_attr; | ||||||
|           } else { |  | ||||||
|             ScreenAttrs[off++] = wp->w_hl_attr_normal; |  | ||||||
|           } |           } | ||||||
|  |  | ||||||
|           if (VCOL_HLC >= rightmost_vcol) |           if (do_virttext) { | ||||||
|  |             attr = hl_combine_attr(attr, virt_attr); | ||||||
|  |           } | ||||||
|  |  | ||||||
|  |           ScreenAttrs[off] = attr; | ||||||
|  |           if (cells == 2) { | ||||||
|  |             ScreenAttrs[off+1] = attr; | ||||||
|  |           } | ||||||
|  |           off += cells; | ||||||
|  |  | ||||||
|  |           if (VCOL_HLC >= rightmost_vcol && *s.p == NUL | ||||||
|  |               && virt_pos >= virt_text.size) { | ||||||
|             break; |             break; | ||||||
|  |           } | ||||||
|  |  | ||||||
|           ++vcol; |           ++vcol; | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -23,7 +23,10 @@ describe('Buffer highlighting', function() | |||||||
|       [7] = {bold = true}, |       [7] = {bold = true}, | ||||||
|       [8] = {underline = true, bold = true, foreground = Screen.colors.SlateBlue}, |       [8] = {underline = true, bold = true, foreground = Screen.colors.SlateBlue}, | ||||||
|       [9] = {foreground = Screen.colors.SlateBlue, underline = true}, |       [9] = {foreground = Screen.colors.SlateBlue, underline = true}, | ||||||
|       [10] = {foreground = Screen.colors.Red} |       [10] = {foreground = Screen.colors.Red}, | ||||||
|  |       [11] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, | ||||||
|  |       [12] = {foreground = Screen.colors.Blue1}, | ||||||
|  |       [13] = {background = Screen.colors.LightGrey}, | ||||||
|     }) |     }) | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
| @@ -77,7 +80,7 @@ describe('Buffer highlighting', function() | |||||||
|                                               | |                                               | | ||||||
|     ]]) |     ]]) | ||||||
|  |  | ||||||
|     clear_hl(-1, 0 , -1) |     clear_hl(-1, 0, -1) | ||||||
|     screen:expect([[ |     screen:expect([[ | ||||||
|       these are some lines                    | |       these are some lines                    | | ||||||
|       ^                                        | |       ^                                        | | ||||||
| @@ -275,4 +278,140 @@ describe('Buffer highlighting', function() | |||||||
|                                               | |                                               | | ||||||
|     ]]) |     ]]) | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
|  |   it('supports virtual text annotations', function() | ||||||
|  |     local set_virtual_text = curbufmeths.set_virtual_text | ||||||
|  |     insert([[ | ||||||
|  |       1 + 2 | ||||||
|  |       3 + | ||||||
|  |       x = 4]]) | ||||||
|  |     feed('O<esc>20A5, <esc>gg') | ||||||
|  |     screen:expect([[ | ||||||
|  |       ^1 + 2                                   | | ||||||
|  |       3 +                                     | | ||||||
|  |       5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5| | ||||||
|  |       , 5, 5, 5, 5, 5, 5,                     | | ||||||
|  |       x = 4                                   | | ||||||
|  |       {1:~                                       }| | ||||||
|  |       {1:~                                       }| | ||||||
|  |                                               | | ||||||
|  |     ]]) | ||||||
|  |  | ||||||
|  |     local id1 = set_virtual_text(0, 0, {{"=", "Statement"}, {" 3", "Number"}}, {}) | ||||||
|  |     set_virtual_text(id1, 1, {{"ERROR:", "ErrorMsg"}, {" invalid syntax"}}, {}) | ||||||
|  |     local id2 = set_virtual_text(0, 2, {{"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."}}, {}) | ||||||
|  |     neq(id2, id1) | ||||||
|  |  | ||||||
|  |     screen:expect([[ | ||||||
|  |       ^1 + 2 {3:=}{2: 3}                               | | ||||||
|  |       3 + {11:ERROR:} invalid syntax               | | ||||||
|  |       5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5| | ||||||
|  |       , 5, 5, 5, 5, 5, 5,  Lorem ipsum dolor s| | ||||||
|  |       x = 4                                   | | ||||||
|  |       {1:~                                       }| | ||||||
|  |       {1:~                                       }| | ||||||
|  |                                               | | ||||||
|  |     ]]) | ||||||
|  |  | ||||||
|  |     clear_hl(id1, 0, -1) | ||||||
|  |     screen:expect([[ | ||||||
|  |       ^1 + 2                                   | | ||||||
|  |       3 +                                     | | ||||||
|  |       5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5| | ||||||
|  |       , 5, 5, 5, 5, 5, 5,  Lorem ipsum dolor s| | ||||||
|  |       x = 4                                   | | ||||||
|  |       {1:~                                       }| | ||||||
|  |       {1:~                                       }| | ||||||
|  |                                               | | ||||||
|  |     ]]) | ||||||
|  |  | ||||||
|  |     -- Handles doublewidth chars, leaving a space if truncating | ||||||
|  |     -- in the middle of a char | ||||||
|  |     set_virtual_text(id1, 1, {{"暗x事zz速野谷質結育副住新覚丸活解終事", "Comment"}}, {}) | ||||||
|  |     screen:expect([[ | ||||||
|  |       ^1 + 2                                   | | ||||||
|  |       3 + {12:暗x事zz速野谷質結育副住新覚丸活解終 }| | ||||||
|  |       5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5| | ||||||
|  |       , 5, 5, 5, 5, 5, 5,  Lorem ipsum dolor s| | ||||||
|  |       x = 4                                   | | ||||||
|  |       {1:~                                       }| | ||||||
|  |       {1:~                                       }| | ||||||
|  |                                               | | ||||||
|  |     ]]) | ||||||
|  |  | ||||||
|  |     feed("2Gx") | ||||||
|  |     screen:expect([[ | ||||||
|  |       1 + 2                                   | | ||||||
|  |       ^ + {12:暗x事zz速野谷質結育副住新覚丸活解終事}| | ||||||
|  |       5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5| | ||||||
|  |       , 5, 5, 5, 5, 5, 5,  Lorem ipsum dolor s| | ||||||
|  |       x = 4                                   | | ||||||
|  |       {1:~                                       }| | ||||||
|  |       {1:~                                       }| | ||||||
|  |                                               | | ||||||
|  |     ]]) | ||||||
|  |  | ||||||
|  |     -- visual selection doesn't highlight virtual text | ||||||
|  |     feed("ggVG") | ||||||
|  |     screen:expect([[ | ||||||
|  |       {13:1 + 2}                                   | | ||||||
|  |       {13: +} {12:暗x事zz速野谷質結育副住新覚丸活解終事}| | ||||||
|  |       {13:5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5}| | ||||||
|  |       {13:, 5, 5, 5, 5, 5, 5, } Lorem ipsum dolor s| | ||||||
|  |       ^x{13: = 4}                                   | | ||||||
|  |       {1:~                                       }| | ||||||
|  |       {1:~                                       }| | ||||||
|  |       {7:-- VISUAL LINE --}                       | | ||||||
|  |     ]]) | ||||||
|  |  | ||||||
|  |     feed("<esc>") | ||||||
|  |     screen:expect([[ | ||||||
|  |       1 + 2                                   | | ||||||
|  |        + {12:暗x事zz速野谷質結育副住新覚丸活解終事}| | ||||||
|  |       5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5| | ||||||
|  |       , 5, 5, 5, 5, 5, 5,  Lorem ipsum dolor s| | ||||||
|  |       ^x = 4                                   | | ||||||
|  |       {1:~                                       }| | ||||||
|  |       {1:~                                       }| | ||||||
|  |                                               | | ||||||
|  |     ]]) | ||||||
|  |  | ||||||
|  |     feed("2Gdd") | ||||||
|  |     screen:expect([[ | ||||||
|  |       1 + 2                                   | | ||||||
|  |       ^5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5| | ||||||
|  |       , 5, 5, 5, 5, 5, 5,  Lorem ipsum dolor s| | ||||||
|  |       x = 4                                   | | ||||||
|  |       {1:~                                       }| | ||||||
|  |       {1:~                                       }| | ||||||
|  |       {1:~                                       }| | ||||||
|  |                                               | | ||||||
|  |     ]]) | ||||||
|  |  | ||||||
|  |     -- listchars=eol:- works, and doesn't shift virtual text | ||||||
|  |     command("set list") | ||||||
|  |     screen:expect([[ | ||||||
|  |       1 + 2                                   | | ||||||
|  |       ^5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5| | ||||||
|  |       , 5, 5, 5, 5, 5, 5,{1:-} Lorem ipsum dolor s| | ||||||
|  |       x = 4                                   | | ||||||
|  |       {1:~                                       }| | ||||||
|  |       {1:~                                       }| | ||||||
|  |       {1:~                                       }| | ||||||
|  |                                               | | ||||||
|  |     ]]) | ||||||
|  |  | ||||||
|  |     clear_hl(-1, 0, -1) | ||||||
|  |     screen:expect([[ | ||||||
|  |       1 + 2                                   | | ||||||
|  |       ^5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5| | ||||||
|  |       , 5, 5, 5, 5, 5, 5,{1:-}                    | | ||||||
|  |       x = 4                                   | | ||||||
|  |       {1:~                                       }| | ||||||
|  |       {1:~                                       }| | ||||||
|  |       {1:~                                       }| | ||||||
|  |                                               | | ||||||
|  |     ]]) | ||||||
|  |  | ||||||
|  |   end) | ||||||
| end) | end) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Björn Linse
					Björn Linse