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:
bfredl
2023-11-22 19:58:17 +01:00
parent 720a3518e3
commit 66ac327db2
9 changed files with 396 additions and 467 deletions

View File

@@ -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 };