Merge pull request #15184 from bfredl/sunjonhl
feat(decorations): allow more than one stacked highlight in a virt_text
This commit is contained in:
commit
b73e733d52
|
@ -1433,8 +1433,14 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id,
|
|||
/// - hl_group : name of the highlight group used to highlight
|
||||
/// this mark.
|
||||
/// - virt_text : virtual text to link to this mark.
|
||||
/// - virt_text_pos : positioning of virtual text. Possible
|
||||
/// values:
|
||||
/// A list of [text, highlight] tuples, each representing a
|
||||
/// text chunk with specified highlight. `highlight` element
|
||||
/// can either be a a single highlight group, or an array of
|
||||
/// multiple highlight groups that will be stacked
|
||||
/// (highest priority last). A highlight group can be supplied
|
||||
/// either as a string or as an integer, the latter which
|
||||
/// can be obtained using |nvim_get_hl_id_by_name|.
|
||||
/// - virt_text_pos : position of virtual text. Possible values:
|
||||
/// - "eol": right after eol character (default)
|
||||
/// - "overlay": display over the specified column, without
|
||||
/// shifting the underlying text.
|
||||
|
@ -1560,7 +1566,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
|
|||
"virt_text is not an Array");
|
||||
goto error;
|
||||
}
|
||||
decor.virt_text = parse_virt_text(v->data.array, err);
|
||||
decor.virt_text = parse_virt_text(v->data.array, err,
|
||||
&decor.virt_text_width);
|
||||
if (ERROR_SET(err)) {
|
||||
goto error;
|
||||
}
|
||||
|
@ -1892,80 +1899,6 @@ void nvim_buf_clear_namespace(Buffer buffer,
|
|||
(int)line_end-1, MAXCOL);
|
||||
}
|
||||
|
||||
/// 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 one cell (|lcs-eol| or space) after the ordinary text.
|
||||
///
|
||||
/// Namespaces are used to support batch deletion/updating of virtual text.
|
||||
/// To create a namespace, use |nvim_create_namespace()|. Virtual text is
|
||||
/// cleared using |nvim_buf_clear_namespace()|. The same `ns_id` can be used for
|
||||
/// both virtual text and highlights added by |nvim_buf_add_highlight()|, both
|
||||
/// can then be cleared with a single call to |nvim_buf_clear_namespace()|. If
|
||||
/// the virtual text never will be cleared by an API call, pass `ns_id = -1`.
|
||||
///
|
||||
/// As a shorthand, `ns_id = 0` can be used to create a new namespace for the
|
||||
/// virtual text, the allocated id is then returned.
|
||||
///
|
||||
/// @param buffer Buffer handle, or 0 for current buffer
|
||||
/// @param ns_id Namespace to use or 0 to create a namespace,
|
||||
/// 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 ns_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;
|
||||
}
|
||||
|
||||
uint64_t ns_id = src2ns(&src_id);
|
||||
|
||||
VirtText virt_text = parse_virt_text(chunks, err);
|
||||
if (ERROR_SET(err)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
VirtText *existing = decor_find_virttext(buf, (int)line, ns_id);
|
||||
|
||||
if (existing) {
|
||||
clear_virttext(existing);
|
||||
*existing = virt_text;
|
||||
return src_id;
|
||||
}
|
||||
|
||||
Decoration *decor = xcalloc(1, sizeof(*decor));
|
||||
decor->virt_text = virt_text;
|
||||
|
||||
extmark_set(buf, ns_id, 0, (int)line, 0, -1, -1, decor, true,
|
||||
false, kExtmarkNoUndo);
|
||||
return src_id;
|
||||
}
|
||||
|
||||
/// call a function with buffer as temporary current buffer
|
||||
///
|
||||
/// This temporarily switches current buffer to "buffer".
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "nvim/api/private/helpers.h"
|
||||
#include "nvim/api/private/defs.h"
|
||||
#include "nvim/lua/executor.h"
|
||||
#include "nvim/extmark.h"
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "api/deprecated.c.generated.h"
|
||||
|
@ -80,6 +81,87 @@ void nvim_buf_clear_highlight(Buffer buffer,
|
|||
}
|
||||
|
||||
|
||||
/// Set the virtual text (annotation) for a buffer line.
|
||||
///
|
||||
/// @deprecated use nvim_buf_set_extmark to use full virtual text
|
||||
/// functionality.
|
||||
///
|
||||
/// 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 one cell (|lcs-eol| or space) after the
|
||||
/// ordinary text.
|
||||
///
|
||||
/// Namespaces are used to support batch deletion/updating of virtual text.
|
||||
/// To create a namespace, use |nvim_create_namespace()|. Virtual text is
|
||||
/// cleared using |nvim_buf_clear_namespace()|. The same `ns_id` can be used for
|
||||
/// both virtual text and highlights added by |nvim_buf_add_highlight()|, both
|
||||
/// can then be cleared with a single call to |nvim_buf_clear_namespace()|. If
|
||||
/// the virtual text never will be cleared by an API call, pass `ns_id = -1`.
|
||||
///
|
||||
/// As a shorthand, `ns_id = 0` can be used to create a new namespace for the
|
||||
/// virtual text, the allocated id is then returned.
|
||||
///
|
||||
/// @param buffer Buffer handle, or 0 for current buffer
|
||||
/// @param ns_id Namespace to use or 0 to create a namespace,
|
||||
/// 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 ns_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)
|
||||
FUNC_API_DEPRECATED_SINCE(8)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
uint64_t ns_id = src2ns(&src_id);
|
||||
int width;
|
||||
|
||||
VirtText virt_text = parse_virt_text(chunks, err, &width);
|
||||
if (ERROR_SET(err)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Decoration *existing = decor_find_virttext(buf, (int)line, ns_id);
|
||||
|
||||
if (existing) {
|
||||
clear_virttext(&existing->virt_text);
|
||||
existing->virt_text = virt_text;
|
||||
existing->virt_text_width = width;
|
||||
return src_id;
|
||||
}
|
||||
|
||||
Decoration *decor = xcalloc(1, sizeof(*decor));
|
||||
decor->virt_text = virt_text;
|
||||
decor->virt_text_width = width;
|
||||
|
||||
extmark_set(buf, ns_id, 0, (int)line, 0, -1, -1, decor, true,
|
||||
false, kExtmarkNoUndo);
|
||||
return src_id;
|
||||
}
|
||||
|
||||
/// Inserts a sequence of lines to a buffer at a certain index
|
||||
///
|
||||
/// @deprecated use nvim_buf_set_lines(buffer, lnum, lnum, true, lines)
|
||||
|
|
|
@ -1592,9 +1592,10 @@ bool extmark_get_index_from_obj(buf_T *buf, Integer ns_id, Object obj, int
|
|||
}
|
||||
}
|
||||
|
||||
VirtText parse_virt_text(Array chunks, Error *err)
|
||||
VirtText parse_virt_text(Array chunks, Error *err, int *width)
|
||||
{
|
||||
VirtText virt_text = KV_INITIAL_VALUE;
|
||||
int w = 0;
|
||||
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");
|
||||
|
@ -1603,25 +1604,44 @@ VirtText parse_virt_text(Array chunks, Error *err)
|
|||
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)) {
|
||||
|| chunk.size > 2) {
|
||||
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 = transstr(str.size > 0 ? str.data : ""); // allocates
|
||||
|
||||
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);
|
||||
Object hl = chunk.items[1];
|
||||
if (hl.type == kObjectTypeArray) {
|
||||
Array arr = hl.data.array;
|
||||
for (size_t j = 0; j < arr.size; j++) {
|
||||
hl_id = object_to_hl_id(arr.items[j], "virt_text highlight", err);
|
||||
if (ERROR_SET(err)) {
|
||||
goto free_exit;
|
||||
}
|
||||
if (j < arr.size-1) {
|
||||
kv_push(virt_text, ((VirtTextChunk){ .text = NULL,
|
||||
.hl_id = hl_id }));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
hl_id = object_to_hl_id(hl, "virt_text highlight", err);
|
||||
if (ERROR_SET(err)) {
|
||||
goto free_exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char *text = transstr(str.size > 0 ? str.data : ""); // allocates
|
||||
w += (int)mb_string2cells((char_u *)text);
|
||||
|
||||
kv_push(virt_text, ((VirtTextChunk){ .text = text, .hl_id = hl_id }));
|
||||
}
|
||||
|
||||
*width = w;
|
||||
return virt_text;
|
||||
|
||||
free_exit:
|
||||
|
@ -1656,7 +1676,7 @@ int object_to_hl_id(Object obj, const char *what, Error *err)
|
|||
String str = obj.data.string;
|
||||
return str.size ? syn_check_group((char_u *)str.data, (int)str.size) : 0;
|
||||
} else if (obj.type == kObjectTypeInteger) {
|
||||
return (int)obj.data.integer;
|
||||
return MAX((int)obj.data.integer, 0);
|
||||
} else {
|
||||
api_set_error(err, kErrorTypeValidation,
|
||||
"%s is not a valid highlight", what);
|
||||
|
@ -1687,6 +1707,7 @@ HlMessage parse_hl_msg(Array chunks, Error *err)
|
|||
if (chunk.size == 2) {
|
||||
String hl = chunk.items[1].data.string;
|
||||
if (hl.size > 0) {
|
||||
// TODO(bfredl): use object_to_hl_id and allow integer
|
||||
int hl_id = syn_check_group((char_u *)hl.data, (int)hl.size);
|
||||
attr = hl_id > 0 ? syn_id2attr(hl_id) : 0;
|
||||
}
|
||||
|
|
|
@ -119,7 +119,7 @@ void clear_virttext(VirtText *text)
|
|||
*text = (VirtText)KV_INITIAL_VALUE;
|
||||
}
|
||||
|
||||
VirtText *decor_find_virttext(buf_T *buf, int row, uint64_t ns_id)
|
||||
Decoration *decor_find_virttext(buf_T *buf, int row, uint64_t ns_id)
|
||||
{
|
||||
MarkTreeIter itr[1] = { 0 };
|
||||
marktree_itr_get(buf->b_marktree, row, 0, itr);
|
||||
|
@ -132,7 +132,7 @@ VirtText *decor_find_virttext(buf_T *buf, int row, uint64_t ns_id)
|
|||
mark.id, false);
|
||||
if (item && (ns_id == 0 || ns_id == item->ns_id)
|
||||
&& item->decor && kv_size(item->decor->virt_text)) {
|
||||
return &item->decor->virt_text;
|
||||
return item->decor;
|
||||
}
|
||||
marktree_itr_next(buf->b_marktree, itr);
|
||||
}
|
||||
|
@ -218,6 +218,7 @@ bool decor_redraw_line(buf_T *buf, int row, DecorState *state)
|
|||
}
|
||||
state->row = row;
|
||||
state->col_until = -1;
|
||||
state->eol_col = -1;
|
||||
return true; // TODO(bfredl): be more precise
|
||||
}
|
||||
|
||||
|
@ -230,10 +231,6 @@ static void decor_add(DecorState *state, int start_row, int start_col,
|
|||
*decor, attr_id,
|
||||
kv_size(decor->virt_text) && owned, -1 };
|
||||
|
||||
if (decor->virt_text_pos == kVTEndOfLine) {
|
||||
range.win_col = -2; // handled separately
|
||||
}
|
||||
|
||||
kv_pushp(state->active);
|
||||
size_t index;
|
||||
for (index = kv_size(state->active)-1; index > 0; index--) {
|
||||
|
@ -345,29 +342,22 @@ void decor_redraw_end(DecorState *state)
|
|||
state->buf = NULL;
|
||||
}
|
||||
|
||||
VirtText decor_redraw_eol(buf_T *buf, DecorState *state, int *eol_attr,
|
||||
bool *aligned)
|
||||
bool decor_redraw_eol(buf_T *buf, DecorState *state, int *eol_attr, int eol_col)
|
||||
{
|
||||
decor_redraw_col(buf, MAXCOL, MAXCOL, false, state);
|
||||
VirtText text = VIRTTEXT_EMPTY;
|
||||
state->eol_col = eol_col;
|
||||
bool has_virttext = false;
|
||||
for (size_t i = 0; i < kv_size(state->active); i++) {
|
||||
DecorRange item = kv_A(state->active, i);
|
||||
if (item.start_row == state->row && kv_size(item.decor.virt_text)) {
|
||||
if (!kv_size(text) && item.decor.virt_text_pos == kVTEndOfLine) {
|
||||
text = item.decor.virt_text;
|
||||
} else if (item.decor.virt_text_pos == kVTRightAlign
|
||||
|| item.decor.virt_text_pos == kVTWinCol) {
|
||||
*aligned = true;
|
||||
}
|
||||
has_virttext = true;
|
||||
}
|
||||
|
||||
|
||||
if (item.decor.hl_eol && item.start_row <= state->row) {
|
||||
*eol_attr = hl_combine_attr(*eol_attr, item.attr_id);
|
||||
}
|
||||
}
|
||||
|
||||
return text;
|
||||
return has_virttext;
|
||||
}
|
||||
|
||||
void decor_add_ephemeral(int start_row, int start_col, int end_row, int end_col,
|
||||
|
|
|
@ -34,19 +34,20 @@ typedef enum {
|
|||
|
||||
struct Decoration
|
||||
{
|
||||
int hl_id; // highlight group
|
||||
VirtText virt_text;
|
||||
int hl_id; // highlight group
|
||||
VirtTextPos virt_text_pos;
|
||||
bool virt_text_hide;
|
||||
HlMode hl_mode;
|
||||
bool virt_text_hide;
|
||||
bool hl_eol;
|
||||
bool shared; // shared decoration, don't free
|
||||
// TODO(bfredl): style, signs, etc
|
||||
DecorPriority priority;
|
||||
bool shared; // shared decoration, don't free
|
||||
int col; // fixed col value, like win_col
|
||||
int virt_text_width; // width of virt_text
|
||||
};
|
||||
#define DECORATION_INIT { 0, KV_INITIAL_VALUE, kVTEndOfLine, false, \
|
||||
kHlModeUnknown, false, DECOR_PRIORITY_BASE, false, 0 }
|
||||
#define DECORATION_INIT { KV_INITIAL_VALUE, 0, kVTEndOfLine, kHlModeUnknown, \
|
||||
false, false, false, DECOR_PRIORITY_BASE, 0, 0 }
|
||||
|
||||
typedef struct {
|
||||
int start_row;
|
||||
|
@ -67,6 +68,8 @@ typedef struct {
|
|||
int row;
|
||||
int col_until;
|
||||
int current;
|
||||
|
||||
int eol_col;
|
||||
VirtText *virt_text;
|
||||
} DecorState;
|
||||
|
||||
|
|
|
@ -165,7 +165,7 @@ static bool resizing = false;
|
|||
#endif
|
||||
#define SEARCH_HL_PRIORITY 0
|
||||
|
||||
static char * provider_first_error = NULL;
|
||||
static char * provider_err = NULL;
|
||||
|
||||
static bool provider_invoke(NS ns_id, const char *name, LuaRef ref,
|
||||
Array args, bool default_true)
|
||||
|
@ -187,10 +187,10 @@ static bool provider_invoke(NS ns_id, const char *name, LuaRef ref,
|
|||
const char *ns_name = describe_ns(ns_id);
|
||||
ELOG("error in provider %s:%s: %s", ns_name, name, err.msg);
|
||||
bool verbose_errs = true; // TODO(bfredl):
|
||||
if (verbose_errs && provider_first_error == NULL) {
|
||||
if (verbose_errs && provider_err == NULL) {
|
||||
static char errbuf[IOSIZE];
|
||||
snprintf(errbuf, sizeof errbuf, "%s: %s", ns_name, err.msg);
|
||||
provider_first_error = xstrdup(errbuf);
|
||||
provider_err = xstrdup(errbuf);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2103,7 +2103,6 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
|
|||
|
||||
bool search_attr_from_match = false; // if search_attr is from :match
|
||||
bool has_decor = false; // this buffer has decoration
|
||||
bool do_virttext = false; // draw virtual text for this line
|
||||
int win_col_offset = 0; // offset for window columns
|
||||
|
||||
char_u buf_fold[FOLD_TEXT_LEN + 1]; // Hold value returned by get_foldtext
|
||||
|
@ -2148,8 +2147,6 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
|
|||
|
||||
row = startrow;
|
||||
|
||||
char *err_text = NULL;
|
||||
|
||||
buf_T *buf = wp->w_buffer;
|
||||
|
||||
if (!number_only) {
|
||||
|
@ -2194,14 +2191,20 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
|
|||
}
|
||||
}
|
||||
|
||||
if (has_decor) {
|
||||
extra_check = true;
|
||||
if (provider_err) {
|
||||
Decoration err_decor = DECORATION_INIT;
|
||||
int hl_err = syn_check_group((char_u *)S_LEN("ErrorMsg"));
|
||||
kv_push(err_decor.virt_text,
|
||||
((VirtTextChunk){ .text = provider_err,
|
||||
.hl_id = hl_err }));
|
||||
err_decor.virt_text_width = mb_string2cells((char_u *)provider_err);
|
||||
decor_add_ephemeral(lnum-1, 0, lnum-1, 0, &err_decor);
|
||||
provider_err = NULL;
|
||||
has_decor = true;
|
||||
}
|
||||
|
||||
if (provider_first_error) {
|
||||
err_text = provider_first_error;
|
||||
provider_first_error = NULL;
|
||||
do_virttext = true;
|
||||
if (has_decor) {
|
||||
extra_check = true;
|
||||
}
|
||||
|
||||
// Check for columns to display for 'colorcolumn'.
|
||||
|
@ -3953,19 +3956,15 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
|
|||
if (draw_color_col)
|
||||
draw_color_col = advance_color_col(VCOL_HLC, &color_cols);
|
||||
|
||||
VirtText virt_text = KV_INITIAL_VALUE;
|
||||
bool has_aligned = false;
|
||||
if (err_text) {
|
||||
int hl_err = syn_check_group((char_u *)S_LEN("ErrorMsg"));
|
||||
kv_push(virt_text, ((VirtTextChunk){ .text = err_text,
|
||||
.hl_id = hl_err }));
|
||||
do_virttext = true;
|
||||
} else if (has_decor) {
|
||||
virt_text = decor_redraw_eol(wp->w_buffer, &decor_state, &line_attr,
|
||||
&has_aligned);
|
||||
if (kv_size(virt_text)) {
|
||||
do_virttext = true;
|
||||
}
|
||||
bool has_virttext = false;
|
||||
// Make sure alignment is the same regardless
|
||||
// if listchars=eol:X is used or not.
|
||||
int eol_skip = (wp->w_p_lcs_chars.eol == lcs_eol_one && eol_hl_off == 0
|
||||
? 1 : 0);
|
||||
|
||||
if (has_decor) {
|
||||
has_virttext = decor_redraw_eol(wp->w_buffer, &decor_state, &line_attr,
|
||||
col + eol_skip);
|
||||
}
|
||||
|
||||
if (((wp->w_p_cuc
|
||||
|
@ -3974,20 +3973,10 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
|
|||
grid->Columns * (row - startrow + 1) + v
|
||||
&& lnum != wp->w_cursor.lnum)
|
||||
|| draw_color_col || line_attr_lowprio || line_attr
|
||||
|| diff_hlf != (hlf_T)0 || do_virttext
|
||||
|| has_aligned)) {
|
||||
|| diff_hlf != (hlf_T)0 || has_virttext)) {
|
||||
int rightmost_vcol = 0;
|
||||
int i;
|
||||
|
||||
size_t virt_pos = 0;
|
||||
LineState s = LINE_STATE("");
|
||||
int virt_attr = 0;
|
||||
|
||||
// Make sure alignment is the same regardless
|
||||
// if listchars=eol:X is used or not.
|
||||
bool delay_virttext = wp->w_p_lcs_chars.eol == lcs_eol_one
|
||||
&& eol_hl_off == 0;
|
||||
|
||||
if (wp->w_p_cuc) {
|
||||
rightmost_vcol = wp->w_virtcol;
|
||||
}
|
||||
|
@ -4013,37 +4002,15 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
|
|||
}
|
||||
|
||||
int base_attr = hl_combine_attr(line_attr_lowprio, diff_attr);
|
||||
if (base_attr || line_attr || has_aligned) {
|
||||
if (base_attr || line_attr || has_virttext) {
|
||||
rightmost_vcol = INT_MAX;
|
||||
}
|
||||
|
||||
int col_stride = wp->w_p_rl ? -1 : 1;
|
||||
|
||||
while (wp->w_p_rl ? col >= 0 : col < grid->Columns) {
|
||||
int cells = -1;
|
||||
if (do_virttext && !delay_virttext) {
|
||||
if (*s.p == NUL) {
|
||||
if (virt_pos < virt_text.size) {
|
||||
s.p = 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, &linebuf_char[off], grid->Columns - col,
|
||||
false);
|
||||
}
|
||||
}
|
||||
delay_virttext = false;
|
||||
|
||||
if (cells == -1) {
|
||||
schar_from_ascii(linebuf_char[off], ' ');
|
||||
cells = 1;
|
||||
}
|
||||
col += cells * col_stride;
|
||||
schar_from_ascii(linebuf_char[off], ' ');
|
||||
col += col_stride;
|
||||
if (draw_color_col) {
|
||||
draw_color_col = advance_color_col(VCOL_HLC, &color_cols);
|
||||
}
|
||||
|
@ -4056,24 +4023,16 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
|
|||
col_attr = mc_attr;
|
||||
}
|
||||
|
||||
if (do_virttext) {
|
||||
col_attr = hl_combine_attr(col_attr, virt_attr);
|
||||
}
|
||||
|
||||
col_attr = hl_combine_attr(col_attr, line_attr);
|
||||
|
||||
linebuf_attr[off] = col_attr;
|
||||
if (cells == 2) {
|
||||
linebuf_attr[off+1] = col_attr;
|
||||
}
|
||||
off += cells * col_stride;
|
||||
off += col_stride;
|
||||
|
||||
if (VCOL_HLC >= rightmost_vcol && *s.p == NUL
|
||||
&& virt_pos >= virt_text.size) {
|
||||
if (VCOL_HLC >= rightmost_vcol) {
|
||||
break;
|
||||
}
|
||||
|
||||
vcol += cells;
|
||||
vcol += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4392,7 +4351,6 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
|
|||
}
|
||||
|
||||
xfree(p_extra_free);
|
||||
xfree(err_text);
|
||||
return row;
|
||||
}
|
||||
|
||||
|
@ -4400,13 +4358,17 @@ void draw_virt_text(buf_T *buf, int col_off, int *end_col, int max_col)
|
|||
{
|
||||
DecorState *state = &decor_state;
|
||||
int right_pos = max_col;
|
||||
bool do_eol = state->eol_col > -1;
|
||||
for (size_t i = 0; i < kv_size(state->active); i++) {
|
||||
DecorRange *item = &kv_A(state->active, i);
|
||||
if (item->start_row == state->row && kv_size(item->decor.virt_text)) {
|
||||
if (item->win_col == -1) {
|
||||
if (item->decor.virt_text_pos == kVTRightAlign) {
|
||||
right_pos -= item->decor.col;
|
||||
right_pos -= item->decor.virt_text_width;
|
||||
item->win_col = right_pos;
|
||||
} else if (item->decor.virt_text_pos == kVTEndOfLine && do_eol) {
|
||||
item->win_col = state->eol_col;
|
||||
state->eol_col += item->decor.virt_text_width;
|
||||
} else if (item->decor.virt_text_pos == kVTWinCol) {
|
||||
item->win_col = MAX(item->decor.col+col_off, 0);
|
||||
}
|
||||
|
@ -4424,14 +4386,20 @@ void draw_virt_text(buf_T *buf, int col_off, int *end_col, int max_col)
|
|||
|
||||
while (col < max_col) {
|
||||
if (!*s.p) {
|
||||
if (virt_pos == kv_size(vt)) {
|
||||
if (virt_pos >= kv_size(vt)) {
|
||||
break;
|
||||
}
|
||||
virt_attr = 0;
|
||||
do {
|
||||
s.p = kv_A(vt, virt_pos).text;
|
||||
int hl_id = kv_A(vt, virt_pos).hl_id;
|
||||
virt_attr = hl_combine_attr(virt_attr,
|
||||
hl_id > 0 ? syn_id2attr(hl_id) : 0);
|
||||
virt_pos++;
|
||||
} while (!s.p && virt_pos < kv_size(vt));
|
||||
if (!s.p) {
|
||||
break;
|
||||
}
|
||||
s.p = kv_A(vt, virt_pos).text;
|
||||
int hl_id = kv_A(vt, virt_pos).hl_id;
|
||||
virt_attr = hl_id > 0 ? syn_id2attr(hl_id) : 0;
|
||||
virt_pos++;
|
||||
continue;
|
||||
}
|
||||
int attr;
|
||||
bool through = false;
|
||||
|
|
|
@ -858,8 +858,8 @@ describe('Buffer highlighting', function()
|
|||
it('works with cursorline', function()
|
||||
command("set cursorline")
|
||||
|
||||
screen:expect([[
|
||||
{14:^1 + 2 }{15:=}{16: 3}{14: }|
|
||||
screen:expect{grid=[[
|
||||
{14:^1 + 2 }{3:=}{2: 3}{14: }|
|
||||
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|
|
||||
|
@ -867,32 +867,32 @@ describe('Buffer highlighting', function()
|
|||
{1:~ }|
|
||||
{1:~ }|
|
||||
|
|
||||
]])
|
||||
]]}
|
||||
|
||||
feed('j')
|
||||
screen:expect([[
|
||||
screen:expect{grid=[[
|
||||
1 + 2 {3:=}{2: 3} |
|
||||
{14:^3 + }{11:ERROR:}{14: invalid syntax }|
|
||||
{14:^3 + }{11:ERROR:} invalid syntax{14: }|
|
||||
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('j')
|
||||
screen:expect([[
|
||||
screen:expect{grid=[[
|
||||
1 + 2 {3:=}{2: 3} |
|
||||
3 + {11:ERROR:} invalid syntax |
|
||||
{14:^5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5}|
|
||||
{14:, 5, 5, 5, 5, 5, 5, Lorem ipsum dolor s}|
|
||||
{14:, 5, 5, 5, 5, 5, 5, }Lorem ipsum dolor s|
|
||||
x = 4 |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
|
|
||||
]])
|
||||
]]}
|
||||
end)
|
||||
|
||||
it('works with color column', function()
|
||||
|
@ -910,11 +910,11 @@ describe('Buffer highlighting', function()
|
|||
|
||||
command("set colorcolumn=9")
|
||||
screen:expect{grid=[[
|
||||
^1 + 2 {3:=}{2: }{17:3} |
|
||||
^1 + 2 {3:=}{2: 3} |
|
||||
3 + {11:ERROR:} invalid syntax |
|
||||
5, 5, 5,{18: }5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5|
|
||||
, 5, 5, 5, 5, 5, 5, Lorem ipsum dolor s|
|
||||
x = 4 {12:暗}{19:x}{12:事} |
|
||||
x = 4 {12:暗x事} |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
|
|
||||
|
|
|
@ -562,7 +562,7 @@ end]]
|
|||
{5:l}{8:blen}{7:dy}{10:e}{7:text}{10:h}{7:-}{10:_}{7:here}ell, count = unpack(item) |
|
||||
{5:i}{12:c}{11:ombining color} {13:nil} {5:then} |
|
||||
{11:replacing color}d_cell |
|
||||
{5:e}{8:bl}{14:endy}{15:i}{14:text}{15:o}{14:-}{15:o}{14:h}{7:ere} |
|
||||
{5:e}{8:bl}{7:endy}{10: }{7:text}{10: }{7:-}{10: }{7:here} |
|
||||
{5:f}{12:co}{11:mbini}{16:n}{11:g color}t {5:or} {13:1}) {5:do} |
|
||||
{11:replacing color} line[colpos] |
|
||||
cell.text = text |
|
||||
|
@ -697,6 +697,52 @@ end]]
|
|||
|
|
||||
]]}
|
||||
end)
|
||||
|
||||
it('can have virtual text which combines foreground and backround groups', function()
|
||||
screen:set_default_attr_ids {
|
||||
[1] = {bold=true, foreground=Screen.colors.Blue};
|
||||
[2] = {background = tonumber('0x123456'), foreground = tonumber('0xbbbbbb')};
|
||||
[3] = {background = tonumber('0x123456'), foreground = tonumber('0xcccccc')};
|
||||
[4] = {background = tonumber('0x234567'), foreground = tonumber('0xbbbbbb')};
|
||||
[5] = {background = tonumber('0x234567'), foreground = tonumber('0xcccccc')};
|
||||
[6] = {bold = true, foreground = tonumber('0xcccccc'), background = tonumber('0x234567')};
|
||||
}
|
||||
|
||||
exec [[
|
||||
hi BgOne guibg=#123456
|
||||
hi BgTwo guibg=#234567
|
||||
hi FgEin guifg=#bbbbbb
|
||||
hi FgZwei guifg=#cccccc
|
||||
hi VeryBold gui=bold
|
||||
]]
|
||||
|
||||
meths.buf_set_extmark(0, ns, 0, 0, { virt_text={
|
||||
{'a', {'BgOne', 'FgEin'}};
|
||||
{'b', {'BgOne', 'FgZwei'}};
|
||||
{'c', {'BgTwo', 'FgEin'}};
|
||||
{'d', {'BgTwo', 'FgZwei'}};
|
||||
{'X', {'BgTwo', 'FgZwei', 'VeryBold'}};
|
||||
}})
|
||||
|
||||
screen:expect{grid=[[
|
||||
^ {2:a}{3:b}{4:c}{5:d}{6:X} |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
|
|
||||
]]}
|
||||
end)
|
||||
|
||||
it('does not crash when deleting a cleared buffer #15212', function()
|
||||
exec_lua [[
|
||||
ns = vim.api.nvim_create_namespace("myplugin")
|
||||
|
|
Loading…
Reference in New Issue