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); | ||||
| } | ||||
|  | ||||
|  | ||||
| /// 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. | ||||
| // Changed the lines from "lo" to "hi" and added "extra" lines (negative if | ||||
| // 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. | ||||
| /// | ||||
| /// @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, | ||||
|                                         linenr_T lnum) | ||||
| { | ||||
|   BufhlLineStatus changed = kBLSUnchanged; | ||||
|   size_t oldsize = kv_size(lineinfo->items); | ||||
|   if (src_id < 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; | ||||
|   } | ||||
|   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); | ||||
|     return kBLSDeleted; | ||||
|   } | ||||
|   return kv_size(lineinfo->items) != oldsize ? kBLSChanged : kBLSUnchanged; | ||||
|   return changed; | ||||
| } | ||||
|  | ||||
|  | ||||
| /// Remove all highlights and free the highlight data | ||||
| 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; | ||||
|   } | ||||
|   info->valid_to = -1; | ||||
|   info->entries = lineinfo->items; | ||||
|   return kv_size(info->entries) > 0; | ||||
|   info->line = lineinfo; | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| /// get highlighting at column col | ||||
| @@ -5548,8 +5599,8 @@ int bufhl_get_attr(BufhlLineInfo *info, colnr_T col) | ||||
|   } | ||||
|   int attr = 0; | ||||
|   info->valid_to = MAXCOL; | ||||
|   for (size_t i = 0; i < kv_size(info->entries); i++) { | ||||
|     BufhlItem entry = kv_A(info->entries, i); | ||||
|   for (size_t i = 0; i < kv_size(info->line->items); i++) { | ||||
|     BufhlItem entry = kv_A(info->line->items, i); | ||||
|     if (entry.start <= col && col <= entry.stop) { | ||||
|       int entry_attr = syn_id2attr(entry.hl_id); | ||||
|       attr = hl_combine_attr(attr, entry_attr); | ||||
|   | ||||
| @@ -14,16 +14,23 @@ typedef struct { | ||||
|   colnr_T stop;  // last column to highlight | ||||
| } BufhlItem; | ||||
|  | ||||
| typedef kvec_t(BufhlItem) BufhlItemVec; | ||||
| typedef struct { | ||||
|   char *text; | ||||
|   int hl_id; | ||||
| } VirtTextChunk; | ||||
|  | ||||
| typedef kvec_t(VirtTextChunk) VirtText; | ||||
|  | ||||
| typedef struct { | ||||
|   linenr_T line; | ||||
|   BufhlItemVec items; | ||||
|   kvec_t(BufhlItem) items; | ||||
|   int virt_text_src; | ||||
|   VirtText virt_text; | ||||
| } BufhlLine; | ||||
| #define BUFHLLINE_INIT(l) { l, KV_INITIAL_VALUE } | ||||
| #define BUFHLLINE_INIT(l) { l, KV_INITIAL_VALUE, 0,  KV_INITIAL_VALUE } | ||||
|  | ||||
| typedef struct { | ||||
|   BufhlItemVec entries; | ||||
|   BufhlLine *line; | ||||
|   int current; | ||||
|   colnr_T valid_to; | ||||
| } BufhlLineInfo; | ||||
|   | ||||
| @@ -132,6 +132,15 @@ static schar_T  *current_ScreenLine; | ||||
| StlClickDefinition *tab_page_click_defs = NULL; | ||||
| 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 | ||||
| # include "screen.c.generated.h" | ||||
| #endif | ||||
| @@ -1731,6 +1740,56 @@ static int compute_foldcolumn(win_T *wp, int col) | ||||
|   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. | ||||
|  */ | ||||
| @@ -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 | ||||
|    *    in columns number-col - window-width. | ||||
|    */ | ||||
|   int cells; | ||||
|   int u8c, u8cc[MAX_MCO]; | ||||
|   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) { | ||||
|     idx = off; | ||||
| @@ -1877,50 +1930,20 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T | ||||
|     idx = off + col; | ||||
|   } | ||||
|  | ||||
|   // Store multibyte characters in ScreenLines[] et al. correctly. | ||||
|   for (p = text; *p != NUL; ) { | ||||
|     cells = utf_ptr2cells(p); | ||||
|     c_len = utfc_ptr2len(p); | ||||
|     if (col + cells > wp->w_width - (wp->w_p_rl ? col : 0)) { | ||||
|   LineState s = LINE_STATE(text); | ||||
|  | ||||
|   while (*s.p != NUL) { | ||||
|     // TODO(bfredl): cargo-culted from the old Vim code: | ||||
|     // 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; | ||||
|     } | ||||
|     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; | ||||
|     idx += cells; | ||||
|     p += c_len; | ||||
|   } | ||||
|  | ||||
|   /* Fill the rest of the line with the fold filler */ | ||||
| @@ -2215,9 +2238,9 @@ win_line ( | ||||
|   int did_line_attr = 0; | ||||
|  | ||||
|   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 | ||||
|   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: */ | ||||
| #define WL_START        0               /* nothing done yet */ | ||||
| @@ -2279,8 +2302,13 @@ win_line ( | ||||
|     } | ||||
|  | ||||
|     if (bufhl_start_line(wp->w_buffer, lnum, &bufhl_info)) { | ||||
|       has_bufhl = true; | ||||
|       extra_check = true; | ||||
|       if (kv_size(bufhl_info.line->items)) { | ||||
|         has_bufhl = true; | ||||
|         extra_check = true; | ||||
|       } | ||||
|       if (kv_size(bufhl_info.line->virt_text)) { | ||||
|         do_virttext = true; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     // Check for columns to display for 'colorcolumn'. | ||||
| @@ -3429,7 +3457,7 @@ win_line ( | ||||
|         } | ||||
|  | ||||
|         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 (!attr_pri) { | ||||
|               char_attr = hl_combine_attr(char_attr, bufhl_attr); | ||||
| @@ -3949,40 +3977,87 @@ win_line ( | ||||
|             && (int)wp->w_virtcol < | ||||
|             wp->w_width * (row - startrow + 1) + v | ||||
|             && lnum != wp->w_cursor.lnum) | ||||
|            || draw_color_col) | ||||
|           && !wp->w_p_rl | ||||
|           ) { | ||||
|            || draw_color_col || do_virttext) | ||||
|           && !wp->w_p_rl) { | ||||
|         int rightmost_vcol = 0; | ||||
|         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; | ||||
|         if (draw_color_col) | ||||
|           /* determine rightmost colorcolumn to possibly draw */ | ||||
|           for (i = 0; color_cols[i] >= 0; ++i) | ||||
|             if (rightmost_vcol < color_cols[i]) | ||||
|         } | ||||
|  | ||||
|         if (draw_color_col) { | ||||
|           // 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]; | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         int cuc_attr = win_hl_attr(wp, HLF_CUC); | ||||
|         int mc_attr = win_hl_attr(wp, HLF_MC); | ||||
|  | ||||
|         while (col < wp->w_width) { | ||||
|           schar_from_ascii(ScreenLines[off], ' '); | ||||
|           col++; | ||||
|           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], ' '); | ||||
|             cells = 1; | ||||
|           } | ||||
|           col += cells; | ||||
|           if (draw_color_col) { | ||||
|             draw_color_col = advance_color_col(VCOL_HLC, &color_cols); | ||||
|           } | ||||
|  | ||||
|           int attr = 0; | ||||
|           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) { | ||||
|             ScreenAttrs[off++] = mc_attr; | ||||
|           } else { | ||||
|             ScreenAttrs[off++] = wp->w_hl_attr_normal; | ||||
|             attr = mc_attr; | ||||
|           } | ||||
|  | ||||
|           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; | ||||
|           } | ||||
|  | ||||
|           ++vcol; | ||||
|         } | ||||
|   | ||||
| @@ -23,7 +23,10 @@ describe('Buffer highlighting', function() | ||||
|       [7] = {bold = true}, | ||||
|       [8] = {underline = true, bold = true, foreground = Screen.colors.SlateBlue}, | ||||
|       [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) | ||||
|  | ||||
| @@ -77,7 +80,7 @@ describe('Buffer highlighting', function() | ||||
|                                               | | ||||
|     ]]) | ||||
|  | ||||
|     clear_hl(-1, 0 , -1) | ||||
|     clear_hl(-1, 0, -1) | ||||
|     screen:expect([[ | ||||
|       these are some lines                    | | ||||
|       ^                                        | | ||||
| @@ -275,4 +278,140 @@ describe('Buffer highlighting', function() | ||||
|                                               | | ||||
|     ]]) | ||||
|   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) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Björn Linse
					Björn Linse