Skip to content

Commit

Permalink
[prompt] filtering commands
Browse files Browse the repository at this point in the history
  • Loading branch information
tstack committed Feb 18, 2025
1 parent 152e87a commit 0c000eb
Show file tree
Hide file tree
Showing 22 changed files with 721 additions and 623 deletions.
265 changes: 261 additions & 4 deletions src/cmds.filtering.cc
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ com_hide_line(exec_context& ec,
auto parse_res = relative_time::from_str(all_args);

if (parse_res.isOk()) {
log_debug("parsed!")
if (tc->get_inner_height() > 0) {
exttm tm;

Expand All @@ -91,14 +90,13 @@ com_hide_line(exec_context& ec,
if (log_vl_ri) {
tm = exttm::from_tv(log_vl_ri.value().ri_time);
tv_opt = parse_res.unwrap().adjust(tm).to_timeval();

log_debug("got line time");
}
}
} else if (dts.convert_to_timeval(all_args, tv_abs)) {
tv_opt = tv_abs;
} else {
log_debug("not parsed: '%s'", all_args.c_str());
return ec.make_error(FMT_STRING("invalid time value: {}"),
all_args);
}

if (tv_opt && !ec.ec_dry_run) {
Expand Down Expand Up @@ -149,6 +147,202 @@ com_show_lines(exec_context& ec,
return Ok(retval);
}

static Result<std::string, lnav::console::user_message> com_enable_filter(
exec_context& ec, std::string cmdline, std::vector<std::string>& args);

static Result<std::string, lnav::console::user_message>
com_filter(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
{
std::string retval;

auto tc = *lnav_data.ld_view_stack.top();
auto tss = tc->get_sub_source();

if (!tss->tss_supports_filtering) {
return ec.make_error("{} view does not support filtering",
lnav_view_strings[tc - lnav_data.ld_views]);
}
if (args.size() > 1) {
const static intern_string_t PATTERN_SRC
= intern_string::lookup("pattern");

auto* tss = tc->get_sub_source();
auto& fs = tss->get_filters();
auto re_frag = remaining_args_frag(cmdline, args);
args[1] = re_frag.to_string();
if (fs.get_filter(args[1]) != nullptr) {
return com_enable_filter(ec, cmdline, args);
}

if (fs.full()) {
return ec.make_error(
"filter limit reached, try combining "
"filters with a pipe symbol (e.g. foo|bar)");
}

auto compile_res = lnav::pcre2pp::code::from(args[1], PCRE2_CASELESS);

if (compile_res.isErr()) {
auto ce = compile_res.unwrapErr();
auto um = lnav::console::to_user_message(PATTERN_SRC, ce);
return Err(um);
}
if (ec.ec_dry_run) {
if (args[0] == "filter-in" && !fs.empty()) {
lnav_data.ld_preview_status_source[0]
.get_description()
.set_value(
"Match preview for :filter-in only works if there are "
"no "
"other filters");
retval = "";
} else {
auto& hm = tc->get_highlights();
highlighter hl(compile_res.unwrap().to_shared());
auto role = (args[0] == "filter-out") ? role_t::VCR_DIFF_DELETE
: role_t::VCR_DIFF_ADD;

hl.with_role(role);
hl.with_attrs(text_attrs::with_styles(
text_attrs::style::blink, text_attrs::style::reverse));

hm[{highlight_source_t::PREVIEW, "preview"}] = hl;
tc->reload_data();

lnav_data.ld_preview_status_source[0]
.get_description()
.set_value(
"Matches are highlighted in %s in the text view",
role == role_t::VCR_DIFF_DELETE ? "red" : "green");

retval = "";
}
} else {
auto lt = (args[0] == "filter-out") ? text_filter::EXCLUDE
: text_filter::INCLUDE;
auto filter_index = fs.next_index();
if (!filter_index) {
return ec.make_error("too many filters");
}
auto pf = std::make_shared<pcre_filter>(
lt, args[1], *filter_index, compile_res.unwrap().to_shared());

log_debug("%s [%d] %s",
args[0].c_str(),
pf->get_index(),
args[1].c_str());
fs.add_filter(pf);
const auto start_time = std::chrono::steady_clock::now();
tss->text_filters_changed();
const auto end_time = std::chrono::steady_clock::now();
const double duration
= std::chrono::duration_cast<std::chrono::milliseconds>(
end_time - start_time)
.count()
/ 1000.0;

retval = fmt::format(FMT_STRING("info: filter activated in {:.3}s"),
duration);
}
} else {
return ec.make_error("expecting a regular expression to filter");
}

return Ok(retval);
}

static readline_context::prompt_result_t
com_filter_prompt(exec_context& ec, const std::string& cmdline)
{
const auto* tc = lnav_data.ld_view_stack.top().value();
std::vector<std::string> args;

split_ws(cmdline, args);
if (args.size() > 1) {
return {};
}

if (tc->tc_selected_text) {
return {"", tc->tc_selected_text->sti_value};
}

return {"", tc->get_current_search()};
}

static Result<std::string, lnav::console::user_message>
com_enable_filter(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
{
std::string retval;

if (args.empty()) {
args.emplace_back("disabled-filter");
} else if (args.size() > 1) {
auto* tc = *lnav_data.ld_view_stack.top();
auto* tss = tc->get_sub_source();
auto& fs = tss->get_filters();
std::shared_ptr<text_filter> lf;

args[1] = remaining_args(cmdline, args);
lf = fs.get_filter(args[1]);
if (lf == nullptr) {
return ec.make_error("no such filter -- {}", args[1]);
}
if (lf->is_enabled()) {
retval = "info: filter already enabled";
} else if (ec.ec_dry_run) {
retval = "";
} else {
fs.set_filter_enabled(lf, true);
tss->text_filters_changed();
retval = "info: filter enabled";
}
} else {
return ec.make_error("expecting disabled filter to enable");
}

return Ok(retval);
}

static Result<std::string, lnav::console::user_message>
com_disable_filter(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
{
std::string retval;

if (args.empty()) {
args.emplace_back("enabled-filter");
} else if (args.size() > 1) {
auto* tc = *lnav_data.ld_view_stack.top();
auto* tss = tc->get_sub_source();
auto& fs = tss->get_filters();
std::shared_ptr<text_filter> lf;

args[1] = remaining_args(cmdline, args);
lf = fs.get_filter(args[1]);
if (lf == nullptr) {
return ec.make_error("no such filter -- {}", args[1]);
}
if (!lf->is_enabled()) {
retval = "info: filter already disabled";
} else if (ec.ec_dry_run) {
retval = "";
} else {
fs.set_filter_enabled(lf, false);
tss->text_filters_changed();
retval = "info: filter disabled";
}
} else {
return ec.make_error("expecting enabled filter to disable");
}

return Ok(retval);
}

static readline_context::command_t FILTERING_COMMANDS[] = {
{
"hide-lines-before",
Expand Down Expand Up @@ -194,6 +388,69 @@ static readline_context::command_t FILTERING_COMMANDS[] = {
.with_opposites({"hide-lines-before", "hide-lines-after"})
.with_tags({"filtering"}),
},
{
"filter-in",
com_filter,

help_text(":filter-in")
.with_summary("Only show lines that match the given regular "
"expression in the current view")
.with_parameter(
help_text("pattern", "The regular expression to match")
.with_format(help_parameter_format_t::HPF_REGEX))
.with_tags({"filtering"})
.with_example({"To filter out log messages that do not have the "
"string 'dhclient'",
"dhclient"}),
com_filter_prompt,
},
{
"filter-out",
com_filter,

help_text(":filter-out")
.with_summary("Remove lines that match the given "
"regular expression "
"in the current view")
.with_parameter(
help_text("pattern", "The regular expression to match")
.with_format(help_parameter_format_t::HPF_REGEX))
.with_tags({"filtering"})
.with_example({"To filter out log messages that "
"contain the string "
"'last message repeated'",
"last message repeated"}),
com_filter_prompt,
},
{
"enable-filter",
com_enable_filter,

help_text(":enable-filter")
.with_summary("Enable a previously created and disabled filter")
.with_parameter(help_text(
"pattern", "The regular expression used in the filter command"))
.with_tags({"filtering"})
.with_opposites({"disable-filter"})
.with_example({"To enable the disabled filter with the "
"pattern 'last "
"message repeated'",
"last message repeated"}),
},
{
"disable-filter",
com_disable_filter,

help_text(":disable-filter")
.with_summary("Disable a filter created with filter-in/filter-out")
.with_parameter(help_text(
"pattern", "The regular expression used in the filter command"))
.with_tags({"filtering"})
.with_opposites({"filter-out", "filter-in"})
.with_example({"To disable the filter with the pattern 'last "
"message repeated'",
"last message repeated"}),
},
};

void
Expand Down
12 changes: 4 additions & 8 deletions src/column_namer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,14 @@
#include "config.h"
#include "sql_util.hh"

const string_fragment column_namer::BUILTIN_COL
= string_fragment::from_const("col");
const string_fragment column_namer::BUILTIN_COL = "col"_frag;

column_namer::column_namer(language lang) : cn_language(lang)
{
this->cn_builtin_names.emplace_back(BUILTIN_COL);
this->cn_builtin_names.emplace_back(
string_fragment::from_const("log_time"));
this->cn_builtin_names.emplace_back(
string_fragment::from_const("log_level"));
this->cn_builtin_names.emplace_back(
string_fragment::from_const("log_opid"));
this->cn_builtin_names.emplace_back("log_time"_frag);
this->cn_builtin_names.emplace_back("log_level"_frag);
this->cn_builtin_names.emplace_back("log_opid"_frag);
}

bool
Expand Down
12 changes: 6 additions & 6 deletions src/filter_sub_source.cc
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,13 @@ using namespace lnav::roles::literals;

filter_sub_source::filter_sub_source(std::shared_ptr<textinput_curses> editor)
: fss_editor(editor),
fss_regexp_history(lnav::textinput::history::for_context(
string_fragment::from_const("regexp-filter"))),
fss_sql_history(lnav::textinput::history::for_context(
string_fragment::from_const("sql-filter")))
fss_regexp_history(
lnav::textinput::history::for_context("regexp-filter"_frag)),
fss_sql_history(lnav::textinput::history::for_context("sql-filter"_frag))
{
this->fss_editor->set_visible(false);
this->fss_editor->set_x(25);
this->fss_editor->tc_popup.set_title("Pattern");
this->fss_editor->tc_height = 1;
this->fss_editor->tc_on_change
= bind_mem(&filter_sub_source::rl_change, this);
Expand All @@ -66,6 +66,7 @@ filter_sub_source::filter_sub_source(std::shared_ptr<textinput_curses> editor)
= bind_mem(&filter_sub_source::rl_completion, this);
this->fss_editor->tc_on_perform
= bind_mem(&filter_sub_source::rl_perform, this);
this->fss_editor->tc_on_blur = bind_mem(&filter_sub_source::rl_blur, this);
this->fss_editor->tc_on_abort
= bind_mem(&filter_sub_source::rl_abort, this);
}
Expand Down Expand Up @@ -703,8 +704,7 @@ void
filter_sub_source::rl_blur(textinput_curses& tc)
{
auto* top_view = *lnav_data.ld_view_stack.top();
top_view->get_highlights().erase(
{highlight_source_t::PREVIEW, "preview"});
top_view->get_highlights().erase({highlight_source_t::PREVIEW, "preview"});
lnav_data.ld_log_source.set_preview_sql_filter(nullptr);
lnav_data.ld_filter_help_status_source.fss_prompt.clear();
lnav_data.ld_filter_help_status_source.fss_error.clear();
Expand Down
4 changes: 2 additions & 2 deletions src/formats/logfmt/logfmt.parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,8 @@ logfmt::parser::step()
auto value_pair = value_start.split_while(bvp);

if (value_pair) {
static constexpr auto TRUE_FRAG = string_fragment::from_const("true");
static constexpr auto FALSE_FRAG = string_fragment::from_const("false");
static constexpr auto TRUE_FRAG = "true"_frag;
static constexpr auto FALSE_FRAG = "false"_frag;

this->p_next_input = value_pair->second;
if (bvp.is_integer()) {
Expand Down
13 changes: 13 additions & 0 deletions src/help_text.hh
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,19 @@ struct help_text {
return *this;
}

bool is_trailing_arg() const
{
switch (this->ht_format) {
case help_parameter_format_t::HPF_TEXT:
case help_parameter_format_t::HPF_TIME_FILTER_POINT:
case help_parameter_format_t::HPF_MULTILINE_TEXT:
case help_parameter_format_t::HPF_REGEX:
return true;
default:
return false;
}
}

help_text& with_enum_values(
const std::initializer_list<const char*>& enum_values) noexcept;

Expand Down
Loading

0 comments on commit 0c000eb

Please sign in to comment.