mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
feat(decorations): support signs
Add the following options to extmarks: - sign_text - sign_hl_group - number_hl_group - line_hl_group - cursorline_hl_group Note: ranges are unsupported and decorations are only applied to start_row
This commit is contained in:
parent
83fc914337
commit
30e4cc3b3f
@ -445,6 +445,27 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
|
|||||||
/// - strict: boolean that indicates extmark should not be placed
|
/// - strict: boolean that indicates extmark should not be placed
|
||||||
/// if the line or column value is past the end of the
|
/// if the line or column value is past the end of the
|
||||||
/// buffer or end of the line respectively. Defaults to true.
|
/// buffer or end of the line respectively. Defaults to true.
|
||||||
|
/// - sign_text: string of length 1-2 used to display in the
|
||||||
|
/// sign column.
|
||||||
|
/// Note: ranges are unsupported and decorations are only
|
||||||
|
/// applied to start_row
|
||||||
|
/// - sign_hl_group: name of the highlight group used to
|
||||||
|
/// highlight the sign column text.
|
||||||
|
/// Note: ranges are unsupported and decorations are only
|
||||||
|
/// applied to start_row
|
||||||
|
/// - number_hl_group: name of the highlight group used to
|
||||||
|
/// highlight the number column.
|
||||||
|
/// Note: ranges are unsupported and decorations are only
|
||||||
|
/// applied to start_row
|
||||||
|
/// - line_hl_group: name of the highlight group used to
|
||||||
|
/// highlight the whole line.
|
||||||
|
/// Note: ranges are unsupported and decorations are only
|
||||||
|
/// applied to start_row
|
||||||
|
/// - cursorline_hl_group: name of the highlight group used to
|
||||||
|
/// highlight the line when the cursor is on the same line
|
||||||
|
/// as the mark and 'cursorline' is enabled.
|
||||||
|
/// Note: ranges are unsupported and decorations are only
|
||||||
|
/// applied to start_row
|
||||||
///
|
///
|
||||||
/// @param[out] err Error details, if any
|
/// @param[out] err Error details, if any
|
||||||
/// @return Id of the created/updated extmark
|
/// @return Id of the created/updated extmark
|
||||||
@ -519,12 +540,27 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HAS_KEY(opts->hl_group)) {
|
struct {
|
||||||
decor.hl_id = object_to_hl_id(opts->hl_group, "hl_group", err);
|
const char *name;
|
||||||
|
Object *opt;
|
||||||
|
int *dest;
|
||||||
|
} hls[] = {
|
||||||
|
{ "hl_group" , &opts->hl_group , &decor.hl_id },
|
||||||
|
{ "sign_hl_group" , &opts->sign_hl_group , &decor.sign_hl_id },
|
||||||
|
{ "number_hl_group" , &opts->number_hl_group , &decor.number_hl_id },
|
||||||
|
{ "line_hl_group" , &opts->line_hl_group , &decor.line_hl_id },
|
||||||
|
{ "cursorline_hl_group", &opts->cursorline_hl_group, &decor.cursorline_hl_id },
|
||||||
|
{ NULL, NULL, NULL },
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int j = 0; hls[j].name && hls[j].dest; j++) {
|
||||||
|
if (HAS_KEY(*hls[j].opt)) {
|
||||||
|
*hls[j].dest = object_to_hl_id(*hls[j].opt, hls[j].name, err);
|
||||||
if (ERROR_SET(err)) {
|
if (ERROR_SET(err)) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (opts->virt_text.type == kObjectTypeArray) {
|
if (opts->virt_text.type == kObjectTypeArray) {
|
||||||
decor.virt_text = parse_virt_text(opts->virt_text.data.array, err,
|
decor.virt_text = parse_virt_text(opts->virt_text.data.array, err,
|
||||||
@ -622,6 +658,17 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (opts->sign_text.type == kObjectTypeString) {
|
||||||
|
if (!init_sign_text(&decor.sign_text,
|
||||||
|
(char_u *)opts->sign_text.data.string.data)) {
|
||||||
|
api_set_error(err, kErrorTypeValidation, "sign_text is not a valid value");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
} else if (HAS_KEY(opts->sign_text)) {
|
||||||
|
api_set_error(err, kErrorTypeValidation, "sign_text is not a String");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
bool right_gravity = true;
|
bool right_gravity = true;
|
||||||
OPTION_TO_BOOL(right_gravity, right_gravity, true);
|
OPTION_TO_BOOL(right_gravity, right_gravity, true);
|
||||||
|
|
||||||
@ -709,6 +756,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
|||||||
|
|
||||||
error:
|
error:
|
||||||
clear_virttext(&decor.virt_text);
|
clear_virttext(&decor.virt_text);
|
||||||
|
xfree(decor.sign_text);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +22,11 @@ return {
|
|||||||
"virt_lines_above";
|
"virt_lines_above";
|
||||||
"virt_lines_leftcol";
|
"virt_lines_leftcol";
|
||||||
"strict";
|
"strict";
|
||||||
|
"sign_text";
|
||||||
|
"sign_hl_group";
|
||||||
|
"number_hl_group";
|
||||||
|
"line_hl_group";
|
||||||
|
"cursorline_hl_group";
|
||||||
};
|
};
|
||||||
keymap = {
|
keymap = {
|
||||||
"noremap";
|
"noremap";
|
||||||
|
@ -1643,3 +1643,39 @@ sctx_T api_set_sctx(uint64_t channel_id)
|
|||||||
}
|
}
|
||||||
return old_current_sctx;
|
return old_current_sctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// adapted from sign.c:sign_define_init_text.
|
||||||
|
// TODO(lewis6991): Consider merging
|
||||||
|
int init_sign_text(char_u **sign_text, char_u *text)
|
||||||
|
{
|
||||||
|
char_u *s;
|
||||||
|
|
||||||
|
char_u *endp = text + (int)STRLEN(text);
|
||||||
|
|
||||||
|
// Count cells and check for non-printable chars
|
||||||
|
int cells = 0;
|
||||||
|
for (s = text; s < endp; s += utfc_ptr2len(s)) {
|
||||||
|
if (!vim_isprintc(utf_ptr2char(s))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cells += utf_ptr2cells(s);
|
||||||
|
}
|
||||||
|
// Currently must be empty, one or two display cells
|
||||||
|
if (s != endp || cells > 2) {
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
if (cells < 1) {
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate one byte more if we need to pad up
|
||||||
|
// with a space.
|
||||||
|
size_t len = (size_t)(endp - text + ((cells == 1) ? 1 : 0));
|
||||||
|
*sign_text = vim_strnsave(text, len);
|
||||||
|
|
||||||
|
if (cells == 1) {
|
||||||
|
STRCPY(*sign_text + len - 1, " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
#include "nvim/cursor.h"
|
#include "nvim/cursor.h"
|
||||||
#include "nvim/diff.h"
|
#include "nvim/diff.h"
|
||||||
#include "nvim/digraph.h"
|
#include "nvim/digraph.h"
|
||||||
|
#include "nvim/decoration.h"
|
||||||
#include "nvim/eval.h"
|
#include "nvim/eval.h"
|
||||||
#include "nvim/ex_cmds.h"
|
#include "nvim/ex_cmds.h"
|
||||||
#include "nvim/ex_cmds2.h"
|
#include "nvim/ex_cmds2.h"
|
||||||
@ -5469,6 +5470,11 @@ static int buf_signcols_inner(buf_T *buf, int maximum)
|
|||||||
|
|
||||||
FOR_ALL_SIGNS_IN_BUF(buf, sign) {
|
FOR_ALL_SIGNS_IN_BUF(buf, sign) {
|
||||||
if (sign->se_lnum > curline) {
|
if (sign->se_lnum > curline) {
|
||||||
|
// Counted all signs, now add extmark signs
|
||||||
|
if (curline > 0) {
|
||||||
|
linesum += decor_signcols(buf, &decor_state, (int)curline-1, (int)curline-1,
|
||||||
|
maximum-linesum);
|
||||||
|
}
|
||||||
if (linesum > signcols) {
|
if (linesum > signcols) {
|
||||||
signcols = linesum;
|
signcols = linesum;
|
||||||
if (signcols >= maximum) {
|
if (signcols >= maximum) {
|
||||||
@ -5483,6 +5489,19 @@ static int buf_signcols_inner(buf_T *buf, int maximum)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (curline > 0) {
|
||||||
|
linesum += decor_signcols(buf, &decor_state, (int)curline-1, (int)curline-1, maximum-linesum);
|
||||||
|
}
|
||||||
|
if (linesum > signcols) {
|
||||||
|
signcols = linesum;
|
||||||
|
if (signcols >= maximum) {
|
||||||
|
return maximum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check extmarks between signs
|
||||||
|
linesum = decor_signcols(buf, &decor_state, 0, (int)buf->b_ml.ml_line_count-1, maximum);
|
||||||
|
|
||||||
if (linesum > signcols) {
|
if (linesum > signcols) {
|
||||||
signcols = linesum;
|
signcols = linesum;
|
||||||
if (signcols >= maximum) {
|
if (signcols >= maximum) {
|
||||||
|
@ -875,6 +875,7 @@ struct file_buffer {
|
|||||||
MarkTree b_marktree[1];
|
MarkTree b_marktree[1];
|
||||||
Map(uint32_t, uint32_t) b_extmark_ns[1]; // extmark namespaces
|
Map(uint32_t, uint32_t) b_extmark_ns[1]; // extmark namespaces
|
||||||
size_t b_virt_line_blocks; // number of virt_line blocks
|
size_t b_virt_line_blocks; // number of virt_line blocks
|
||||||
|
size_t b_signs; // number of sign extmarks
|
||||||
|
|
||||||
// array of channel_id:s which have asked to receive updates for this
|
// array of channel_id:s which have asked to receive updates for this
|
||||||
// buffer.
|
// buffer.
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include "nvim/extmark.h"
|
#include "nvim/extmark.h"
|
||||||
#include "nvim/highlight.h"
|
#include "nvim/highlight.h"
|
||||||
#include "nvim/lua/executor.h"
|
#include "nvim/lua/executor.h"
|
||||||
|
#include "nvim/move.h"
|
||||||
#include "nvim/screen.h"
|
#include "nvim/screen.h"
|
||||||
#include "nvim/syntax.h"
|
#include "nvim/syntax.h"
|
||||||
#include "nvim/vim.h"
|
#include "nvim/vim.h"
|
||||||
@ -65,9 +66,16 @@ void bufhl_add_hl_pos_offset(buf_T *buf, int src_id, int hl_id, lpos_T pos_start
|
|||||||
|
|
||||||
void decor_redraw(buf_T *buf, int row1, int row2, Decoration *decor)
|
void decor_redraw(buf_T *buf, int row1, int row2, Decoration *decor)
|
||||||
{
|
{
|
||||||
if ((!decor || decor->hl_id) && row2 >= row1) {
|
if (row2 >= row1) {
|
||||||
|
if (decor && decor->sign_text) {
|
||||||
|
buf->b_signcols_valid = false;
|
||||||
|
changed_line_abv_curs();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!decor || decor->hl_id || decor_has_sign(decor)) {
|
||||||
redraw_buf_range_later(buf, row1+1, row2+1);
|
redraw_buf_range_later(buf, row1+1, row2+1);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (decor && kv_size(decor->virt_text)) {
|
if (decor && kv_size(decor->virt_text)) {
|
||||||
redraw_buf_line_later(buf, row1+1);
|
redraw_buf_line_later(buf, row1+1);
|
||||||
@ -82,10 +90,16 @@ void decor_redraw(buf_T *buf, int row1, int row2, Decoration *decor)
|
|||||||
void decor_remove(buf_T *buf, int row, int row2, Decoration *decor)
|
void decor_remove(buf_T *buf, int row, int row2, Decoration *decor)
|
||||||
{
|
{
|
||||||
decor_redraw(buf, row, row2, decor);
|
decor_redraw(buf, row, row2, decor);
|
||||||
if (decor && kv_size(decor->virt_lines)) {
|
if (decor) {
|
||||||
|
if (kv_size(decor->virt_lines)) {
|
||||||
assert(buf->b_virt_line_blocks > 0);
|
assert(buf->b_virt_line_blocks > 0);
|
||||||
buf->b_virt_line_blocks--;
|
buf->b_virt_line_blocks--;
|
||||||
}
|
}
|
||||||
|
if (decor_has_sign(decor)) {
|
||||||
|
assert(buf->b_signs > 0);
|
||||||
|
buf->b_signs--;
|
||||||
|
}
|
||||||
|
}
|
||||||
decor_free(decor);
|
decor_free(decor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,6 +111,7 @@ void decor_free(Decoration *decor)
|
|||||||
clear_virttext(&kv_A(decor->virt_lines, i).line);
|
clear_virttext(&kv_A(decor->virt_lines, i).line);
|
||||||
}
|
}
|
||||||
kv_destroy(decor->virt_lines);
|
kv_destroy(decor->virt_lines);
|
||||||
|
xfree(decor->sign_text);
|
||||||
xfree(decor);
|
xfree(decor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -136,6 +151,7 @@ bool decor_redraw_reset(buf_T *buf, DecorState *state)
|
|||||||
{
|
{
|
||||||
state->row = -1;
|
state->row = -1;
|
||||||
state->buf = buf;
|
state->buf = buf;
|
||||||
|
state->has_sign_decor = false;
|
||||||
for (size_t i = 0; i < kv_size(state->active); i++) {
|
for (size_t i = 0; i < kv_size(state->active); i++) {
|
||||||
DecorRange item = kv_A(state->active, i);
|
DecorRange item = kv_A(state->active, i);
|
||||||
if (item.virt_text_owned) {
|
if (item.virt_text_owned) {
|
||||||
@ -180,9 +196,23 @@ bool decor_redraw_start(buf_T *buf, int top_row, DecorState *state)
|
|||||||
|
|
||||||
Decoration decor = get_decor(mark);
|
Decoration decor = get_decor(mark);
|
||||||
|
|
||||||
|
// Exclude non-paired marks unless they contain virt_text or a sign
|
||||||
|
if (!mt_paired(mark)
|
||||||
|
&& !kv_size(decor.virt_text)
|
||||||
|
&& !decor_has_sign(&decor)) {
|
||||||
|
goto next_mark;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't add signs for end marks as the start mark has already been added.
|
||||||
|
if (mt_end(mark) && decor_has_sign(&decor)) {
|
||||||
|
goto next_mark;
|
||||||
|
}
|
||||||
|
|
||||||
mtpos_t altpos = marktree_get_altpos(buf->b_marktree, mark, NULL);
|
mtpos_t altpos = marktree_get_altpos(buf->b_marktree, mark, NULL);
|
||||||
|
|
||||||
if ((!mt_end(mark) && altpos.row < top_row
|
// Exclude start marks if the end mark position is above the top row
|
||||||
|
// Exclude end marks if we have already added the start mark
|
||||||
|
if ((mt_start(mark) && altpos.row < top_row
|
||||||
&& !kv_size(decor.virt_text))
|
&& !kv_size(decor.virt_text))
|
||||||
|| (mt_end(mark) && altpos.row >= top_row)) {
|
|| (mt_end(mark) && altpos.row >= top_row)) {
|
||||||
goto next_mark;
|
goto next_mark;
|
||||||
@ -241,6 +271,10 @@ static void decor_add(DecorState *state, int start_row, int start_col, int end_r
|
|||||||
kv_A(state->active, index) = kv_A(state->active, index-1);
|
kv_A(state->active, index) = kv_A(state->active, index-1);
|
||||||
}
|
}
|
||||||
kv_A(state->active, index) = range;
|
kv_A(state->active, index) = range;
|
||||||
|
|
||||||
|
if (decor_has_sign(decor)) {
|
||||||
|
state->has_sign_decor = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int decor_redraw_col(buf_T *buf, int col, int win_col, bool hidden, DecorState *state)
|
int decor_redraw_col(buf_T *buf, int col, int win_col, bool hidden, DecorState *state)
|
||||||
@ -294,7 +328,8 @@ next_mark:
|
|||||||
bool active = false, keep = true;
|
bool active = false, keep = true;
|
||||||
if (item.end_row < state->row
|
if (item.end_row < state->row
|
||||||
|| (item.end_row == state->row && item.end_col <= col)) {
|
|| (item.end_row == state->row && item.end_col <= col)) {
|
||||||
if (!(item.start_row >= state->row && kv_size(item.decor.virt_text))) {
|
if (!(item.start_row >= state->row && kv_size(item.decor.virt_text))
|
||||||
|
&& !decor_has_sign(&item.decor)) {
|
||||||
keep = false;
|
keep = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -329,6 +364,131 @@ next_mark:
|
|||||||
return attr;
|
return attr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void decor_redraw_signs(buf_T *buf, DecorState *state, int row,
|
||||||
|
int *num_signs, sign_attrs_T sattrs[])
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < kv_size(state->active); i++) {
|
||||||
|
DecorRange item = kv_A(state->active, i);
|
||||||
|
Decoration *decor = &item.decor;
|
||||||
|
|
||||||
|
if (!decor_has_sign(decor)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->row != item.start_row) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int j;
|
||||||
|
for (j = (*num_signs); j > 0; j--) {
|
||||||
|
if (sattrs[j].sat_prio <= decor->priority) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sattrs[j] = sattrs[j-1];
|
||||||
|
}
|
||||||
|
if (j < SIGN_SHOW_MAX) {
|
||||||
|
memset(&sattrs[j], 0, sizeof(sign_attrs_T));
|
||||||
|
sattrs[j].sat_text = decor->sign_text;
|
||||||
|
if (decor->sign_hl_id != 0) {
|
||||||
|
sattrs[j].sat_texthl = syn_id2attr(decor->sign_hl_id);
|
||||||
|
}
|
||||||
|
if (decor->number_hl_id != 0) {
|
||||||
|
sattrs[j].sat_numhl = syn_id2attr(decor->number_hl_id);
|
||||||
|
}
|
||||||
|
if (decor->line_hl_id != 0) {
|
||||||
|
sattrs[j].sat_linehl = syn_id2attr(decor->line_hl_id);
|
||||||
|
}
|
||||||
|
if (decor->cursorline_hl_id != 0) {
|
||||||
|
sattrs[j].sat_culhl = syn_id2attr(decor->cursorline_hl_id);
|
||||||
|
}
|
||||||
|
sattrs[j].sat_prio = decor->priority;
|
||||||
|
(*num_signs)++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the maximum required amount of sign columns needed between row and
|
||||||
|
// end_row.
|
||||||
|
int decor_signcols(buf_T *buf, DecorState *state, int row, int end_row, int max)
|
||||||
|
{
|
||||||
|
int count = 0; // count for the number of signs on a given row
|
||||||
|
int count_remove = 0; // how much to decrement count by when iterating marks for a new row
|
||||||
|
int signcols = 0; // highest value of count
|
||||||
|
int currow = -1; // current row
|
||||||
|
|
||||||
|
if (max <= 1 && buf->b_signs >= (size_t)max) {
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buf->b_signs == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
MarkTreeIter itr[1] = { 0 };
|
||||||
|
marktree_itr_get(buf->b_marktree, 0, -1, itr);
|
||||||
|
while (true) {
|
||||||
|
mtkey_t mark = marktree_itr_current(itr);
|
||||||
|
if (mark.pos.row < 0 || mark.pos.row > end_row) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((mark.pos.row < row && mt_end(mark))
|
||||||
|
|| marktree_decor_level(mark) < kDecorLevelVisible
|
||||||
|
|| !mark.decor_full) {
|
||||||
|
goto next_mark;
|
||||||
|
}
|
||||||
|
|
||||||
|
Decoration decor = get_decor(mark);
|
||||||
|
|
||||||
|
if (!decor.sign_text) {
|
||||||
|
goto next_mark;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mark.pos.row > currow) {
|
||||||
|
count -= count_remove;
|
||||||
|
count_remove = 0;
|
||||||
|
currow = mark.pos.row;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mt_paired(mark)) {
|
||||||
|
if (mark.pos.row >= row) {
|
||||||
|
count++;
|
||||||
|
if (count > signcols) {
|
||||||
|
signcols = count;
|
||||||
|
if (signcols >= max) {
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
count_remove++;
|
||||||
|
}
|
||||||
|
goto next_mark;
|
||||||
|
}
|
||||||
|
|
||||||
|
mtpos_t altpos = marktree_get_altpos(buf->b_marktree, mark, NULL);
|
||||||
|
|
||||||
|
if (mt_end(mark)) {
|
||||||
|
if (mark.pos.row >= row && altpos.row <= end_row) {
|
||||||
|
count_remove++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (altpos.row >= row) {
|
||||||
|
count++;
|
||||||
|
if (count > signcols) {
|
||||||
|
signcols = count;
|
||||||
|
if (signcols >= max) {
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
next_mark:
|
||||||
|
marktree_itr_next(buf->b_marktree, itr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return signcols;
|
||||||
|
}
|
||||||
|
|
||||||
void decor_redraw_end(DecorState *state)
|
void decor_redraw_end(DecorState *state)
|
||||||
{
|
{
|
||||||
state->buf = NULL;
|
state->buf = NULL;
|
||||||
|
@ -47,13 +47,18 @@ struct Decoration {
|
|||||||
bool virt_text_hide;
|
bool virt_text_hide;
|
||||||
bool hl_eol;
|
bool hl_eol;
|
||||||
bool virt_lines_above;
|
bool virt_lines_above;
|
||||||
// TODO(bfredl): style, signs, etc
|
// TODO(bfredl): style, etc
|
||||||
DecorPriority priority;
|
DecorPriority priority;
|
||||||
int col; // fixed col value, like win_col
|
int col; // fixed col value, like win_col
|
||||||
int virt_text_width; // width of virt_text
|
int virt_text_width; // width of virt_text
|
||||||
|
char_u *sign_text;
|
||||||
|
int sign_hl_id;
|
||||||
|
int number_hl_id;
|
||||||
|
int line_hl_id;
|
||||||
|
int cursorline_hl_id;
|
||||||
};
|
};
|
||||||
#define DECORATION_INIT { KV_INITIAL_VALUE, KV_INITIAL_VALUE, 0, kVTEndOfLine, kHlModeUnknown, \
|
#define DECORATION_INIT { KV_INITIAL_VALUE, KV_INITIAL_VALUE, 0, kVTEndOfLine, kHlModeUnknown, \
|
||||||
false, false, false, DECOR_PRIORITY_BASE, 0, 0 }
|
false, false, false, DECOR_PRIORITY_BASE, 0, 0, NULL, 0, 0, 0, 0 }
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int start_row;
|
int start_row;
|
||||||
@ -75,6 +80,7 @@ typedef struct {
|
|||||||
int col_until;
|
int col_until;
|
||||||
int current;
|
int current;
|
||||||
int eol_col;
|
int eol_col;
|
||||||
|
bool has_sign_decor;
|
||||||
} DecorState;
|
} DecorState;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -98,6 +104,15 @@ EXTERN bool provider_active INIT(= false);
|
|||||||
LUA_NOREF, LUA_NOREF, LUA_NOREF, \
|
LUA_NOREF, LUA_NOREF, LUA_NOREF, \
|
||||||
LUA_NOREF, -1 }
|
LUA_NOREF, -1 }
|
||||||
|
|
||||||
|
static inline bool decor_has_sign(Decoration *decor)
|
||||||
|
{
|
||||||
|
return decor->sign_text
|
||||||
|
|| decor->sign_hl_id
|
||||||
|
|| decor->number_hl_id
|
||||||
|
|| decor->line_hl_id
|
||||||
|
|| decor->cursorline_hl_id;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
# include "decoration.h.generated.h"
|
# include "decoration.h.generated.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -67,7 +67,9 @@ void extmark_set(buf_T *buf, uint32_t ns_id, uint32_t *idp, int row, colnr_T col
|
|||||||
|
|
||||||
uint8_t decor_level = kDecorLevelNone; // no decor
|
uint8_t decor_level = kDecorLevelNone; // no decor
|
||||||
if (decor) {
|
if (decor) {
|
||||||
if (kv_size(decor->virt_text) || kv_size(decor->virt_lines)) {
|
if (kv_size(decor->virt_text)
|
||||||
|
|| kv_size(decor->virt_lines)
|
||||||
|
|| decor_has_sign(decor)) {
|
||||||
decor_full = true;
|
decor_full = true;
|
||||||
decor = xmemdup(decor, sizeof *decor);
|
decor = xmemdup(decor, sizeof *decor);
|
||||||
}
|
}
|
||||||
@ -142,6 +144,9 @@ revised:
|
|||||||
if (kv_size(decor->virt_lines)) {
|
if (kv_size(decor->virt_lines)) {
|
||||||
buf->b_virt_line_blocks++;
|
buf->b_virt_line_blocks++;
|
||||||
}
|
}
|
||||||
|
if (decor_has_sign(decor)) {
|
||||||
|
buf->b_signs++;
|
||||||
|
}
|
||||||
decor_redraw(buf, row, end_row > -1 ? end_row : row, decor);
|
decor_redraw(buf, row, end_row > -1 ? end_row : row, decor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,6 +87,11 @@ static inline bool mt_end(mtkey_t key)
|
|||||||
return key.flags & MT_FLAG_END;
|
return key.flags & MT_FLAG_END;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool mt_start(mtkey_t key)
|
||||||
|
{
|
||||||
|
return mt_paired(key) && !mt_end(key);
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool mt_right(mtkey_t key)
|
static inline bool mt_right(mtkey_t key)
|
||||||
{
|
{
|
||||||
return key.flags & MT_FLAG_RIGHT_GRAVITY;
|
return key.flags & MT_FLAG_RIGHT_GRAVITY;
|
||||||
|
@ -2208,6 +2208,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
|
|||||||
buf_T *buf = wp->w_buffer;
|
buf_T *buf = wp->w_buffer;
|
||||||
bool end_fill = (lnum == buf->b_ml.ml_line_count+1);
|
bool end_fill = (lnum == buf->b_ml.ml_line_count+1);
|
||||||
|
|
||||||
|
has_decor = decor_redraw_line(buf, lnum-1, &decor_state);
|
||||||
|
|
||||||
if (!number_only) {
|
if (!number_only) {
|
||||||
// To speed up the loop below, set extra_check when there is linebreak,
|
// To speed up the loop below, set extra_check when there is linebreak,
|
||||||
// trailing white space and/or syntax processing to be done.
|
// trailing white space and/or syntax processing to be done.
|
||||||
@ -2229,9 +2231,6 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
has_decor = decor_redraw_line(wp->w_buffer, lnum-1,
|
|
||||||
&decor_state);
|
|
||||||
|
|
||||||
for (size_t k = 0; k < kv_size(*providers); k++) {
|
for (size_t k = 0; k < kv_size(*providers); k++) {
|
||||||
DecorProvider *p = kv_A(*providers, k);
|
DecorProvider *p = kv_A(*providers, k);
|
||||||
if (p && p->redraw_line != LUA_NOREF) {
|
if (p && p->redraw_line != LUA_NOREF) {
|
||||||
@ -2460,6 +2459,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
|
|||||||
|
|
||||||
memset(sattrs, 0, sizeof(sattrs));
|
memset(sattrs, 0, sizeof(sattrs));
|
||||||
num_signs = buf_get_signattrs(wp->w_buffer, lnum, sattrs);
|
num_signs = buf_get_signattrs(wp->w_buffer, lnum, sattrs);
|
||||||
|
if (decor_state.has_sign_decor) {
|
||||||
|
decor_redraw_signs(buf, &decor_state, lnum-1, &num_signs, sattrs);
|
||||||
|
}
|
||||||
|
|
||||||
// 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 decoration!
|
// TODO(bfredl, vigoux): this should not take priority over decoration!
|
||||||
|
@ -506,6 +506,8 @@ int buf_get_signattrs(buf_T *buf, linenr_T lnum, sign_attrs_T sattrs[])
|
|||||||
if (sp->sn_num_hl != 0) {
|
if (sp->sn_num_hl != 0) {
|
||||||
sattr.sat_numhl = syn_id2attr(sp->sn_num_hl);
|
sattr.sat_numhl = syn_id2attr(sp->sn_num_hl);
|
||||||
}
|
}
|
||||||
|
// Store the priority so we can mesh in extmark signs later
|
||||||
|
sattr.sat_prio = sign->se_priority;
|
||||||
}
|
}
|
||||||
|
|
||||||
sattrs[nr_matches] = sattr;
|
sattrs[nr_matches] = sattr;
|
||||||
|
@ -40,6 +40,7 @@ typedef struct sign_attrs_S {
|
|||||||
int sat_linehl;
|
int sat_linehl;
|
||||||
int sat_culhl;
|
int sat_culhl;
|
||||||
int sat_numhl;
|
int sat_numhl;
|
||||||
|
int sat_prio; // Used for inserting extmark signs
|
||||||
} sign_attrs_T;
|
} sign_attrs_T;
|
||||||
|
|
||||||
#define SIGN_SHOW_MAX 9
|
#define SIGN_SHOW_MAX 9
|
||||||
|
@ -1362,3 +1362,276 @@ if (h->n_buckets < new_n_buckets) { // expand
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
describe('decorations: signs', function()
|
||||||
|
local screen, ns
|
||||||
|
before_each(function()
|
||||||
|
clear()
|
||||||
|
screen = Screen.new(50, 10)
|
||||||
|
screen:attach()
|
||||||
|
screen:set_default_attr_ids {
|
||||||
|
[1] = {foreground = Screen.colors.Blue4, background = Screen.colors.Grey};
|
||||||
|
[2] = {foreground = Screen.colors.Blue1, bold = true};
|
||||||
|
}
|
||||||
|
|
||||||
|
ns = meths.create_namespace 'test'
|
||||||
|
meths.win_set_option(0, 'signcolumn', 'auto:9')
|
||||||
|
end)
|
||||||
|
|
||||||
|
local example_text = [[
|
||||||
|
l1
|
||||||
|
l2
|
||||||
|
l3
|
||||||
|
l4
|
||||||
|
l5
|
||||||
|
]]
|
||||||
|
|
||||||
|
it('can add a single sign (no end row)', function()
|
||||||
|
insert(example_text)
|
||||||
|
feed 'gg'
|
||||||
|
|
||||||
|
meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S'})
|
||||||
|
|
||||||
|
screen:expect{grid=[[
|
||||||
|
{1: }^l1 |
|
||||||
|
S l2 |
|
||||||
|
{1: }l3 |
|
||||||
|
{1: }l4 |
|
||||||
|
{1: }l5 |
|
||||||
|
{1: } |
|
||||||
|
{2:~ }|
|
||||||
|
{2:~ }|
|
||||||
|
{2:~ }|
|
||||||
|
|
|
||||||
|
]]}
|
||||||
|
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('can add a single sign (with end row)', function()
|
||||||
|
insert(example_text)
|
||||||
|
feed 'gg'
|
||||||
|
|
||||||
|
meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S', end_row=1})
|
||||||
|
|
||||||
|
screen:expect{grid=[[
|
||||||
|
{1: }^l1 |
|
||||||
|
S l2 |
|
||||||
|
{1: }l3 |
|
||||||
|
{1: }l4 |
|
||||||
|
{1: }l5 |
|
||||||
|
{1: } |
|
||||||
|
{2:~ }|
|
||||||
|
{2:~ }|
|
||||||
|
{2:~ }|
|
||||||
|
|
|
||||||
|
]]}
|
||||||
|
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('can add multiple signs (single extmark)', function()
|
||||||
|
pending('TODO(lewis6991): Support ranged signs')
|
||||||
|
insert(example_text)
|
||||||
|
feed 'gg'
|
||||||
|
|
||||||
|
meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S', end_row = 2})
|
||||||
|
|
||||||
|
screen:expect{grid=[[
|
||||||
|
{1: }^l1 |
|
||||||
|
S l2 |
|
||||||
|
S l3 |
|
||||||
|
{1: }l4 |
|
||||||
|
{1: }l5 |
|
||||||
|
{1: } |
|
||||||
|
{2:~ }|
|
||||||
|
{2:~ }|
|
||||||
|
{2:~ }|
|
||||||
|
|
|
||||||
|
]]}
|
||||||
|
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('can add multiple signs (multiple extmarks)', function()
|
||||||
|
pending('TODO(lewis6991): Support ranged signs')
|
||||||
|
insert(example_text)
|
||||||
|
feed'gg'
|
||||||
|
|
||||||
|
meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S1'})
|
||||||
|
meths.buf_set_extmark(0, ns, 3, -1, {sign_text='S2', end_row = 4})
|
||||||
|
|
||||||
|
screen:expect{grid=[[
|
||||||
|
{1: }^l1 |
|
||||||
|
S1l2 |
|
||||||
|
{1: }l3 |
|
||||||
|
S2l4 |
|
||||||
|
S2l5 |
|
||||||
|
{1: } |
|
||||||
|
{2:~ }|
|
||||||
|
{2:~ }|
|
||||||
|
{2:~ }|
|
||||||
|
|
|
||||||
|
]]}
|
||||||
|
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('can add multiple signs (multiple extmarks) 2', function()
|
||||||
|
insert(example_text)
|
||||||
|
feed 'gg'
|
||||||
|
|
||||||
|
meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S1'})
|
||||||
|
meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S2'})
|
||||||
|
|
||||||
|
screen:expect{grid=[[
|
||||||
|
{1: }^l1 |
|
||||||
|
S2S1l2 |
|
||||||
|
{1: }l3 |
|
||||||
|
{1: }l4 |
|
||||||
|
{1: }l5 |
|
||||||
|
{1: } |
|
||||||
|
{2:~ }|
|
||||||
|
{2:~ }|
|
||||||
|
{2:~ }|
|
||||||
|
|
|
||||||
|
]]}
|
||||||
|
|
||||||
|
-- TODO(lewis6991): Support ranged signs
|
||||||
|
-- meths.buf_set_extmark(1, ns, 1, -1, {sign_text='S3', end_row = 2})
|
||||||
|
|
||||||
|
-- screen:expect{grid=[[
|
||||||
|
-- {1: }^l1 |
|
||||||
|
-- S3S2S1l2 |
|
||||||
|
-- S3{1: }l3 |
|
||||||
|
-- {1: }l4 |
|
||||||
|
-- {1: }l5 |
|
||||||
|
-- {1: } |
|
||||||
|
-- {2:~ }|
|
||||||
|
-- {2:~ }|
|
||||||
|
-- {2:~ }|
|
||||||
|
-- |
|
||||||
|
-- ]]}
|
||||||
|
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('can add multiple signs (multiple extmarks) 3', function()
|
||||||
|
pending('TODO(lewis6991): Support ranged signs')
|
||||||
|
|
||||||
|
insert(example_text)
|
||||||
|
feed 'gg'
|
||||||
|
|
||||||
|
meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S1', end_row=2})
|
||||||
|
meths.buf_set_extmark(0, ns, 2, -1, {sign_text='S2', end_row=3})
|
||||||
|
|
||||||
|
screen:expect{grid=[[
|
||||||
|
{1: }^l1 |
|
||||||
|
S1{1: }l2 |
|
||||||
|
S2S1l3 |
|
||||||
|
S2{1: }l4 |
|
||||||
|
{1: }l5 |
|
||||||
|
{1: } |
|
||||||
|
{2:~ }|
|
||||||
|
{2:~ }|
|
||||||
|
{2:~ }|
|
||||||
|
|
|
||||||
|
]]}
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('can add multiple signs (multiple extmarks) 4', function()
|
||||||
|
insert(example_text)
|
||||||
|
feed 'gg'
|
||||||
|
|
||||||
|
meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S1', end_row=0})
|
||||||
|
meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S2', end_row=1})
|
||||||
|
|
||||||
|
screen:expect{grid=[[
|
||||||
|
S1^l1 |
|
||||||
|
S2l2 |
|
||||||
|
{1: }l3 |
|
||||||
|
{1: }l4 |
|
||||||
|
{1: }l5 |
|
||||||
|
{1: } |
|
||||||
|
{2:~ }|
|
||||||
|
{2:~ }|
|
||||||
|
{2:~ }|
|
||||||
|
|
|
||||||
|
]]}
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('works with old signs', function()
|
||||||
|
insert(example_text)
|
||||||
|
feed 'gg'
|
||||||
|
|
||||||
|
helpers.command('sign define Oldsign text=x')
|
||||||
|
helpers.command([[exe 'sign place 42 line=2 name=Oldsign buffer=' . bufnr('')]])
|
||||||
|
|
||||||
|
meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S1'})
|
||||||
|
meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S2'})
|
||||||
|
meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S4'})
|
||||||
|
meths.buf_set_extmark(0, ns, 2, -1, {sign_text='S5'})
|
||||||
|
|
||||||
|
screen:expect{grid=[[
|
||||||
|
S4S1^l1 |
|
||||||
|
S2x l2 |
|
||||||
|
S5{1: }l3 |
|
||||||
|
{1: }l4 |
|
||||||
|
{1: }l5 |
|
||||||
|
{1: } |
|
||||||
|
{2:~ }|
|
||||||
|
{2:~ }|
|
||||||
|
{2:~ }|
|
||||||
|
|
|
||||||
|
]]}
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('works with old signs (with range)', function()
|
||||||
|
pending('TODO(lewis6991): Support ranged signs')
|
||||||
|
insert(example_text)
|
||||||
|
feed 'gg'
|
||||||
|
|
||||||
|
helpers.command('sign define Oldsign text=x')
|
||||||
|
helpers.command([[exe 'sign place 42 line=2 name=Oldsign buffer=' . bufnr('')]])
|
||||||
|
|
||||||
|
meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S1'})
|
||||||
|
meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S2'})
|
||||||
|
meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S3', end_row = 4})
|
||||||
|
meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S4'})
|
||||||
|
meths.buf_set_extmark(0, ns, 2, -1, {sign_text='S5'})
|
||||||
|
|
||||||
|
screen:expect{grid=[[
|
||||||
|
S3S4S1^l1 |
|
||||||
|
S2S3x l2 |
|
||||||
|
S5S3{1: }l3 |
|
||||||
|
S3{1: }l4 |
|
||||||
|
S3{1: }l5 |
|
||||||
|
{1: } |
|
||||||
|
{2:~ }|
|
||||||
|
{2:~ }|
|
||||||
|
{2:~ }|
|
||||||
|
|
|
||||||
|
]]}
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('can add a ranged sign (with start out of view)', function()
|
||||||
|
pending('TODO(lewis6991): Support ranged signs')
|
||||||
|
|
||||||
|
insert(example_text)
|
||||||
|
command 'set signcolumn=yes:2'
|
||||||
|
feed 'gg'
|
||||||
|
feed '2<C-e>'
|
||||||
|
|
||||||
|
meths.buf_set_extmark(0, ns, 1, -1, {sign_text='X', end_row=3})
|
||||||
|
|
||||||
|
screen:expect{grid=[[
|
||||||
|
X {1: }^l3 |
|
||||||
|
X {1: }l4 |
|
||||||
|
{1: }l5 |
|
||||||
|
{1: } |
|
||||||
|
{2:~ }|
|
||||||
|
{2:~ }|
|
||||||
|
{2:~ }|
|
||||||
|
{2:~ }|
|
||||||
|
{2:~ }|
|
||||||
|
|
|
||||||
|
]]}
|
||||||
|
|
||||||
|
end)
|
||||||
|
|
||||||
|
end)
|
||||||
|
Loading…
Reference in New Issue
Block a user