mirror of
https://github.com/neovim/neovim.git
synced 2025-10-05 01:16:31 +00:00
feat(api/ui): win_extmarks
This commit is contained in:
@@ -146,6 +146,10 @@ static Array extmark_to_array(ExtmarkInfo extmark, bool id, bool add_dict)
|
||||
STRING_OBJ(cstr_to_string(virt_text_pos_str[decor->virt_text_pos])));
|
||||
}
|
||||
|
||||
if (decor->ui_watched) {
|
||||
PUT(dict, "ui_watched", BOOLEAN_OBJ(true));
|
||||
}
|
||||
|
||||
if (kv_size(decor->virt_lines)) {
|
||||
Array all_chunks = ARRAY_DICT_INIT;
|
||||
bool virt_lines_leftcol = false;
|
||||
@@ -170,7 +174,7 @@ static Array extmark_to_array(ExtmarkInfo extmark, bool id, bool add_dict)
|
||||
PUT(dict, "virt_lines_leftcol", BOOLEAN_OBJ(virt_lines_leftcol));
|
||||
}
|
||||
|
||||
if (decor->hl_id || kv_size(decor->virt_text)) {
|
||||
if (decor->hl_id || kv_size(decor->virt_text) || decor->ui_watched) {
|
||||
PUT(dict, "priority", INTEGER_OBJ(decor->priority));
|
||||
}
|
||||
|
||||
@@ -472,6 +476,10 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
|
||||
/// When a character is supplied it is used as |:syn-cchar|.
|
||||
/// "hl_group" is used as highlight for the cchar if provided,
|
||||
/// otherwise it defaults to |hl-Conceal|.
|
||||
/// - ui_watched: boolean that indicates the mark should be drawn
|
||||
/// by a UI. When set, the UI will receive win_extmark events.
|
||||
/// Note: the mark is positioned by virt_text attributes. Can be
|
||||
/// used together with virt_text.
|
||||
///
|
||||
/// @param[out] err Error details, if any
|
||||
/// @return Id of the created/updated extmark
|
||||
@@ -709,6 +717,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
||||
bool ephemeral = false;
|
||||
OPTION_TO_BOOL(ephemeral, ephemeral, false);
|
||||
|
||||
OPTION_TO_BOOL(decor.ui_watched, ui_watched, false);
|
||||
|
||||
if (line < 0) {
|
||||
api_set_error(err, kErrorTypeValidation, "line value outside range");
|
||||
goto error;
|
||||
@@ -762,7 +772,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
||||
|
||||
// TODO(bfredl): synergize these two branches even more
|
||||
if (ephemeral && decor_state.buf == buf) {
|
||||
decor_add_ephemeral((int)line, (int)col, line2, col2, &decor);
|
||||
decor_add_ephemeral((int)line, (int)col, line2, col2, &decor, (uint64_t)ns_id, id);
|
||||
} else {
|
||||
if (ephemeral) {
|
||||
api_set_error(err, kErrorTypeException, "not yet implemented");
|
||||
|
@@ -28,6 +28,7 @@ return {
|
||||
"line_hl_group";
|
||||
"cursorline_hl_group";
|
||||
"conceal";
|
||||
"ui_watched";
|
||||
};
|
||||
keymap = {
|
||||
"noremap";
|
||||
|
@@ -810,3 +810,4 @@ static void remote_ui_inspect(UI *ui, Dictionary *info)
|
||||
UIData *data = ui->data;
|
||||
PUT(*info, "chan", INTEGER_OBJ((Integer)data->channel_id));
|
||||
}
|
||||
|
||||
|
@@ -4,6 +4,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "nvim/api/private/defs.h"
|
||||
#include "nvim/map.h"
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "api/ui.h.generated.h"
|
||||
|
@@ -123,6 +123,10 @@ void win_viewport(Integer grid, Window win, Integer topline,
|
||||
Integer line_count)
|
||||
FUNC_API_SINCE(7) FUNC_API_REMOTE_ONLY;
|
||||
|
||||
void win_extmark(Integer grid, Window win, Integer ns_id, Integer mark_id,
|
||||
Integer row, Integer col)
|
||||
FUNC_API_SINCE(10) FUNC_API_REMOTE_ONLY;
|
||||
|
||||
void popupmenu_show(Array items, Integer selected,
|
||||
Integer row, Integer col, Integer grid)
|
||||
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
|
||||
|
@@ -2,6 +2,7 @@
|
||||
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
|
||||
|
||||
#include "nvim/buffer.h"
|
||||
#include "nvim/api/ui.h"
|
||||
#include "nvim/decoration.h"
|
||||
#include "nvim/extmark.h"
|
||||
#include "nvim/highlight.h"
|
||||
@@ -73,7 +74,8 @@ void decor_redraw(buf_T *buf, int row1, int row2, Decoration *decor)
|
||||
}
|
||||
}
|
||||
|
||||
if (decor && kv_size(decor->virt_text)) {
|
||||
if (decor && (kv_size(decor->virt_text)
|
||||
|| decor->ui_watched)) {
|
||||
redraw_buf_line_later(buf, row1 + 1);
|
||||
}
|
||||
|
||||
@@ -195,9 +197,11 @@ bool decor_redraw_start(buf_T *buf, int top_row, DecorState *state)
|
||||
Decoration decor = get_decor(mark);
|
||||
|
||||
// Exclude non-paired marks unless they contain virt_text or a sign
|
||||
// or they are ui-watched
|
||||
if (!mt_paired(mark)
|
||||
&& !kv_size(decor.virt_text)
|
||||
&& !decor_has_sign(&decor)) {
|
||||
&& !decor_has_sign(&decor)
|
||||
&& !decor.ui_watched) {
|
||||
goto next_mark;
|
||||
}
|
||||
|
||||
@@ -206,21 +210,22 @@ bool decor_redraw_start(buf_T *buf, int top_row, DecorState *state)
|
||||
// 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)
|
||||
&& !decor.ui_watched)
|
||||
|| (mt_end(mark) && altpos.row >= top_row)) {
|
||||
goto next_mark;
|
||||
}
|
||||
|
||||
if (mt_end(mark)) {
|
||||
decor_add(state, altpos.row, altpos.col, mark.pos.row, mark.pos.col,
|
||||
&decor, false);
|
||||
&decor, false, mark.ns, mark.id);
|
||||
} else {
|
||||
if (altpos.row == -1) {
|
||||
altpos.row = mark.pos.row;
|
||||
altpos.col = mark.pos.col;
|
||||
}
|
||||
decor_add(state, mark.pos.row, mark.pos.col, altpos.row, altpos.col,
|
||||
&decor, false);
|
||||
&decor, false, mark.ns, mark.id);
|
||||
}
|
||||
|
||||
next_mark:
|
||||
@@ -246,13 +251,13 @@ bool decor_redraw_line(buf_T *buf, int row, DecorState *state)
|
||||
}
|
||||
|
||||
static void decor_add(DecorState *state, int start_row, int start_col, int end_row, int end_col,
|
||||
Decoration *decor, bool owned)
|
||||
Decoration *decor, bool owned, uint64_t ns_id, uint64_t mark_id)
|
||||
{
|
||||
int attr_id = decor->hl_id > 0 ? syn_id2attr(decor->hl_id) : 0;
|
||||
|
||||
DecorRange range = { start_row, start_col, end_row, end_col,
|
||||
*decor, attr_id,
|
||||
kv_size(decor->virt_text) && owned, -1 };
|
||||
kv_size(decor->virt_text) && owned, -1, ns_id, mark_id };
|
||||
|
||||
kv_pushp(state->active);
|
||||
size_t index;
|
||||
@@ -298,13 +303,13 @@ int decor_redraw_col(buf_T *buf, int col, int win_col, bool hidden, DecorState *
|
||||
|
||||
if (endpos.row < mark.pos.row
|
||||
|| (endpos.row == mark.pos.row && endpos.col <= mark.pos.col)) {
|
||||
if (!kv_size(decor.virt_text)) {
|
||||
if (!kv_size(decor.virt_text) && !decor.ui_watched) {
|
||||
goto next_mark;
|
||||
}
|
||||
}
|
||||
|
||||
decor_add(state, mark.pos.row, mark.pos.col, endpos.row, endpos.col,
|
||||
&decor, false);
|
||||
&decor, false, mark.ns, mark.id);
|
||||
|
||||
next_mark:
|
||||
marktree_itr_next(buf->b_marktree, state->itr);
|
||||
@@ -321,7 +326,8 @@ next_mark:
|
||||
bool active = false, keep = true;
|
||||
if (item.end_row < state->row
|
||||
|| (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) || item.decor.ui_watched))) {
|
||||
keep = false;
|
||||
}
|
||||
} else {
|
||||
@@ -349,7 +355,7 @@ next_mark:
|
||||
}
|
||||
}
|
||||
if ((item.start_row == state->row && item.start_col <= col)
|
||||
&& kv_size(item.decor.virt_text)
|
||||
&& (kv_size(item.decor.virt_text) || item.decor.ui_watched)
|
||||
&& item.decor.virt_text_pos == kVTOverlay && item.win_col == -1) {
|
||||
item.win_col = (item.decor.virt_text_hide && hidden) ? -2 : win_col;
|
||||
}
|
||||
@@ -517,7 +523,8 @@ bool decor_redraw_eol(buf_T *buf, DecorState *state, int *eol_attr, int 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 (item.start_row == state->row
|
||||
&& (kv_size(item.decor.virt_text) || item.decor.ui_watched)) {
|
||||
has_virttext = true;
|
||||
}
|
||||
|
||||
@@ -528,13 +535,14 @@ bool decor_redraw_eol(buf_T *buf, DecorState *state, int *eol_attr, int eol_col)
|
||||
return has_virttext;
|
||||
}
|
||||
|
||||
void decor_add_ephemeral(int start_row, int start_col, int end_row, int end_col, Decoration *decor)
|
||||
void decor_add_ephemeral(int start_row, int start_col, int end_row, int end_col,
|
||||
Decoration *decor, uint64_t ns_id, uint64_t mark_id)
|
||||
{
|
||||
if (end_row == -1) {
|
||||
end_row = start_row;
|
||||
end_col = start_col;
|
||||
}
|
||||
decor_add(&decor_state, start_row, start_col, end_row, end_col, decor, true);
|
||||
decor_add(&decor_state, start_row, start_col, end_row, end_col, decor, true, ns_id, mark_id);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -60,10 +60,11 @@ struct Decoration {
|
||||
// TODO(bfredl): in principle this should be a schar_T, but we
|
||||
// probably want some kind of glyph cache for that..
|
||||
int conceal_char;
|
||||
bool ui_watched; // watched for win_extmark
|
||||
};
|
||||
#define DECORATION_INIT { KV_INITIAL_VALUE, KV_INITIAL_VALUE, 0, kVTEndOfLine, \
|
||||
kHlModeUnknown, false, false, false, false, DECOR_PRIORITY_BASE, \
|
||||
0, 0, NULL, 0, 0, 0, 0, 0 }
|
||||
0, 0, NULL, 0, 0, 0, 0, 0, false }
|
||||
|
||||
typedef struct {
|
||||
int start_row;
|
||||
@@ -74,6 +75,8 @@ typedef struct {
|
||||
int attr_id; // cached lookup of decor.hl_id
|
||||
bool virt_text_owned;
|
||||
int win_col;
|
||||
uint64_t ns_id;
|
||||
uint64_t mark_id;
|
||||
} DecorRange;
|
||||
|
||||
typedef struct {
|
||||
|
@@ -69,7 +69,8 @@ void extmark_set(buf_T *buf, uint32_t ns_id, uint32_t *idp, int row, colnr_T col
|
||||
if (decor) {
|
||||
if (kv_size(decor->virt_text)
|
||||
|| kv_size(decor->virt_lines)
|
||||
|| decor_has_sign(decor)) {
|
||||
|| decor_has_sign(decor)
|
||||
|| decor->ui_watched) {
|
||||
decor_full = true;
|
||||
decor = xmemdup(decor, sizeof *decor);
|
||||
}
|
||||
|
@@ -67,6 +67,7 @@
|
||||
|
||||
#include "nvim/api/extmark.h"
|
||||
#include "nvim/api/private/helpers.h"
|
||||
#include "nvim/api/ui.h"
|
||||
#include "nvim/api/vim.h"
|
||||
#include "nvim/arabic.h"
|
||||
#include "nvim/ascii.h"
|
||||
@@ -161,6 +162,14 @@ static bool msg_grid_invalid = false;
|
||||
|
||||
static bool resizing = false;
|
||||
|
||||
typedef struct {
|
||||
NS ns_id;
|
||||
uint64_t mark_id;
|
||||
int win_row;
|
||||
int win_col;
|
||||
} WinExtmark;
|
||||
static kvec_t(WinExtmark) win_extmark_arr INIT(= KV_INITIAL_VALUE);
|
||||
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "screen.c.generated.h"
|
||||
@@ -1314,6 +1323,8 @@ static void win_update(win_T *wp, DecorProviders *providers)
|
||||
srow = 0;
|
||||
lnum = wp->w_topline; // first line shown in window
|
||||
|
||||
win_extmark_arr.size = 0;
|
||||
|
||||
decor_redraw_reset(buf, &decor_state);
|
||||
|
||||
DecorProviders line_providers;
|
||||
@@ -1692,6 +1703,16 @@ static void win_update(win_T *wp, DecorProviders *providers)
|
||||
wp->w_old_topfill = wp->w_topfill;
|
||||
wp->w_old_botfill = wp->w_botfill;
|
||||
|
||||
// Send win_extmarks if needed
|
||||
if (kv_size(win_extmark_arr) > 0) {
|
||||
for (size_t n = 0; n < kv_size(win_extmark_arr); n++) {
|
||||
ui_call_win_extmark(
|
||||
wp->w_grid_alloc.handle, wp->handle,
|
||||
kv_A(win_extmark_arr, n).ns_id, kv_A(win_extmark_arr, n).mark_id,
|
||||
kv_A(win_extmark_arr, n).win_row, kv_A(win_extmark_arr, n).win_col);
|
||||
}
|
||||
}
|
||||
|
||||
if (dollar_vcol == -1) {
|
||||
/*
|
||||
* There is a trick with w_botline. If we invalidate it on each
|
||||
@@ -1964,7 +1985,7 @@ static inline void provider_err_virt_text(linenr_T lnum, char *err)
|
||||
((VirtTextChunk){ .text = provider_err,
|
||||
.hl_id = hl_err }));
|
||||
err_decor.virt_text_width = mb_string2cells((char_u *)err);
|
||||
decor_add_ephemeral(lnum - 1, 0, lnum - 1, 0, &err_decor);
|
||||
decor_add_ephemeral(lnum - 1, 0, lnum - 1, 0, &err_decor, 0, 0);
|
||||
}
|
||||
|
||||
static inline void get_line_number_str(win_T *wp, linenr_T lnum, char_u *buf, size_t buf_len)
|
||||
@@ -2881,7 +2902,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
|
||||
&& vcol >= (long)wp->w_virtcol)
|
||||
|| (number_only && draw_state > WL_NR))
|
||||
&& filler_todo <= 0) {
|
||||
draw_virt_text(buf, win_col_offset, &col, grid->Columns);
|
||||
draw_virt_text(wp, buf, win_col_offset, &col, grid->Columns, row);
|
||||
grid_put_linebuf(grid, row, 0, col, -grid->Columns, wp->w_p_rl, wp,
|
||||
wp->w_hl_attr_normal, false);
|
||||
// Pretend we have finished updating the window. Except when
|
||||
@@ -3951,7 +3972,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
|
||||
}
|
||||
}
|
||||
|
||||
draw_virt_text(buf, win_col_offset, &col, grid->Columns);
|
||||
draw_virt_text(wp, buf, win_col_offset, &col, grid->Columns, row);
|
||||
grid_put_linebuf(grid, row, 0, col, grid->Columns, wp->w_p_rl, wp,
|
||||
wp->w_hl_attr_normal, false);
|
||||
row++;
|
||||
@@ -4195,7 +4216,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
|
||||
kHlModeReplace, grid->Columns, offset);
|
||||
}
|
||||
} else {
|
||||
draw_virt_text(buf, win_col_offset, &draw_col, grid->Columns);
|
||||
draw_virt_text(wp, buf, win_col_offset, &draw_col, grid->Columns, row);
|
||||
}
|
||||
|
||||
grid_put_linebuf(grid, row, 0, draw_col, grid->Columns, wp->w_p_rl,
|
||||
@@ -4274,14 +4295,15 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
|
||||
return row;
|
||||
}
|
||||
|
||||
void draw_virt_text(buf_T *buf, int col_off, int *end_col, int max_col)
|
||||
void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int max_col, int win_row)
|
||||
{
|
||||
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->start_row == state->row
|
||||
&& (kv_size(item->decor.virt_text) || item->decor.ui_watched))) {
|
||||
continue;
|
||||
}
|
||||
if (item->win_col == -1) {
|
||||
@@ -4297,9 +4319,17 @@ void draw_virt_text(buf_T *buf, int col_off, int *end_col, int max_col)
|
||||
if (item->win_col < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int col = draw_virt_text_item(buf, item->win_col, item->decor.virt_text,
|
||||
item->decor.hl_mode, max_col, item->win_col - col_off);
|
||||
int col;
|
||||
if (item->decor.ui_watched) {
|
||||
// send mark position to UI
|
||||
col = item->win_col;
|
||||
WinExtmark m = { item->ns_id, item->mark_id, win_row, col };
|
||||
kv_push(win_extmark_arr, m);
|
||||
}
|
||||
if (kv_size(item->decor.virt_text)) {
|
||||
col = draw_virt_text_item(buf, item->win_col, item->decor.virt_text,
|
||||
item->decor.hl_mode, max_col, item->win_col - col_off);
|
||||
}
|
||||
item->win_col = -2; // deactivate
|
||||
if (item->decor.virt_text_pos == kVTEndOfLine && do_eol) {
|
||||
state->eol_col = col + 1;
|
||||
|
Reference in New Issue
Block a user