forked from WHC/Hermit
Query CSI ? u at startup and push the disambiguate flag on the alternate screen when the terminal answers; CSI u reports are mapped back to legacy input (Shift/Ctrl+Enter insert a newline, Ctrl+Backspace deletes a word). Ctrl/Alt+Left/Right move by word, Ctrl/Alt+Delete delete the next word. Our define_key codes move above the range ncurses uses for terminfo extended keys.
164 lines
6.4 KiB
C
164 lines
6.4 KiB
C
#include "cst_keymap.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#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\":[\"<leader> 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;
|
|
}
|