[config] initial support for themes

For #422

Still more to do
pull/672/head
Timothy Stack 5 years ago
parent 6f451eec35
commit 06d1098211

10
NEWS

@ -1,3 +1,13 @@
lnav v0.8.6:
Features:
* Added support for themes and included a few as well: default,
night-owl, solarized-light, and solarized-dark. The theme can
be changed using the ':config' command, like so:
:config /ui/theme night-owl
Consult the online documentation for defining a new theme.
Fixes:
* Added 'notice' log level.
lnav v0.8.5:
Features:

@ -49,7 +49,7 @@ master_doc = 'index'
# General information about the project.
project = u'lnav'
copyright = u'2018, Tim Stack'
copyright = u'2019, Tim Stack'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the

@ -149,6 +149,7 @@ set(diag_STAT_SRCS
readline_possibilities.hh
regexp_vtab.hh
relative_time.hh
styling.hh
ring_span.hh
sequence_sink.hh
shlex.hh

@ -26,8 +26,19 @@ dump-pid-sh.c: $(srcdir)/dump-pid.sh bin2c
default-log-formats-json.c: $(srcdir)/default-log-formats.json bin2c
$(BIN2C_V)./bin2c -z -c $(srcdir)/default-log-formats.json $@
default-config-json.c: $(srcdir)/default-config.json bin2c
$(BIN2C_V)./bin2c -z -c $(srcdir)/default-config.json $@
CONFIG_FILES = \
$(srcdir)/root-config.json \
$(srcdir)/themes/default.json \
$(srcdir)/themes/night-owl.json \
$(srcdir)/themes/solarized-dark.json \
$(srcdir)/themes/solarized-light.json \
$()
default-config.json: $(CONFIG_FILES)
cat $(CONFIG_FILES) > $@
default-config-json.c: default-config.json bin2c
$(BIN2C_V)./bin2c -z -c default-config.json $@
init-sql.c: bin2c init.sql
$(BIN2C_V)./bin2c -z -c $(srcdir)/init.sql $@
@ -125,13 +136,17 @@ LDADD = \
dist_noinst_DATA = \
alpha-release.sh \
default-log-formats.json \
default-config.json \
dhclient-summary.lnav \
dump-pid.sh \
lnav-pop-view.lnav \
keymap-default.json \
partition-by-boot.lnav \
search-for.lnav
root-config.json \
search-for.lnav \
themes/default.json \
themes/night-owl.json \
themes/solarized-dark.json \
themes/solarized-light.json
noinst_HEADERS = \
all_logs_vtab.hh \
@ -226,6 +241,7 @@ noinst_HEADERS = \
shlex.hh \
simdutf8check.h \
spectro_source.hh \
styling.hh \
sql_util.hh \
sqlite-extension-func.hh \
statusview_curses.hh \
@ -275,6 +291,7 @@ endif
libdiag_a_SOURCES = \
ansi-palette.c \
ansi_scrubber.cc \
bookmarks.cc \
bottom_status_source.cc \
@ -379,6 +396,7 @@ libdiag_a_SOURCES += yajl/yajl.c \
endif
TEXT2C_FILES = \
ansi-palette.o \
dump-pid-sh.o \
help.o \
init-sql.o \
@ -408,7 +426,9 @@ ptimec_SOURCES = ptimec.cc
ptimec_LDADD =
DISTCLEANFILES = \
ansi-palette.c \
data_scanner_re.cc \
default-config.json \
default-config-json.c \
default-log-formats-json.c \
dump-pid-sh.c \

@ -0,0 +1,122 @@
[
{
"colorId": 0,
"hexString": "#000000",
"rgb": {
"r": 0,
"g": 0,
"b": 0
},
"hsl": {
"h": 0,
"s": 0,
"l": 0
},
"name": "Black"
},
{
"colorId": 1,
"hexString": "#800000",
"rgb": {
"r": 128,
"g": 0,
"b": 0
},
"hsl": {
"h": 0,
"s": 100,
"l": 25
},
"name": "Maroon"
},
{
"colorId": 2,
"hexString": "#008000",
"rgb": {
"r": 0,
"g": 128,
"b": 0
},
"hsl": {
"h": 120,
"s": 100,
"l": 25
},
"name": "Green"
},
{
"colorId": 3,
"hexString": "#808000",
"rgb": {
"r": 128,
"g": 128,
"b": 0
},
"hsl": {
"h": 60,
"s": 100,
"l": 25
},
"name": "Olive"
},
{
"colorId": 4,
"hexString": "#000080",
"rgb": {
"r": 0,
"g": 0,
"b": 128
},
"hsl": {
"h": 240,
"s": 100,
"l": 25
},
"name": "Navy"
},
{
"colorId": 5,
"hexString": "#800080",
"rgb": {
"r": 128,
"g": 0,
"b": 128
},
"hsl": {
"h": 300,
"s": 100,
"l": 25
},
"name": "Purple"
},
{
"colorId": 6,
"hexString": "#008080",
"rgb": {
"r": 0,
"g": 128,
"b": 128
},
"hsl": {
"h": 180,
"s": 100,
"l": 25
},
"name": "Teal"
},
{
"colorId": 7,
"hexString": "#c0c0c0",
"rgb": {
"r": 192,
"g": 192,
"b": 192
},
"hsl": {
"h": 0,
"s": 0,
"l": 75
},
"name": "Silver"
}
]

@ -58,7 +58,7 @@ struct exec_context {
ec_pipe_callback(pipe_callback) {
this->ec_local_vars.push(std::map<std::string, std::string>());
this->ec_path_stack.emplace_back(".");
this->ec_source.emplace("unknown", 0);
this->ec_source.emplace("command", 1);
this->ec_output_stack.emplace_back(nonstd::nullopt);
}

@ -577,7 +577,7 @@
},
{
"line": "[Tue Apr 04 06:18:29.712806 2017] [mpm_prefork:notice] [pid 17725] AH00163: Apache/2.4.23 (Unix) configured -- resuming normal operations",
"level" : "info"
"level" : "notice"
},
{
"line": "[Tue Apr 04 06:28:08.605341 2017] [core:error] [pid 17962] [client 127.0.0.1:60444] AH00135: Invalid method in request FOO /",
@ -585,7 +585,7 @@
},
{
"line": "[Thu Jan 17 02:42:49 2013] [notice] Digest: generating secret for digest authentication ...",
"level" : "info"
"level" : "notice"
}
]
},

@ -48,10 +48,11 @@ public:
doc_status_source() {
this->tss_fields[TSF_TITLE].set_width(14);
this->tss_fields[TSF_TITLE].set_left_pad(1);
this->tss_fields[TSF_TITLE].set_role(view_colors::VCR_VIEW_STATUS);
this->tss_fields[TSF_TITLE].set_role(view_colors::VCR_STATUS_TITLE);
this->tss_fields[TSF_STITCH_TITLE].set_width(2);
this->tss_fields[TSF_STITCH_TITLE].set_stitch_value(
view_colors::ansi_color_pair_index(COLOR_BLUE, COLOR_WHITE));
view_colors::VCR_STATUS_STITCH_TITLE_TO_NORMAL,
view_colors::VCR_STATUS_STITCH_NORMAL_TO_TITLE);
this->tss_fields[TSF_DESCRIPTION].set_share(1);
this->tss_fields[TSF_DESCRIPTION].set_role(view_colors::VCR_STATUS);
};

@ -45,12 +45,13 @@ static auto HOTKEY_HELP = " "
filter_status_source::filter_status_source()
{
this->tss_fields[TSF_TITLE].set_width(9);
this->tss_fields[TSF_TITLE].set_role(view_colors::VCR_VIEW_STATUS);
this->tss_fields[TSF_TITLE].set_role(view_colors::VCR_STATUS_TITLE);
this->tss_fields[TSF_TITLE].set_value(" Filters ");
this->tss_fields[TSF_STITCH_TITLE].set_width(2);
this->tss_fields[TSF_STITCH_TITLE].set_stitch_value(
view_colors::ansi_color_pair_index(COLOR_BLUE, COLOR_WHITE));
view_colors::VCR_STATUS_STITCH_TITLE_TO_NORMAL,
view_colors::VCR_STATUS_STITCH_NORMAL_TO_TITLE);
this->tss_fields[TSF_COUNT].set_min_width(16);
this->tss_fields[TSF_COUNT].set_share(1);
@ -58,7 +59,7 @@ filter_status_source::filter_status_source()
this->tss_fields[TSF_FILTERED].set_min_width(20);
this->tss_fields[TSF_FILTERED].set_share(1);
this->tss_fields[TSF_FILTERED].set_role(view_colors::VCR_BOLD_STATUS);
this->tss_fields[TSF_FILTERED].set_role(view_colors::VCR_STATUS);
this->tss_fields[TSF_HELP].right_justify(true);
this->tss_fields[TSF_HELP].set_width(20);
@ -139,12 +140,13 @@ void filter_status_source::update_filtered(text_sub_source *tss)
}
else {
ui_periodic_timer &timer = ui_periodic_timer::singleton();
attr_line_t &al = sf.get_value();
if (tss->get_filtered_count() == this->bss_last_filtered_count) {
if (timer.fade_diff(this->bss_filter_counter) == 0) {
this->tss_fields[TSF_FILTERED].set_role(
view_colors::VCR_BOLD_STATUS);
view_colors::VCR_STATUS);
al.with_attr(string_attr(line_range{0, -1}, &view_curses::VC_STYLE, A_BOLD));
}
}
else {

@ -55,6 +55,7 @@ struct highlighter {
this->h_pattern = other.h_pattern;
this->h_fg = other.h_fg;
this->h_bg = other.h_bg;
this->h_role = other.h_role;
this->h_code = other.h_code;
pcre_refcount(this->h_code, 1);
this->study();
@ -73,6 +74,7 @@ struct highlighter {
this->h_pattern = other.h_pattern;
this->h_fg = other.h_fg;
this->h_bg = other.h_bg;
this->h_role = other.h_role;
this->h_code = other.h_code;
pcre_refcount(this->h_code, 1);
this->study();
@ -115,7 +117,7 @@ struct highlighter {
}
highlighter &with_role(view_colors::role_t role) {
this->h_attrs = view_colors::singleton().attrs_for_role(role);
this->h_role = role;
return *this;
};
@ -187,7 +189,16 @@ struct highlighter {
}
if (lr.lr_end > lr.lr_start) {
sa.emplace_back(lr, &view_curses::VC_STYLE, this->h_attrs);
int attrs = 0;
if (this->h_attrs != -1) {
attrs = this->h_attrs;
}
if (this->h_role != view_colors::VCR_NONE) {
attrs |= view_colors::singleton().attrs_for_role(
this->h_role);
}
sa.emplace_back(lr, &view_curses::VC_STYLE, attrs);
off = matches[1];
}
@ -202,6 +213,7 @@ struct highlighter {
};
std::string h_pattern;
view_colors::role_t h_role{view_colors::VCR_NONE};
rgb_color h_fg;
rgb_color h_bg;
pcre *h_code;

@ -766,6 +766,7 @@ void handle_paging_key(int ch)
}
}
rollback_lnav_config = lnav_config;
lnav_data.ld_doc_status_source.set_title("Command Help");
add_view_text_possibilities(lnav_data.ld_rl_view, LNM_COMMAND, "filter", tc);
lnav_data.ld_rl_view->add_possibility(LNM_COMMAND, "filter", tc->get_last_search());

@ -37,6 +37,8 @@
#include <string>
#include "strnatcmp.h"
struct string_fragment {
explicit string_fragment(const char *str, int begin = 0, int end = -1)
: sf_string(str), sf_begin(begin), sf_end(end == -1 ? strlen(str) : end) {
@ -91,6 +93,15 @@ struct string_fragment {
return memcmp(this->data(), sf.data(), sf.length()) == 0;
};
bool iequal(const string_fragment &sf) const {
if (this->length() != sf.length()) {
return false;
}
return strnatcasecmp(this->length(), this->data(),
sf.length(), sf.data()) == 0;
};
bool operator==(const char *str) const {
size_t len = strlen(str);

@ -145,6 +145,10 @@
#include "shlex.hh"
#include "log_actions.hh"
#ifndef SYSCONFDIR
#define SYSCONFDIR "/usr/etc"
#endif
using namespace std;
static multimap<lnav_flags_t, string> DEFAULT_FILES;
@ -1855,6 +1859,9 @@ int main(int argc, char *argv[])
#endif
lnav_data.ld_debug_log_name = "/dev/null";
lnav_data.ld_config_paths.emplace_back("/etc/lnav");
lnav_data.ld_config_paths.emplace_back(SYSCONFDIR "/lnav");
lnav_data.ld_config_paths.emplace_back(dotlnav_path(""));
while ((c = getopt(argc, argv, "hHarRCc:I:iuf:d:nqtw:vVW")) != -1) {
switch (c) {
case 'h':

@ -38,6 +38,7 @@
#include <unordered_map>
#include <pcrecpp.h>
#include <yajl/api/yajl_tree.h>
#include "lnav.hh"
#include "lnav_config.hh"
@ -3208,64 +3209,126 @@ static string com_config(exec_context &ec, string cmdline, vector<string> &args)
yajlpp_parse_context ypc("input", lnav_config_handlers);
string option = args[1];
lnav_config = rollback_lnav_config;
ypc.set_path(option)
.with_obj(lnav_config);
ypc.ypc_active_paths.insert(option);
ypc.update_callbacks();
if (ypc.ypc_current_handler != NULL) {
if (args.size() == 2) {
auto_mem<yajl_gen_t> handle(yajl_gen_free);
const json_path_handler_base *jph = ypc.ypc_current_handler;
handle = yajl_gen_alloc(NULL);
if (jph == nullptr && !ypc.ypc_handler_stack.empty()) {
jph = ypc.ypc_handler_stack.back();
}
if (jph != nullptr) {
auto_mem<yajl_gen_t> handle(yajl_gen_free);
const json_path_handler_base *jph = ypc.ypc_current_handler;
yajlpp_gen_context ygc(handle, lnav_config_handlers);
ygc.with_context(ypc);
handle = yajl_gen_alloc(nullptr);
yajlpp_gen_context ygc(handle, lnav_config_handlers);
yajl_gen_config(handle, yajl_gen_beautify, 1);
ygc.with_context(ypc);
if (ypc.ypc_current_handler == nullptr) {
ygc.gen();
} else {
jph->gen(ygc, handle);
}
const unsigned char *buffer;
size_t len;
yajl_gen_get_buf(handle, &buffer, &len);
string old_value((char *) buffer, len);
if (args.size() == 2 || ypc.ypc_current_handler == nullptr) {
vector<string> errors;
const unsigned char *buffer;
size_t len;
lnav_config = rollback_lnav_config;
reload_config(errors);
yajl_gen_get_buf(handle, &buffer, &len);
if (ec.ec_dry_run) {
attr_line_t al(old_value);
retval = "info: " + option + " = " + string((char *) buffer, len);
lnav_data.ld_preview_source
.replace_with(al)
.set_text_format(detect_text_format(old_value.c_str(),
old_value.size()))
.truncate_to(10);
lnav_data.ld_preview_status_source.get_description()
.set_value("Value of option: %s", option.c_str());
char help_text[1024];
snprintf(help_text, sizeof(help_text),
ANSI_BOLD("%s") " " ANSI_UNDERLINE("%s") " -- %s",
jph->jph_path,
jph->jph_synopsis,
jph->jph_description);
retval = help_text;
} else {
retval = "info: " + option + " = " + trim(old_value);
}
}
else {
string value = remaining_args(cmdline, args, 2);
vector<string> errors;
bool changed = false;
if (ec.ec_dry_run) {
char help_text[1024];
snprintf(help_text, sizeof(help_text),
ANSI_BOLD("%s %s") " -- %s",
jph->jph_path,
jph->jph_synopsis,
jph->jph_description);
retval = help_text;
}
if (ypc.ypc_current_handler->jph_callbacks.yajl_string) {
if (ec.ec_dry_run) {
retval = "";
} else {
ypc.ypc_callbacks.yajl_string(
&ypc, (const unsigned char *) value.c_str(),
value.size());
retval = "info: changed config option -- " + option;
}
ypc.ypc_callbacks.yajl_string(
&ypc, (const unsigned char *) value.c_str(),
value.size());
changed = true;
}
else if (ypc.ypc_current_handler->jph_callbacks.yajl_boolean) {
if (ec.ec_dry_run) {
retval = "";
} else {
bool bvalue = false;
bool bvalue = false;
if (strcasecmp(value.c_str(), "true") == 0) {
bvalue = true;
}
ypc.ypc_callbacks.yajl_boolean(&ypc, bvalue);
retval = "info: changed config option -- " + option;
if (strcasecmp(value.c_str(), "true") == 0) {
bvalue = true;
}
ypc.ypc_callbacks.yajl_boolean(&ypc, bvalue);
changed = true;
}
else {
retval = "error: unhandled type";
}
reload_config();
if (changed) {
intern_string_t path = intern_string::lookup(option);
lnav_config_locations[path] = {
intern_string::lookup(ec.ec_source.top().first),
ec.ec_source.top().second
};
reload_config(errors);
if (!errors.empty()) {
lnav_config = rollback_lnav_config;
retval = "error:" + errors[0];
reload_config(errors);
} else if (!ec.ec_dry_run) {
retval = "info: changed config option -- " + option;
rollback_lnav_config = lnav_config;
}
}
}
}
else {
} else {
retval = "error: unknown configuration option -- " + option;
}
}
@ -3293,22 +3356,27 @@ static string com_reset_config(exec_context &ec, string cmdline, vector<string>
if (args.empty()) {
args.emplace_back("config-option");
}
else if (!ec.ec_dry_run) {
else {
yajlpp_parse_context ypc("input", lnav_config_handlers);
string option = args[1];
lnav_config = rollback_lnav_config;
ypc.set_path(option)
.with_obj(lnav_config);
ypc.ypc_active_paths.insert(option);
ypc.update_callbacks();
if (option == "*" || ypc.ypc_current_handler != NULL) {
reset_config(option);
if (option == "*" || (ypc.ypc_current_handler != NULL ||
!ypc.ypc_handler_stack.empty())) {
if (!ec.ec_dry_run) {
reset_config(option);
rollback_lnav_config = lnav_config;
}
if (option == "*") {
retval = "info: reset all options";
}
else {
retval = "info: reset option";
retval = "info: reset option -- " + option;
}
}
else {
@ -4348,7 +4416,7 @@ readline_context::command_t STD_COMMANDS[] = {
help_text(":config")
.with_summary("Read or write a configuration option")
.with_parameter(help_text("option", "The path to the option to read or write"))
.with_parameter({"option", "The path to the option to read or write"})
.with_parameter(help_text("value", "The value to write. If not given, the current value is returned")
.optional())
.with_example({"/ui/clock-format"})

@ -41,17 +41,20 @@
#include <libgen.h>
#include <iostream>
#include <stdexcept>
#include "pcrecpp.h"
#include "auto_fd.hh"
#include "lnav_log.hh"
#include "lnav_util.hh"
#include "auto_mem.hh"
#include "auto_pid.hh"
#include "lnav_config.hh"
#include "yajlpp.hh"
#include "yajlpp_def.hh"
#include "shlex.hh"
#include "styling.hh"
using namespace std;
@ -63,8 +66,11 @@ extern const char keymap_default_json[];
}
struct _lnav_config lnav_config;
struct _lnav_config rollback_lnav_config;
static struct _lnav_config lnav_default_config;
std::map<intern_string_t, source_location> lnav_config_locations;
lnav_config_listener *lnav_config_listener::LISTENER_LIST;
string dotlnav_path(const char *sub)
@ -283,6 +289,19 @@ struct userdata {
vector<string> &ud_errors;
};
static void config_error_reporter(const yajlpp_parse_context &ypc,
lnav_log_level_t level,
const char *msg)
{
if (level >= LOG_LEVEL_ERROR) {
struct userdata *ud = (userdata *) ypc.ypc_userdata;
ud->ud_errors.emplace_back(msg);
} else {
fprintf(stderr, "warning:%s\n", msg);
}
}
static struct json_path_handler keymap_def_handlers[] = {
json_path_handler("(?<key_seq>(x[0-9a-f]{2})+)#")
.with_synopsis("<command>")
@ -290,7 +309,7 @@ static struct json_path_handler keymap_def_handlers[] = {
.with_pattern("[:|;].*")
.with_path_provider<key_map>([](key_map *km, vector<string> &paths_out) {
for (const auto &iter : km->km_seq_to_cmd) {
paths_out.push_back(iter.first);
paths_out.emplace_back(iter.first);
}
})
.FOR_FIELD(key_map, km_seq_to_cmd),
@ -308,7 +327,7 @@ static struct json_path_handler keymap_defs_handlers[] = {
})
.with_path_provider<_lnav_config>([](struct _lnav_config *cfg, vector<string> &paths_out) {
for (const auto &iter : cfg->lc_ui_keymaps) {
paths_out.push_back(iter.first);
paths_out.emplace_back(iter.first);
}
})
.with_children(keymap_def_handlers),
@ -322,7 +341,7 @@ static struct json_path_handler global_var_handlers[] = {
.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);
paths_out.emplace_back(iter.first);
}
})
.FOR_FIELD(_lnav_config, lc_global_vars),
@ -340,32 +359,329 @@ static struct json_path_handler root_config_handlers[] = {
json_path_handler()
};
static struct json_path_handler style_config_handlers[] = {
json_path_handler("color")
.with_synopsis("#hex|color_name")
.with_description("Foreground color")
.FOR_FIELD(style_config, sc_color),
json_path_handler("background-color")
.with_synopsis("#hex|color_name")
.with_description("Background color")
.FOR_FIELD(style_config, sc_background_color),
json_path_handler("selected-color")
.with_synopsis("#hex|color_name")
.with_description("Background color when selected")
.FOR_FIELD(style_config, sc_selected_color),
json_path_handler("underline")
.with_description("Underline")
.FOR_FIELD(style_config, sc_underline),
json_path_handler("bold")
.with_description("Bold")
.FOR_FIELD(style_config, sc_bold),
json_path_handler()
};
static struct json_path_handler theme_styles_handlers[] = {
json_path_handler("identifier/")
.with_description("Styling for identifiers in logs")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_identifier;
})
.with_children(style_config_handlers),
json_path_handler("text/")
.with_description("Styling for plain text")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_text;
})
.with_children(style_config_handlers),
json_path_handler("alt-text/")
.with_description("Styling for plain text when alternating")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_alt_text;
})
.with_children(style_config_handlers),
json_path_handler("error/")
.with_description("Styling for error messages")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_error;
})
.with_children(style_config_handlers),
json_path_handler("ok/")
.with_description("Styling for success messages")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_ok;
})
.with_children(style_config_handlers),
json_path_handler("warning/")
.with_description("Styling for warning messages")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_warning;
})
.with_children(style_config_handlers),
json_path_handler("hidden/")
.with_description("Styling for hidden fields in logs")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_hidden;
})
.with_children(style_config_handlers),
json_path_handler("adjusted-time/")
.with_description("Styling for timestamps that have been adjusted")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_adjusted_time;
})
.with_children(style_config_handlers),
json_path_handler("skewed-time/")
.with_description("Styling for timestamps ")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_skewed_time;
})
.with_children(style_config_handlers),
json_path_handler("offset-time/")
.with_description("Styling for hidden fields")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_offset_time;
})
.with_children(style_config_handlers),
json_path_handler("popup/")
.with_description("Styling for popup windows")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_popup;
})
.with_children(style_config_handlers),
json_path_handler()
};
static struct json_path_handler theme_syntax_styles_handlers[] = {
json_path_handler("keyword/")
.with_description("Styling for keywords in source files")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_keyword;
})
.with_children(style_config_handlers),
json_path_handler("string/")
.with_description("Styling for single/double-quoted strings in text")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_string;
})
.with_children(style_config_handlers),
json_path_handler("comment/")
.with_description("Styling for comments in source files")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_comment;
})
.with_children(style_config_handlers),
json_path_handler("variable/")
.with_description("Styling for variables in text")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_variable;
})
.with_children(style_config_handlers),
json_path_handler("symbol/")
.with_description("Styling for symbols in source files")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_symbol;
})
.with_children(style_config_handlers),
json_path_handler("number/")
.with_description("Styling for numbers in source files")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_number;
})
.with_children(style_config_handlers),
json_path_handler("re-special/")
.with_description("Styling for special characters in regular expressions")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_re_special;
})
.with_children(style_config_handlers),
json_path_handler("re-repeat/")
.with_description("Styling for repeats in regular expressions")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_re_repeat;
})
.with_children(style_config_handlers),
json_path_handler("diff-delete/")
.with_description("Styling for deleted lines in diffs")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_diff_delete;
})
.with_children(style_config_handlers),
json_path_handler("diff-add/")
.with_description("Styling for added lines in diffs")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_diff_add;
})
.with_children(style_config_handlers),
json_path_handler("diff-section/")
.with_description("Styling for diffs")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_diff_section;
})
.with_children(style_config_handlers),
json_path_handler("file/")
.with_description("Styling for file names in source files")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_file;
})
.with_children(style_config_handlers),
json_path_handler()
};
static struct json_path_handler theme_status_styles_handlers[] = {
json_path_handler("text/")
.with_description("Styling for status bars")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_status;
})
.with_children(style_config_handlers),
json_path_handler("warn/")
.with_description("Styling for warnings in status bars")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_warn_status;
})
.with_children(style_config_handlers),
json_path_handler("alert/")
.with_description("Styling for alerts in status bars")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_alert_status;
})
.with_children(style_config_handlers),
json_path_handler("active/")
.with_description("Styling for activity in status bars")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_active_status;
})
.with_children(style_config_handlers),
json_path_handler("inactive/")
.with_description("Styling for inactive status bars")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_inactive_status;
})
.with_children(style_config_handlers),
json_path_handler("title/")
.with_description("Styling for title sections of status bars")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_status_title;
})
.with_children(style_config_handlers),
json_path_handler("subtitle/")
.with_description("Styling for subtitle sections of status bars")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_status_subtitle;
})
.with_children(style_config_handlers),
json_path_handler()
};
static struct json_path_handler theme_log_level_styles_handlers[] = {
json_path_handler("(?<level>trace|debug5|debug4|debug3|debug2|debug|info|stats|notice|warning|error|critical|fatal)"
"/")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
style_config &sc = root->lt_level_styles[
string2level(ypc.ypc_extractor.get_substr_i("level").get())];
return &sc;
})
.with_path_provider<lnav_theme>([](struct lnav_theme *cfg, vector<string> &paths_out) {
for (int lpc = LEVEL_TRACE; lpc < LEVEL__MAX; lpc++) {
paths_out.emplace_back(level_names[lpc]);
}
})
.with_children(style_config_handlers),
json_path_handler()
};
static struct json_path_handler theme_vars_handlers[] = {
json_path_handler("(?<var_name>\\w+)")
.with_synopsis("name")
.with_description("A theme variable definition")
.with_path_provider<lnav_theme>([](struct lnav_theme *lt, vector<string> &paths_out) {
for (const auto &iter : lt->lt_vars) {
paths_out.emplace_back(iter.first);
}
})
.FOR_FIELD(lnav_theme, lt_vars),
json_path_handler()
};
static struct json_path_handler theme_def_handlers[] = {
json_path_handler("vars/")
.with_description("Variables definitions that are used in this theme")
.with_children(theme_vars_handlers),
json_path_handler("styles/")
.with_children(theme_styles_handlers),
json_path_handler("syntax-styles/")
.with_children(theme_syntax_styles_handlers),
json_path_handler("status-styles/")
.with_children(theme_status_styles_handlers),
json_path_handler("log-level-styles/")
.with_children(theme_log_level_styles_handlers),
json_path_handler()
};
static struct json_path_handler theme_defs_handlers[] = {
json_path_handler("(?<theme_name>[^/]+)/")
.with_obj_provider<lnav_theme, _lnav_config>([](const yajlpp_provider_context &ypc, _lnav_config *root) {
lnav_theme &lt = root->lc_ui_theme_defs[ypc.ypc_extractor.get_substr("theme_name")];
return &lt;
})
.with_path_provider<_lnav_config>([](struct _lnav_config *cfg, vector<string> &paths_out) {
for (const auto &iter : cfg->lc_ui_theme_defs) {
paths_out.emplace_back(iter.first);
}
})
.with_children(theme_def_handlers),
json_path_handler()
};
static struct json_path_handler ui_handlers[] = {
json_path_handler("clock-format")
.with_synopsis("<format>")
.with_synopsis("format")
.with_description(
"The format for the clock displayed in "
"the top-left corner using strftime(3) conversions")
.FOR_FIELD(_lnav_config, lc_ui_clock_format),
json_path_handler("dim-text")
.with_synopsis("<bool>")
.with_synopsis("bool")
.with_description("Reduce the brightness of text (useful for xterms)")
.FOR_FIELD(_lnav_config, lc_ui_dim_text),
json_path_handler("default-colors")
.with_synopsis("<bool>")
.with_synopsis("bool")
.with_description("Use default terminal fg/bg colors")
.FOR_FIELD(_lnav_config, lc_ui_default_colors),
json_path_handler("keymap")
.with_synopsis("<name>")
.with_synopsis("name")
.with_description("The name of the keymap to use")
.FOR_FIELD(_lnav_config, lc_ui_keymap),
json_path_handler("theme")
.with_synopsis("theme_name")
.with_description("The name of the theme to use")
.FOR_FIELD(_lnav_config, lc_ui_theme),
json_path_handler("theme-defs/")
.with_description("Theme definitions")
.with_children(theme_defs_handlers),
json_path_handler()
};
struct json_path_handler lnav_config_handlers[] = {
json_path_handler("/ui/")
.with_children(ui_handlers),
.with_description("User-interface settings")
.with_children(ui_handlers),
json_path_handler()
};
@ -376,6 +692,7 @@ static void load_config_from(const string &path, vector<string> &errors)
struct userdata ud(errors);
auto_fd fd;
ypc.ypc_locations = &lnav_config_locations;
ypc.with_obj(lnav_config);
ypc.ypc_userdata = &ud;
if ((fd = open(path.c_str(), O_RDONLY)) == -1) {
@ -396,6 +713,8 @@ static void load_config_from(const string &path, vector<string> &errors)
handle = yajl_alloc(&ypc.ypc_callbacks, NULL, &ypc);
yajl_config(handle, yajl_allow_comments, 1);
yajl_config(handle, yajl_allow_multiple_values, 1);
ypc.ypc_handle = handle;
while (true) {
rc = read(fd, buffer, sizeof(buffer));
if (rc == 0) {
@ -407,7 +726,7 @@ static void load_config_from(const string &path, vector<string> &errors)
string(strerror(errno)));
break;
}
if (yajl_parse(handle, (const unsigned char *)buffer, rc) != yajl_status_ok) {
if (ypc.parse((const unsigned char *)buffer, rc) != yajl_status_ok) {
errors.push_back(path +
": invalid json -- " +
string((char *)yajl_get_error(handle, 1, (unsigned char *)buffer, rc)));
@ -416,7 +735,7 @@ static void load_config_from(const string &path, vector<string> &errors)
offset += rc;
}
if (rc == 0) {
if (yajl_complete_parse(handle) != yajl_status_ok) {
if (ypc.complete_parse() != yajl_status_ok) {
errors.push_back(path +
": invalid json -- " +
string((char *)yajl_get_error(handle, 0, NULL, 0)));
@ -434,16 +753,18 @@ static void load_default_config(yajlpp_parse_context &ypc_builtin,
struct userdata ud(errors);
handle = yajl_alloc(&ypc_builtin.ypc_callbacks, NULL, &ypc_builtin);
ypc_builtin.with_handle(handle);
ypc_builtin.with_obj(config_obj);
ypc_builtin.with_error_reporter(config_error_reporter);
ypc_builtin.ypc_userdata = &ud;
yajl_config(handle, yajl_allow_comments, 1);
if (yajl_parse(handle,
(const unsigned char *) config_json,
strlen(config_json)) != yajl_status_ok) {
yajl_config(handle, yajl_allow_multiple_values, 1);
if (ypc_builtin.parse((const unsigned char *) config_json,
strlen(config_json)) != yajl_status_ok ||
ypc_builtin.complete_parse() != yajl_status_ok) {
errors.push_back("builtin: invalid json -- " +
string((char *)yajl_get_error(handle, 1, (unsigned char *) config_json, strlen(config_json))));
}
yajl_complete_parse(handle);
}
void load_config(const vector<string> &extra_paths, vector<string> &errors)
@ -452,6 +773,7 @@ void load_config(const vector<string> &extra_paths, vector<string> &errors)
{
yajlpp_parse_context ypc_builtin("keymap", root_config_handlers);
ypc_builtin.ypc_locations = &lnav_config_locations;
load_default_config(ypc_builtin, lnav_config, keymap_default_json,
errors);
}
@ -464,16 +786,39 @@ void load_config(const vector<string> &extra_paths, vector<string> &errors)
{
yajlpp_parse_context ypc_builtin("builtin", lnav_config_handlers);
ypc_builtin.ypc_locations = &lnav_config_locations;
ypc_builtin.reset(lnav_config_handlers);
load_default_config(ypc_builtin, lnav_default_config,
default_config_json, errors);
ypc_builtin.reset(lnav_config_handlers);
load_default_config(ypc_builtin, lnav_config, default_config_json,
errors);
for (const auto &extra_path : extra_paths) {
string format_path = extra_path + "/formats/*/*.json";
static_root_mem<glob_t, globfree> gl;
if (glob(format_path.c_str(), 0, NULL, gl.inout()) == 0) {
for (int lpc = 0; lpc < (int)gl->gl_pathc; lpc++) {
const char *base = basename(gl->gl_pathv[lpc]);
if (!startswith(base, "config.")) {
continue;
}
string filename(gl->gl_pathv[lpc]);
load_config_from(filename, errors);
}
}
}
load_config_from(user_config, errors);
}
reload_config();
reload_config(errors);
rollback_lnav_config = lnav_config;
}
void reset_config(const std::string &path)
@ -487,6 +832,8 @@ void reset_config(const std::string &path)
}
load_default_config(ypc_builtin, lnav_config, default_config_json, errors);
reload_config(errors);
}
string save_config()
@ -537,12 +884,42 @@ string save_config()
return "info: configuration saved";
}
void reload_config()
void reload_config(vector<string> &errors)
{
lnav_config_listener *curr = lnav_config_listener::LISTENER_LIST;
while (curr != NULL) {
curr->reload_config();
auto reporter = [&errors](const void *cfg_value, const std::string &errmsg) {
auto cb = [&cfg_value, &errors, &errmsg](
const json_path_handler_base &jph,
const string &path,
void *mem) {
if (mem != cfg_value) {
return;
}
auto loc_iter = lnav_config_locations.find(intern_string::lookup(path));
if (loc_iter == lnav_config_locations.end()) {
return;
}
char msg[1024];
snprintf(msg, sizeof(msg),
"%s:%d:%s",
loc_iter->second.sl_source.get(),
loc_iter->second.sl_line_number,
errmsg.c_str());
errors.emplace_back(msg);
};
for (int lpc = 0; lnav_config_handlers[lpc].jph_path[0]; lpc++) {
lnav_config_handlers[lpc].walk(cb, &lnav_config);
}
};
curr->reload_config(reporter);
curr = curr->lcl_next;
}
}

@ -37,10 +37,17 @@
#include <map>
#include <string>
#include <vector>
#include <functional>
#include <unordered_map>
#include "yajlpp.hh"
#include "log_level.hh"
#include "styling.hh"
class lnav_config_listener {
public:
using error_reporter = const std::function<void(const void *, const std::string msg)>;
lnav_config_listener() {
this->lcl_next = LISTENER_LIST;
LISTENER_LIST = this;
@ -49,7 +56,7 @@ public:
virtual ~lnav_config_listener() {
};
virtual void reload_config() {
virtual void reload_config(error_reporter &reporter) {
};
@ -97,12 +104,16 @@ struct _lnav_config {
bool lc_ui_dim_text;
bool lc_ui_default_colors;
std::string lc_ui_keymap;
std::string lc_ui_theme;
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;
std::map<std::string, lnav_theme> lc_ui_theme_defs;
};
extern struct _lnav_config lnav_config;
extern struct _lnav_config rollback_lnav_config;
extern std::map<intern_string_t, source_location> lnav_config_locations;
extern struct json_path_handler lnav_config_handlers[];
@ -111,7 +122,7 @@ void load_config(const std::vector<std::string> &extra_paths,
void reset_config(const std::string &path);
void reload_config();
void reload_config(std::vector<std::string> &errors);
std::string save_config();

@ -52,10 +52,6 @@
#include "log_format_loader.hh"
#ifndef SYSCONFDIR
#define SYSCONFDIR "/usr/etc"
#endif
using namespace std;
static void extract_metadata(const char *contents, size_t len, struct script_metadata &meta_out);
@ -552,6 +548,7 @@ static const json_path_handler_base::enum_value_t LEVEL_ENUM[] = {
{ level_names[LEVEL_DEBUG], LEVEL_DEBUG },
{ level_names[LEVEL_INFO], LEVEL_INFO },
{ level_names[LEVEL_STATS], LEVEL_STATS },
{ level_names[LEVEL_NOTICE], LEVEL_NOTICE },
{ level_names[LEVEL_WARNING], LEVEL_WARNING },
{ level_names[LEVEL_ERROR], LEVEL_ERROR },
{ level_names[LEVEL_CRITICAL], LEVEL_CRITICAL },
@ -741,7 +738,7 @@ std::vector<intern_string_t> load_format_file(const string &filename, std::vecto
"error:unable to open format file '%s' -- %s",
filename.c_str(),
strerror(errno));
errors.push_back(errmsg);
errors.emplace_back(errmsg);
}
else {
auto_mem<yajl_handle_t> handle(yajl_free);
@ -807,6 +804,12 @@ static void load_from_path(const string &path, std::vector<string> &errors)
log_info("loading formats from path: %s", format_path.c_str());
if (glob(format_path.c_str(), 0, NULL, gl.inout()) == 0) {
for (int lpc = 0; lpc < (int)gl->gl_pathc; lpc++) {
const char *base = basename(gl->gl_pathv[lpc]);
if (startswith(base, "config.")) {
continue;
}
string filename(gl->gl_pathv[lpc]);
vector<intern_string_t> format_list;
@ -815,9 +818,9 @@ static void load_from_path(const string &path, std::vector<string> &errors)
log_warning("Empty format file: %s", filename.c_str());
}
else {
for (vector<intern_string_t>::iterator iter = format_list.begin();
iter != format_list.end();
++iter) {
for (auto iter = format_list.begin();
iter != format_list.end();
++iter) {
log_info(" found format: %s", iter->get());
}
}
@ -854,14 +857,8 @@ void load_formats(const std::vector<std::string> &extra_paths,
ypc_builtin.complete_parse();
yajl_free(handle);
load_from_path("/etc/lnav", errors);
load_from_path(SYSCONFDIR "/lnav", errors);
load_from_path(dotlnav_path(""), errors);
for (vector<string>::const_iterator path_iter = extra_paths.begin();
path_iter != extra_paths.end();
++path_iter) {
load_from_path(*path_iter, errors);
for (const auto & extra_path : extra_paths) {
load_from_path(extra_path, errors);
}
if (!errors.empty()) {

@ -42,6 +42,7 @@ const char *level_names[LEVEL__MAX + 1] = {
"debug",
"info",
"stats",
"notice",
"warning",
"error",
"critical",
@ -75,10 +76,11 @@ log_level_t abbrev2level(const char *levelstr, ssize_t len)
}
return LEVEL_DEBUG;
case 'I':
case 'N': // NOTICE
return LEVEL_INFO;
case 'S':
return LEVEL_STATS;
case 'N':
return LEVEL_NOTICE;
case 'W':
return LEVEL_WARNING;
case 'E':

@ -35,7 +35,7 @@
/**
* The logging level identifiers for a line(s).
*/
typedef enum {
enum log_level_t : int {
LEVEL_UNKNOWN,
LEVEL_TRACE,
LEVEL_DEBUG5,
@ -45,6 +45,7 @@ typedef enum {
LEVEL_DEBUG,
LEVEL_INFO,
LEVEL_STATS,
LEVEL_NOTICE,
LEVEL_WARNING,
LEVEL_ERROR,
LEVEL_CRITICAL,
@ -62,7 +63,7 @@ typedef enum {
LEVEL_MARK |
LEVEL_CONTINUED
)
} log_level_t;
};
extern const char *level_names[LEVEL__MAX + 1];

@ -1,4 +1,4 @@
/* Generated by re2c 1.1.1 on Tue Oct 16 06:58:50 2018 */
/* Generated by re2c 1.1.1 on Thu Apr 18 18:55:08 2019 */
#line 1 "../../lnav2/src/log_level_re.re"
/**
* Copyright (c) 2018, Timothy Stack
@ -69,7 +69,7 @@ log_level_t string2level(const char *levelstr, ssize_t len, bool exact)
const unsigned char *yyt1;
loop:
#line 73 "log_level_re.cc"
#line 73 "../../lnav2/src/log_level_re.cc"
{
YYCTYPE yych;
unsigned int yyaccept = 0;
@ -100,13 +100,13 @@ yy2:
YYSKIP ();
#line 75 "../../lnav2/src/log_level_re.re"
{ RET(LEVEL_UNKNOWN); }
#line 104 "log_level_re.cc"
#line 104 "../../lnav2/src/log_level_re.cc"
yy4:
YYSKIP ();
yy5:
#line 102 "../../lnav2/src/log_level_re.re"
{ goto loop; }
#line 110 "log_level_re.cc"
#line 110 "../../lnav2/src/log_level_re.cc"
yy6:
yyaccept = 0;
YYSKIP ();
@ -315,7 +315,7 @@ yy28:
yy29:
#line 98 "../../lnav2/src/log_level_re.re"
{ RET(LEVEL_ERROR); }
#line 319 "log_level_re.cc"
#line 319 "../../lnav2/src/log_level_re.cc"
yy30:
YYSKIP ();
yych = YYPEEK ();
@ -408,7 +408,7 @@ yy41:
YYSKIP ();
#line 94 "../../lnav2/src/log_level_re.re"
{ RET(LEVEL_INFO); }
#line 412 "log_level_re.cc"
#line 412 "../../lnav2/src/log_level_re.cc"
yy43:
YYSKIP ();
yych = YYPEEK ();
@ -454,7 +454,7 @@ yy47:
yy48:
#line 97 "../../lnav2/src/log_level_re.re"
{ RET(LEVEL_WARNING); }
#line 458 "log_level_re.cc"
#line 458 "../../lnav2/src/log_level_re.cc"
yy49:
YYSKIP ();
yych = YYPEEK ();
@ -495,7 +495,7 @@ yy51:
RET(LEVEL_DEBUG);
}
}
#line 499 "log_level_re.cc"
#line 499 "../../lnav2/src/log_level_re.cc"
yy52:
YYSKIP ();
goto yy29;
@ -503,7 +503,7 @@ yy53:
YYSKIP ();
#line 101 "../../lnav2/src/log_level_re.re"
{ RET(LEVEL_FATAL); }
#line 507 "log_level_re.cc"
#line 507 "../../lnav2/src/log_level_re.cc"
yy55:
YYSKIP ();
yych = YYPEEK ();
@ -524,12 +524,12 @@ yy57:
YYSKIP ();
#line 96 "../../lnav2/src/log_level_re.re"
{ RET(LEVEL_STATS); }
#line 528 "log_level_re.cc"
#line 528 "../../lnav2/src/log_level_re.cc"
yy59:
YYSKIP ();
#line 76 "../../lnav2/src/log_level_re.re"
{ RET(LEVEL_TRACE); }
#line 533 "log_level_re.cc"
#line 533 "../../lnav2/src/log_level_re.cc"
yy61:
YYSKIP ();
yych = YYPEEK ();
@ -553,13 +553,13 @@ yy63:
yy64:
YYSKIP ();
#line 95 "../../lnav2/src/log_level_re.re"
{ RET(LEVEL_INFO); }
#line 558 "log_level_re.cc"
{ RET(LEVEL_NOTICE); }
#line 558 "../../lnav2/src/log_level_re.cc"
yy66:
YYSKIP ();
#line 100 "../../lnav2/src/log_level_re.re"
{ RET(LEVEL_CRITICAL); }
#line 563 "log_level_re.cc"
#line 563 "../../lnav2/src/log_level_re.cc"
yy68:
YYSKIP ();
yych = YYPEEK ();
@ -583,7 +583,7 @@ yy71:
YYSKIP ();
#line 99 "../../lnav2/src/log_level_re.re"
{ RET(LEVEL_CRITICAL); }
#line 587 "log_level_re.cc"
#line 587 "../../lnav2/src/log_level_re.cc"
}
#line 104 "../../lnav2/src/log_level_re.re"

@ -92,7 +92,7 @@ log_level_t string2level(const char *levelstr, ssize_t len, bool exact)
}
}
'info' { RET(LEVEL_INFO); }
'notice' { RET(LEVEL_INFO); }
'notice' { RET(LEVEL_NOTICE); }
'stats' { RET(LEVEL_STATS); }
'warn'|'warning' { RET(LEVEL_WARNING); }
'err'|'error' { RET(LEVEL_ERROR); }

@ -40,6 +40,7 @@
#include "logfile_sub_source.hh"
#include "command_executor.hh"
#include "ansi_scrubber.hh"
#include "lnav_config.hh"
using namespace std;
@ -326,21 +327,8 @@ void logfile_sub_source::text_attrs_for_line(textview_curses &lv,
int attrs = 0;
value_out = this->lss_token_attrs;
switch (this->lss_token_line->get_msg_level()) {
case LEVEL_FATAL:
case LEVEL_CRITICAL:
case LEVEL_ERROR:
attrs = vc.attrs_for_role(view_colors::VCR_ERROR);
break;
case LEVEL_WARNING:
attrs = vc.attrs_for_role(view_colors::VCR_WARNING);
break;
default:
attrs = vc.attrs_for_role(view_colors::VCR_TEXT);
break;
}
attrs = vc.vc_level_attrs[this->lss_token_line->get_msg_level()].first;
if ((row + 1) < (int)this->lss_filtered_index.size()) {
next_line = this->find_line(this->at(vis_line_t(row + 1)));

@ -221,12 +221,16 @@ public:
pi_length(s.length()),
pi_string(s.data()) {};
pcre_input(const string_fragment &&) = delete;
pcre_input(const std::string &str, size_t off = 0)
: pi_offset(off),
pi_next_offset(off),
pi_length(str.length()),
pi_string(str.c_str()) {};
pcre_input(const std::string &&, size_t off = 0) = delete;
const char *get_string() const { return this->pi_string; };
const char *get_substr_start(pcre_context::const_iterator iter) const

@ -50,15 +50,16 @@ public:
static const char TOGGLE_MSG[] = "Press CTRL+P to show/hide";
this->tss_fields[TSF_TITLE].set_width(14);
this->tss_fields[TSF_TITLE].set_role(view_colors::VCR_VIEW_STATUS);
this->tss_fields[TSF_TITLE].set_role(view_colors::VCR_STATUS_TITLE);
this->tss_fields[TSF_TITLE].set_value(" Preview Data ");
this->tss_fields[TSF_STITCH_TITLE].set_width(2);
this->tss_fields[TSF_STITCH_TITLE].set_stitch_value(
view_colors::ansi_color_pair_index(COLOR_BLUE, COLOR_WHITE));
view_colors::VCR_STATUS_STITCH_TITLE_TO_NORMAL,
view_colors::VCR_STATUS_STITCH_NORMAL_TO_TITLE);
this->tss_fields[TSF_DESCRIPTION].set_share(1);
this->tss_fields[TSF_TOGGLE].set_width(strlen(TOGGLE_MSG) + 1);
this->tss_fields[TSF_TOGGLE].set_value(TOGGLE_MSG);
this->tss_fields[TSF_TOGGLE].set_left_pad(1);
this->tss_fields[TSF_TOGGLE].right_justify(true);
};
size_t statusview_fields(void) { return TSF__MAX; };

@ -507,6 +507,10 @@ void rl_abort(void *dummy, readline_curses *rc)
tc->get_highlights().erase("$preview");
tc->get_highlights().erase("$bodypreview");
vector<string> errors;
lnav_config = rollback_lnav_config;
reload_config(errors);
lnav_data.ld_bottom_source.grep_error("");
switch (lnav_data.ld_mode) {
case LNM_SEARCH:

@ -56,6 +56,7 @@
#include <string>
#include "pcrepp.hh"
#include "shlex.hh"
#include "auto_mem.hh"
#include "lnav_log.hh"
#include "lnav_util.hh"
@ -312,31 +313,50 @@ char **readline_context::attempted_completion(const char *text,
else {
char * space;
string cmd;
vector<string> prefix;
int point = rl_point;
while (point > 0 && rl_line_buffer[point] != ' ') {
point -= 1;
}
shlex lexer(rl_line_buffer, point);
map<string, string> scope;
arg_possibilities = nullptr;
rl_completion_append_character = 0;
space = strchr(rl_line_buffer, ' ');
if (space == nullptr) {
space = rl_line_buffer + strlen(rl_line_buffer);
}
cmd = string(rl_line_buffer, space - rl_line_buffer);
if (lexer.split(prefix, scope)) {
string prefix2 = join(prefix.begin(), prefix.end(), "\x1f");
auto prefix_iter = loaded_context->rc_prefixes.find(prefix2);
auto iter = loaded_context->rc_prototypes.find(cmd);
if (prefix_iter != loaded_context->rc_prefixes.end()) {
arg_possibilities = &(loaded_context->rc_possibilities[prefix_iter->second]);
}
}
if (iter == loaded_context->rc_prototypes.end()) {
if (loaded_context->rc_possibilities.find("*") !=
loaded_context->rc_possibilities.end()) {
arg_possibilities = &loaded_context->rc_possibilities["*"];
rl_completion_append_character = loaded_context->rc_append_character;
if (arg_possibilities == nullptr) {
space = strchr(rl_line_buffer, ' ');
if (space == nullptr) {
space = rl_line_buffer + strlen(rl_line_buffer);
}
} else {
vector<string> &proto = loaded_context->rc_prototypes[cmd];
cmd = string(rl_line_buffer, space - rl_line_buffer);
if (proto.empty()) {
arg_possibilities = NULL;
} else if (proto[0] == "filename") {
return NULL; /* XXX */
auto iter = loaded_context->rc_prototypes.find(cmd);
if (iter == loaded_context->rc_prototypes.end()) {
if (loaded_context->rc_possibilities.find("*") !=
loaded_context->rc_possibilities.end()) {
arg_possibilities = &loaded_context->rc_possibilities["*"];
rl_completion_append_character = loaded_context->rc_append_character;
}
} else {
arg_possibilities = &(loaded_context->rc_possibilities[proto[0]]);
vector<string> &proto = loaded_context->rc_prototypes[cmd];
if (proto.empty()) {
arg_possibilities = nullptr;
} else if (proto[0] == "filename") {
return nullptr; /* XXX */
} else {
arg_possibilities = &(loaded_context->rc_possibilities[proto[0]]);
}
}
}
}
@ -596,7 +616,7 @@ void readline_curses::start()
}
else {
int context, prompt_start = 0;
char type[32];
char type[1024];
msg[rc] = '\0';
if (sscanf(msg, "i:%d:%n", &rl_point, &prompt_start) == 1) {
@ -640,6 +660,16 @@ void readline_curses::start()
_exit(1);
}
}
else if (sscanf(msg,
"apre:%d:%1023[^\x1d]\x1d%n",
&context,
type,
&prompt_start) == 2) {
require(this->rc_contexts[context] != NULL);
this->rc_contexts[context]->rc_prefixes[string(type)] =
string(&msg[prompt_start]);
}
else if (sscanf(msg,
"ap:%d:%31[^:]:%n",
&context,
@ -662,6 +692,9 @@ void readline_curses::start()
rem_possibility(string(type),
string(&msg[prompt_start]));
}
else if (sscanf(msg, "cpre:%d", &context) == 1) {
this->rc_contexts[context]->rc_prefixes.clear();
}
else if (sscanf(msg, "cp:%d:%s", &context, type)) {
this->rc_contexts[context]->clear_possibilities(type);
}
@ -955,6 +988,37 @@ void readline_curses::abort()
}
}
void readline_curses::add_prefix(int context,
const vector<string> &prefix,
const string &value)
{
char buffer[1024];
string prefix_wire = join(prefix.begin(), prefix.end(), "\x1f");
snprintf(buffer, sizeof(buffer),
"apre:%d:%s\x1d%s",
context,
prefix_wire.c_str(),
value.c_str());
if (sendstring(this->rc_command_pipe[RCF_MASTER],
buffer,
strlen(buffer) + 1) == -1) {
perror("add_possibility: write failed");
}
}
void readline_curses::clear_prefixes(int context)
{
char buffer[1024];
snprintf(buffer, sizeof(buffer), "cpre:%d", context);
if (sendstring(this->rc_command_pipe[RCF_MASTER],
buffer,
strlen(buffer) + 1) == -1) {
perror("add_possibility: write failed");
}
}
void readline_curses::add_possibility(int context,
const string &type,
const string &value)
@ -1015,9 +1079,11 @@ void readline_curses::do_update()
int alt_start = -1;
struct line_range lr(0, 0);
attr_line_t al, alt_al;
view_colors &vc = view_colors::singleton();
wmove(this->vc_window, this->get_actual_y(), this->vc_left);
wclrtoeol(this->vc_window);
wattron(this->vc_window, vc.attrs_for_role(view_colors::VCR_TEXT));
whline(this->vc_window, ' ', this->vc_width);
if (time(nullptr) > this->rc_value_expiration) {
this->rc_value.clear();
@ -1070,7 +1136,7 @@ void readline_curses::do_update()
std::string readline_curses::get_match_string() const
{
auto len = this->vc_x - this->rc_match_start;
auto len = ::min((size_t) this->vc_x, this->rc_line_buffer.size()) - this->rc_match_start;
auto context = this->get_active_context();
if (context->get_append_character() != 0 &&

@ -209,6 +209,8 @@ public:
};
static int command_complete(int, int);
std::map<std::string, std::string> rc_prefixes;
private:
static char **attempted_completion(const char *text, int start, int end);
static char *completion_generator(const char *text, int state);
@ -349,6 +351,12 @@ public:
void line_ready(const char *line);
void add_prefix(int context,
const std::vector<std::string> &prefix,
const std::string &value);
void clear_prefixes(int context);
void add_possibility(int context,
const std::string &type,
const std::string &value);

@ -359,6 +359,8 @@ void readline_command_highlighter(attr_line_t &al, int x)
R"(^:(filter-in|filter-out|delete-filter|enable-filter|disable-filter|highlight|clear-highlight|create-search-table\s+[^\s]+\s+))");
static const pcrepp SH_PREFIXES("^:(eval|open|append-to|write-to|write-csv-to|write-json-to)");
static const pcrepp IDENT_PREFIXES("^:(tag|untag|delete-tags)");
static const pcrepp COLOR_PREFIXES("^:(config)");
static const pcrepp COLOR_RE("(#(?:[a-fA-F0-9]{3}|[a-fA-F0-9]{6}))");
view_colors &vc = view_colors::singleton();
int keyword_attrs = (
@ -370,7 +372,6 @@ void readline_command_highlighter(attr_line_t &al, int x)
size_t ws_index;
ws_index = line.find(' ');
string command = line.substr(0, ws_index);
if (ws_index != string::npos) {
@ -387,6 +388,28 @@ void readline_command_highlighter(attr_line_t &al, int x)
readline_shlex_highlighter(al, x);
}
pi.reset(line);
if (COLOR_PREFIXES.match(pc, pi)) {
pi.reset(line);
if (COLOR_RE.match(pc, pi)) {
pcre_context::capture_t *cap = pc[0];
string hash_color = pi.get_substr(cap);
string errmsg;
rgb_color rgb_fg, rgb_bg;
attr_t color_hint_attrs = vc.attrs_for_role(view_colors::VCR_COLOR_HINT);
int pnum = PAIR_NUMBER(color_hint_attrs);
if (rgb_color::from_str(hash_color, rgb_bg, errmsg)) {
pnum -= 1;
vc.ensure_color_pair(pnum, rgb_fg, rgb_bg);
al.get_attrs().emplace_back(
line_range{cap->c_begin, cap->c_begin + 1},
&view_curses::VC_ROLE,
view_colors::VCR_COLOR_HINT);
}
}
}
pi.reset(line);
if (IDENT_PREFIXES.match(pc, pi) && ws_index != string::npos) {
size_t start = ws_index, last;

@ -281,14 +281,35 @@ void add_mark_possibilities()
void add_config_possibilities()
{
readline_curses *rc = lnav_data.ld_rl_view;
vector<string> config_options;
set<string> visited;
auto cb = [rc, &visited](const json_path_handler_base &jph,
const string &path,
void *mem) {
if (jph.jph_children) {
for (auto named_iter = jph.jph_regex.named_begin();
named_iter != jph.jph_regex.named_end();
++named_iter) {
if (visited.count(named_iter->pnc_name) == 0) {
rc->clear_possibilities(LNM_COMMAND, named_iter->pnc_name);
visited.insert(named_iter->pnc_name);
}
rc->clear_possibilities(LNM_COMMAND, "config-option");
rc->add_possibility(LNM_COMMAND, named_iter->pnc_name, path);
}
} else {
rc->add_possibility(LNM_COMMAND, "config-option", path);
if (jph.jph_synopsis) {
rc->add_prefix(LNM_COMMAND,
vector<string>{"config", path},
jph.jph_synopsis);
}
}
};
rc->clear_possibilities(LNM_COMMAND, "config-option");
for (int lpc = 0; lnav_config_handlers[lpc].jph_path[0]; lpc++) {
lnav_config_handlers[lpc].possibilities(config_options, &lnav_config);
lnav_config_handlers[lpc].walk(cb, &lnav_config);
}
rc->add_possibility(LNM_COMMAND, "config-option", config_options);
}
void add_tag_possibilities()

@ -1,6 +1,7 @@
{
"ui" : {
"clock-format": "%a %b %d %H:%M:%S %Z",
"keymap": "default"
"keymap": "default",
"theme": "default"
}
}

@ -1280,7 +1280,6 @@ void save_session()
continue;
}
filter_stack::iterator filter_iter;
filter_stack &fs = tss->get_filters();
view_map.gen("commands");

@ -280,7 +280,7 @@ public:
}
while (this->tokenize(cap, token)) {
if (start_new) {
result.push_back("");
result.emplace_back("");
start_new = false;
}
result.back().append(&this->s_str[last_index], cap.c_begin - last_index);

@ -49,9 +49,10 @@ void status_field::set_value(std::string value)
if (this->sf_cylon) {
struct line_range lr(this->sf_cylon_pos, this->sf_width);
view_colors &vc = view_colors::singleton();
sa.push_back(string_attr(lr, &view_curses::VC_STYLE,
view_colors::ansi_color_pair(COLOR_WHITE, COLOR_GREEN) | A_BOLD));
sa.emplace_back(lr, &view_curses::VC_STYLE,
vc.attrs_for_role(view_colors::VCR_ACTIVE_STATUS) | A_REVERSE);
this->sf_cylon_pos += 1;
if (this->sf_cylon_pos > this->sf_width) {
@ -98,6 +99,8 @@ void statusview_curses::do_update()
for (auto &sa : val.get_attrs()) {
if (sa.sa_type == &view_curses::VC_STYLE) {
sa.sa_value.sav_int &= ~(A_REVERSE | A_COLOR);
} else if (sa.sa_type == &view_curses::VC_ROLE) {
sa.sa_value.sav_int = view_colors::VCR_NONE;
}
}
}

@ -76,17 +76,16 @@ public:
va_end(args);
};
void set_stitch_value(int color_pair)
void set_stitch_value(view_colors::role_t left, view_colors::role_t right)
{
string_attrs_t &sa = this->sf_value.get_attrs();
struct line_range lr(0, 1);
this->sf_value.get_string() = "::";
sa.push_back(string_attr(lr, &view_curses::VC_STYLE,
A_REVERSE | COLOR_PAIR(color_pair)));
sa.emplace_back(lr, &view_curses::VC_ROLE, left);
lr.lr_start = 1;
lr.lr_end = 2;
sa.push_back(string_attr(lr, &view_curses::VC_STYLE, COLOR_PAIR(color_pair)));
sa.emplace_back(lr, &view_curses::VC_ROLE, right);
};
void set_left_pad(size_t val) { this->sf_left_pad = val; };

@ -0,0 +1,103 @@
/**
* Copyright (c) 2019, Timothy Stack
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Timothy Stack nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef styling_hh
#define styling_hh
#include <map>
#include <string>
#include "log_level.hh"
#include "intern_string.hh"
struct rgb_color {
static bool from_str(const string_fragment &color,
rgb_color &rgb_out,
std::string &errmsg);
explicit rgb_color(short r = -1, short g = -1, short b = -1)
: rc_r(r), rc_g(g), rc_b(b) {
}
bool empty() const {
return this->rc_r == -1 && this->rc_g == -1 && this->rc_b == -1;
}
short rc_r;
short rc_g;
short rc_b;
};
struct style_config {
std::string sc_color;
std::string sc_background_color;
std::string sc_selected_color;
bool sc_underline{false};
bool sc_bold{false};
};
struct lnav_theme {
std::map<std::string, std::string> lt_vars;
style_config lt_style_identifier;
style_config lt_style_text;
style_config lt_style_alt_text;
style_config lt_style_ok;
style_config lt_style_error;
style_config lt_style_warning;
style_config lt_style_popup;
style_config lt_style_hidden;
style_config lt_style_adjusted_time;
style_config lt_style_skewed_time;
style_config lt_style_offset_time;
style_config lt_style_status_title;
style_config lt_style_status_subtitle;
style_config lt_style_keyword;
style_config lt_style_string;
style_config lt_style_comment;
style_config lt_style_variable;
style_config lt_style_symbol;
style_config lt_style_number;
style_config lt_style_re_special;
style_config lt_style_re_repeat;
style_config lt_style_diff_delete;
style_config lt_style_diff_add;
style_config lt_style_diff_section;
style_config lt_style_low_threshold;
style_config lt_style_med_threshold;
style_config lt_style_high_threshold;
style_config lt_style_status;
style_config lt_style_warn_status;
style_config lt_style_alert_status;
style_config lt_style_active_status;
style_config lt_style_inactive_status;
style_config lt_style_file;
std::map<log_level_t, style_config> lt_level_styles;
};
#endif

@ -322,10 +322,10 @@ void setup_highlights(textview_curses::highlight_map_t &hm)
.with_text_format(TF_SQL)
.with_role(view_colors::VCR_KEYWORD);
hm["$srcfile"] = static_highlighter(
hm["$srcfile"] = highlighter(xpcre_compile(
"[\\w\\-_]+\\."
"(?:java|a|o|so|c|cc|cpp|cxx|h|hh|hpp|hxx|py|pyc|rb):"
"\\d+")
"\\d+"))
.with_role(view_colors::VCR_FILE);
hm["$xml"] = static_highlighter("<(/?[^ >=]+)[^>]*>");
hm["$stringd"] = highlighter(xpcre_compile(
@ -358,10 +358,18 @@ void setup_highlights(textview_curses::highlight_map_t &hm)
"@(?:author|deprecated|exception|file|param|return|see|since|throws|todo|version)");
hm["$var"] = highlighter(xpcre_compile(
"(?:"
"(?:var\\s+)?([\\-\\w]+)\\s*=|"
"(?:var\\s+)?([\\-\\w]+)\\s*[!=+\\-*/|&^]?=|"
"(?<!\\$)\\$(\\w+)|"
"(?<!\\$)\\$\\((\\w+)\\)|"
"(?<!\\$)\\$\\{(\\w+)\\}"
")"))
.with_role(view_colors::VCR_VARIABLE);
hm["$sym"] = highlighter(xpcre_compile(
"\\b[A-Z_][A-Z0-9_]+\\b"))
.with_text_format(TF_C_LIKE)
.with_role(view_colors::VCR_SYMBOL);
hm["$num"] = highlighter(xpcre_compile(
"\\b-?(?:\\d+|0x[a-zA-Z0-9]+)\\b"))
.with_text_format(TF_C_LIKE)
.with_role(view_colors::VCR_NUMBER);
}

@ -0,0 +1,127 @@
{
"ui": {
"theme-defs": {
"default": {
"styles": {
"text": {
"color": "Silver"
},
"alt-text": {
"color": "Silver",
"bold": true
},
"ok": {
"color": "Green",
"bold": true
},
"error": {
"color": "Red",
"bold": true
},
"warning": {
"color": "Yellow",
"bold": true
},
"hidden": {
"color": "Yellow",
"bold": true
},
"adjusted-time": {
"color": "Maroon"
},
"skewed-time": {
"color": "Yellow"
},
"offset-time": {
"color": "Teal"
},
"popup": {
"color": "Silver",
"background-color": "Teal"
}
},
"syntax-styles": {
"keyword": {
"color": "Blue"
},
"string": {
"color": "Green",
"bold": true
},
"comment": {
"color": "Green"
},
"variable": {
"color": "Teal"
},
"symbol": {
"color": "Blue"
},
"re-special": {
"color": "Teal"
},
"re-repeat": {
"color": "Yellow"
},
"diff-delete": {
"color": "Red"
},
"diff-add": {
"color": "Green"
},
"diff-section": {
"color": "Maroon"
},
"file": {
"color": "Blue"
}
},
"status-styles": {
"title": {
"color": "Silver",
"background-color": "Blue",
"bold": true
},
"subtitle": {
"color": "Black",
"background-color": "Teal"
},
"text": {
"color": "Black",
"background-color": "Silver"
},
"warn": {
"color": "Yellow",
"background-color": "Silver"
},
"alert": {
"color": "Red",
"background-color": "Silver"
},
"active": {
"color": "Green",
"background-color": "Silver"
},
"inactive": {
"color": "Silver",
"background-color": "Grey37"
}
},
"log-level-styles": {
"warning": {
"color": "Yellow"
},
"error": {
"color": "Red"
},
"critical": {
"color": "Red"
},
"fatal": {
"color": "Red"
}
}
}
}
}
}

@ -0,0 +1,143 @@
{
"ui": {
"theme-defs": {
"night-owl": {
"vars": {
"red": "#ff6868",
"green": "#007f00",
"yellow": "#cdcd00",
"blue": "#5394ec",
"magenta": "#ff70ff",
"cyan": "#33cccc"
},
"styles": {
"identifier": {
"background-color": "#011627"
},
"text": {
"color": "#d6deeb",
"background-color": "#011627"
},
"alt-text": {
"color": "#d6deeb",
"background-color": "#011627",
"bold": true
},
"ok": {
"color": "$green",
"bold": true
},
"error": {
"color": "#ef5350",
"bold": true
},
"warning": {
"color": "#b39554",
"bold": true
},
"hidden": {
"color": "$yellow",
"bold": true
},
"adjusted-time": {
"color": "$magenta"
},
"skewed-time": {
"color": "$yellow"
},
"offset-time": {
"color": "$cyan"
},
"popup": {
"color": "$base00",
"background-color": "$base3"
}
},
"syntax-styles": {
"keyword": {
"color": "#c792ea"
},
"string": {
"color": "#ecc48d",
"bold": true
},
"comment": {
"color": "#676e95"
},
"variable": {
"color": "#addb67"
},
"symbol": {
"color": "#82aaff"
},
"number": {
"color": "#f78c6c"
},
"re-special": {
"color": "$cyan"
},
"re-repeat": {
"color": "$yellow"
},
"diff-delete": {
"color": "#b03435"
},
"diff-add": {
"color": "#264b33"
},
"diff-section": {
"color": "$magenta"
},
"file": {
"color": "#82aaff"
}
},
"status-styles": {
"title": {
"color": "#f8f0f0",
"background-color": "#2d5a80",
"bold": true
},
"subtitle": {
"color": "#f8f8f0",
"background-color": "#005f5f"
},
"text": {
"color": "#f8f8f0",
"background-color": "#162d40"
},
"warn": {
"color": "#b39554",
"background-color": "#162d40"
},
"alert": {
"color": "#ef5350",
"background-color": "#162d40"
},
"active": {
"color": "#264b33",
"background-color": "#162d40"
},
"inactive": {
"color": "#f0f0f0",
"background-color": "#0F1F2B"
}
},
"log-level-styles": {
"warning": {
"color": "#b39554"
},
"error": {
"color": "#ef5350"
},
"critical": {
"color": "#ef5350"
},
"fatal": {
"color": "#ef5350"
}
}
}
}
}
}

@ -0,0 +1,152 @@
{
"ui": {
"theme-defs": {
"solarized-dark": {
"vars": {
"base03": "#002b36",
"base02": "#073642",
"base01": "#586e75",
"base00": "#657b83",
"base0": "#839496",
"base1": "#93a1a1",
"base2": "#eee8d5",
"base3": "#fdf6e3",
"black": "#002b36",
"yellow": "#b58900",
"orange": "#cb4b16",
"red": "#dc322f",
"magenta": "#d33682",
"violet": "#6c71c4",
"blue": "#268bd2",
"cyan": "#2aa198",
"green": "#859900"
},
"styles": {
"identifier": {
"background-color": "$base03"
},
"text": {
"color": "$base0",
"background-color": "$base03"
},
"alt-text": {
"color": "$base0",
"background-color": "$base03",
"bold": true
},
"ok": {
"color": "$green",
"bold": true
},
"error": {
"color": "$red",
"bold": true
},
"warning": {
"color": "$yellow",
"bold": true
},
"hidden": {
"color": "$yellow",
"bold": true
},
"adjusted-time": {
"color": "$magenta"
},
"skewed-time": {
"color": "$yellow"
},
"offset-time": {
"color": "$cyan"
},
"popup": {
"color": "$base00",
"background-color": "$base3"
}
},
"syntax-styles": {
"keyword": {
"color": "$yellow"
},
"string": {
"color": "$cyan",
"bold": true
},
"comment": {
"color": "$base01"
},
"variable": {
"color": "$blue"
},
"symbol": {
"color": "$blue"
},
"re-special": {
"color": "$cyan"
},
"re-repeat": {
"color": "$yellow"
},
"diff-delete": {
"color": "$red"
},
"diff-add": {
"color": "$green"
},
"diff-section": {
"color": "$magenta"
},
"file": {
"color": "$blue"
}
},
"status-styles": {
"title": {
"color": "$base02",
"background-color": "$blue",
"bold": true
},
"subtitle": {
"color": "$base00",
"background-color": "$cyan",
"bold": true
},
"text": {
"color": "$base2",
"background-color": "$base01"
},
"warn": {
"color": "$yellow",
"background-color": "$base01"
},
"alert": {
"color": "$red",
"background-color": "$base01"
},
"active": {
"color": "$green",
"background-color": "$base01"
},
"inactive": {
"color": "$base1",
"background-color": "$base02"
}
},
"log-level-styles": {
"warning": {
"color": "$yellow"
},
"error": {
"color": "$red"
},
"critical": {
"color": "$red"
},
"fatal": {
"color": "$red"
}
}
}
}
}
}

@ -0,0 +1,152 @@
{
"ui": {
"theme-defs": {
"solarized-light": {
"vars": {
"base03": "#002b36",
"base02": "#073642",
"base01": "#586e75",
"base00": "#657b83",
"base0": "#839496",
"base1": "#93a1a1",
"base2": "#eee8d5",
"base3": "#fdf6e3",
"black": "#002b36",
"yellow": "#b58900",
"orange": "#cb4b16",
"red": "#dc322f",
"magenta": "#d33682",
"violet": "#6c71c4",
"blue": "#268bd2",
"cyan": "#2aa198",
"green": "#859900"
},
"styles": {
"identifier": {
"background-color": "$base3"
},
"text": {
"color": "$base00",
"background-color": "$base3"
},
"alt-text": {
"color": "$base00",
"background-color": "$base3",
"bold": true
},
"ok": {
"color": "$green",
"bold": true
},
"error": {
"color": "$red",
"bold": true
},
"warning": {
"color": "$yellow",
"bold": true
},
"hidden": {
"color": "$yellow",
"bold": true
},
"adjusted-time": {
"color": "$magenta"
},
"skewed-time": {
"color": "$yellow"
},
"offset-time": {
"color": "$cyan"
},
"popup": {
"color": "$base00",
"background-color": "$base3"
}
},
"syntax-styles": {
"keyword": {
"color": "$yellow"
},
"string": {
"color": "$cyan",
"bold": true
},
"comment": {
"color": "$base1"
},
"variable": {
"color": "$blue"
},
"symbol": {
"color": "$blue"
},
"re-special": {
"color": "$cyan"
},
"re-repeat": {
"color": "$yellow"
},
"diff-delete": {
"color": "$red"
},
"diff-add": {
"color": "$green"
},
"diff-section": {
"color": "$magenta"
},
"file": {
"color": "$blue"
}
},
"status-styles": {
"title": {
"color": "$base2",
"background-color": "$base0",
"bold": true
},
"subtitle": {
"color": "$base2",
"background-color": "$base01",
"bold": true
},
"text": {
"color": "$base2",
"background-color": "$base03"
},
"warn": {
"color": "$yellow",
"background-color": "$base03"
},
"alert": {
"color": "$red",
"background-color": "$base03"
},
"active": {
"color": "$green",
"background-color": "$base03"
},
"inactive": {
"color": "$base1",
"background-color": "$base03"
}
},
"log-level-styles": {
"warning": {
"color": "$yellow"
},
"error": {
"color": "$red"
},
"critical": {
"color": "$red"
},
"fatal": {
"color": "$red"
}
}
}
}
}
}

@ -63,17 +63,20 @@ public:
this->tss_fields[TSF_PARTITION_NAME].set_width(34);
this->tss_fields[TSF_PARTITION_NAME].set_left_pad(1);
this->tss_fields[TSF_VIEW_NAME].set_width(8);
this->tss_fields[TSF_VIEW_NAME].set_role(view_colors::VCR_VIEW_STATUS);
this->tss_fields[TSF_VIEW_NAME].set_role(view_colors::VCR_STATUS_TITLE);
this->tss_fields[TSF_VIEW_NAME].right_justify(true);
this->tss_fields[TSF_STITCH_VIEW_FORMAT].set_width(2);
this->tss_fields[TSF_STITCH_VIEW_FORMAT].set_stitch_value(
view_colors::ansi_color_pair_index(COLOR_CYAN, COLOR_BLUE));
view_colors::VCR_STATUS_STITCH_SUB_TO_TITLE,
view_colors::VCR_STATUS_STITCH_TITLE_TO_SUB);
this->tss_fields[TSF_STITCH_VIEW_FORMAT].right_justify(true);
this->tss_fields[TSF_FORMAT].set_width(20);
this->tss_fields[TSF_FORMAT].set_role(view_colors::VCR_STATUS_SUBTITLE);
this->tss_fields[TSF_FORMAT].right_justify(true);
this->tss_fields[TSF_STITCH_FORMAT_FILENAME].set_width(2);
this->tss_fields[TSF_STITCH_FORMAT_FILENAME].set_stitch_value(
view_colors::ansi_color_pair_index(COLOR_WHITE, COLOR_CYAN));
view_colors::VCR_STATUS_STITCH_NORMAL_TO_SUB,
view_colors::VCR_STATUS_STITCH_SUB_TO_NORMAL);
this->tss_fields[TSF_STITCH_FORMAT_FILENAME].right_justify(true);
this->tss_fields[TSF_FILENAME].set_min_width(35); /* XXX */
this->tss_fields[TSF_FILENAME].set_share(1);
@ -165,9 +168,6 @@ public:
sf_filename.set_value(lc->get_data_source()->listview_source_name(*lc));
}
}
sf_format.get_value().get_attrs().push_back(
string_attr(lr, &view_curses::VC_STYLE,
A_REVERSE | view_colors::ansi_color_pair(COLOR_CYAN, COLOR_BLACK)));
};
void update_view_name(listview_curses *lc) {

@ -43,17 +43,18 @@
#include "yajlpp_def.hh"
#include "xterm-palette.hh"
#include "attr_line.hh"
#include "shlex.hh"
using namespace std;
struct xterm_color {
struct term_color {
short xc_id;
string xc_name;
rgb_color xc_color;
lab_color xc_lab_color;
};
static struct json_path_handler xterm_color_rgb_handler[] = {
static struct json_path_handler term_color_rgb_handler[] = {
json_path_handler("r")
.FOR_FIELD(rgb_color, rc_r),
json_path_handler("g")
@ -64,46 +65,47 @@ static struct json_path_handler xterm_color_rgb_handler[] = {
json_path_handler()
};
static struct json_path_handler xterm_color_handler[] = {
static struct json_path_handler term_color_handler[] = {
json_path_handler("colorId")
.FOR_FIELD(xterm_color, xc_id),
.FOR_FIELD(term_color, xc_id),
json_path_handler("name")
.FOR_FIELD(xterm_color, xc_name),
.FOR_FIELD(term_color, xc_name),
json_path_handler("rgb/")
.with_obj_provider<rgb_color, xterm_color>([](const auto &pc, xterm_color *xc) { return &xc->xc_color; })
.with_children(xterm_color_rgb_handler),
.with_obj_provider<rgb_color, term_color>([](const auto &pc, term_color *xc) { return &xc->xc_color; })
.with_children(term_color_rgb_handler),
json_path_handler()
};
static struct json_path_handler root_color_handler[] = {
json_path_handler("#/")
.with_obj_provider<xterm_color, vector<xterm_color>>(
[](const yajlpp_provider_context &ypc, vector<xterm_color> *palette) {
.with_obj_provider<term_color, vector<term_color>>(
[](const yajlpp_provider_context &ypc, vector<term_color> *palette) {
palette->resize(ypc.ypc_index + 1);
return &((*palette)[ypc.ypc_index]);
})
.with_children(xterm_color_handler),
.with_children(term_color_handler),
json_path_handler()
};
static struct _xterm_colors {
_xterm_colors() {
yajlpp_parse_context ypc_xterm("xterm-palette.json", root_color_handler);
struct term_color_palette {
term_color_palette(const unsigned char *json) {
yajlpp_parse_context ypc_xterm("palette.json", root_color_handler);
yajl_handle handle;
handle = yajl_alloc(&ypc_xterm.ypc_callbacks, nullptr, &ypc_xterm);
ypc_xterm
.with_ignore_unused(true)
.with_obj(this->xc_palette)
.with_obj(this->tc_palette)
.with_handle(handle);
ypc_xterm.parse((const unsigned char *) xterm_palette_json,
strlen(xterm_palette_json));
ypc_xterm.complete_parse();
yajl_status st = ypc_xterm.parse(json, strlen((const char *) json));
ensure(st == yajl_status_ok);
st = ypc_xterm.complete_parse();
ensure(st == yajl_status_ok);
yajl_free(handle);
for (auto &xc : this->xc_palette) {
for (auto &xc : this->tc_palette) {
xc.xc_lab_color = lab_color(xc.xc_color);
}
};
@ -112,7 +114,7 @@ static struct _xterm_colors {
double lowest = 1000.0;
short lowest_id = -1;
for (auto &xc : this->xc_palette) {
for (auto &xc : this->tc_palette) {
double xc_delta = xc.xc_lab_color.deltaE(to_match);
if (lowest_id == -1) {
@ -130,13 +132,22 @@ static struct _xterm_colors {
return lowest_id;
};
vector<xterm_color> xc_palette;
} xterm_colors;
vector<term_color> tc_palette;
};
term_color_palette xterm_colors(xterm_palette_json);
term_color_palette ansi_colors(ansi_palette_json);
term_color_palette *ACTIVE_PALETTE = &ansi_colors;
bool rgb_color::from_str(const string_fragment &color,
rgb_color &rgb_out,
std::string &errmsg)
{
if (color.empty()) {
return true;
}
if (color[0] == '#') {
switch (color.length()) {
case 4:
@ -159,20 +170,22 @@ bool rgb_color::from_str(const string_fragment &color,
return false;
}
for (const auto &xc : xterm_colors.xc_palette) {
if (color == xc.xc_name) {
for (const auto &xc : xterm_colors.tc_palette) {
if (color.iequal(xc.xc_name)) {
rgb_out = xc.xc_color;
return true;
}
}
errmsg = "Unknown color: " + color.to_string() +
". See https://jonasjacek.github.io/colors/ for a list of supported color names";
errmsg = "Unknown color: '" + color.to_string() +
"'. See https://jonasjacek.github.io/colors/ for a list of supported color names";
return false;
}
string_attr_type view_curses::VC_ROLE("role");
string_attr_type view_curses::VC_STYLE("style");
string_attr_type view_curses::VC_GRAPHIC("graphic");
string_attr_type view_curses::VC_SELECTED("selected");
string_attr_type view_curses::VC_FOREGROUND("foreground");
string_attr_type view_curses::VC_BACKGROUND("background");
@ -469,7 +482,8 @@ void view_curses::mvwattrline(WINDOW *window,
expanded_line[exp_index] = '\0';
full_line = string(expanded_line);
text_attrs = view_colors::singleton().attrs_for_role(base_role);
view_colors &vc = view_colors::singleton();
text_attrs = vc.attrs_for_role(base_role);
attrs = text_attrs;
wmove(window, y, x);
wattron(window, attrs);
@ -488,7 +502,8 @@ void view_curses::mvwattrline(WINDOW *window,
require(attr_range.lr_start >= 0);
require(attr_range.lr_end >= -1);
if (!(iter->sa_type == &VC_STYLE ||
if (!(iter->sa_type == &VC_ROLE ||
iter->sa_type == &VC_STYLE ||
iter->sa_type == &VC_GRAPHIC ||
iter->sa_type == &VC_FOREGROUND ||
iter->sa_type == &VC_BACKGROUND)) {
@ -520,7 +535,7 @@ void view_curses::mvwattrline(WINDOW *window,
for (int index = attr_range.lr_start;
index < attr_range.lr_end;
index++) {
mvwaddch(window, y, x + index, iter->sa_value.sav_int);
mvwaddch(window, y, x + index, iter->sa_value.sav_int | text_attrs);
}
continue;
}
@ -547,8 +562,14 @@ void view_curses::mvwattrline(WINDOW *window,
int awidth = attr_range.length();
int color_pair;
attrs = iter->sa_value.sav_int & ~A_COLOR;
color_pair = PAIR_NUMBER(iter->sa_value.sav_int);
if (iter->sa_type == &VC_STYLE) {
attrs = iter->sa_value.sav_int & ~A_COLOR;
color_pair = PAIR_NUMBER(iter->sa_value.sav_int);
} else {
attrs = vc.attrs_for_role((view_colors::role_t) iter->sa_value.sav_int);
color_pair = PAIR_NUMBER(attrs);
attrs = attrs & ~A_COLOR;
}
if (attrs || color_pair > 0) {
int x_pos = x + attr_range.lr_start;
@ -611,14 +632,6 @@ void view_curses::mvwattrline(WINDOW *window,
#endif
}
class color_listener : public lnav_config_listener {
void reload_config() {
view_colors::singleton().init_roles(0);
}
};
static color_listener _COLOR_LISTENER;
attr_t view_colors::BASIC_HL_PAIRS[view_colors::BASIC_COLOR_COUNT] = {
ansi_color_pair(COLOR_BLUE, COLOR_BLACK),
ansi_color_pair(COLOR_CYAN, COLOR_BLACK),
@ -643,10 +656,44 @@ view_colors::view_colors() : vc_color_pair_end(0)
bool view_colors::initialized = false;
void view_colors::init(void)
{
int color_pair_base = VC_ANSI_END;
static string COLOR_NAMES[] = {
"black",
"red",
"green",
"yellow",
"blue",
"magenta",
"cyan",
"white",
};
class color_listener : public lnav_config_listener {
public:
void reload_config(error_reporter &reporter) {
view_colors &vc = view_colors::singleton();
for (const auto &pair : lnav_config.lc_ui_theme_defs) {
vc.init_roles(pair.second, reporter);
}
auto iter = lnav_config.lc_ui_theme_defs.find(lnav_config.lc_ui_theme);
if (iter == lnav_config.lc_ui_theme_defs.end()) {
reporter(&lnav_config.lc_ui_theme,
"unknown theme -- " + lnav_config.lc_ui_theme);
return;
}
if (view_colors::initialized) {
vc.init_roles(iter->second, reporter);
}
}
};
static color_listener _COLOR_LISTENER;
void view_colors::init()
{
if (has_colors()) {
static int ansi_colors_to_curses[] = {
COLOR_BLACK,
@ -673,26 +720,20 @@ void view_colors::init(void)
ansi_colors_to_curses[bg]);
}
}
if (COLORS == 256) {
int bg = (lnav_config.lc_ui_default_colors ? -1 : COLOR_BLACK);
for (int z = 0; z < 6; z++) {
for (int x = 1; x < 6; x += 2) {
for (int y = 1; y < 6; y += 2) {
int fg = 16 + x + (y * 6) + (z * 6 * 6);
init_pair(color_pair_base, fg, bg);
color_pair_base += 1;
}
}
}
if (COLORS >= 256) {
ACTIVE_PALETTE = &xterm_colors;
}
}
singleton().init_roles(color_pair_base);
initialized = true;
{
auto reporter = [](const void *, const std::string &) {
};
_COLOR_LISTENER.reload_config(reporter);
}
}
inline attr_t attr_for_colors(int &pair_base, short fg, short bg)
@ -712,80 +753,329 @@ inline attr_t attr_for_colors(int &pair_base, short fg, short bg)
int pair = ++pair_base;
init_pair(pair, fg, bg);
if (view_colors::initialized) {
init_pair(pair, fg, bg);
}
return COLOR_PAIR(pair);
}
void view_colors::init_roles(int color_pair_base)
pair<attr_t, attr_t> view_colors::to_attrs(
int &pair_base,
const lnav_theme &lt, const style_config &sc,
lnav_config_listener::error_reporter &reporter)
{
rgb_color fg, bg, sbg;
string fg1, bg1, sbg1, fg_color, bg_color, sbg_color, errmsg;
fg1 = sc.sc_color;
if (fg1.empty()) {
fg1 = lt.lt_style_text.sc_color;
}
bg1 = sc.sc_background_color;
if (bg1.empty()) {
bg1 = lt.lt_style_text.sc_background_color;
}
sbg1 = sc.sc_selected_color;
if (sbg1.empty()) {
sbg1 = lt.lt_style_text.sc_selected_color;
}
shlex(fg1).eval(fg_color, lt.lt_vars);
shlex(bg1).eval(bg_color, lt.lt_vars);
shlex(sbg1).eval(sbg_color, lt.lt_vars);
if (!rgb_color::from_str(fg_color, fg, errmsg)) {
reporter(&sc.sc_color, errmsg);
}
if (!rgb_color::from_str(bg_color, bg, errmsg)) {
reporter(&sc.sc_background_color, errmsg);
}
if (!rgb_color::from_str(sbg_color, sbg, errmsg)) {
reporter(&sc.sc_selected_color, errmsg);
}
attr_t retval1 = this->ensure_color_pair(pair_base, fg, bg);
attr_t retval2 = this->ensure_color_pair(pair_base, fg, sbg);
if (sc.sc_underline) {
retval1 |= A_UNDERLINE;
retval2 |= A_UNDERLINE;
}
if (sc.sc_bold) {
retval1 |= A_BOLD;
retval2 |= A_BOLD;
}
return make_pair(retval1, retval2);
}
void view_colors::init_roles(const lnav_theme &lt,
lnav_config_listener::error_reporter &reporter)
{
int color_pair_base = VC_ANSI_END;
rgb_color fg, bg;
string err;
if (COLORS == 256) {
const style_config &ident_sc = lt.lt_style_identifier;
int ident_bg = (lnav_config.lc_ui_default_colors ? -1 : COLOR_BLACK);
if (!ident_sc.sc_background_color.empty()) {
string bg_color, errmsg;
rgb_color rgb_bg;
shlex(ident_sc.sc_background_color).eval(bg_color, lt.lt_vars);
if (!rgb_color::from_str(bg_color, rgb_bg, errmsg)) {
reporter(&ident_sc.sc_background_color, errmsg);
}
ident_bg = ACTIVE_PALETTE->match_color(rgb_bg);
}
for (int z = 0; z < 6; z++) {
for (int x = 1; x < 6; x += 2) {
for (int y = 1; y < 6; y += 2) {
int fg = 16 + x + (y * 6) + (z * 6 * 6);
init_pair(color_pair_base, fg, ident_bg);
color_pair_base += 1;
}
}
}
} else {
color_pair_base = VC_ANSI_END + HI_COLOR_COUNT;
}
/* Setup the mappings from roles to actual colors. */
this->vc_role_colors[VCR_TEXT] =
attr_for_colors(color_pair_base, COLOR_WHITE, COLOR_BLACK);
this->vc_role_colors[VCR_TEXT] = this->to_attrs(color_pair_base,
lt, lt.lt_style_text, reporter);
{
int pnum = PAIR_NUMBER(this->vc_role_colors[VCR_TEXT].first);
short text_fg, text_bg;
pair_content(pnum, &text_fg, &text_bg);
for (int ansi_fg = 0; ansi_fg < 8; ansi_fg++) {
for (int ansi_bg = 0; ansi_bg < 8; ansi_bg++) {
if (ansi_fg == 0 && ansi_bg == 0) {
continue;
}
auto fg_str = lt.lt_vars.find(COLOR_NAMES[ansi_fg]);
auto bg_str = lt.lt_vars.find(COLOR_NAMES[ansi_bg]);
rgb_color rgb_fg, rgb_bg;
string errmsg;
if (fg_str != lt.lt_vars.end() &&
!rgb_color::from_str(fg_str->second, rgb_fg, errmsg)) {
reporter(&fg_str->second, errmsg);
return;
}
if (!rgb_color::from_str(bg_str->second, rgb_bg, errmsg)) {
reporter(&bg_str->second, errmsg);
return;
}
short fg = ACTIVE_PALETTE->match_color(rgb_fg);
short bg = ACTIVE_PALETTE->match_color(rgb_bg);
if (rgb_fg.empty()) {
fg = ansi_fg;
}
if (rgb_bg.empty()) {
bg = ansi_bg;
}
init_pair(ansi_color_pair_index(ansi_fg, ansi_bg), fg, bg);
}
}
}
if (lnav_config.lc_ui_dim_text) {
this->vc_role_colors[VCR_TEXT] |= A_DIM;
}
this->vc_role_colors[VCR_SEARCH] = A_REVERSE;
this->vc_role_colors[VCR_OK] = attr_for_colors(color_pair_base, COLOR_GREEN, COLOR_BLACK) | A_BOLD;
this->vc_role_colors[VCR_ERROR] = attr_for_colors(color_pair_base, COLOR_RED, COLOR_BLACK) | A_BOLD;
this->vc_role_colors[VCR_WARNING] = attr_for_colors(color_pair_base, COLOR_YELLOW, COLOR_BLACK) | A_BOLD;
this->vc_role_colors[VCR_ALT_ROW] = this->vc_role_colors[VCR_TEXT] | A_BOLD;
this->vc_role_colors[VCR_HIDDEN] = attr_for_colors(color_pair_base, COLOR_YELLOW, COLOR_BLACK);
this->vc_role_colors[VCR_ADJUSTED_TIME] = attr_for_colors(color_pair_base, COLOR_MAGENTA, COLOR_BLACK);
this->vc_role_colors[VCR_SKEWED_TIME] = attr_for_colors(color_pair_base, COLOR_YELLOW, COLOR_BLACK) | A_UNDERLINE;
this->vc_role_colors[VCR_OFFSET_TIME] = attr_for_colors(color_pair_base, COLOR_CYAN, COLOR_BLACK);
this->vc_role_colors[VCR_STATUS] =
attr_for_colors(color_pair_base, COLOR_BLACK, COLOR_WHITE);
this->vc_role_colors[VCR_WARN_STATUS] =
attr_for_colors(color_pair_base, COLOR_YELLOW, COLOR_WHITE) | A_BOLD;
this->vc_role_colors[VCR_ALERT_STATUS] =
attr_for_colors(color_pair_base, COLOR_RED, COLOR_WHITE) | A_BOLD;
this->vc_role_colors[VCR_ACTIVE_STATUS] =
attr_for_colors(color_pair_base, COLOR_GREEN, COLOR_WHITE);
this->vc_role_colors[VCR_TEXT].first |= A_DIM;
this->vc_role_colors[VCR_TEXT].second |= A_DIM;
}
this->vc_role_colors[VCR_SEARCH] = make_pair(A_REVERSE, A_REVERSE);
this->vc_role_colors[VCR_OK] = this->to_attrs(color_pair_base,
lt, lt.lt_style_ok,
reporter);
this->vc_role_colors[VCR_ERROR] = this->to_attrs(color_pair_base,
lt, lt.lt_style_error,
reporter);
this->vc_role_colors[VCR_WARNING] = this->to_attrs(color_pair_base,
lt, lt.lt_style_warning,
reporter);
this->vc_role_colors[VCR_ALT_ROW] = this->to_attrs(color_pair_base,
lt, lt.lt_style_alt_text,
reporter);
this->vc_role_colors[VCR_HIDDEN] = this->to_attrs(color_pair_base,
lt, lt.lt_style_hidden,
reporter);
this->vc_role_colors[VCR_ADJUSTED_TIME] = this->to_attrs(
color_pair_base, lt, lt.lt_style_adjusted_time, reporter);
this->vc_role_colors[VCR_SKEWED_TIME] = this->to_attrs(
color_pair_base, lt, lt.lt_style_skewed_time, reporter);
this->vc_role_colors[VCR_OFFSET_TIME] = this->to_attrs(
color_pair_base, lt, lt.lt_style_offset_time, reporter);
this->vc_role_colors[VCR_STATUS] = this->to_attrs(color_pair_base,
lt, lt.lt_style_status, reporter);
this->vc_role_colors[VCR_WARN_STATUS] = this->to_attrs(color_pair_base,
lt, lt.lt_style_warn_status, reporter);
this->vc_role_colors[VCR_ALERT_STATUS] = this->to_attrs(color_pair_base,
lt, lt.lt_style_alert_status, reporter);
this->vc_role_colors[VCR_ACTIVE_STATUS] = this->to_attrs(color_pair_base,
lt, lt.lt_style_active_status, reporter);
this->vc_role_colors[VCR_ACTIVE_STATUS2] =
attr_for_colors(color_pair_base, COLOR_GREEN, COLOR_WHITE) | A_BOLD;
this->vc_role_colors[VCR_BOLD_STATUS] =
attr_for_colors(color_pair_base, COLOR_BLACK, COLOR_WHITE) | A_BOLD;
this->vc_role_colors[VCR_VIEW_STATUS] =
attr_for_colors(color_pair_base, COLOR_WHITE, COLOR_BLUE) | A_BOLD;
rgb_color::from_str(string_fragment("White"), fg, err);
rgb_color::from_str(string_fragment("Grey37"), bg, err);
this->vc_role_colors[VCR_INACTIVE_STATUS] = ensure_color_pair(fg, bg);
this->vc_role_colors[VCR_POPUP] =
attr_for_colors(color_pair_base, COLOR_WHITE, COLOR_CYAN) | A_BOLD;
this->vc_role_colors[VCR_KEYWORD] = attr_for_colors(color_pair_base, COLOR_BLUE, COLOR_BLACK);
this->vc_role_colors[VCR_STRING] = attr_for_colors(color_pair_base, COLOR_GREEN, COLOR_BLACK) | A_BOLD;
this->vc_role_colors[VCR_COMMENT] = attr_for_colors(color_pair_base, COLOR_GREEN, COLOR_BLACK);
this->vc_role_colors[VCR_VARIABLE] = attr_for_colors(color_pair_base, COLOR_CYAN, COLOR_BLACK);
this->vc_role_colors[VCR_SYMBOL] = attr_for_colors(color_pair_base, COLOR_MAGENTA, COLOR_BLACK) | A_BOLD;
this->vc_role_colors[VCR_RE_SPECIAL] = attr_for_colors(color_pair_base, COLOR_CYAN, COLOR_BLACK);
this->vc_role_colors[VCR_RE_REPEAT] = attr_for_colors(color_pair_base, COLOR_YELLOW, COLOR_BLACK);
this->vc_role_colors[VCR_FILE] = attr_for_colors(color_pair_base, COLOR_BLUE, COLOR_BLACK);
this->vc_role_colors[VCR_DIFF_DELETE] = attr_for_colors(color_pair_base, COLOR_RED, COLOR_BLACK);
this->vc_role_colors[VCR_DIFF_ADD] = attr_for_colors(color_pair_base, COLOR_GREEN, COLOR_BLACK);
this->vc_role_colors[VCR_DIFF_SECTION] = attr_for_colors(color_pair_base, COLOR_MAGENTA, COLOR_BLACK);
this->vc_role_colors[VCR_LOW_THRESHOLD] = attr_for_colors(color_pair_base, COLOR_BLACK, COLOR_GREEN);
this->vc_role_colors[VCR_MED_THRESHOLD] = attr_for_colors(color_pair_base, COLOR_BLACK, COLOR_YELLOW);
this->vc_role_colors[VCR_HIGH_THRESHOLD] = attr_for_colors(color_pair_base, COLOR_BLACK, COLOR_RED);
this->vc_color_pair_end = color_pair_base + 1;
make_pair(this->vc_role_colors[VCR_ACTIVE_STATUS].first | A_BOLD,
this->vc_role_colors[VCR_ACTIVE_STATUS].second | A_BOLD);
this->vc_role_colors[VCR_STATUS_TITLE] = this->to_attrs(
color_pair_base, lt, lt.lt_style_status_title, reporter);
this->vc_role_colors[VCR_STATUS_SUBTITLE] = this->to_attrs(
color_pair_base, lt, lt.lt_style_status_subtitle, reporter);
{
style_config stitch_sc;
stitch_sc.sc_color = lt.lt_style_status_subtitle.sc_background_color;
stitch_sc.sc_background_color =
lt.lt_style_status_title.sc_background_color;
this->vc_role_colors[VCR_STATUS_STITCH_TITLE_TO_SUB] =
this->to_attrs(color_pair_base, lt, stitch_sc, reporter);
}
{
style_config stitch_sc;
stitch_sc.sc_color = lt.lt_style_status_title.sc_background_color;
stitch_sc.sc_background_color =
lt.lt_style_status_subtitle.sc_background_color;
this->vc_role_colors[VCR_STATUS_STITCH_SUB_TO_TITLE] =
this->to_attrs(color_pair_base, lt, stitch_sc, reporter);
}
{
style_config stitch_sc;
stitch_sc.sc_color = lt.lt_style_status.sc_background_color;
stitch_sc.sc_background_color =
lt.lt_style_status_subtitle.sc_background_color;
this->vc_role_colors[VCR_STATUS_STITCH_SUB_TO_NORMAL] =
this->to_attrs(color_pair_base, lt, stitch_sc, reporter);
}
{
style_config stitch_sc;
stitch_sc.sc_color = lt.lt_style_status_subtitle.sc_background_color;
stitch_sc.sc_background_color =
lt.lt_style_status.sc_background_color;
this->vc_role_colors[VCR_STATUS_STITCH_NORMAL_TO_SUB] =
this->to_attrs(color_pair_base, lt, stitch_sc, reporter);
}
{
style_config stitch_sc;
stitch_sc.sc_color = lt.lt_style_status.sc_background_color;
stitch_sc.sc_background_color =
lt.lt_style_status_title.sc_background_color;
this->vc_role_colors[VCR_STATUS_STITCH_TITLE_TO_NORMAL] =
this->to_attrs(color_pair_base, lt, stitch_sc, reporter);
}
{
style_config stitch_sc;
stitch_sc.sc_color = lt.lt_style_status_title.sc_background_color;
stitch_sc.sc_background_color =
lt.lt_style_status.sc_background_color;
this->vc_role_colors[VCR_STATUS_STITCH_NORMAL_TO_TITLE] =
this->to_attrs(color_pair_base, lt, stitch_sc, reporter);
}
this->vc_role_colors[VCR_INACTIVE_STATUS] = this->to_attrs(color_pair_base,
lt, lt.lt_style_inactive_status, reporter);
this->vc_role_colors[VCR_POPUP] = this->to_attrs(
color_pair_base, lt, lt.lt_style_popup, reporter);
this->vc_role_colors[VCR_COLOR_HINT] = make_pair(
COLOR_PAIR(color_pair_base), COLOR_PAIR(color_pair_base + 1));
color_pair_base += 2;
this->vc_role_colors[VCR_KEYWORD] = this->to_attrs(color_pair_base,
lt, lt.lt_style_keyword, reporter);
this->vc_role_colors[VCR_STRING] = this->to_attrs(color_pair_base,
lt, lt.lt_style_string, reporter);
this->vc_role_colors[VCR_COMMENT] = this->to_attrs(color_pair_base,
lt, lt.lt_style_comment, reporter);
this->vc_role_colors[VCR_VARIABLE] = this->to_attrs(color_pair_base,
lt, lt.lt_style_variable, reporter);
this->vc_role_colors[VCR_SYMBOL] = this->to_attrs(color_pair_base,
lt, lt.lt_style_symbol, reporter);
this->vc_role_colors[VCR_NUMBER] = this->to_attrs(
color_pair_base, lt, lt.lt_style_number, reporter);
this->vc_role_colors[VCR_RE_SPECIAL] = this->to_attrs(
color_pair_base, lt, lt.lt_style_re_special, reporter);
this->vc_role_colors[VCR_RE_REPEAT] = this->to_attrs(
color_pair_base, lt, lt.lt_style_re_repeat, reporter);
this->vc_role_colors[VCR_FILE] = this->to_attrs(color_pair_base,
lt, lt.lt_style_file, reporter);
this->vc_role_colors[VCR_DIFF_DELETE] = this->to_attrs(
color_pair_base, lt, lt.lt_style_diff_delete, reporter);
this->vc_role_colors[VCR_DIFF_ADD] = this->to_attrs(
color_pair_base, lt, lt.lt_style_diff_add, reporter);
this->vc_role_colors[VCR_DIFF_SECTION] = this->to_attrs(
color_pair_base, lt, lt.lt_style_diff_section, reporter);
this->vc_role_colors[VCR_LOW_THRESHOLD] = this->to_attrs(
color_pair_base, lt, lt.lt_style_low_threshold, reporter);
this->vc_role_colors[VCR_MED_THRESHOLD] = this->to_attrs(
color_pair_base, lt, lt.lt_style_med_threshold, reporter);
this->vc_role_colors[VCR_HIGH_THRESHOLD] = this->to_attrs(
color_pair_base, lt, lt.lt_style_high_threshold, reporter);
for (log_level_t level = static_cast<log_level_t>(LEVEL_UNKNOWN + 1);
level < LEVEL__MAX;
level = static_cast<log_level_t>(level + 1)) {
auto level_iter = lt.lt_level_styles.find(level);
if (level_iter == lt.lt_level_styles.end()) {
this->vc_level_attrs[level] = this->to_attrs(
color_pair_base, lt, lt.lt_style_text, reporter);
} else {
this->vc_level_attrs[level] = this->to_attrs(
color_pair_base, lt, level_iter->second, reporter);
}
}
if (initialized && this->vc_color_pair_end == 0) {
this->vc_color_pair_end = color_pair_base + 1;
}
}
int view_colors::ensure_color_pair(const rgb_color &rgb_fg, const rgb_color &rgb_bg)
int view_colors::ensure_color_pair(int &pair_base, const rgb_color &rgb_fg, const rgb_color &rgb_bg)
{
return attr_for_colors(
this->vc_color_pair_end,
xterm_colors.match_color(rgb_fg),
rgb_bg.empty() ? (short) COLOR_BLACK : xterm_colors.match_color(rgb_bg));
pair_base,
rgb_fg.empty() ? (short) COLOR_WHITE : ACTIVE_PALETTE->match_color(rgb_fg),
rgb_bg.empty() ? (short) COLOR_BLACK : ACTIVE_PALETTE->match_color(rgb_bg));
}
attr_t view_colors::attrs_for_ident(const char *str, size_t len) const
{
unsigned long index = crc32(1, (const Bytef*)str, len);
attr_t retval;
if (COLORS >= 256) {
unsigned long offset = index % HI_COLOR_COUNT;
retval = COLOR_PAIR(VC_ANSI_END + offset);
short fg, bg;
int pnum = PAIR_NUMBER(retval);
pair_content(pnum, &fg, &bg);
}
else {
retval = BASIC_HL_PAIRS[index % BASIC_COLOR_COUNT];
}
return retval;
}
lab_color::lab_color(const rgb_color &rgb)

@ -64,6 +64,9 @@
#include "attr_line.hh"
#include "optional.hpp"
#include "lnav_util.hh"
#include "styling.hh"
#include "log_level.hh"
#include "lnav_config.hh"
#define KEY_CTRL_G 7
#define KEY_CTRL_L 12
@ -289,24 +292,6 @@ private:
void (*va_invoker)(void *functor, _Sender *sender);
};
struct rgb_color {
static bool from_str(const string_fragment &color,
rgb_color &rgb_out,
std::string &errmsg);
explicit rgb_color(short r = -1, short g = -1, short b = -1)
: rc_r(r), rc_g(g), rc_b(b) {
}
bool empty() const {
return this->rc_r == -1 && this->rc_g == -1 && this->rc_b == -1;
}
short rc_r;
short rc_g;
short rc_b;
};
struct lab_color {
lab_color() : lc_l(0), lc_a(0), lc_b(0) {
};
@ -357,16 +342,24 @@ public:
VCR_ALERT_STATUS, /*< Alert status line text. */
VCR_ACTIVE_STATUS, /*< */
VCR_ACTIVE_STATUS2, /*< */
VCR_BOLD_STATUS,
VCR_VIEW_STATUS,
VCR_STATUS_TITLE,
VCR_STATUS_SUBTITLE,
VCR_STATUS_STITCH_TITLE_TO_SUB,
VCR_STATUS_STITCH_SUB_TO_TITLE,
VCR_STATUS_STITCH_SUB_TO_NORMAL,
VCR_STATUS_STITCH_NORMAL_TO_SUB,
VCR_STATUS_STITCH_TITLE_TO_NORMAL,
VCR_STATUS_STITCH_NORMAL_TO_TITLE,
VCR_INACTIVE_STATUS,
VCR_POPUP,
VCR_COLOR_HINT,
VCR_KEYWORD,
VCR_STRING,
VCR_COMMENT,
VCR_VARIABLE,
VCR_SYMBOL,
VCR_NUMBER,
VCR_RE_SPECIAL,
VCR_RE_REPEAT,
VCR_FILE,
@ -392,18 +385,23 @@ public:
*/
static void init(void);
void init_roles(int color_pair_base);
void init_roles(const lnav_theme &lt, lnav_config_listener::error_reporter &reporter);
/**
* @param role The role to retrieve character attributes for.
* @return The attributes to use for the given role.
*/
attr_t attrs_for_role(role_t role) const
attr_t attrs_for_role(role_t role, bool selected = false) const
{
if (role == VCR_NONE) {
return 0;
}
require(role >= 0);
require(role < VCR__MAX);
return this->vc_role_colors[role];
return selected ? this->vc_role_colors[role].second :
this->vc_role_colors[role].first;
};
attr_t reverse_attrs_for_role(role_t role) const
@ -414,26 +412,17 @@ public:
return this->vc_role_reverse_colors[role];
};
attr_t attrs_for_ident(const char *str, size_t len) const {
unsigned long index = crc32(1, (const Bytef*)str, len);
attr_t retval;
if (COLORS >= 256) {
unsigned long offset = index % HI_COLOR_COUNT;
retval = COLOR_PAIR(VC_ANSI_END + offset);
}
else {
retval = BASIC_HL_PAIRS[index % BASIC_COLOR_COUNT];
}
return retval;
};
attr_t attrs_for_ident(const char *str, size_t len) const;;
attr_t attrs_for_ident(const std::string &str) const {
return this->attrs_for_ident(str.c_str(), str.length());
};
int ensure_color_pair(const rgb_color &fg, const rgb_color &bg);
int ensure_color_pair(int &pair_base, const rgb_color &fg, const rgb_color &bg);
int ensure_color_pair(const rgb_color &fg, const rgb_color &bg) {
return this->ensure_color_pair(this->vc_color_pair_end, fg, bg);
}
static inline int ansi_color_pair_index(int fg, int bg)
{
@ -445,20 +434,24 @@ public:
return COLOR_PAIR(ansi_color_pair_index(fg, bg));
};
enum {
VC_ANSI_START = 0,
VC_ANSI_END = VC_ANSI_START + (8 * 8),
};
static const int VC_ANSI_START = 0;
static const int VC_ANSI_END = VC_ANSI_START + (8 * 8);
std::pair<attr_t, attr_t> to_attrs(int &pair_base,
const lnav_theme &lt, const style_config &sc,
lnav_config_listener::error_reporter &reporter);
std::pair<attr_t, attr_t> vc_level_attrs[LEVEL__MAX];
static bool initialized;
private:
/** Private constructor that initializes the member fields. */
view_colors();
static bool initialized;
/** Map of role IDs to attribute values. */
attr_t vc_role_colors[VCR__MAX];
std::pair<attr_t, attr_t> vc_role_colors[VCR__MAX];
/** Map of role IDs to reverse-video attribute values. */
attr_t vc_role_reverse_colors[VCR__MAX];
int vc_color_pair_end;
@ -554,8 +547,10 @@ public:
return this->vc_width;
}
static string_attr_type VC_ROLE;
static string_attr_type VC_STYLE;
static string_attr_type VC_GRAPHIC;
static string_attr_type VC_SELECTED;
static string_attr_type VC_FOREGROUND;
static string_attr_type VC_BACKGROUND;

@ -37,6 +37,8 @@ extern "C" {
* The help message text. The value for this comes from the "init.sql" file,
* which gets linked into the executable by the Makefile.
*/
extern const char xterm_palette_json[];
extern const unsigned char xterm_palette_json[];
extern const unsigned char ansi_palette_json[];
}
#endif

File diff suppressed because one or more lines are too long

@ -71,6 +71,11 @@ yajl_gen_status json_path_handler_base::gen(yajlpp_gen_context &ygc, yajl_gen ha
start = lpc + 1;
}
}
if (this->jph_path_provider) {
yajl_gen_pstring(handle, &lpath[start], -1);
yajl_gen_map_open(handle);
ygc.ygc_depth += 1;
}
if (this->jph_obj_provider) {
pcre_context_static<30> pc;
@ -116,13 +121,20 @@ yajl_gen_status json_path_handler_base::gen(yajlpp_gen_context &ygc, yajl_gen ha
return yajl_gen_status_ok;
}
void json_path_handler_base::possibilities(
std::vector<std::string> &dst, void *root, const string &base) const
void json_path_handler_base::walk(
const std::function<void(const json_path_handler_base &,
const std::string &,
void *)> &cb,
void *root, const string &base) const
{
vector<string> local_paths;
if (this->jph_path_provider) {
this->jph_path_provider(root, local_paths);
for (auto &lpath : local_paths) {
cb(*this, lpath, nullptr);
}
}
else {
local_paths.emplace_back(this->jph_path);
@ -149,7 +161,8 @@ void json_path_handler_base::possibilities(
.with_obj(root)
.update_callbacks();
if (this->jph_obj_provider) {
pcre_input pi(lpath + "/");
string full_path = lpath + "/";
pcre_input pi(full_path);
if (!this->jph_regex.match(ypc.ypc_pcre_context, pi)) {
ensure(false);
@ -158,13 +171,18 @@ void json_path_handler_base::possibilities(
{{ypc.ypc_pcre_context, pi}, -1}, root);
}
this->jph_children[lpc].possibilities(dst, child_root, full_path);
this->jph_children[lpc].walk(cb, child_root, full_path);
}
}
}
else {
for (const auto &lpath : local_paths) {
dst.push_back(base + lpath);
for (auto &lpath : local_paths) {
void *field = nullptr;
if (this->jph_field_getter) {
field = this->jph_field_getter(root, lpath);
}
cb(*this, base + lpath, field);
}
}
}
@ -253,6 +271,7 @@ void yajlpp_parse_context::update_callbacks(const json_path_handler_base *orig_h
if (handlers == NULL) {
handlers = this->ypc_handlers;
this->ypc_handler_stack.clear();
}
if (!this->ypc_active_paths.empty()) {
@ -286,11 +305,18 @@ void yajlpp_parse_context::update_callbacks(const json_path_handler_base *orig_h
}
if (jph.jph_children) {
if (this->ypc_path[child_start + cap->c_end - 1] != '/') {
char last = this->ypc_path[child_start + cap->c_end - 1];
if (last != '/') {
continue;
}
this->update_callbacks(jph.jph_children, child_start + cap->c_end);
this->ypc_handler_stack.emplace_back(&jph);
if (child_start + cap->c_end != (int)this->ypc_path.size() - 1) {
this->update_callbacks(jph.jph_children,
child_start + cap->c_end);
}
}
else {
if (child_start + cap->c_end != (int)this->ypc_path.size() - 1) {
@ -300,18 +326,22 @@ void yajlpp_parse_context::update_callbacks(const json_path_handler_base *orig_h
this->ypc_current_handler = &jph;
}
if (jph.jph_callbacks.yajl_null != NULL)
if (jph.jph_callbacks.yajl_null != nullptr)
this->ypc_callbacks.yajl_null = jph.jph_callbacks.yajl_null;
if (jph.jph_callbacks.yajl_boolean != NULL)
if (jph.jph_callbacks.yajl_boolean != nullptr)
this->ypc_callbacks.yajl_boolean = jph.jph_callbacks.yajl_boolean;
if (jph.jph_callbacks.yajl_integer != NULL)
if (jph.jph_callbacks.yajl_integer != nullptr)
this->ypc_callbacks.yajl_integer = jph.jph_callbacks.yajl_integer;
if (jph.jph_callbacks.yajl_double != NULL)
if (jph.jph_callbacks.yajl_double != nullptr)
this->ypc_callbacks.yajl_double = jph.jph_callbacks.yajl_double;
if (jph.jph_callbacks.yajl_string != NULL)
if (jph.jph_callbacks.yajl_string != nullptr)
this->ypc_callbacks.yajl_string = jph.jph_callbacks.yajl_string;
return;
}
}
this->ypc_handler_stack.emplace_back(nullptr);
}
int yajlpp_parse_context::map_end(void *ctx)
@ -478,3 +508,16 @@ void yajlpp_gen_context::gen()
jph.gen(*this, this->ygc_handle);
}
}
yajlpp_gen_context &yajlpp_gen_context::with_context(yajlpp_parse_context &ypc)
{
this->ygc_obj_stack = ypc.ypc_obj_stack;
this->ygc_base_name = ypc.get_path_fragment(-1);
if (ypc.ypc_current_handler == nullptr &&
!ypc.ypc_handler_stack.empty() &&
ypc.ypc_handler_stack.back() != nullptr) {
this->ygc_handlers = static_cast<json_path_handler *>(ypc.ypc_handler_stack.back()->jph_children);
this->ygc_depth += 1;
}
return *this;
}

@ -44,6 +44,7 @@
#include <vector>
#include <string>
#include <algorithm>
#include <functional>
#include "optional.hpp"
#include "pcrepp.hh"
@ -135,6 +136,7 @@ struct json_path_handler_base {
: jph_path(path),
jph_regex(path, PCRE_ANCHORED),
jph_gen_callback(NULL),
jph_field_getter(nullptr),
jph_obj_provider(NULL),
jph_path_provider(NULL),
jph_synopsis(""),
@ -163,9 +165,12 @@ struct json_path_handler_base {
};
virtual yajl_gen_status gen(yajlpp_gen_context &ygc, yajl_gen handle) const;
virtual void possibilities(std::vector<std::string> &dst,
void *root = NULL,
const std::string &base = "") const;
virtual void walk(
const std::function<void(const json_path_handler_base &,
const std::string &,
void *)> &cb,
void *root = nullptr,
const std::string &base = "") const;
const char * jph_path;
pcrepp jph_regex;
@ -173,6 +178,7 @@ struct json_path_handler_base {
yajl_gen_status (*jph_gen_callback)(yajlpp_gen_context &, const json_path_handler_base &, yajl_gen);
void (*jph_validator)(yajlpp_parse_context &ypc,
const json_path_handler_base &jph);
void *(*jph_field_getter)(void *root, nonstd::optional<std::string> name);
void *(*jph_obj_provider)(const yajlpp_provider_context &pe, void *root);
void (*jph_path_provider)(void *root, std::vector<std::string> &paths_out);
const char * jph_synopsis;
@ -181,6 +187,7 @@ struct json_path_handler_base {
bool jph_kv_pair;
std::shared_ptr<pcrepp> jph_pattern;
const char * jph_pattern_re{nullptr};
std::function<void(const string_fragment &)> jph_string_validator;
size_t jph_min_length;
size_t jph_max_length;
const enum_value_t *jph_enum_values;
@ -190,6 +197,19 @@ struct json_path_handler_base {
struct json_path_handler;
struct source_location {
source_location()
: sl_source(intern_string::lookup("unknown")),
sl_line_number(-1) {
}
source_location(intern_string_t source, int line)
: sl_source(source), sl_line_number(line) {};
intern_string_t sl_source;
int sl_line_number;
};
class yajlpp_parse_context {
public:
typedef void (*error_reporter_t)(const yajlpp_parse_context &ypc,
@ -256,6 +276,11 @@ public:
this->ypc_path.size() - 2);
};
const intern_string_t get_full_path() const {
return intern_string::lookup(&this->ypc_path[0],
this->ypc_path.size() - 1);
};
bool is_level(size_t level) const {
return this->ypc_path_index_stack.size() == level;
};
@ -426,12 +451,14 @@ public:
std::vector<char> ypc_path;
std::vector<size_t> ypc_path_index_stack;
std::vector<int> ypc_array_index;
std::vector<const json_path_handler_base *> ypc_handler_stack;
pcre_context_static<30> ypc_pcre_context;
bool ypc_ignore_unused{false};
const struct json_path_handler_base *ypc_sibling_handlers{nullptr};
const struct json_path_handler_base *ypc_current_handler{nullptr};
std::set<std::string> ypc_active_paths;
error_reporter_t ypc_error_reporter{nullptr};
std::map<intern_string_t, source_location> *ypc_locations{nullptr};
void update_callbacks(const json_path_handler_base *handlers = NULL,
int child_start = 0);
@ -565,11 +592,7 @@ public:
return *this;
};
yajlpp_gen_context &with_context(yajlpp_parse_context &ypc) {
this->ygc_obj_stack = ypc.ypc_obj_stack;
this->ygc_base_name = ypc.get_path_fragment(-1);
return *this;
}
yajlpp_gen_context &with_context(yajlpp_parse_context &ypc);
void gen();

@ -153,6 +153,11 @@ struct json_path_handler : public json_path_handler_base {
return *this;
};
json_path_handler &with_string_validator(std::function<void(const string_fragment &)> val) {
this->jph_string_validator = val;
return *this;
};
json_path_handler &with_min_value(long long val) {
this->jph_min_value = val;
return *this;
@ -170,10 +175,25 @@ struct json_path_handler : public json_path_handler_base {
return *this;
}
template<typename T, typename MEM_T, MEM_T T::*MEM>
static void *get_field_lvalue_cb(void *root, nonstd::optional<std::string> name) {
auto obj = (T *) root;
auto &mem = obj->*MEM;
return &mem;
};
template<typename T, typename STR_T, STR_T T::*STR>
static int string_field_cb(yajlpp_parse_context *ypc, const unsigned char *str, size_t len) {
auto handler = ypc->ypc_current_handler;
if (ypc->ypc_locations) {
intern_string_t src = intern_string::lookup(ypc->ypc_source);
(*ypc->ypc_locations)[ypc->get_full_path()] = {
src, ypc->get_line_number() };
}
assign(ypc->get_lvalue(ypc->get_obj_member<T, STR_T, STR>()), string_fragment(str, 0, len));
handler->jph_validator(*ypc, *handler);
@ -248,7 +268,8 @@ struct json_path_handler : public json_path_handler_base {
auto &field_ptr = ypc.get_rvalue(ypc.get_obj_member<T, STR_T, STR>());
if (jph.jph_pattern) {
pcre_input pi(to_string_fragment(field_ptr));
string_fragment sf = to_string_fragment(field_ptr);
pcre_input pi(sf);
pcre_context_static<30> pc;
if (!jph.jph_pattern->match(pc, pi)) {
@ -257,6 +278,15 @@ struct json_path_handler : public json_path_handler_base {
jph.jph_pattern_re);
}
}
if (jph.jph_string_validator) {
try {
jph.jph_string_validator(to_string_fragment(field_ptr));
} catch (const std::exception &e) {
ypc.report_error(LOG_LEVEL_ERROR,
"%s",
e.what());
}
}
if (field_ptr.empty() && jph.jph_min_length > 0) {
ypc.report_error(LOG_LEVEL_ERROR, "value must not be empty");
} else if (field_ptr.size() < jph.jph_min_length) {
@ -296,11 +326,44 @@ struct json_path_handler : public json_path_handler_base {
return gen(obj->*FIELD);
};
static yajl_gen_status map_field_gen(yajlpp_gen_context &ygc,
const json_path_handler_base &jph,
yajl_gen handle)
{
const auto def_obj = (std::map<std::string, std::string> *) (
ygc.ygc_default_stack.empty() ? nullptr
: ygc.ygc_default_stack.top());
auto obj = (std::map<std::string, std::string> *) ygc.ygc_obj_stack.top();
yajl_gen_status rc;
for (auto &pair : *obj) {
if (def_obj != nullptr) {
auto iter = def_obj->find(pair.first);
if (iter != def_obj->end() && iter->second == pair.second) {
continue;
}
}
if ((rc = yajl_gen_string(handle, pair.first)) !=
yajl_gen_status_ok) {
return rc;
}
if ((rc = yajl_gen_string(handle, pair.second)) !=
yajl_gen_status_ok) {
return rc;
}
}
return yajl_gen_status_ok;
};
template<typename T, typename STR_T, std::string T::*STR>
json_path_handler &for_field() {
this->add_cb(string_field_cb<T, STR_T, STR>);
this->jph_gen_callback = field_gen<T, STR_T, STR>;
this->jph_validator = string_field_validator<T, STR_T, STR>;
this->jph_field_getter = get_field_lvalue_cb<T, STR_T, STR>;
return *this;
};
@ -308,6 +371,7 @@ struct json_path_handler : public json_path_handler_base {
template<typename T, typename STR_T, std::map<std::string, std::string> T::*STR>
json_path_handler &for_field() {
this->add_cb(string_field_cb<T, STR_T, STR>);
this->jph_gen_callback = map_field_gen;
this->jph_validator = string_field_validator<T, STR_T, STR>;
return *this;

@ -220,6 +220,7 @@ dist_noinst_SCRIPTS = \
parser_debugger.py \
test_cli.sh \
test_cmds.sh \
test_config.sh \
test_curl.sh \
test_data_parser.sh \
test_format_installer.sh \
@ -372,6 +373,7 @@ TESTS = \
test_format_loader.sh \
test_cli.sh \
test_cmds.sh \
test_config.sh \
test_line_buffer2 \
test_line_buffer.sh \
test_listview.sh \

@ -0,0 +1,5 @@
{
"ui": {
"theme": "foo"
}
}

@ -126,7 +126,7 @@ EOF
check_output "config clock-format" <<EOF
info: changed config option -- /ui/clock-format
info: reset option
info: reset option -- /ui/clock-format
info: /ui/clock-format = "%a %b %d %H:%M:%S %Z"
EOF

@ -0,0 +1,27 @@
#!/usr/bin/env bash
run_test ${lnav_test} -n \
-c ":config /ui/theme-defs/default/styles/text/color #f" \
${test_dir}/logfile_access_log.0
check_error_output "config bad color" <<EOF
error:command-option:1:Could not parse color: #f
EOF
run_test ${lnav_test} -n \
-c ":config /ui/theme baddy" \
${test_dir}/logfile_access_log.0
check_error_output "config bad theme" <<EOF
error:command-option:1:unknown theme -- baddy
EOF
run_test ${lnav_test} -n \
-I ${test_dir}/bad-config2 \
${test_dir}/logfile_access_log.0
sed -i "" -e "s|/.*/format|format|g" `test_err_filename`
check_error_output "config bad theme" <<EOF
formats/invalid-config/config.json:3:unknown theme -- foo
EOF
Loading…
Cancel
Save