mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-03-15 05:33:58 -04:00
search/config: configurable key bindings for (scrollback) search mode
This commit is contained in:
parent
c1653c7237
commit
919f31ffcb
4 changed files with 213 additions and 9 deletions
|
|
@ -20,6 +20,7 @@
|
||||||
controlled by the **scrollback-indicator-position** and
|
controlled by the **scrollback-indicator-position** and
|
||||||
**scrollback-indicator-format** options in `footrc`
|
**scrollback-indicator-format** options in `footrc`
|
||||||
(https://codeberg.org/dnkl/foot/issues/42).
|
(https://codeberg.org/dnkl/foot/issues/42).
|
||||||
|
* Key bindings in _scollback search_ mode are now configurable.
|
||||||
|
|
||||||
|
|
||||||
### Deprecated
|
### Deprecated
|
||||||
|
|
|
||||||
133
config.c
133
config.c
|
|
@ -48,7 +48,7 @@ static const uint32_t default_bright[] = {
|
||||||
0xffffff,
|
0xffffff,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *binding_action_map[] = {
|
static const char *const binding_action_map[] = {
|
||||||
[BIND_ACTION_NONE] = NULL,
|
[BIND_ACTION_NONE] = NULL,
|
||||||
[BIND_ACTION_SCROLLBACK_UP] = "scrollback-up",
|
[BIND_ACTION_SCROLLBACK_UP] = "scrollback-up",
|
||||||
[BIND_ACTION_SCROLLBACK_DOWN] = "scrollback-down",
|
[BIND_ACTION_SCROLLBACK_DOWN] = "scrollback-down",
|
||||||
|
|
@ -70,6 +70,29 @@ static const char *binding_action_map[] = {
|
||||||
static_assert(ALEN(binding_action_map) == BIND_ACTION_COUNT,
|
static_assert(ALEN(binding_action_map) == BIND_ACTION_COUNT,
|
||||||
"binding action map size mismatch");
|
"binding action map size mismatch");
|
||||||
|
|
||||||
|
static const char *const search_binding_action_map[] = {
|
||||||
|
[BIND_ACTION_SEARCH_NONE] = NULL,
|
||||||
|
[BIND_ACTION_SEARCH_CANCEL] = "cancel",
|
||||||
|
[BIND_ACTION_SEARCH_COMMIT] = "commit",
|
||||||
|
[BIND_ACTION_SEARCH_FIND_PREV] = "find-prev",
|
||||||
|
[BIND_ACTION_SEARCH_FIND_NEXT] = "find-next",
|
||||||
|
[BIND_ACTION_SEARCH_EDIT_LEFT] = "cursor-left",
|
||||||
|
[BIND_ACTION_SEARCH_EDIT_LEFT_WORD] = "cursor-left-word",
|
||||||
|
[BIND_ACTION_SEARCH_EDIT_RIGHT] = "cursor-right",
|
||||||
|
[BIND_ACTION_SEARCH_EDIT_RIGHT_WORD] = "cursor-right-word",
|
||||||
|
[BIND_ACTION_SEARCH_EDIT_HOME] = "cursor-home",
|
||||||
|
[BIND_ACTION_SEARCH_EDIT_END] = "cursor-end",
|
||||||
|
[BIND_ACTION_SEARCH_DELETE_PREV] = "delete-prev",
|
||||||
|
[BIND_ACTION_SEARCH_DELETE_PREV_WORD] = "delete-prev-word",
|
||||||
|
[BIND_ACTION_SEARCH_DELETE_NEXT] = "delete-next",
|
||||||
|
[BIND_ACTION_SEARCH_DELETE_NEXT_WORD] = "delete-next-word",
|
||||||
|
[BIND_ACTION_SEARCH_EXTEND_WORD] = "extend-to-word-boundary",
|
||||||
|
[BIND_ACTION_SEARCH_EXTEND_WORD_WS] = "extend-to-next-whitespace",
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(ALEN(search_binding_action_map) == BIND_ACTION_SEARCH_COUNT,
|
||||||
|
"search binding action map size mismatch");
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
get_shell(void)
|
get_shell(void)
|
||||||
{
|
{
|
||||||
|
|
@ -523,6 +546,7 @@ parse_section_csd(const char *key, const char *value, struct config *conf,
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
verify_key_combo(const struct config *conf, enum bind_action_normal action,
|
verify_key_combo(const struct config *conf, enum bind_action_normal action,
|
||||||
|
const char *const binding_action_map[],
|
||||||
const char *combo, const char *path, unsigned lineno)
|
const char *combo, const char *path, unsigned lineno)
|
||||||
{
|
{
|
||||||
tll_foreach(conf->bindings.key, it) {
|
tll_foreach(conf->bindings.key, it) {
|
||||||
|
|
@ -602,7 +626,7 @@ parse_section_key_bindings(
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!verify_key_combo(conf, action, value, path, lineno)) {
|
if (!verify_key_combo(conf, action, binding_action_map, value, path, lineno)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -641,6 +665,95 @@ parse_section_key_bindings(
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
parse_section_search_bindings(
|
||||||
|
const char *key, const char *value, struct config *conf,
|
||||||
|
const char *path, unsigned lineno)
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
const char *pipe_cmd = NULL;
|
||||||
|
size_t pipe_len = 0;
|
||||||
|
|
||||||
|
if (value[0] == '[') {
|
||||||
|
const char *pipe_cmd_end = strrchr(value, ']');
|
||||||
|
if (pipe_cmd_end == NULL) {
|
||||||
|
LOG_ERR("%s:%d: unclosed '['", path, lineno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pipe_cmd = &value[1];
|
||||||
|
pipe_len = pipe_cmd_end - pipe_cmd;
|
||||||
|
|
||||||
|
value = pipe_cmd_end + 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (enum bind_action_search action = 0;
|
||||||
|
action < BIND_ACTION_SEARCH_COUNT;
|
||||||
|
action++)
|
||||||
|
{
|
||||||
|
if (search_binding_action_map[action] == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (strcmp(key, search_binding_action_map[action]) != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (strcasecmp(value, "none") == 0) {
|
||||||
|
tll_foreach(conf->bindings.search, it) {
|
||||||
|
if (it->item.action == action) {
|
||||||
|
free(it->item.key);
|
||||||
|
// free(it->item.pipe_cmd);
|
||||||
|
tll_remove(conf->bindings.search, it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!verify_key_combo(conf, action, search_binding_action_map, value, path, lineno)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool already_added = false;
|
||||||
|
tll_foreach(conf->bindings.search, it) {
|
||||||
|
if (it->item.action == action
|
||||||
|
#if 0
|
||||||
|
&&
|
||||||
|
((it->item.pipe_cmd == NULL && pipe_cmd == NULL) ||
|
||||||
|
(it->item.pipe_cmd != NULL && pipe_cmd != NULL &&
|
||||||
|
strncmp(it->item.pipe_cmd, pipe_cmd, pipe_len) == 0))
|
||||||
|
#endif
|
||||||
|
)
|
||||||
|
{
|
||||||
|
|
||||||
|
free(it->item.key);
|
||||||
|
// free(it->item.pipe_cmd);
|
||||||
|
|
||||||
|
it->item.key = strdup(value);
|
||||||
|
#if 0
|
||||||
|
it->item.pipe_cmd = pipe_cmd != NULL
|
||||||
|
? strndup(pipe_cmd, pipe_len) : NULL;
|
||||||
|
#endif
|
||||||
|
already_added = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!already_added) {
|
||||||
|
struct config_key_binding_search binding = {
|
||||||
|
.action = action,
|
||||||
|
.key = strdup(value),
|
||||||
|
// .pipe_cmd = pipe_cmd != NULL ? strndup(pipe_cmd, pipe_len) : NULL,
|
||||||
|
};
|
||||||
|
tll_push_back(conf->bindings.search, binding);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_ERR("%s:%u: invalid key: %s", path, lineno, key);
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
parse_section_mouse_bindings(
|
parse_section_mouse_bindings(
|
||||||
const char *key, const char *value, struct config *conf,
|
const char *key, const char *value, struct config *conf,
|
||||||
|
|
@ -785,6 +898,7 @@ parse_config_file(FILE *f, struct config *conf, const char *path)
|
||||||
SECTION_CURSOR,
|
SECTION_CURSOR,
|
||||||
SECTION_CSD,
|
SECTION_CSD,
|
||||||
SECTION_KEY_BINDINGS,
|
SECTION_KEY_BINDINGS,
|
||||||
|
SECTION_SEARCH_BINDINGS,
|
||||||
SECTION_MOUSE_BINDINGS,
|
SECTION_MOUSE_BINDINGS,
|
||||||
SECTION_TWEAK,
|
SECTION_TWEAK,
|
||||||
SECTION_COUNT,
|
SECTION_COUNT,
|
||||||
|
|
@ -799,13 +913,14 @@ parse_config_file(FILE *f, struct config *conf, const char *path)
|
||||||
parser_fun_t fun;
|
parser_fun_t fun;
|
||||||
const char *name;
|
const char *name;
|
||||||
} section_info[] = {
|
} section_info[] = {
|
||||||
[SECTION_MAIN] = {&parse_section_main, "main"},
|
[SECTION_MAIN] = {&parse_section_main, "main"},
|
||||||
[SECTION_COLORS] = {&parse_section_colors, "colors"},
|
[SECTION_COLORS] = {&parse_section_colors, "colors"},
|
||||||
[SECTION_CURSOR] = {&parse_section_cursor, "cursor"},
|
[SECTION_CURSOR] = {&parse_section_cursor, "cursor"},
|
||||||
[SECTION_CSD] = {&parse_section_csd, "csd"},
|
[SECTION_CSD] = {&parse_section_csd, "csd"},
|
||||||
[SECTION_KEY_BINDINGS] = {&parse_section_key_bindings, "key-bindings"},
|
[SECTION_KEY_BINDINGS] = {&parse_section_key_bindings, "key-bindings"},
|
||||||
[SECTION_MOUSE_BINDINGS] = {&parse_section_mouse_bindings, "mouse-bindings"},
|
[SECTION_SEARCH_BINDINGS] = {&parse_section_search_bindings, "search-bindings"},
|
||||||
[SECTION_TWEAK] = {&parse_section_tweak, "tweak"},
|
[SECTION_MOUSE_BINDINGS] = {&parse_section_mouse_bindings, "mouse-bindings"},
|
||||||
|
[SECTION_TWEAK] = {&parse_section_tweak, "tweak"},
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(ALEN(section_info) == SECTION_COUNT, "section info array size mismatch");
|
static_assert(ALEN(section_info) == SECTION_COUNT, "section info array size mismatch");
|
||||||
|
|
|
||||||
|
|
@ -265,6 +265,76 @@ e.g. *search-start=none*.
|
||||||
|
|
||||||
Default: _not bound_
|
Default: _not bound_
|
||||||
|
|
||||||
|
# SECTION: search-bindings
|
||||||
|
|
||||||
|
This section lets you override the default key bindings used in
|
||||||
|
scrollback search mode. The syntax is exactly the same as the regular
|
||||||
|
**key-bindings**.
|
||||||
|
|
||||||
|
*cancel*
|
||||||
|
Aborts the search. The viewport is restored and the _primary
|
||||||
|
selection_ is **not** updated. Default: _Control+g Escape_.
|
||||||
|
|
||||||
|
*commit*
|
||||||
|
Exit search mode and copy current selection into the _primary
|
||||||
|
selection_. Viewport is **not** restored. To copy the selection to
|
||||||
|
the regular _clipboard_, use *Control+Shift+C*. Default: _Return_.
|
||||||
|
|
||||||
|
*find-prev*
|
||||||
|
Search **backwards** in the scrollback history for the next
|
||||||
|
match. Default: _Control+r_.
|
||||||
|
|
||||||
|
*find-next*
|
||||||
|
Searchs **forwards** in the scrollback history for the next
|
||||||
|
match. Default: _Control+s_.
|
||||||
|
|
||||||
|
*cursor-left*
|
||||||
|
Moves the cursor in the search box one **character** to the
|
||||||
|
left. Default: _Left Control+b_.
|
||||||
|
|
||||||
|
*cursor-left-word*
|
||||||
|
Moves the cursor in the search box one **word** to the
|
||||||
|
left. Default: _Control+Left Mod1+b_.
|
||||||
|
|
||||||
|
*cursor-right*
|
||||||
|
Moves the cursor in the search box one **character** to the
|
||||||
|
right. Default: _Right Control+f_.
|
||||||
|
|
||||||
|
*cursor-right-word*
|
||||||
|
Moves the cursor in the search box one **word** to the
|
||||||
|
right. Default: _Control+Left Mod1+b_.
|
||||||
|
|
||||||
|
*cursor-home*
|
||||||
|
Moves the cursor in the search box to the beginning of the
|
||||||
|
input. Default: _Home Control+a_.
|
||||||
|
|
||||||
|
*cursor-end*
|
||||||
|
Moves the cursor in the search box to the end of the
|
||||||
|
input. Default: _End Control+e_.
|
||||||
|
|
||||||
|
*delete-prev*
|
||||||
|
Deletes the **character before** the cursor. Default: _BackSpace_.
|
||||||
|
|
||||||
|
*delete-prev-word*
|
||||||
|
Deletes the **word before** the cursor. Default: _Mod1+BackSpace
|
||||||
|
Control+BackSpace_.
|
||||||
|
|
||||||
|
*delete-next*
|
||||||
|
Deletes the **character after** the cursor. Default: _Delete_.
|
||||||
|
|
||||||
|
*delete-next-word*
|
||||||
|
Deletes the **word after** the cursor. Default: _Mod1+b
|
||||||
|
Control+Delete_.
|
||||||
|
|
||||||
|
*extend-to-word-boundary*
|
||||||
|
Extend current selection to the next word boundary. Default:
|
||||||
|
_Control+w_.
|
||||||
|
|
||||||
|
*extend-to-next-whitespace*
|
||||||
|
Extend the current selection to the next whitespace. Default:
|
||||||
|
_Control+Shift+W_.
|
||||||
|
|
||||||
|
|
||||||
# SECTION: mouse-bindings
|
# SECTION: mouse-bindings
|
||||||
|
|
||||||
This section lets you override the default mouse bindings.
|
This section lets you override the default mouse bindings.
|
||||||
|
|
|
||||||
18
footrc
18
footrc
|
|
@ -63,5 +63,23 @@
|
||||||
# pipe-visible=[sh -c "xurls | bemenu | xargs -r firefox"] none
|
# pipe-visible=[sh -c "xurls | bemenu | xargs -r firefox"] none
|
||||||
# pipe-scrollback=[sh -c "xurls | bemenu | xargs -r firefox"] none
|
# pipe-scrollback=[sh -c "xurls | bemenu | xargs -r firefox"] none
|
||||||
|
|
||||||
|
[search-bindings]
|
||||||
|
# cancel=Control+g Escape
|
||||||
|
# commit=Return
|
||||||
|
# find-prev=Control+r
|
||||||
|
# find-next=Control+s
|
||||||
|
# cursor-left=Left Control+b
|
||||||
|
# cursor-left-word=Control+Left Mod1+b
|
||||||
|
# cursor-right=Right Control+f
|
||||||
|
# cursor-right-word=Control+Right Mod1+f
|
||||||
|
# cursor-home=Home Control+a
|
||||||
|
# cursor-end=End Control+e
|
||||||
|
# delete-prev=BackSpace
|
||||||
|
# delete-prev-word=Mod1+BackSpace Control+BackSpace
|
||||||
|
# delete-next=Delete
|
||||||
|
# delete-next-word=Mod1+d Control+Delete
|
||||||
|
# extend-to-word-boundary=Control+w
|
||||||
|
# extend-to-next-whitespace=Control+Shift+W
|
||||||
|
|
||||||
[mouse-bindings]
|
[mouse-bindings]
|
||||||
# primary-paste=BTN_MIDDLE
|
# primary-paste=BTN_MIDDLE
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue