[keymap] convert more hotkeys to keymaps

Fixes #416
This commit is contained in:
Timothy Stack 2017-03-01 08:44:16 -08:00
parent d12769cd70
commit 71aa54ad4d
11 changed files with 275 additions and 72 deletions

View File

@ -162,15 +162,21 @@ string execute_sql(exec_context &ec, const string &sql, string &alt_msg)
SQLITE_TRANSIENT);
}
else if (name[0] == '$') {
map<string, string> &vars = ec.ec_local_vars.top();
map<string, string>::iterator local_var;
map<string, string> &lvars = ec.ec_local_vars.top();
map<string, string> &gvars = ec.ec_global_vars;
map<string, string>::iterator local_var, global_var;
const char *env_value;
if ((local_var = vars.find(&name[1])) != vars.end()) {
if ((local_var = lvars.find(&name[1])) != lvars.end()) {
sqlite3_bind_text(stmt.in(), lpc + 1,
local_var->second.c_str(), -1,
SQLITE_TRANSIENT);
}
else if ((global_var = gvars.find(&name[1])) != gvars.end()) {
sqlite3_bind_text(stmt.in(), lpc + 1,
global_var->second.c_str(), -1,
SQLITE_TRANSIENT);
}
else if ((env_value = getenv(&name[1])) != NULL) {
sqlite3_bind_text(stmt.in(), lpc + 1, env_value, -1, SQLITE_STATIC);
}

View File

@ -58,6 +58,7 @@ struct exec_context {
std::map<std::string, std::string> ec_override;
std::vector<logline_value> *ec_line_values;
std::stack<std::map<std::string, std::string> > ec_local_vars;
std::map<std::string, std::string> ec_global_vars;
std::stack<std::string> ec_path_stack;
std::string ec_accumulator;

View File

@ -299,24 +299,6 @@ void handle_paging_key(int ch)
lnav_data.ld_rl_view->set_value("Cleared bookmarks");
break;
case 'e':
moveto_cluster(&bookmark_vector<vis_line_t>::next,
&logfile_sub_source::BM_ERRORS,
tc->get_top());
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_2(
w, W,
"to move forward/backward through warning messages"));
break;
case 'E':
moveto_cluster(&bookmark_vector<vis_line_t>::prev,
&logfile_sub_source::BM_ERRORS,
tc->get_top());
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_2(
w, W,
"to move forward/backward through warning messages"));
break;
case 'w':
moveto_cluster(&bookmark_vector<vis_line_t>::next,
&logfile_sub_source::BM_WARNINGS,
@ -335,24 +317,6 @@ void handle_paging_key(int ch)
"to move to next/previous hour boundary"));
break;
case 'n':
moveto_cluster(&bookmark_vector<vis_line_t>::next,
&textview_curses::BM_SEARCH,
search_forward_from(tc));
lnav_data.ld_bottom_source.grep_error("");
lnav_data.ld_rl_view->set_alt_value(
"Press '" ANSI_BOLD(">") "' or '" ANSI_BOLD("<")
"' to scroll horizontally to a search result");
break;
case 'N':
previous_cluster(&textview_curses::BM_SEARCH, tc);
lnav_data.ld_bottom_source.grep_error("");
lnav_data.ld_rl_view->set_alt_value(
"Press '" ANSI_BOLD(">") "' or '" ANSI_BOLD("<")
"' to scroll horizontally to a search result");
break;
case 'y':
tc->set_top(bm[&BM_QUERY].next(tc->get_top()));
break;
@ -502,17 +466,6 @@ void handle_paging_key(int ch)
break;
}
case 'm':
lnav_data.ld_last_user_mark[tc] = tc->get_top();
tc->toggle_user_mark(&textview_curses::BM_USER,
vis_line_t(lnav_data.ld_last_user_mark[tc]));
tc->reload_data();
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_2(
u, U,
"to move forward/backward through user bookmarks"));
break;
case 'J':
if (lnav_data.ld_last_user_mark.find(tc) ==
lnav_data.ld_last_user_mark.end() ||

View File

@ -1,8 +1,36 @@
{
"global": {
"keymap_def_alt_warning": "Press ${ansi_bold}w${ansi_norm}/${ansi_bold}W${ansi_norm} to move forward/backward through ${ansi_yellow}warning${ansi_norm} messages",
"keymap_def_scroll_horiz": "Press \\'${ansi_bold}>${ansi_norm}\\' or \\'${ansi_bold}<${ansi_norm}\\' to scroll horizontally to a search result",
"keymap_def_next_user_mark": "Press ${ansi_bold}u${ansi_norm}/${ansi_bold}U${ansi_norm} to move forward/backward through user bookmarks"
},
"keymap_def": {
"default": {
"x67": ":goto 0",
"x50": ":switch-to-view pretty"
"x45": [
":prev-mark error",
":eval :alt-msg ${keymap_def_alt_warning}"
],
"x65": [
":next-mark error",
":eval :alt-msg ${keymap_def_alt_warning}"
],
"x67": [":goto 0"],
"x6d": [
":mark",
":eval :alt-msg ${keymap_def_next_user_mark}"
],
"x4e": [
":prev-mark search",
":eval :alt-msg ${keymap_def_scroll_horiz}"
],
"x6e": [
":next-mark search",
":eval :alt-msg ${keymap_def_scroll_horiz}"
],
"x50": [":switch-to-view pretty"]
}
}
}

View File

@ -134,6 +134,7 @@
#include "field_overlay_source.hh"
#include "url_loader.hh"
#include "log_search_table.hh"
#include "shlex.hh"
using namespace std;
@ -241,6 +242,22 @@ public:
};
};
static void add_global_vars(exec_context &ec)
{
for (const auto &iter : lnav_config.lc_global_vars) {
shlex subber(iter.second);
string str;
if (!subber.eval(str, ec.ec_global_vars)) {
log_error("Unable to evaluate global variable value: %s",
iter.second.c_str());
continue;
}
ec.ec_global_vars[iter.first] = str;
}
}
bool setup_logline_table()
{
// Hidden columns don't show up in the table_info pragma.
@ -791,7 +808,8 @@ static void open_pretty_view(void)
format->annotate(sbr, sa, values);
exec_context ec(&values, pretty_sql_callback, pretty_pipe_callback);
add_ansi_vars(ec.ec_local_vars.top());
add_ansi_vars(ec.ec_global_vars);
add_global_vars(ec);
format->rewrite(ec, sbr, sa, rewritten_line);
data_scanner ds(rewritten_line);
@ -1699,9 +1717,11 @@ static void handle_key(int ch) {
const auto &iter = km.km_seq_to_cmd.find(keyseq);
if (iter != km.km_seq_to_cmd.end()) {
log_debug("executing key sequence x%02x: %s",
keyseq, iter->second.c_str());
execute_any(lnav_data.ld_exec_context, iter->second);
for (string cmd : iter->second) {
log_debug("executing key sequence x%02x: %s",
keyseq, cmd.c_str());
execute_any(lnav_data.ld_exec_context, cmd);
}
return;
}
}
@ -2524,7 +2544,7 @@ int main(int argc, char *argv[])
lnav_data.ld_exec_context.ec_pipe_callback = pipe_callback;
lnav_data.ld_program_name = argv[0];
add_ansi_vars(ec.ec_local_vars.top());
add_ansi_vars(ec.ec_global_vars);
rl_readline_name = "lnav";
@ -2666,6 +2686,7 @@ int main(int argc, char *argv[])
print_errors(config_errors);
return EXIT_FAILURE;
}
add_global_vars(ec);
string formats_path = dotlnav_path("formats/");

View File

@ -348,6 +348,23 @@ static string com_relative_goto(exec_context &ec, string cmdline, vector<string>
return retval;
}
static string com_mark(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "";
if (args.empty()) {
} else {
textview_curses *tc = lnav_data.ld_view_stack.top();
lnav_data.ld_last_user_mark[tc] = tc->get_top();
tc->toggle_user_mark(&textview_curses::BM_USER,
vis_line_t(lnav_data.ld_last_user_mark[tc]));
tc->reload_data();
}
return retval;
}
static string com_goto_mark(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "";
@ -368,11 +385,14 @@ static string com_goto_mark(exec_context &ec, string cmdline, vector<string> &ar
retval = "error: unknown bookmark type";
}
else {
moveto_cluster(args[0] == "next-mark" ?
&bookmark_vector<vis_line_t>::next :
&bookmark_vector<vis_line_t>::prev,
bt,
tc->get_top());
if (args[0] == "next-mark") {
moveto_cluster(&bookmark_vector<vis_line_t>::next,
bt,
search_forward_from(tc));
} else {
previous_cluster(bt, tc);
}
lnav_data.ld_bottom_source.grep_error("");
}
}
@ -466,8 +486,12 @@ static string com_save_to(exec_context &ec, string cmdline, vector<string> &args
vector<string> split_args;
shlex lexer(fn);
scoped_resolver scopes = {
&ec.ec_local_vars.top(),
&ec.ec_global_vars,
};
if (!lexer.split(split_args, ec.ec_local_vars.top())) {
if (!lexer.split(split_args, scopes)) {
return "error: unable to parse arguments";
}
if (split_args.size() > 1) {
@ -1313,8 +1337,12 @@ static string com_open(exec_context &ec, string cmdline, vector<string> &args)
vector<string> split_args;
shlex lexer(pat);
scoped_resolver scopes = {
&ec.ec_local_vars.top(),
&ec.ec_global_vars,
};
if (!lexer.split(split_args, ec.ec_local_vars.top())) {
if (!lexer.split(split_args, scopes)) {
return "error: unable to parse arguments";
}
@ -2279,6 +2307,31 @@ static string com_echo(exec_context &ec, string cmdline, vector<string> &args)
return retval;
}
static string com_alt_msg(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "error: expecting a message";
if (args.empty()) {
}
else if (args.size() == 1) {
if (lnav_data.ld_rl_view != NULL) {
lnav_data.ld_rl_view->set_alt_value("");
}
}
else {
string msg = remaining_args(cmdline, args);
if (lnav_data.ld_rl_view != NULL) {
lnav_data.ld_rl_view->set_alt_value(msg);
}
retval = "";
}
return retval;
}
static string com_eval(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "error: expecting a command or query to evaluate";
@ -2292,7 +2345,10 @@ static string com_eval(exec_context &ec, string cmdline, vector<string> &args)
shlex lexer(all_args.c_str(), all_args.size());
log_debug("Evaluating: %s", all_args.c_str());
if (!lexer.eval(expanded_cmd, ec.ec_local_vars.top())) {
if (!lexer.eval(expanded_cmd, {
&ec.ec_local_vars.top(),
&ec.ec_global_vars,
})) {
return "error: invalid arguments";
}
log_debug("Expanded command to evaluate: %s", expanded_cmd.c_str());
@ -2821,6 +2877,12 @@ readline_context::command_t STD_COMMANDS[] = {
"Move the current view up or down by the given amount",
com_relative_goto,
},
{
"mark",
NULL,
"Toggle the bookmark state for the top line in the current view",
com_mark,
},
{
"next-mark",
"error|warning|search|user|file|partition",
@ -3095,6 +3157,12 @@ readline_context::command_t STD_COMMANDS[] = {
"Echo the given message",
com_echo,
},
{
"alt-msg",
"<msg>",
"Display a message in the alternate command position",
com_alt_msg,
},
{
"eval",
"<msg>",

View File

@ -238,7 +238,7 @@ struct userdata {
};
static struct json_path_handler keymap_def_handlers[] = {
json_path_handler("(?<key_seq>(x[0-9a-f]{2})+)")
json_path_handler("(?<key_seq>(x[0-9a-f]{2})+)#")
.with_synopsis("<command>")
.with_description("The command to execute for the given key sequence")
.with_pattern("[:|;].*")
@ -270,10 +270,25 @@ static struct json_path_handler keymap_defs_handlers[] = {
json_path_handler()
};
static struct json_path_handler global_var_handlers[] = {
json_path_handler("(?<var_name>\\w+)")
.with_synopsis("<name>")
.with_description("A global variable definition")
.with_path_provider<_lnav_config>([](struct _lnav_config *cfg, vector<string> &paths_out) {
for (const auto &iter : cfg->lc_global_vars) {
paths_out.push_back(iter.first);
}
})
.for_field(&nullobj<_lnav_config>()->lc_global_vars),
};
static struct json_path_handler root_config_handlers[] = {
json_path_handler("/keymap_def/")
.with_children(keymap_defs_handlers),
json_path_handler("/global/")
.with_children(global_var_handlers),
json_path_handler()
};

View File

@ -90,7 +90,7 @@ void install_git_format(const char *repo);
void install_extra_formats();
struct key_map {
std::map<std::string, std::string> km_seq_to_cmd;
std::map<std::string, std::vector<std::string>> km_seq_to_cmd;
};
struct _lnav_config {
@ -99,6 +99,7 @@ struct _lnav_config {
std::string lc_ui_keymap;
std::unordered_map<std::string, key_map> lc_ui_keymaps;
std::map<std::string, std::string> lc_ui_key_overrides;
std::map<std::string, std::string> lc_global_vars;
};
extern struct _lnav_config lnav_config;

View File

@ -33,6 +33,8 @@
#define LNAV_SHLEX_HH_H
#include <map>
#include <vector>
#include <string>
#include "pcrepp.hh"
@ -49,6 +51,33 @@ enum shlex_token_t {
ST_TILDE,
};
class scoped_resolver {
public:
scoped_resolver(std::initializer_list<std::map<std::string, std::string> *> l) {
this->sr_stack.insert(this->sr_stack.end(), l.begin(), l.end());
};
typedef std::map<std::string, std::string>::const_iterator const_iterator;
const_iterator find(const std::string &str) const {
const_iterator retval;
for (auto scope : this->sr_stack) {
if ((retval = scope->find(str)) != scope->end()) {
return retval;
}
}
return this->end();
};
const_iterator end() const {
return this->sr_stack.back()->end();
}
std::vector<const std::map<std::string, std::string> *> sr_stack;
};
class shlex {
public:
shlex(const char *str, size_t len)
@ -165,7 +194,8 @@ public:
return false;
};
bool eval(std::string &result, const std::map<std::string, std::string> &vars) {
template <typename Resolver = scoped_resolver>
bool eval(std::string &result, const Resolver &vars) {
result.clear();
pcre_context::capture_t cap;
@ -220,7 +250,8 @@ public:
return true;
};
bool split(std::vector<std::string> &result, const std::map<std::string, std::string> &vars) {
template <typename Resolver>
bool split(std::vector<std::string> &result, const Resolver &vars) {
result.clear();
pcre_context::capture_t cap;

View File

@ -85,10 +85,19 @@ int yajlpp_static_string(yajlpp_parse_context *ypc, const unsigned char *str, si
int yajlpp_static_string_vector(yajlpp_parse_context *ypc, const unsigned char *str, size_t len)
{
vector<string> &field_ptr = resolve_root<vector<string>>(ypc);
const json_path_handler_base *jph = ypc->ypc_current_handler;
field_ptr.push_back(string((const char *) str, len));
yajlpp_validator_for_string(*ypc, *ypc->ypc_current_handler);
if (jph->jph_kv_pair) {
map<string, vector<string>> &field_ptr = resolve_root<map<string, vector<string>>>(ypc);
field_ptr[ypc->get_path_fragment(-2)].push_back(string((const char *) str, len));
} else {
vector<string> &field_ptr = resolve_root<vector<string>>(ypc);
field_ptr.push_back(string((const char *) str, len));
}
yajlpp_validator_for_string_vector(*ypc, *ypc->ypc_current_handler);
return 1;
}
@ -180,6 +189,43 @@ yajl_gen_status yajlpp_static_gen_string(yajlpp_gen_context &ygc,
}
}
yajl_gen_status yajlpp_static_gen_string_vector(yajlpp_gen_context &ygc,
const json_path_handler_base &jph,
yajl_gen handle)
{
if (jph.jph_kv_pair) {
map<string, vector<string>> *default_field_ptr = resolve_root<map<string, vector<string>>>(
ygc.ygc_default_stack, jph);
map<string, vector<string>> *field_ptr = resolve_root<map<string, vector<string>>>(
ygc.ygc_obj_stack, jph);
const string &base_name = ygc.ygc_base_name;
if (default_field_ptr != NULL &&
((*default_field_ptr)[base_name] == (*field_ptr)[base_name])) {
return yajl_gen_status_ok;
}
if (ygc.ygc_depth) {
yajl_gen_string(handle, base_name);
}
{
yajlpp_array arr(handle);
for (string str : (*field_ptr)[base_name]) {
yajl_gen_string(handle, str);
}
}
return yajl_gen_status_ok;
}
else {
ensure(0);
return yajl_gen_status_ok;
}
}
void yajlpp_validator_for_string(yajlpp_parse_context &ypc,
const json_path_handler_base &jph)
{
@ -197,6 +243,25 @@ void yajlpp_validator_for_string(yajlpp_parse_context &ypc,
}
}
void yajlpp_validator_for_string_vector(yajlpp_parse_context &ypc,
const json_path_handler_base &jph)
{
if (jph.jph_kv_pair) {
return; // XXX
}
vector<string> &field_ptr = resolve_root<vector<string>>(&ypc);
for (string str : field_ptr) {
if (str.empty() && jph.jph_min_length > 0) {
ypc.report_error("value must not be empty");
} else if (str.size() < jph.jph_min_length) {
ypc.report_error("value must be at least %lu characters long",
jph.jph_min_length);
}
}
}
void yajlpp_validator_for_intern_string(yajlpp_parse_context &ypc,
const json_path_handler_base &jph)
{

View File

@ -158,8 +158,13 @@ int yajlpp_static_enum(yajlpp_parse_context *, const unsigned char *, size_t);
yajl_gen_status yajlpp_static_gen_string(yajlpp_gen_context &ygc,
const json_path_handler_base &,
yajl_gen);
yajl_gen_status yajlpp_static_gen_string_vector(yajlpp_gen_context &ygc,
const json_path_handler_base &,
yajl_gen);
void yajlpp_validator_for_string(yajlpp_parse_context &ypc,
const json_path_handler_base &jph);
void yajlpp_validator_for_string_vector(yajlpp_parse_context &ypc,
const json_path_handler_base &jph);
void yajlpp_validator_for_intern_string(yajlpp_parse_context &ypc,
const json_path_handler_base &jph);
void yajlpp_validator_for_int(yajlpp_parse_context &ypc,
@ -318,6 +323,15 @@ struct json_path_handler : public json_path_handler_base {
return *this;
};
json_path_handler &for_field(std::map<std::string, std::vector<std::string>> *field) {
this->add_cb(yajlpp_static_string_vector);
this->jph_kv_pair = true;
this->jph_simple_offset = field;
this->jph_gen_callback = yajlpp_static_gen_string_vector;
this->jph_validator = yajlpp_validator_for_string_vector;
return *this;
};
template<typename T>
json_path_handler &for_enum(T *field) {
this->add_cb(yajlpp_static_enum);