mirror of
https://github.com/neovim/neovim.git
synced 2024-12-19 18:55:14 -07:00
feat(api): evaluate statusline string #16020
Adds API function `nvim_eval_statusline` to allow evaluating a statusline string and obtaining information regarding it. Closes https://github.com/neovim/neovim/issues/15849
This commit is contained in:
parent
e7ea54a3df
commit
9086938f7b
@ -51,5 +51,12 @@ return {
|
||||
runtime = {
|
||||
"is_lua";
|
||||
};
|
||||
eval_statusline = {
|
||||
"winid";
|
||||
"maxwidth";
|
||||
"fillchar";
|
||||
"highlights";
|
||||
"use_tabline";
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1681,3 +1681,15 @@ bool set_mark(buf_T *buf, String name, Integer line, Integer col, Error *err)
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Get default statusline highlight for window
|
||||
const char *get_default_stl_hl(win_T *wp)
|
||||
{
|
||||
if (wp == NULL) {
|
||||
return "TabLineFill";
|
||||
} else if (wp == curwin) {
|
||||
return "StatusLine";
|
||||
} else {
|
||||
return "StatusLineNC";
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "nvim/api/window.h"
|
||||
#include "nvim/ascii.h"
|
||||
#include "nvim/buffer.h"
|
||||
#include "nvim/buffer_defs.h"
|
||||
#include "nvim/context.h"
|
||||
#include "nvim/decoration.h"
|
||||
#include "nvim/edit.h"
|
||||
@ -2906,3 +2907,165 @@ Array nvim_get_mark(String name, Error *err)
|
||||
return rv;
|
||||
}
|
||||
|
||||
/// Evaluates statusline string.
|
||||
///
|
||||
/// @param str Statusline string (see 'statusline').
|
||||
/// @param opts Optional parameters.
|
||||
/// - winid: (number) |window-ID| of the window to use as context for statusline.
|
||||
/// - maxwidth: (number) Maximum width of statusline.
|
||||
/// - fillchar: (string) Character to fill blank spaces in the statusline (see
|
||||
/// 'fillchars').
|
||||
/// - highlights: (boolean) Return highlight information.
|
||||
/// - use_tabline: (boolean) Evaluate tabline instead of statusline. When |TRUE|, {winid}
|
||||
/// is ignored.
|
||||
///
|
||||
/// @param[out] err Error details, if any.
|
||||
/// @return Dictionary containing statusline information, with these keys:
|
||||
/// - str: (string) Characters that will be displayed on the statusline.
|
||||
/// - width: (number) Display width of the statusline.
|
||||
/// - highlights: Array containing highlight information of the statusline. Only included when
|
||||
/// the "highlights" key in {opts} is |TRUE|. Each element of the array is a
|
||||
/// |Dictionary| with these keys:
|
||||
/// - start: (number) Byte index (0-based) of first character that uses the highlight.
|
||||
/// - group: (string) Name of highlight group.
|
||||
Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *err)
|
||||
FUNC_API_SINCE(8) FUNC_API_FAST
|
||||
{
|
||||
Dictionary result = ARRAY_DICT_INIT;
|
||||
|
||||
Window window = 0;
|
||||
int maxwidth = 0;
|
||||
char fillchar = 0;
|
||||
bool use_tabline = false;
|
||||
bool highlights = false;
|
||||
|
||||
if (HAS_KEY(opts->winid)) {
|
||||
if (opts->winid.type != kObjectTypeInteger) {
|
||||
api_set_error(err, kErrorTypeValidation, "winid must be an integer");
|
||||
return result;
|
||||
}
|
||||
|
||||
window = (Window)opts->winid.data.integer;
|
||||
}
|
||||
|
||||
if (HAS_KEY(opts->maxwidth)) {
|
||||
if (opts->maxwidth.type != kObjectTypeInteger) {
|
||||
api_set_error(err, kErrorTypeValidation, "maxwidth must be an integer");
|
||||
return result;
|
||||
}
|
||||
|
||||
maxwidth = (int)opts->maxwidth.data.integer;
|
||||
}
|
||||
|
||||
if (HAS_KEY(opts->fillchar)) {
|
||||
if (opts->fillchar.type != kObjectTypeString || opts->fillchar.data.string.size > 1) {
|
||||
api_set_error(err, kErrorTypeValidation, "fillchar must be an ASCII character");
|
||||
return result;
|
||||
}
|
||||
|
||||
fillchar = opts->fillchar.data.string.data[0];
|
||||
}
|
||||
|
||||
if (HAS_KEY(opts->highlights)) {
|
||||
highlights = api_object_to_bool(opts->highlights, "highlights", false, err);
|
||||
|
||||
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);
|
||||
|
||||
if (ERROR_SET(err)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
win_T *wp, *ewp;
|
||||
|
||||
if (use_tabline) {
|
||||
wp = NULL;
|
||||
ewp = curwin;
|
||||
fillchar = ' ';
|
||||
} else {
|
||||
wp = find_window_by_handle(window, err);
|
||||
ewp = wp;
|
||||
|
||||
if (fillchar == 0) {
|
||||
int attr;
|
||||
fillchar = (char)fillchar_status(&attr, wp);
|
||||
}
|
||||
}
|
||||
|
||||
if (maxwidth == 0) {
|
||||
maxwidth = use_tabline ? 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;
|
||||
ewp->w_p_crb = false;
|
||||
|
||||
int width = build_stl_str_hl(
|
||||
ewp,
|
||||
(char_u *)buf,
|
||||
sizeof(buf),
|
||||
(char_u *)str.data,
|
||||
false,
|
||||
(char_u)fillchar,
|
||||
maxwidth,
|
||||
hltab_ptr,
|
||||
NULL);
|
||||
|
||||
PUT(result, "width", INTEGER_OBJ(width));
|
||||
|
||||
// Restore original value of 'cursorbind'
|
||||
ewp->w_p_crb = p_crb_save;
|
||||
|
||||
if (highlights) {
|
||||
Array hl_values = ARRAY_DICT_INIT;
|
||||
const char *grpname;
|
||||
char user_group[6];
|
||||
|
||||
// If first character doesn't have a defined highlight,
|
||||
// add the default highlight at the beginning of the highlight list
|
||||
if (hltab->start == NULL || ((char *)hltab->start - buf) != 0) {
|
||||
Dictionary hl_info = ARRAY_DICT_INIT;
|
||||
grpname = get_default_stl_hl(wp);
|
||||
|
||||
PUT(hl_info, "start", INTEGER_OBJ(0));
|
||||
PUT(hl_info, "group", CSTR_TO_OBJ(grpname));
|
||||
|
||||
ADD(hl_values, DICTIONARY_OBJ(hl_info));
|
||||
}
|
||||
|
||||
for (stl_hlrec_t *sp = hltab; sp->start != NULL; sp++) {
|
||||
Dictionary hl_info = ARRAY_DICT_INIT;
|
||||
|
||||
PUT(hl_info, "start", INTEGER_OBJ((char *)sp->start - buf));
|
||||
|
||||
if (sp->userhl == 0) {
|
||||
grpname = get_default_stl_hl(wp);
|
||||
} else if (sp->userhl < 0) {
|
||||
grpname = (char *)syn_id2name(-sp->userhl);
|
||||
} else {
|
||||
snprintf(user_group, sizeof(user_group), "User%d", sp->userhl);
|
||||
grpname = user_group;
|
||||
}
|
||||
|
||||
PUT(hl_info, "group", CSTR_TO_OBJ(grpname));
|
||||
|
||||
ADD(hl_values, DICTIONARY_OBJ(hl_info));
|
||||
}
|
||||
|
||||
PUT(result, "highlights", ARRAY_OBJ(hl_values));
|
||||
}
|
||||
|
||||
PUT(result, "str", CSTR_TO_OBJ((char *)buf));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -7379,7 +7379,7 @@ void get_trans_bufname(buf_T *buf)
|
||||
/*
|
||||
* Get the character to use in a status line. Get its attributes in "*attr".
|
||||
*/
|
||||
static int fillchar_status(int *attr, win_T *wp)
|
||||
int fillchar_status(int *attr, win_T *wp)
|
||||
{
|
||||
int fill;
|
||||
bool is_curwin = (wp == curwin);
|
||||
|
@ -2367,4 +2367,79 @@ describe('API', function()
|
||||
eq({2, 2, buf.id, mark[4]}, mark)
|
||||
end)
|
||||
end)
|
||||
describe('nvim_eval_statusline', function()
|
||||
it('works', function()
|
||||
eq({
|
||||
str = '%StatusLineStringWithHighlights',
|
||||
width = 31
|
||||
},
|
||||
meths.eval_statusline(
|
||||
'%%StatusLineString%#WarningMsg#WithHighlights',
|
||||
{}))
|
||||
end)
|
||||
it('doesn\'t exceed maxwidth', function()
|
||||
eq({
|
||||
str = 'Should be trun>',
|
||||
width = 15
|
||||
},
|
||||
meths.eval_statusline(
|
||||
'Should be truncated%<',
|
||||
{ maxwidth = 15 }))
|
||||
end)
|
||||
describe('highlight parsing', function()
|
||||
it('works', function()
|
||||
eq({
|
||||
str = "TextWithWarningHighlightTextWithUserHighlight",
|
||||
width = 45,
|
||||
highlights = {
|
||||
{ start = 0, group = 'WarningMsg' },
|
||||
{ start = 24, group = 'User1' }
|
||||
},
|
||||
},
|
||||
meths.eval_statusline(
|
||||
'%#WarningMsg#TextWithWarningHighlight%1*TextWithUserHighlight',
|
||||
{ highlights = true }))
|
||||
end)
|
||||
it('works with no highlight', function()
|
||||
eq({
|
||||
str = "TextWithNoHighlight",
|
||||
width = 19,
|
||||
highlights = {
|
||||
{ start = 0, group = 'StatusLine' },
|
||||
},
|
||||
},
|
||||
meths.eval_statusline(
|
||||
'TextWithNoHighlight',
|
||||
{ highlights = true }))
|
||||
end)
|
||||
it('works with inactive statusline', function()
|
||||
command('split')
|
||||
|
||||
eq({
|
||||
str = 'TextWithNoHighlightTextWithWarningHighlight',
|
||||
width = 43,
|
||||
highlights = {
|
||||
{ start = 0, group = 'StatusLineNC' },
|
||||
{ start = 19, group = 'WarningMsg' }
|
||||
}
|
||||
},
|
||||
meths.eval_statusline(
|
||||
'TextWithNoHighlight%#WarningMsg#TextWithWarningHighlight',
|
||||
{ winid = meths.list_wins()[2].id, highlights = true }))
|
||||
end)
|
||||
it('works with tabline', function()
|
||||
eq({
|
||||
str = 'TextWithNoHighlightTextWithWarningHighlight',
|
||||
width = 43,
|
||||
highlights = {
|
||||
{ start = 0, group = 'TabLineFill' },
|
||||
{ start = 19, group = 'WarningMsg' }
|
||||
}
|
||||
},
|
||||
meths.eval_statusline(
|
||||
'TextWithNoHighlight%#WarningMsg#TextWithWarningHighlight',
|
||||
{ use_tabline = true, highlights = true }))
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
Loading…
Reference in New Issue
Block a user