[keymap] handle unicode keys and add UK keymap

Fixes #392
pull/757/head
Timothy Stack 4 years ago
parent ba1bc28209
commit 8734795505

@ -18,6 +18,12 @@ The configuration for **lnav** is stored in the following JSON files in
* :file:`configs/*/*.json` -- Other directories that contain :file:`*.json`
files will be loaded on startup.
.. note::
Log format definitions are stored separately in the :file:`~/.lnav/formats`
directly. See the :ref:`Log Formats<log_formats>` chapter for more
information.
Options
-------

@ -1,5 +1,5 @@
.. _log-formats:
.. _log_formats:
Log Formats
===========

@ -25,6 +25,7 @@ When compiling from source, the following dependencies are required:
* `ZLib <http://wwww.zlib.net>`_
* `Bzip2 <http://www.bzip.org>`_
* `Readline <http://www.gnu.org/s/readline>`_
* `curl <https://curl.haxx.se>`_
Installation
------------

@ -164,6 +164,7 @@ list(APPEND GEN_SRCS default-formats.h default-formats.c)
set(CONFIG_FILES
root-config.json
keymaps/default-keymap.json
keymaps/uk-keymap.json
themes/default-theme.json
themes/eldar.json
themes/monocai.json
@ -188,6 +189,7 @@ set(BUILTIN_LNAV_SCRIPTS
scripts/dhclient-summary.lnav
scripts/lnav-pop-view.lnav
scripts/partition-by-boot.lnav
scripts/rename-stdin.lnav
scripts/search-for.lnav)
set(BUILTIN_LNAV_SCRIPT_PATHS ${BUILTIN_LNAV_SCRIPTS})

@ -62,6 +62,7 @@ default-formats.h default-formats.c: bin2c $(FORMAT_FILES)
CONFIG_FILES = \
$(srcdir)/root-config.json \
$(srcdir)/keymaps/default-keymap.json \
$(srcdir)/keymaps/uk-keymap.json \
$(srcdir)/themes/default-theme.json \
$(srcdir)/themes/eldar.json \
$(srcdir)/themes/monocai.json \

@ -133,40 +133,40 @@ static int key_sql_callback(exec_context &ec, sqlite3_stmt *stmt)
bool handle_keyseq(const char *keyseq)
{
key_map &km = lnav_config.lc_ui_keymaps[lnav_config.lc_ui_keymap];
key_map &km = lnav_config.lc_active_keymap;
const auto &iter = km.km_seq_to_cmd.find(keyseq);
if (iter != km.km_seq_to_cmd.end()) {
vector<logline_value> values;
exec_context ec(&values, key_sql_callback, pipe_callback);
auto &var_stack = ec.ec_local_vars;
ec.ec_global_vars = lnav_data.ld_exec_context.ec_global_vars;
var_stack.push(map<string, string>());
auto &vars = var_stack.top();
vars["keyseq"] = keyseq;
const auto &kc = iter->second;
log_debug("executing key sequence x%02x: %s", keyseq, kc.kc_cmd.c_str());
auto result = execute_any(ec, kc.kc_cmd);
lnav_data.ld_rl_view->set_value(result.orElse(err_to_ok).unwrap());
if (!kc.kc_alt_msg.empty()) {
shlex lexer(kc.kc_alt_msg);
string expanded_msg;
if (lexer.eval(expanded_msg, {
&ec.ec_local_vars.top(),
&ec.ec_global_vars,
})) {
lnav_data.ld_rl_view->set_alt_value(expanded_msg);
}
}
if (iter == km.km_seq_to_cmd.end()) {
return false;
}
return true;
vector<logline_value> values;
exec_context ec(&values, key_sql_callback, pipe_callback);
auto &var_stack = ec.ec_local_vars;
ec.ec_global_vars = lnav_data.ld_exec_context.ec_global_vars;
var_stack.push(map<string, string>());
auto &vars = var_stack.top();
vars["keyseq"] = keyseq;
const auto &kc = iter->second;
log_debug("executing key sequence %s: %s", keyseq, kc.kc_cmd.c_str());
auto result = execute_any(ec, kc.kc_cmd);
lnav_data.ld_rl_view->set_value(result.orElse(err_to_ok).unwrap());
if (!kc.kc_alt_msg.empty()) {
shlex lexer(kc.kc_alt_msg);
string expanded_msg;
if (lexer.eval(expanded_msg, {
&ec.ec_local_vars.top(),
&ec.ec_global_vars,
})) {
lnav_data.ld_rl_view->set_alt_value(expanded_msg);
}
}
return false;
return true;
}
void handle_paging_key(int ch)
@ -1086,7 +1086,7 @@ void handle_paging_key(int ch)
break;
default:
log_warning("unhandled %d", ch);
log_warning("unhandled x%02x", ch);
lnav_data.ld_rl_view->set_value("Unrecognized keystroke, press "
ANSI_BOLD("?")
" to view help");

@ -33,6 +33,7 @@
#include <string.h>
#include <sys/time.h>
#include <array>
#if defined HAVE_NCURSESW_CURSES_H
# include <ncursesw/curses.h>
@ -52,13 +53,23 @@
#include "input_dispatcher.hh"
#include "lnav_util.hh"
template<typename A>
static void to_key_seq(A &dst, char *src)
{
dst[0] = '\0';
for (size_t lpc = 0; src[lpc]; lpc++) {
snprintf(dst.data() + strlen(dst.data()),
dst.size() - strlen(dst.data()),
"x%02x",
src[lpc] & 0xff);
}
}
void input_dispatcher::new_input(const struct timeval &current_time, int ch)
{
switch (ch) {
case KEY_ESCAPE:
this->id_escape_index = 0;
this->append_to_escape_buffer(ch);
this->id_escape_start_time = current_time;
this->reset_escape_buffer(ch, current_time);
break;
case KEY_MOUSE:
this->id_mouse_handler();
@ -71,7 +82,10 @@ void input_dispatcher::new_input(const struct timeval &current_time, int ch)
this->id_mouse_handler();
this->id_escape_index = 0;
} else {
switch (this->id_escape_matcher(this->id_escape_buffer)) {
std::array<char, 32 * 3 + 1> keyseq;
to_key_seq(keyseq, this->id_escape_buffer);
switch (this->id_escape_matcher(keyseq.data())) {
case escape_match_t::NONE:
for (int lpc = 0; this->id_escape_buffer[lpc]; lpc++) {
this->id_key_handler(this->id_escape_buffer[lpc]);
@ -81,11 +95,21 @@ void input_dispatcher::new_input(const struct timeval &current_time, int ch)
case escape_match_t::PARTIAL:
break;
case escape_match_t::FULL:
this->id_escape_handler(this->id_escape_buffer);
this->id_escape_handler(keyseq.data());
this->id_escape_index = 0;
break;
}
}
if (this->id_escape_expected_size != -1 &&
this->id_escape_index == this->id_escape_expected_size) {
this->id_escape_index = 0;
}
} else if ((ch & 0xf8) == 0xf0) {
this->reset_escape_buffer(ch, current_time, 3);
} else if ((ch & 0xf0) == 0xe0) {
this->reset_escape_buffer(ch, current_time, 2);
} else if ((ch & 0xe0) == 0xc0) {
this->reset_escape_buffer(ch, current_time, 1);
} else {
this->id_key_handler(ch);
}
@ -107,6 +131,15 @@ void input_dispatcher::poll(const struct timeval &current_time)
}
}
void input_dispatcher::reset_escape_buffer(int ch, const timeval &current_time,
ssize_t expected_size)
{
this->id_escape_index = 0;
this->append_to_escape_buffer(ch);
this->id_escape_expected_size = expected_size;
this->id_escape_start_time = current_time;
}
void input_dispatcher::append_to_escape_buffer(int ch)
{
if (this->id_escape_index < (sizeof(this->id_escape_buffer) - 1)) {

@ -60,10 +60,14 @@ public:
std::function<void(const char *)> id_escape_handler;
std::function<void()> id_mouse_handler;
private:
void reset_escape_buffer(int ch,
const struct timeval &current_time,
ssize_t expected_size = -1);
void append_to_escape_buffer(int ch);
char id_escape_buffer[32];
size_t id_escape_index{0};
ssize_t id_escape_expected_size{-1};
struct timeval id_escape_start_time{0, 0};
};

@ -0,0 +1,15 @@
{
"$schema": "https://lnav.org/schemas/config-v1.schema.json",
"ui": {
"keymap-defs": {
"uk": {
"x22": {
"command": ":goto last 20 minutes after the hour"
},
"xc2xa3": {
"command": ":goto last 30 minutes after the hour"
}
}
}
}
}

@ -1166,28 +1166,19 @@ static void handle_key(int ch) {
}
}
static input_dispatcher::escape_match_t match_escape_seq(const char *escape_buffer)
static input_dispatcher::escape_match_t match_escape_seq(const char *keyseq)
{
if (lnav_data.ld_mode != LNM_PAGING) {
return input_dispatcher::escape_match_t::NONE;
}
char keyseq[32 * 3 + 1] = "";
for (size_t lpc = 0; escape_buffer[lpc]; lpc++) {
snprintf(keyseq + strlen(keyseq), sizeof(keyseq) - strlen(keyseq),
"x%02x",
escape_buffer[lpc]);
}
auto &km = lnav_config.lc_ui_keymaps[lnav_config.lc_ui_keymap];
auto &km = lnav_config.lc_active_keymap;
auto iter = km.km_seq_to_cmd.find(keyseq);
if (iter != km.km_seq_to_cmd.end()) {
return input_dispatcher::escape_match_t::FULL;
}
auto lb = km.km_seq_to_cmd.lower_bound(keyseq);
if (lb == km.km_seq_to_cmd.end()) {
return input_dispatcher::escape_match_t::NONE;
}
@ -1197,26 +1188,13 @@ static input_dispatcher::escape_match_t match_escape_seq(const char *escape_buff
return l.first.size() < r.first.size();
});
if (strlen(escape_buffer) < longest->first.size()) {
if (strlen(keyseq) < longest->first.size()) {
return input_dispatcher::escape_match_t::PARTIAL;
}
return input_dispatcher::escape_match_t::NONE;
}
static void handle_escape_seq(const char *escape_buffer)
{
char keyseq[32 * 3 + 1] = "";
for (size_t lpc = 0; escape_buffer[lpc]; lpc++) {
snprintf(keyseq + strlen(keyseq), sizeof(keyseq) - strlen(keyseq),
"x%02x",
escape_buffer[lpc]);
}
handle_keyseq(keyseq);
}
void update_hits(void *dummy, textview_curses *tc)
{
auto top_tc = lnav_data.ld_view_stack.top();
@ -1517,7 +1495,7 @@ static void looper()
input_dispatcher &id = lnav_data.ld_input_dispatcher;
id.id_escape_matcher = match_escape_seq;
id.id_escape_handler = handle_escape_seq;
id.id_escape_handler = handle_keyseq;
id.id_key_handler = handle_key;
id.id_mouse_handler = bind(&xterm_mouse::handle_mouse, &lnav_data.ld_mouse);
}

@ -808,6 +808,20 @@ struct json_path_container lnav_config_handlers = json_path_container {
}
.with_schema_id(SUPPORTED_CONFIG_SCHEMAS.back());
class active_key_map_listener : public lnav_config_listener {
public:
void reload_config(error_reporter &reporter) override
{
lnav_config.lc_active_keymap = lnav_config.lc_ui_keymaps["default"];
for (const auto &pair :
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] = pair.second;
}
}
};
static active_key_map_listener KEYMAP_LISTENER;
Result<config_file_type, std::string>
detect_config_file_type(const filesystem::path &path)
{

@ -115,6 +115,8 @@ struct _lnav_config {
std::map<std::string, std::string> lc_ui_key_overrides;
std::map<std::string, std::string> lc_global_vars;
std::map<std::string, lnav_theme> lc_ui_theme_defs;
key_map lc_active_keymap;
};
extern struct _lnav_config lnav_config;

Loading…
Cancel
Save