#include "cst_keymap.h" #include #include #include #define CHECK(c) \ do { \ if (!(c)) { \ fprintf(stderr, "%s:%d: CHECK(%s) failed\n", __FILE__, __LINE__, #c); \ exit(1); \ } \ } while (0) static int lookup1(const cst_keymap_t *km, int k, int *a) { int seq[1] = {k}; return cst_keymap_lookup(km, seq, 1, a); } static int lookup2(const cst_keymap_t *km, int k1, int k2, int *a) { int seq[2] = {k1, k2}; return cst_keymap_lookup(km, seq, 2, a); } static void test_parse(void) { char b[32]; CHECK(cst_key_parse("ctrl+p") == 16); CHECK(cst_key_parse("Ctrl+X") == 24); CHECK(cst_key_parse("tab") == 9); CHECK(cst_key_parse("shift+tab") == CST_K_BTAB); CHECK(cst_key_parse("alt+enter") == (13 | CST_KEY_ALT)); CHECK(cst_key_parse("alt+up") == CST_K_ALT_UP); CHECK(cst_key_parse("ctrl+home") == CST_K_CTRL_HOME); CHECK(cst_key_parse("f1") == CST_K_F1); CHECK(cst_key_parse("f12") == CST_K_F1 + 11); CHECK(cst_key_parse("pgdn") == CST_K_PGDN); CHECK(cst_key_parse("n") == 'n'); CHECK(cst_key_parse("é") == 0xE9); CHECK(cst_key_parse("ctrl+") == -1); CHECK(cst_key_parse("hyper+x") == -1); CHECK(cst_key_parse("f13") == -1); CHECK(cst_key_parse("ctrl+f1") == -1); CHECK(strcmp(cst_key_name(24, b, sizeof(b)), "ctrl+x") == 0); CHECK(strcmp(cst_key_name(CST_K_BTAB, b, sizeof(b)), "shift+tab") == 0); CHECK(strcmp(cst_key_name(13 | CST_KEY_ALT, b, sizeof(b)), "alt+enter") == 0); CHECK(strcmp(cst_key_name(CST_K_F1 + 1, b, sizeof(b)), "f2") == 0); for (int a = 1; a < CST_KA_COUNT; a++) { const cst_key_action_info_t *in = cst_key_action_info(a); CHECK(in && in->name && in->group && in->desc); CHECK(cst_key_action_find(in->name) == a); } } static void test_defaults(void) { cst_keymap_t km; cst_keymap_defaults(&km); int a = 0; CHECK(lookup1(&km, 16, &a) == CST_KM_MATCH && a == CST_KA_PALETTE); CHECK(lookup1(&km, 24, &a) == CST_KM_PREFIX); CHECK(lookup2(&km, 24, 'n', &a) == CST_KM_MATCH && a == CST_KA_NEW_SESSION); CHECK(lookup2(&km, 24, 'z', &a) == CST_KM_NONE); CHECK(lookup1(&km, 'q', &a) == CST_KM_NONE); CHECK(lookup1(&km, CST_K_F1, &a) == CST_KM_MATCH && a == CST_KA_KEYS); char d[64]; cst_keymap_describe(&km, CST_KA_KEYS, d, sizeof(d)); CHECK(strcmp(d, "ctrl+x h, f1") == 0); char conf[256]; CHECK(cst_keymap_conflicts(&km, conf, sizeof(conf)) == 0); } static void test_json(void) { cst_keymap_t km; cst_keymap_defaults(&km); char err[128]; CHECK(cst_keymap_apply_json(&km, "{\"leader\":\"ctrl+g\",\"palette\":\"f2\"," "\"todo\":\"none\",\"model\":[\" M\"," "\"alt+m\"]}", err, sizeof(err)) == 0); int a = 0; CHECK(lookup1(&km, 16, &a) == CST_KM_NONE); CHECK(lookup1(&km, CST_K_F1 + 1, &a) == CST_KM_MATCH && a == CST_KA_PALETTE); CHECK(lookup1(&km, 20, &a) == CST_KM_NONE); /* ctrl+t unbound */ CHECK(lookup1(&km, 24, &a) == CST_KM_NONE); /* ctrl+x no longer leader */ CHECK(lookup2(&km, 7, 'n', &a) == CST_KM_MATCH && a == CST_KA_NEW_SESSION); CHECK(lookup2(&km, 7, 'M', &a) == CST_KM_MATCH && a == CST_KA_MODEL); CHECK(lookup2(&km, 7, 'm', &a) == CST_KM_NONE); CHECK(lookup1(&km, 'm' | CST_KEY_ALT, &a) == CST_KM_MATCH && a == CST_KA_MODEL); int user = 0; for (int i = 0; i < km.n; i++) if (km.b[i].action == CST_KA_MODEL) user += km.b[i].user; CHECK(user == 2); /* bad entries are reported, the rest still applies */ cst_keymap_defaults(&km); CHECK(cst_keymap_apply_json(&km, "{\"nope\":\"f3\",\"undo\":\"ctrl+\"," "\"retry\":\"f4\"}", err, sizeof(err)) == -1); CHECK(strstr(err, "nope") != NULL); CHECK(lookup1(&km, CST_K_F1 + 3, &a) == CST_KM_MATCH && a == CST_KA_RETRY); CHECK(cst_keymap_apply_json(&km, "[1]", err, sizeof(err)) == -1); /* conflicts: same key twice, and a single key shadowing a prefix */ cst_keymap_defaults(&km); CHECK(cst_keymap_apply_json(&km, "{\"undo\":\"ctrl+o\",\"redo_none\":1}", err, sizeof(err)) == -1); char conf[256]; CHECK(cst_keymap_conflicts(&km, conf, sizeof(conf)) == 1); CHECK(strstr(conf, "ctrl+o") && strstr(conf, "undo")); cst_keymap_defaults(&km); CHECK(cst_keymap_apply_json(&km, "{\"copy\":\"ctrl+x\"}", err, sizeof(err)) == 0); CHECK(cst_keymap_conflicts(&km, conf, sizeof(conf)) > 0); } static int kitty(int cp, int mods, int *a, int *b) { int out[2] = {-1, -1}; int n = cst_key_from_kitty(cp, mods, out); *a = out[0], *b = out[1]; return n; } static void test_kitty(void) { int a, b; CHECK(kitty('c', 4, &a, &b) == 1 && a == 3); /* ctrl+c */ CHECK(kitty('a', 5, &a, &b) == 1 && a == 1); /* ctrl+shift+a */ CHECK(kitty(' ', 4, &a, &b) == 1 && a == 0); /* ctrl+space */ CHECK(kitty('[', 4, &a, &b) == 1 && a == 27); /* ctrl+[ */ CHECK(kitty('b', 2, &a, &b) == 2 && a == 27 && b == 'b'); CHECK(kitty('b', 3, &a, &b) == 2 && a == 27 && b == 'B'); CHECK(kitty('1', 3, &a, &b) == 2 && a == 27 && b == '!'); CHECK(kitty('x', 6, &a, &b) == 2 && a == 27 && b == 24); /* ctrl+alt+x */ CHECK(kitty(27, 0, &a, &b) == 1 && a == 27); CHECK(kitty(13, 0, &a, &b) == 1 && a == 13); CHECK(kitty(13, 1, &a, &b) == 2 && a == 27 && b == 13); /* shift+enter */ CHECK(kitty(13, 4, &a, &b) == 2 && a == 27 && b == 13); /* ctrl+enter */ CHECK(kitty(127, 4, &a, &b) == 2 && a == 27 && b == 127); CHECK(kitty(127, 1, &a, &b) == 1 && a == 127); CHECK(kitty(9, 1, &a, &b) == 1 && a == CST_K_BTAB); CHECK(kitty(9, 4, &a, &b) == 1 && a == 9); CHECK(kitty('a', 8, &a, &b) == 0); /* super+a */ CHECK(kitty(5, 4, &a, &b) == 0); /* the result goes through the keymap like legacy input */ cst_keymap_t km; cst_keymap_defaults(&km); int act = 0; kitty(13, 1, &a, &b); CHECK(lookup1(&km, b | CST_KEY_ALT, &act) == CST_KM_MATCH && act == CST_KA_NEWLINE); } int main(void) { test_parse(); test_defaults(); test_json(); test_kitty(); puts("test_keymap: ok"); return 0; }