feat(api): evaluate 'statuscolumn' with nvim_eval_statusline()

This commit is contained in:
Luuk van Baal 2023-04-01 14:58:52 +02:00
parent 9e7426718b
commit f0ac91c58b
13 changed files with 153 additions and 74 deletions

View File

@ -773,6 +773,8 @@ nvim_eval_statusline({str}, {*opts}) *nvim_eval_statusline()*
• use_tabline: (boolean) Evaluate tabline instead of
statusline. When true, {winid} is ignored. Mutually
exclusive with {use_winbar}.
• use_statuscol: (boolean) Evaluate statuscolumn instead of
statusline.
Return: ~
Dictionary containing statusline information, with these keys:

View File

@ -73,6 +73,9 @@ NEW FEATURES *news-features*
The following new APIs or features were added.
• |nvim_eval_statusline()| supports evaluating the |'statuscolumn'| through a
new `opts` field: `use_statuscol`.
• |nvim_buf_get_extmarks()| now accepts a -1 `ns_id` to request extmarks from
all namespaces and adds the namespace id to the details array.
Other missing properties have been added to the details array and marks can

View File

@ -97,6 +97,7 @@ return {
"highlights";
"use_winbar";
"use_tabline";
"use_statuscol";
}};
{ 'option', {
"scope";

View File

@ -959,12 +959,14 @@ bool set_mark(buf_T *buf, String name, Integer line, Integer col, Error *err)
}
/// Get default statusline highlight for window
const char *get_default_stl_hl(win_T *wp, bool use_winbar)
const char *get_default_stl_hl(win_T *wp, bool use_winbar, int stc_hl_id)
{
if (wp == NULL) {
return "TabLineFill";
} else if (use_winbar) {
return (wp == curwin) ? "WinBar" : "WinBarNC";
} else if (stc_hl_id > 0) {
return syn_id2name(stc_hl_id);
} else {
return (wp == curwin) ? "StatusLine" : "StatusLineNC";
}

View File

@ -25,6 +25,7 @@
#include "nvim/buffer.h"
#include "nvim/channel.h"
#include "nvim/context.h"
#include "nvim/cursor.h"
#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
@ -59,6 +60,7 @@
#include "nvim/popupmenu.h"
#include "nvim/pos.h"
#include "nvim/runtime.h"
#include "nvim/sign.h"
#include "nvim/state.h"
#include "nvim/statusline.h"
#include "nvim/strings.h"
@ -2054,6 +2056,7 @@ Array nvim_get_mark(String name, Dictionary opts, Error *err)
/// - use_winbar: (boolean) Evaluate winbar instead of statusline.
/// - use_tabline: (boolean) Evaluate tabline instead of statusline. When true, {winid}
/// is ignored. Mutually exclusive with {use_winbar}.
/// - use_statuscol: (boolean) Evaluate statuscolumn instead of statusline.
///
/// @param[out] err Error details, if any.
/// @return Dictionary containing statusline information, with these keys:
@ -2071,9 +2074,11 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
int maxwidth;
int fillchar = 0;
int use_bools = 0;
Window window = 0;
bool use_winbar = false;
bool use_tabline = false;
bool use_statuscol = false;
bool highlights = false;
if (str.size < 2 || memcmp(str.data, "%!", 2) != 0) {
@ -2111,23 +2116,34 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
}
if (HAS_KEY(opts->use_winbar)) {
use_winbar = api_object_to_bool(opts->use_winbar, "use_winbar", false, err);
use_bools++;
if (ERROR_SET(err)) {
return result;
}
}
if (HAS_KEY(opts->use_tabline)) {
use_tabline = api_object_to_bool(opts->use_tabline, "use_tabline", false, err);
use_bools++;
if (ERROR_SET(err)) {
return result;
}
}
VALIDATE(!(use_winbar && use_tabline), "%s", "Cannot use both 'use_winbar' and 'use_tabline'", {
if (HAS_KEY(opts->use_statuscol)) {
use_statuscol = api_object_to_bool(opts->use_statuscol, "use_statuscol", false, err);
use_bools++;
if (ERROR_SET(err)) {
return result;
}
}
VALIDATE(use_bools <= 1, "%s",
"Can only use one of 'use_winbar', 'use_tabline' and 'use_statuscol'", {
return result;
});
win_T *wp, *ewp;
int stc_hl_id = 0;
statuscol_T statuscol = { 0 };
SignTextAttrs sattrs[SIGN_SHOW_MAX] = { 0 };
if (use_tabline) {
wp = NULL;
@ -2149,6 +2165,31 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
fillchar = fillchar_status(&attr, wp);
}
}
if (use_statuscol) {
HlPriId line = { 0 };
HlPriId cul = { 0 };
HlPriId num = { 0 };
linenr_T lnum = (linenr_T)get_vim_var_nr(VV_LNUM);
int num_signs = buf_get_signattrs(wp->w_buffer, lnum, sattrs, &num, &line, &cul);
decor_redraw_signs(wp->w_buffer, lnum - 1, &num_signs, sattrs, &num, &line, &cul);
statuscol.sattrs = sattrs;
statuscol.foldinfo = fold_info(wp, lnum);
statuscol.use_cul = wp->w_p_cul && lnum == wp->w_cursorline
&& (wp->w_p_culopt_flags & CULOPT_NBR);
statuscol.sign_cul_id = statuscol.use_cul ? cul.hl_id : 0;
if (num.hl_id) {
stc_hl_id = num.hl_id;
} else if (statuscol.use_cul) {
stc_hl_id = HLF_CLN + 1;
} else if (wp->w_p_rnu) {
stc_hl_id = (lnum < wp->w_cursor.lnum ? HLF_LNA : HLF_LNB) + 1;
} else {
stc_hl_id = HLF_N + 1;
}
set_vim_var_nr(VV_RELNUM, labs(get_cursor_rel_lnum(wp, lnum)));
set_vim_var_nr(VV_VIRTNUM, 0);
}
}
if (HAS_KEY(opts->maxwidth)) {
@ -2158,12 +2199,12 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
maxwidth = (int)opts->maxwidth.data.integer;
} else {
maxwidth = (use_tabline || (!use_winbar && global_stl_height() > 0)) ? Columns : wp->w_width;
maxwidth = use_statuscol ? win_col_off(wp)
: (use_tabline || (!use_winbar && global_stl_height() > 0)) ? Columns : wp->w_width;
}
char buf[MAXPATHL];
stl_hlrec_t *hltab;
stl_hlrec_t **hltab_ptr = highlights ? &hltab : NULL;
// Temporarily reset 'cursorbind' to prevent side effects from moving the cursor away and back.
int p_crb_save = ewp->w_p_crb;
@ -2177,9 +2218,9 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
0,
fillchar,
maxwidth,
hltab_ptr,
highlights ? &hltab : NULL,
NULL,
NULL);
use_statuscol ? &statuscol : NULL);
PUT(result, "width", INTEGER_OBJ(width));
@ -2195,7 +2236,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
// add the default highlight at the beginning of the highlight list
if (hltab->start == NULL || (hltab->start - buf) != 0) {
Dictionary hl_info = ARRAY_DICT_INIT;
grpname = get_default_stl_hl(wp, use_winbar);
grpname = get_default_stl_hl(wp, use_winbar, stc_hl_id);
PUT(hl_info, "start", INTEGER_OBJ(0));
PUT(hl_info, "group", CSTR_TO_OBJ(grpname));
@ -2209,7 +2250,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
PUT(hl_info, "start", INTEGER_OBJ(sp->start - buf));
if (sp->userhl == 0) {
grpname = get_default_stl_hl(wp, use_winbar);
grpname = get_default_stl_hl(wp, use_winbar, stc_hl_id);
} else if (sp->userhl < 0) {
grpname = syn_id2name(-sp->userhl);
} else {

View File

@ -386,7 +386,7 @@ next_mark:
}
void decor_redraw_signs(buf_T *buf, int row, int *num_signs, SignTextAttrs sattrs[],
HlPriAttr *num_attrs, HlPriAttr *line_attrs, HlPriAttr *cul_attrs)
HlPriId *num_id, HlPriId *line_id, HlPriId *cul_id)
{
if (!buf->b_signs) {
return;
@ -422,23 +422,23 @@ void decor_redraw_signs(buf_T *buf, int row, int *num_signs, SignTextAttrs sattr
if (j < SIGN_SHOW_MAX) {
sattrs[j] = (SignTextAttrs) {
.text = decor->sign_text,
.hl_attr_id = decor->sign_hl_id == 0 ? 0 : syn_id2attr(decor->sign_hl_id),
.hl_id = decor->sign_hl_id,
.priority = decor->priority
};
(*num_signs)++;
}
}
struct { HlPriAttr *dest; int hl; } cattrs[] = {
{ line_attrs, decor->line_hl_id },
{ num_attrs, decor->number_hl_id },
{ cul_attrs, decor->cursorline_hl_id },
struct { HlPriId *dest; int hl; } cattrs[] = {
{ line_id, decor->line_hl_id },
{ num_id, decor->number_hl_id },
{ cul_id, decor->cursorline_hl_id },
{ NULL, -1 },
};
for (int i = 0; cattrs[i].dest; i++) {
if (cattrs[i].hl != 0 && decor->priority >= cattrs[i].dest->priority) {
*cattrs[i].dest = (HlPriAttr) {
.attr_id = syn_id2attr(cattrs[i].hl),
*cattrs[i].dest = (HlPriId) {
.hl_id = cattrs[i].hl,
.priority = decor->priority
};
}

View File

@ -508,31 +508,12 @@ static void get_sign_display_info(bool nrcol, win_T *wp, winlinevars_T *wlv, int
if (use_cursor_line_highlight(wp, wlv->lnum) && sign_cul_attr > 0) {
wlv->char_attr = sign_cul_attr;
} else {
wlv->char_attr = sattr->hl_attr_id;
wlv->char_attr = sattr->hl_id ? syn_id2attr(sattr->hl_id) : 0;
}
}
}
}
static int get_sign_attrs(buf_T *buf, winlinevars_T *wlv, int *sign_num_attrp, int *sign_cul_attrp)
{
HlPriAttr line_attrs = { wlv->line_attr, 0 };
HlPriAttr num_attrs = { *sign_num_attrp, 0 };
HlPriAttr cul_attrs = { *sign_cul_attrp, 0 };
// TODO(bfredl, vigoux): line_attr should not take priority over decoration!
int num_signs = buf_get_signattrs(buf, wlv->lnum, wlv->sattrs, &num_attrs, &line_attrs,
&cul_attrs);
decor_redraw_signs(buf, wlv->lnum - 1, &num_signs, wlv->sattrs, &num_attrs, &line_attrs,
&cul_attrs);
wlv->line_attr = line_attrs.attr_id;
*sign_num_attrp = num_attrs.attr_id;
*sign_cul_attrp = cul_attrs.attr_id;
return num_signs;
}
/// Returns width of the signcolumn that should be used for the whole window
///
/// @param wp window we want signcolumn width from
@ -726,7 +707,7 @@ static void get_statuscol_display_info(statuscol_T *stcp, winlinevars_T *wlv)
if (stcp->textp + wlv->n_extra < stcp->text_end) {
int hl = stcp->hlrecp->userhl;
stcp->textp = stcp->hlrecp->start;
stcp->cur_attr = hl < 0 ? syn_id2attr(-hl) : hl > 0 ? hl : stcp->num_attr;
stcp->cur_attr = hl < 0 ? syn_id2attr(-hl) : stcp->num_attr;
stcp->hlrecp++;
wlv->draw_state = WL_STC - 1;
}
@ -1288,9 +1269,37 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
area_highlighting = true;
}
int sign_num_attr = 0; // sign attribute for the number column
int sign_cul_attr = 0; // sign attribute for cursorline
int num_signs = get_sign_attrs(buf, &wlv, &sign_num_attr, &sign_cul_attr);
HlPriId line_id = { 0 };
HlPriId sign_cul = { 0 };
HlPriId sign_num = { 0 };
// TODO(bfredl, vigoux): line_attr should not take priority over decoration!
int num_signs = buf_get_signattrs(buf, wlv.lnum, wlv.sattrs, &sign_num, &line_id, &sign_cul);
decor_redraw_signs(buf, wlv.lnum - 1, &num_signs, wlv.sattrs, &sign_num, &line_id, &sign_cul);
int sign_cul_attr = 0;
int sign_num_attr = 0;
statuscol_T statuscol = { 0 };
if (*wp->w_p_stc != NUL) {
// Draw the 'statuscolumn' if option is set.
statuscol.draw = true;
statuscol.sattrs = wlv.sattrs;
statuscol.foldinfo = foldinfo;
statuscol.width = win_col_off(wp) - (cmdwin_type != 0 && wp == curwin);
statuscol.use_cul = use_cursor_line_highlight(wp, lnum);
statuscol.sign_cul_id = statuscol.use_cul ? sign_cul.hl_id : 0;
statuscol.num_attr = sign_num.hl_id ? syn_id2attr(sign_num.hl_id)
: get_line_number_attr(wp, &wlv);
} else {
if (sign_cul.hl_id > 0) {
sign_cul_attr = syn_id2attr(sign_cul.hl_id);
}
if (sign_num.hl_id > 0) {
sign_num_attr = syn_id2attr(sign_num.hl_id);
}
}
if (line_id.hl_id > 0) {
wlv.line_attr = syn_id2attr(line_id.hl_id);
}
// Highlight the current line in the quickfix window.
if (bt_quickfix(wp->w_buffer) && qf_current_entry(wp) == lnum) {
@ -1495,18 +1504,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
extra_check = true;
}
statuscol_T statuscol = { 0 };
if (*wp->w_p_stc != NUL) {
// Draw the 'statuscolumn' if option is set.
statuscol.draw = true;
statuscol.sattrs = wlv.sattrs;
statuscol.foldinfo = foldinfo;
statuscol.width = win_col_off(wp) - (cmdwin_type != 0 && wp == curwin);
statuscol.use_cul = use_cursor_line_highlight(wp, lnum);
statuscol.sign_cul_attr = statuscol.use_cul ? sign_cul_attr : 0;
statuscol.num_attr = sign_num_attr ? sign_num_attr : get_line_number_attr(wp, &wlv);
}
int sign_idx = 0;
int virt_line_index;
int virt_line_offset = -1;

View File

@ -247,8 +247,8 @@ typedef struct {
/// highlight attributes with associated priorities
typedef struct {
int attr_id;
int hl_id;
int priority;
} HlPriAttr;
} HlPriId;
#endif // NVIM_HIGHLIGHT_DEFS_H

View File

@ -491,8 +491,8 @@ SignTextAttrs *sign_get_attr(int idx, SignTextAttrs sattrs[], int max_signs)
/// @param lnum Line in which to search
/// @param sattrs Output array for attrs
/// @return Number of signs of which attrs were found
int buf_get_signattrs(buf_T *buf, linenr_T lnum, SignTextAttrs sattrs[], HlPriAttr *num_attrs,
HlPriAttr *line_attrs, HlPriAttr *cul_attrs)
int buf_get_signattrs(buf_T *buf, linenr_T lnum, SignTextAttrs sattrs[], HlPriId *num_id,
HlPriId *line_id, HlPriId *cul_id)
{
sign_entry_T *sign;
@ -517,21 +517,21 @@ int buf_get_signattrs(buf_T *buf, linenr_T lnum, SignTextAttrs sattrs[], HlPriAt
if (sp->sn_text != NULL && sattr_matches < SIGN_SHOW_MAX) {
sattrs[sattr_matches++] = (SignTextAttrs) {
.text = sp->sn_text,
.hl_attr_id = sp->sn_text_hl == 0 ? 0 : syn_id2attr(sp->sn_text_hl),
.hl_id = sp->sn_text_hl,
.priority = sign->se_priority
};
}
struct { HlPriAttr *dest; int hl; } cattrs[] = {
{ line_attrs, sp->sn_line_hl },
{ num_attrs, sp->sn_num_hl },
{ cul_attrs, sp->sn_cul_hl },
struct { HlPriId *dest; int hl; } cattrs[] = {
{ line_id, sp->sn_line_hl },
{ num_id, sp->sn_num_hl },
{ cul_id, sp->sn_cul_hl },
{ NULL, -1 },
};
for (int i = 0; cattrs[i].dest; i++) {
if (cattrs[i].hl != 0 && sign->se_priority >= cattrs[i].dest->priority) {
*cattrs[i].dest = (HlPriAttr) {
.attr_id = syn_id2attr(cattrs[i].hl),
*cattrs[i].dest = (HlPriId) {
.hl_id = cattrs[i].hl,
.priority = sign->se_priority
};
}

View File

@ -35,7 +35,7 @@ struct sign_entry {
/// Sign attributes. Used by the screen refresh routines.
typedef struct {
char *text;
int hl_attr_id;
int hl_id;
int priority;
} SignTextAttrs;

View File

@ -1512,7 +1512,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n
case STL_LINE:
// Overload %l with v:lnum for 'statuscolumn'
if (opt_name != NULL && strcmp(opt_name, "statuscolumn") == 0) {
if (stcp != NULL) {
if (wp->w_p_nu && !get_vim_var_nr(VV_VIRTNUM)) {
num = (int)get_vim_var_nr(VV_LNUM);
}
@ -1617,7 +1617,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n
case STL_ROFLAG:
case STL_ROFLAG_ALT:
// Overload %r with v:relnum for 'statuscolumn'
if (opt_name != NULL && strcmp(opt_name, "statuscolumn") == 0) {
if (stcp != NULL) {
if (wp->w_p_rnu && !get_vim_var_nr(VV_VIRTNUM)) {
num = (int)get_vim_var_nr(VV_RELNUM);
}
@ -1652,7 +1652,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n
char *p;
if (fold) {
size_t n = fill_foldcolumn(out_p, wp, stcp->foldinfo, (linenr_T)get_vim_var_nr(VV_LNUM));
stl_items[curitem].minwid = win_hl_attr(wp, stcp->use_cul ? HLF_CLF : HLF_FC);
stl_items[curitem].minwid = -((stcp->use_cul ? HLF_CLF : HLF_FC) + 1);
p = out_p;
p[n] = NUL;
}
@ -1668,9 +1668,8 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n
} else if (!fold) {
SignTextAttrs *sattr = virtnum ? NULL : sign_get_attr(i, stcp->sattrs, wp->w_scwidth);
p = sattr && sattr->text ? sattr->text : " ";
stl_items[curitem].minwid = sattr ? stcp->sign_cul_attr ? stcp->sign_cul_attr
: sattr->hl_attr_id
: win_hl_attr(wp, stcp->use_cul ? HLF_CLS : HLF_SC);
stl_items[curitem].minwid = -(sattr ? stcp->sign_cul_id ? stcp->sign_cul_id
: sattr->hl_id : (stcp->use_cul ? HLF_CLS : HLF_SC) + 1);
}
stl_items[curitem].type = Highlight;
stl_items[curitem].start = out_p + strlen(buf_tmp);

View File

@ -62,8 +62,8 @@ typedef struct statuscol statuscol_T;
struct statuscol {
int width; ///< width of the status column
int cur_attr; ///< current attributes in text
int num_attr; ///< attributes used for line number
int sign_cul_attr; ///< cursorline sign attr
int num_attr; ///< default highlight attr
int sign_cul_id; ///< cursorline sign highlight id
int truncate; ///< truncated width
bool draw; ///< whether to draw the statuscolumn
bool use_cul; ///< whether to use cursorline attrs

View File

@ -3421,6 +3421,40 @@ describe('API', function()
'TextWithNoHighlight%#WarningMsg#TextWithWarningHighlight',
{ use_winbar = true, highlights = true }))
end)
it('works with statuscolumn', function()
command([[
let &stc='%C%s%=%l '
set cul nu nuw=3 scl=yes:2 fdc=2
call setline(1, repeat(['aaaaa'], 5))
let g:ns = nvim_create_namespace('')
call sign_define('a', {'text':'aa', 'texthl':'IncSearch', 'numhl':'Normal'})
call sign_place(2, 1, 'a', bufnr(), {'lnum':4})
call nvim_buf_set_extmark(0, g:ns, 3, 1, { 'sign_text':'bb', 'sign_hl_group':'ErrorMsg' })
1,5fold | 1,5 fold | foldopen!
]])
command('norm 4G')
command('let v:lnum=4')
eq({
str = '││aabb 4 ',
width = 9,
highlights = {
{ group = 'CursorLineFold', start = 0 },
{ group = 'Normal', start = 6 },
{ group = 'IncSearch', start = 6 },
{ group = 'ErrorMsg', start = 8 },
{ group = 'Normal', start = 10 }
}
}, meths.eval_statusline('%C%s%=%l ', { use_statuscol = true, highlights = true }))
command('let v:lnum=3')
eq({
str = '3 ' ,
width = 2,
highlights = {
{ group = 'LineNr', start = 0 },
{ group = 'ErrorMsg', start = 1 }
}
}, meths.eval_statusline('%l%#ErrorMsg# ', { use_statuscol = true, highlights = true }))
end)
it('no memory leak with click functions', function()
meths.eval_statusline('%@ClickFunc@StatusLineStringWithClickFunc%T', {})
eq({