[keymap] handle invalid command better

Related to #1258
master
Tim Stack 4 weeks ago
parent 99c6aabfdd
commit 5b88dbd162

@ -90,6 +90,7 @@ Interface changes:
can now hide/show fields by moving the cursor line to the can now hide/show fields by moving the cursor line to the
given field and pressing the space bar or by clicking on given field and pressing the space bar or by clicking on
the diamond with the mouse. the diamond with the mouse.
* The `sv` keymap binds `§` to focus the breadcrumb bar.
Bug Fixes: Bug Fixes:
* With the recent xz backdoor shenanigans, it seems like a good * With the recent xz backdoor shenanigans, it seems like a good

@ -803,7 +803,7 @@
"title": "/ui/keymap-defs/<keymap_name>/<key_seq>/command", "title": "/ui/keymap-defs/<keymap_name>/<key_seq>/command",
"description": "The command to execute for the given key sequence. Use a script to execute more complicated operations.", "description": "The command to execute for the given key sequence. Use a script to execute more complicated operations.",
"type": "string", "type": "string",
"pattern": "^[:|;].*", "pattern": "^$|^[:|;].*",
"examples": [ "examples": [
":goto next hour" ":goto next hour"
] ]

@ -785,6 +785,17 @@ execute_from_file(exec_context& ec,
Result<std::string, lnav::console::user_message> Result<std::string, lnav::console::user_message>
execute_any(exec_context& ec, const std::string& cmdline_with_mode) execute_any(exec_context& ec, const std::string& cmdline_with_mode)
{ {
if (cmdline_with_mode.empty()) {
auto um = lnav::console::user_message::error("empty command")
.with_help(
"a command should start with ':', ';', '/', '|' and "
"followed by the operation to perform");
if (!ec.ec_source.empty()) {
um.with_snippet(ec.ec_source.back());
}
return Err(um);
}
std::string retval, alt_msg, cmdline = cmdline_with_mode.substr(1); std::string retval, alt_msg, cmdline = cmdline_with_mode.substr(1);
auto _cleanup = finally([&ec] { auto _cleanup = finally([&ec] {
if (ec.is_read_write() && if (ec.is_read_write() &&

@ -166,8 +166,12 @@ handle_keyseq(const char* keyseq)
vars["keyseq"] = keyseq; vars["keyseq"] = keyseq;
const auto& kc = iter->second; const auto& kc = iter->second;
log_debug("executing key sequence %s: %s", keyseq, kc.kc_cmd.c_str()); log_debug(
auto result = execute_any(ec, kc.kc_cmd); "executing key sequence %s: %s", keyseq, kc.kc_cmd.pp_value.c_str());
auto sg = ec.enter_source(kc.kc_cmd.pp_location.sl_source,
kc.kc_cmd.pp_location.sl_line_number,
kc.kc_cmd.pp_value);
auto result = execute_any(ec, kc.kc_cmd.pp_value);
if (result.isOk()) { if (result.isOk()) {
lnav_data.ld_rl_view->set_value(result.unwrap()); lnav_data.ld_rl_view->set_value(result.unwrap());
} else { } else {
@ -194,7 +198,7 @@ handle_keyseq(const char* keyseq)
} }
bool bool
handle_paging_key(int ch) handle_paging_key(int ch, const char* keyseq)
{ {
if (lnav_data.ld_view_stack.empty()) { if (lnav_data.ld_view_stack.empty()) {
return false; return false;
@ -211,8 +215,7 @@ handle_paging_key(int ch)
} }
} }
auto keyseq = fmt::format(FMT_STRING("x{:02x}"), ch); if (handle_keyseq(keyseq)) {
if (handle_keyseq(keyseq.c_str())) {
return true; return true;
} }

@ -31,6 +31,6 @@
#define LNAV_HOTKEYS_H #define LNAV_HOTKEYS_H
bool handle_keyseq(const char* keyseq); bool handle_keyseq(const char* keyseq);
bool handle_paging_key(int ch); bool handle_paging_key(int ch, const char* keyseq);
#endif // LNAV_HOTKEYS_H #endif // LNAV_HOTKEYS_H

@ -102,8 +102,12 @@ input_dispatcher::new_input(const struct timeval& current_time, int ch)
for (int lpc = 0; this->id_escape_buffer[lpc]; for (int lpc = 0; this->id_escape_buffer[lpc];
lpc++) lpc++)
{ {
snprintf(keyseq.data(),
keyseq.size(),
"x%02x",
this->id_escape_buffer[lpc] & 0xff);
handled = this->id_key_handler( handled = this->id_key_handler(
this->id_escape_buffer[lpc]); this->id_escape_buffer[lpc], keyseq.data());
} }
this->id_escape_index = 0; this->id_escape_index = 0;
break; break;
@ -121,6 +125,13 @@ input_dispatcher::new_input(const struct timeval& current_time, int ch)
{ {
this->id_escape_index = 0; this->id_escape_index = 0;
} }
} else if (ch > 0xff) {
if (KEY_F(0) <= ch && ch <= KEY_F(64)) {
snprintf(keyseq.data(), keyseq.size(), "f%d", ch - KEY_F0);
} else {
snprintf(keyseq.data(), keyseq.size(), "n%04o", ch);
}
handled = this->id_key_handler(ch, keyseq.data());
} else { } else {
auto seq_size = utf::utf8::char_size([ch]() { auto seq_size = utf::utf8::char_size([ch]() {
return std::make_pair(ch, 16); return std::make_pair(ch, 16);
@ -128,7 +139,7 @@ input_dispatcher::new_input(const struct timeval& current_time, int ch)
if (seq_size == 1) { if (seq_size == 1) {
snprintf(keyseq.data(), keyseq.size(), "x%02x", ch & 0xff); snprintf(keyseq.data(), keyseq.size(), "x%02x", ch & 0xff);
handled = this->id_key_handler(ch); handled = this->id_key_handler(ch, keyseq.data());
} else { } else {
this->reset_escape_buffer(ch, current_time, seq_size); this->reset_escape_buffer(ch, current_time, seq_size);
} }
@ -150,7 +161,8 @@ input_dispatcher::poll(const struct timeval& current_time)
timersub(&current_time, &this->id_escape_start_time, &diff); timersub(&current_time, &this->id_escape_start_time, &diff);
if (escape_threshold < diff) { if (escape_threshold < diff) {
this->id_key_handler(KEY_ESCAPE); static const char ESC_KEYSEQ[] = "\x1b";
this->id_key_handler(KEY_ESCAPE, ESC_KEYSEQ);
this->id_escape_index = 0; this->id_escape_index = 0;
} }
} }

@ -53,7 +53,7 @@ public:
}; };
std::function<escape_match_t(const char*)> id_escape_matcher; std::function<escape_match_t(const char*)> id_escape_matcher;
std::function<bool(int)> id_key_handler; std::function<bool(int, const char*)> id_key_handler;
std::function<void(const char*)> id_escape_handler; std::function<void(const char*)> id_escape_handler;
std::function<void()> id_mouse_handler; std::function<void()> id_mouse_handler;
std::function<void(const char*)> id_unhandled_handler; std::function<void(const char*)> id_unhandled_handler;

@ -22,7 +22,8 @@
"command": ";UPDATE lnav_views SET paused = 1 - paused" "command": ";UPDATE lnav_views SET paused = 1 - paused"
}, },
"x60": { "x60": {
"id": "" "id": "",
"command": ":prompt breadcrumb"
}, },
"xc2xa7": { "xc2xa7": {
"id": "org.lnav.key.breadcrumb.focus", "id": "org.lnav.key.breadcrumb.focus",

@ -367,7 +367,7 @@ sigchld(int sig)
} }
static void static void
handle_rl_key(int ch) handle_rl_key(int ch, const char* keyseq)
{ {
switch (ch) { switch (ch) {
case KEY_F(2): case KEY_F(2):
@ -379,7 +379,7 @@ handle_rl_key(int ch)
case KEY_PPAGE: case KEY_PPAGE:
case KEY_NPAGE: case KEY_NPAGE:
case KEY_CTRL('p'): case KEY_CTRL('p'):
handle_paging_key(ch); handle_paging_key(ch, keyseq);
break; break;
case KEY_CTRL(']'): case KEY_CTRL(']'):
@ -675,7 +675,7 @@ update_view_position(listview_curses* lv)
} }
static bool static bool
handle_config_ui_key(int ch) handle_config_ui_key(int ch, const char* keyseq)
{ {
bool retval = false; bool retval = false;
@ -730,14 +730,14 @@ handle_config_ui_key(int ch)
lnav_data.ld_filter_view.reload_data(); lnav_data.ld_filter_view.reload_data();
lnav_data.ld_status[LNS_FILTER].set_needs_update(); lnav_data.ld_status[LNS_FILTER].set_needs_update();
} else { } else {
return handle_paging_key(ch); return handle_paging_key(ch, keyseq);
} }
return true; return true;
} }
static bool static bool
handle_key(int ch) handle_key(int ch, const char* keyseq)
{ {
static auto* breadcrumb_view = injector::get<breadcrumb_curses*>(); static auto* breadcrumb_view = injector::get<breadcrumb_curses*>();
@ -749,7 +749,7 @@ handle_key(int ch)
default: { default: {
switch (lnav_data.ld_mode) { switch (lnav_data.ld_mode) {
case ln_mode_t::PAGING: case ln_mode_t::PAGING:
return handle_paging_key(ch); return handle_paging_key(ch, keyseq);
case ln_mode_t::BREADCRUMBS: case ln_mode_t::BREADCRUMBS:
if (ch == '`' || !breadcrumb_view->handle_key(ch)) { if (ch == '`' || !breadcrumb_view->handle_key(ch)) {
@ -761,7 +761,7 @@ handle_key(int ch)
case ln_mode_t::FILTER: case ln_mode_t::FILTER:
case ln_mode_t::FILES: case ln_mode_t::FILES:
return handle_config_ui_key(ch); return handle_config_ui_key(ch, keyseq);
case ln_mode_t::SPECTRO_DETAILS: { case ln_mode_t::SPECTRO_DETAILS: {
if (ch == '\t' || ch == 'q') { if (ch == '\t' || ch == 'q') {
@ -800,7 +800,7 @@ handle_key(int ch)
case ln_mode_t::SQL: case ln_mode_t::SQL:
case ln_mode_t::EXEC: case ln_mode_t::EXEC:
case ln_mode_t::USER: case ln_mode_t::USER:
handle_rl_key(ch); handle_rl_key(ch, keyseq);
break; break;
case ln_mode_t::BUSY: case ln_mode_t::BUSY:

@ -5971,9 +5971,7 @@ readline_context::command_t STD_COMMANDS[] = {
"The initial value to fill in for the prompt") "The initial value to fill in for the prompt")
.optional()) .optional())
.with_example({ .with_example({
"To open the command prompt with 'filter-in' already " "To open the command prompt with 'filter-in' already filled in",
"filled "
"in",
"command : 'filter-in '", "command : 'filter-in '",
}) })
.with_example({ .with_example({

@ -498,7 +498,7 @@ static const struct json_path_container key_command_handlers = {
.with_description( .with_description(
"The command to execute for the given key sequence. Use a script " "The command to execute for the given key sequence. Use a script "
"to execute more complicated operations.") "to execute more complicated operations.")
.with_pattern("^[:|;].*") .with_pattern("^$|^[:|;].*")
.with_example(":goto next hour") .with_example(":goto next hour")
.for_field(&key_command::kc_cmd), .for_field(&key_command::kc_cmd),
yajlpp::property_handler("alt-msg") yajlpp::property_handler("alt-msg")
@ -520,6 +520,14 @@ static const struct json_path_container keymap_def_handlers = {
[](const yajlpp_provider_context& ypc, key_map* km) { [](const yajlpp_provider_context& ypc, key_map* km) {
auto& retval = km->km_seq_to_cmd[ypc.get_substr("key_seq")]; auto& retval = km->km_seq_to_cmd[ypc.get_substr("key_seq")];
if (ypc.ypc_parse_context != nullptr) {
retval.kc_cmd.pp_path
= ypc.ypc_parse_context->get_full_path();
retval.kc_cmd.pp_location.sl_source
= ypc.ypc_parse_context->ypc_source;
retval.kc_cmd.pp_location.sl_line_number
= ypc.ypc_parse_context->get_line_number();
}
return &retval; return &retval;
}) })
.with_path_provider<key_map>( .with_path_provider<key_map>(
@ -1570,8 +1578,12 @@ public:
for (const auto& pair : for (const auto& pair :
lnav_config.lc_ui_keymaps[lnav_config.lc_ui_keymap].km_seq_to_cmd) lnav_config.lc_ui_keymaps[lnav_config.lc_ui_keymap].km_seq_to_cmd)
{ {
lnav_config.lc_active_keymap.km_seq_to_cmd[pair.first] if (pair.second.kc_cmd.pp_value.empty()) {
= pair.second; lnav_config.lc_active_keymap.km_seq_to_cmd.erase(pair.first);
} else {
lnav_config.lc_active_keymap.km_seq_to_cmd[pair.first]
= pair.second;
}
} }
auto& ec = injector::get<exec_context&>(); auto& ec = injector::get<exec_context&>();
@ -1580,31 +1592,52 @@ public:
continue; continue;
} }
auto keyseq_sf = string_fragment::from_str(pair.first);
std::string keystr; std::string keystr;
if (keyseq_sf.startswith("f")) {
auto sv = string_fragment::from_str(pair.first).to_string_view(); auto sv = keyseq_sf.to_string_view();
while (!sv.empty()) {
int32_t value; int32_t value;
auto scan_res = scn::scan(sv, "x{:2x}", value); auto scan_res = scn::scan(sv, "f{}", value);
if (!scan_res) { if (!scan_res) {
throw "invalid hex input"; log_error("invalid function key sequence: %s", keyseq_sf);
continue;
} }
auto ch = (char) (value & 0xff); if (value < 0 || value > 64) {
switch (ch) { log_error("invalid function key number: %s", keyseq_sf);
case '\t': continue;
keystr.append("TAB"); }
break;
case '\r': keystr = toupper(pair.first);
keystr.append("ENTER"); } else {
break; auto sv
default: = string_fragment::from_str(pair.first).to_string_view();
keystr.push_back(ch); while (!sv.empty()) {
int32_t value;
auto scan_res = scn::scan(sv, "x{:2x}", value);
if (!scan_res) {
log_error("invalid key sequence: %s",
pair.first.c_str());
break; break;
}
auto ch = (char) (value & 0xff);
switch (ch) {
case '\t':
keystr.append("TAB");
break;
case '\r':
keystr.append("ENTER");
break;
default:
keystr.push_back(ch);
break;
}
sv = scan_res.range_as_string_view();
} }
sv = scan_res.range_as_string_view();
} }
ec.ec_global_vars[pair.second.kc_id] = keystr; if (!keystr.empty()) {
ec.ec_global_vars[pair.second.kc_id] = keystr;
}
} }
} }
}; };
@ -1908,7 +1941,7 @@ save_config()
void void
reload_config(std::vector<lnav::console::user_message>& errors) reload_config(std::vector<lnav::console::user_message>& errors)
{ {
lnav_config_listener* curr = lnav_config_listener::LISTENER_LIST; auto* curr = lnav_config_listener::LISTENER_LIST;
while (curr != nullptr) { while (curr != nullptr) {
auto reporter = [&errors](const void* cfg_value, auto reporter = [&errors](const void* cfg_value,

@ -81,7 +81,7 @@ void install_extra_formats();
struct key_command { struct key_command {
std::string kc_id; std::string kc_id;
std::string kc_cmd; positioned_property<std::string> kc_cmd;
std::string kc_alt_msg; std::string kc_alt_msg;
}; };

@ -5118,7 +5118,7 @@
}, },
"x60": { "x60": {
"id": "", "id": "",
"command": "", "command": ":prompt breadcrumb",
"alt-msg": "" "alt-msg": ""
}, },
"xc2xa4": { "xc2xa4": {

@ -155,10 +155,11 @@
/ui/keymap-defs/sv/x26/command -> sv-keymap.json:16 /ui/keymap-defs/sv/x26/command -> sv-keymap.json:16
/ui/keymap-defs/sv/x2b/command -> sv-keymap.json:22 /ui/keymap-defs/sv/x2b/command -> sv-keymap.json:22
/ui/keymap-defs/sv/x3d/command -> sv-keymap.json:19 /ui/keymap-defs/sv/x3d/command -> sv-keymap.json:19
/ui/keymap-defs/sv/x60/command -> sv-keymap.json:26
/ui/keymap-defs/sv/x60/id -> sv-keymap.json:25 /ui/keymap-defs/sv/x60/id -> sv-keymap.json:25
/ui/keymap-defs/sv/xc2xa4/command -> sv-keymap.json:10 /ui/keymap-defs/sv/xc2xa4/command -> sv-keymap.json:10
/ui/keymap-defs/sv/xc2xa7/command -> sv-keymap.json:29 /ui/keymap-defs/sv/xc2xa7/command -> sv-keymap.json:30
/ui/keymap-defs/sv/xc2xa7/id -> sv-keymap.json:28 /ui/keymap-defs/sv/xc2xa7/id -> sv-keymap.json:29
/ui/keymap-defs/sv/xe2x82xac/command -> sv-keymap.json:13 /ui/keymap-defs/sv/xe2x82xac/command -> sv-keymap.json:13
/ui/keymap-defs/uk/x22/command -> uk-keymap.json:7 /ui/keymap-defs/uk/x22/command -> uk-keymap.json:7
/ui/keymap-defs/uk/xc2xa3/command -> uk-keymap.json:10 /ui/keymap-defs/uk/xc2xa3/command -> uk-keymap.json:10

Loading…
Cancel
Save