mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
refactor(drawline): remove LineDrawState and wlv->saved_n_extra
We do not need an enum to keep track of what place in win_line() we currently are at. We already have a variable which keeps track where in the code we currently are (and thus what part of the line we are currently rendering), it is called the _program counter_. When we need non-linear or self-referential control-flow anyway for a laugh, we have a mechanism for that, it is called _function calls_. Do not "save" and "restore" the wlv->n_extra state every time the columns are to be drawn. This sort of thing needs to go away. Instead of setting the n_extra variables and then going to the outer while loop, the text in the columns can be rendered by just simply putting the text into the cells of the screen line, right away. Even in nvim this can be tricky sometimes, luckily we can use function calls to abstract this logic, which means that this handy data structure called the _call stack_ is handling saving away state temporarily, and restoring it back when we need it again. Lastly, but not least, as we now have direct control how signs are rendered, these can be stored as schar_T[2] and be directly put on screen as such.
This commit is contained in:
parent
720a3518e3
commit
66ac327db2
@ -665,9 +665,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
||||
}
|
||||
|
||||
if (HAS_KEY(opts, set_extmark, sign_text)) {
|
||||
sign.text.ptr = NULL;
|
||||
VALIDATE_S(init_sign_text(NULL, &sign.text.ptr, opts->sign_text.data),
|
||||
"sign_text", "", {
|
||||
sign.text[0] = 0;
|
||||
VALIDATE_S(init_sign_text(NULL, sign.text, opts->sign_text.data), "sign_text", "", {
|
||||
goto error;
|
||||
});
|
||||
sign.flags |= kSHIsSign;
|
||||
@ -785,7 +784,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
||||
uint32_t decor_indexed = DECOR_ID_INVALID;
|
||||
if (sign.flags & kSHIsSign) {
|
||||
decor_indexed = decor_put_sh(sign);
|
||||
if (sign.text.ptr != NULL) {
|
||||
if (sign.text[0]) {
|
||||
decor_flags |= MT_FLAG_DECOR_SIGNTEXT;
|
||||
}
|
||||
if (sign.number_hl_id || sign.line_hl_id || sign.cursorline_hl_id) {
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "nvim/api/extmark.h"
|
||||
#include "nvim/api/private/defs.h"
|
||||
#include "nvim/api/private/helpers.h"
|
||||
#include "nvim/ascii_defs.h"
|
||||
#include "nvim/buffer_defs.h"
|
||||
#include "nvim/decoration.h"
|
||||
#include "nvim/drawscreen.h"
|
||||
@ -20,6 +21,7 @@
|
||||
#include "nvim/move.h"
|
||||
#include "nvim/option_vars.h"
|
||||
#include "nvim/pos_defs.h"
|
||||
#include "nvim/sign.h"
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "decoration.c.generated.h"
|
||||
@ -159,7 +161,7 @@ DecorSignHighlight decor_sh_from_inline(DecorHighlightInline item)
|
||||
DecorSignHighlight conv = {
|
||||
.flags = item.flags,
|
||||
.priority = item.priority,
|
||||
.text.sc[0] = item.conceal_char,
|
||||
.text[0] = item.conceal_char,
|
||||
.hl_id = item.hl_id,
|
||||
.number_hl_id = 0,
|
||||
.line_hl_id = 0,
|
||||
@ -208,7 +210,7 @@ void buf_put_decor_sh(buf_T *buf, DecorSignHighlight *sh, int row1, int row2)
|
||||
if (sh->flags & kSHIsSign) {
|
||||
sh->sign_add_id = sign_add_id++;
|
||||
buf->b_signs++;
|
||||
if (sh->text.ptr) {
|
||||
if (sh->text[0]) {
|
||||
buf->b_signs_with_text++;
|
||||
buf_signcols_invalidate_range(buf, row1, row2, 1);
|
||||
}
|
||||
@ -254,7 +256,7 @@ void buf_remove_decor_sh(buf_T *buf, int row1, int row2, DecorSignHighlight *sh)
|
||||
if (sh->flags & kSHIsSign) {
|
||||
assert(buf->b_signs > 0);
|
||||
buf->b_signs--;
|
||||
if (sh->text.ptr) {
|
||||
if (sh->text[0]) {
|
||||
assert(buf->b_signs_with_text > 0);
|
||||
buf->b_signs_with_text--;
|
||||
if (row2 >= row1) {
|
||||
@ -312,9 +314,6 @@ void decor_free_inner(DecorVirtText *vt, uint32_t first_idx)
|
||||
uint32_t idx = first_idx;
|
||||
while (idx != DECOR_ID_INVALID) {
|
||||
DecorSignHighlight *sh = &kv_A(decor_items, idx);
|
||||
if (sh->flags & kSHIsSign) {
|
||||
xfree(sh->text.ptr);
|
||||
}
|
||||
if (sh->flags & kSHIsSign) {
|
||||
xfree(sh->sign_name);
|
||||
}
|
||||
@ -363,8 +362,11 @@ void decor_check_invalid_glyphs(void)
|
||||
{
|
||||
for (size_t i = 0; i < kv_size(decor_items); i++) {
|
||||
DecorSignHighlight *it = &kv_A(decor_items, i);
|
||||
if ((it->flags & kSHConceal) && schar_high(it->text.sc[0])) {
|
||||
it->text.sc[0] = schar_from_char(schar_get_first_codepoint(it->text.sc[0]));
|
||||
int width = (it->flags & kSHIsSign) ? SIGN_WIDTH : ((it->flags & kSHConceal) ? 1 : 0);
|
||||
for (int j = 0; j < width; j++) {
|
||||
if (schar_high(it->text[j])) {
|
||||
it->text[j] = schar_from_char(schar_get_first_codepoint(it->text[j]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -662,7 +664,7 @@ next_mark:
|
||||
if (item.start_row == state->row && item.start_col == col) {
|
||||
DecorSignHighlight *sh = &item.data.sh;
|
||||
conceal = 2;
|
||||
conceal_char = sh->text.sc[0];
|
||||
conceal_char = sh->text[0];
|
||||
state->col_until = MIN(state->col_until, item.start_col);
|
||||
conceal_attr = item.attr_id;
|
||||
}
|
||||
@ -730,7 +732,7 @@ void decor_redraw_signs(win_T *wp, buf_T *buf, int row, SignTextAttrs sattrs[],
|
||||
while (marktree_itr_step_overlap(buf->b_marktree, itr, &pair)) {
|
||||
if (!mt_invalid(pair.start) && mt_decor_sign(pair.start)) {
|
||||
DecorSignHighlight *sh = decor_find_sign(mt_decor(pair.start));
|
||||
num_text += (sh->text.ptr != NULL);
|
||||
num_text += (sh->text[0] != NUL);
|
||||
kv_push(signs, ((SignItem){ sh, pair.start.id }));
|
||||
}
|
||||
}
|
||||
@ -742,7 +744,7 @@ void decor_redraw_signs(win_T *wp, buf_T *buf, int row, SignTextAttrs sattrs[],
|
||||
}
|
||||
if (!mt_end(mark) && !mt_invalid(mark) && mt_decor_sign(mark)) {
|
||||
DecorSignHighlight *sh = decor_find_sign(mt_decor(mark));
|
||||
num_text += (sh->text.ptr != NULL);
|
||||
num_text += (sh->text[0] != NUL);
|
||||
kv_push(signs, ((SignItem){ sh, mark.id }));
|
||||
}
|
||||
|
||||
@ -756,8 +758,8 @@ void decor_redraw_signs(win_T *wp, buf_T *buf, int row, SignTextAttrs sattrs[],
|
||||
|
||||
for (size_t i = 0; i < kv_size(signs); i++) {
|
||||
DecorSignHighlight *sh = kv_A(signs, i).sh;
|
||||
if (idx >= 0 && sh->text.ptr) {
|
||||
sattrs[idx].text = sh->text.ptr;
|
||||
if (idx >= 0 && sh->text[0]) {
|
||||
memcpy(sattrs[idx].text, sh->text, SIGN_WIDTH * sizeof(sattr_T));
|
||||
sattrs[idx--].hl_id = sh->hl_id;
|
||||
}
|
||||
if (*num_id == 0) {
|
||||
@ -1034,7 +1036,7 @@ void decor_to_dict_legacy(Dictionary *dict, DecorInline decor, bool hl_name)
|
||||
PUT(*dict, "hl_eol", BOOLEAN_OBJ(sh_hl.flags & kSHHlEol));
|
||||
if (sh_hl.flags & kSHConceal) {
|
||||
char buf[MAX_SCHAR_SIZE];
|
||||
schar_get(buf, sh_hl.text.sc[0]);
|
||||
schar_get(buf, sh_hl.text[0]);
|
||||
PUT(*dict, "conceal", CSTR_TO_OBJ(buf));
|
||||
}
|
||||
|
||||
@ -1082,8 +1084,10 @@ void decor_to_dict_legacy(Dictionary *dict, DecorInline decor, bool hl_name)
|
||||
}
|
||||
|
||||
if (sh_sign.flags & kSHIsSign) {
|
||||
if (sh_sign.text.ptr) {
|
||||
PUT(*dict, "sign_text", CSTR_TO_OBJ(sh_sign.text.ptr));
|
||||
if (sh_sign.text[0]) {
|
||||
char buf[SIGN_WIDTH * MAX_SCHAR_SIZE];
|
||||
describe_sign_text(buf, sh_sign.text);
|
||||
PUT(*dict, "sign_text", CSTR_TO_OBJ(buf));
|
||||
}
|
||||
|
||||
if (sh_sign.sign_name) {
|
||||
|
@ -59,11 +59,7 @@ typedef struct {
|
||||
uint16_t flags;
|
||||
DecorPriority priority;
|
||||
int hl_id; // if sign: highlight of sign text
|
||||
// TODO(bfredl): Later signs should use sc[2] as well.
|
||||
union {
|
||||
char *ptr; // sign
|
||||
schar_T sc[2]; // conceal text (only sc[0] used)
|
||||
} text;
|
||||
schar_T text[SIGN_WIDTH]; // conceal text only uses text[0]
|
||||
// NOTE: if more functionality is added to a Highlight these should be overloaded
|
||||
// or restructured
|
||||
char *sign_name;
|
||||
@ -74,7 +70,7 @@ typedef struct {
|
||||
uint32_t next;
|
||||
} DecorSignHighlight;
|
||||
|
||||
#define DECOR_SIGN_HIGHLIGHT_INIT { 0, DECOR_PRIORITY_BASE, 0, { .ptr = NULL }, NULL, 0, 0, 0, 0, \
|
||||
#define DECOR_SIGN_HIGHLIGHT_INIT { 0, DECOR_PRIORITY_BASE, 0, { 0, 0 }, NULL, 0, 0, 0, 0, \
|
||||
DECOR_ID_INVALID }
|
||||
|
||||
enum {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -27,6 +27,7 @@
|
||||
#include "nvim/fold.h"
|
||||
#include "nvim/gettext.h"
|
||||
#include "nvim/globals.h"
|
||||
#include "nvim/grid.h"
|
||||
#include "nvim/highlight.h"
|
||||
#include "nvim/highlight_group.h"
|
||||
#include "nvim/macros_defs.h"
|
||||
@ -105,7 +106,7 @@ static void buf_set_sign(buf_T *buf, uint32_t *id, char *group, int prio, linenr
|
||||
DecorSignHighlight sign = DECOR_SIGN_HIGHLIGHT_INIT;
|
||||
|
||||
sign.flags |= kSHIsSign;
|
||||
sign.text.ptr = sp->sn_text ? xstrdup(sp->sn_text) : NULL;
|
||||
memcpy(sign.text, sp->sn_text, SIGN_WIDTH * sizeof(schar_T));
|
||||
sign.sign_name = xstrdup(sp->sn_name);
|
||||
sign.hl_id = sp->sn_text_hl;
|
||||
sign.line_hl_id = sp->sn_line_hl;
|
||||
@ -114,7 +115,7 @@ static void buf_set_sign(buf_T *buf, uint32_t *id, char *group, int prio, linenr
|
||||
sign.priority = (DecorPriority)prio;
|
||||
|
||||
bool has_hl = (sp->sn_line_hl || sp->sn_num_hl || sp->sn_cul_hl);
|
||||
uint16_t decor_flags = (sp->sn_text ? MT_FLAG_DECOR_SIGNTEXT : 0)
|
||||
uint16_t decor_flags = (sp->sn_text[0] ? MT_FLAG_DECOR_SIGNTEXT : 0)
|
||||
| (has_hl ? MT_FLAG_DECOR_SIGNHL : 0);
|
||||
|
||||
DecorInline decor = { .ext = true, .data.ext = { .vt = NULL, .sh_idx = decor_put_sh(sign) } };
|
||||
@ -334,9 +335,24 @@ static int sign_cmd_idx(char *begin_cmd, char *end_cmd)
|
||||
return idx;
|
||||
}
|
||||
|
||||
/// buf must be SIGN_WIDTH * MAX_SCHAR_SIZE (no extra +1 needed)
|
||||
size_t describe_sign_text(char *buf, schar_T *sign_text)
|
||||
{
|
||||
size_t p = 0;
|
||||
for (int i = 0; i < SIGN_WIDTH; i++) {
|
||||
schar_get(buf + p, sign_text[i]);
|
||||
size_t len = strlen(buf + p);
|
||||
if (len == 0) {
|
||||
break;
|
||||
}
|
||||
p += len;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/// Initialize the "text" for a new sign and store in "sign_text".
|
||||
/// "sp" is NULL for signs added through nvim_buf_set_extmark().
|
||||
int init_sign_text(sign_T *sp, char **sign_text, char *text)
|
||||
int init_sign_text(sign_T *sp, schar_T *sign_text, char *text)
|
||||
{
|
||||
char *s;
|
||||
char *endp = text + (int)strlen(text);
|
||||
@ -351,34 +367,29 @@ int init_sign_text(sign_T *sp, char **sign_text, char *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))) {
|
||||
int c;
|
||||
sign_text[cells] = utfc_ptr2schar(s, &c);
|
||||
if (!vim_isprintc(c)) {
|
||||
break;
|
||||
}
|
||||
cells += utf_ptr2cells(s);
|
||||
int width = utf_char2cells(c);
|
||||
if (width == 2) {
|
||||
sign_text[cells + 1] = 0;
|
||||
}
|
||||
cells += width;
|
||||
}
|
||||
// Currently must be empty, one or two display cells
|
||||
if (s != endp || cells > 2) {
|
||||
if (s != endp || cells > SIGN_WIDTH) {
|
||||
if (sp != NULL) {
|
||||
semsg(_("E239: Invalid sign text: %s"), text);
|
||||
}
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
if (cells < 1) {
|
||||
if (sp != NULL) {
|
||||
sp->sn_text = NULL;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
if (sp != NULL) {
|
||||
xfree(sp->sn_text);
|
||||
}
|
||||
// Allocate one byte more if we need to pad up with a space.
|
||||
size_t len = (size_t)(endp - text + (cells == 1));
|
||||
*sign_text = xstrnsave(text, len);
|
||||
|
||||
if (cells == 1) {
|
||||
STRCPY(*sign_text + len - 1, " ");
|
||||
sign_text[0] = 0;
|
||||
} else if (cells == 1) {
|
||||
sign_text[1] = schar_from_ascii(' ');
|
||||
}
|
||||
|
||||
return OK;
|
||||
@ -412,7 +423,7 @@ static int sign_define_by_name(char *name, char *icon, char *text, char *linehl,
|
||||
backslash_halve((*sp)->sn_icon);
|
||||
}
|
||||
|
||||
if (text != NULL && (init_sign_text(*sp, &(*sp)->sn_text, text) == FAIL)) {
|
||||
if (text != NULL && (init_sign_text(*sp, (*sp)->sn_text, text) == FAIL)) {
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
@ -437,7 +448,6 @@ static int sign_undefine_by_name(const char *name)
|
||||
}
|
||||
|
||||
xfree(sp->sn_name);
|
||||
xfree(sp->sn_text);
|
||||
xfree(sp->sn_icon);
|
||||
xfree(sp);
|
||||
return OK;
|
||||
@ -452,9 +462,11 @@ static void sign_list_defined(sign_T *sp)
|
||||
msg_outtrans(sp->sn_icon, 0);
|
||||
msg_puts(_(" (not supported)"));
|
||||
}
|
||||
if (sp->sn_text != NULL) {
|
||||
if (sp->sn_text[0]) {
|
||||
msg_puts(" text=");
|
||||
msg_outtrans(sp->sn_text, 0);
|
||||
char buf[SIGN_WIDTH * MAX_SCHAR_SIZE];
|
||||
describe_sign_text(buf, sp->sn_text);
|
||||
msg_outtrans(buf, 0);
|
||||
}
|
||||
static char *arg[] = { " linehl=", " texthl=", " culhl=", " numhl=" };
|
||||
int hl[] = { sp->sn_line_hl, sp->sn_text_hl, sp->sn_cul_hl, sp->sn_num_hl };
|
||||
@ -881,8 +893,10 @@ static dict_T *sign_get_info_dict(sign_T *sp)
|
||||
if (sp->sn_icon != NULL) {
|
||||
tv_dict_add_str(d, S_LEN("icon"), sp->sn_icon);
|
||||
}
|
||||
if (sp->sn_text != NULL) {
|
||||
tv_dict_add_str(d, S_LEN("text"), sp->sn_text);
|
||||
if (sp->sn_text[0]) {
|
||||
char buf[SIGN_WIDTH * MAX_SCHAR_SIZE];
|
||||
describe_sign_text(buf, sp->sn_text);
|
||||
tv_dict_add_str(d, S_LEN("text"), buf);
|
||||
}
|
||||
static char *arg[] = { "linehl", "texthl", "culhl", "numhl" };
|
||||
int hl[] = { sp->sn_line_hl, sp->sn_text_hl, sp->sn_cul_hl, sp->sn_num_hl };
|
||||
|
@ -1,8 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "nvim/types_defs.h"
|
||||
|
||||
/// Sign attributes. Used by the screen refresh routines.
|
||||
typedef struct {
|
||||
char *text;
|
||||
schar_T text[SIGN_WIDTH];
|
||||
int hl_id;
|
||||
} SignTextAttrs;
|
||||
|
||||
@ -10,13 +12,12 @@ typedef struct {
|
||||
typedef struct sign {
|
||||
char *sn_name; // name of sign
|
||||
char *sn_icon; // name of pixmap
|
||||
char *sn_text; // text used instead of pixmap
|
||||
schar_T sn_text[SIGN_WIDTH]; // text used instead of pixmap
|
||||
int sn_line_hl; // highlight ID for line
|
||||
int sn_text_hl; // highlight ID for text
|
||||
int sn_cul_hl; // highlight ID for text on current line when 'cursorline' is set
|
||||
int sn_num_hl; // highlight ID for line number
|
||||
} sign_T;
|
||||
|
||||
enum { SIGN_WIDTH = 2, }; ///< Number of display cells for a sign in the signcolumn
|
||||
enum { SIGN_SHOW_MAX = 9, }; ///< Maximum number of signs shown on a single line
|
||||
enum { SIGN_DEF_PRIO = 10, }; ///< Default sign highlight priority
|
||||
|
@ -1641,20 +1641,32 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
|
||||
|
||||
char *p = NULL;
|
||||
if (fold) {
|
||||
size_t n = fill_foldcolumn(out_p, wp, stcp->foldinfo,
|
||||
(linenr_T)get_vim_var_nr(VV_LNUM), NULL);
|
||||
schar_T fold_buf[10];
|
||||
size_t n = fill_foldcolumn(NULL, wp, stcp->foldinfo,
|
||||
(linenr_T)get_vim_var_nr(VV_LNUM), 0, fold_buf);
|
||||
stl_items[curitem].minwid = -((stcp->use_cul ? HLF_CLF : HLF_FC) + 1);
|
||||
size_t buflen = 0;
|
||||
// TODO(bfredl): this is very backwards. we must support schar_T
|
||||
// being used directly in 'statuscol'
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
schar_get(out_p + buflen, fold_buf[i]);
|
||||
buflen += strlen(out_p + buflen);
|
||||
}
|
||||
p = out_p;
|
||||
p[n] = NUL;
|
||||
}
|
||||
|
||||
char buf[SIGN_WIDTH * MAX_SCHAR_SIZE];
|
||||
size_t buflen = 0;
|
||||
varnumber_T virtnum = get_vim_var_nr(VV_VIRTNUM);
|
||||
for (int i = 0; i < width; i++) {
|
||||
if (!fold) {
|
||||
SignTextAttrs *sattr = virtnum ? NULL : &stcp->sattrs[i];
|
||||
p = sattr && sattr->text ? sattr->text : " ";
|
||||
stl_items[curitem].minwid = -(sattr && sattr->text
|
||||
p = " ";
|
||||
if (sattr && sattr->text[0]) {
|
||||
describe_sign_text(buf, sattr->text);
|
||||
p = buf;
|
||||
}
|
||||
stl_items[curitem].minwid = -(sattr && sattr->text[0]
|
||||
? (stcp->sign_cul_id ? stcp->sign_cul_id : sattr->hl_id)
|
||||
: (stcp->use_cul ? HLF_CLS : HLF_SC) + 1);
|
||||
}
|
||||
|
@ -56,6 +56,8 @@ typedef struct {
|
||||
} SignRange;
|
||||
#define SIGNRANGE_INIT { 0, 0 }
|
||||
|
||||
enum { SIGN_WIDTH = 2, }; ///< Number of display cells for a sign in the signcolumn
|
||||
|
||||
typedef struct file_buffer buf_T;
|
||||
typedef struct syn_state synstate_T;
|
||||
typedef struct terminal Terminal;
|
||||
|
@ -389,8 +389,8 @@ describe('Signs', function()
|
||||
feed('gg100aa<Esc>')
|
||||
screen:expect([[
|
||||
{1: >> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
|
||||
aa^a |
|
||||
{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
|
||||
{8: }aa^a |
|
||||
{8: 2 }b |
|
||||
{6: 3 }c |
|
||||
{6: 4 } |
|
||||
@ -404,10 +404,10 @@ describe('Signs', function()
|
||||
feed('<C-Y>')
|
||||
-- number column on virtual lines should be empty
|
||||
screen:expect([[
|
||||
VIRT LINES |
|
||||
{6: }VIRT LINES |
|
||||
{1: >> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
|
||||
aa^a |
|
||||
{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
|
||||
{8: }aa^a |
|
||||
{8: 2 }b |
|
||||
{6: 3 }c |
|
||||
{6: 4 } |
|
||||
|
Loading…
Reference in New Issue
Block a user