fix(float): title/footer shows printable control chars #40047

Problem:
Literal tab (or other control char) in a float title/footer is
not made printable, so it renders incorrectly instead of as "^I".

Solution:
Normalize title/footer text for display like 'statusline'
tab to "^I", parameterize parse_virt_text() with untab
and recompute the width.
This commit is contained in:
glepnir
2026-05-30 22:02:16 +08:00
committed by GitHub
parent a9ef076817
commit 70cfeabe23
5 changed files with 58 additions and 29 deletions

View File

@@ -234,7 +234,7 @@ Integer nvim_buf_set_virtual_text(Buffer buffer, Integer src_id, Integer line, A
uint32_t ns_id = src2ns(&src_id);
int width;
VirtText virt_text = parse_virt_text(chunks, err, &width);
VirtText virt_text = parse_virt_text(chunks, err, &width, false);
if (ERROR_SET(err)) {
return 0;
}

View File

@@ -669,7 +669,7 @@ Integer nvim_buf_set_extmark(Buffer buf, Integer ns_id, Integer line, Integer co
}
if (HAS_KEY(opts, set_extmark, virt_text)) {
virt_text.data.virt_text = parse_virt_text(opts->virt_text, err, &virt_text.width);
virt_text.data.virt_text = parse_virt_text(opts->virt_text, err, &virt_text.width, false);
if (ERROR_SET(err)) {
goto error;
}
@@ -742,7 +742,7 @@ Integer nvim_buf_set_extmark(Buffer buf, Integer ns_id, Integer line, Integer co
goto error;
});
int dummig;
VirtText jtem = parse_virt_text(a.items[j].data.array, err, &dummig);
VirtText jtem = parse_virt_text(a.items[j].data.array, err, &dummig, false);
kv_push(virt_lines.data.virt_lines, ((struct virt_line){ jtem, virt_lines_flags }));
if (ERROR_SET(err)) {
goto error;
@@ -1192,7 +1192,7 @@ static bool extmark_get_index_from_obj(buf_T *buf, Integer ns_id, Object obj, in
}
}
VirtText parse_virt_text(Array chunks, Error *err, int *width)
VirtText parse_virt_text(Array chunks, Error *err, int *width, bool untab)
{
VirtText virt_text = KV_INITIAL_VALUE;
int w = 0;
@@ -1230,7 +1230,7 @@ VirtText parse_virt_text(Array chunks, Error *err, int *width)
}
}
char *text = transstr(str.size > 0 ? str.data : "", false); // allocates
char *text = transstr(str.size > 0 ? str.data : "", untab); // allocates
w += (int)mb_string2cells(text);
kv_push(virt_text, ((VirtTextChunk){ .text = text, .hl_id = hl_id }));

View File

@@ -15,6 +15,7 @@
#include "nvim/autocmd_defs.h"
#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/decoration_defs.h"
#include "nvim/drawscreen.h"
#include "nvim/errors.h"
@@ -1021,38 +1022,27 @@ static void parse_bordertext(Object bordertext, BorderTextType bordertext_type,
return;
});
bool *is_present;
VirtText *chunks;
int *width;
switch (bordertext_type) {
case kBorderTextTitle:
is_present = &fconfig->title;
chunks = &fconfig->title_chunks;
width = &fconfig->title_width;
break;
case kBorderTextFooter:
is_present = &fconfig->footer;
chunks = &fconfig->footer_chunks;
width = &fconfig->footer_width;
break;
}
bool is_title = bordertext_type == kBorderTextTitle;
bool *is_present = is_title ? &fconfig->title : &fconfig->footer;
VirtText *chunks = is_title ? &fconfig->title_chunks : &fconfig->footer_chunks;
int *width = is_title ? &fconfig->title_width : &fconfig->footer_width;
if (bordertext.type == kObjectTypeString) {
if (bordertext.data.string.size == 0) {
*is_present = false;
return;
}
char *text = transstr(bordertext.data.string.data, true);
kv_init(*chunks);
kv_push(*chunks, ((VirtTextChunk){ .text = xstrdup(bordertext.data.string.data),
.hl_id = -1 }));
*width = (int)mb_string2cells(bordertext.data.string.data);
*is_present = true;
return;
kv_push(*chunks, ((VirtTextChunk){ .text = text, .hl_id = -1 }));
*width = (int)mb_string2cells(text);
} else {
*chunks = parse_virt_text(bordertext.data.array, err, width, true);
if (ERROR_SET(err)) {
return;
}
}
*width = 0;
*chunks = parse_virt_text(bordertext.data.array, err, width);
*is_present = true;
}