lnav/src/hotkeys.cc

981 lines
36 KiB
C++
Raw Normal View History

/**
* Copyright (c) 2015, 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;
2022-03-16 22:38:08 +00:00
* 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.
*/
2022-03-16 22:38:08 +00:00
#include "hotkeys.hh"
2022-04-12 23:07:13 +00:00
#include "base/ansi_scrubber.hh"
#include "base/injector.hh"
#include "base/itertools.hh"
#include "base/math_util.hh"
2022-03-16 22:38:08 +00:00
#include "base/opt_util.hh"
#include "bookmarks.hh"
2022-03-16 22:38:08 +00:00
#include "bound_tags.hh"
#include "command_executor.hh"
#include "config.h"
#include "environ_vtab.hh"
2022-03-16 22:38:08 +00:00
#include "field_overlay_source.hh"
#include "lnav.hh"
#include "lnav_config.hh"
#include "lnav_util.hh"
#include "log_data_helper.hh"
#include "plain_text_source.hh"
#include "readline_highlighters.hh"
#include "shlex.hh"
2022-03-16 22:38:08 +00:00
#include "sql_util.hh"
#include "sysclip.hh"
#include "termios_guard.hh"
#include "xterm_mouse.hh"
2022-07-22 06:30:49 +00:00
using namespace lnav::roles::literals;
2015-11-25 04:59:24 +00:00
class logline_helper {
public:
explicit logline_helper(logfile_sub_source& lss) : lh_sub_source(lss) {}
2015-11-25 04:59:24 +00:00
2022-03-16 22:38:08 +00:00
logline& move_to_msg_start()
{
2015-11-25 04:59:24 +00:00
content_line_t cl = this->lh_sub_source.at(this->lh_current_line);
std::shared_ptr<logfile> lf = this->lh_sub_source.find(cl);
auto ll = lf->begin() + cl;
while (!ll->is_message()) {
2015-11-25 04:59:24 +00:00
--ll;
--this->lh_current_line;
}
return (*lf)[cl];
2022-05-11 04:58:32 +00:00
}
2015-11-25 04:59:24 +00:00
logline& current_line() const
2022-03-16 22:38:08 +00:00
{
content_line_t cl = this->lh_sub_source.at(this->lh_current_line);
std::shared_ptr<logfile> lf = this->lh_sub_source.find(cl);
return (*lf)[cl];
2022-05-11 04:58:32 +00:00
}
2022-03-16 22:38:08 +00:00
void annotate()
{
2015-11-25 04:59:24 +00:00
this->lh_string_attrs.clear();
this->lh_line_values.clear();
content_line_t cl = this->lh_sub_source.at(this->lh_current_line);
auto lf = this->lh_sub_source.find(cl);
auto ll = lf->begin() + cl;
auto format = lf->get_format();
lf->read_full_message(ll, this->lh_line_values.lvv_sbr);
this->lh_line_values.lvv_sbr.erase_ansi();
format->annotate(
cl, this->lh_string_attrs, this->lh_line_values, false);
2022-05-11 04:58:32 +00:00
}
2015-11-25 04:59:24 +00:00
2022-04-04 05:10:20 +00:00
std::string to_string(const struct line_range& lr) const
2022-03-16 22:38:08 +00:00
{
const char* start = this->lh_line_values.lvv_sbr.get_data();
2015-11-25 04:59:24 +00:00
return {&start[lr.lr_start], (size_t) lr.length()};
2015-11-25 04:59:24 +00:00
}
2022-03-16 22:38:08 +00:00
logfile_sub_source& lh_sub_source;
2015-11-25 04:59:24 +00:00
vis_line_t lh_current_line;
string_attrs_t lh_string_attrs;
logline_value_vector lh_line_values;
2015-11-25 04:59:24 +00:00
};
2022-03-16 22:38:08 +00:00
static int
key_sql_callback(exec_context& ec, sqlite3_stmt* stmt)
{
if (!sqlite3_stmt_busy(stmt)) {
return 0;
}
int ncols = sqlite3_column_count(stmt);
2022-03-16 22:38:08 +00:00
auto& vars = ec.ec_local_vars.top();
for (int lpc = 0; lpc < ncols; lpc++) {
2022-03-16 22:38:08 +00:00
const char* column_name = sqlite3_column_name(stmt, lpc);
if (sql_ident_needs_quote(column_name)) {
continue;
}
auto* raw_value = sqlite3_column_value(stmt, lpc);
auto value_type = sqlite3_column_type(stmt, lpc);
scoped_value_t value;
switch (value_type) {
case SQLITE_INTEGER:
value = (int64_t) sqlite3_value_int64(raw_value);
break;
case SQLITE_FLOAT:
value = sqlite3_value_double(raw_value);
break;
case SQLITE_NULL:
value = null_value_t{};
break;
default:
2022-08-07 17:00:34 +00:00
value = std::string((const char*) sqlite3_value_text(raw_value),
sqlite3_value_bytes(raw_value));
break;
}
vars[column_name] = value;
}
return 0;
}
2022-03-16 22:38:08 +00:00
bool
handle_keyseq(const char* keyseq)
{
2022-03-16 22:38:08 +00:00
key_map& km = lnav_config.lc_active_keymap;
2022-03-16 22:38:08 +00:00
const auto& iter = km.km_seq_to_cmd.find(keyseq);
if (iter == km.km_seq_to_cmd.end()) {
return false;
}
logline_value_vector values;
exec_context ec(&values, key_sql_callback, pipe_callback);
2022-03-16 22:38:08 +00:00
auto& var_stack = ec.ec_local_vars;
ec.ec_global_vars = lnav_data.ld_exec_context.ec_global_vars;
ec.ec_error_callback_stack
= lnav_data.ld_exec_context.ec_error_callback_stack;
var_stack.push(std::map<std::string, scoped_value_t>());
2022-03-16 22:38:08 +00:00
auto& vars = var_stack.top();
vars["keyseq"] = keyseq;
2022-03-16 22:38:08 +00:00
const auto& kc = iter->second;
log_debug("executing key sequence %s: %s", keyseq, kc.kc_cmd.c_str());
auto result = execute_any(ec, kc.kc_cmd);
if (result.isOk()) {
lnav_data.ld_rl_view->set_value(result.unwrap());
} else {
auto um = result.unwrapErr();
ec.ec_error_callback_stack.back()(um);
}
if (!kc.kc_alt_msg.empty()) {
shlex lexer(kc.kc_alt_msg);
2022-03-31 15:59:19 +00:00
std::string expanded_msg;
2022-03-16 22:38:08 +00:00
if (lexer.eval(expanded_msg,
{
&vars,
&ec.ec_global_vars,
}))
{
lnav_data.ld_rl_view->set_alt_value(expanded_msg);
}
}
return true;
}
2022-03-16 22:38:08 +00:00
bool
handle_paging_key(int ch)
{
if (lnav_data.ld_view_stack.empty()) {
return false;
}
2022-03-16 22:38:08 +00:00
textview_curses* tc = *lnav_data.ld_view_stack.top();
exec_context& ec = lnav_data.ld_exec_context;
logfile_sub_source* lss = nullptr;
text_sub_source* tc_tss = tc->get_sub_source();
bookmarks<vis_line_t>::type& bm = tc->get_bookmarks();
2022-03-25 23:38:11 +00:00
auto keyseq = fmt::format(FMT_STRING("x{:02x}"), ch);
if (handle_keyseq(keyseq.c_str())) {
return true;
}
if (tc->handle_key(ch)) {
return true;
}
2022-03-16 22:38:08 +00:00
lss = dynamic_cast<logfile_sub_source*>(tc->get_sub_source());
/* process the command keystroke */
switch (ch) {
case 0x7f:
case KEY_BACKSPACE:
break;
case 'a':
if (lnav_data.ld_last_view == nullptr) {
alerter::singleton().chime("no last view available");
2022-03-16 22:38:08 +00:00
} else {
auto* tc = lnav_data.ld_last_view;
lnav_data.ld_last_view = nullptr;
ensure_view(tc);
}
break;
case 'A':
if (lnav_data.ld_last_view == nullptr) {
alerter::singleton().chime("no last view available");
2022-03-16 22:38:08 +00:00
} else {
auto* tc = lnav_data.ld_last_view;
auto* top_tc = *lnav_data.ld_view_stack.top();
2022-03-16 22:38:08 +00:00
auto* dst_view
= dynamic_cast<text_time_translator*>(tc->get_sub_source());
auto* src_view = dynamic_cast<text_time_translator*>(
top_tc->get_sub_source());
lnav_data.ld_last_view = nullptr;
if (src_view != nullptr && dst_view != nullptr) {
2022-03-16 22:38:08 +00:00
src_view->time_for_row(top_tc->get_top()) |
[dst_view, tc](auto top_time) {
dst_view->row_for_time(top_time) |
[tc](auto row) { tc->set_selection(row); };
};
}
ensure_view(tc);
}
break;
case KEY_F(2):
if (xterm_mouse::is_available()) {
auto& mouse_i = injector::get<xterm_mouse&>();
mouse_i.set_enabled(!mouse_i.is_enabled());
2022-07-22 06:30:49 +00:00
auto um = lnav::console::user_message::ok(
attr_line_t("mouse mode -- ")
.append(mouse_i.is_enabled() ? "enabled"_symbol
: "disabled"_symbol));
lnav_data.ld_rl_view->set_attr_value(um.to_attr_line());
2022-03-16 22:38:08 +00:00
} else {
lnav_data.ld_rl_view->set_value(
2022-03-16 22:38:08 +00:00
"error: mouse support is not available, make sure your "
"TERM is set to "
"xterm or xterm-256color");
}
break;
case 'C':
if (lss) {
lss->text_clear_marks(&textview_curses::BM_USER);
}
lnav_data.ld_select_start.erase(tc);
lnav_data.ld_last_user_mark.erase(tc);
tc->get_bookmarks()[&textview_curses::BM_USER].clear();
tc->reload_data();
2022-07-22 06:30:49 +00:00
lnav_data.ld_rl_view->set_attr_value(
lnav::console::user_message::ok("Cleared bookmarks")
.to_attr_line());
break;
2022-03-16 22:38:08 +00:00
case '>': {
auto range_opt = tc->horiz_shift(
tc->get_top(), tc->get_bottom(), tc->get_left());
if (range_opt && range_opt.value().second != INT_MAX) {
tc->set_left(range_opt.value().second);
lnav_data.ld_rl_view->set_alt_value(
2022-03-16 22:38:08 +00:00
HELP_MSG_1(m, "to bookmark a line"));
} else {
alerter::singleton().chime("no more search hits to the right");
}
2022-03-16 22:38:08 +00:00
} break;
case '<':
if (tc->get_left() == 0) {
alerter::singleton().chime("no more search hits to the left");
2022-03-16 22:38:08 +00:00
} else {
auto range_opt = tc->horiz_shift(
tc->get_top(), tc->get_bottom(), tc->get_left());
if (range_opt && range_opt.value().first != -1) {
tc->set_left(range_opt.value().first);
2022-03-16 22:38:08 +00:00
} else {
tc->set_left(0);
}
lnav_data.ld_rl_view->set_alt_value(
2022-03-16 22:38:08 +00:00
HELP_MSG_1(m, "to bookmark a line"));
}
break;
case 'f':
if (tc == &lnav_data.ld_views[LNV_LOG]) {
bm[&logfile_sub_source::BM_FILES].next(tc->get_top()) |
[&tc](auto vl) { tc->set_top(vl); };
2022-03-16 22:38:08 +00:00
} else if (tc == &lnav_data.ld_views[LNV_TEXT]) {
textfile_sub_source& tss = lnav_data.ld_text_source;
if (!tss.empty()) {
tss.rotate_left();
}
tc->reload_data();
}
break;
case 'F':
if (tc == &lnav_data.ld_views[LNV_LOG]) {
bm[&logfile_sub_source::BM_FILES].prev(tc->get_top()) |
2022-11-01 19:45:54 +00:00
[&tc](auto vl) {
// setting the selection for movement to previous file
// marker instead of the top will move the cursor, too,
// if needed.
tc->set_selection(vl);
};
2022-03-16 22:38:08 +00:00
} else if (tc == &lnav_data.ld_views[LNV_TEXT]) {
textfile_sub_source& tss = lnav_data.ld_text_source;
if (!tss.empty()) {
tss.rotate_right();
}
tc->reload_data();
}
break;
case 'z':
if ((lnav_data.ld_zoom_level - 1) < 0) {
alerter::singleton().chime("maximum zoom-in level reached");
2022-03-16 22:38:08 +00:00
} else {
execute_command(
ec,
"zoom-to "
2022-06-23 05:53:52 +00:00
+ lnav_zoom_strings[lnav_data.ld_zoom_level - 1]);
}
break;
case 'Z':
if ((lnav_data.ld_zoom_level + 1) >= ZOOM_COUNT) {
alerter::singleton().chime("maximum zoom-out level reached");
2022-03-16 22:38:08 +00:00
} else {
execute_command(
ec,
"zoom-to "
2022-06-23 05:53:52 +00:00
+ lnav_zoom_strings[lnav_data.ld_zoom_level + 1]);
}
break;
case 'J':
2022-03-16 22:38:08 +00:00
if (lnav_data.ld_last_user_mark.find(tc)
== lnav_data.ld_last_user_mark.end()
|| !tc->is_line_visible(
vis_line_t(lnav_data.ld_last_user_mark[tc])))
{
lnav_data.ld_select_start[tc] = tc->get_selection();
lnav_data.ld_last_user_mark[tc] = tc->get_selection();
2022-03-16 22:38:08 +00:00
} else {
vis_line_t height;
unsigned long width;
tc->get_dimensions(height, width);
2022-03-16 22:38:08 +00:00
if (lnav_data.ld_last_user_mark[tc] > (tc->get_bottom() - 2)
&& tc->get_selection() + height < tc->get_inner_height())
2022-03-16 22:38:08 +00:00
{
tc->shift_top(1_vl);
}
2022-03-16 22:38:08 +00:00
if (lnav_data.ld_last_user_mark[tc] + 1
>= tc->get_inner_height())
{
break;
}
lnav_data.ld_last_user_mark[tc] += 1;
}
tc->toggle_user_mark(&textview_curses::BM_USER,
vis_line_t(lnav_data.ld_last_user_mark[tc]));
if (tc->is_selectable()
&& tc->get_selection() + 1_vl < tc->get_inner_height())
{
tc->set_selection(tc->get_selection() + 1_vl);
}
tc->reload_data();
2022-03-16 22:38:08 +00:00
lnav_data.ld_rl_view->set_alt_value(
HELP_MSG_1(c, "to copy marked lines to the clipboard"));
break;
2022-03-16 22:38:08 +00:00
case 'K': {
int new_mark;
2022-03-16 22:38:08 +00:00
if (lnav_data.ld_last_user_mark.find(tc)
== lnav_data.ld_last_user_mark.end()
|| !tc->is_line_visible(
vis_line_t(lnav_data.ld_last_user_mark[tc])))
{
new_mark = tc->get_selection();
2022-03-16 22:38:08 +00:00
} else {
new_mark = lnav_data.ld_last_user_mark[tc];
}
tc->toggle_user_mark(&textview_curses::BM_USER,
vis_line_t(new_mark));
if (new_mark == tc->get_selection() && tc->get_top() > 0_vl) {
tc->shift_top(-1_vl);
}
if (new_mark > 0) {
lnav_data.ld_last_user_mark[tc] = new_mark - 1;
2022-03-16 22:38:08 +00:00
} else {
lnav_data.ld_last_user_mark[tc] = new_mark;
alerter::singleton().chime("no more lines to mark");
}
lnav_data.ld_select_start[tc] = tc->get_selection();
if (tc->is_selectable() && tc->get_selection() > 0_vl) {
tc->set_selection(tc->get_selection() - 1_vl);
}
tc->reload_data();
2022-03-16 22:38:08 +00:00
lnav_data.ld_rl_view->set_alt_value(
HELP_MSG_1(c, "to copy marked lines to the clipboard"));
break;
}
case 'M':
2022-03-16 22:38:08 +00:00
if (lnav_data.ld_last_user_mark.find(tc)
== lnav_data.ld_last_user_mark.end())
{
alerter::singleton().chime("no lines have been marked");
2022-03-16 22:38:08 +00:00
} else {
int start_line = std::min((int) tc->get_selection(),
2022-03-31 15:59:19 +00:00
lnav_data.ld_last_user_mark[tc] + 1);
int end_line = std::max((int) tc->get_selection(),
2022-03-31 15:59:19 +00:00
lnav_data.ld_last_user_mark[tc] - 1);
tc->toggle_user_mark(&textview_curses::BM_USER,
vis_line_t(start_line),
vis_line_t(end_line));
tc->reload_data();
}
break;
#if 0
case 'S':
{
bookmark_vector<vis_line_t>::iterator iter;
for (iter = bm[&textview_curses::BM_SEARCH].begin();
iter != bm[&textview_curses::BM_SEARCH].end();
++iter) {
tc->toggle_user_mark(&textview_curses::BM_USER, *iter);
}
lnav_data.ld_last_user_mark[tc] = -1;
tc->reload_data();
}
break;
#endif
case 's':
if (lss) {
auto next_top = tc->get_selection() + 2_vl;
if (!lss->is_time_offset_enabled()) {
lnav_data.ld_rl_view->set_alt_value(
2022-03-16 22:38:08 +00:00
HELP_MSG_1(T, "to disable elapsed-time mode"));
}
lss->set_time_offset(true);
while (next_top < tc->get_inner_height()) {
if (!lss->find_line(lss->at(next_top))->is_message()) {
2022-03-16 22:38:08 +00:00
} else if (lss->get_line_accel_direction(next_top)
== log_accel::A_DECEL)
{
--next_top;
tc->set_selection(next_top);
break;
}
++next_top;
}
}
break;
case 'S':
if (lss) {
auto next_top = tc->get_selection();
if (!lss->is_time_offset_enabled()) {
lnav_data.ld_rl_view->set_alt_value(
2022-03-16 22:38:08 +00:00
HELP_MSG_1(T, "to disable elapsed-time mode"));
}
lss->set_time_offset(true);
while (0 <= next_top && next_top < tc->get_inner_height()) {
if (!lss->find_line(lss->at(next_top))->is_message()) {
2022-03-16 22:38:08 +00:00
} else if (lss->get_line_accel_direction(next_top)
== log_accel::A_DECEL)
{
--next_top;
tc->set_selection(next_top);
break;
}
--next_top;
}
}
break;
case '9':
if (lss) {
2022-03-16 22:38:08 +00:00
double tenth = ((double) tc->get_inner_height()) / 10.0;
tc->shift_top(vis_line_t(tenth));
}
break;
case '(':
if (lss) {
2022-03-16 22:38:08 +00:00
double tenth = ((double) tc->get_inner_height()) / 10.0;
tc->shift_top(vis_line_t(-tenth));
}
break;
case '0':
if (lss) {
const int step = 24 * 60 * 60;
lss->time_for_row(tc->get_top()) | [lss, tc](auto first_time) {
2022-03-16 22:38:08 +00:00
lss->find_from_time(roundup_size(first_time.tv_sec, step)) |
[tc](auto line) { tc->set_top(line); };
};
}
break;
case ')':
if (lss) {
lss->time_for_row(tc->get_top()) | [lss, tc](auto first_time) {
time_t day = rounddown(first_time.tv_sec, 24 * 60 * 60);
lss->find_from_time(day) | [tc](auto line) {
if (line != 0_vl) {
--line;
}
tc->set_top(line);
};
};
}
break;
case 'D':
if (tc->get_top() == 0) {
alerter::singleton().chime(
"the top of the log has been reached");
2022-03-16 22:38:08 +00:00
} else if (lss) {
lss->time_for_row(tc->get_top()) |
[lss, ch, tc](auto first_time) {
int step = ch == 'D' ? (24 * 60 * 60) : (60 * 60);
time_t top_time = first_time.tv_sec;
lss->find_from_time(top_time - step) | [tc](auto line) {
if (line != 0_vl) {
--line;
}
tc->set_top(line);
};
};
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_1(/, "to search"));
}
break;
case 'd':
if (lss) {
2022-03-16 22:38:08 +00:00
lss->time_for_row(tc->get_top()) |
[ch, lss, tc](auto first_time) {
int step = ch == 'd' ? (24 * 60 * 60) : (60 * 60);
lss->find_from_time(first_time.tv_sec + step) |
[tc](auto line) { tc->set_top(line); };
};
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_1(/, "to search"));
}
break;
2015-11-25 04:59:24 +00:00
case 'o':
case 'O':
if (lss != nullptr) {
2015-11-25 04:59:24 +00:00
logline_helper start_helper(*lss);
start_helper.lh_current_line = tc->get_selection();
auto& start_line = start_helper.move_to_msg_start();
2015-11-25 04:59:24 +00:00
start_helper.annotate();
struct line_range opid_range = find_string_attr_range(
2022-03-16 22:38:08 +00:00
start_helper.lh_string_attrs, &logline::L_OPID);
2015-11-25 04:59:24 +00:00
if (!opid_range.is_valid()) {
alerter::singleton().chime(
"Log message does not contain an opid");
2022-07-22 06:30:49 +00:00
lnav_data.ld_rl_view->set_attr_value(
lnav::console::user_message::error(
"Log message does not contain an opid")
.to_attr_line());
2015-11-25 04:59:24 +00:00
} else {
unsigned int opid_hash = start_line.get_opid();
logline_helper next_helper(*lss);
bool found = false;
next_helper.lh_current_line = start_helper.lh_current_line;
while (true) {
if (ch == 'o') {
2022-03-16 22:38:08 +00:00
if (++next_helper.lh_current_line
>= tc->get_inner_height())
{
2015-11-25 04:59:24 +00:00
break;
}
2022-03-16 22:38:08 +00:00
} else {
2015-11-25 04:59:24 +00:00
if (--next_helper.lh_current_line <= 0) {
break;
}
}
2022-03-16 22:38:08 +00:00
logline& next_line = next_helper.current_line();
if (!next_line.is_message()) {
2015-11-25 04:59:24 +00:00
continue;
}
if (next_line.get_opid() != opid_hash) {
continue;
}
next_helper.annotate();
2022-03-16 22:38:08 +00:00
struct line_range opid_next_range
= find_string_attr_range(
2015-11-25 04:59:24 +00:00
next_helper.lh_string_attrs, &logline::L_OPID);
2022-03-16 22:38:08 +00:00
const char* start_opid
= start_helper.lh_line_values.lvv_sbr.get_data_at(
2022-03-16 22:38:08 +00:00
opid_range.lr_start);
const char* next_opid
= next_helper.lh_line_values.lvv_sbr.get_data_at(
2022-03-16 22:38:08 +00:00
opid_next_range.lr_start);
if (opid_range.length() != opid_next_range.length()
|| memcmp(
start_opid, next_opid, opid_range.length())
!= 0)
{
2015-11-25 04:59:24 +00:00
continue;
}
found = true;
break;
}
if (found) {
lnav_data.ld_rl_view->set_value("");
tc->set_selection(next_helper.lh_current_line);
2022-03-16 22:38:08 +00:00
} else {
2022-03-31 15:59:19 +00:00
const auto opid_str
= start_helper.to_string(opid_range);
2015-11-25 04:59:24 +00:00
2022-07-22 06:30:49 +00:00
lnav_data.ld_rl_view->set_attr_value(
lnav::console::user_message::error(
attr_line_t(
"No more messages found with opid: ")
.append(lnav::roles::symbol(opid_str)))
.to_attr_line());
alerter::singleton().chime(
"no more messages found with opid");
2015-11-25 04:59:24 +00:00
}
}
}
break;
case 'p':
if (tc == &lnav_data.ld_views[LNV_LOG]) {
2022-06-04 02:17:00 +00:00
auto* fos = dynamic_cast<field_overlay_source*>(
tc->get_overlay_source());
2022-03-16 22:38:08 +00:00
fos->fos_contexts.top().c_show
= !fos->fos_contexts.top().c_show;
2022-06-04 02:17:00 +00:00
tc->set_needs_update();
2022-03-16 22:38:08 +00:00
} else if (tc == &lnav_data.ld_views[LNV_DB]) {
2022-06-04 02:17:00 +00:00
auto* dos = dynamic_cast<db_overlay_source*>(
tc->get_overlay_source());
dos->dos_active = !dos->dos_active;
2022-06-04 02:17:00 +00:00
tc->set_needs_update();
}
break;
case 't':
if (lnav_data.ld_text_source.current_file() == nullptr) {
alerter::singleton().chime("No text files loaded");
2022-07-22 06:30:49 +00:00
lnav_data.ld_rl_view->set_attr_value(
lnav::console::user_message::error("No text files loaded")
.to_attr_line());
2022-03-16 22:38:08 +00:00
} else if (toggle_view(&lnav_data.ld_views[LNV_TEXT])) {
lnav_data.ld_rl_view->set_alt_value(
HELP_MSG_2(f, F, "to switch to the next/previous file"));
}
break;
case 'T':
lnav_data.ld_log_source.toggle_time_offset();
if (lss && lss->is_time_offset_enabled()) {
2022-03-16 22:38:08 +00:00
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_2(
s, S, "to move forward/backward through slow downs"));
}
tc->reload_data();
break;
2022-03-16 22:38:08 +00:00
case 'I': {
auto& hist_tc = lnav_data.ld_views[LNV_HISTOGRAM];
2019-05-22 05:14:36 +00:00
if (toggle_view(&hist_tc)) {
2022-03-16 22:38:08 +00:00
auto* src_view
= dynamic_cast<text_time_translator*>(tc->get_sub_source());
2019-05-22 05:14:36 +00:00
if (src_view != nullptr) {
src_view->time_for_row(tc->get_top()) | [](auto log_top) {
2022-03-16 22:38:08 +00:00
lnav_data.ld_hist_source2.row_for_time(log_top) |
[](auto row) {
lnav_data.ld_views[LNV_HISTOGRAM].set_top(row);
};
};
2019-05-22 05:14:36 +00:00
}
2022-03-16 22:38:08 +00:00
} else {
2019-05-22 05:37:36 +00:00
lnav_data.ld_view_stack.top() | [&](auto top_tc) {
2022-03-16 22:38:08 +00:00
auto* dst_view = dynamic_cast<text_time_translator*>(
top_tc->get_sub_source());
2019-05-22 05:14:36 +00:00
if (dst_view != nullptr) {
auto& hs = lnav_data.ld_hist_source2;
2022-03-16 22:38:08 +00:00
auto hist_top_time_opt
= hs.time_for_row(hist_tc.get_top());
auto curr_top_time_opt
= dst_view->time_for_row(top_tc->get_top());
if (hist_top_time_opt && curr_top_time_opt
&& hs.row_for_time(hist_top_time_opt.value())
!= hs.row_for_time(curr_top_time_opt.value()))
{
dst_view->row_for_time(hist_top_time_opt.value()) |
[top_tc](auto new_top) {
top_tc->set_top(new_top);
top_tc->set_needs_update();
};
2019-05-22 05:14:36 +00:00
}
}
};
}
2022-03-16 22:38:08 +00:00
} break;
2022-03-16 22:38:08 +00:00
case 'V': {
textview_curses* db_tc = &lnav_data.ld_views[LNV_DB];
db_label_source& dls = lnav_data.ld_db_row_source;
if (toggle_view(db_tc)) {
auto log_line_index = dls.column_name_to_index("log_line");
if (!log_line_index) {
log_line_index = dls.column_name_to_index("min(log_line)");
}
if (log_line_index) {
2022-03-25 23:38:11 +00:00
fmt::memory_buffer linestr;
2022-03-16 22:38:08 +00:00
int line_number = (int) tc->get_top();
unsigned int row;
2022-03-25 23:38:11 +00:00
fmt::format_to(std::back_inserter(linestr),
FMT_STRING("{}"),
line_number);
linestr.push_back('\0');
for (row = 0; row < dls.dls_rows.size(); row++) {
if (strcmp(dls.dls_rows[row][log_line_index.value()],
2022-03-25 23:38:11 +00:00
linestr.data())
== 0)
{
vis_line_t db_line(row);
db_tc->set_top(db_line);
db_tc->set_needs_update();
break;
}
}
}
2022-03-16 22:38:08 +00:00
} else if (db_tc->get_inner_height() > 0) {
int db_row = db_tc->get_top();
tc = &lnav_data.ld_views[LNV_LOG];
auto log_line_index = dls.column_name_to_index("log_line");
if (!log_line_index) {
log_line_index = dls.column_name_to_index("min(log_line)");
}
if (log_line_index) {
unsigned int line_number;
if (sscanf(dls.dls_rows[db_row][log_line_index.value()],
"%d",
2022-03-16 22:38:08 +00:00
&line_number)
&& line_number < tc->listview_rows(*tc))
{
tc->set_top(vis_line_t(line_number));
tc->set_needs_update();
}
2022-03-16 22:38:08 +00:00
} else {
for (size_t lpc = 0; lpc < dls.dls_headers.size(); lpc++) {
date_time_scanner dts;
struct timeval tv;
struct exttm tm;
2022-03-16 22:38:08 +00:00
const char* col_value = dls.dls_rows[db_row][lpc];
size_t col_len = strlen(col_value);
2022-03-16 22:38:08 +00:00
if (dts.scan(col_value, col_len, nullptr, &tm, tv)
!= nullptr)
{
2022-03-16 22:38:08 +00:00
lnav_data.ld_log_source.find_from_time(tv) |
[tc](auto vl) {
tc->set_top(vl);
tc->set_needs_update();
};
break;
}
}
}
}
2022-03-16 22:38:08 +00:00
} break;
case '\t':
case KEY_BTAB:
2022-03-16 22:38:08 +00:00
if (tc == &lnav_data.ld_views[LNV_DB]) {
auto& chart = lnav_data.ld_db_row_source.dls_chart;
const auto& state = chart.show_next_ident(
ch == '\t' ? stacked_bar_chart_base::direction::forward
: stacked_bar_chart_base::direction::backward);
state.match(
2022-03-16 22:38:08 +00:00
[&](stacked_bar_chart_base::show_none) {
lnav_data.ld_rl_view->set_value("Graphing no values");
},
2022-03-16 22:38:08 +00:00
[&](stacked_bar_chart_base::show_all) {
lnav_data.ld_rl_view->set_value("Graphing all values");
},
2022-03-16 22:38:08 +00:00
[&](stacked_bar_chart_base::show_one) {
2022-03-31 15:59:19 +00:00
std::string colname;
chart.get_ident_to_show(colname);
lnav_data.ld_rl_view->set_value(
2022-03-16 22:38:08 +00:00
"Graphing column " ANSI_BOLD_START + colname
+ ANSI_NORM);
});
tc->reload_data();
} else if (tc == &lnav_data.ld_views[LNV_SPECTRO]) {
lnav_data.ld_mode = ln_mode_t::SPECTRO_DETAILS;
2018-11-11 06:55:08 +00:00
} else if (tc_tss != nullptr && tc_tss->tss_supports_filtering) {
2020-10-29 04:23:16 +00:00
lnav_data.ld_mode = lnav_data.ld_last_config_mode;
lnav_data.ld_filter_view.reload_data();
2020-10-29 04:23:16 +00:00
lnav_data.ld_files_view.reload_data();
if (tc->get_inner_height() > 0_vl) {
std::vector<attr_line_t> rows(1);
2022-03-16 22:38:08 +00:00
tc->get_data_source()->listview_value_for_rows(
*tc, tc->get_top(), rows);
string_attrs_t& sa = rows[0].get_attrs();
auto line_attr_opt = get_string_attr(sa, logline::L_FILE);
if (line_attr_opt) {
2022-03-16 22:38:08 +00:00
const auto& fc = lnav_data.ld_active_files;
auto lf = line_attr_opt.value().get();
auto index_opt
= fc.fc_files | lnav::itertools::find(lf);
if (index_opt) {
auto index_vl = vis_line_t(index_opt.value());
2020-10-29 04:23:16 +00:00
lnav_data.ld_files_view.set_top(index_vl);
lnav_data.ld_files_view.set_selection(index_vl);
}
}
}
2018-11-11 06:55:08 +00:00
} else {
alerter::singleton().chime(
"no configuration panels in this view");
}
break;
case 'x':
if (tc->toggle_hide_fields()) {
lnav_data.ld_rl_view->set_value("Showing hidden fields");
} else {
lnav_data.ld_rl_view->set_value("Hiding hidden fields");
}
tc->set_needs_update();
break;
case 'r':
case 'R':
if (lss != nullptr) {
const auto& last_time = injector::get<const relative_time&,
last_relative_time_tag>();
if (last_time.empty()) {
2015-09-14 15:56:42 +00:00
lnav_data.ld_rl_view->set_value(
2022-03-16 22:38:08 +00:00
"Use the 'goto' command to set the relative time to "
"move by");
} else {
2018-08-12 15:37:16 +00:00
vis_line_t vl = tc->get_top(), new_vl;
relative_time rt = last_time;
2015-09-14 15:56:42 +00:00
content_line_t cl;
struct exttm tm;
2018-08-12 15:37:16 +00:00
bool done = false;
2015-09-14 15:56:42 +00:00
2018-08-12 15:37:16 +00:00
if (ch == 'r') {
if (rt.is_negative()) {
rt.negate();
}
} else if (ch == 'R') {
if (!rt.is_negative()) {
rt.negate();
}
2015-09-14 15:56:42 +00:00
}
cl = lnav_data.ld_log_source.at(vl);
2022-03-16 22:38:08 +00:00
logline* ll = lnav_data.ld_log_source.find_line(cl);
2015-09-14 15:56:42 +00:00
ll->to_exttm(tm);
2018-08-12 15:37:16 +00:00
do {
tm = rt.adjust(tm);
2022-03-16 22:38:08 +00:00
auto new_vl_opt
= lnav_data.ld_log_source.find_from_time(tm);
if (!new_vl_opt) {
break;
}
new_vl = new_vl_opt.value();
2018-08-12 15:37:16 +00:00
2022-03-16 22:38:08 +00:00
if (new_vl == 0_vl || new_vl != vl || !rt.is_relative())
{
2018-08-12 15:37:16 +00:00
vl = new_vl;
done = true;
2015-09-14 15:56:42 +00:00
}
2018-08-12 15:37:16 +00:00
} while (!done);
2015-09-14 15:56:42 +00:00
tc->set_top(vl);
2018-08-12 15:37:16 +00:00
lnav_data.ld_rl_view->set_value(" " + rt.to_string());
2015-09-14 15:56:42 +00:00
}
}
break;
case KEY_CTRL_W:
2022-03-16 22:38:08 +00:00
execute_command(ec,
lnav_data.ld_views[LNV_LOG].get_word_wrap()
? "disable-word-wrap"
: "enable-word-wrap");
break;
case KEY_CTRL_P:
lnav_data.ld_preview_hidden = !lnav_data.ld_preview_hidden;
break;
default:
log_debug("key sequence %x", ch);
return false;
}
return true;
}