xaizek / libvle (License: GPLv3+) (since 2019-04-21)
Library for building Vim-like applications.
Commit 1a23cc48f87656b617c276d053948de7fcbd09ec

Synchronize with Vifm
Includes:
* bug fixes
* foreign keys
* small refactoring
Author: xaizek
Author date (UTC): 2022-02-07 09:43
Committer name: xaizek
Committer date (UTC): 2022-02-07 09:43
Parent(s): 007e559a5fdf94372d590925d07a58c9de82cc85
Signing key: 99DC5E4DB05F6BE2
Tree: 2c47bd9df18f28081ae9e34dd92561034b5215c1
File Lines added Lines deleted
Modes.cpp 2 2
engine/keys.c 111 37
engine/keys.h 21 10
File Modes.cpp changed (mode: 100644) (index e95b5ff..dd5aa08)
... ... static inline void
37 37 keyHandler(key_info_t key_info, keys_info_t */*keys_info*/) keyHandler(key_info_t key_info, keys_info_t */*keys_info*/)
38 38 { {
39 39 auto handler = static_cast<std::function<Shortcut::handler_t> *>( auto handler = static_cast<std::function<Shortcut::handler_t> *>(
40 key_info.context
40 key_info.user_data
41 41 ); );
42 42 (*handler)(key_info.count); (*handler)(key_info.count);
43 43 } }
 
... ... Modes::init(std::vector<Mode> &&modesInfo)
96 96 keys_add_info_t addInfo = {}; keys_add_info_t addInfo = {};
97 97 addInfo.info.data.handler = &keyHandler; addInfo.info.data.handler = &keyHandler;
98 98 addInfo.info.descr = sh.descr.c_str(); addInfo.info.descr = sh.descr.c_str();
99 addInfo.info.context = const_cast<void *>(
99 addInfo.info.user_data = const_cast<void *>(
100 100 static_cast<const void *>(&sh.handler) static_cast<const void *>(&sh.handler)
101 101 ); );
102 102
File engine/keys.c changed (mode: 100644) (index caec4f4..af51e05)
43 43 typedef enum typedef enum
44 44 { {
45 45 BUILTIN_WAIT_POINT, /* Infinite wait of next key press. */ BUILTIN_WAIT_POINT, /* Infinite wait of next key press. */
46 BUILTIN_KEYS, /* Normal builtin key. */
46 BUILTIN_KEYS, /* Normal builtin key or a foreign if in user's tree. */
47 47 BUILTIN_NIM_KEYS, /* NIM - number in the middle. */ BUILTIN_NIM_KEYS, /* NIM - number in the middle. */
48 48 USER_CMD, /* User mapping. */ USER_CMD, /* User mapping. */
49 49 } }
 
... ... static default_handler *def_handlers;
86 86 static size_t counter; static size_t counter;
87 87 /* Main external functions enter recursion level. */ /* Main external functions enter recursion level. */
88 88 static size_t enters_counter; static size_t enters_counter;
89 /* Current (when inside) or previous (when outside) sequence number of entering
90 * input resolution machinery. */
91 static size_t enter_seq;
89 92 /* Shows whether a mapping handler is being executed at the moment. */ /* Shows whether a mapping handler is being executed at the moment. */
90 93 static int inside_mapping; static int inside_mapping;
94 /* Current (when inside mapping) or previous (when outside) sequence number of a
95 * mapping state. It gets updated when a mapping handling is started within
96 * current input resolution request (basically unit was entered from outside
97 * and hasn't left yet). */
98 static int mapping_state;
99 /* Value of enter_seq variable last seen on processing a mapping. */
100 static size_t mapping_enter_seq;
91 101 /* User-provided callback for silencing UI. */ /* User-provided callback for silencing UI. */
92 102 static vle_silence_func silence_ui; static vle_silence_func silence_ui;
93 103
 
... ... static int
282 292 execute_keys_general_wrapper(const wchar_t keys[], int timed_out, int mapped, execute_keys_general_wrapper(const wchar_t keys[], int timed_out, int mapped,
283 293 int no_remap) int no_remap)
284 294 { {
285 int result;
286
287 enters_counter++;
288 result = execute_keys_general(keys, timed_out, mapped, no_remap);
289 enters_counter--;
295 if(++enters_counter == 1)
296 {
297 enter_seq = (enter_seq == INT_MAX ? 1 : enter_seq + 1);
298 }
299 int result = execute_keys_general(keys, timed_out, mapped, no_remap);
300 --enters_counter;
290 301
291 302 return result; return result;
292 303 } }
 
... ... dispatch_keys_at_root(const wchar_t keys[], keys_info_t *keys_info,
415 426 return KEYS_UNKNOWN; return KEYS_UNKNOWN;
416 427 } }
417 428
418 key_info.context = curr->conf.context;
429 key_info.user_data = curr->conf.user_data;
419 430
420 431 has_duplicate = root == &user_cmds_root[vle_mode_get()] && has_duplicate = root == &user_cmds_root[vle_mode_get()] &&
421 432 contains_chain(&builtin_cmds_root[vle_mode_get()], keys_start, keys); contains_chain(&builtin_cmds_root[vle_mode_get()], keys_start, keys);
422 433 result = execute_next_keys(curr, curr->type == USER_CMD ? keys : L"", result = execute_next_keys(curr, curr->type == USER_CMD ? keys : L"",
423 434 &key_info, keys_info, has_duplicate, no_remap); &key_info, keys_info, has_duplicate, no_remap);
435
424 436 if(curr->type == USER_CMD) if(curr->type == USER_CMD)
425 return result;
437 {
438 /* We've at least attempted to execute a user mapping. Maybe it did
439 * nothing, but it's also possible that it executed something and failed
440 * afterwards. Either way, finishing processing successfully is the
441 * best course of action. We either really did succeed or we need to
442 * avoid any further processing anyway (trying to interpret LHS of the
443 * mapping in a different way would be a mistake and waiting of any kind
444 * within a mapping can only be ignored). */
445 return 0;
446 }
447
426 448 if(IS_KEYS_RET_CODE(result)) if(IS_KEYS_RET_CODE(result))
427 449 { {
428 450 return (result == KEYS_WAIT_SHORT) ? KEYS_UNKNOWN : result; return (result == KEYS_WAIT_SHORT) ? KEYS_UNKNOWN : result;
 
... ... dispatch_keys_at_root(const wchar_t keys[], keys_info_t *keys_info,
441 463 return KEYS_WAIT_SHORT; return KEYS_WAIT_SHORT;
442 464 } }
443 465
444 key_info.context = curr->conf.context;
466 key_info.user_data = curr->conf.user_data;
445 467
446 468 has_duplicate = root == &user_cmds_root[vle_mode_get()] && has_duplicate = root == &user_cmds_root[vle_mode_get()] &&
447 469 contains_chain(&builtin_cmds_root[vle_mode_get()], keys_start, keys); contains_chain(&builtin_cmds_root[vle_mode_get()], keys_start, keys);
 
... ... dispatch_selector(const wchar_t keys[], keys_info_t *keys_info,
500 522 return KEYS_WAIT_SHORT; return KEYS_WAIT_SHORT;
501 523 } }
502 524
503 key_info.context = curr->conf.context;
525 key_info.user_data = curr->conf.user_data;
504 526
505 527 /* Execute the selector. */ /* Execute the selector. */
506 528 if(curr->conf.followed == FOLLOWED_BY_MULTIKEY && keys[0] != L'\0') if(curr->conf.followed == FOLLOWED_BY_MULTIKEY && keys[0] != L'\0')
 
... ... dispatch_selector(const wchar_t keys[], keys_info_t *keys_info,
539 561 return 0; return 0;
540 562 } }
541 563 /* Process the rest of the line. */ /* Process the rest of the line. */
542 return execute_keys_general(keys, keys_info->after_wait, 0, no_remap);
564 return execute_keys_general(keys, keys_info->after_wait, keys_info->mapped,
565 no_remap);
543 566 } }
544 567
545 568 /* Fills *key_info advancing input if necessary. Returns zero on success, /* Fills *key_info advancing input if necessary. Returns zero on success,
 
... ... execute_after_remapping(const wchar_t rhs[], const wchar_t left_keys[],
793 816 } }
794 817 if(key_info.count != NO_COUNT_GIVEN) if(key_info.count != NO_COUNT_GIVEN)
795 818 { {
819 /* XXX: this can insert thousand separators in some locales... */
796 820 vifm_swprintf(buf + wcslen(buf), ARRAY_LEN(buf) - wcslen(buf), L"%d", vifm_swprintf(buf + wcslen(buf), ARRAY_LEN(buf) - wcslen(buf), L"%d",
797 821 key_info.count); key_info.count);
798 822 } }
 
... ... execute_after_remapping(const wchar_t rhs[], const wchar_t left_keys[],
804 828 init_keys_info(&keys_info, 1); init_keys_info(&keys_info, 1);
805 829 } }
806 830
831 /* XXX: keys_info.mapped now covers both RHS of a mapping and the rest of
832 * the keys (`left_keys`)! This is bad. */
833
807 834 enter_chunk(curr); enter_chunk(curr);
808 835 result = dispatch_keys(buf, &keys_info, curr->no_remap, NO_COUNT_GIVEN); result = dispatch_keys(buf, &keys_info, curr->no_remap, NO_COUNT_GIVEN);
809 836 leave_chunk(curr); leave_chunk(curr);
 
... ... combine_counts(int count_a, int count_b)
923 950 } }
924 951 } }
925 952
953 int
954 vle_keys_foreign_add(const wchar_t lhs[], const key_conf_t *info, int mode)
955 {
956 key_chunk_t *curr = add_keys_inner(&user_cmds_root[mode], lhs);
957 if(curr == NULL)
958 {
959 return -1;
960 }
961
962 if(curr->type == USER_CMD)
963 {
964 free(curr->conf.data.cmd);
965 }
966
967 curr->type = BUILTIN_KEYS;
968 curr->conf = *info;
969 return 0;
970 }
971
926 972 int int
927 973 vle_keys_user_add(const wchar_t lhs[], const wchar_t rhs[], int mode, vle_keys_user_add(const wchar_t lhs[], const wchar_t rhs[], int mode,
928 974 int flags) int flags)
 
... ... vle_keys_user_remove(const wchar_t keys[], int mode)
960 1006 if((curr = find_user_keys(keys, mode)) == NULL) if((curr = find_user_keys(keys, mode)) == NULL)
961 1007 return -1; return -1;
962 1008
963 free(curr->conf.data.cmd);
1009 if(curr->type == USER_CMD)
1010 {
1011 free(curr->conf.data.cmd);
1012 }
1013
964 1014 curr->type = BUILTIN_WAIT_POINT; curr->type = BUILTIN_WAIT_POINT;
965 1015 curr->conf.data.handler = NULL; curr->conf.data.handler = NULL;
966 1016
 
... ... find_user_keys(const wchar_t *keys, int mode)
1013 1063 curr = p; curr = p;
1014 1064 keys++; keys++;
1015 1065 } }
1016 return (curr->type == USER_CMD) ? curr : NULL;
1066 return curr;
1017 1067 } }
1018 1068
1019 1069 int int
 
... ... vle_keys_list(int mode, vle_keys_list_cb cb, int user_only)
1129 1179 const key_chunk_t *user = &user_cmds_root[mode]; const key_chunk_t *user = &user_cmds_root[mode];
1130 1180 const key_chunk_t *builtin = &builtin_cmds_root[mode]; const key_chunk_t *builtin = &builtin_cmds_root[mode];
1131 1181
1182 cb(L"", L"", "User mappings:");
1183
1132 1184 /* Don't traverse empty tries. */ /* Don't traverse empty tries. */
1133 1185 if(user->children_count != 0U) if(user->children_count != 0U)
1134 1186 { {
1135 1187 traverse_children(user, L"", &list_chunk, cb); traverse_children(user, L"", &list_chunk, cb);
1136
1137 /* Put separator only when it's needed. */
1138 if(!user_only && builtin->children_count != 0U)
1139 {
1140 cb(L"", L"", "");
1141 }
1142 1188 } }
1143 1189
1190 cb(L"", L"", "");
1191 cb(L"", L"", "Builtin mappings:");
1192
1144 1193 if(!user_only && builtin->children_count != 0U) if(!user_only && builtin->children_count != 0U)
1145 1194 { {
1146 1195 traverse_children(builtin, L"", &list_chunk, cb); traverse_children(builtin, L"", &list_chunk, cb);
 
... ... is_recursive(void)
1187 1236 } }
1188 1237
1189 1238 int int
1190 vle_keys_inside_mapping(void)
1239 vle_keys_mapping_state(void)
1191 1240 { {
1192 return (inside_mapping != 0);
1241 return (inside_mapping ? mapping_state : 0);
1193 1242 } }
1194 1243
1195 1244 /* Executes handler for a mapping, if any. Error or success code is /* Executes handler for a mapping, if any. Error or success code is
 
... ... execute_mapping_handler(const key_conf_t *info, key_info_t key_info,
1212 1261 static void static void
1213 1262 pre_execute_mapping_handler(const keys_info_t *keys_info) pre_execute_mapping_handler(const keys_info_t *keys_info)
1214 1263 { {
1215 inside_mapping += keys_info->mapped != 0;
1216 assert(inside_mapping >= 0 && "Calls to pre/post funcs should be balanced");
1264 if(keys_info->mapped)
1265 {
1266 ++inside_mapping;
1267 assert(inside_mapping >= 0 && "Calls to pre/post funcs should be balanced");
1268
1269 if(inside_mapping == 1 && mapping_enter_seq != enter_seq)
1270 {
1271 mapping_state = (mapping_state == INT_MAX ? 1 : mapping_state + 1);
1272 mapping_enter_seq = enter_seq;
1273 }
1274 }
1217 1275 } }
1218 1276
1219 1277 /* Post-execution of a mapping handler callback. */ /* Post-execution of a mapping handler callback. */
 
... ... keys_suggest(const key_chunk_t *root, const wchar_t keys[],
1258 1316 } }
1259 1317 } }
1260 1318
1261 /* Go to the next character if a match found. */
1319 /* Go to the next character if a match is found. */
1262 1320 if(p != NULL && p->key == *keys) if(p != NULL && p->key == *keys)
1263 1321 { {
1264 1322 ++keys; ++keys;
1265 1323 curr = p; curr = p;
1324
1325 /* Branch into matching selectors at every step to handle situation when
1326 * multichar selector and multichar command continuation are available at
1327 * the same time.. */
1328 if(curr->type == BUILTIN_WAIT_POINT &&
1329 curr->conf.followed == FOLLOWED_BY_SELECTOR)
1330 {
1331 /* Suggest selectors. */
1332 keys_suggest(&selectors_root[vle_mode_get()], keys, L"sel: ", cb,
1333 custom_only, fold_subkeys);
1334 }
1335
1266 1336 continue; continue;
1267 1337 } }
1268 1338
 
... ... keys_suggest(const key_chunk_t *root, const wchar_t keys[],
1306 1376
1307 1377 if(!custom_only && *keys == L'\0') if(!custom_only && *keys == L'\0')
1308 1378 { {
1379 if(curr->type == USER_CMD)
1380 {
1381 if(!curr->no_remap)
1382 {
1383 keys_suggest(&user_cmds_root[vle_mode_get()], curr->conf.data.cmd,
1384 prefix, cb, custom_only, fold_subkeys);
1385 }
1386 keys_suggest(&builtin_cmds_root[vle_mode_get()], curr->conf.data.cmd,
1387 prefix, cb, custom_only, fold_subkeys);
1388 return;
1389 }
1390
1309 1391 suggest_children(curr, prefix, cb, fold_subkeys); suggest_children(curr, prefix, cb, fold_subkeys);
1310 1392 } }
1311 1393
1312 if(curr->type == BUILTIN_WAIT_POINT)
1394 if(curr->type == BUILTIN_WAIT_POINT &&
1395 curr->conf.followed == FOLLOWED_BY_MULTIKEY)
1313 1396 { {
1314 if(curr->conf.followed == FOLLOWED_BY_SELECTOR)
1397 /* Invoke optional external function to provide suggestions. */
1398 if(curr->conf.suggest != NULL)
1315 1399 { {
1316 /* Suggest selectors. */
1317 keys_suggest(&selectors_root[vle_mode_get()], keys, L"sel: ", cb,
1318 custom_only, fold_subkeys);
1319 }
1320 else if(curr->conf.followed == FOLLOWED_BY_MULTIKEY)
1321 {
1322 /* Invoke optional external function to provide suggestions. */
1323 if(curr->conf.suggest != NULL)
1324 {
1325 curr->conf.suggest(cb);
1326 }
1400 curr->conf.suggest(cb);
1327 1401 } }
1328 1402 } }
1329 1403 } }
File engine/keys.h changed (mode: 100644) (index 188adcc..dacbb66)
... ... extern "C"
26 26 { {
27 27 #endif #endif
28 28
29 enum
30 {
31 MAX_LHS_LEN = 4
32 };
33
29 34 enum enum
30 35 { {
31 36 NO_COUNT_GIVEN = -1, NO_COUNT_GIVEN = -1,
 
... ... FollowedBy;
75 80 /* Describes single key (command or selector) on its own. */ /* Describes single key (command or selector) on its own. */
76 81 typedef struct typedef struct
77 82 { {
78 int count; /* Repeat count, may be equal NO_COUNT_GIVEN. */
79 int reg; /* Number of selected register. */
80 int multi; /* Multikey. */
81 void *context; /* User data for the key (can be NULL). */
83 int count; /* Repeat count, may be equal NO_COUNT_GIVEN. */
84 int reg; /* Number of selected register. */
85 int multi; /* Multikey. */
86 void *user_data; /* User data for the key (can be NULL). */
82 87 } }
83 88 key_info_t; key_info_t;
84 89
 
... ... keys_info_t;
99 104 typedef void (*vle_keys_handler)(key_info_t key_info, keys_info_t *keys_info); typedef void (*vle_keys_handler)(key_info_t key_info, keys_info_t *keys_info);
100 105 /* Type of function invoked by vle_keys_list() and vle_keys_suggest(). rhs is /* Type of function invoked by vle_keys_list() and vle_keys_suggest(). rhs is
101 106 * provided for user-defined keys and is empty otherwise. Description is empty * provided for user-defined keys and is empty otherwise. Description is empty
102 * for user-defined keys or when not set. */
107 * for user-defined keys or when not set. Extra messages have empty lhs and
108 * rhs, but can have non-empty description. */
103 109 typedef void (*vle_keys_list_cb)(const wchar_t lhs[], const wchar_t rhs[], typedef void (*vle_keys_list_cb)(const wchar_t lhs[], const wchar_t rhs[],
104 110 const char descr[]); const char descr[]);
105 111 /* User-provided suggestion callback for multikeys. */ /* User-provided suggestion callback for multikeys. */
 
... ... typedef struct
124 130 vle_suggest_func suggest; /* Suggestion function (can be NULL). Invoked for vle_suggest_func suggest; /* Suggestion function (can be NULL). Invoked for
125 131 multikeys. */ multikeys. */
126 132 const char *descr; /* Brief description of the key (can be NULL). */ const char *descr; /* Brief description of the key (can be NULL). */
127 void *context; /* User data for the key (can be NULL). */
133 void *user_data; /* User data for the key (can be NULL). */
128 134 int nim; /* Whether additional count in the middle is int nim; /* Whether additional count in the middle is
129 135 allowed. */ allowed. */
130 136 int skip_suggestion; /* Do not print this among suggestions. */ int skip_suggestion; /* Do not print this among suggestions. */
 
... ... key_conf_t;
133 139
134 140 typedef struct typedef struct
135 141 { {
136 const wchar_t keys[5];
142 const wchar_t keys[MAX_LHS_LEN + 1];
137 143 key_conf_t info; key_conf_t info;
138 144 } }
139 145 keys_add_info_t; keys_add_info_t;
 
... ... int vle_keys_add(keys_add_info_t cmds[], size_t len, int mode);
176 182 * error, otherwise zero is returned. */ * error, otherwise zero is returned. */
177 183 int vle_keys_add_selectors(keys_add_info_t cmds[], size_t len, int mode); int vle_keys_add_selectors(keys_add_info_t cmds[], size_t len, int mode);
178 184
185 /* Registers a foreign builtin-like key, but does it among user's keys.
186 * Returns non-zero on error, otherwise zero is returned. */
187 int vle_keys_foreign_add(const wchar_t lhs[], const key_conf_t *info, int mode);
188
179 189 /* Registers user key mapping. The flags parameter accepts combinations of /* Registers user key mapping. The flags parameter accepts combinations of
180 190 * KEYS_FLAG_*. Returns non-zero or error, otherwise zero is returned. */ * KEYS_FLAG_*. Returns non-zero or error, otherwise zero is returned. */
181 191 int vle_keys_user_add(const wchar_t keys[], const wchar_t rhs[], int mode, int vle_keys_user_add(const wchar_t keys[], const wchar_t rhs[], int mode,
 
... ... void vle_keys_list(int mode, vle_keys_list_cb cb, int user_only);
196 206 * difference of returned values. Returns the number. */ * difference of returned values. Returns the number. */
197 207 size_t vle_keys_counter(void); size_t vle_keys_counter(void);
198 208
199 /* Checks whether a mapping handler is currently been executed. Returns
200 * non-zero if so, otherwise zero is returned. */
201 int vle_keys_inside_mapping(void);
209 /* Retrieves current mapping state. Entering a mapping at the top level (so not
210 * a nested mapping) increases state number. Returns the state number or zero
211 * if no mapping is currently active. */
212 int vle_keys_mapping_state(void);
202 213
203 214 /* Invokes cb for each possible keys continuation. Intended to be used on /* Invokes cb for each possible keys continuation. Intended to be used on
204 215 * KEYS_WAIT and KEYS_WAIT_SHORT returns. The custom_only flag limits * KEYS_WAIT and KEYS_WAIT_SHORT returns. The custom_only flag limits
Hints

Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/libvle

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/libvle

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master