From 0d37a8c142175d93d5cdf442bd3760ea858903d0 Mon Sep 17 00:00:00 2001 From: Timothy Stack Date: Tue, 18 Aug 2015 20:58:20 -0700 Subject: [PATCH] [text] pretty-printing should work in the text view --- NEWS | 1 + docs/source/hotkeys.rst | 2 +- src/CMakeLists.txt | 2 ++ src/help.txt | 6 ++-- src/hotkeys.cc | 8 +---- src/lnav.cc | 55 ++++++++++++++++++++++++++--------- src/pretty_printer.hh | 24 +++++++++++++-- src/timer.cc | 29 +++++++++++------- src/timer.hh | 3 +- test/Makefile.am | 2 ++ test/test_cmds.sh | 36 +++++++++++++++++++++++ test/textfile_json_indented.0 | 12 ++++++++ test/textfile_json_one_line.0 | 1 + 13 files changed, 142 insertions(+), 39 deletions(-) create mode 100644 test/textfile_json_indented.0 create mode 100644 test/textfile_json_one_line.0 diff --git a/NEWS b/NEWS index b2e5c064..230dfee9 100644 --- a/NEWS +++ b/NEWS @@ -30,6 +30,7 @@ lnav v0.8.0: summary of the currently entered command. Fixes: + * The pretty-print view should now work for text files. * Nested fields in JSON logs are now supported for levels, bodies, etc... * Tab-completion should work for quoted SQL identifiers. * 'lo-fi' mode key shortcut changed to CTRL+L. diff --git a/docs/source/hotkeys.rst b/docs/source/hotkeys.rst index f7c0271e..5c892a9e 100644 --- a/docs/source/hotkeys.rst +++ b/docs/source/hotkeys.rst @@ -183,7 +183,7 @@ Display * - |ks| q |ke| - Return to the previous view/quit * - |ks| Shift |ke| + |ks| p |ke| - - Switch to/from the pretty-printed view of the displayed log messages + - Switch to/from the pretty-printed view of the displayed log or text files * - |ks| Shift |ke| + |ks| t |ke| - Display elapsed time between lines * - |ks| t |ke| diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ce947a18..e5bced20 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -50,6 +50,7 @@ set(diag_STAT_SRCS state-extension-functions.cc strnatcmp.c textview_curses.cc + timer.cc view_curses.cc views_vtab.cc vt52_curses.cc @@ -112,6 +113,7 @@ set(diag_STAT_SRCS termios_guard.hh textfile_sub_source.hh time_T.hh + timer.hh top_status_source.hh url_loader.hh views_vtab.hh diff --git a/src/help.txt b/src/help.txt index d27e26d9..e0d6a5d1 100644 --- a/src/help.txt +++ b/src/help.txt @@ -166,9 +166,9 @@ through the file. >/< Move horizontally to the next/previous search hit. - P Switch to/from the pretty-printed view of the log messages - currently displayed. In this view, structured data, such - as XML, will be reformatted to make it easier to read. + P Switch to/from the pretty-printed view of the log or text + files currently displayed. In this view, structured data, + such as XML, will be reformatted to make it easier to read. t Switch to/from the text file view. The text file view is for any files that are not recognized as log files. diff --git a/src/hotkeys.cc b/src/hotkeys.cc index ec52ce18..f7f16639 100644 --- a/src/hotkeys.cc +++ b/src/hotkeys.cc @@ -817,13 +817,7 @@ void handle_paging_key(int ch) break; case 'P': - if (tc == &lnav_data.ld_views[LNV_PRETTY] || - (lss && lss->text_line_count() > 0)) { - toggle_view(&lnav_data.ld_views[LNV_PRETTY]); - } - else { - lnav_data.ld_rl_view->set_value("Pretty-printed only works with log messages"); - } + toggle_view(&lnav_data.ld_views[LNV_PRETTY]); break; case 't': diff --git a/src/lnav.cc b/src/lnav.cc index c7ebb495..db77f2a9 100644 --- a/src/lnav.cc +++ b/src/lnav.cc @@ -757,14 +757,25 @@ static void open_schema_view(void) static void open_pretty_view(void) { - textview_curses *log_tc = &lnav_data.ld_views[LNV_LOG]; + static const char *NOTHING_MSG = + "Nothing to pretty-print"; + + textview_curses *top_tc = lnav_data.ld_view_stack.top(); textview_curses *pretty_tc = &lnav_data.ld_views[LNV_PRETTY]; - logfile_sub_source &lss = lnav_data.ld_log_source; - if (lss.text_line_count() > 0) { - ostringstream stream; + textview_curses *log_tc = &lnav_data.ld_views[LNV_LOG]; + textview_curses *text_tc = &lnav_data.ld_views[LNV_TEXT]; + ostringstream stream; + + delete pretty_tc->get_sub_source(); + if (top_tc->get_inner_height() == 0) { + pretty_tc->set_sub_source(new plain_text_source(NOTHING_MSG)); + return; + } + + if (top_tc == log_tc) { + logfile_sub_source &lss = lnav_data.ld_log_source; bool first_line = true; - delete pretty_tc->get_sub_source(); for (vis_line_t vl = log_tc->get_top(); vl <= log_tc->get_bottom(); ++vl) { content_line_t cl = lss.at(vl); logfile *lf = lss.find(cl); @@ -784,15 +795,26 @@ static void open_pretty_view(void) stream << trim(pp.print()) << endl; first_line = false; } - pretty_tc->set_sub_source(new plain_text_source(stream.str())); - if (lnav_data.ld_last_pretty_print_top != log_tc->get_top()) { - pretty_tc->set_top(vis_line_t(0)); + } + else if (top_tc == text_tc) { + logfile *lf = lnav_data.ld_text_source.current_file(); + + for (vis_line_t vl = text_tc->get_top(); vl <= text_tc->get_bottom(); ++vl) { + logfile::iterator ll = lf->begin() + vl; + shared_buffer_ref sbr; + + lf->read_full_message(ll, sbr); + data_scanner ds(sbr); + pretty_printer pp(&ds); + + stream << pp.print() << endl; } - lnav_data.ld_last_pretty_print_top = log_tc->get_top(); } - else { - log_warning("no log data to pretty-print"); + pretty_tc->set_sub_source(new plain_text_source(stream.str())); + if (lnav_data.ld_last_pretty_print_top != log_tc->get_top()) { + pretty_tc->set_top(vis_line_t(0)); } + lnav_data.ld_last_pretty_print_top = log_tc->get_top(); redo_search(LNV_PRETTY); } @@ -2795,7 +2817,7 @@ int main(int argc, char *argv[]) if (lnav_data.ld_flags & LNF_HEADLESS) { std::vector > msgs; std::vector >::iterator msg_iter; - textview_curses *log_tc, *tc; + textview_curses *log_tc, *text_tc, *tc; attr_line_t al; const std::string &line = al.get_string(); bool found_error = false; @@ -2809,7 +2831,14 @@ int main(int argc, char *argv[]) wait_for_pipers(); rebuild_indexes(true); - lnav_data.ld_views[LNV_LOG].set_top(vis_line_t(0)); + log_tc->set_top(vis_line_t(0)); + text_tc = &lnav_data.ld_views[LNV_TEXT]; + text_tc->set_top(vis_line_t(0)); + text_tc->set_height(vis_line_t(text_tc->get_inner_height())); + if (lnav_data.ld_log_source.text_line_count() == 0 && + lnav_data.ld_text_source.text_line_count() > 0) { + toggle_view(&lnav_data.ld_views[LNV_TEXT]); + } log_info("Executing initial commands"); execute_init_commands(msgs); diff --git a/src/pretty_printer.hh b/src/pretty_printer.hh index d4db9e2c..13cfeb6f 100644 --- a/src/pretty_printer.hh +++ b/src/pretty_printer.hh @@ -41,6 +41,7 @@ #include #include #include +#include #include "timer.hh" #include "ansi_scrubber.hh" @@ -65,7 +66,10 @@ public: }; pretty_printer(data_scanner *ds) - : pp_depth(0), pp_line_length(0), pp_scanner(ds) { + : pp_leading_indent(0), + pp_depth(0), + pp_line_length(0), + pp_scanner(ds) { this->pp_body_lines.push(0); }; @@ -118,11 +122,20 @@ public: continue; } break; + case DT_WHITE: + if (this->pp_values.empty() && this->pp_depth == 0) { + this->pp_leading_indent = el.e_capture.length(); + continue; + } + break; default: break; } this->pp_values.push_back(el); } + while (this->pp_depth > 0) { + this->ascend(); + } this->flush_values(); return this->pp_stream.str(); @@ -157,7 +170,7 @@ private: break; } if (rc == 1 && reverse_lookup_enabled) { - const struct timeval timeout = {3, 0}; + const struct timeval timeout = {0, 500 * 1000}; { timer::interrupt_timer t(timeout, sigalrm_handler); @@ -238,13 +251,17 @@ private: } void append_indent() { + this->pp_stream << std::string(this->pp_leading_indent, ' '); + if (this->pp_stream.tellp() == this->pp_leading_indent) { + return; + } for (int lpc = 0; lpc < this->pp_depth; lpc++) { this->pp_stream << " "; } } void write_element(const element &el) { - if (this->pp_depth > 0 && this->pp_line_length == 0 && el.e_token == DT_WHITE) { + if (this->pp_line_length == 0 && el.e_token == DT_WHITE) { return; } if (this->pp_line_length == 0 && el.e_token == DT_LINE) { @@ -270,6 +287,7 @@ private: } } + int pp_leading_indent; int pp_depth; int pp_line_length; std::stack pp_body_lines; diff --git a/src/timer.cc b/src/timer.cc index 5dc3eb68..8c0fa902 100644 --- a/src/timer.cc +++ b/src/timer.cc @@ -27,10 +27,12 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "config.h" + #include "timer.hh" #include "lnav_log.hh" -const struct itimerval disable = { +static const struct itimerval DISABLE_TV = { { 0, 0 }, { 0, 0 } }; @@ -39,20 +41,25 @@ timer::error::error(int err):e_err(err) { } timer::interrupt_timer::interrupt_timer(struct timeval t, sighandler_t_ sighandler=SIG_IGN) : new_handler(sighandler), - old_handler(NULL), new_val((struct itimerval){{0,0},t}), - old_val(disable), armed(false) { } + new_val((struct itimerval){{0,0},t}), + old_val(DISABLE_TV), armed(false) { + memset(&this->old_handler, 0, sizeof(this->old_handler)); +} int timer::interrupt_timer::arm_timer() { + struct sigaction sa; + // Disable the interval timer before setting the handler and arming the // interval timer or else we will have a race-condition where the timer // might fire and the appropriate handler might not be set. - if (setitimer(ITIMER_REAL, &disable, &this->old_val) != 0) { + if (setitimer(ITIMER_REAL, &DISABLE_TV, &this->old_val) != 0) { log_error("Unable to disable the timer: %s", strerror(errno)); return -1; } - this->old_handler = signal(SIGALRM, this->new_handler); - if (this->old_handler == SIG_ERR) { + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = this->new_handler; + if (sigaction(SIGALRM, &sa, &this->old_handler) == -1) { log_error("Unable to set the signal handler: %s", strerror(errno)); if (setitimer(ITIMER_REAL, &this->old_val, NULL) != 0) { @@ -64,7 +71,7 @@ int timer::interrupt_timer::arm_timer() { } if (setitimer(ITIMER_REAL, &this->new_val, NULL) != 0) { - if(signal(SIGALRM, this->old_handler) == SIG_ERR) { + if(sigaction(SIGALRM, &this->old_handler, NULL) == -1) { log_error("Unable to reset the signal handler: %s", strerror(errno)); throw timer::error(errno); @@ -86,12 +93,12 @@ void timer::interrupt_timer::disarm_timer() { // the previous interval timer or else we will have a race-condition // where the timer might fire and the appropriate handler might not be // set. - if (setitimer(ITIMER_REAL, &disable, NULL) != 0) { + if (setitimer(ITIMER_REAL, &DISABLE_TV, NULL) != 0) { log_error("Failed to disable the timer: %s", strerror(errno)); throw timer::error(errno); } - if (signal(SIGALRM, this->old_handler) == SIG_ERR) { + if (sigaction(SIGALRM, &this->old_handler, NULL) == -1) { log_error("Failed to reinstall previous SIGALRM handler: %s", strerror(errno)); throw timer::error(errno); @@ -102,8 +109,8 @@ void timer::interrupt_timer::disarm_timer() { throw timer::error(errno); } this->armed = false; - this->old_val = disable; - this->old_handler = NULL; + this->old_val = DISABLE_TV; + memset(&this->old_handler, 0, sizeof(this->old_handler)); } } diff --git a/src/timer.hh b/src/timer.hh index 5d4e1cd2..6c4ebcfd 100644 --- a/src/timer.hh +++ b/src/timer.hh @@ -56,7 +56,8 @@ class interrupt_timer { bool is_armed(); ~interrupt_timer(); private: - sighandler_t_ new_handler, old_handler; + sighandler_t_ new_handler; + struct sigaction old_handler; struct itimerval new_val, old_val; bool armed; }; diff --git a/test/Makefile.am b/test/Makefile.am index 548bed3d..6bcad71f 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -251,6 +251,8 @@ dist_noinst_DATA = \ logfile_with_a_really_long_name_to_test_a_bug_with_long_names.0 \ multiline.lnav \ mvwattrline_output.0 \ + textfile_json_indented.0 \ + textfile_json_one_line.0 \ view_colors_output.0 \ vt52_curses_input.0 \ vt52_curses_input.1 \ diff --git a/test/test_cmds.sh b/test/test_cmds.sh index da3e1c0e..016863cd 100644 --- a/test/test_cmds.sh +++ b/test/test_cmds.sh @@ -355,6 +355,42 @@ check_output "pipe-line-to env vars are not working" <