From 7a367c6967d8bd1e386e391216a41b15bde5b28a Mon Sep 17 00:00:00 2001 From: James McCoy Date: Wed, 27 Nov 2024 11:17:42 -0500 Subject: test(vterm): move test functions into vterm_test fixture In order to run unittests with a release build, we need the test functions to be accessible when NDEBUG is defined. Moving the functions into the test fixture ensures they are available and only available for use by the unit tests. --- test/unit/fixtures/vterm_test.c | 504 ++++++++++++++++++++++++++++++++++++++++ test/unit/fixtures/vterm_test.h | 37 +++ test/unit/vterm_spec.lua | 6 +- 3 files changed, 546 insertions(+), 1 deletion(-) create mode 100644 test/unit/fixtures/vterm_test.c create mode 100644 test/unit/fixtures/vterm_test.h (limited to 'test/unit') diff --git a/test/unit/fixtures/vterm_test.c b/test/unit/fixtures/vterm_test.c new file mode 100644 index 0000000000..47aa071f9b --- /dev/null +++ b/test/unit/fixtures/vterm_test.c @@ -0,0 +1,504 @@ +#include "vterm_test.h" + +#include + +int parser_text(const char bytes[], size_t len, void *user) +{ + FILE *f = fopen(VTERM_TEST_FILE, "a"); + fprintf(f, "text "); + size_t i; + for(i = 0; i < len; i++) { + unsigned char b = (unsigned char)bytes[i]; + if(b < 0x20 || b == 0x7f || (b >= 0x80 && b < 0xa0)) { + break; + } + fprintf(f, i ? ",%x" : "%x", b); + } + fprintf(f, "\n"); + fclose(f); + + return (int)i; +} + +static void printchars(const char *s, size_t len, FILE *f) +{ + while(len--) { + fprintf(f, "%c", (s++)[0]); + } +} + +int parser_csi(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user) +{ + FILE *f = fopen(VTERM_TEST_FILE, "a"); + fprintf(f, "csi %02x", command); + + if(leader && leader[0]) { + fprintf(f, " L="); + for(int i = 0; leader[i]; i++) { + fprintf(f, "%02x", leader[i]); + } + } + + for(int i = 0; i < argcount; i++) { + char sep = i ? ',' : ' '; + + if(args[i] == CSI_ARG_MISSING) { + fprintf(f, "%c*", sep); + } else { + fprintf(f, "%c%ld%s", sep, CSI_ARG(args[i]), CSI_ARG_HAS_MORE(args[i]) ? "+" : ""); + } + } + + if(intermed && intermed[0]) { + fprintf(f, " I="); + for(int i = 0; intermed[i]; i++) { + fprintf(f, "%02x", intermed[i]); + } + } + + fprintf(f, "\n"); + + fclose(f); + + return 1; +} + +int parser_osc(int command, VTermStringFragment frag, void *user) +{ + FILE *f = fopen(VTERM_TEST_FILE, "a"); + fprintf(f, "osc "); + + if(frag.initial) { + if(command == -1) { + fprintf(f, "["); + } else { + fprintf(f, "[%d;", command); + } + } + + printchars(frag.str, frag.len, f); + + if(frag.final) { + fprintf(f, "]"); + } + + fprintf(f, "\n"); + fclose(f); + + return 1; +} + +int parser_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user) +{ + FILE *f = fopen(VTERM_TEST_FILE, "a"); + fprintf(f, "dcs "); + + if(frag.initial) { + fprintf(f, "["); + for(size_t i = 0; i < commandlen; i++) { + fprintf(f, "%c", command[i]); + } + } + + printchars(frag.str, frag.len,f); + + if(frag.final) { + fprintf(f, "]"); + } + + fprintf(f, "\n"); + fclose(f); + + return 1; +} + +int parser_apc(VTermStringFragment frag, void *user) +{ + FILE *f = fopen(VTERM_TEST_FILE, "a"); + fprintf(f, "apc "); + + if(frag.initial) { + fprintf(f, "["); + } + + printchars(frag.str, frag.len, f); + + if(frag.final) { + fprintf(f, "]"); + } + + fprintf(f, "\n"); + fclose(f); + + return 1; +} + +int parser_pm(VTermStringFragment frag, void *user) +{ + FILE *f = fopen(VTERM_TEST_FILE, "a"); + fprintf(f, "pm "); + + if(frag.initial) { + fprintf(f, "["); + } + + printchars(frag.str, frag.len,f); + + if(frag.final) { + fprintf(f, "]"); + } + + fprintf(f, "\n"); + fclose(f); + + return 1; +} + +int parser_sos(VTermStringFragment frag, void *user) +{ + FILE *f = fopen(VTERM_TEST_FILE, "a"); + fprintf(f, "sos "); + + if(frag.initial) { + fprintf(f, "["); + } + + printchars(frag.str, frag.len,f); + + if(frag.final) { + fprintf(f, "]"); + } + + fprintf(f, "\n"); + fclose(f); + + return 1; +} + +int selection_set(VTermSelectionMask mask, VTermStringFragment frag, void *user) +{ + FILE *f = fopen(VTERM_TEST_FILE, "a"); + fprintf(f, "selection-set mask=%04X ", mask); + if(frag.initial) { + fprintf(f, "["); +} + printchars(frag.str, frag.len, f); + if(frag.final) { + fprintf(f, "]"); +} + fprintf(f,"\n"); + + fclose(f); + return 1; +} + +int selection_query(VTermSelectionMask mask, void *user) +{ + FILE *f = fopen(VTERM_TEST_FILE, "a"); + fprintf(f,"selection-query mask=%04X\n", mask); + + fclose(f); + return 1; +} + +bool want_state_putglyph; +int state_putglyph(VTermGlyphInfo *info, VTermPos pos, void *user) +{ + if(!want_state_putglyph) { + return 1; + } + + FILE *f = fopen(VTERM_TEST_FILE, "a"); + fprintf(f, "putglyph "); + for(int i = 0; i < VTERM_MAX_CHARS_PER_CELL && info->chars[i]; i++) { + fprintf(f, i ? ",%x" : "%x", info->chars[i]); + } + fprintf(f, " %d %d,%d", info->width, pos.row, pos.col); + if(info->protected_cell) { + fprintf(f, " prot"); + } + if(info->dwl) { + fprintf(f, " dwl"); + } + if(info->dhl) { + fprintf(f, " dhl-%s", info->dhl == 1 ? "top" : info->dhl == 2 ? "bottom" : "?" ); + } + fprintf(f, "\n"); + + fclose(f); + + return 1; +} + +bool want_state_movecursor; +VTermPos state_pos; +int state_movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user) +{ + FILE *f = fopen(VTERM_TEST_FILE, "a"); + state_pos = pos; + + if(want_state_movecursor) { + fprintf(f,"movecursor %d,%d\n", pos.row, pos.col); + } + + fclose(f); + return 1; +} + +bool want_state_scrollrect; +int state_scrollrect(VTermRect rect, int downward, int rightward, void *user) +{ + if(!want_state_scrollrect) { + return 0; + } + + FILE *f = fopen(VTERM_TEST_FILE, "a"); + + fprintf(f,"scrollrect %d..%d,%d..%d => %+d,%+d\n", + rect.start_row, rect.end_row, rect.start_col, rect.end_col, + downward, rightward); + + fclose(f); + return 1; +} + +bool want_state_moverect; +int state_moverect(VTermRect dest, VTermRect src, void *user) +{ + if(!want_state_moverect) { + return 0; + } + + FILE *f = fopen(VTERM_TEST_FILE, "a"); + fprintf(f,"moverect %d..%d,%d..%d -> %d..%d,%d..%d\n", + src.start_row, src.end_row, src.start_col, src.end_col, + dest.start_row, dest.end_row, dest.start_col, dest.end_col); + + fclose(f); + return 1; +} + +void print_color(const VTermColor *col) +{ + FILE *f = fopen(VTERM_TEST_FILE, "a"); + if (VTERM_COLOR_IS_RGB(col)) { + fprintf(f,"rgb(%d,%d,%d", col->rgb.red, col->rgb.green, col->rgb.blue); + } + else if (VTERM_COLOR_IS_INDEXED(col)) { + fprintf(f,"idx(%d", col->indexed.idx); + } + else { + fprintf(f,"invalid(%d", col->type); + } + if (VTERM_COLOR_IS_DEFAULT_FG(col)) { + fprintf(f,",is_default_fg"); + } + if (VTERM_COLOR_IS_DEFAULT_BG(col)) { + fprintf(f,",is_default_bg"); + } + fprintf(f,")"); + fclose(f); +} + +bool want_state_settermprop; +int state_settermprop(VTermProp prop, VTermValue *val, void *user) +{ + if(!want_state_settermprop) { + return 1; + } + + int errcode = 0; + FILE *f = fopen(VTERM_TEST_FILE, "a"); + + VTermValueType type = vterm_get_prop_type(prop); + switch(type) { + case VTERM_VALUETYPE_BOOL: + fprintf(f,"settermprop %d %s\n", prop, val->boolean ? "true" : "false"); + errcode = 1; + goto end; + case VTERM_VALUETYPE_INT: + fprintf(f,"settermprop %d %d\n", prop, val->number); + errcode = 1; + goto end; + case VTERM_VALUETYPE_STRING: + fprintf(f,"settermprop %d %s\"%.*s\"%s\n", prop, + val->string.initial ? "[" : "", (int)val->string.len, val->string.str, val->string.final ? "]" : ""); + errcode=0; + goto end; + case VTERM_VALUETYPE_COLOR: + fprintf(f,"settermprop %d ", prop); + print_color(&val->color); + fprintf(f,"\n"); + errcode=1; + goto end; + case VTERM_N_VALUETYPES: + goto end; + } + +end: + fclose(f); + return errcode; +} + +bool want_state_erase; +int state_erase(VTermRect rect, int selective, void *user) +{ + if(!want_state_erase) { + return 1; + } + + FILE *f = fopen(VTERM_TEST_FILE, "a"); + + fprintf(f,"erase %d..%d,%d..%d%s\n", + rect.start_row, rect.end_row, rect.start_col, rect.end_col, + selective ? " selective" : ""); + + fclose(f); + return 1; +} + +struct { + int bold; + int underline; + int italic; + int blink; + int reverse; + int conceal; + int strike; + int font; + int small; + int baseline; + VTermColor foreground; + VTermColor background; +} state_pen; + +int state_setpenattr(VTermAttr attr, VTermValue *val, void *user) +{ + switch(attr) { + case VTERM_ATTR_BOLD: + state_pen.bold = val->boolean; + break; + case VTERM_ATTR_UNDERLINE: + state_pen.underline = val->number; + break; + case VTERM_ATTR_ITALIC: + state_pen.italic = val->boolean; + break; + case VTERM_ATTR_BLINK: + state_pen.blink = val->boolean; + break; + case VTERM_ATTR_REVERSE: + state_pen.reverse = val->boolean; + break; + case VTERM_ATTR_CONCEAL: + state_pen.conceal = val->boolean; + break; + case VTERM_ATTR_STRIKE: + state_pen.strike = val->boolean; + break; + case VTERM_ATTR_FONT: + state_pen.font = val->number; + break; + case VTERM_ATTR_SMALL: + state_pen.small = val->boolean; + break; + case VTERM_ATTR_BASELINE: + state_pen.baseline = val->number; + break; + case VTERM_ATTR_FOREGROUND: + state_pen.foreground = val->color; + break; + case VTERM_ATTR_BACKGROUND: + state_pen.background = val->color; + break; + + case VTERM_N_ATTRS: + return 0; + default: + break; + } + + return 1; +} + +bool want_state_scrollback; +int state_sb_clear(void *user) { + if(!want_state_scrollback) { + return 1; + } + + FILE *f = fopen(VTERM_TEST_FILE, "a"); + fprintf(f,"sb_clear\n"); + fclose(f); + + return 0; +} + +bool want_screen_scrollback; +int screen_sb_pushline(int cols, const VTermScreenCell *cells, void *user) +{ + if(!want_screen_scrollback) { + return 1; + } + + int eol = cols; + while(eol && !cells[eol-1].chars[0]) { + eol--; + } + + FILE *f = fopen(VTERM_TEST_FILE, "a"); + fprintf(f, "sb_pushline %d =", cols); + for(int c = 0; c < eol; c++) { + fprintf(f, " %02X", cells[c].chars[0]); + } + fprintf(f, "\n"); + + fclose(f); + + return 1; +} + +int screen_sb_popline(int cols, VTermScreenCell *cells, void *user) +{ + if(!want_screen_scrollback) { + return 0; + } + + // All lines of scrollback contain "ABCDE" + for(int col = 0; col < cols; col++) { + if(col < 5) { + cells[col].chars[0] = (uint32_t)('A' + col); + } else { + cells[col].chars[0] = 0; + } + + cells[col].width = 1; + } + + FILE *f = fopen(VTERM_TEST_FILE, "a"); + fprintf(f,"sb_popline %d\n", cols); + fclose(f); + return 1; +} + +int screen_sb_clear(void *user) +{ + if(!want_screen_scrollback) { + return 1; + } + + FILE *f = fopen(VTERM_TEST_FILE, "a"); + fprintf(f, "sb_clear\n"); + fclose(f); + return 0; +} + +void term_output(const char *s, size_t len, void *user) +{ + FILE *f = fopen(VTERM_TEST_FILE, "a"); + fprintf(f, "output "); + for(size_t i = 0; i < len; i++) { + fprintf(f, "%x%s", (unsigned char)s[i], i < len-1 ? "," : "\n"); + } + fclose(f); +} diff --git a/test/unit/fixtures/vterm_test.h b/test/unit/fixtures/vterm_test.h new file mode 100644 index 0000000000..924c6c1633 --- /dev/null +++ b/test/unit/fixtures/vterm_test.h @@ -0,0 +1,37 @@ +#include +#include + +#include "nvim/macros_defs.h" +#include "vterm/vterm.h" + +int parser_text(const char bytes[], size_t len, void *user); +int parser_csi(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user); +int parser_osc(int command, VTermStringFragment frag, void *user); +int parser_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user); +int parser_apc(VTermStringFragment frag, void *user); +int parser_pm(VTermStringFragment frag, void *user); +int parser_sos(VTermStringFragment frag, void *user); +int selection_set(VTermSelectionMask mask, VTermStringFragment frag, void *user); +int selection_query(VTermSelectionMask mask, void *user); +int state_putglyph(VTermGlyphInfo *info, VTermPos pos, void *user); +int state_movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user); +int state_scrollrect(VTermRect rect, int downward, int rightward, void *user); +int state_moverect(VTermRect dest, VTermRect src, void *user); +int state_settermprop(VTermProp prop, VTermValue *val, void *user); +int state_erase(VTermRect rect, int selective, void *user); +int state_setpenattr(VTermAttr attr, VTermValue *val, void *user); +int state_sb_clear(void *user); +void print_color(const VTermColor *col); +int screen_sb_pushline(int cols, const VTermScreenCell *cells, void *user); +int screen_sb_popline(int cols, VTermScreenCell *cells, void *user); +int screen_sb_clear(void *user); +void term_output(const char *s, size_t len, void *user); +EXTERN VTermPos state_pos; +EXTERN bool want_state_putglyph INIT (=false); +EXTERN bool want_state_movecursor INIT(= false); +EXTERN bool want_state_erase INIT(= false); +EXTERN bool want_state_scrollrect INIT(= false); +EXTERN bool want_state_moverect INIT(= false); +EXTERN bool want_state_settermprop INIT(= false); +EXTERN bool want_state_scrollback INIT(= false); +EXTERN bool want_screen_scrollback INIT(= false); diff --git a/test/unit/vterm_spec.lua b/test/unit/vterm_spec.lua index 4ea5d9c29a..a05579b4ff 100644 --- a/test/unit/vterm_spec.lua +++ b/test/unit/vterm_spec.lua @@ -79,7 +79,11 @@ local bit = require('bit') --- @field vterm_state_set_callbacks function --- @field vterm_state_set_selection_callbacks function --- @field vterm_state_set_unrecognised_fallbacks function -local vterm = t.cimport('./src/vterm/vterm.h', './src/vterm/vterm_internal.h') +local vterm = t.cimport( + './src/vterm/vterm.h', + './src/vterm/vterm_internal.h', + './test/unit/fixtures/vterm_test.h' +) --- @return string local function read_rm() -- cgit From fd05c7f19d942edce39a2c99b5c064b71e66bc31 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Sat, 21 Dec 2024 13:03:17 +0100 Subject: test: format C test files and fix clang-tidy warnings It's probably not worth adding the C test files to regular formatting as they're pretty much never touched, but ensuring the files are formatted according to our standards and getting rid of warnings is a cheap one-time fix. --- test/unit/fixtures/multiqueue.c | 5 +- test/unit/fixtures/multiqueue.h | 4 +- test/unit/fixtures/posix.h | 6 +- test/unit/fixtures/vterm_test.c | 201 ++++++++++++++++++++-------------------- test/unit/fixtures/vterm_test.h | 3 +- 5 files changed, 111 insertions(+), 108 deletions(-) (limited to 'test/unit') diff --git a/test/unit/fixtures/multiqueue.c b/test/unit/fixtures/multiqueue.c index 149daca893..2003bc7a5a 100644 --- a/test/unit/fixtures/multiqueue.c +++ b/test/unit/fixtures/multiqueue.c @@ -1,8 +1,9 @@ -#include #include -#include "nvim/event/multiqueue.h" +#include + #include "multiqueue.h" +#include "nvim/event/multiqueue.h" void ut_multiqueue_put(MultiQueue *self, const char *str) { diff --git a/test/unit/fixtures/multiqueue.h b/test/unit/fixtures/multiqueue.h index 78a3a89063..37da1d4db9 100644 --- a/test/unit/fixtures/multiqueue.h +++ b/test/unit/fixtures/multiqueue.h @@ -1,4 +1,4 @@ #include "nvim/event/multiqueue.h" -void ut_multiqueue_put(MultiQueue *queue, const char *str); -const char *ut_multiqueue_get(MultiQueue *queue); +void ut_multiqueue_put(MultiQueue *self, const char *str); +const char *ut_multiqueue_get(MultiQueue *self); diff --git a/test/unit/fixtures/posix.h b/test/unit/fixtures/posix.h index f6f24cd9dc..0d16f8aac9 100644 --- a/test/unit/fixtures/posix.h +++ b/test/unit/fixtures/posix.h @@ -1,8 +1,8 @@ -#include -#include #include -#include #include +#include +#include +#include enum { kPOSIXErrnoEINTR = EINTR, diff --git a/test/unit/fixtures/vterm_test.c b/test/unit/fixtures/vterm_test.c index 47aa071f9b..f227ae4591 100644 --- a/test/unit/fixtures/vterm_test.c +++ b/test/unit/fixtures/vterm_test.c @@ -1,15 +1,15 @@ -#include "vterm_test.h" - #include +#include "vterm_test.h" + int parser_text(const char bytes[], size_t len, void *user) { FILE *f = fopen(VTERM_TEST_FILE, "a"); fprintf(f, "text "); size_t i; - for(i = 0; i < len; i++) { + for (i = 0; i < len; i++) { unsigned char b = (unsigned char)bytes[i]; - if(b < 0x20 || b == 0x7f || (b >= 0x80 && b < 0xa0)) { + if (b < 0x20 || b == 0x7f || (b >= 0x80 && b < 0xa0)) { break; } fprintf(f, i ? ",%x" : "%x", b); @@ -22,36 +22,37 @@ int parser_text(const char bytes[], size_t len, void *user) static void printchars(const char *s, size_t len, FILE *f) { - while(len--) { + while (len--) { fprintf(f, "%c", (s++)[0]); } } -int parser_csi(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user) +int parser_csi(const char *leader, const long args[], int argcount, const char *intermed, + char command, void *user) { FILE *f = fopen(VTERM_TEST_FILE, "a"); fprintf(f, "csi %02x", command); - if(leader && leader[0]) { + if (leader && leader[0]) { fprintf(f, " L="); - for(int i = 0; leader[i]; i++) { + for (int i = 0; leader[i]; i++) { fprintf(f, "%02x", leader[i]); } } - for(int i = 0; i < argcount; i++) { + for (int i = 0; i < argcount; i++) { char sep = i ? ',' : ' '; - if(args[i] == CSI_ARG_MISSING) { + if (args[i] == CSI_ARG_MISSING) { fprintf(f, "%c*", sep); } else { fprintf(f, "%c%ld%s", sep, CSI_ARG(args[i]), CSI_ARG_HAS_MORE(args[i]) ? "+" : ""); } } - if(intermed && intermed[0]) { + if (intermed && intermed[0]) { fprintf(f, " I="); - for(int i = 0; intermed[i]; i++) { + for (int i = 0; intermed[i]; i++) { fprintf(f, "%02x", intermed[i]); } } @@ -68,8 +69,8 @@ int parser_osc(int command, VTermStringFragment frag, void *user) FILE *f = fopen(VTERM_TEST_FILE, "a"); fprintf(f, "osc "); - if(frag.initial) { - if(command == -1) { + if (frag.initial) { + if (command == -1) { fprintf(f, "["); } else { fprintf(f, "[%d;", command); @@ -78,7 +79,7 @@ int parser_osc(int command, VTermStringFragment frag, void *user) printchars(frag.str, frag.len, f); - if(frag.final) { + if (frag.final) { fprintf(f, "]"); } @@ -93,16 +94,16 @@ int parser_dcs(const char *command, size_t commandlen, VTermStringFragment frag, FILE *f = fopen(VTERM_TEST_FILE, "a"); fprintf(f, "dcs "); - if(frag.initial) { + if (frag.initial) { fprintf(f, "["); - for(size_t i = 0; i < commandlen; i++) { + for (size_t i = 0; i < commandlen; i++) { fprintf(f, "%c", command[i]); } } - printchars(frag.str, frag.len,f); + printchars(frag.str, frag.len, f); - if(frag.final) { + if (frag.final) { fprintf(f, "]"); } @@ -117,13 +118,13 @@ int parser_apc(VTermStringFragment frag, void *user) FILE *f = fopen(VTERM_TEST_FILE, "a"); fprintf(f, "apc "); - if(frag.initial) { + if (frag.initial) { fprintf(f, "["); } printchars(frag.str, frag.len, f); - if(frag.final) { + if (frag.final) { fprintf(f, "]"); } @@ -138,13 +139,13 @@ int parser_pm(VTermStringFragment frag, void *user) FILE *f = fopen(VTERM_TEST_FILE, "a"); fprintf(f, "pm "); - if(frag.initial) { + if (frag.initial) { fprintf(f, "["); } - printchars(frag.str, frag.len,f); + printchars(frag.str, frag.len, f); - if(frag.final) { + if (frag.final) { fprintf(f, "]"); } @@ -159,13 +160,13 @@ int parser_sos(VTermStringFragment frag, void *user) FILE *f = fopen(VTERM_TEST_FILE, "a"); fprintf(f, "sos "); - if(frag.initial) { + if (frag.initial) { fprintf(f, "["); } - printchars(frag.str, frag.len,f); + printchars(frag.str, frag.len, f); - if(frag.final) { + if (frag.final) { fprintf(f, "]"); } @@ -179,14 +180,14 @@ int selection_set(VTermSelectionMask mask, VTermStringFragment frag, void *user) { FILE *f = fopen(VTERM_TEST_FILE, "a"); fprintf(f, "selection-set mask=%04X ", mask); - if(frag.initial) { + if (frag.initial) { fprintf(f, "["); -} + } printchars(frag.str, frag.len, f); - if(frag.final) { + if (frag.final) { fprintf(f, "]"); -} - fprintf(f,"\n"); + } + fprintf(f, "\n"); fclose(f); return 1; @@ -195,7 +196,7 @@ int selection_set(VTermSelectionMask mask, VTermStringFragment frag, void *user) int selection_query(VTermSelectionMask mask, void *user) { FILE *f = fopen(VTERM_TEST_FILE, "a"); - fprintf(f,"selection-query mask=%04X\n", mask); + fprintf(f, "selection-query mask=%04X\n", mask); fclose(f); return 1; @@ -204,24 +205,24 @@ int selection_query(VTermSelectionMask mask, void *user) bool want_state_putglyph; int state_putglyph(VTermGlyphInfo *info, VTermPos pos, void *user) { - if(!want_state_putglyph) { + if (!want_state_putglyph) { return 1; } FILE *f = fopen(VTERM_TEST_FILE, "a"); fprintf(f, "putglyph "); - for(int i = 0; i < VTERM_MAX_CHARS_PER_CELL && info->chars[i]; i++) { + for (int i = 0; i < VTERM_MAX_CHARS_PER_CELL && info->chars[i]; i++) { fprintf(f, i ? ",%x" : "%x", info->chars[i]); } fprintf(f, " %d %d,%d", info->width, pos.row, pos.col); - if(info->protected_cell) { + if (info->protected_cell) { fprintf(f, " prot"); } - if(info->dwl) { + if (info->dwl) { fprintf(f, " dwl"); } - if(info->dhl) { - fprintf(f, " dhl-%s", info->dhl == 1 ? "top" : info->dhl == 2 ? "bottom" : "?" ); + if (info->dhl) { + fprintf(f, " dhl-%s", info->dhl == 1 ? "top" : info->dhl == 2 ? "bottom" : "?"); } fprintf(f, "\n"); @@ -237,8 +238,8 @@ int state_movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user) FILE *f = fopen(VTERM_TEST_FILE, "a"); state_pos = pos; - if(want_state_movecursor) { - fprintf(f,"movecursor %d,%d\n", pos.row, pos.col); + if (want_state_movecursor) { + fprintf(f, "movecursor %d,%d\n", pos.row, pos.col); } fclose(f); @@ -248,15 +249,15 @@ int state_movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user) bool want_state_scrollrect; int state_scrollrect(VTermRect rect, int downward, int rightward, void *user) { - if(!want_state_scrollrect) { + if (!want_state_scrollrect) { return 0; } FILE *f = fopen(VTERM_TEST_FILE, "a"); - fprintf(f,"scrollrect %d..%d,%d..%d => %+d,%+d\n", - rect.start_row, rect.end_row, rect.start_col, rect.end_col, - downward, rightward); + fprintf(f, "scrollrect %d..%d,%d..%d => %+d,%+d\n", + rect.start_row, rect.end_row, rect.start_col, rect.end_col, + downward, rightward); fclose(f); return 1; @@ -265,14 +266,14 @@ int state_scrollrect(VTermRect rect, int downward, int rightward, void *user) bool want_state_moverect; int state_moverect(VTermRect dest, VTermRect src, void *user) { - if(!want_state_moverect) { + if (!want_state_moverect) { return 0; } FILE *f = fopen(VTERM_TEST_FILE, "a"); - fprintf(f,"moverect %d..%d,%d..%d -> %d..%d,%d..%d\n", - src.start_row, src.end_row, src.start_col, src.end_col, - dest.start_row, dest.end_row, dest.start_col, dest.end_col); + fprintf(f, "moverect %d..%d,%d..%d -> %d..%d,%d..%d\n", + src.start_row, src.end_row, src.start_col, src.end_col, + dest.start_row, dest.end_row, dest.start_col, dest.end_col); fclose(f); return 1; @@ -282,28 +283,26 @@ void print_color(const VTermColor *col) { FILE *f = fopen(VTERM_TEST_FILE, "a"); if (VTERM_COLOR_IS_RGB(col)) { - fprintf(f,"rgb(%d,%d,%d", col->rgb.red, col->rgb.green, col->rgb.blue); - } - else if (VTERM_COLOR_IS_INDEXED(col)) { - fprintf(f,"idx(%d", col->indexed.idx); - } - else { - fprintf(f,"invalid(%d", col->type); + fprintf(f, "rgb(%d,%d,%d", col->rgb.red, col->rgb.green, col->rgb.blue); + } else if (VTERM_COLOR_IS_INDEXED(col)) { + fprintf(f, "idx(%d", col->indexed.idx); + } else { + fprintf(f, "invalid(%d", col->type); } if (VTERM_COLOR_IS_DEFAULT_FG(col)) { - fprintf(f,",is_default_fg"); + fprintf(f, ",is_default_fg"); } if (VTERM_COLOR_IS_DEFAULT_BG(col)) { - fprintf(f,",is_default_bg"); + fprintf(f, ",is_default_bg"); } - fprintf(f,")"); + fprintf(f, ")"); fclose(f); } bool want_state_settermprop; int state_settermprop(VTermProp prop, VTermValue *val, void *user) { - if(!want_state_settermprop) { + if (!want_state_settermprop) { return 1; } @@ -311,28 +310,29 @@ int state_settermprop(VTermProp prop, VTermValue *val, void *user) FILE *f = fopen(VTERM_TEST_FILE, "a"); VTermValueType type = vterm_get_prop_type(prop); - switch(type) { - case VTERM_VALUETYPE_BOOL: - fprintf(f,"settermprop %d %s\n", prop, val->boolean ? "true" : "false"); - errcode = 1; - goto end; - case VTERM_VALUETYPE_INT: - fprintf(f,"settermprop %d %d\n", prop, val->number); - errcode = 1; - goto end; - case VTERM_VALUETYPE_STRING: - fprintf(f,"settermprop %d %s\"%.*s\"%s\n", prop, - val->string.initial ? "[" : "", (int)val->string.len, val->string.str, val->string.final ? "]" : ""); - errcode=0; - goto end; - case VTERM_VALUETYPE_COLOR: - fprintf(f,"settermprop %d ", prop); - print_color(&val->color); - fprintf(f,"\n"); - errcode=1; - goto end; - case VTERM_N_VALUETYPES: - goto end; + switch (type) { + case VTERM_VALUETYPE_BOOL: + fprintf(f, "settermprop %d %s\n", prop, val->boolean ? "true" : "false"); + errcode = 1; + goto end; + case VTERM_VALUETYPE_INT: + fprintf(f, "settermprop %d %d\n", prop, val->number); + errcode = 1; + goto end; + case VTERM_VALUETYPE_STRING: + fprintf(f, "settermprop %d %s\"%.*s\"%s\n", prop, + val->string.initial ? "[" : "", (int)val->string.len, val->string.str, + val->string.final ? "]" : ""); + errcode = 0; + goto end; + case VTERM_VALUETYPE_COLOR: + fprintf(f, "settermprop %d ", prop); + print_color(&val->color); + fprintf(f, "\n"); + errcode = 1; + goto end; + case VTERM_N_VALUETYPES: + goto end; } end: @@ -343,15 +343,15 @@ end: bool want_state_erase; int state_erase(VTermRect rect, int selective, void *user) { - if(!want_state_erase) { + if (!want_state_erase) { return 1; } FILE *f = fopen(VTERM_TEST_FILE, "a"); - fprintf(f,"erase %d..%d,%d..%d%s\n", - rect.start_row, rect.end_row, rect.start_col, rect.end_col, - selective ? " selective" : ""); + fprintf(f, "erase %d..%d,%d..%d%s\n", + rect.start_row, rect.end_row, rect.start_col, rect.end_col, + selective ? " selective" : ""); fclose(f); return 1; @@ -374,7 +374,7 @@ struct { int state_setpenattr(VTermAttr attr, VTermValue *val, void *user) { - switch(attr) { + switch (attr) { case VTERM_ATTR_BOLD: state_pen.bold = val->boolean; break; @@ -422,13 +422,14 @@ int state_setpenattr(VTermAttr attr, VTermValue *val, void *user) } bool want_state_scrollback; -int state_sb_clear(void *user) { - if(!want_state_scrollback) { +int state_sb_clear(void *user) +{ + if (!want_state_scrollback) { return 1; } FILE *f = fopen(VTERM_TEST_FILE, "a"); - fprintf(f,"sb_clear\n"); + fprintf(f, "sb_clear\n"); fclose(f); return 0; @@ -437,18 +438,18 @@ int state_sb_clear(void *user) { bool want_screen_scrollback; int screen_sb_pushline(int cols, const VTermScreenCell *cells, void *user) { - if(!want_screen_scrollback) { + if (!want_screen_scrollback) { return 1; } int eol = cols; - while(eol && !cells[eol-1].chars[0]) { + while (eol && !cells[eol - 1].chars[0]) { eol--; } FILE *f = fopen(VTERM_TEST_FILE, "a"); fprintf(f, "sb_pushline %d =", cols); - for(int c = 0; c < eol; c++) { + for (int c = 0; c < eol; c++) { fprintf(f, " %02X", cells[c].chars[0]); } fprintf(f, "\n"); @@ -460,13 +461,13 @@ int screen_sb_pushline(int cols, const VTermScreenCell *cells, void *user) int screen_sb_popline(int cols, VTermScreenCell *cells, void *user) { - if(!want_screen_scrollback) { + if (!want_screen_scrollback) { return 0; } // All lines of scrollback contain "ABCDE" - for(int col = 0; col < cols; col++) { - if(col < 5) { + for (int col = 0; col < cols; col++) { + if (col < 5) { cells[col].chars[0] = (uint32_t)('A' + col); } else { cells[col].chars[0] = 0; @@ -476,14 +477,14 @@ int screen_sb_popline(int cols, VTermScreenCell *cells, void *user) } FILE *f = fopen(VTERM_TEST_FILE, "a"); - fprintf(f,"sb_popline %d\n", cols); + fprintf(f, "sb_popline %d\n", cols); fclose(f); return 1; } int screen_sb_clear(void *user) { - if(!want_screen_scrollback) { + if (!want_screen_scrollback) { return 1; } @@ -497,8 +498,8 @@ void term_output(const char *s, size_t len, void *user) { FILE *f = fopen(VTERM_TEST_FILE, "a"); fprintf(f, "output "); - for(size_t i = 0; i < len; i++) { - fprintf(f, "%x%s", (unsigned char)s[i], i < len-1 ? "," : "\n"); + for (size_t i = 0; i < len; i++) { + fprintf(f, "%x%s", (unsigned char)s[i], i < len - 1 ? "," : "\n"); } fclose(f); } diff --git a/test/unit/fixtures/vterm_test.h b/test/unit/fixtures/vterm_test.h index 924c6c1633..a05e7d499e 100644 --- a/test/unit/fixtures/vterm_test.h +++ b/test/unit/fixtures/vterm_test.h @@ -5,7 +5,8 @@ #include "vterm/vterm.h" int parser_text(const char bytes[], size_t len, void *user); -int parser_csi(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user); +int parser_csi(const char *leader, const long args[], int argcount, const char *intermed, + char command, void *user); int parser_osc(int command, VTermStringFragment frag, void *user); int parser_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user); int parser_apc(VTermStringFragment frag, void *user); -- cgit From e3bfcf2fd4a4ebf00b104b082cfe83c8144a842d Mon Sep 17 00:00:00 2001 From: bfredl Date: Wed, 18 Dec 2024 14:49:38 +0100 Subject: feat(terminal): support grapheme clusters, including emoji --- test/unit/fixtures/vterm_test.c | 37 ++++++++++++++---- test/unit/vterm_spec.lua | 83 ++++++++++++++++++++++++++++++----------- 2 files changed, 91 insertions(+), 29 deletions(-) (limited to 'test/unit') diff --git a/test/unit/fixtures/vterm_test.c b/test/unit/fixtures/vterm_test.c index f227ae4591..8755e32e7a 100644 --- a/test/unit/fixtures/vterm_test.c +++ b/test/unit/fixtures/vterm_test.c @@ -1,4 +1,6 @@ #include +#include "nvim/grid.h" +#include "nvim/mbyte.h" #include "vterm_test.h" @@ -202,6 +204,26 @@ int selection_query(VTermSelectionMask mask, void *user) return 1; } +static void print_schar(FILE *f, schar_T schar) { + char buf[MAX_SCHAR_SIZE]; + schar_get(buf, schar); + StrCharInfo ci = utf_ptr2StrCharInfo(buf); + bool did = false; + while (*ci.ptr != 0) { + if (did) { + fprintf(f, ","); + } + + if (ci.chr.len == 1 && ci.chr.value >= 0x80) { + fprintf(f, "??%x", ci.chr.value); + } else { + fprintf(f, "%x", ci.chr.value); + } + did = true; + ci = utf_ptr2StrCharInfo(ci.ptr + ci.chr.len); + } +} + bool want_state_putglyph; int state_putglyph(VTermGlyphInfo *info, VTermPos pos, void *user) { @@ -211,9 +233,7 @@ int state_putglyph(VTermGlyphInfo *info, VTermPos pos, void *user) FILE *f = fopen(VTERM_TEST_FILE, "a"); fprintf(f, "putglyph "); - for (int i = 0; i < VTERM_MAX_CHARS_PER_CELL && info->chars[i]; i++) { - fprintf(f, i ? ",%x" : "%x", info->chars[i]); - } + print_schar(f, info->schar); fprintf(f, " %d %d,%d", info->width, pos.row, pos.col); if (info->protected_cell) { fprintf(f, " prot"); @@ -443,14 +463,15 @@ int screen_sb_pushline(int cols, const VTermScreenCell *cells, void *user) } int eol = cols; - while (eol && !cells[eol - 1].chars[0]) { + while (eol && !cells[eol-1].schar) { eol--; } FILE *f = fopen(VTERM_TEST_FILE, "a"); fprintf(f, "sb_pushline %d =", cols); for (int c = 0; c < eol; c++) { - fprintf(f, " %02X", cells[c].chars[0]); + fprintf(f, " "); + print_schar(f, cells[c].schar); } fprintf(f, "\n"); @@ -467,10 +488,10 @@ int screen_sb_popline(int cols, VTermScreenCell *cells, void *user) // All lines of scrollback contain "ABCDE" for (int col = 0; col < cols; col++) { - if (col < 5) { - cells[col].chars[0] = (uint32_t)('A' + col); + if(col < 5) { + cells[col].schar = schar_from_ascii((uint32_t)('A' + col)); } else { - cells[col].chars[0] = 0; + cells[col].schar = 0; } cells[col].width = 1; diff --git a/test/unit/vterm_spec.lua b/test/unit/vterm_spec.lua index a05579b4ff..2457525fb7 100644 --- a/test/unit/vterm_spec.lua +++ b/test/unit/vterm_spec.lua @@ -17,7 +17,6 @@ local bit = require('bit') --- @field VTERM_KEY_NONE integer --- @field VTERM_KEY_TAB integer --- @field VTERM_KEY_UP integer ---- @field VTERM_MAX_CHARS_PER_CELL integer --- @field VTERM_MOD_ALT integer --- @field VTERM_MOD_CTRL integer --- @field VTERM_MOD_SHIFT integer @@ -80,6 +79,8 @@ local bit = require('bit') --- @field vterm_state_set_selection_callbacks function --- @field vterm_state_set_unrecognised_fallbacks function local vterm = t.cimport( + './src/nvim/mbyte.h', + './src/nvim/grid.h', './src/vterm/vterm.h', './src/vterm/vterm_internal.h', './test/unit/fixtures/vterm_test.h' @@ -302,16 +303,12 @@ local function screen_chars(start_row, start_col, end_row, end_col, expected, sc rect['end_row'] = end_row rect['end_col'] = end_col - local len = vterm.vterm_screen_get_chars(screen, nil, 0, rect) - - local chars = t.ffi.new('uint32_t[?]', len) - vterm.vterm_screen_get_chars(screen, chars, len, rect) + local len = vterm.vterm_screen_get_text(screen, nil, 0, rect) - local actual = '' - for i = 0, tonumber(len) - 1 do - actual = actual .. string.char(chars[i]) - end + local text = t.ffi.new('unsigned char[?]', len) + vterm.vterm_screen_get_text(screen, text, len, rect) + local actual = t.ffi.string(text, len) t.eq(expected, actual) end @@ -349,7 +346,7 @@ local function screen_row(row, expected, screen, end_col) local text = t.ffi.new('unsigned char[?]', len) vterm.vterm_screen_get_text(screen, text, len, rect) - t.eq(expected, t.ffi.string(text)) + t.eq(expected, t.ffi.string(text, len)) end local function screen_cell(row, col, expected, screen) @@ -360,14 +357,20 @@ local function screen_cell(row, col, expected, screen) local cell = t.ffi.new('VTermScreenCell') vterm.vterm_screen_get_cell(screen, pos, cell) + local buf = t.ffi.new('unsigned char[32]') + vterm.schar_get(buf, cell.schar) + local actual = '{' - for i = 0, vterm.VTERM_MAX_CHARS_PER_CELL - 1 do - if cell['chars'][i] ~= 0 then - if i > 0 then - actual = actual .. ',' - end - actual = string.format('%s%02x', actual, cell['chars'][i]) + local i = 0 + while buf[i] > 0 do + local char = vterm.utf_ptr2char(buf + i) + local charlen = vterm.utf_ptr2len(buf + i) + if i > 0 then + actual = actual .. ',' end + local invalid = char >= 128 and charlen == 1 + actual = string.format('%s%s%02x', actual, invalid and '?' or '', char) + i = i + charlen end actual = string.format('%s} width=%d attrs={', actual, cell['width']) actual = actual .. (cell['attrs'].bold ~= 0 and 'B' or '') @@ -962,8 +965,8 @@ describe('vterm', function() -- Spare combining chars get truncated reset(state, nil) - push('e' .. string.rep('\xCC\x81', 10), vt) - expect('putglyph 65,301,301,301,301,301 1 0,0') -- and nothing more + push('e' .. string.rep('\xCC\x81', 20), vt) + expect('putglyph 65,301,301,301,301,301,301,301,301,301,301,301,301,301,301 1 0,0') -- and nothing more reset(state, nil) push('e', vt) @@ -973,6 +976,34 @@ describe('vterm', function() push('\xCC\x82', vt) expect('putglyph 65,301,302 1 0,0') + -- emoji with ZWJ and variant selectors, as one chunk + reset(state, nil) + push('🏳️‍🌈🏳️‍⚧️🏴‍☠️', vt) + expect([[putglyph 1f3f3,fe0f,200d,1f308 2 0,0 +putglyph 1f3f3,fe0f,200d,26a7,fe0f 2 0,2 +putglyph 1f3f4,200d,2620,fe0f 2 0,4]]) + + -- emoji, one code point at a time + reset(state, nil) + push('🏳', vt) + expect('putglyph 1f3f3 2 0,0') + push('\xef\xb8\x8f', vt) + expect('putglyph 1f3f3,fe0f 2 0,0') + push('\xe2\x80\x8d', vt) + expect('putglyph 1f3f3,fe0f,200d 2 0,0') + push('🌈', vt) + expect('putglyph 1f3f3,fe0f,200d,1f308 2 0,0') + + -- modifier can change width + push('❤', vt) + expect('putglyph 2764 1 0,2') + push('\xef\xb8\x8f', vt) + expect('putglyph 2764,fe0f 2 0,2') + + -- also works batched + push('❤️', vt) + expect('putglyph 2764,fe0f 2 0,4') + -- DECSCA protected reset(state, nil) push('A\x1b[1"qB\x1b[2"qC', vt) @@ -3046,7 +3077,7 @@ describe('vterm', function() screen_cell( 0, 0, - '{65,301,302,303,304,305} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', + '{65,301,302,303,304,305,306,307,308,309,30a} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen ) @@ -3063,7 +3094,7 @@ describe('vterm', function() screen_cell( 0, 0, - '{65,301,301,301,301,301} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', + '{65,301,301,301,301,301,301,301,301,301,301,301,301,301,301} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen ) @@ -3072,6 +3103,16 @@ describe('vterm', function() push('\x1b[80G\xEF\xBC\x90', vt) screen_cell(0, 79, '{} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen) screen_cell(1, 0, '{ff10} width=2 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen) + + -- Outputting emoji with ZWJ and variant selectors + reset(nil, screen) + push('🏳️‍🌈🏳️‍⚧️🏴‍☠️', vt) + + -- stylua: ignore start + screen_cell(0, 0, '{1f3f3,fe0f,200d,1f308} width=2 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen) + screen_cell(0, 2, '{1f3f3,fe0f,200d,26a7,fe0f} width=2 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen) + screen_cell(0, 4, '{1f3f4,200d,2620,fe0f} width=2 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen) + -- stylua: ignore end end) pending('62screen_damage', function() end) @@ -3125,7 +3166,7 @@ describe('vterm', function() screen = wantscreen(vt, { b = true }) resize(20, 80, vt) expect( - 'sb_pushline 80 = 54 6F 70\nsb_pushline 80 =\nsb_pushline 80 =\nsb_pushline 80 =\nsb_pushline 80 =' + 'sb_pushline 80 = 54 6f 70\nsb_pushline 80 =\nsb_pushline 80 =\nsb_pushline 80 =\nsb_pushline 80 =' ) -- TODO(dundargoc): fix or remove -- screen_row( 0 , "",screen) -- cgit From d8bc08db7fd8d0efbf2e9ebf39fecb6f732f84e8 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Fri, 3 Jan 2025 15:40:46 +0100 Subject: refactor: adopt vterm We have changed too much to consider it a mere bundled dependency (such as unicode handling in e3bfcf2fd4a4ebf00b104b082cfe83c8144a842d), and can consider it our own at this point. --- test/unit/fixtures/vterm_test.c | 271 +++++++++++++++++++++++++++++++++++++++- test/unit/fixtures/vterm_test.h | 27 ++-- test/unit/vterm_spec.lua | 14 ++- 3 files changed, 293 insertions(+), 19 deletions(-) (limited to 'test/unit') diff --git a/test/unit/fixtures/vterm_test.c b/test/unit/fixtures/vterm_test.c index 8755e32e7a..7522962a05 100644 --- a/test/unit/fixtures/vterm_test.c +++ b/test/unit/fixtures/vterm_test.c @@ -1,7 +1,11 @@ #include +#include + #include "nvim/grid.h" #include "nvim/mbyte.h" - +#include "nvim/vterm/pen.h" +#include "nvim/vterm/screen.h" +#include "nvim/vterm/vterm_internal_defs.h" #include "vterm_test.h" int parser_text(const char bytes[], size_t len, void *user) @@ -204,7 +208,8 @@ int selection_query(VTermSelectionMask mask, void *user) return 1; } -static void print_schar(FILE *f, schar_T schar) { +static void print_schar(FILE *f, schar_T schar) +{ char buf[MAX_SCHAR_SIZE]; schar_get(buf, schar); StrCharInfo ci = utf_ptr2StrCharInfo(buf); @@ -319,6 +324,34 @@ void print_color(const VTermColor *col) fclose(f); } +static VTermValueType vterm_get_prop_type(VTermProp prop) +{ + switch (prop) { + case VTERM_PROP_CURSORVISIBLE: + return VTERM_VALUETYPE_BOOL; + case VTERM_PROP_CURSORBLINK: + return VTERM_VALUETYPE_BOOL; + case VTERM_PROP_ALTSCREEN: + return VTERM_VALUETYPE_BOOL; + case VTERM_PROP_TITLE: + return VTERM_VALUETYPE_STRING; + case VTERM_PROP_ICONNAME: + return VTERM_VALUETYPE_STRING; + case VTERM_PROP_REVERSE: + return VTERM_VALUETYPE_BOOL; + case VTERM_PROP_CURSORSHAPE: + return VTERM_VALUETYPE_INT; + case VTERM_PROP_MOUSE: + return VTERM_VALUETYPE_INT; + case VTERM_PROP_FOCUSREPORT: + return VTERM_VALUETYPE_BOOL; + + case VTERM_N_PROPS: + return 0; + } + return 0; // UNREACHABLE +} + bool want_state_settermprop; int state_settermprop(VTermProp prop, VTermValue *val, void *user) { @@ -463,14 +496,14 @@ int screen_sb_pushline(int cols, const VTermScreenCell *cells, void *user) } int eol = cols; - while (eol && !cells[eol-1].schar) { + while (eol && !cells[eol - 1].schar) { eol--; } FILE *f = fopen(VTERM_TEST_FILE, "a"); fprintf(f, "sb_pushline %d =", cols); for (int c = 0; c < eol; c++) { - fprintf(f, " "); + fprintf(f, " "); print_schar(f, cells[c].schar); } fprintf(f, "\n"); @@ -488,7 +521,7 @@ int screen_sb_popline(int cols, VTermScreenCell *cells, void *user) // All lines of scrollback contain "ABCDE" for (int col = 0; col < cols; col++) { - if(col < 5) { + if (col < 5) { cells[col].schar = schar_from_ascii((uint32_t)('A' + col)); } else { cells[col].schar = 0; @@ -524,3 +557,231 @@ void term_output(const char *s, size_t len, void *user) } fclose(f); } + +int vterm_state_get_penattr(const VTermState *state, VTermAttr attr, VTermValue *val) +{ + switch (attr) { + case VTERM_ATTR_BOLD: + val->boolean = state->pen.bold; + return 1; + + case VTERM_ATTR_UNDERLINE: + val->number = state->pen.underline; + return 1; + + case VTERM_ATTR_ITALIC: + val->boolean = state->pen.italic; + return 1; + + case VTERM_ATTR_BLINK: + val->boolean = state->pen.blink; + return 1; + + case VTERM_ATTR_REVERSE: + val->boolean = state->pen.reverse; + return 1; + + case VTERM_ATTR_CONCEAL: + val->boolean = state->pen.conceal; + return 1; + + case VTERM_ATTR_STRIKE: + val->boolean = state->pen.strike; + return 1; + + case VTERM_ATTR_FONT: + val->number = state->pen.font; + return 1; + + case VTERM_ATTR_FOREGROUND: + val->color = state->pen.fg; + return 1; + + case VTERM_ATTR_BACKGROUND: + val->color = state->pen.bg; + return 1; + + case VTERM_ATTR_SMALL: + val->boolean = state->pen.small; + return 1; + + case VTERM_ATTR_BASELINE: + val->number = state->pen.baseline; + return 1; + + case VTERM_ATTR_URI: + val->number = state->pen.uri; + return 1; + + case VTERM_N_ATTRS: + return 0; + } + + return 0; +} + +static int attrs_differ(VTermAttrMask attrs, ScreenCell *a, ScreenCell *b) +{ + if ((attrs & VTERM_ATTR_BOLD_MASK) && (a->pen.bold != b->pen.bold)) { + return 1; + } + if ((attrs & VTERM_ATTR_UNDERLINE_MASK) && (a->pen.underline != b->pen.underline)) { + return 1; + } + if ((attrs & VTERM_ATTR_ITALIC_MASK) && (a->pen.italic != b->pen.italic)) { + return 1; + } + if ((attrs & VTERM_ATTR_BLINK_MASK) && (a->pen.blink != b->pen.blink)) { + return 1; + } + if ((attrs & VTERM_ATTR_REVERSE_MASK) && (a->pen.reverse != b->pen.reverse)) { + return 1; + } + if ((attrs & VTERM_ATTR_CONCEAL_MASK) && (a->pen.conceal != b->pen.conceal)) { + return 1; + } + if ((attrs & VTERM_ATTR_STRIKE_MASK) && (a->pen.strike != b->pen.strike)) { + return 1; + } + if ((attrs & VTERM_ATTR_FONT_MASK) && (a->pen.font != b->pen.font)) { + return 1; + } + if ((attrs & VTERM_ATTR_FOREGROUND_MASK) && !vterm_color_is_equal(&a->pen.fg, &b->pen.fg)) { + return 1; + } + if ((attrs & VTERM_ATTR_BACKGROUND_MASK) && !vterm_color_is_equal(&a->pen.bg, &b->pen.bg)) { + return 1; + } + if ((attrs & VTERM_ATTR_SMALL_MASK) && (a->pen.small != b->pen.small)) { + return 1; + } + if ((attrs & VTERM_ATTR_BASELINE_MASK) && (a->pen.baseline != b->pen.baseline)) { + return 1; + } + if ((attrs & VTERM_ATTR_URI_MASK) && (a->pen.uri != b->pen.uri)) { + return 1; + } + + return 0; +} + +int vterm_screen_get_attrs_extent(const VTermScreen *screen, VTermRect *extent, VTermPos pos, + VTermAttrMask attrs) +{ + ScreenCell *target = getcell(screen, pos.row, pos.col); + + // TODO(vterm): bounds check + extent->start_row = pos.row; + extent->end_row = pos.row + 1; + + if (extent->start_col < 0) { + extent->start_col = 0; + } + if (extent->end_col < 0) { + extent->end_col = screen->cols; + } + + int col; + + for (col = pos.col - 1; col >= extent->start_col; col--) { + if (attrs_differ(attrs, target, getcell(screen, pos.row, col))) { + break; + } + } + extent->start_col = col + 1; + + for (col = pos.col + 1; col < extent->end_col; col++) { + if (attrs_differ(attrs, target, getcell(screen, pos.row, col))) { + break; + } + } + extent->end_col = col - 1; + + return 1; +} + +/// Does not NUL-terminate the buffer +size_t vterm_screen_get_text(const VTermScreen *screen, char *buffer, size_t len, + const VTermRect rect) +{ + size_t outpos = 0; + int padding = 0; + +#define PUT(bytes, thislen) \ + if (true) { \ + if (buffer && outpos + thislen <= len) \ + memcpy((char *)buffer + outpos, bytes, thislen); \ + outpos += thislen; \ + } \ + + for (int row = rect.start_row; row < rect.end_row; row++) { + for (int col = rect.start_col; col < rect.end_col; col++) { + ScreenCell *cell = getcell(screen, row, col); + + if (cell->schar == 0) { + // Erased cell, might need a space + padding++; + } else if (cell->schar == (uint32_t)-1) { + // Gap behind a double-width char, do nothing + } else { + while (padding) { + PUT(" ", 1); + padding--; + } + char buf[MAX_SCHAR_SIZE + 1]; + size_t thislen = schar_get(buf, cell->schar); + PUT(buf, thislen); + } + } + + if (row < rect.end_row - 1) { + PUT("\n", 1); + padding = 0; + } + } + + return outpos; +} + +int vterm_screen_is_eol(const VTermScreen *screen, VTermPos pos) +{ + // This cell is EOL if this and every cell to the right is black + for (; pos.col < screen->cols; pos.col++) { + ScreenCell *cell = getcell(screen, pos.row, pos.col); + if (cell->schar != 0) { + return 0; + } + } + + return 1; +} + +void vterm_state_get_cursorpos(const VTermState *state, VTermPos *cursorpos) +{ + *cursorpos = state->pos; +} + +void vterm_state_set_bold_highbright(VTermState *state, int bold_is_highbright) +{ + state->bold_is_highbright = bold_is_highbright; +} + +/// Compares two colours. Returns true if the colors are equal, false otherwise. +int vterm_color_is_equal(const VTermColor *a, const VTermColor *b) +{ + // First make sure that the two colours are of the same type (RGB/Indexed) + if (a->type != b->type) { + return false; + } + + // Depending on the type inspect the corresponding members + if (VTERM_COLOR_IS_INDEXED(a)) { + return a->indexed.idx == b->indexed.idx; + } else if (VTERM_COLOR_IS_RGB(a)) { + return (a->rgb.red == b->rgb.red) + && (a->rgb.green == b->rgb.green) + && (a->rgb.blue == b->rgb.blue); + } + + return 0; +} diff --git a/test/unit/fixtures/vterm_test.h b/test/unit/fixtures/vterm_test.h index a05e7d499e..ef6463af6d 100644 --- a/test/unit/fixtures/vterm_test.h +++ b/test/unit/fixtures/vterm_test.h @@ -2,8 +2,17 @@ #include #include "nvim/macros_defs.h" -#include "vterm/vterm.h" +#include "nvim/vterm/vterm.h" +EXTERN VTermPos state_pos; +EXTERN bool want_state_putglyph INIT (=false); +EXTERN bool want_state_movecursor INIT(= false); +EXTERN bool want_state_erase INIT(= false); +EXTERN bool want_state_scrollrect INIT(= false); +EXTERN bool want_state_moverect INIT(= false); +EXTERN bool want_state_settermprop INIT(= false); +EXTERN bool want_state_scrollback INIT(= false); +EXTERN bool want_screen_scrollback INIT(= false); int parser_text(const char bytes[], size_t len, void *user); int parser_csi(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user); @@ -27,12 +36,10 @@ int screen_sb_pushline(int cols, const VTermScreenCell *cells, void *user); int screen_sb_popline(int cols, VTermScreenCell *cells, void *user); int screen_sb_clear(void *user); void term_output(const char *s, size_t len, void *user); -EXTERN VTermPos state_pos; -EXTERN bool want_state_putglyph INIT (=false); -EXTERN bool want_state_movecursor INIT(= false); -EXTERN bool want_state_erase INIT(= false); -EXTERN bool want_state_scrollrect INIT(= false); -EXTERN bool want_state_moverect INIT(= false); -EXTERN bool want_state_settermprop INIT(= false); -EXTERN bool want_state_scrollback INIT(= false); -EXTERN bool want_screen_scrollback INIT(= false); +int vterm_state_get_penattr(const VTermState *state, VTermAttr attr, VTermValue *val); +int vterm_screen_get_attrs_extent(const VTermScreen *screen, VTermRect *extent, VTermPos pos, VTermAttrMask attrs); +size_t vterm_screen_get_text(const VTermScreen *screen, char *buffer, size_t len, VTermRect rect); +int vterm_screen_is_eol(const VTermScreen *screen, VTermPos pos); +void vterm_state_get_cursorpos(const VTermState *state, VTermPos *cursorpos); +void vterm_state_set_bold_highbright(VTermState *state, int bold_is_highbright); +int vterm_color_is_equal(const VTermColor *a, const VTermColor *b); diff --git a/test/unit/vterm_spec.lua b/test/unit/vterm_spec.lua index 2457525fb7..db0aa3c575 100644 --- a/test/unit/vterm_spec.lua +++ b/test/unit/vterm_spec.lua @@ -61,7 +61,6 @@ local bit = require('bit') --- @field vterm_screen_enable_reflow function --- @field vterm_screen_get_attrs_extent function --- @field vterm_screen_get_cell function ---- @field vterm_screen_get_chars fun(any, any, any, any):any --- @field vterm_screen_get_text fun(any, any, any, any):any --- @field vterm_screen_is_eol fun(any, any):any --- @field vterm_screen_reset function @@ -79,10 +78,17 @@ local bit = require('bit') --- @field vterm_state_set_selection_callbacks function --- @field vterm_state_set_unrecognised_fallbacks function local vterm = t.cimport( - './src/nvim/mbyte.h', './src/nvim/grid.h', - './src/vterm/vterm.h', - './src/vterm/vterm_internal.h', + './src/nvim/mbyte.h', + './src/nvim/vterm/encoding.h', + './src/nvim/vterm/keyboard.h', + './src/nvim/vterm/mouse.h', + './src/nvim/vterm/parser.h', + './src/nvim/vterm/pen.h', + './src/nvim/vterm/screen.h', + './src/nvim/vterm/state.h', + './src/nvim/vterm/vterm.h', + './src/nvim/vterm/vterm_internal.h', './test/unit/fixtures/vterm_test.h' ) -- cgit From 6a425e7045cca609d95612c0f2cd08d0265238a9 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Sun, 24 Nov 2024 11:29:39 +0100 Subject: docs: misc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Axel Co-authored-by: Colin Kennedy Co-authored-by: Daiki Noda Co-authored-by: Evgeni Chasnovski Co-authored-by: Jean-Jacq du Plessis <1030058+jj-du-plessis@users.noreply.github.com> Co-authored-by: Juan Giordana Co-authored-by: Lincoln Wallace Co-authored-by: Matti Hellström Co-authored-by: Steven Locorotondo Co-authored-by: Yochem van Rosmalen Co-authored-by: glepnir Co-authored-by: ifish --- test/unit/mbyte_spec.lua | 6 +++--- test/unit/vterm_spec.lua | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'test/unit') diff --git a/test/unit/mbyte_spec.lua b/test/unit/mbyte_spec.lua index bdc111de2c..2c52aa9217 100644 --- a/test/unit/mbyte_spec.lua +++ b/test/unit/mbyte_spec.lua @@ -58,11 +58,11 @@ describe('mbyte', function() lib.schar_get(buf, lib.utfc_ptr2schar(to_string(seq), firstc)) local str = ffi.string(buf) if 1 > 2 then -- for debugging - local tabel = {} + local tbl = {} for i = 1, #str do - table.insert(tabel, string.format('0x%02x', string.byte(str, i))) + table.insert(tbl, string.format('0x%02x', string.byte(str, i))) end - print('{ ' .. table.concat(tabel, ', ') .. ' }') + print('{ ' .. table.concat(tbl, ', ') .. ' }') io.stdout:flush() end return { str, firstc[0] } diff --git a/test/unit/vterm_spec.lua b/test/unit/vterm_spec.lua index db0aa3c575..0bf4bf70f8 100644 --- a/test/unit/vterm_spec.lua +++ b/test/unit/vterm_spec.lua @@ -1131,7 +1131,7 @@ putglyph 1f3f4,200d,2620,fe0f 2 0,4]]) push('\x1b[0F', vt) cursor(0, 0, state) - -- Cursor Horizonal Absolute + -- Cursor Horizontal Absolute push('\n', vt) cursor(1, 0, state) push('\x1b[20G', vt) @@ -3104,7 +3104,7 @@ putglyph 1f3f4,200d,2620,fe0f 2 0,4]]) screen ) - -- Outputing CJK doublewidth in 80th column should wraparound to next line and not crash" + -- Outputting CJK doublewidth in 80th column should wraparound to next line and not crash" reset(nil, screen) push('\x1b[80G\xEF\xBC\x90', vt) screen_cell(0, 79, '{} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen) -- cgit From 47866cd8d20c62afa8a3c3929d3aada2db9162f5 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Thu, 9 Jan 2025 17:28:27 +0100 Subject: refactor: delete duplicate utf8-functionality Also remove British National Replacement Character Set. We keep the DEC Special Graphics and ASCII despite it not being unicode as some old software such as calcurse still rely on this functionality. References: - https://github.com/neovim/neovim/pull/31934#discussion_r1911046426 - https://en.wikipedia.org/wiki/DEC_Special_Graphics - https://vt100.net/docs/vt220-rm/chapter2.html#S2.4.3 --- test/unit/vterm_spec.lua | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'test/unit') diff --git a/test/unit/vterm_spec.lua b/test/unit/vterm_spec.lua index 0bf4bf70f8..6ff3c18d2a 100644 --- a/test/unit/vterm_spec.lua +++ b/test/unit/vterm_spec.lua @@ -28,6 +28,7 @@ local bit = require('bit') --- @field parser_sos function --- @field parser_text function --- @field print_color function +--- @field schar_get fun(any, any):integer --- @field screen_sb_clear function --- @field screen_sb_popline function --- @field screen_sb_pushline function @@ -43,6 +44,8 @@ local bit = require('bit') --- @field state_setpenattr function --- @field state_settermprop function --- @field term_output function +--- @field utf_ptr2char fun(any):integer +--- @field utf_ptr2len fun(any):integer --- @field vterm_input_write function --- @field vterm_keyboard_end_paste function --- @field vterm_keyboard_key function @@ -360,7 +363,7 @@ local function screen_cell(row, col, expected, screen) pos['row'] = row pos['col'] = col - local cell = t.ffi.new('VTermScreenCell') + local cell = t.ffi.new('VTermScreenCell') ---@type any vterm.vterm_screen_get_cell(screen, pos, cell) local buf = t.ffi.new('unsigned char[32]') @@ -1705,12 +1708,6 @@ putglyph 1f3f4,200d,2620,fe0f 2 0,4]]) push('#', vt) expect('putglyph 23 1 0,0') - -- Designate G0=UK - reset(state, nil) - push('\x1b(A', vt) - push('#', vt) - expect('putglyph a3 1 0,0') - -- Designate G0=DEC drawing reset(state, nil) push('\x1b(0', vt) -- cgit From 34e2185022ab698827b72751d77e218a1b6b6afe Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Fri, 10 Jan 2025 10:20:43 +0000 Subject: fix(options): better handling of empty values Problem: Whether an option is allowed to be empty isn't well defined and isn't properly checked. Solution: - For non-list string options, explicitly check the option value if it is empty. - Annotate non-list string options that can accept an empty value. - Adjust command completion to ignore the empty value. - Render values in Lua meta files --- test/unit/optionstr_spec.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'test/unit') diff --git a/test/unit/optionstr_spec.lua b/test/unit/optionstr_spec.lua index b9c9ceaa85..1f5b42485f 100644 --- a/test/unit/optionstr_spec.lua +++ b/test/unit/optionstr_spec.lua @@ -11,8 +11,8 @@ local check_ff_value = function(ff) end describe('check_ff_value', function() - itp('views empty string as valid', function() - eq(1, check_ff_value('')) + itp('views empty string as invalid', function() + eq(0, check_ff_value('')) end) itp('views "unix", "dos" and "mac" as valid', function() -- cgit From f1c45fc7a4a595e460cd245172a5767bddeb09e9 Mon Sep 17 00:00:00 2001 From: Gregory Anders Date: Tue, 14 Jan 2025 08:18:59 -0600 Subject: feat(terminal): support theme update notifications (DEC mode 2031) (#31999) --- test/unit/fixtures/vterm_test.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'test/unit') diff --git a/test/unit/fixtures/vterm_test.c b/test/unit/fixtures/vterm_test.c index 7522962a05..6744305960 100644 --- a/test/unit/fixtures/vterm_test.c +++ b/test/unit/fixtures/vterm_test.c @@ -345,6 +345,8 @@ static VTermValueType vterm_get_prop_type(VTermProp prop) return VTERM_VALUETYPE_INT; case VTERM_PROP_FOCUSREPORT: return VTERM_VALUETYPE_BOOL; + case VTERM_PROP_THEMEUPDATES: + return VTERM_VALUETYPE_BOOL; case VTERM_N_PROPS: return 0; -- cgit From 6f0bde11ccd82d257fcda25ecad26227eba3335e Mon Sep 17 00:00:00 2001 From: Gregory Anders Date: Wed, 15 Jan 2025 11:07:51 -0600 Subject: feat(terminal): add support for kitty keyboard protocol This commit adds basic support for the kitty keyboard protocol to Neovim's builtin terminal. For now only the first mode ("Disambiguate escape codes") is supported. --- test/unit/vterm_spec.lua | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'test/unit') diff --git a/test/unit/vterm_spec.lua b/test/unit/vterm_spec.lua index 6ff3c18d2a..9f70187fad 100644 --- a/test/unit/vterm_spec.lua +++ b/test/unit/vterm_spec.lua @@ -2324,6 +2324,9 @@ putglyph 1f3f4,200d,2620,fe0f 2 0,4]]) local vt = init() local state = wantstate(vt) + -- Disambiguate escape codes + push('\x1b[>1u', vt) + -- Unmodified ASCII inchar(41, vt) expect('output 29') @@ -2478,6 +2481,8 @@ putglyph 1f3f4,200d,2620,fe0f 2 0,4]]) expect_output('\x1b[I') vterm.vterm_state_focus_out(state) expect_output('\x1b[O') + + push('\x1b[ Date: Wed, 15 Jan 2025 13:08:18 -0600 Subject: test: use esc sequences in vterm unit tests --- test/unit/vterm_spec.lua | 253 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 198 insertions(+), 55 deletions(-) (limited to 'test/unit') diff --git a/test/unit/vterm_spec.lua b/test/unit/vterm_spec.lua index 9f70187fad..bad2b3e658 100644 --- a/test/unit/vterm_spec.lua +++ b/test/unit/vterm_spec.lua @@ -17,6 +17,9 @@ local bit = require('bit') --- @field VTERM_KEY_NONE integer --- @field VTERM_KEY_TAB integer --- @field VTERM_KEY_UP integer +--- @field VTERM_KEY_BACKSPACE integer +--- @field VTERM_KEY_ESCAPE integer +--- @field VTERM_KEY_DEL integer --- @field VTERM_MOD_ALT integer --- @field VTERM_MOD_CTRL integer --- @field VTERM_MOD_SHIFT integer @@ -505,6 +508,18 @@ local function strp_key(input_key) return vterm.VTERM_KEY_ENTER end + if input_key == 'bs' then + return vterm.VTERM_KEY_BACKSPACE + end + + if input_key == 'del' then + return vterm.VTERM_KEY_DEL + end + + if input_key == 'esc' then + return vterm.VTERM_KEY_ESCAPE + end + if input_key == 'f1' then return vterm.VTERM_KEY_FUNCTION_0 + 1 end @@ -2324,68 +2339,83 @@ putglyph 1f3f4,200d,2620,fe0f 2 0,4]]) local vt = init() local state = wantstate(vt) - -- Disambiguate escape codes + -- Disambiguate escape codes enabled push('\x1b[>1u', vt) -- Unmodified ASCII - inchar(41, vt) - expect('output 29') - inchar(61, vt) - expect('output 3d') + inchar(0x41, vt) + expect_output('A') + inchar(0x61, vt) + expect_output('a') -- Ctrl modifier on ASCII letters - inchar(41, vt, { C = true }) - expect('output 1b,5b,34,31,3b,35,75') - inchar(61, vt, { C = true }) - expect('output 1b,5b,36,31,3b,35,75') + inchar(0x41, vt, { C = true }) + expect_output('\x1b[97;6u') + inchar(0x61, vt, { C = true }) + expect_output('\x1b[97;5u') -- Alt modifier on ASCII letters - inchar(41, vt, { A = true }) - expect('output 1b,29') - inchar(61, vt, { A = true }) - expect('output 1b,3d') + inchar(0x41, vt, { A = true }) + expect_output('\x1b[97;4u') + inchar(0x61, vt, { A = true }) + expect_output('\x1b[97;3u') -- Ctrl-Alt modifier on ASCII letters - inchar(41, vt, { C = true, A = true }) - expect('output 1b,5b,34,31,3b,37,75') - inchar(61, vt, { C = true, A = true }) - expect('output 1b,5b,36,31,3b,37,75') - - -- Special handling of Ctrl-I - inchar(49, vt) - expect('output 31') - inchar(69, vt) - expect('output 45') - inchar(49, vt, { C = true }) - expect('output 1b,5b,34,39,3b,35,75') - inchar(69, vt, { C = true }) - expect('output 1b,5b,36,39,3b,35,75') - inchar(49, vt, { A = true }) - expect('output 1b,31') - inchar(69, vt, { A = true }) - expect('output 1b,45') - inchar(49, vt, { A = true, C = true }) - expect('output 1b,5b,34,39,3b,37,75') - inchar(69, vt, { A = true, C = true }) - expect('output 1b,5b,36,39,3b,37,75') + inchar(0x41, vt, { C = true, A = true }) + expect_output('\x1b[97;8u') + inchar(0x61, vt, { C = true, A = true }) + expect_output('\x1b[97;7u') + + -- Ctrl-I is disambiguated + inchar(0x49, vt) + expect_output('I') + inchar(0x69, vt) + expect_output('i') + inchar(0x49, vt, { C = true }) + expect_output('\x1b[105;6u') + inchar(0x69, vt, { C = true }) + expect_output('\x1b[105;5u') + inchar(0x49, vt, { A = true }) + expect_output('\x1b[105;4u') + inchar(0x69, vt, { A = true }) + expect_output('\x1b[105;3u') + inchar(0x49, vt, { A = true, C = true }) + expect_output('\x1b[105;8u') + inchar(0x69, vt, { A = true, C = true }) + expect_output('\x1b[105;7u') + + -- Ctrl+Digits + for i = 0, 9 do + local c = 0x30 + i + inchar(c, vt) + expect_output(tostring(i)) + inchar(c, vt, { C = true }) + expect_output(string.format('\x1b[%d;5u', c)) + inchar(c, vt, { C = true, S = true }) + expect_output(string.format('\x1b[%d;6u', c)) + inchar(c, vt, { C = true, A = true }) + expect_output(string.format('\x1b[%d;7u', c)) + inchar(c, vt, { C = true, A = true, S = true }) + expect_output(string.format('\x1b[%d;8u', c)) + end -- Special handling of Space - inchar(20, vt) - expect('output 14') - inchar(20, vt, { S = true }) - expect('output 14') - inchar(20, vt, { C = true }) - expect('output 1b,5b,32,30,3b,35,75') - inchar(20, vt, { C = true, S = true }) - expect('output 1b,5b,32,30,3b,35,75') - inchar(20, vt, { A = true }) - expect('output 1b,14') - inchar(20, vt, { S = true, A = true }) - expect('output 1b,14') - inchar(20, vt, { C = true, A = true }) - expect('output 1b,5b,32,30,3b,37,75') - inchar(20, vt, { S = true, C = true, A = true }) - expect('output 1b,5b,32,30,3b,37,75') + inchar(0x20, vt) + expect_output(' ') + inchar(0x20, vt, { S = true }) + expect_output('\x1b[32;2u') + inchar(0x20, vt, { C = true }) + expect_output('\x1b[32;5u') + inchar(0x20, vt, { C = true, S = true }) + expect_output('\x1b[32;6u') + inchar(0x20, vt, { A = true }) + expect_output('\x1b[32;3u') + inchar(0x20, vt, { S = true, A = true }) + expect_output('\x1b[32;4u') + inchar(0x20, vt, { C = true, A = true }) + expect_output('\x1b[32;7u') + inchar(0x20, vt, { S = true, C = true, A = true }) + expect_output('\x1b[32;8u') -- Cursor keys in reset (cursor) mode inkey('up', vt) @@ -2416,21 +2446,65 @@ putglyph 1f3f4,200d,2620,fe0f 2 0,4]]) inkey('up', vt, { C = true }) expect_output('\x1b[1;5A') - -- Shift-Tab should be different + -- Tab inkey('tab', vt) expect_output('\x09') inkey('tab', vt, { S = true }) - expect_output('\x1b[Z') + expect_output('\x1b[9;2u') inkey('tab', vt, { C = true }) expect_output('\x1b[9;5u') inkey('tab', vt, { A = true }) - expect_output('\x1b\x09') + expect_output('\x1b[9;3u') inkey('tab', vt, { C = true, A = true }) expect_output('\x1b[9;7u') + -- Backspace + inkey('bs', vt) + expect_output('\x7f') + inkey('bs', vt, { S = true }) + expect_output('\x1b[127;2u') + inkey('bs', vt, { C = true }) + expect_output('\x1b[127;5u') + inkey('bs', vt, { A = true }) + expect_output('\x1b[127;3u') + inkey('bs', vt, { C = true, A = true }) + expect_output('\x1b[127;7u') + + -- DEL + inkey('del', vt) + expect_output('\x1b[3~') + inkey('del', vt, { S = true }) + expect_output('\x1b[3;2~') + inkey('del', vt, { C = true }) + expect_output('\x1b[3;5~') + inkey('del', vt, { A = true }) + expect_output('\x1b[3;3~') + inkey('del', vt, { C = true, A = true }) + expect_output('\x1b[3;7~') + + -- ESC + inkey('esc', vt) + expect_output('\x1b[27;1u') + inkey('esc', vt, { S = true }) + expect_output('\x1b[27;2u') + inkey('esc', vt, { C = true }) + expect_output('\x1b[27;5u') + inkey('esc', vt, { A = true }) + expect_output('\x1b[27;3u') + inkey('esc', vt, { C = true, A = true }) + expect_output('\x1b[27;7u') + -- Enter in linefeed mode inkey('enter', vt) expect_output('\x0d') + inkey('enter', vt, { S = true }) + expect_output('\x1b[13;2u') + inkey('enter', vt, { C = true }) + expect_output('\x1b[13;5u') + inkey('enter', vt, { A = true }) + expect_output('\x1b[13;3u') + inkey('enter', vt, { C = true, A = true }) + expect_output('\x1b[13;7u') -- Enter in newline mode push('\x1b[20h', vt) @@ -2451,7 +2525,7 @@ putglyph 1f3f4,200d,2620,fe0f 2 0,4]]) -- Keypad in DECKPNM inkey('kp0', vt) - expect_output('0') + expect_output('\x1b[57399;1u') -- Keypad in DECKPAM push('\x1b=', vt) @@ -2482,7 +2556,76 @@ putglyph 1f3f4,200d,2620,fe0f 2 0,4]]) vterm.vterm_state_focus_out(state) expect_output('\x1b[O') + -- Disambiguate escape codes disabled push('\x1b[ Date: Tue, 21 Jan 2025 21:00:56 +0800 Subject: feat(terminal): forward X1 and X2 mouse events Ref: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Other-buttons --- test/unit/vterm_spec.lua | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'test/unit') diff --git a/test/unit/vterm_spec.lua b/test/unit/vterm_spec.lua index bad2b3e658..c5293a21cb 100644 --- a/test/unit/vterm_spec.lua +++ b/test/unit/vterm_spec.lua @@ -2079,6 +2079,18 @@ putglyph 1f3f4,200d,2620,fe0f 2 0,4]]) mousebtn('u', 1, vt) expect_output('\x1b[<0;301;301m') + -- Button 8 on SGR extended encoding mode + mousebtn('d', 8, vt) + expect_output('\x1b[<128;301;301M') + mousebtn('u', 8, vt) + expect_output('\x1b[<128;301;301m') + + -- Button 9 on SGR extended encoding mode + mousebtn('d', 9, vt) + expect_output('\x1b[<129;301;301M') + mousebtn('u', 9, vt) + expect_output('\x1b[<129;301;301m') + -- DECRQM on SGR extended encoding mode push('\x1b[?1005$p', vt) expect_output('\x1b[?1005;2$y') @@ -2094,6 +2106,18 @@ putglyph 1f3f4,200d,2620,fe0f 2 0,4]]) mousebtn('u', 1, vt) expect_output('\x1b[3;301;301M') + -- Button 8 on rxvt extended encoding mode + mousebtn('d', 8, vt) + expect_output('\x1b[128;301;301M') + mousebtn('u', 8, vt) + expect_output('\x1b[3;301;301M') + + -- Button 9 on rxvt extended encoding mode + mousebtn('d', 9, vt) + expect_output('\x1b[129;301;301M') + mousebtn('u', 9, vt) + expect_output('\x1b[3;301;301M') + -- DECRQM on rxvt extended encoding mode push('\x1b[?1005$p', vt) expect_output('\x1b[?1005;2$y') -- cgit From a7be4b7bf857de9680ee3d1723a9f616e8a20776 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Tue, 28 Jan 2025 20:32:40 -0500 Subject: test(unit/strings_spec): provide context for vim_snprintf tests Since these assertions all use a common function to perform the test assertions, it's difficult to figure out which test failed: ERROR test/unit/testutil.lua @ 785: vim_snprintf() positional arguments test/unit/testutil.lua:757: test/unit/testutil.lua:741: (string) ' test/unit/strings_spec.lua:143: Expected objects to be the same. Passed in: (number) 6400 Expected: (number) 6' exit code: 256 Adding context to the assertion makes it clearer what the problem is: ERROR test/unit/testutil.lua @ 785: vim_snprintf() positional arguments test/unit/testutil.lua:757: test/unit/testutil.lua:741: (string) ' test/unit/strings_spec.lua:149: snprintf(buf, 0, "%1$0.*2$b", cdata: 0xf78d0f38, cdata: 0xf78dc4e0) = 001100 Expected objects to be the same. Passed in: (number) 6400 Expected: (number) 6' exit code: 256 --- test/unit/strings_spec.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'test/unit') diff --git a/test/unit/strings_spec.lua b/test/unit/strings_spec.lua index 25cdc27b28..86d528f0e8 100644 --- a/test/unit/strings_spec.lua +++ b/test/unit/strings_spec.lua @@ -140,7 +140,13 @@ end) describe('vim_snprintf()', function() local function a(expected, buf, bsize, fmt, ...) - eq(#expected, strings.vim_snprintf(buf, bsize, fmt, ...)) + local args = { ... } + local ctx = string.format('snprintf(buf, %d, "%s"', bsize, fmt) + for _, x in ipairs(args) do + ctx = ctx .. ', ' .. tostring(x) + end + ctx = ctx .. string.format(') = %s', expected) + eq(#expected, strings.vim_snprintf(buf, bsize, fmt, ...), ctx) if bsize > 0 then local actual = ffi.string(buf, math.min(#expected + 1, bsize)) eq(expected:sub(1, bsize - 1) .. '\0', actual) -- cgit From 1426f3f3ce91816351412f8cdf5849b76fd5a4a0 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Tue, 28 Jan 2025 20:54:32 -0500 Subject: test(unit/strings_spec): use correct type for binary values When 9.0.1856 was ported, the numbers being formatted as binary were cast to "unsigned int" rather than uvarnumber_T, as is done upstream. --- test/unit/strings_spec.lua | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'test/unit') diff --git a/test/unit/strings_spec.lua b/test/unit/strings_spec.lua index 86d528f0e8..2b7a4d6261 100644 --- a/test/unit/strings_spec.lua +++ b/test/unit/strings_spec.lua @@ -1,6 +1,7 @@ local t = require('test.unit.testutil') local itp = t.gen_itp(it) +local child_call_once = t.child_call_once local cimport = t.cimport local eq = t.eq local ffi = t.ffi @@ -8,6 +9,12 @@ local to_cstr = t.to_cstr local strings = cimport('stdlib.h', './src/nvim/strings.h', './src/nvim/memory.h') +local UVARNUM_TYPE + +child_call_once(function() + UVARNUM_TYPE = ffi.typeof('uvarnumber_T') +end) + describe('vim_strsave_escaped()', function() local vim_strsave_escaped = function(s, chars) local res = strings.vim_strsave_escaped(to_cstr(s), to_cstr(chars)) @@ -153,6 +160,9 @@ describe('vim_snprintf()', function() end end + local function uv(n) + return ffi.cast(UVARNUM_TYPE, n) + end local function i(n) return ffi.cast('int', n) end @@ -187,7 +197,7 @@ describe('vim_snprintf()', function() a(' 1234567', buf, bsize, '%9ld', l(1234567)) a('1234567 ', buf, bsize, '%-9ld', l(1234567)) a('deadbeef', buf, bsize, '%x', u(0xdeadbeef)) - a('001100', buf, bsize, '%06b', u(12)) + a('001100', buf, bsize, '%06b', uv(12)) a('one two', buf, bsize, '%s %s', 'one', 'two') a('1.234000', buf, bsize, '%f', 1.234) a('1.234000e+00', buf, bsize, '%e', 1.234) @@ -229,10 +239,10 @@ describe('vim_snprintf()', function() a('three one two', buf, bsize, '%3$s %1$s %2$s', 'one', 'two', 'three') a('1234567', buf, bsize, '%1$d', i(1234567)) a('deadbeef', buf, bsize, '%1$x', u(0xdeadbeef)) - a('001100', buf, bsize, '%2$0*1$b', i(6), u(12)) - a('001100', buf, bsize, '%1$0.*2$b', u(12), i(6)) + a('001100', buf, bsize, '%2$0*1$b', i(6), uv(12)) + a('001100', buf, bsize, '%1$0.*2$b', uv(12), i(6)) a('one two', buf, bsize, '%1$s %2$s', 'one', 'two') - a('001100', buf, bsize, '%06b', u(12)) + a('001100', buf, bsize, '%06b', uv(12)) a('two one', buf, bsize, '%2$s %1$s', 'one', 'two') a('1.234000', buf, bsize, '%1$f', 1.234) a('1.234000e+00', buf, bsize, '%1$e', 1.234) -- cgit