[log-view] in time-offset mode, show negative times for messages before the first mark

Prototype for #395
pull/672/head
Timothy Stack 5 years ago
parent d61f2568d6
commit b81b35c866

@ -2,5 +2,16 @@
cmake_minimum_required (VERSION 2.6)
SET(CMAKE_CXX_STANDARD 14)
project (lnav)
set(lnav_LIBS
/usr/lib/libz.dylib
/usr/lib/libbz2.dylib
/usr/local/opt/sqlite/lib/libsqlite3.a
/usr/local/opt/pcre/lib/libpcre.a
/usr/local/opt/pcre/lib/libpcrecpp.a
/usr/local/opt/readline/lib/libreadline.a
/usr/local/opt/ncurses/lib/libncurses.a)
add_subdirectory(src)
add_subdirectory(test)

@ -1,5 +1,5 @@
set(diag_STAT_SRCS
add_library(diag STATIC
ansi_scrubber.cc
bookmarks.cc
bottom_status_source.cc
@ -67,6 +67,7 @@ set(diag_STAT_SRCS
ptimec.cc
sql_util.cc
state-extension-functions.cc
styling.cc
base/string_util.cc
strnatcmp.c
text_format.cc
@ -200,7 +201,13 @@ set(diag_STAT_SRCS
fmtlib/fmt/format.h
fmtlib/format.cc
log_level_re.cc
../../lbuild-debug/src/config.h
../../lbuild-debug/src/time_fmts.cc
../../lbuild-debug/src/default-config.c
../../lbuild-debug/src/ansi-palette-json.c
../../lbuild-debug/src/xterm-palette-json.c
)
set(lnav_SRCS lnav.cc)
@ -214,16 +221,10 @@ include_directories(
fmtlib
)
include_directories(SYSTEM .)
add_executable(lnav ${lnav_SRCS} ${diag_STAT_SRCS})
add_executable(lnav ${lnav_SRCS})
target_link_libraries(lnav
/usr/lib/libz.dylib
/usr/lib/libbz2.dylib
/usr/local/opt/curl/lib/libcurl.dylib
/usr/local/opt/sqlite/lib/libsqlite3.a
/usr/local/opt/pcre/lib/libpcre.a
/usr/local/opt/pcre/lib/libpcrecpp.a
/usr/local/opt/readline/lib/libreadline.a
/usr/local/opt/ncurses/lib/libncurses.a)
diag
${lnav_LIBS})
add_executable(bin2c bin2c.c)

@ -381,6 +381,7 @@ libdiag_a_SOURCES = \
sqlite-extension-func.cc \
statusview_curses.cc \
string-extension-functions.cc \
styling.cc \
text_format.cc \
timer.cc \
piper_proc.cc \

@ -74,9 +74,9 @@ void field_overlay_source::build_summary_lines(const listview_curses &lv)
last_line = lss.find_line(lss.at(lv.get_bottom()));
last_time = "Last message: " ANSI_BOLD_START + precise_time_ago(
last_line->get_timeval(), true) + ANSI_NORM;
str2reltime(last_line->get_time_in_millis() -
first_line->get_time_in_millis(),
time_span);
duration2str(last_line->get_time_in_millis() -
first_line->get_time_in_millis(),
time_span);
time_t local_now = convert_log_time_to_local(now);
time_t five_minutes_ago = local_now - (5 * 60 * 60);

@ -299,22 +299,31 @@ void logfile_sub_source::text_value_for_line(textview_curses &tc,
}
if (this->lss_flags & F_TIME_OFFSET) {
int64_t start_millis, curr_millis;
int64_t curr_millis, diff;
curr_millis = this->lss_token_line->get_time_in_millis();
vis_line_t prev_mark =
tc.get_bookmarks()[&textview_curses::BM_USER].prev(vis_line_t(row));
if (prev_mark == -1) {
prev_mark = vis_line_t(0);
}
vis_line_t next_mark =
tc.get_bookmarks()[&textview_curses::BM_USER].next(vis_line_t(row));
if (prev_mark == -1 && next_mark != -1) {
auto next_line = this->find_line(this->at(next_mark));
logline *first_line = this->find_line(this->at(prev_mark));
start_millis = first_line->get_time_in_millis();
curr_millis = this->lss_token_line->get_time_in_millis();
int64_t diff = curr_millis - start_millis;
diff = curr_millis - next_line->get_time_in_millis();
} else {
if (prev_mark == -1) {
prev_mark = 0_vl;
}
auto first_line = this->find_line(this->at(prev_mark));
auto start_millis = first_line->get_time_in_millis();
diff = curr_millis - start_millis;
}
value_out = "|" + value_out;
string relstr;
size_t rel_length = str2reltime(diff, relstr);
size_t rel_length = duration2str(diff, relstr);
value_out.insert(0, relstr);
if (rel_length < 12) {
value_out.insert(0, 12 - rel_length, ' ');

@ -505,7 +505,7 @@ std::string relative_time::to_string()
return dst;
}
size_t str2reltime(int64_t millis, std::string &value_out)
size_t duration2str(int64_t millis, std::string &value_out)
{
/* 24h22m33s111 */
@ -523,7 +523,15 @@ size_t str2reltime(int64_t millis, std::string &value_out)
};
struct rel_interval *curr_interval = intervals;
size_t in_len = value_out.length(), retval = 0;
size_t retval = 0;
if (millis < 0) {
value_out.append(1, '-');
millis = -millis;
retval += 1;
}
size_t in_len = value_out.length();
if (millis >= (10 * 60 * 1000)) {
millis /= 1000;

@ -314,11 +314,11 @@ public:
int rt_absolute_field_end;
};
size_t str2reltime(int64_t millis, std::string &value_out);
size_t duration2str(int64_t millis, std::string &value_out);
inline
size_t str2reltime(const struct timeval &tv, std::string &value_out) {
return str2reltime(tv.tv_sec * 1000 + tv.tv_usec / 1000, value_out);
return duration2str(tv.tv_sec * 1000 + tv.tv_usec / 1000, value_out);
};
#endif //LNAV_RELATIVE_TIME_HH

@ -0,0 +1,169 @@
/**
* 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.
*/
#include "config.h"
#include <stdlib.h>
#include <string>
#include "yajlpp/yajlpp.hh"
#include "yajlpp/yajlpp_def.hh"
#include "styling.hh"
#include "ansi-palette-json.h"
#include "xterm-palette-json.h"
using namespace std;
static struct json_path_handler term_color_rgb_handler[] = {
json_path_handler("r")
.FOR_FIELD(rgb_color, rc_r),
json_path_handler("g")
.FOR_FIELD(rgb_color, rc_g),
json_path_handler("b")
.FOR_FIELD(rgb_color, rc_b),
json_path_handler()
};
static struct json_path_handler term_color_handler[] = {
json_path_handler("colorId")
.FOR_FIELD(term_color, xc_id),
json_path_handler("name")
.FOR_FIELD(term_color, xc_name),
json_path_handler("rgb/")
.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<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(term_color_handler),
json_path_handler()
};
term_color_palette xterm_colors(xterm_palette_json.bsf_data);
term_color_palette ansi_colors(ansi_palette_json.bsf_data);
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:
if (sscanf(color.data(), "#%1hx%1hx%1hx",
&rgb_out.rc_r, &rgb_out.rc_g, &rgb_out.rc_b) == 3) {
rgb_out.rc_r |= rgb_out.rc_r << 4;
rgb_out.rc_g |= rgb_out.rc_g << 4;
rgb_out.rc_b |= rgb_out.rc_b << 4;
return true;
}
break;
case 7:
if (sscanf(color.data(), "#%2hx%2hx%2hx",
&rgb_out.rc_r, &rgb_out.rc_g, &rgb_out.rc_b) == 3) {
return true;
}
break;
}
errmsg = "Could not parse color: " + color.to_string();
return false;
}
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";
return false;
}
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->tc_palette)
.with_handle(handle);
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->tc_palette) {
xc.xc_lab_color = lab_color(xc.xc_color);
}
}
short term_color_palette::match_color(const lab_color &to_match)
{
double lowest = 1000.0;
short lowest_id = -1;
for (auto &xc : this->tc_palette) {
double xc_delta = xc.xc_lab_color.deltaE(to_match);
if (lowest_id == -1) {
lowest = xc_delta;
lowest_id = xc.xc_id;
continue;
}
if (xc_delta < lowest) {
lowest = xc_delta;
lowest_id = xc.xc_id;
}
}
return lowest_id;
}

@ -32,6 +32,7 @@
#include <map>
#include <string>
#include <vector>
#include "log_level.hh"
#include "intern_string.hh"
@ -54,6 +55,42 @@ struct rgb_color {
short rc_b;
};
struct lab_color {
lab_color() : lc_l(0), lc_a(0), lc_b(0) {
};
explicit lab_color(const rgb_color &rgb);
double deltaE(const lab_color &other) const;
lab_color& operator=(const lab_color &other) {
this->lc_l = other.lc_l;
this->lc_a = other.lc_a;
this->lc_b = other.lc_b;
return *this;
};
double lc_l;
double lc_a;
double lc_b;
};
struct term_color {
short xc_id;
std::string xc_name;
rgb_color xc_color;
lab_color xc_lab_color;
};
struct term_color_palette {
explicit term_color_palette(const unsigned char *json);
short match_color(const lab_color &to_match);
std::vector<term_color> tc_palette;
};
struct style_config {
std::string sc_color;
std::string sc_background_color;
@ -100,4 +137,9 @@ struct lnav_theme {
std::map<log_level_t, style_config> lt_level_styles;
};
extern term_color_palette xterm_colors;
extern term_color_palette ansi_colors;
extern term_color_palette *ACTIVE_PALETTE;
#endif

@ -41,148 +41,11 @@
#include "lnav_config.hh"
#include "yajlpp/yajlpp.hh"
#include "yajlpp/yajlpp_def.hh"
#include "ansi-palette-json.h"
#include "xterm-palette-json.h"
#include "attr_line.hh"
#include "shlex.hh"
using namespace std;
struct term_color {
short xc_id;
string xc_name;
rgb_color xc_color;
lab_color xc_lab_color;
};
static struct json_path_handler term_color_rgb_handler[] = {
json_path_handler("r")
.FOR_FIELD(rgb_color, rc_r),
json_path_handler("g")
.FOR_FIELD(rgb_color, rc_g),
json_path_handler("b")
.FOR_FIELD(rgb_color, rc_b),
json_path_handler()
};
static struct json_path_handler term_color_handler[] = {
json_path_handler("colorId")
.FOR_FIELD(term_color, xc_id),
json_path_handler("name")
.FOR_FIELD(term_color, xc_name),
json_path_handler("rgb/")
.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<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(term_color_handler),
json_path_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->tc_palette)
.with_handle(handle);
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->tc_palette) {
xc.xc_lab_color = lab_color(xc.xc_color);
}
};
short match_color(const lab_color &to_match) {
double lowest = 1000.0;
short lowest_id = -1;
for (auto &xc : this->tc_palette) {
double xc_delta = xc.xc_lab_color.deltaE(to_match);
if (lowest_id == -1) {
lowest = xc_delta;
lowest_id = xc.xc_id;
continue;
}
if (xc_delta < lowest) {
lowest = xc_delta;
lowest_id = xc.xc_id;
}
}
return lowest_id;
};
vector<term_color> tc_palette;
};
term_color_palette xterm_colors(xterm_palette_json.bsf_data);
term_color_palette ansi_colors(ansi_palette_json.bsf_data);
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:
if (sscanf(color.data(), "#%1hx%1hx%1hx",
&rgb_out.rc_r, &rgb_out.rc_g, &rgb_out.rc_b) == 3) {
rgb_out.rc_r |= rgb_out.rc_r << 4;
rgb_out.rc_g |= rgb_out.rc_g << 4;
rgb_out.rc_b |= rgb_out.rc_b << 4;
return true;
}
break;
case 7:
if (sscanf(color.data(), "#%2hx%2hx%2hx",
&rgb_out.rc_r, &rgb_out.rc_g, &rgb_out.rc_b) == 3) {
return true;
}
break;
}
errmsg = "Could not parse color: " + color.to_string();
return false;
}
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";
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");
@ -190,7 +53,8 @@ string_attr_type view_curses::VC_SELECTED("selected");
string_attr_type view_curses::VC_FOREGROUND("foreground");
string_attr_type view_curses::VC_BACKGROUND("background");
const struct itimerval ui_periodic_timer::INTERVAL = { { 0, 350 * 1000 },
const struct itimerval ui_periodic_timer::INTERVAL = {
{ 0, 350 * 1000 },
{ 0, 350 * 1000 }
};
@ -821,7 +685,7 @@ void view_colors::init_roles(const lnav_theme &lt,
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);
ident_bg = ACTIVE_PALETTE->match_color(lab_color(rgb_bg));
}
for (int z = 0; z < 6; z++) {
for (int x = 1; x < 6; x += 2) {
@ -868,8 +732,8 @@ void view_colors::init_roles(const lnav_theme &lt,
return;
}
short fg = ACTIVE_PALETTE->match_color(rgb_fg);
short bg = ACTIVE_PALETTE->match_color(rgb_bg);
short fg = ACTIVE_PALETTE->match_color(lab_color(rgb_fg));
short bg = ACTIVE_PALETTE->match_color(lab_color(rgb_bg));
if (rgb_fg.empty()) {
fg = ansi_fg;
@ -1072,8 +936,8 @@ int view_colors::ensure_color_pair(int &pair_base, const rgb_color &rgb_fg, cons
{
return attr_for_colors(
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));
rgb_fg.empty() ? (short) COLOR_WHITE : ACTIVE_PALETTE->match_color(lab_color(rgb_fg)),
rgb_bg.empty() ? (short) COLOR_BLACK : ACTIVE_PALETTE->match_color(lab_color(rgb_bg)));
}
attr_t view_colors::attrs_for_ident(const char *str, size_t len) const

@ -292,27 +292,6 @@ private:
void (*va_invoker)(void *functor, _Sender *sender);
};
struct lab_color {
lab_color() : lc_l(0), lc_a(0), lc_b(0) {
};
lab_color(const rgb_color &rgb);
double deltaE(const lab_color &other) const;
lab_color& operator=(const lab_color &other) {
this->lc_l = other.lc_l;
this->lc_a = other.lc_a;
this->lc_b = other.lc_b;
return *this;
};
double lc_l;
double lc_a;
double lc_b;
};
/**
* Singleton used to manage the colorspace.
*/

@ -9,12 +9,8 @@ include_directories(
)
add_executable(lnav_doctests
lnav_doctests.cc
../src/relative_time.cc
../../lbuild-debug/src/time_fmts.cc
../src/ptimec_rt.cc
../src/pcrepp/pcrepp.cc
../src/base/lnav_log.cc)
lnav_doctests.cc)
target_link_libraries(lnav_doctests diag ${lnav_LIBS})
add_executable(test_pcrepp test_pcrepp.cc ../src/base/lnav_log.cc ../src/pcrepp/pcrepp.cc)
add_executable(test_line_buffer2
test_line_buffer2.cc

@ -39,23 +39,29 @@
using namespace std;
TEST_CASE("str2reltime") {
TEST_CASE("duration2str") {
string val;
str2reltime(25 * 60 * 60 * 1000 + 123, val);
duration2str(25 * 60 * 60 * 1000 + 123, val);
CHECK(val == "1d1h0m0s");
val.clear();
str2reltime(10 * 1000 + 123, val);
duration2str(10 * 1000 + 123, val);
CHECK(val == "10s123");
val.clear();
str2reltime(10 * 1000, val);
duration2str(10 * 1000, val);
CHECK(val == "10s000");
val.clear();
str2reltime(100, val);
duration2str(100, val);
CHECK(val == "100");
val.clear();
str2reltime(0, val);
duration2str(0, val);
CHECK(val == "");
val.clear();
duration2str(-10, val);
CHECK(val == "-010");
val.clear();
duration2str(-10 * 1000, val);
CHECK(val == "-10s000");
}
TEST_CASE("ptime_fmt") {

Loading…
Cancel
Save