diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 31cbf6d559..a250f2aaa2 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -1405,8 +1405,10 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr, int *te bool bg_set = vt_bg_idx && vt_bg_idx <= 16 && term->color_set[vt_bg_idx - 1]; int hl_attrs = (cell.attrs.bold ? HL_BOLD : 0) + | (cell.attrs.dim ? HL_DIM : 0) | (cell.attrs.blink ? HL_BLINK : 0) | (cell.attrs.conceal ? HL_CONCEALED : 0) + | (cell.attrs.overline ? HL_OVERLINE : 0) | (cell.attrs.italic ? HL_ITALIC : 0) | (cell.attrs.reverse ? HL_INVERSE : 0) | get_underline_hl_flag(cell.attrs) diff --git a/src/nvim/vterm/pen.c b/src/nvim/vterm/pen.c index cc283ee795..f2054633d5 100644 --- a/src/nvim/vterm/pen.c +++ b/src/nvim/vterm/pen.c @@ -182,6 +182,8 @@ void vterm_state_resetpen(VTermState *state) state->pen.font = 0; setpenattr_int(state, VTERM_ATTR_FONT, 0); state->pen.small = 0; setpenattr_bool(state, VTERM_ATTR_SMALL, 0); state->pen.baseline = 0; setpenattr_int(state, VTERM_ATTR_BASELINE, 0); + state->pen.dim = 0; setpenattr_bool(state, VTERM_ATTR_DIM, 0); + state->pen.overline = 0; setpenattr_bool(state, VTERM_ATTR_OVERLINE, 0); state->pen.fg = state->default_fg; setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->default_fg); @@ -208,6 +210,8 @@ void vterm_state_savepen(VTermState *state, int save) setpenattr_int(state, VTERM_ATTR_FONT, state->pen.font); setpenattr_bool(state, VTERM_ATTR_SMALL, state->pen.small); setpenattr_int(state, VTERM_ATTR_BASELINE, state->pen.baseline); + setpenattr_bool(state, VTERM_ATTR_DIM, state->pen.dim); + setpenattr_bool(state, VTERM_ATTR_OVERLINE, state->pen.overline); setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg); setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg); @@ -285,6 +289,11 @@ void vterm_state_setpen(VTermState *state, const long args[], int argcount) break; } + case 2: // Dim/faint on + state->pen.dim = 1; + setpenattr_bool(state, VTERM_ATTR_DIM, 1); + break; + case 3: // Italic on state->pen.italic = 1; setpenattr_bool(state, VTERM_ATTR_ITALIC, 1); @@ -351,9 +360,11 @@ void vterm_state_setpen(VTermState *state, const long args[], int argcount) setpenattr_int(state, VTERM_ATTR_UNDERLINE, state->pen.underline); break; - case 22: // Bold off + case 22: // Normal intensity (bold and dim off) state->pen.bold = 0; setpenattr_bool(state, VTERM_ATTR_BOLD, 0); + state->pen.dim = 0; + setpenattr_bool(state, VTERM_ATTR_DIM, 0); break; case 23: // Italic and Gothic (currently unsupported) off @@ -441,6 +452,16 @@ void vterm_state_setpen(VTermState *state, const long args[], int argcount) setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg); break; + case 53: // Overline on + state->pen.overline = 1; + setpenattr_bool(state, VTERM_ATTR_OVERLINE, 1); + break; + + case 55: // Overline off + state->pen.overline = 0; + setpenattr_bool(state, VTERM_ATTR_OVERLINE, 0); + break; + case 73: // Superscript case 74: // Subscript case 75: // Superscript/subscript off @@ -528,6 +549,10 @@ int vterm_state_getpen(VTermState *state, long args[], int argcount) args[argi++] = 1; } + if (state->pen.dim) { + args[argi++] = 2; + } + if (state->pen.italic) { args[argi++] = 3; } @@ -567,6 +592,10 @@ int vterm_state_getpen(VTermState *state, long args[], int argcount) argi = vterm_state_getpen_color(&state->pen.bg, argi, args, false); + if (state->pen.overline) { + args[argi++] = 53; + } + if (state->pen.small) { if (state->pen.baseline == VTERM_BASELINE_RAISE) { args[argi++] = 73; @@ -630,6 +659,12 @@ int vterm_state_set_penattr(VTermState *state, VTermAttr attr, VTermValueType ty case VTERM_ATTR_URI: state->pen.uri = val->number; break; + case VTERM_ATTR_DIM: + state->pen.dim = (unsigned)val->boolean; + break; + case VTERM_ATTR_OVERLINE: + state->pen.overline = (unsigned)val->boolean; + break; default: return 0; } diff --git a/src/nvim/vterm/screen.c b/src/nvim/vterm/screen.c index 020a70d739..1206373241 100644 --- a/src/nvim/vterm/screen.c +++ b/src/nvim/vterm/screen.c @@ -399,6 +399,12 @@ static int setpenattr(VTermAttr attr, VTermValue *val, void *user) case VTERM_ATTR_URI: screen->pen.uri = val->number; return 1; + case VTERM_ATTR_DIM: + screen->pen.dim = (unsigned)val->boolean; + return 1; + case VTERM_ATTR_OVERLINE: + screen->pen.overline = (unsigned)val->boolean; + return 1; case VTERM_N_ATTRS: return 0; @@ -670,6 +676,8 @@ static void resize_buffer(VTermScreen *screen, int bufidx, int new_rows, int new dst->pen.font = src->attrs.font; dst->pen.small = src->attrs.small; dst->pen.baseline = src->attrs.baseline; + dst->pen.dim = src->attrs.dim; + dst->pen.overline = src->attrs.overline; dst->pen.fg = src->fg; dst->pen.bg = src->bg; @@ -931,6 +939,8 @@ int vterm_screen_get_cell(const VTermScreen *screen, VTermPos pos, VTermScreenCe cell->attrs.font = intcell->pen.font; cell->attrs.small = intcell->pen.small; cell->attrs.baseline = intcell->pen.baseline; + cell->attrs.dim = intcell->pen.dim; + cell->attrs.overline = intcell->pen.overline; cell->attrs.dwl = intcell->pen.dwl; cell->attrs.dhl = intcell->pen.dhl; diff --git a/src/nvim/vterm/vterm.c b/src/nvim/vterm/vterm.c index 9c4193c109..f05817f78f 100644 --- a/src/nvim/vterm/vterm.c +++ b/src/nvim/vterm/vterm.c @@ -260,6 +260,10 @@ VTermValueType vterm_get_attr_type(VTermAttr attr) return VTERM_VALUETYPE_INT; case VTERM_ATTR_URI: return VTERM_VALUETYPE_INT; + case VTERM_ATTR_DIM: + return VTERM_VALUETYPE_BOOL; + case VTERM_ATTR_OVERLINE: + return VTERM_VALUETYPE_BOOL; case VTERM_N_ATTRS: return 0; diff --git a/src/nvim/vterm/vterm_defs.h b/src/nvim/vterm/vterm_defs.h index 8e148524c6..ed0aca8204 100644 --- a/src/nvim/vterm/vterm_defs.h +++ b/src/nvim/vterm/vterm_defs.h @@ -65,6 +65,8 @@ typedef struct { unsigned dhl : 2; // On a DECDHL line (1=top 2=bottom) unsigned small : 1; unsigned baseline : 2; + unsigned dim : 1; + unsigned overline : 1; } VTermScreenCellAttrs; typedef struct { @@ -158,8 +160,10 @@ typedef enum { VTERM_ATTR_SMALL_MASK = 1 << 10, VTERM_ATTR_BASELINE_MASK = 1 << 11, VTERM_ATTR_URI_MASK = 1 << 12, + VTERM_ATTR_DIM_MASK = 1 << 13, + VTERM_ATTR_OVERLINE_MASK = 1 << 14, - VTERM_ALL_ATTRS_MASK = (1 << 13) - 1, + VTERM_ALL_ATTRS_MASK = (1 << 15) - 1, } VTermAttrMask; typedef enum { @@ -187,6 +191,8 @@ typedef enum { VTERM_ATTR_SMALL, // bool: 73, 74, 75 VTERM_ATTR_BASELINE, // number: 73, 74, 75 VTERM_ATTR_URI, // number + VTERM_ATTR_DIM, // bool: 2, 22 + VTERM_ATTR_OVERLINE, // bool: 53, 55 VTERM_N_ATTRS, } VTermAttr; @@ -314,6 +320,8 @@ typedef struct { unsigned font : 4; // 0 to 9 unsigned small : 1; unsigned baseline : 2; + unsigned dim : 1; + unsigned overline : 1; // Extra state storage that isn't strictly pen-related unsigned protected_cell : 1; diff --git a/src/nvim/vterm/vterm_internal_defs.h b/src/nvim/vterm/vterm_internal_defs.h index 69564e8529..b3d051ab9d 100644 --- a/src/nvim/vterm/vterm_internal_defs.h +++ b/src/nvim/vterm/vterm_internal_defs.h @@ -51,6 +51,8 @@ struct VTermPen { unsigned font:4; // To store 0-9 unsigned small:1; unsigned baseline:2; + unsigned dim:1; + unsigned overline:1; }; // https://sw.kovidgoyal.net/kitty/keyboard-protocol/#progressive-enhancement diff --git a/test/unit/fixtures/vterm_test.c b/test/unit/fixtures/vterm_test.c index 6744305960..e3558fee02 100644 --- a/test/unit/fixtures/vterm_test.c +++ b/test/unit/fixtures/vterm_test.c @@ -423,6 +423,8 @@ struct { int font; int small; int baseline; + int dim; + int overline; VTermColor foreground; VTermColor background; } state_pen; @@ -460,6 +462,12 @@ int state_setpenattr(VTermAttr attr, VTermValue *val, void *user) case VTERM_ATTR_BASELINE: state_pen.baseline = val->number; break; + case VTERM_ATTR_DIM: + state_pen.dim = val->boolean; + break; + case VTERM_ATTR_OVERLINE: + state_pen.overline = val->boolean; + break; case VTERM_ATTR_FOREGROUND: state_pen.foreground = val->color; break; @@ -615,6 +623,14 @@ int vterm_state_get_penattr(const VTermState *state, VTermAttr attr, VTermValue val->number = state->pen.uri; return 1; + case VTERM_ATTR_DIM: + val->boolean = state->pen.dim; + return 1; + + case VTERM_ATTR_OVERLINE: + val->boolean = state->pen.overline; + return 1; + case VTERM_N_ATTRS: return 0; } @@ -663,6 +679,12 @@ static int attrs_differ(VTermAttrMask attrs, ScreenCell *a, ScreenCell *b) if ((attrs & VTERM_ATTR_URI_MASK) && (a->pen.uri != b->pen.uri)) { return 1; } + if ((attrs & VTERM_ATTR_DIM_MASK) && (a->pen.dim != b->pen.dim)) { + return 1; + } + if ((attrs & VTERM_ATTR_OVERLINE_MASK) && (a->pen.overline != b->pen.overline)) { + return 1; + } return 0; } diff --git a/test/unit/vterm_spec.lua b/test/unit/vterm_spec.lua index c5bd6a755a..80a85bb011 100644 --- a/test/unit/vterm_spec.lua +++ b/test/unit/vterm_spec.lua @@ -285,7 +285,8 @@ local function lineinfo(row, expected, state) end local function pen(attribute, expected, state) - local is_bool = { bold = true, italic = true, blink = true, reverse = true } + local is_bool = + { bold = true, italic = true, blink = true, reverse = true, dim = true, overline = true } local vterm_attribute = { bold = vterm.VTERM_ATTR_BOLD, underline = vterm.VTERM_ATTR_UNDERLINE, @@ -293,6 +294,8 @@ local function pen(attribute, expected, state) blink = vterm.VTERM_ATTR_BLINK, reverse = vterm.VTERM_ATTR_REVERSE, font = vterm.VTERM_ATTR_FONT, + dim = vterm.VTERM_ATTR_DIM, + overline = vterm.VTERM_ATTR_OVERLINE, } local val = t.ffi.new('VTermValue') --- @type {boolean: integer} @@ -386,11 +389,13 @@ local function screen_cell(row, col, expected, screen) end actual = string.format('%s} width=%d attrs={', actual, cell['width']) actual = actual .. (cell['attrs'].bold ~= 0 and 'B' or '') + actual = actual .. (cell['attrs'].dim ~= 0 and 'D' or '') actual = actual .. (cell['attrs'].underline ~= 0 and string.format('U%d', cell['attrs'].underline) or '') actual = actual .. (cell['attrs'].italic ~= 0 and 'I' or '') actual = actual .. (cell['attrs'].blink ~= 0 and 'K' or '') actual = actual .. (cell['attrs'].reverse ~= 0 and 'R' or '') + actual = actual .. (cell['attrs'].overline ~= 0 and 'O' or '') actual = actual .. (cell['attrs'].font ~= 0 and string.format('F%d', cell['attrs'].font) or '') actual = actual .. (cell['attrs'].small ~= 0 and 'S' or '') if cell['attrs'].baseline ~= 0 then @@ -2950,6 +2955,30 @@ putglyph 1f3f4,200d,2620,fe0f 2 0,4]]) push('\x1b[11m\x1b[m', vt) pen('font', 0, state) + -- Dim + push('\x1b[2m', vt) + pen('dim', true, state) + push('\x1b[22m', vt) + pen('dim', false, state) + push('\x1b[2m\x1b[m', vt) + pen('dim', false, state) + + -- Bold and Dim interaction (SGR 22 turns off both) + push('\x1b[1;2m', vt) + pen('bold', true, state) + pen('dim', true, state) + push('\x1b[22m', vt) + pen('bold', false, state) + pen('dim', false, state) + + -- Overline + push('\x1b[53m', vt) + pen('overline', true, state) + push('\x1b[55m', vt) + pen('overline', false, state) + push('\x1b[53m\x1b[m', vt) + pen('overline', false, state) + -- TODO(dundargoc): fix -- Foreground -- push "\x1b[31m" @@ -3485,6 +3514,14 @@ putglyph 1f3f4,200d,2620,fe0f 2 0,4]]) screen_cell(0, 9, '{30} width=1 attrs={S_} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen) screen_cell(0, 10, '{32} width=1 attrs={S^} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen) + -- Dim + push('\x1b[2mI\x1b[m', vt) + screen_cell(0, 11, '{49} width=1 attrs={D} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen) + + -- Overline + push('\x1b[53mJ\x1b[m', vt) + screen_cell(0, 12, '{4a} width=1 attrs={O} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen) + -- EL sets only colours to end of line, not other attrs push('\x1b[H\x1b[7;33;44m\x1b[K', vt) screen_cell(0, 0, '{} width=1 attrs={} fg=rgb(224,224,0) bg=rgb(0,0,224)', screen)