mirror of
https://github.com/tstack/lnav.git
synced 2024-09-11 13:05:51 +03:00
[filter] add panel that shows the set of filters
Initial version, still needs some cleanup
This commit is contained in:
parent
80456791ac
commit
2345a32423
6
NEWS
6
NEWS
@ -1,6 +1,12 @@
|
||||
|
||||
lnav v0.8.5:
|
||||
Features:
|
||||
* Added a visual filter editor to make it easier to update existing
|
||||
filters. The editor can be opened by pressing backquote (`). Once
|
||||
the editor is opened, you can create/delete, enable/disable, and edit
|
||||
the patterns with hotkeys.
|
||||
* Added an 'lnav_view_filters' SQL table that can be used to
|
||||
programmatically manipulate filters.
|
||||
* The ":write-*" commands will now accept "/dev/clipboard" as a file name
|
||||
that writes to the system clipboard.
|
||||
* Added a ":redirect-to <path>" command to redirect command output to the
|
||||
|
@ -69,6 +69,17 @@ this table:
|
||||
|
||||
:name: The name of the view.
|
||||
|
||||
lnav_view_filters
|
||||
-----------------
|
||||
|
||||
The **lnav_view_filters** table allows you to manipulate the filters in the
|
||||
**lnav** views. The following columns are available in this table:
|
||||
|
||||
:view_name: The name of the view.
|
||||
:enabled: Indicates whether this filter is enabled or disabled.
|
||||
:type: The type of filter, either 'in' or 'out'.
|
||||
:pattern: The regular expression to filter on.
|
||||
|
||||
all_logs
|
||||
--------
|
||||
|
||||
|
@ -11,6 +11,8 @@ set(diag_STAT_SRCS
|
||||
extension-functions.cc
|
||||
field_overlay_source.cc
|
||||
file_vtab.cc
|
||||
filter_status_source.cc
|
||||
filter_sub_source.cc
|
||||
fs-extension-functions.cc
|
||||
fstat_vtab.cc
|
||||
fts_fuzzy_match.cc
|
||||
@ -30,6 +32,7 @@ set(diag_STAT_SRCS
|
||||
lnav_log.cc
|
||||
lnav_util.cc
|
||||
log_accel.cc
|
||||
log_actions.cc
|
||||
log_format.cc
|
||||
log_format_loader.cc
|
||||
log_level.cc
|
||||
@ -62,11 +65,13 @@ set(diag_STAT_SRCS
|
||||
state-extension-functions.cc
|
||||
strnatcmp.c
|
||||
text_format.cc
|
||||
textfile_highlighters.cc
|
||||
textview_curses.cc
|
||||
time-extension-functions.cc
|
||||
timer.cc
|
||||
unique_path.hh
|
||||
view_curses.cc
|
||||
view_helpers.cc
|
||||
views_vtab.cc
|
||||
vt52_curses.cc
|
||||
vtab_module.cc
|
||||
@ -107,6 +112,8 @@ set(diag_STAT_SRCS
|
||||
field_overlay_source.hh
|
||||
file_vtab.hh
|
||||
filter_observer.hh
|
||||
filter_status_source.hh
|
||||
filter_sub_source.hh
|
||||
format-text-files.hh
|
||||
fstat_vtab.hh
|
||||
fts_fuzzy_match.hh
|
||||
@ -119,6 +126,7 @@ set(diag_STAT_SRCS
|
||||
intern_string.hh
|
||||
is_utf8.hh
|
||||
k_merge_tree.h
|
||||
log_actions.hh
|
||||
log_data_helper.hh
|
||||
log_data_table.hh
|
||||
log_format_impls.cc
|
||||
@ -146,6 +154,7 @@ set(diag_STAT_SRCS
|
||||
term_extra.hh
|
||||
termios_guard.hh
|
||||
text_format.hh
|
||||
textfile_highlighters.hh
|
||||
textfile_sub_source.hh
|
||||
time_T.hh
|
||||
timer.hh
|
||||
|
@ -159,6 +159,8 @@ noinst_HEADERS = \
|
||||
field_overlay_source.hh \
|
||||
file_vtab.hh \
|
||||
filter_observer.hh \
|
||||
filter_status_source.hh \
|
||||
filter_sub_source.hh \
|
||||
format-text-files.hh \
|
||||
fstat_vtab.hh \
|
||||
fts_fuzzy_match.hh \
|
||||
@ -185,6 +187,7 @@ noinst_HEADERS = \
|
||||
lnav_log.hh \
|
||||
lnav_util.hh \
|
||||
log_accel.hh \
|
||||
log_actions.hh \
|
||||
log_data_helper.hh \
|
||||
log_data_table.hh \
|
||||
log_format.hh \
|
||||
@ -230,6 +233,7 @@ noinst_HEADERS = \
|
||||
termios_guard.hh \
|
||||
term_extra.hh \
|
||||
text_format.hh \
|
||||
textfile_highlighters.hh \
|
||||
textfile_sub_source.hh \
|
||||
textview_curses.hh \
|
||||
time_T.hh \
|
||||
@ -280,6 +284,8 @@ libdiag_a_SOURCES = \
|
||||
extension-functions.cc \
|
||||
field_overlay_source.cc \
|
||||
file_vtab.cc \
|
||||
filter_status_source.cc \
|
||||
filter_sub_source.cc \
|
||||
fstat_vtab.cc \
|
||||
fs-extension-functions.cc \
|
||||
fts_fuzzy_match.cc \
|
||||
@ -299,6 +305,7 @@ libdiag_a_SOURCES = \
|
||||
lnav_log.cc \
|
||||
lnav_util.cc \
|
||||
log_accel.cc \
|
||||
log_actions.cc \
|
||||
log_format.cc \
|
||||
log_format_loader.cc \
|
||||
log_level.cc \
|
||||
@ -333,10 +340,12 @@ libdiag_a_SOURCES = \
|
||||
state-extension-functions.cc \
|
||||
strnatcmp.c \
|
||||
sysclip.cc \
|
||||
textfile_highlighters.cc \
|
||||
textview_curses.cc \
|
||||
time-extension-functions.cc \
|
||||
time_fmts.cc \
|
||||
view_curses.cc \
|
||||
view_helpers.cc \
|
||||
views_vtab.cc \
|
||||
vt52_curses.cc \
|
||||
vtab_module.cc \
|
||||
|
@ -459,8 +459,12 @@ public:
|
||||
if (padding > 0) {
|
||||
this->al_string.insert(0, padding, ' ');
|
||||
for (auto &al_attr : this->al_attrs) {
|
||||
al_attr.sa_range.lr_start += padding;
|
||||
al_attr.sa_range.lr_end += padding;
|
||||
if (al_attr.sa_range.lr_start > 0) {
|
||||
al_attr.sa_range.lr_start += padding;
|
||||
}
|
||||
if (al_attr.sa_range.lr_end != -1) {
|
||||
al_attr.sa_range.lr_end += padding;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,6 @@ public:
|
||||
BSF_LINE_NUMBER,
|
||||
BSF_PERCENT,
|
||||
BSF_HITS,
|
||||
BSF_FILTERED,
|
||||
BSF_LOADING,
|
||||
BSF_HELP,
|
||||
|
||||
@ -63,16 +62,12 @@ public:
|
||||
bss_prompt(1024, view_colors::VCR_STATUS),
|
||||
bss_error(1024, view_colors::VCR_ALERT_STATUS),
|
||||
bss_hit_spinner(0),
|
||||
bss_load_percent(0),
|
||||
bss_last_filtered_count(0),
|
||||
bss_filter_counter(0)
|
||||
bss_load_percent(0)
|
||||
{
|
||||
this->bss_fields[BSF_LINE_NUMBER].set_width(11);
|
||||
this->bss_fields[BSF_LINE_NUMBER].set_width(14);
|
||||
this->bss_fields[BSF_PERCENT].set_width(4);
|
||||
this->bss_fields[BSF_PERCENT].set_left_pad(1);
|
||||
this->bss_fields[BSF_HITS].set_width(36);
|
||||
this->bss_fields[BSF_FILTERED].set_width(20);
|
||||
this->bss_fields[BSF_FILTERED].set_role(view_colors::VCR_BOLD_STATUS);
|
||||
this->bss_fields[BSF_LOADING].set_width(13);
|
||||
this->bss_fields[BSF_LOADING].set_cylon(true);
|
||||
this->bss_fields[BSF_LOADING].right_justify(true);
|
||||
@ -105,7 +100,7 @@ public:
|
||||
this->bss_error.set_value(msg);
|
||||
};
|
||||
|
||||
size_t statusview_fields(void)
|
||||
size_t statusview_fields()
|
||||
{
|
||||
size_t retval;
|
||||
|
||||
@ -234,40 +229,11 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
void update_filtered(text_sub_source *tss)
|
||||
{
|
||||
status_field &sf = this->bss_fields[BSF_FILTERED];
|
||||
|
||||
if (tss == NULL || tss->get_filtered_count() == 0) {
|
||||
sf.clear();
|
||||
}
|
||||
else {
|
||||
ui_periodic_timer &timer = ui_periodic_timer::singleton();
|
||||
|
||||
if (tss->get_filtered_count() == this->bss_last_filtered_count) {
|
||||
|
||||
if (timer.fade_diff(this->bss_filter_counter) == 0) {
|
||||
this->bss_fields[BSF_FILTERED].set_role(
|
||||
view_colors::VCR_BOLD_STATUS);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this->bss_fields[BSF_FILTERED].set_role(
|
||||
view_colors::VCR_ALERT_STATUS);
|
||||
this->bss_last_filtered_count = tss->get_filtered_count();
|
||||
timer.start_fade(this->bss_filter_counter, 3);
|
||||
}
|
||||
sf.set_value("%'9d Not Shown", tss->get_filtered_count());
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
status_field bss_prompt;
|
||||
status_field bss_error;
|
||||
status_field bss_fields[BSF__MAX];
|
||||
int bss_hit_spinner;
|
||||
int bss_load_percent;
|
||||
int bss_last_filtered_count;
|
||||
sig_atomic_t bss_filter_counter;
|
||||
};
|
||||
#endif
|
||||
|
@ -271,7 +271,7 @@ string execute_sql(exec_context &ec, const string &sql, string &alt_msg)
|
||||
}
|
||||
}
|
||||
|
||||
if (lnav_data.ld_rl_view != NULL) {
|
||||
if (lnav_data.ld_rl_view != nullptr) {
|
||||
lnav_data.ld_rl_view->set_value("");
|
||||
}
|
||||
}
|
||||
@ -284,7 +284,7 @@ string execute_sql(exec_context &ec, const string &sql, string &alt_msg)
|
||||
if (!ec.ec_accumulator.empty()) {
|
||||
retval = ec.ec_accumulator.get_string();
|
||||
}
|
||||
else if (dls.dls_rows.size() > 0) {
|
||||
else if (!dls.dls_rows.empty()) {
|
||||
vis_bookmarks &bm = lnav_data.ld_views[LNV_LOG].get_bookmarks();
|
||||
|
||||
if (lnav_data.ld_flags & LNF_HEADLESS) {
|
||||
@ -355,7 +355,7 @@ string execute_sql(exec_context &ec, const string &sql, string &alt_msg)
|
||||
if (!(lnav_data.ld_flags & LNF_HEADLESS)) {
|
||||
lnav_data.ld_bottom_source.update_loading(0, 0);
|
||||
lnav_data.ld_status[LNS_BOTTOM].do_update();
|
||||
redo_search(LNV_DB);
|
||||
lnav_data.ld_views[LNV_DB].redo_search();
|
||||
}
|
||||
|
||||
return retval;
|
||||
@ -540,12 +540,9 @@ string execute_from_file(exec_context &ec, const string &path, int line_number,
|
||||
retval = execute_command(ec, cmdline);
|
||||
break;
|
||||
case '/':
|
||||
if (!lnav_data.ld_view_stack.empty()) {
|
||||
lnav_view_t index = lnav_view_t(lnav_data.ld_view_stack.back() - lnav_data.ld_views);
|
||||
|
||||
execute_search(index, cmdline.substr(1));
|
||||
retval = "";
|
||||
}
|
||||
lnav_data.ld_view_stack.top() | [cmdline] (auto tc) {
|
||||
tc->execute_search(cmdline.substr(1));
|
||||
};
|
||||
break;
|
||||
case ';':
|
||||
setup_logline_table(ec);
|
||||
@ -581,12 +578,9 @@ string execute_any(exec_context &ec, const string &cmdline_with_mode)
|
||||
retval = execute_command(ec, cmdline);
|
||||
break;
|
||||
case '/':
|
||||
if (!lnav_data.ld_view_stack.empty()) {
|
||||
lnav_view_t index = lnav_view_t(lnav_data.ld_view_stack.back() - lnav_data.ld_views);
|
||||
|
||||
execute_search(index, cmdline.substr(1));
|
||||
retval = "";
|
||||
}
|
||||
lnav_data.ld_view_stack.top() | [cmdline] (auto tc) {
|
||||
tc->execute_search(cmdline.substr(1));
|
||||
};
|
||||
break;
|
||||
case ';':
|
||||
setup_logline_table(ec);
|
||||
@ -628,11 +622,9 @@ void execute_init_commands(exec_context &ec, vector<pair<string, string> > &msgs
|
||||
msg = execute_command(ec, cmd.substr(1));
|
||||
break;
|
||||
case '/':
|
||||
if (!lnav_data.ld_view_stack.empty()) {
|
||||
lnav_view_t index = lnav_view_t(lnav_data.ld_view_stack.back() - lnav_data.ld_views);
|
||||
|
||||
execute_search(index, cmd.substr(1));
|
||||
}
|
||||
lnav_data.ld_view_stack.top() | [cmd] (auto tc) {
|
||||
tc->execute_search(cmd.substr(1));
|
||||
};
|
||||
break;
|
||||
case ';':
|
||||
setup_logline_table(ec);
|
||||
|
@ -187,14 +187,12 @@ void db_label_source::push_column(const char *colstr)
|
||||
|
||||
if (jpw.parse(colstr, value_len) == yajl_status_ok &&
|
||||
jpw.complete_parse() == yajl_status_ok) {
|
||||
for (json_ptr_walk::walk_list_t::iterator iter = jpw.jpw_values.begin();
|
||||
iter != jpw.jpw_values.end();
|
||||
++iter) {
|
||||
if (iter->wt_type == yajl_t_number &&
|
||||
sscanf(iter->wt_value.c_str(), "%lf", &num_value) == 1) {
|
||||
this->dls_chart.add_value(iter->wt_ptr, num_value);
|
||||
for (auto &jpw_value : jpw.jpw_values) {
|
||||
if (jpw_value.wt_type == yajl_t_number &&
|
||||
sscanf(jpw_value.wt_value.c_str(), "%lf", &num_value) == 1) {
|
||||
this->dls_chart.add_value(jpw_value.wt_ptr, num_value);
|
||||
this->dls_chart.with_attrs_for_ident(
|
||||
iter->wt_ptr, vc.attrs_for_ident(iter->wt_ptr));
|
||||
jpw_value.wt_ptr, vc.attrs_for_ident(jpw_value.wt_ptr));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -289,7 +287,7 @@ size_t db_overlay_source::list_overlay_count(const listview_curses &lv)
|
||||
}
|
||||
|
||||
int curr_line = start_line;
|
||||
for (json_ptr_walk::walk_list_t::iterator iter = jpw.jpw_values.begin();
|
||||
for (auto iter = jpw.jpw_values.begin();
|
||||
iter != jpw.jpw_values.end();
|
||||
++iter, curr_line++) {
|
||||
double num_value = 0.0;
|
||||
|
@ -59,6 +59,9 @@ public:
|
||||
lf.get_format()->get_subline(*ll, sbr);
|
||||
}
|
||||
for (auto &filter : this->lfo_filter_stack) {
|
||||
if (filter->lf_deleted) {
|
||||
continue;
|
||||
}
|
||||
if (offset >= this->lfo_filter_state.tfs_filter_count[filter->get_index()]) {
|
||||
filter->add_line(this->lfo_filter_state, ll, sbr);
|
||||
}
|
||||
@ -85,6 +88,9 @@ public:
|
||||
size_t retval = max;
|
||||
|
||||
for (auto &filter : this->lfo_filter_stack) {
|
||||
if (filter->lf_deleted) {
|
||||
continue;
|
||||
}
|
||||
retval = std::min(retval, this->lfo_filter_state.tfs_filter_count[filter->get_index()]);
|
||||
}
|
||||
|
||||
@ -95,6 +101,10 @@ public:
|
||||
uint32_t used_mask = 0;
|
||||
|
||||
for (auto &filter : this->lfo_filter_stack) {
|
||||
if (filter->lf_deleted) {
|
||||
log_debug("skipping deleted %d", filter->get_index());
|
||||
continue;
|
||||
}
|
||||
used_mask |= (1UL << filter->get_index());
|
||||
}
|
||||
this->lfo_filter_state.clear_deleted_filter_state(used_mask);
|
||||
|
127
src/filter_status_source.cc
Normal file
127
src/filter_status_source.cc
Normal file
@ -0,0 +1,127 @@
|
||||
/**
|
||||
* Copyright (c) 2018, 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 "lnav.hh"
|
||||
#include "filter_status_source.hh"
|
||||
|
||||
static auto TOGGLE_MSG = "Press backquote (" ANSI_BOLD("`") ") to edit ";
|
||||
static auto HOTKEY_HELP =
|
||||
ANSI_BOLD("SPC") ": Enable/Disable | "
|
||||
ANSI_BOLD("ENTER") ": Edit | "
|
||||
ANSI_BOLD("i") "/" ANSI_BOLD("o") ": Create in/out | "
|
||||
ANSI_BOLD("D") ": Delete ";
|
||||
|
||||
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_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));
|
||||
|
||||
this->tss_fields[TSF_FILTERED].set_width(20);
|
||||
this->tss_fields[TSF_FILTERED].set_role(view_colors::VCR_BOLD_STATUS);
|
||||
|
||||
this->tss_fields[TSF_HELP].right_justify(true);
|
||||
this->tss_fields[TSF_HELP].set_min_width(35);
|
||||
this->tss_fields[TSF_HELP].set_width(1024);
|
||||
this->tss_fields[TSF_HELP].set_value(TOGGLE_MSG);
|
||||
this->tss_fields[TSF_HELP].set_left_pad(1);
|
||||
this->tss_fields[TSF_HELP].set_share(1);
|
||||
|
||||
this->tss_prompt.set_left_pad(1);
|
||||
this->tss_prompt.set_min_width(35);
|
||||
this->tss_prompt.set_share(1);
|
||||
this->tss_error.set_left_pad(1);
|
||||
this->tss_error.set_min_width(35);
|
||||
this->tss_error.set_share(1);
|
||||
}
|
||||
|
||||
size_t filter_status_source::statusview_fields()
|
||||
{
|
||||
if (lnav_data.ld_mode == LNM_FILTER) {
|
||||
this->tss_fields[TSF_HELP].set_value(HOTKEY_HELP);
|
||||
} else {
|
||||
this->tss_fields[TSF_HELP].set_value(TOGGLE_MSG);
|
||||
}
|
||||
|
||||
if (this->tss_prompt.empty() && this->tss_error.empty()) {
|
||||
return TSF__MAX;
|
||||
}
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
status_field &filter_status_source::statusview_value_for_field(int field)
|
||||
{
|
||||
if (field <= 1) {
|
||||
return this->tss_fields[field];
|
||||
}
|
||||
|
||||
if (!this->tss_error.empty()) {
|
||||
return this->tss_error;
|
||||
}
|
||||
|
||||
if (!this->tss_prompt.empty()) {
|
||||
return this->tss_prompt;
|
||||
}
|
||||
|
||||
return this->tss_fields[field];
|
||||
}
|
||||
|
||||
void filter_status_source::update_filtered(text_sub_source *tss)
|
||||
{
|
||||
status_field &sf = this->tss_fields[TSF_FILTERED];
|
||||
|
||||
if (tss == nullptr || tss->get_filtered_count() == 0) {
|
||||
sf.clear();
|
||||
}
|
||||
else {
|
||||
ui_periodic_timer &timer = ui_periodic_timer::singleton();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this->tss_fields[TSF_FILTERED].set_role(
|
||||
view_colors::VCR_ALERT_STATUS);
|
||||
this->bss_last_filtered_count = tss->get_filtered_count();
|
||||
timer.start_fade(this->bss_filter_counter, 3);
|
||||
}
|
||||
sf.set_value("%'9d Not Shown", tss->get_filtered_count());
|
||||
}
|
||||
}
|
67
src/filter_status_source.hh
Normal file
67
src/filter_status_source.hh
Normal file
@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Copyright (c) 2018, 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 _filter_status_source_hh
|
||||
#define _filter_status_source_hh
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "textview_curses.hh"
|
||||
#include "statusview_curses.hh"
|
||||
|
||||
class filter_status_source
|
||||
: public status_data_source {
|
||||
public:
|
||||
typedef enum {
|
||||
TSF_TITLE,
|
||||
TSF_STITCH_TITLE,
|
||||
TSF_COUNT,
|
||||
TSF_FILTERED,
|
||||
TSF_HELP,
|
||||
|
||||
TSF__MAX
|
||||
} field_t;
|
||||
|
||||
filter_status_source();
|
||||
|
||||
size_t statusview_fields() override;
|
||||
|
||||
status_field &statusview_value_for_field(int field) override;
|
||||
|
||||
void update_filtered(text_sub_source *tss);
|
||||
|
||||
status_field tss_error{1024, view_colors::VCR_ALERT_STATUS};
|
||||
status_field tss_prompt{1024, view_colors::VCR_STATUS};
|
||||
private:
|
||||
status_field tss_fields[TSF__MAX];
|
||||
int bss_last_filtered_count{0};
|
||||
sig_atomic_t bss_filter_counter{0};
|
||||
};
|
||||
|
||||
#endif
|
446
src/filter_sub_source.cc
Normal file
446
src/filter_sub_source.cc
Normal file
@ -0,0 +1,446 @@
|
||||
/**
|
||||
* Copyright (c) 2018, 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 "lnav.hh"
|
||||
|
||||
#include "filter_sub_source.hh"
|
||||
#include "readline_possibilities.hh"
|
||||
#include "readline_highlighters.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
filter_sub_source::filter_sub_source()
|
||||
: fss_change_wire(*this, &filter_sub_source::rl_change),
|
||||
fss_perform_wire(*this, &filter_sub_source::rl_perform),
|
||||
fss_abort_wire(*this, &filter_sub_source::rl_abort),
|
||||
fss_display_match_wire(*this, &filter_sub_source::rl_display_matches),
|
||||
fss_display_next_wire(*this, &filter_sub_source::rl_display_next)
|
||||
{
|
||||
this->filter_context.set_highlighter(readline_regex_highlighter)
|
||||
.set_append_character(0);
|
||||
this->fss_editor.add_context(LNM_FILTER, this->filter_context);
|
||||
this->fss_editor.set_change_action(&this->fss_change_wire);
|
||||
this->fss_editor.set_perform_action(&this->fss_perform_wire);
|
||||
this->fss_editor.set_abort_action(&this->fss_abort_wire);
|
||||
this->fss_editor.set_display_match_action(&this->fss_display_match_wire);
|
||||
this->fss_editor.set_display_next_action(&this->fss_display_next_wire);
|
||||
this->fss_match_view.set_sub_source(&this->fss_match_source);
|
||||
this->fss_match_view.set_height(0_vl);
|
||||
this->fss_match_view.set_show_scrollbar(true);
|
||||
this->fss_match_view.set_default_role(view_colors::VCR_POPUP);
|
||||
}
|
||||
|
||||
bool filter_sub_source::list_input_handle_key(listview_curses &lv, int ch)
|
||||
{
|
||||
if (this->fss_editing) {
|
||||
switch (ch) {
|
||||
case KEY_CTRL_RBRACKET:
|
||||
this->fss_editor.abort();
|
||||
return true;
|
||||
default:
|
||||
this->fss_editor.handle_key(ch);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
switch (ch) {
|
||||
case '`':
|
||||
case 'q':
|
||||
lnav_data.ld_mode = LNM_PAGING;
|
||||
lnav_data.ld_filter_view.reload_data();
|
||||
return true;
|
||||
case ' ': {
|
||||
textview_curses *top_view = *lnav_data.ld_view_stack.top();
|
||||
text_sub_source *tss = top_view->get_sub_source();
|
||||
filter_stack &fs = tss->get_filters();
|
||||
|
||||
if (fs.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
shared_ptr<text_filter> tf = *(fs.begin() + lv.get_selection());
|
||||
|
||||
fs.set_filter_enabled(tf, !tf->is_enabled());
|
||||
tss->text_filters_changed();
|
||||
lv.reload_data();
|
||||
return true;
|
||||
}
|
||||
case 'D': {
|
||||
textview_curses *top_view = *lnav_data.ld_view_stack.top();
|
||||
text_sub_source *tss = top_view->get_sub_source();
|
||||
filter_stack &fs = tss->get_filters();
|
||||
|
||||
if (fs.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
shared_ptr<text_filter> tf = *(fs.begin() + lv.get_selection());
|
||||
|
||||
fs.delete_filter(tf->get_id());
|
||||
tss->text_filters_changed();
|
||||
lv.reload_data();
|
||||
return true;
|
||||
}
|
||||
case 'i': {
|
||||
textview_curses *top_view = *lnav_data.ld_view_stack.top();
|
||||
text_sub_source *tss = top_view->get_sub_source();
|
||||
filter_stack &fs = tss->get_filters();
|
||||
|
||||
if (fs.full()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto ef = make_shared<empty_filter>(text_filter::type_t::INCLUDE, fs.next_index());
|
||||
fs.add_filter(ef);
|
||||
lv.set_selection(vis_line_t(fs.size() - 1));
|
||||
lv.reload_data();
|
||||
|
||||
this->fss_editing = true;
|
||||
|
||||
add_view_text_possibilities(&this->fss_editor, LNM_FILTER, "*", top_view);
|
||||
lnav_data.ld_filter_status_source.tss_prompt.set_value(
|
||||
"Enter a regular expression to match lines to filter in:");
|
||||
this->fss_editor.set_window(lv.get_window());
|
||||
this->fss_editor.set_visible(true);
|
||||
this->fss_editor.set_y(lv.get_y() + (int) (lv.get_selection() - lv.get_top()));
|
||||
this->fss_editor.set_left(8);
|
||||
this->fss_editor.set_width(this->tss_view->get_width() - 8 - 1);
|
||||
this->fss_editor.focus(LNM_FILTER, "", "");
|
||||
this->fss_filter_state = true;
|
||||
ef->disable();
|
||||
tss->text_filters_changed();
|
||||
return true;
|
||||
}
|
||||
case 'o': {
|
||||
textview_curses *top_view = *lnav_data.ld_view_stack.top();
|
||||
text_sub_source *tss = top_view->get_sub_source();
|
||||
filter_stack &fs = tss->get_filters();
|
||||
|
||||
if (fs.full()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto ef = make_shared<empty_filter>(text_filter::type_t::EXCLUDE, fs.next_index());
|
||||
fs.add_filter(ef);
|
||||
lv.set_selection(vis_line_t(fs.size() - 1));
|
||||
lv.reload_data();
|
||||
|
||||
this->fss_editing = true;
|
||||
|
||||
add_view_text_possibilities(&this->fss_editor, LNM_FILTER, "*", top_view);
|
||||
lnav_data.ld_filter_status_source.tss_prompt.set_value(
|
||||
"Enter a regular expression to match lines to filter out:");
|
||||
this->fss_editor.set_window(lv.get_window());
|
||||
this->fss_editor.set_visible(true);
|
||||
this->fss_editor.set_y(lv.get_y() + (int) (lv.get_selection() - lv.get_top()));
|
||||
this->fss_editor.set_left(8);
|
||||
this->fss_editor.set_width(this->tss_view->get_width() - 8 - 1);
|
||||
this->fss_editor.focus(LNM_FILTER, "", "");
|
||||
this->fss_filter_state = true;
|
||||
ef->disable();
|
||||
tss->text_filters_changed();
|
||||
return true;
|
||||
}
|
||||
case '\r':
|
||||
case KEY_ENTER: {
|
||||
textview_curses *top_view = *lnav_data.ld_view_stack.top();
|
||||
text_sub_source *tss = top_view->get_sub_source();
|
||||
filter_stack &fs = tss->get_filters();
|
||||
|
||||
if (fs.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
shared_ptr<text_filter> tf = *(fs.begin() + lv.get_selection());
|
||||
|
||||
this->fss_editing = true;
|
||||
|
||||
add_view_text_possibilities(&this->fss_editor, LNM_FILTER, "*", top_view);
|
||||
this->fss_editor.set_window(lv.get_window());
|
||||
this->fss_editor.set_visible(true);
|
||||
this->fss_editor.set_y(lv.get_y() + (int) (lv.get_selection() - lv.get_top()));
|
||||
this->fss_editor.set_left(8);
|
||||
this->fss_editor.set_width(this->tss_view->get_width() - 8 - 1);
|
||||
this->fss_editor.focus(LNM_FILTER, "", tf->get_id().c_str());
|
||||
this->fss_filter_state = tf->is_enabled();
|
||||
tf->disable();
|
||||
tss->text_filters_changed();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
log_debug("unhandled %x", ch);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t filter_sub_source::text_line_count()
|
||||
{
|
||||
return (lnav_data.ld_view_stack.top() | [] (auto tc) {
|
||||
text_sub_source *tss = tc->get_sub_source();
|
||||
filter_stack &fs = tss->get_filters();
|
||||
|
||||
return nonstd::make_optional(fs.size());
|
||||
}).value_or(0);
|
||||
}
|
||||
|
||||
size_t filter_sub_source::text_line_width(textview_curses &curses)
|
||||
{
|
||||
textview_curses *top_view = *lnav_data.ld_view_stack.top();
|
||||
text_sub_source *tss = top_view->get_sub_source();
|
||||
filter_stack &fs = tss->get_filters();
|
||||
size_t retval = 0;
|
||||
|
||||
for (auto &filter : fs) {
|
||||
retval = std::max(filter->get_id().size() + 8, retval);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
void filter_sub_source::text_value_for_line(textview_curses &tc, int line,
|
||||
std::string &value_out,
|
||||
text_sub_source::line_flags_t flags)
|
||||
{
|
||||
textview_curses *top_view = *lnav_data.ld_view_stack.top();
|
||||
text_sub_source *tss = top_view->get_sub_source();
|
||||
filter_stack &fs = tss->get_filters();
|
||||
shared_ptr<text_filter> tf = *(fs.begin() + line);
|
||||
|
||||
value_out = " ";
|
||||
switch (tf->get_type()) {
|
||||
case text_filter::INCLUDE:
|
||||
value_out.append(" IN ");
|
||||
break;
|
||||
case text_filter::EXCLUDE:
|
||||
value_out.append("OUT ");
|
||||
break;
|
||||
default:
|
||||
ensure(0);
|
||||
break;
|
||||
}
|
||||
|
||||
value_out.append(tf->get_id());
|
||||
}
|
||||
|
||||
void filter_sub_source::text_attrs_for_line(textview_curses &tc, int line,
|
||||
string_attrs_t &value_out)
|
||||
{
|
||||
textview_curses *top_view = *lnav_data.ld_view_stack.top();
|
||||
text_sub_source *tss = top_view->get_sub_source();
|
||||
filter_stack &fs = tss->get_filters();
|
||||
shared_ptr<text_filter> tf = *(fs.begin() + line);
|
||||
bool selected = lnav_data.ld_mode == LNM_FILTER && line == tc.get_selection();
|
||||
int bg = selected ? COLOR_WHITE : COLOR_BLACK;
|
||||
|
||||
if (selected) {
|
||||
value_out.emplace_back(line_range{0, 1}, &view_curses::VC_GRAPHIC, ACS_RARROW);
|
||||
}
|
||||
|
||||
chtype enabled = tf->is_enabled() ? ACS_DIAMOND : ' ';
|
||||
|
||||
line_range lr{2, 3};
|
||||
value_out.emplace_back(lr, &view_curses::VC_GRAPHIC, enabled);
|
||||
if (tf->is_enabled()) {
|
||||
value_out.emplace_back(lr, &view_curses::VC_FOREGROUND, COLOR_GREEN);
|
||||
}
|
||||
|
||||
int fg = tf->get_type() == text_filter::INCLUDE ? COLOR_GREEN : COLOR_RED;
|
||||
value_out.emplace_back(line_range{4, 7}, &view_curses::VC_FOREGROUND, fg);
|
||||
value_out.emplace_back(line_range{4, 7}, &view_curses::VC_STYLE, A_BOLD);
|
||||
fg = selected ? COLOR_BLACK : COLOR_WHITE;
|
||||
value_out.emplace_back(line_range{0, -1}, &view_curses::VC_FOREGROUND, fg);
|
||||
value_out.emplace_back(line_range{0, -1}, &view_curses::VC_BACKGROUND, bg);
|
||||
}
|
||||
|
||||
size_t filter_sub_source::text_size_for_line(textview_curses &tc, int line,
|
||||
text_sub_source::line_flags_t raw)
|
||||
{
|
||||
textview_curses *top_view = *lnav_data.ld_view_stack.top();
|
||||
text_sub_source *tss = top_view->get_sub_source();
|
||||
filter_stack &fs = tss->get_filters();
|
||||
shared_ptr<text_filter> tf = *(fs.begin() + line);
|
||||
|
||||
return 8 + tf->get_id().size();
|
||||
}
|
||||
|
||||
void filter_sub_source::rl_change(readline_curses *rc)
|
||||
{
|
||||
textview_curses *top_view = *lnav_data.ld_view_stack.top();
|
||||
text_sub_source *tss = top_view->get_sub_source();
|
||||
filter_stack &fs = tss->get_filters();
|
||||
if (fs.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto iter = fs.begin() + this->tss_view->get_selection();
|
||||
shared_ptr<text_filter> tf = *iter;
|
||||
string new_value = rc->get_line_buffer();
|
||||
auto_mem<pcre> code;
|
||||
const char *errptr;
|
||||
int eoff;
|
||||
|
||||
if ((code = pcre_compile(new_value.c_str(),
|
||||
PCRE_CASELESS,
|
||||
&errptr,
|
||||
&eoff,
|
||||
nullptr)) == nullptr) {
|
||||
lnav_data.ld_filter_status_source.tss_error
|
||||
.set_value("error: %s", errptr);
|
||||
} else {
|
||||
textview_curses::highlight_map_t &hm = top_view->get_highlights();
|
||||
highlighter hl(code.release());
|
||||
int color;
|
||||
|
||||
if (tf->get_type() == text_filter::EXCLUDE) {
|
||||
color = COLOR_RED;
|
||||
} else {
|
||||
color = COLOR_GREEN;
|
||||
}
|
||||
hl.with_attrs(
|
||||
view_colors::ansi_color_pair(COLOR_BLACK, color) | A_BLINK);
|
||||
|
||||
hm["$preview"] = hl;
|
||||
top_view->reload_data();
|
||||
lnav_data.ld_filter_status_source.tss_error.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void filter_sub_source::rl_perform(readline_curses *rc)
|
||||
{
|
||||
textview_curses *top_view = *lnav_data.ld_view_stack.top();
|
||||
text_sub_source *tss = top_view->get_sub_source();
|
||||
filter_stack &fs = tss->get_filters();
|
||||
auto iter = fs.begin() + this->tss_view->get_selection();
|
||||
shared_ptr<text_filter> tf = *iter;
|
||||
string new_value = rc->get_value();
|
||||
auto_mem<pcre> code;
|
||||
const char *errptr;
|
||||
int eoff;
|
||||
|
||||
if (new_value.empty()) {
|
||||
this->rl_abort(rc);
|
||||
} else if ((code = pcre_compile(new_value.c_str(),
|
||||
PCRE_CASELESS,
|
||||
&errptr,
|
||||
&eoff,
|
||||
nullptr)) == nullptr) {
|
||||
this->rl_abort(rc);
|
||||
} else {
|
||||
tf->lf_deleted = true;
|
||||
tss->text_filters_changed();
|
||||
|
||||
auto pf = make_shared<pcre_filter>(tf->get_type(),
|
||||
new_value, tf->get_index(), code.release());
|
||||
|
||||
*iter = pf;
|
||||
tss->text_filters_changed();
|
||||
}
|
||||
|
||||
lnav_data.ld_filter_status_source.tss_prompt.clear();
|
||||
this->fss_editing = false;
|
||||
this->fss_editor.set_visible(false);
|
||||
this->tss_view->reload_data();
|
||||
}
|
||||
|
||||
void filter_sub_source::rl_abort(readline_curses *rc)
|
||||
{
|
||||
textview_curses *top_view = *lnav_data.ld_view_stack.top();
|
||||
text_sub_source *tss = top_view->get_sub_source();
|
||||
filter_stack &fs = tss->get_filters();
|
||||
auto iter = fs.begin() + this->tss_view->get_selection();
|
||||
shared_ptr<text_filter> tf = *iter;
|
||||
|
||||
lnav_data.ld_filter_status_source.tss_prompt.clear();
|
||||
lnav_data.ld_filter_status_source.tss_error.clear();
|
||||
top_view->get_highlights().erase("$preview");
|
||||
top_view->reload_data();
|
||||
fs.delete_filter("");
|
||||
this->fss_editor.set_visible(false);
|
||||
this->fss_editing = false;
|
||||
this->tss_view->set_needs_update();
|
||||
tf->set_enabled(this->fss_filter_state);
|
||||
tss->text_filters_changed();
|
||||
this->tss_view->reload_data();
|
||||
}
|
||||
|
||||
void filter_sub_source::rl_display_matches(readline_curses *rc)
|
||||
{
|
||||
const std::vector<std::string> &matches = rc->get_matches();
|
||||
unsigned long width = 0;
|
||||
|
||||
if (matches.empty()) {
|
||||
this->fss_match_source.clear();
|
||||
this->fss_match_view.set_height(0_vl);
|
||||
this->tss_view->set_needs_update();
|
||||
} else {
|
||||
string current_match = rc->get_match_string();
|
||||
attr_line_t al;
|
||||
vis_line_t line, selected_line;
|
||||
|
||||
for (auto &match : matches) {
|
||||
if (match == current_match) {
|
||||
al.append(match, &view_curses::VC_STYLE, A_REVERSE);
|
||||
selected_line = line;
|
||||
} else {
|
||||
al.append(match);
|
||||
}
|
||||
al.append(1, '\n');
|
||||
width = std::max(width, match.size());
|
||||
line += 1_vl;
|
||||
}
|
||||
|
||||
this->fss_match_view.set_selection(selected_line);
|
||||
this->fss_match_source.replace_with(al);
|
||||
this->fss_match_view.set_height(std::min(vis_line_t(matches.size()), 3_vl));
|
||||
}
|
||||
|
||||
this->fss_match_view.set_window(this->tss_view->get_window());
|
||||
this->fss_match_view.set_y(rc->get_y() + 1);
|
||||
this->fss_match_view.set_x(rc->get_left() + rc->get_match_start());
|
||||
this->fss_match_view.set_width(width + 3);
|
||||
this->fss_match_view.set_needs_update();
|
||||
this->fss_match_view.scroll_selection_into_view();
|
||||
this->fss_match_view.reload_data();
|
||||
}
|
||||
|
||||
void filter_sub_source::rl_display_next(readline_curses *rc)
|
||||
{
|
||||
textview_curses &tc = this->fss_match_view;
|
||||
|
||||
if (tc.get_top() >= (tc.get_top_for_last_row() - 1)) {
|
||||
tc.set_top(0_vl);
|
||||
}
|
||||
else {
|
||||
tc.shift_top(tc.get_height());
|
||||
}
|
||||
}
|
84
src/filter_sub_source.hh
Normal file
84
src/filter_sub_source.hh
Normal file
@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Copyright (c) 2018, 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 filter_sub_source_hh
|
||||
#define filter_sub_source_hh
|
||||
|
||||
#include "readline_curses.hh"
|
||||
#include "textview_curses.hh"
|
||||
#include "plain_text_source.hh"
|
||||
|
||||
class filter_sub_source
|
||||
: public text_sub_source, public list_input_delegate {
|
||||
public:
|
||||
using fss_functor_t = readline_curses::action::mem_functor_t<filter_sub_source>;
|
||||
|
||||
filter_sub_source();
|
||||
|
||||
bool list_input_handle_key(listview_curses &lv, int ch) override;
|
||||
|
||||
size_t text_line_count() override;;
|
||||
|
||||
size_t text_line_width(textview_curses &curses) override;
|
||||
|
||||
void
|
||||
text_value_for_line(textview_curses &tc, int line, std::string &value_out,
|
||||
line_flags_t flags) override;
|
||||
|
||||
void text_attrs_for_line(textview_curses &tc, int line,
|
||||
string_attrs_t &value_out) override;
|
||||
|
||||
size_t
|
||||
text_size_for_line(textview_curses &tc, int line, line_flags_t raw) override;
|
||||
|
||||
void rl_change(readline_curses *rc);
|
||||
|
||||
void rl_perform(readline_curses *rc);
|
||||
|
||||
void rl_abort(readline_curses *rc);
|
||||
|
||||
void rl_display_matches(readline_curses *rc);
|
||||
|
||||
void rl_display_next(readline_curses *rc);
|
||||
|
||||
readline_context filter_context{"filter"};
|
||||
readline_curses fss_editor;
|
||||
plain_text_source fss_match_source;
|
||||
textview_curses fss_match_view;
|
||||
|
||||
bool fss_editing{false};
|
||||
fss_functor_t fss_change_wire;
|
||||
fss_functor_t fss_perform_wire;
|
||||
fss_functor_t fss_abort_wire;
|
||||
fss_functor_t fss_display_match_wire;
|
||||
fss_functor_t fss_display_next_wire;
|
||||
bool fss_filter_state;
|
||||
};
|
||||
|
||||
#endif
|
@ -35,25 +35,5 @@
|
||||
#include "grep_proc.hh"
|
||||
#include "textview_curses.hh"
|
||||
|
||||
class grep_highlighter {
|
||||
public:
|
||||
grep_highlighter(std::unique_ptr<grep_proc<vis_line_t>> &gp,
|
||||
std::string hl_name,
|
||||
textview_curses::highlight_map_t &hl_map)
|
||||
: gh_grep_proc(std::move(gp)),
|
||||
gh_hl_name(std::move(hl_name)),
|
||||
gh_hl_map(hl_map) { };
|
||||
|
||||
~grep_highlighter()
|
||||
{
|
||||
this->gh_hl_map.erase(this->gh_hl_map.find(this->gh_hl_name));
|
||||
};
|
||||
|
||||
grep_proc<vis_line_t> *get_grep_proc() { return this->gh_grep_proc.get(); };
|
||||
|
||||
private:
|
||||
std::unique_ptr<grep_proc<vis_line_t>> gh_grep_proc;
|
||||
std::string gh_hl_name;
|
||||
textview_curses::highlight_map_t &gh_hl_map;
|
||||
};
|
||||
#endif
|
||||
|
@ -214,8 +214,8 @@ public:
|
||||
* Queue a request to search the input between the given line numbers.
|
||||
*
|
||||
* @param start The line number to start the search at.
|
||||
* @param stop The line number to stop the search at or -1 to read until
|
||||
* the end-of-file.
|
||||
* @param stop The line number to stop the search at (exclusive) or -1 to
|
||||
* read until the end-of-file.
|
||||
*/
|
||||
grep_proc &queue_request(LineType start = LineType(0),
|
||||
LineType stop = LineType(-1))
|
||||
|
18
src/help.txt
18
src/help.txt
@ -176,6 +176,8 @@ Views
|
||||
leaving the spectrogram view with 'Q', the top time in that
|
||||
view will be matched to the top time in the log view.
|
||||
|
||||
` Toggle focusing on the filter editor or the main view.
|
||||
|
||||
a/A Restore the view that was previously popped with 'q/Q'.
|
||||
The 'A' hotkey will try to match the top times between the
|
||||
two views.
|
||||
@ -425,6 +427,22 @@ Session
|
||||
session state (filters, highlights) and then reset the
|
||||
state to the factory default.
|
||||
|
||||
Filter Editor
|
||||
-------------
|
||||
|
||||
The following hotkeys are only available when the focus is on the filter
|
||||
editor. You can change the focus by pressing backquote (`).
|
||||
|
||||
q Switch the focus back to the main view.
|
||||
j/down-arrow Select the next filter.
|
||||
k/up-arrow Select the previous filter.
|
||||
o Create a new "out" filter.
|
||||
i Create a new "in" filter .
|
||||
SPACE Toggle the enabled/disabled state of the currently
|
||||
selected filter.
|
||||
ENTER Edit the selected filter.
|
||||
D Delete the selected filter.
|
||||
|
||||
|
||||
MOUSE SUPPORT (experimental)
|
||||
============================
|
||||
|
@ -80,7 +80,7 @@ public:
|
||||
this->lh_line_values.clear();
|
||||
content_line_t cl = this->lh_sub_source.at(this->lh_current_line);
|
||||
std::shared_ptr<logfile> lf = this->lh_sub_source.find(cl);
|
||||
logfile::iterator ll = lf->begin() + cl;
|
||||
auto ll = lf->begin() + cl;
|
||||
log_format *format = lf->get_format();
|
||||
lf->read_full_message(ll, this->lh_msg_buffer);
|
||||
format->annotate(this->lh_msg_buffer,
|
||||
@ -103,11 +103,11 @@ public:
|
||||
|
||||
void handle_paging_key(int ch)
|
||||
{
|
||||
if (lnav_data.ld_view_stack.empty()) {
|
||||
if (lnav_data.ld_view_stack.vs_views.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
textview_curses * tc = lnav_data.ld_view_stack.back();
|
||||
textview_curses *tc = *lnav_data.ld_view_stack.top();
|
||||
exec_context &ec = lnav_data.ld_exec_context;
|
||||
logfile_sub_source *lss = NULL;
|
||||
bookmarks<vis_line_t>::type & bm = tc->get_bookmarks();
|
||||
@ -124,6 +124,11 @@ void handle_paging_key(int ch)
|
||||
case KEY_BACKSPACE:
|
||||
break;
|
||||
|
||||
case '`':
|
||||
lnav_data.ld_mode = LNM_FILTER;
|
||||
lnav_data.ld_filter_view.reload_data();
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
if (lnav_data.ld_last_view == NULL) {
|
||||
alerter::singleton().chime();
|
||||
@ -142,9 +147,9 @@ void handle_paging_key(int ch)
|
||||
}
|
||||
else {
|
||||
textview_curses *tc = lnav_data.ld_last_view;
|
||||
textview_curses *top_tc = lnav_data.ld_view_stack.back();
|
||||
text_time_translator *dst_view = dynamic_cast<text_time_translator *>(tc->get_sub_source());
|
||||
text_time_translator *src_view = dynamic_cast<text_time_translator *>(top_tc->get_sub_source());
|
||||
textview_curses *top_tc = *lnav_data.ld_view_stack.top();
|
||||
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 = NULL;
|
||||
if (src_view != NULL && dst_view != NULL) {
|
||||
@ -236,7 +241,6 @@ void handle_paging_key(int ch)
|
||||
|
||||
if (!tss.empty()) {
|
||||
tss.rotate_left();
|
||||
redo_search(LNV_TEXT);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -250,7 +254,6 @@ void handle_paging_key(int ch)
|
||||
|
||||
if (!tss.empty()) {
|
||||
tss.rotate_right();
|
||||
redo_search(LNV_TEXT);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -323,7 +326,7 @@ void handle_paging_key(int ch)
|
||||
case 'J':
|
||||
if (lnav_data.ld_last_user_mark.find(tc) ==
|
||||
lnav_data.ld_last_user_mark.end() ||
|
||||
!tc->is_visible(vis_line_t(lnav_data.ld_last_user_mark[tc]))) {
|
||||
!tc->is_line_visible(vis_line_t(lnav_data.ld_last_user_mark[tc]))) {
|
||||
lnav_data.ld_select_start[tc] = tc->get_top();
|
||||
lnav_data.ld_last_user_mark[tc] = tc->get_top();
|
||||
}
|
||||
@ -357,7 +360,7 @@ void handle_paging_key(int ch)
|
||||
|
||||
if (lnav_data.ld_last_user_mark.find(tc) ==
|
||||
lnav_data.ld_last_user_mark.end() ||
|
||||
!tc->is_visible(vis_line_t(lnav_data.ld_last_user_mark[tc]))) {
|
||||
!tc->is_line_visible(vis_line_t(lnav_data.ld_last_user_mark[tc]))) {
|
||||
new_mark = tc->get_top();
|
||||
}
|
||||
else {
|
||||
@ -486,10 +489,10 @@ void handle_paging_key(int ch)
|
||||
|
||||
case '0':
|
||||
if (lss) {
|
||||
time_t first_time = lnav_data.ld_top_time.tv_sec;
|
||||
struct timeval first_time = lss->time_for_row(tc->get_top());
|
||||
int step = 24 * 60 * 60;
|
||||
vis_line_t line =
|
||||
lss->find_from_time(roundup_size(first_time, step));
|
||||
lss->find_from_time(roundup_size(first_time.tv_sec, step));
|
||||
|
||||
tc->set_top(line);
|
||||
}
|
||||
@ -497,7 +500,8 @@ void handle_paging_key(int ch)
|
||||
|
||||
case ')':
|
||||
if (lss) {
|
||||
time_t day = rounddown(lnav_data.ld_top_time.tv_sec, 24 * 60 * 60);
|
||||
struct timeval first_time = lss->time_for_row(tc->get_top());
|
||||
time_t day = rounddown(first_time.tv_sec, 24 * 60 * 60);
|
||||
vis_line_t line = lss->find_from_time(day);
|
||||
|
||||
--line;
|
||||
@ -510,8 +514,9 @@ void handle_paging_key(int ch)
|
||||
alerter::singleton().chime();
|
||||
}
|
||||
else if (lss) {
|
||||
struct timeval first_time = lss->time_for_row(tc->get_top());
|
||||
int step = ch == 'D' ? (24 * 60 * 60) : (60 * 60);
|
||||
time_t top_time = lnav_data.ld_top_time.tv_sec;
|
||||
time_t top_time = first_time.tv_sec;
|
||||
vis_line_t line = lss->find_from_time(top_time - step);
|
||||
|
||||
if (line != 0) {
|
||||
@ -525,9 +530,10 @@ void handle_paging_key(int ch)
|
||||
|
||||
case 'd':
|
||||
if (lss) {
|
||||
struct timeval first_time = lss->time_for_row(tc->get_top());
|
||||
int step = ch == 'd' ? (24 * 60 * 60) : (60 * 60);
|
||||
vis_line_t line =
|
||||
lss->find_from_time(lnav_data.ld_top_time.tv_sec + step);
|
||||
lss->find_from_time(first_time.tv_sec + step);
|
||||
|
||||
tc->set_top(line);
|
||||
|
||||
@ -696,10 +702,8 @@ void handle_paging_key(int ch)
|
||||
}
|
||||
}
|
||||
|
||||
add_view_text_possibilities(LNM_COMMAND, "filter", tc);
|
||||
lnav_data.ld_rl_view->
|
||||
add_possibility(LNM_COMMAND, "filter",
|
||||
lnav_data.ld_last_search[tc - lnav_data.ld_views]);
|
||||
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());
|
||||
add_filter_possibilities(tc);
|
||||
add_mark_possibilities();
|
||||
add_config_possibilities();
|
||||
@ -711,9 +715,9 @@ void handle_paging_key(int ch)
|
||||
|
||||
case '/':
|
||||
lnav_data.ld_mode = LNM_SEARCH;
|
||||
lnav_data.ld_previous_search = lnav_data.ld_last_search[tc - lnav_data.ld_views];
|
||||
lnav_data.ld_previous_search = tc->get_last_search();
|
||||
lnav_data.ld_search_start_line = tc->get_top();
|
||||
add_view_text_possibilities(LNM_SEARCH, "*", tc);
|
||||
add_view_text_possibilities(lnav_data.ld_rl_view, LNM_SEARCH, "*", tc);
|
||||
lnav_data.ld_rl_view->focus(LNM_SEARCH, "/");
|
||||
lnav_data.ld_bottom_source.set_prompt(
|
||||
"Enter a regular expression to search for: "
|
||||
@ -758,7 +762,7 @@ void handle_paging_key(int ch)
|
||||
for (const auto &iter : scripts) {
|
||||
lnav_data.ld_rl_view->add_possibility(LNM_EXEC, "__command", iter.first);
|
||||
}
|
||||
add_view_text_possibilities(LNM_EXEC, "*", tc);
|
||||
add_view_text_possibilities(lnav_data.ld_rl_view, LNM_EXEC, "*", tc);
|
||||
add_env_possibilities(LNM_EXEC);
|
||||
lnav_data.ld_rl_view->focus(LNM_EXEC, "|");
|
||||
lnav_data.ld_bottom_source.set_prompt(
|
||||
@ -806,11 +810,11 @@ void handle_paging_key(int ch)
|
||||
|
||||
case 'I':
|
||||
{
|
||||
struct timeval log_top = lnav_data.ld_top_time;
|
||||
struct timeval log_top = lss->time_for_row(lnav_data.ld_views[LNV_LOG].get_top());
|
||||
hist_source2 &hs = lnav_data.ld_hist_source2;
|
||||
|
||||
if (toggle_view(&lnav_data.ld_views[LNV_HISTOGRAM])) {
|
||||
tc = lnav_data.ld_view_stack.back();
|
||||
tc = *lnav_data.ld_view_stack.top();
|
||||
tc->set_top(vis_line_t(hs.row_for_time(log_top)));
|
||||
}
|
||||
else {
|
||||
@ -940,22 +944,6 @@ void handle_paging_key(int ch)
|
||||
tc->set_needs_update();
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
{
|
||||
string ex;
|
||||
|
||||
for (auto &iter : bm[&BM_EXAMPLE]) {
|
||||
string line;
|
||||
|
||||
tc->get_sub_source()->text_value_for_line(*tc, iter, line);
|
||||
ex += line + "\n";
|
||||
}
|
||||
lnav_data.ld_views[LNV_EXAMPLE].set_sub_source(new plain_text_source(
|
||||
ex));
|
||||
ensure_view(&lnav_data.ld_views[LNV_EXAMPLE]);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
case 'R':
|
||||
if (lss) {
|
||||
|
@ -52,7 +52,6 @@ listview_curses::listview_curses()
|
||||
lv_top(0),
|
||||
lv_left(0),
|
||||
lv_height(0),
|
||||
lv_needs_update(true),
|
||||
lv_overlay_needs_update(true),
|
||||
lv_show_scrollbar(true),
|
||||
lv_show_bottom_border(false),
|
||||
@ -74,18 +73,23 @@ void listview_curses::reload_data(void)
|
||||
this->lv_top = 0_vl;
|
||||
this->lv_left = 0;
|
||||
}
|
||||
else if (this->lv_top >= this->get_inner_height()) {
|
||||
this->lv_top = max(0_vl, vis_line_t(this->get_inner_height() - 1));
|
||||
else {
|
||||
if (this->lv_top >= this->get_inner_height()) {
|
||||
this->lv_top = max(0_vl, vis_line_t(this->get_inner_height() - 1));
|
||||
}
|
||||
if (this->get_inner_height() == 0) {
|
||||
this->lv_selection = 0_vl;
|
||||
} else if (this->lv_selection >= this->get_inner_height()) {
|
||||
this->lv_selection = this->get_inner_height() - 1_vl;
|
||||
}
|
||||
}
|
||||
this->lv_needs_update = true;
|
||||
this->vc_needs_update = true;
|
||||
}
|
||||
|
||||
bool listview_curses::handle_key(int ch)
|
||||
{
|
||||
for (list<list_input_delegate *>::iterator iter = this->lv_input_delegates.begin();
|
||||
iter != this->lv_input_delegates.end();
|
||||
++iter) {
|
||||
if ((*iter)->list_input_handle_key(*this, ch)) {
|
||||
for (auto &lv_input_delegate : this->lv_input_delegates) {
|
||||
if (lv_input_delegate->list_input_handle_key(*this, ch)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -118,12 +122,20 @@ bool listview_curses::handle_key(int ch)
|
||||
case '\r':
|
||||
case 'j':
|
||||
case KEY_DOWN:
|
||||
this->shift_top(1_vl);
|
||||
if (this->is_selectable()) {
|
||||
this->shift_selection(1);
|
||||
} else {
|
||||
this->shift_top(1_vl);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'k':
|
||||
case KEY_UP:
|
||||
this->shift_top(-1_vl);
|
||||
if (this->is_selectable()) {
|
||||
this->shift_selection(-1);
|
||||
} else {
|
||||
this->shift_top(-1_vl);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
@ -181,23 +193,29 @@ bool listview_curses::handle_key(int ch)
|
||||
return retval;
|
||||
}
|
||||
|
||||
void listview_curses::do_update(void)
|
||||
void listview_curses::do_update()
|
||||
{
|
||||
if (this->lv_window == NULL || this->lv_height == 0) {
|
||||
if (this->lv_window == nullptr || this->lv_height == 0) {
|
||||
view_curses::do_update();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->lv_needs_update) {
|
||||
if (this->vc_needs_update) {
|
||||
view_colors &vc = view_colors::singleton();
|
||||
vis_line_t height, row;
|
||||
attr_line_t overlay_line;
|
||||
vis_line_t overlay_height(0);
|
||||
struct line_range lr;
|
||||
unsigned long width, wrap_width;
|
||||
size_t row_count;
|
||||
int y = this->lv_y, bottom;
|
||||
attr_t role_attrs = vc.attrs_for_role(this->vc_default_role);
|
||||
|
||||
this->get_dimensions(height, width);
|
||||
|
||||
if (this->vc_width > 0) {
|
||||
width = std::min((unsigned long) this->vc_width, width);
|
||||
}
|
||||
|
||||
wrap_width = width - (this->lv_word_wrap ? 1 : this->lv_show_scrollbar ? 1 : 0);
|
||||
|
||||
row_count = this->get_inner_height();
|
||||
@ -214,7 +232,7 @@ void listview_curses::do_update(void)
|
||||
y - this->lv_y, bottom - this->lv_y,
|
||||
row,
|
||||
overlay_line)) {
|
||||
this->mvwattrline(this->lv_window, y, this->lv_x, overlay_line, lr);
|
||||
mvwattrline(this->lv_window, y, this->lv_x, overlay_line, lr);
|
||||
overlay_line.clear();
|
||||
++y;
|
||||
}
|
||||
@ -222,10 +240,10 @@ void listview_curses::do_update(void)
|
||||
attr_line_t &al = rows[row - this->lv_top];
|
||||
|
||||
do {
|
||||
this->mvwattrline(this->lv_window, y, this->lv_x, al, lr);
|
||||
mvwattrline(this->lv_window, y, this->lv_x, al, lr,
|
||||
this->vc_default_role);
|
||||
if (this->lv_word_wrap) {
|
||||
wmove(this->lv_window, y, wrap_width);
|
||||
wclrtoeol(this->lv_window);
|
||||
mvwhline(this->lv_window, y, this->lv_x + wrap_width, ' ', width - wrap_width);
|
||||
}
|
||||
lr.lr_start += wrap_width;
|
||||
lr.lr_end += wrap_width;
|
||||
@ -234,14 +252,14 @@ void listview_curses::do_update(void)
|
||||
++row;
|
||||
}
|
||||
else {
|
||||
wmove(this->lv_window, y, this->lv_x);
|
||||
wclrtoeol(this->lv_window);
|
||||
wattron(this->lv_window, role_attrs);
|
||||
mvwhline(this->lv_window, y, this->lv_x, ' ', width);
|
||||
wattroff(this->lv_window, role_attrs);
|
||||
++y;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->lv_show_scrollbar) {
|
||||
view_colors &vc = view_colors::singleton();
|
||||
double progress = 1.0;
|
||||
double coverage = 1.0;
|
||||
double adjusted_height = (double)row_count / (double)height;
|
||||
@ -259,7 +277,7 @@ void listview_curses::do_update(void)
|
||||
gutter_y < (this->lv_y + height);
|
||||
gutter_y++) {
|
||||
int range_start = 0, range_end;
|
||||
view_colors::role_t role = view_colors::VCR_TEXT;
|
||||
view_colors::role_t role = this->vc_default_role;
|
||||
view_colors::role_t bar_role = view_colors::VCR_STATUS;
|
||||
int attrs;
|
||||
chtype ch = ACS_VLINE;
|
||||
@ -276,7 +294,7 @@ void listview_curses::do_update(void)
|
||||
}
|
||||
attrs = vc.attrs_for_role(role);
|
||||
wattron(this->lv_window, attrs);
|
||||
mvwaddch(this->lv_window, gutter_y, width - 1, ch);
|
||||
mvwaddch(this->lv_window, gutter_y, this->lv_x + width - 1, ch);
|
||||
wattroff(this->lv_window, attrs);
|
||||
}
|
||||
wmove(this->lv_window, this->lv_y + height - 1, this->lv_x);
|
||||
@ -293,8 +311,11 @@ void listview_curses::do_update(void)
|
||||
mvwadd_wchnstr(this->lv_window, y, this->lv_x, row_ch, width - 1);
|
||||
}
|
||||
|
||||
this->lv_needs_update = false;
|
||||
this->vc_needs_update = false;
|
||||
}
|
||||
|
||||
view_curses::do_update();
|
||||
|
||||
#if 0
|
||||
else if (this->lv_overlay_needs_update && this->lv_overlay_source != NULL) {
|
||||
vis_line_t y(this->lv_y), height, bottom;
|
||||
|
@ -96,8 +96,6 @@ public:
|
||||
view_colors::role_t &role_out,
|
||||
view_colors::role_t &bar_role_out) {
|
||||
ch_out = ACS_VLINE;
|
||||
role_out = view_colors::VCR_TEXT;
|
||||
bar_role_out = view_colors::VCR_STATUS;
|
||||
};
|
||||
};
|
||||
|
||||
@ -129,7 +127,7 @@ public:
|
||||
/** Construct an empty list view. */
|
||||
listview_curses();
|
||||
|
||||
virtual ~listview_curses();
|
||||
~listview_curses();
|
||||
|
||||
void set_title(const std::string &title) {
|
||||
this->lv_title = title;
|
||||
@ -197,6 +195,50 @@ public:
|
||||
};
|
||||
bool get_show_bottom_border() const { return this->lv_show_bottom_border; };
|
||||
|
||||
void set_selectable(bool sel) {
|
||||
this->lv_selectable = sel;
|
||||
};
|
||||
|
||||
bool is_selectable() const {
|
||||
return this->lv_selectable;
|
||||
};
|
||||
|
||||
void set_selection(vis_line_t sel) {
|
||||
if (this->lv_selection != sel) {
|
||||
this->lv_selection = sel;
|
||||
this->scroll_selection_into_view();
|
||||
this->set_needs_update();
|
||||
}
|
||||
}
|
||||
|
||||
void scroll_selection_into_view() {
|
||||
unsigned long width;
|
||||
vis_line_t height;
|
||||
|
||||
this->get_dimensions(height, width);
|
||||
if (this->lv_selection >= (this->lv_top + height - 1)) {
|
||||
this->set_top(this->lv_selection - height + 2_vl, true);
|
||||
} else if (this->lv_selection < this->lv_top) {
|
||||
this->set_top(this->lv_selection, true);
|
||||
}
|
||||
}
|
||||
|
||||
void shift_selection(int offset) {
|
||||
vis_line_t new_selection = this->lv_selection + vis_line_t(offset);
|
||||
|
||||
if (new_selection >= 0_vl &&
|
||||
new_selection < this->get_inner_height()) {
|
||||
this->set_selection(new_selection);
|
||||
this->scroll_selection_into_view();
|
||||
} else {
|
||||
alerter::singleton().chime();
|
||||
}
|
||||
}
|
||||
|
||||
vis_line_t get_selection() const {
|
||||
return this->lv_selection;
|
||||
}
|
||||
|
||||
listview_curses &set_word_wrap(bool ww) {
|
||||
bool scroll_down = this->lv_top >= this->get_top_for_last_row();
|
||||
|
||||
@ -267,7 +309,7 @@ public:
|
||||
{
|
||||
if (y != this->lv_y) {
|
||||
this->lv_y = y;
|
||||
this->lv_needs_update = true;
|
||||
this->set_needs_update();
|
||||
}
|
||||
};
|
||||
unsigned int get_y() const { return this->lv_y; };
|
||||
@ -276,7 +318,7 @@ public:
|
||||
{
|
||||
if (x != this->lv_x) {
|
||||
this->lv_x = x;
|
||||
this->lv_needs_update = true;
|
||||
this->set_needs_update();
|
||||
}
|
||||
};
|
||||
unsigned int get_x() const { return this->lv_x; };
|
||||
@ -335,7 +377,7 @@ public:
|
||||
};
|
||||
|
||||
/** @return True if the given line is visible. */
|
||||
bool is_visible(vis_line_t line)
|
||||
bool is_line_visible(vis_line_t line)
|
||||
{
|
||||
return this->get_top() <= line && line <= this->get_bottom();
|
||||
};
|
||||
@ -388,7 +430,7 @@ public:
|
||||
|
||||
this->lv_left = left;
|
||||
this->invoke_scroll();
|
||||
this->lv_needs_update = true;
|
||||
this->set_needs_update();
|
||||
};
|
||||
|
||||
/** @return The column number that is displayed at the left. */
|
||||
@ -426,7 +468,7 @@ public:
|
||||
{
|
||||
if (this->lv_height != height) {
|
||||
this->lv_height = height;
|
||||
this->lv_needs_update = true;
|
||||
this->set_needs_update();
|
||||
}
|
||||
};
|
||||
|
||||
@ -445,8 +487,6 @@ public:
|
||||
this->lv_source->listview_width(*this);
|
||||
};
|
||||
|
||||
void set_needs_update() { this->lv_needs_update = true; };
|
||||
|
||||
void set_overlay_needs_update() { this->lv_overlay_needs_update = true; };
|
||||
|
||||
/**
|
||||
@ -459,7 +499,7 @@ public:
|
||||
{
|
||||
unsigned long height;
|
||||
|
||||
if (this->lv_window == NULL) {
|
||||
if (this->lv_window == nullptr) {
|
||||
height_out = std::max(this->lv_height, vis_line_t(1));
|
||||
if (this->lv_source) {
|
||||
width_out = this->lv_source->listview_width(*this);
|
||||
@ -486,7 +526,7 @@ public:
|
||||
};
|
||||
|
||||
/** This method should be called when the data source has changed. */
|
||||
virtual void reload_data(void);
|
||||
virtual void reload_data();
|
||||
|
||||
/**
|
||||
* @param ch The input to be handled.
|
||||
@ -497,7 +537,7 @@ public:
|
||||
/**
|
||||
* Query the data source and draw the visible lines on the display.
|
||||
*/
|
||||
virtual void do_update(void);
|
||||
void do_update();
|
||||
|
||||
bool handle_mouse(mouse_event &me);
|
||||
|
||||
@ -514,6 +554,10 @@ public:
|
||||
log_debug(" lv_top=%d", (int) this->lv_top);
|
||||
};
|
||||
|
||||
virtual void invoke_scroll() {
|
||||
this->lv_scroll.invoke(this);
|
||||
}
|
||||
|
||||
protected:
|
||||
enum lv_mode_t {
|
||||
LV_MODE_NONE,
|
||||
@ -524,12 +568,8 @@ protected:
|
||||
|
||||
static list_gutter_source DEFAULT_GUTTER_SOURCE;
|
||||
|
||||
virtual void invoke_scroll() {
|
||||
this->lv_scroll.invoke(this);
|
||||
}
|
||||
|
||||
std::string lv_title;
|
||||
list_data_source * lv_source; /*< The data source delegate. */
|
||||
list_data_source *lv_source; /*< The data source delegate. */
|
||||
std::list<list_input_delegate *> lv_input_delegates;
|
||||
list_overlay_source *lv_overlay_source;
|
||||
action lv_scroll; /*< The scroll action. */
|
||||
@ -539,14 +579,13 @@ protected:
|
||||
vis_line_t lv_top; /*< The line at the top of the view. */
|
||||
unsigned int lv_left; /*< The column at the left of the view. */
|
||||
vis_line_t lv_height; /*< The abs/rel height of the view. */
|
||||
bool lv_needs_update; /*< Flag to indicate if a display update
|
||||
* is needed.
|
||||
*/
|
||||
bool lv_overlay_needs_update;
|
||||
bool lv_show_scrollbar; /*< Draw the scrollbar in the view. */
|
||||
bool lv_show_bottom_border;
|
||||
list_gutter_source *lv_gutter_source;
|
||||
bool lv_word_wrap;
|
||||
bool lv_selectable{false};
|
||||
vis_line_t lv_selection{0};
|
||||
|
||||
struct timeval lv_mouse_time;
|
||||
int lv_scroll_accel;
|
||||
|
1388
src/lnav.cc
1388
src/lnav.cc
File diff suppressed because it is too large
Load Diff
21
src/lnav.hh
21
src/lnav.hh
@ -64,11 +64,14 @@
|
||||
#include "spectro_source.hh"
|
||||
#include "command_executor.hh"
|
||||
#include "plain_text_source.hh"
|
||||
#include "filter_sub_source.hh"
|
||||
#include "filter_status_source.hh"
|
||||
#include "preview_status_source.hh"
|
||||
|
||||
/** The command modes that are available while viewing a file. */
|
||||
typedef enum {
|
||||
LNM_PAGING,
|
||||
LNM_FILTER,
|
||||
LNM_COMMAND,
|
||||
LNM_SEARCH,
|
||||
LNM_CAPTURE,
|
||||
@ -118,7 +121,6 @@ typedef enum {
|
||||
LNV_HELP,
|
||||
LNV_HISTOGRAM,
|
||||
LNV_DB,
|
||||
LNV_EXAMPLE,
|
||||
LNV_SCHEMA,
|
||||
LNV_PRETTY,
|
||||
LNV_SPECTRO,
|
||||
@ -134,6 +136,7 @@ extern const char *lnav_zoom_strings[];
|
||||
typedef enum {
|
||||
LNS_TOP,
|
||||
LNS_BOTTOM,
|
||||
LNS_FILTER,
|
||||
LNS_DOC,
|
||||
LNS_PREVIEW,
|
||||
|
||||
@ -238,19 +241,19 @@ struct _lnav_data {
|
||||
statusview_curses ld_status[LNS__MAX];
|
||||
top_status_source ld_top_source;
|
||||
bottom_status_source ld_bottom_source;
|
||||
filter_status_source ld_filter_status_source;
|
||||
doc_status_source ld_doc_status_source;
|
||||
preview_status_source ld_preview_status_source;
|
||||
bool ld_preview_hidden;
|
||||
listview_curses::action::broadcaster ld_scroll_broadcaster;
|
||||
listview_curses::action::broadcaster ld_view_stack_broadcaster;
|
||||
|
||||
struct timeval ld_top_time;
|
||||
struct timeval ld_bottom_time;
|
||||
|
||||
plain_text_source ld_help_source;
|
||||
|
||||
plain_text_source ld_doc_source;
|
||||
textview_curses ld_doc_view;
|
||||
filter_sub_source ld_filter_source;
|
||||
textview_curses ld_filter_view;
|
||||
plain_text_source ld_example_source;
|
||||
textview_curses ld_example_view;
|
||||
plain_text_source ld_match_source;
|
||||
@ -258,10 +261,9 @@ struct _lnav_data {
|
||||
plain_text_source ld_preview_source;
|
||||
textview_curses ld_preview_view;
|
||||
|
||||
std::vector<textview_curses *> ld_view_stack;
|
||||
view_stack<textview_curses> ld_view_stack;
|
||||
textview_curses *ld_last_view;
|
||||
textview_curses ld_views[LNV__MAX];
|
||||
std::unique_ptr<grep_highlighter> ld_search_child[LNV__MAX];
|
||||
std::shared_ptr<grep_proc<vis_line_t>> ld_meta_search;
|
||||
vis_line_t ld_search_start_line;
|
||||
readline_curses * ld_rl_view;
|
||||
@ -281,7 +283,6 @@ struct _lnav_data {
|
||||
std::vector<std::string> ld_db_key_names;
|
||||
|
||||
std::string ld_previous_search;
|
||||
std::string ld_last_search[LNV__MAX];
|
||||
|
||||
vis_line_t ld_last_pretty_print_top;
|
||||
|
||||
@ -324,16 +325,14 @@ extern const ssize_t ZOOM_COUNT;
|
||||
|
||||
void rebuild_hist();
|
||||
void rebuild_indexes();
|
||||
void execute_examples();
|
||||
|
||||
bool ensure_view(textview_curses *expected_tc);
|
||||
bool toggle_view(textview_curses *toggle_tc);
|
||||
void layout_views();
|
||||
|
||||
bool setup_logline_table(exec_context &ec);
|
||||
|
||||
void execute_search(lnav_view_t view, const std::string ®ex);
|
||||
|
||||
void redo_search(lnav_view_t view_index);
|
||||
|
||||
bool rescan_files(bool required = false);
|
||||
|
||||
void wait_for_children();
|
||||
|
@ -260,7 +260,7 @@ static string com_goto(exec_context &ec, string cmdline, vector<string> &args)
|
||||
}
|
||||
else if (args.size() > 1) {
|
||||
string all_args = remaining_args(cmdline, args);
|
||||
textview_curses *tc = lnav_data.ld_view_stack.back();
|
||||
textview_curses *tc = *lnav_data.ld_view_stack.top();
|
||||
auto ttt = dynamic_cast<text_time_translator *>(tc->get_sub_source());
|
||||
int line_number, consumed;
|
||||
date_time_scanner dts;
|
||||
@ -283,7 +283,8 @@ static string com_goto(exec_context &ec, string cmdline, vector<string> &args)
|
||||
do {
|
||||
struct exttm tm = rt.add(tv);
|
||||
|
||||
new_vl = vis_line_t(ttt->row_for_time(tm.to_timeval()));
|
||||
tv = tm.to_timeval();
|
||||
new_vl = vis_line_t(ttt->row_for_time(tv));
|
||||
|
||||
if (new_vl == 0_vl || new_vl != vl || !rt.is_relative()) {
|
||||
vl = new_vl;
|
||||
@ -355,7 +356,7 @@ static string com_relative_goto(exec_context &ec, string cmdline, vector<string>
|
||||
if (args.empty()) {
|
||||
}
|
||||
else if (args.size() > 1) {
|
||||
textview_curses *tc = lnav_data.ld_view_stack.back();
|
||||
textview_curses *tc = *lnav_data.ld_view_stack.top();
|
||||
int line_offset, consumed;
|
||||
float value;
|
||||
|
||||
@ -386,10 +387,10 @@ static string com_mark(exec_context &ec, string cmdline, vector<string> &args)
|
||||
{
|
||||
string retval;
|
||||
|
||||
if (args.empty() || lnav_data.ld_view_stack.empty()) {
|
||||
if (args.empty() || lnav_data.ld_view_stack.vs_views.empty()) {
|
||||
|
||||
} else if (!ec.ec_dry_run) {
|
||||
textview_curses *tc = lnav_data.ld_view_stack.back();
|
||||
textview_curses *tc = *lnav_data.ld_view_stack.top();
|
||||
lnav_data.ld_last_user_mark[tc] = tc->get_top();
|
||||
tc->toggle_user_mark(&textview_curses::BM_USER,
|
||||
vis_line_t(lnav_data.ld_last_user_mark[tc]));
|
||||
@ -407,7 +408,7 @@ static string com_goto_mark(exec_context &ec, string cmdline, vector<string> &ar
|
||||
args.emplace_back("mark-type");
|
||||
}
|
||||
else {
|
||||
textview_curses *tc = lnav_data.ld_view_stack.back();
|
||||
textview_curses *tc = *lnav_data.ld_view_stack.top();
|
||||
string type_name = "user";
|
||||
|
||||
if (args.size() > 1) {
|
||||
@ -540,7 +541,7 @@ static string com_save_to(exec_context &ec, string cmdline, vector<string> &args
|
||||
mode = "w";
|
||||
}
|
||||
|
||||
textview_curses * tc = lnav_data.ld_view_stack.back();
|
||||
textview_curses * tc = *lnav_data.ld_view_stack.top();
|
||||
bookmark_vector<vis_line_t> &bv =
|
||||
tc->get_bookmarks()[&textview_curses::BM_USER];
|
||||
db_label_source &dls = lnav_data.ld_db_row_source;
|
||||
@ -812,7 +813,7 @@ static string com_pipe_to(exec_context &ec, string cmdline, vector<string> &args
|
||||
return "";
|
||||
}
|
||||
|
||||
textview_curses * tc = lnav_data.ld_view_stack.back();
|
||||
textview_curses * tc = *lnav_data.ld_view_stack.top();
|
||||
bookmark_vector<vis_line_t> &bv =
|
||||
tc->get_bookmarks()[&textview_curses::BM_USER];
|
||||
bool pipe_line_to = (args[0] == "pipe-line-to");
|
||||
@ -997,7 +998,7 @@ static string com_highlight(exec_context &ec, string cmdline, vector<string> &ar
|
||||
args.emplace_back("filter");
|
||||
}
|
||||
else if (args.size() > 1) {
|
||||
textview_curses *tc = lnav_data.ld_view_stack.back();
|
||||
textview_curses *tc = *lnav_data.ld_view_stack.top();
|
||||
textview_curses::highlight_map_t &hm = tc->get_highlights();
|
||||
const char *errptr;
|
||||
auto_mem<pcre> code;
|
||||
@ -1056,7 +1057,7 @@ static string com_clear_highlight(exec_context &ec, string cmdline, vector<strin
|
||||
args.emplace_back("highlight");
|
||||
}
|
||||
else if (args.size() > 1 && args[1][0] != '$') {
|
||||
textview_curses *tc = lnav_data.ld_view_stack.back();
|
||||
textview_curses *tc = *lnav_data.ld_view_stack.top();
|
||||
textview_curses::highlight_map_t &hm = tc->get_highlights();
|
||||
textview_curses::highlight_map_t::iterator hm_iter;
|
||||
|
||||
@ -1096,32 +1097,6 @@ static string com_help(exec_context &ec, string cmdline, vector<string> &args)
|
||||
return retval;
|
||||
}
|
||||
|
||||
class pcre_filter
|
||||
: public text_filter {
|
||||
public:
|
||||
pcre_filter(type_t type, const string id, size_t index, pcre *code)
|
||||
: text_filter(type, id, index),
|
||||
pf_pcre(code) { };
|
||||
|
||||
~pcre_filter() override { };
|
||||
|
||||
bool matches(const logfile &lf, const logline &ll, shared_buffer_ref &line) override {
|
||||
pcre_context_static<30> pc;
|
||||
pcre_input pi(line.get_data(), 0, line.length());
|
||||
|
||||
return this->pf_pcre.match(pc, pi);
|
||||
};
|
||||
|
||||
std::string to_command() override {
|
||||
return (this->lf_type == text_filter::INCLUDE ?
|
||||
"filter-in " : "filter-out ") +
|
||||
this->lf_id;
|
||||
};
|
||||
|
||||
protected:
|
||||
pcrepp pf_pcre;
|
||||
};
|
||||
|
||||
static string com_enable_filter(exec_context &ec, string cmdline, vector<string> &args);
|
||||
|
||||
static string com_filter(exec_context &ec, string cmdline, vector<string> &args)
|
||||
@ -1132,7 +1107,7 @@ static string com_filter(exec_context &ec, string cmdline, vector<string> &args)
|
||||
args.emplace_back("filter");
|
||||
}
|
||||
else if (args.size() > 1) {
|
||||
textview_curses *tc = lnav_data.ld_view_stack.back();
|
||||
textview_curses *tc = *lnav_data.ld_view_stack.top();
|
||||
text_sub_source *tss = tc->get_sub_source();
|
||||
filter_stack &fs = tss->get_filters();
|
||||
const char *errptr;
|
||||
@ -1188,16 +1163,11 @@ static string com_filter(exec_context &ec, string cmdline, vector<string> &args)
|
||||
text_filter::EXCLUDE :
|
||||
text_filter::INCLUDE;
|
||||
auto pf = make_shared<pcre_filter>(lt, args[1], fs.next_index(), code.release());
|
||||
lnav_view_t view_index = lnav_view_t(tc - lnav_data.ld_views);
|
||||
|
||||
log_debug("%s [%d] %s", args[0].c_str(), pf->get_index(), args[1].c_str());
|
||||
fs.add_filter(pf);
|
||||
tss->text_filters_changed();
|
||||
redo_search(view_index);
|
||||
if (lnav_data.ld_rl_view != nullptr) {
|
||||
lnav_data.ld_rl_view->add_possibility(
|
||||
LNM_COMMAND, "enabled-filter", args[1]);
|
||||
}
|
||||
tc->reload_data();
|
||||
|
||||
retval = "info: filter now active";
|
||||
}
|
||||
@ -1214,7 +1184,7 @@ static string com_delete_filter(exec_context &ec, string cmdline, vector<string>
|
||||
args.emplace_back("all-filters");
|
||||
}
|
||||
else if (args.size() > 1) {
|
||||
textview_curses *tc = lnav_data.ld_view_stack.back();
|
||||
textview_curses *tc = *lnav_data.ld_view_stack.top();
|
||||
text_sub_source *tss = tc->get_sub_source();
|
||||
filter_stack &fs = tss->get_filters();
|
||||
|
||||
@ -1222,7 +1192,6 @@ static string com_delete_filter(exec_context &ec, string cmdline, vector<string>
|
||||
if (fs.delete_filter(args[1])) {
|
||||
retval = "info: deleted filter";
|
||||
tss->text_filters_changed();
|
||||
redo_search(lnav_view_t(tc - lnav_data.ld_views));
|
||||
}
|
||||
else {
|
||||
retval = "error: unknown filter -- " + args[1];
|
||||
@ -1240,7 +1209,7 @@ static string com_enable_filter(exec_context &ec, string cmdline, vector<string>
|
||||
args.emplace_back("disabled-filter");
|
||||
}
|
||||
else if (args.size() > 1) {
|
||||
textview_curses *tc = lnav_data.ld_view_stack.back();
|
||||
textview_curses *tc = *lnav_data.ld_view_stack.top();
|
||||
text_sub_source *tss = tc->get_sub_source();
|
||||
filter_stack &fs = tss->get_filters();
|
||||
shared_ptr<text_filter> lf;
|
||||
@ -1259,7 +1228,6 @@ static string com_enable_filter(exec_context &ec, string cmdline, vector<string>
|
||||
else {
|
||||
fs.set_filter_enabled(lf, true);
|
||||
tss->text_filters_changed();
|
||||
redo_search(lnav_view_t(tc - lnav_data.ld_views));
|
||||
retval = "info: filter enabled";
|
||||
}
|
||||
}
|
||||
@ -1275,7 +1243,7 @@ static string com_disable_filter(exec_context &ec, string cmdline, vector<string
|
||||
args.emplace_back("enabled-filter");
|
||||
}
|
||||
else if (args.size() > 1) {
|
||||
textview_curses *tc = lnav_data.ld_view_stack.back();
|
||||
textview_curses *tc = *lnav_data.ld_view_stack.top();
|
||||
text_sub_source *tss = tc->get_sub_source();
|
||||
filter_stack &fs = tss->get_filters();
|
||||
shared_ptr<text_filter> lf;
|
||||
@ -1294,7 +1262,6 @@ static string com_disable_filter(exec_context &ec, string cmdline, vector<string
|
||||
else {
|
||||
fs.set_filter_enabled(lf, false);
|
||||
tss->text_filters_changed();
|
||||
redo_search(lnav_view_t(tc - lnav_data.ld_views));
|
||||
retval = "info: filter disabled";
|
||||
}
|
||||
}
|
||||
@ -1446,7 +1413,7 @@ static string com_create_search_table(exec_context &ec, string cmdline, vector<s
|
||||
regex = remaining_args(cmdline, args, 2);
|
||||
}
|
||||
else {
|
||||
regex = lnav_data.ld_last_search[LNV_LOG];
|
||||
regex = lnav_data.ld_views[LNV_LOG].get_last_search();
|
||||
}
|
||||
|
||||
if ((code = pcre_compile(regex.c_str(),
|
||||
@ -1843,7 +1810,7 @@ static string com_close(exec_context &ec, string cmdline, vector<string> &args)
|
||||
|
||||
}
|
||||
else {
|
||||
textview_curses *tc = lnav_data.ld_view_stack.back();
|
||||
textview_curses *tc = *lnav_data.ld_view_stack.top();
|
||||
string fn;
|
||||
|
||||
if (tc == &lnav_data.ld_views[LNV_TEXT]) {
|
||||
@ -1857,7 +1824,7 @@ static string com_close(exec_context &ec, string cmdline, vector<string> &args)
|
||||
tss.current_file()->close();
|
||||
|
||||
if (tss.size() == 1) {
|
||||
lnav_data.ld_view_stack.pop_back();
|
||||
lnav_data.ld_view_stack.vs_views.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1904,7 +1871,7 @@ static string com_comment(exec_context &ec, string cmdline, vector<string> &args
|
||||
if (ec.ec_dry_run) {
|
||||
return "";
|
||||
}
|
||||
textview_curses *tc = lnav_data.ld_view_stack.back();
|
||||
textview_curses *tc = *lnav_data.ld_view_stack.top();
|
||||
|
||||
if (tc != &lnav_data.ld_views[LNV_LOG]) {
|
||||
return "error: The :comment command only works in the log view";
|
||||
@ -1936,7 +1903,7 @@ static string com_clear_comment(exec_context &ec, string cmdline, vector<string>
|
||||
else if (ec.ec_dry_run) {
|
||||
return "";
|
||||
} else {
|
||||
textview_curses *tc = lnav_data.ld_view_stack.back();
|
||||
textview_curses *tc = *lnav_data.ld_view_stack.top();
|
||||
|
||||
if (tc != &lnav_data.ld_views[LNV_LOG]) {
|
||||
return "error: The :clear-comment command only works in the log view";
|
||||
@ -1956,6 +1923,7 @@ static string com_clear_comment(exec_context &ec, string cmdline, vector<string>
|
||||
|
||||
retval = "info: cleared comment";
|
||||
}
|
||||
tc->search_new_data();
|
||||
}
|
||||
|
||||
return retval;
|
||||
@ -1973,7 +1941,7 @@ static string com_tag(exec_context &ec, string cmdline, vector<string> &args)
|
||||
if (ec.ec_dry_run) {
|
||||
return "";
|
||||
}
|
||||
textview_curses *tc = lnav_data.ld_view_stack.back();
|
||||
textview_curses *tc = *lnav_data.ld_view_stack.top();
|
||||
|
||||
if (tc != &lnav_data.ld_views[LNV_LOG]) {
|
||||
return "error: The :tag command only works in the log view";
|
||||
@ -1992,6 +1960,7 @@ static string com_tag(exec_context &ec, string cmdline, vector<string> &args)
|
||||
bookmark_metadata::KNOWN_TAGS.insert(tag);
|
||||
line_meta.add_tag(tag);
|
||||
}
|
||||
tc->search_new_data();
|
||||
|
||||
retval = "info: tag(s) added to line";
|
||||
}
|
||||
@ -2011,7 +1980,7 @@ static string com_untag(exec_context &ec, string cmdline, vector<string> &args)
|
||||
if (ec.ec_dry_run) {
|
||||
return "";
|
||||
}
|
||||
textview_curses *tc = lnav_data.ld_view_stack.back();
|
||||
textview_curses *tc = *lnav_data.ld_view_stack.top();
|
||||
|
||||
if (tc != &lnav_data.ld_views[LNV_LOG]) {
|
||||
return "error: The :untag command only works in the log view";
|
||||
@ -2035,6 +2004,7 @@ static string com_untag(exec_context &ec, string cmdline, vector<string> &args)
|
||||
tc->set_user_mark(&textview_curses::BM_META, tc->get_top(), false);
|
||||
}
|
||||
}
|
||||
tc->search_new_data();
|
||||
|
||||
retval = "info: tag(s) removed from line";
|
||||
}
|
||||
@ -2054,7 +2024,7 @@ static string com_delete_tags(exec_context &ec, string cmdline, vector<string> &
|
||||
if (ec.ec_dry_run) {
|
||||
return "";
|
||||
}
|
||||
textview_curses *tc = lnav_data.ld_view_stack.back();
|
||||
textview_curses *tc = *lnav_data.ld_view_stack.top();
|
||||
|
||||
if (tc != &lnav_data.ld_views[LNV_LOG]) {
|
||||
return "error: The :delete-tag command only works in the log view";
|
||||
@ -2475,7 +2445,7 @@ static string com_add_test(exec_context &ec, string cmdline, vector<string> &arg
|
||||
|
||||
}
|
||||
else {
|
||||
textview_curses *tc = lnav_data.ld_view_stack.back();
|
||||
textview_curses *tc = *lnav_data.ld_view_stack.top();
|
||||
|
||||
bookmark_vector<vis_line_t> &bv =
|
||||
tc->get_bookmarks()[&textview_curses::BM_USER];
|
||||
@ -2581,9 +2551,7 @@ static string com_zoom_to(exec_context &ec, string cmdline, vector<string> &args
|
||||
old_time)));
|
||||
}
|
||||
|
||||
if (!lnav_data.ld_view_stack.empty()) {
|
||||
lnav_data.ld_view_stack.back()->set_needs_update();
|
||||
}
|
||||
lnav_data.ld_view_stack.set_needs_update();
|
||||
|
||||
found = true;
|
||||
}
|
||||
@ -2670,7 +2638,7 @@ static string com_toggle_field(exec_context &ec, string cmdline, vector<string>
|
||||
} else if (args.size() < 2) {
|
||||
retval = "error: Expecting a log message field name";
|
||||
} else {
|
||||
textview_curses *tc = lnav_data.ld_view_stack.back();
|
||||
textview_curses *tc = *lnav_data.ld_view_stack.top();
|
||||
|
||||
if (tc != &lnav_data.ld_views[LNV_LOG]) {
|
||||
retval = "error: hiding fields only works in the log view";
|
||||
@ -2749,7 +2717,7 @@ static string com_hide_line(exec_context &ec, string cmdline, vector<string> &ar
|
||||
args.emplace_back("move-time");
|
||||
}
|
||||
else if (args.size() == 1) {
|
||||
textview_curses *tc = lnav_data.ld_view_stack.back();
|
||||
textview_curses *tc = *lnav_data.ld_view_stack.top();
|
||||
logfile_sub_source &lss = lnav_data.ld_log_source;
|
||||
|
||||
if (tc == &lnav_data.ld_views[LNV_LOG]) {
|
||||
@ -2786,7 +2754,7 @@ static string com_hide_line(exec_context &ec, string cmdline, vector<string> &ar
|
||||
}
|
||||
else if (args.size() >= 2) {
|
||||
string all_args = remaining_args(cmdline, args);
|
||||
textview_curses *tc = lnav_data.ld_view_stack.back();
|
||||
textview_curses *tc = *lnav_data.ld_view_stack.top();
|
||||
logfile_sub_source &lss = lnav_data.ld_log_source;
|
||||
date_time_scanner dts;
|
||||
struct timeval tv;
|
||||
@ -2857,7 +2825,7 @@ static string com_show_lines(exec_context &ec, string cmdline, vector<string> &a
|
||||
}
|
||||
else if (!args.empty()) {
|
||||
logfile_sub_source &lss = lnav_data.ld_log_source;
|
||||
textview_curses *tc = lnav_data.ld_view_stack.back();
|
||||
textview_curses *tc = *lnav_data.ld_view_stack.top();
|
||||
|
||||
if (tc == &lnav_data.ld_views[LNV_LOG]) {
|
||||
lss.clear_min_max_log_times();
|
||||
@ -2876,7 +2844,7 @@ static string com_hide_unmarked(exec_context &ec, string cmdline, vector<string>
|
||||
} else if (ec.ec_dry_run) {
|
||||
retval = "";
|
||||
} else {
|
||||
textview_curses * tc = lnav_data.ld_view_stack.back();
|
||||
textview_curses * tc = *lnav_data.ld_view_stack.top();
|
||||
bookmark_vector<vis_line_t> &bv =
|
||||
tc->get_bookmarks()[&textview_curses::BM_USER];
|
||||
|
||||
@ -3526,7 +3494,7 @@ static string com_spectrogram(exec_context &ec, string cmdline, vector<string> &
|
||||
}
|
||||
ss.invalidate();
|
||||
|
||||
if (lnav_data.ld_view_stack.back() == &lnav_data.ld_views[LNV_DB]) {
|
||||
if (*lnav_data.ld_view_stack.top() == &lnav_data.ld_views[LNV_DB]) {
|
||||
unique_ptr<db_spectro_value_source> dsvs(
|
||||
new db_spectro_value_source(colname));
|
||||
|
||||
|
@ -38,6 +38,9 @@
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <libgen.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "pcrecpp.h"
|
||||
|
||||
@ -166,6 +169,47 @@ void install_git_format(const char *repo)
|
||||
git_cmd.wait_for_child();
|
||||
}
|
||||
|
||||
bool update_git_formats()
|
||||
{
|
||||
static_root_mem<glob_t, globfree> gl;
|
||||
string formats_path = dotlnav_path("formats/");
|
||||
string git_formats = formats_path + "*/.git";
|
||||
bool found = false, retval = true;
|
||||
|
||||
if (glob(git_formats.c_str(), GLOB_NOCHECK, NULL, gl.inout()) == 0) {
|
||||
for (int lpc = 0; lpc < (int) gl->gl_pathc; lpc++) {
|
||||
char *git_dir = dirname(gl->gl_pathv[lpc]);
|
||||
char pull_cmd[1024];
|
||||
|
||||
printf("Updating formats in %s\n", git_dir);
|
||||
snprintf(pull_cmd, sizeof(pull_cmd),
|
||||
"cd %s && git pull",
|
||||
git_dir);
|
||||
int ret = system(pull_cmd);
|
||||
if (ret == -1) {
|
||||
std::cerr << "Failed to spawn command "
|
||||
<< "\"" << pull_cmd << "\": "
|
||||
<< strerror(errno) << std::endl;
|
||||
retval = false;
|
||||
}
|
||||
else if (ret > 0) {
|
||||
std::cerr << "Command "
|
||||
<< "\"" << pull_cmd << "\" failed: "
|
||||
<< strerror(errno) << std::endl;
|
||||
retval = false;
|
||||
}
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
printf("No formats from git repositories found, "
|
||||
"use 'lnav -i extra' to install third-party foramts\n");
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int read_repo_path(yajlpp_parse_context *ypc, const unsigned char *str, size_t len)
|
||||
{
|
||||
string path = string((const char *)str, len);
|
||||
|
@ -81,9 +81,10 @@ bool check_experimental(const char *feature_name);
|
||||
/**
|
||||
* Ensure that the '.lnav' directory exists.
|
||||
*/
|
||||
void ensure_dotlnav(void);
|
||||
void ensure_dotlnav();
|
||||
|
||||
void install_git_format(const char *repo);
|
||||
bool update_git_formats();
|
||||
|
||||
void install_extra_formats();
|
||||
|
||||
|
@ -47,9 +47,11 @@
|
||||
#include <sstream>
|
||||
#include <numeric>
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
|
||||
#include "ptimec.hh"
|
||||
#include "byte_array.hh"
|
||||
#include "optional.hpp"
|
||||
|
||||
inline std::string trim(const std::string &str)
|
||||
{
|
||||
@ -491,4 +493,34 @@ inline void rusageadd(const struct rusage &left, const struct rusage &right, str
|
||||
|
||||
size_t abbreviate_str(char *str, size_t len, size_t max_len);
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <class T>
|
||||
typename std::enable_if<std::is_void<T>::value, T>::type
|
||||
void_or_nullopt()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
typename std::enable_if<not std::is_void<T>::value, T>::type
|
||||
void_or_nullopt()
|
||||
{
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
struct is_optional : std::false_type {};
|
||||
|
||||
template <class T>
|
||||
struct is_optional<nonstd::optional<T>> : std::true_type {};
|
||||
}
|
||||
|
||||
template <class T, class F, std::enable_if_t<detail::is_optional<std::decay_t<T>>::value, int> = 0>
|
||||
auto operator|(T&& t, F f) -> decltype(detail::void_or_nullopt<decltype(f(std::forward<T>(t).operator*()))>()) {
|
||||
using return_type = decltype(f(std::forward<T>(t).operator*()));
|
||||
if (t) return f(std::forward<T>(t).operator*());
|
||||
else return detail::void_or_nullopt<return_type>();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
223
src/log_actions.cc
Normal file
223
src/log_actions.cc
Normal file
@ -0,0 +1,223 @@
|
||||
/**
|
||||
* Copyright (c) 2018, 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 "lnav.hh"
|
||||
#include "log_actions.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
static string execute_action(log_data_helper &ldh,
|
||||
int value_index,
|
||||
const string &action_name)
|
||||
{
|
||||
std::map<string, log_format::action_def>::const_iterator iter;
|
||||
logline_value &lv = ldh.ldh_line_values[value_index];
|
||||
shared_ptr<logfile> lf = ldh.ldh_file;
|
||||
const log_format *format = lf->get_format();
|
||||
pid_t child_pid;
|
||||
string retval;
|
||||
|
||||
iter = format->lf_action_defs.find(action_name);
|
||||
|
||||
const log_format::action_def &action = iter->second;
|
||||
|
||||
auto_pipe in_pipe(STDIN_FILENO);
|
||||
auto_pipe out_pipe(STDOUT_FILENO);
|
||||
auto_pipe err_pipe(STDERR_FILENO);
|
||||
|
||||
in_pipe.open();
|
||||
if (action.ad_capture_output)
|
||||
out_pipe.open();
|
||||
err_pipe.open();
|
||||
|
||||
child_pid = fork();
|
||||
|
||||
in_pipe.after_fork(child_pid);
|
||||
out_pipe.after_fork(child_pid);
|
||||
err_pipe.after_fork(child_pid);
|
||||
|
||||
switch (child_pid) {
|
||||
case -1:
|
||||
retval = "error: unable to fork child process -- " + string(strerror(errno));
|
||||
break;
|
||||
case 0: {
|
||||
const char *args[action.ad_cmdline.size() + 1];
|
||||
set<std::string> path_set(format->get_source_path());
|
||||
char env_buffer[64];
|
||||
int value_line;
|
||||
string path;
|
||||
|
||||
setenv("LNAV_ACTION_FILE", lf->get_filename().c_str(), 1);
|
||||
snprintf(env_buffer, sizeof(env_buffer),
|
||||
"%ld",
|
||||
(ldh.ldh_line - lf->begin()) + 1);
|
||||
setenv("LNAV_ACTION_FILE_LINE", env_buffer, 1);
|
||||
snprintf(env_buffer, sizeof(env_buffer), "%d", ldh.ldh_y_offset + 1);
|
||||
setenv("LNAV_ACTION_MSG_LINE", env_buffer, 1);
|
||||
setenv("LNAV_ACTION_VALUE_NAME", lv.lv_name.get(), 1);
|
||||
value_line = ldh.ldh_y_offset - ldh.get_value_line(lv) + 1;
|
||||
snprintf(env_buffer, sizeof(env_buffer), "%d", value_line);
|
||||
setenv("LNAV_ACTION_VALUE_LINE", env_buffer, 1);
|
||||
|
||||
for (const auto &path_iter : path_set) {
|
||||
if (!path.empty()) {
|
||||
path += ":";
|
||||
}
|
||||
path += path_iter;
|
||||
}
|
||||
path += ":" + string(getenv("PATH"));
|
||||
setenv("PATH", path.c_str(), 1);
|
||||
for (size_t lpc = 0; lpc < action.ad_cmdline.size(); lpc++) {
|
||||
args[lpc] = action.ad_cmdline[lpc].c_str();
|
||||
}
|
||||
args[action.ad_cmdline.size()] = NULL;
|
||||
execvp(args[0], (char *const *) args);
|
||||
fprintf(stderr,
|
||||
"error: could not exec process -- %s:%s\n",
|
||||
args[0],
|
||||
strerror(errno));
|
||||
_exit(0);
|
||||
}
|
||||
break;
|
||||
default: {
|
||||
static int exec_count = 0;
|
||||
|
||||
string value = lv.to_string();
|
||||
line_buffer lb;
|
||||
off_t off = 0;
|
||||
line_value lv;
|
||||
|
||||
lnav_data.ld_children.push_back(child_pid);
|
||||
|
||||
if (write(in_pipe.write_end(), value.c_str(), value.size()) == -1) {
|
||||
perror("execute_action write");
|
||||
}
|
||||
in_pipe.close();
|
||||
|
||||
lb.set_fd(err_pipe.read_end());
|
||||
|
||||
lb.read_line(off, lv);
|
||||
|
||||
if (out_pipe.read_end() != -1) {
|
||||
auto pp = make_shared<piper_proc>(out_pipe.read_end(), false);
|
||||
char desc[128];
|
||||
|
||||
lnav_data.ld_pipers.push_back(pp);
|
||||
snprintf(desc,
|
||||
sizeof(desc), "[%d] Output of %s",
|
||||
exec_count++,
|
||||
action.ad_cmdline[0].c_str());
|
||||
lnav_data.ld_file_names[desc]
|
||||
.with_fd(pp->get_fd());
|
||||
lnav_data.ld_files_to_front.push_back({ desc, 0 });
|
||||
}
|
||||
|
||||
retval = string(lv.lv_start, lv.lv_len);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool action_delegate::text_handle_mouse(textview_curses &tc, mouse_event &me)
|
||||
{
|
||||
bool retval = false;
|
||||
|
||||
if (me.me_button != BUTTON_LEFT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
vis_line_t mouse_line = vis_line_t(tc.get_top() + me.me_y);
|
||||
int mouse_left = tc.get_left() + me.me_x;
|
||||
|
||||
switch (me.me_state) {
|
||||
case BUTTON_STATE_PRESSED:
|
||||
if (mouse_line >= vis_line_t(0) && mouse_line <= tc.get_bottom()) {
|
||||
size_t line_end_index = 0;
|
||||
int x_offset;
|
||||
|
||||
this->ad_press_line = mouse_line;
|
||||
this->ad_log_helper.parse_line(mouse_line, true);
|
||||
|
||||
this->ad_log_helper.get_line_bounds(this->ad_line_index, line_end_index);
|
||||
|
||||
struct line_range lr(this->ad_line_index, line_end_index);
|
||||
|
||||
this->ad_press_value = -1;
|
||||
|
||||
x_offset = this->ad_line_index + mouse_left;
|
||||
if (lr.contains(x_offset)) {
|
||||
for (size_t lpc = 0;
|
||||
lpc < this->ad_log_helper.ldh_line_values.size();
|
||||
lpc++) {
|
||||
logline_value &lv = this->ad_log_helper.ldh_line_values[lpc];
|
||||
|
||||
if (lv.lv_origin.contains(x_offset)) {
|
||||
this->ad_press_value = lpc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case BUTTON_STATE_DRAGGED:
|
||||
if (mouse_line != this->ad_press_line) {
|
||||
this->ad_press_value = -1;
|
||||
}
|
||||
if (this->ad_press_value != -1) {
|
||||
retval = true;
|
||||
}
|
||||
break;
|
||||
case BUTTON_STATE_RELEASED:
|
||||
if (this->ad_press_value != -1 && this->ad_press_line == mouse_line) {
|
||||
logline_value &lv = this->ad_log_helper.ldh_line_values[this->ad_press_value];
|
||||
int x_offset = this->ad_line_index + mouse_left;
|
||||
|
||||
if (lv.lv_origin.contains(x_offset)) {
|
||||
shared_ptr<logfile> lf = this->ad_log_helper.ldh_file;
|
||||
const vector<string> *actions;
|
||||
|
||||
actions = lf->get_format()->get_actions(lv);
|
||||
if (actions != NULL && !actions->empty()) {
|
||||
string rc = execute_action(
|
||||
this->ad_log_helper, this->ad_press_value, actions->at(0));
|
||||
|
||||
lnav_data.ld_rl_view->set_value(rc);
|
||||
}
|
||||
}
|
||||
retval = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
54
src/log_actions.hh
Normal file
54
src/log_actions.hh
Normal file
@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright (c) 2018, 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 log_actions_hh
|
||||
#define log_actions_hh
|
||||
|
||||
#include "logfile_sub_source.hh"
|
||||
#include "log_data_helper.hh"
|
||||
|
||||
class action_delegate : public text_delegate {
|
||||
public:
|
||||
action_delegate(logfile_sub_source &lss)
|
||||
: ad_log_helper(lss),
|
||||
ad_press_line(-1),
|
||||
ad_press_value(-1),
|
||||
ad_line_index(0) {
|
||||
|
||||
};
|
||||
|
||||
virtual bool text_handle_mouse(textview_curses &tc, mouse_event &me);;
|
||||
|
||||
log_data_helper ad_log_helper;
|
||||
vis_line_t ad_press_line;
|
||||
int ad_press_value;
|
||||
size_t ad_line_index;
|
||||
};
|
||||
|
||||
#endif
|
@ -735,6 +735,17 @@ logfile_sub_source::rebuild_result logfile_sub_source::rebuild_index()
|
||||
}
|
||||
}
|
||||
|
||||
switch (retval) {
|
||||
case rebuild_result::rr_no_change:
|
||||
break;
|
||||
case rebuild_result::rr_full_rebuild:
|
||||
this->tss_view->redo_search();
|
||||
break;
|
||||
case rebuild_result::rr_appended_lines:
|
||||
this->tss_view->search_new_data();
|
||||
break;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
@ -861,6 +872,9 @@ void logfile_sub_source::text_filters_changed()
|
||||
if (this->lss_index_delegate != nullptr) {
|
||||
this->lss_index_delegate->index_complete(*this);
|
||||
}
|
||||
|
||||
this->tss_view->reload_data();
|
||||
this->tss_view->redo_search();
|
||||
}
|
||||
|
||||
bool logfile_sub_source::list_input_handle_key(listview_curses &lv, int ch)
|
||||
@ -888,3 +902,12 @@ bool logfile_sub_source::list_input_handle_key(listview_curses &lv, int ch)
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
nonstd::optional<pair<grep_proc_source<vis_line_t> *, grep_proc_sink<vis_line_t> *>>
|
||||
logfile_sub_source::get_grepper()
|
||||
{
|
||||
return make_pair(
|
||||
(grep_proc_source<vis_line_t> *) &this->lss_meta_grepper,
|
||||
(grep_proc_sink<vis_line_t> *) &this->lss_meta_grepper
|
||||
);
|
||||
}
|
||||
|
@ -71,6 +71,32 @@ public:
|
||||
};
|
||||
};
|
||||
|
||||
class pcre_filter
|
||||
: public text_filter {
|
||||
public:
|
||||
pcre_filter(type_t type, const std::string id, size_t index, pcre *code)
|
||||
: text_filter(type, id, index),
|
||||
pf_pcre(code) { };
|
||||
|
||||
~pcre_filter() override { };
|
||||
|
||||
bool matches(const logfile &lf, const logline &ll, shared_buffer_ref &line) override {
|
||||
pcre_context_static<30> pc;
|
||||
pcre_input pi(line.get_data(), 0, line.length());
|
||||
|
||||
return this->pf_pcre.match(pc, pi);
|
||||
};
|
||||
|
||||
std::string to_command() override {
|
||||
return (this->lf_type == text_filter::INCLUDE ?
|
||||
"filter-in " : "filter-out ") +
|
||||
this->lf_id;
|
||||
};
|
||||
|
||||
protected:
|
||||
pcrepp pf_pcre;
|
||||
};
|
||||
|
||||
/**
|
||||
* Delegate class that merges the contents of multiple log files into a single
|
||||
* source of data for a text view.
|
||||
@ -295,7 +321,8 @@ public:
|
||||
}
|
||||
if (bm == &textview_curses::BM_META &&
|
||||
this->lss_meta_grepper.gps_proc != nullptr) {
|
||||
this->lss_meta_grepper.gps_proc->queue_request(line, line + 1_vl);
|
||||
this->tss_view->search_range(line, line + 1_vl);
|
||||
this->tss_view->search_new_data();
|
||||
}
|
||||
};
|
||||
|
||||
@ -478,7 +505,7 @@ public:
|
||||
* logfile have been indexed.
|
||||
*/
|
||||
struct logfile_data {
|
||||
logfile_data(size_t index, filter_stack &fs, std::shared_ptr<logfile> lf)
|
||||
logfile_data(size_t index, filter_stack &fs, const std::shared_ptr<logfile> &lf)
|
||||
: ld_file_index(index),
|
||||
ld_filter_state(fs, lf),
|
||||
ld_lines_indexed(0),
|
||||
@ -644,9 +671,8 @@ public:
|
||||
bool lmg_done{false};
|
||||
};
|
||||
|
||||
meta_grepper &get_meta_grepper() {
|
||||
return this->lss_meta_grepper;
|
||||
}
|
||||
nonstd::optional<std::pair<grep_proc_source<vis_line_t> *, grep_proc_sink<vis_line_t> *>>
|
||||
get_grepper();
|
||||
|
||||
static const uint64_t MAX_CONTENT_LINES = (1ULL << 40) - 1;
|
||||
static const uint64_t MAX_LINES_PER_FILE = 256 * 1024 * 1024;
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "lnav.hh"
|
||||
#include "lnav_util.hh"
|
||||
#include "sysclip.hh"
|
||||
#include "vtab_module.hh"
|
||||
#include "plain_text_source.hh"
|
||||
#include "command_executor.hh"
|
||||
#include "readline_curses.hh"
|
||||
@ -48,7 +49,7 @@ static const char *LNAV_CMD_PROMPT = "Enter an lnav command: " ABORT_MSG;
|
||||
|
||||
void rl_change(void *dummy, readline_curses *rc)
|
||||
{
|
||||
textview_curses *tc = lnav_data.ld_view_stack.back();
|
||||
textview_curses *tc = *lnav_data.ld_view_stack.top();
|
||||
|
||||
tc->get_highlights().erase("$preview");
|
||||
tc->get_highlights().erase("$bodypreview");
|
||||
@ -59,7 +60,7 @@ void rl_change(void *dummy, readline_curses *rc)
|
||||
case LNM_COMMAND: {
|
||||
string line = rc->get_line_buffer();
|
||||
vector<string> args;
|
||||
readline_context::command_map_t::iterator iter = lnav_commands.end();
|
||||
auto iter = lnav_commands.end();
|
||||
|
||||
split_ws(line, args);
|
||||
|
||||
@ -155,7 +156,7 @@ void rl_change(void *dummy, readline_curses *rc)
|
||||
|
||||
static void rl_search_internal(void *dummy, readline_curses *rc, bool complete = false)
|
||||
{
|
||||
textview_curses *tc = lnav_data.ld_view_stack.back();
|
||||
textview_curses *tc = *lnav_data.ld_view_stack.top();
|
||||
string term_val;
|
||||
string name;
|
||||
|
||||
@ -200,7 +201,7 @@ static void rl_search_internal(void *dummy, readline_curses *rc, bool complete =
|
||||
case LNM_SQL: {
|
||||
term_val = trim(rc->get_value() + ";");
|
||||
|
||||
if (term_val.size() > 0 && term_val[0] == '.') {
|
||||
if (!term_val.empty() && term_val[0] == '.') {
|
||||
lnav_data.ld_bottom_source.grep_error("");
|
||||
} else if (!sqlite3_complete(term_val.c_str())) {
|
||||
lnav_data.ld_bottom_source.
|
||||
@ -316,14 +317,16 @@ static void rl_search_internal(void *dummy, readline_curses *rc, bool complete =
|
||||
auto ident_iter = find_string_attr_containing(sa, &SQL_IDENTIFIER_ATTR, x);
|
||||
if (ident_iter != sa.end()) {
|
||||
string ident = al.get_substring(ident_iter->sa_range);
|
||||
intern_string_t intern_ident = intern_string::lookup(ident);
|
||||
|
||||
auto vtab = lnav_data.ld_vtab_manager->lookup_impl(
|
||||
intern_string::lookup(ident));
|
||||
auto vtab = lnav_data.ld_vtab_manager->lookup_impl(intern_ident);
|
||||
auto vtab_module_iter = vtab_module_ddls.find(intern_ident);
|
||||
string ddl;
|
||||
|
||||
if (vtab != nullptr) {
|
||||
ddl = trim(vtab->get_table_statement());
|
||||
|
||||
} else if (vtab_module_iter != vtab_module_ddls.end()) {
|
||||
ddl = vtab_module_iter->second;
|
||||
} else {
|
||||
auto table_ddl_iter = lnav_data.ld_table_ddl.find(ident);
|
||||
|
||||
@ -358,17 +361,15 @@ static void rl_search_internal(void *dummy, readline_curses *rc, bool complete =
|
||||
break;
|
||||
}
|
||||
|
||||
lnav_view_t index = (lnav_view_t)(tc - lnav_data.ld_views);
|
||||
|
||||
if (!complete) {
|
||||
tc->set_top(lnav_data.ld_search_start_line);
|
||||
}
|
||||
execute_search(index, rc->get_value());
|
||||
tc->execute_search(rc->get_value());
|
||||
}
|
||||
|
||||
void rl_search(void *dummy, readline_curses *rc)
|
||||
{
|
||||
textview_curses *tc = lnav_data.ld_view_stack.back();
|
||||
textview_curses *tc = *lnav_data.ld_view_stack.top();
|
||||
|
||||
rl_search_internal(dummy, rc);
|
||||
tc->set_follow_search_for(0);
|
||||
@ -376,8 +377,7 @@ void rl_search(void *dummy, readline_curses *rc)
|
||||
|
||||
void rl_abort(void *dummy, readline_curses *rc)
|
||||
{
|
||||
textview_curses *tc = lnav_data.ld_view_stack.back();
|
||||
lnav_view_t index = (lnav_view_t)(tc - lnav_data.ld_views);
|
||||
textview_curses *tc = *lnav_data.ld_view_stack.top();
|
||||
|
||||
lnav_data.ld_bottom_source.set_prompt("");
|
||||
lnav_data.ld_example_source.clear();
|
||||
@ -391,7 +391,7 @@ void rl_abort(void *dummy, readline_curses *rc)
|
||||
switch (lnav_data.ld_mode) {
|
||||
case LNM_SEARCH:
|
||||
tc->set_top(lnav_data.ld_search_start_line);
|
||||
execute_search(index, lnav_data.ld_previous_search);
|
||||
tc->execute_search(lnav_data.ld_previous_search);
|
||||
break;
|
||||
case LNM_SQL:
|
||||
tc->reload_data();
|
||||
@ -405,7 +405,7 @@ void rl_abort(void *dummy, readline_curses *rc)
|
||||
|
||||
void rl_callback(void *dummy, readline_curses *rc)
|
||||
{
|
||||
textview_curses *tc = lnav_data.ld_view_stack.back();
|
||||
textview_curses *tc = *lnav_data.ld_view_stack.top();
|
||||
exec_context &ec = lnav_data.ld_exec_context;
|
||||
string alt_msg;
|
||||
|
||||
@ -418,6 +418,7 @@ void rl_callback(void *dummy, readline_curses *rc)
|
||||
tc->get_highlights().erase("$bodypreview");
|
||||
switch (lnav_data.ld_mode) {
|
||||
case LNM_PAGING:
|
||||
case LNM_FILTER:
|
||||
require(0);
|
||||
break;
|
||||
|
||||
@ -431,13 +432,13 @@ void rl_callback(void *dummy, readline_curses *rc)
|
||||
rl_search_internal(dummy, rc, true);
|
||||
if (rc->get_value().size() > 0) {
|
||||
auto_mem<FILE> pfile(pclose);
|
||||
textview_curses *tc = lnav_data.ld_view_stack.back();
|
||||
textview_curses *tc = *lnav_data.ld_view_stack.top();
|
||||
vis_bookmarks &bm = tc->get_bookmarks();
|
||||
const auto &bv = bm[&textview_curses::BM_SEARCH];
|
||||
vis_line_t vl = bv.next(tc->get_top());
|
||||
|
||||
pfile = open_clipboard(CT_FIND);
|
||||
if (pfile.in() != NULL) {
|
||||
if (pfile.in() != nullptr) {
|
||||
fprintf(pfile, "%s", rc->get_value().c_str());
|
||||
}
|
||||
if (vl != -1_vl) {
|
||||
@ -544,7 +545,7 @@ void rl_display_matches(void *dummy, readline_curses *rc)
|
||||
attr_line_t al;
|
||||
bool add_nl = false;
|
||||
|
||||
for (auto match : matches) {
|
||||
for (const auto &match : matches) {
|
||||
if (add_nl) {
|
||||
al.append(1, '\n');
|
||||
add_nl = false;
|
||||
|
@ -307,7 +307,7 @@ char **readline_context::attempted_completion(const char *text,
|
||||
int start,
|
||||
int end)
|
||||
{
|
||||
char **retval = NULL;
|
||||
char **retval = nullptr;
|
||||
|
||||
completion_start = start;
|
||||
if (start == 0 && loaded_context->rc_possibilities.find("__command") !=
|
||||
@ -385,67 +385,6 @@ readline_curses::readline_curses()
|
||||
rc_matches_remaining(0),
|
||||
rc_max_match_length(0)
|
||||
{
|
||||
struct winsize ws;
|
||||
int sp[2];
|
||||
|
||||
if (socketpair(PF_UNIX, SOCK_STREAM, 0, sp) < 0) {
|
||||
throw error(errno);
|
||||
}
|
||||
|
||||
this->rc_command_pipe[RCF_MASTER] = sp[RCF_MASTER];
|
||||
this->rc_command_pipe[RCF_SLAVE] = sp[RCF_SLAVE];
|
||||
|
||||
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1) {
|
||||
throw error(errno);
|
||||
}
|
||||
|
||||
if (openpty(this->rc_pty[RCF_MASTER].out(),
|
||||
this->rc_pty[RCF_SLAVE].out(),
|
||||
NULL,
|
||||
NULL,
|
||||
&ws) < 0) {
|
||||
perror("error: failed to open terminal(openpty)");
|
||||
throw error(errno);
|
||||
}
|
||||
|
||||
if ((this->rc_child = fork()) == -1) {
|
||||
throw error(errno);
|
||||
}
|
||||
|
||||
if (this->rc_child == 0) {
|
||||
char buffer[1024];
|
||||
|
||||
this->rc_command_pipe[RCF_MASTER].reset();
|
||||
this->rc_pty[RCF_MASTER].reset();
|
||||
|
||||
signal(SIGALRM, sigalrm);
|
||||
signal(SIGWINCH, sigwinch);
|
||||
signal(SIGINT, sigterm);
|
||||
signal(SIGTERM, sigterm);
|
||||
|
||||
dup2(this->rc_pty[RCF_SLAVE], STDIN_FILENO);
|
||||
dup2(this->rc_pty[RCF_SLAVE], STDOUT_FILENO);
|
||||
|
||||
setenv("TERM", "vt52", 1);
|
||||
|
||||
rl_initialize();
|
||||
using_history();
|
||||
stifle_history(HISTORY_SIZE);
|
||||
|
||||
rl_add_defun("rubout-char-or-abort", rubout_char_or_abort, '\b');
|
||||
// rl_add_defun("command-complete", readline_context::command_complete, ' ');
|
||||
|
||||
for (int lpc = 0; RL_INIT[lpc]; lpc++) {
|
||||
snprintf(buffer, sizeof(buffer), "%s", RL_INIT[lpc]);
|
||||
rl_parse_and_bind(buffer); /* NOTE: buffer is modified */
|
||||
}
|
||||
|
||||
child_this = this;
|
||||
}
|
||||
else {
|
||||
this->rc_command_pipe[RCF_SLAVE].reset();
|
||||
this->rc_pty[RCF_SLAVE].reset();
|
||||
}
|
||||
}
|
||||
|
||||
readline_curses::~readline_curses()
|
||||
@ -456,6 +395,7 @@ readline_curses::~readline_curses()
|
||||
else if (this->rc_child > 0) {
|
||||
int status;
|
||||
|
||||
log_debug("term child %d", this->rc_child);
|
||||
kill(this->rc_child, SIGTERM);
|
||||
this->rc_child = -1;
|
||||
|
||||
@ -501,12 +441,76 @@ void readline_curses::store_matches(
|
||||
}
|
||||
}
|
||||
|
||||
void readline_curses::start(void)
|
||||
void readline_curses::start()
|
||||
{
|
||||
if (this->rc_child != 0) {
|
||||
if (this->rc_child > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct winsize ws;
|
||||
int sp[2];
|
||||
|
||||
if (socketpair(PF_UNIX, SOCK_STREAM, 0, sp) < 0) {
|
||||
throw error(errno);
|
||||
}
|
||||
|
||||
this->rc_command_pipe[RCF_MASTER] = sp[RCF_MASTER];
|
||||
this->rc_command_pipe[RCF_SLAVE] = sp[RCF_SLAVE];
|
||||
|
||||
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1) {
|
||||
throw error(errno);
|
||||
}
|
||||
|
||||
if (openpty(this->rc_pty[RCF_MASTER].out(),
|
||||
this->rc_pty[RCF_SLAVE].out(),
|
||||
NULL,
|
||||
NULL,
|
||||
&ws) < 0) {
|
||||
perror("error: failed to open terminal(openpty)");
|
||||
throw error(errno);
|
||||
}
|
||||
|
||||
if ((this->rc_child = fork()) == -1) {
|
||||
throw error(errno);
|
||||
}
|
||||
|
||||
if (this->rc_child != 0) {
|
||||
this->rc_command_pipe[RCF_SLAVE].reset();
|
||||
this->rc_pty[RCF_SLAVE].reset();
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
char buffer[1024];
|
||||
|
||||
this->rc_command_pipe[RCF_MASTER].reset();
|
||||
this->rc_pty[RCF_MASTER].reset();
|
||||
|
||||
signal(SIGALRM, sigalrm);
|
||||
signal(SIGWINCH, sigwinch);
|
||||
signal(SIGINT, sigterm);
|
||||
signal(SIGTERM, sigterm);
|
||||
|
||||
dup2(this->rc_pty[RCF_SLAVE], STDIN_FILENO);
|
||||
dup2(this->rc_pty[RCF_SLAVE], STDOUT_FILENO);
|
||||
|
||||
setenv("TERM", "vt52", 1);
|
||||
|
||||
rl_initialize();
|
||||
using_history();
|
||||
stifle_history(HISTORY_SIZE);
|
||||
|
||||
rl_add_defun("rubout-char-or-abort", rubout_char_or_abort, '\b');
|
||||
// rl_add_defun("command-complete", readline_context::command_complete, ' ');
|
||||
|
||||
for (int lpc = 0; RL_INIT[lpc]; lpc++) {
|
||||
snprintf(buffer, sizeof(buffer), "%s", RL_INIT[lpc]);
|
||||
rl_parse_and_bind(buffer); /* NOTE: buffer is modified */
|
||||
}
|
||||
|
||||
child_this = this;
|
||||
}
|
||||
|
||||
map<int, readline_context *>::iterator current_context;
|
||||
int maxfd;
|
||||
|
||||
@ -566,6 +570,10 @@ void readline_curses::start(void)
|
||||
rl_last_func != rl_menu_complete &&
|
||||
rl_last_func != rl_backward_menu_complete);
|
||||
|
||||
if (complete_done) {
|
||||
last_match_str_valid = false;
|
||||
}
|
||||
|
||||
if (h1 == last_h1 && h2 == last_h2) {
|
||||
// do nothing
|
||||
} else if (sendcmd(this->rc_command_pipe[RCF_SLAVE],
|
||||
@ -592,10 +600,19 @@ void readline_curses::start(void)
|
||||
char type[32];
|
||||
|
||||
msg[rc] = '\0';
|
||||
if (sscanf(msg, "f:%d:%n", &context, &prompt_start) == 1 &&
|
||||
prompt_start != 0 &&
|
||||
(current_context = this->rc_contexts.find(context)) !=
|
||||
this->rc_contexts.end()) {
|
||||
if (msg[0] == 'i') {
|
||||
const char *initial = &msg[2];
|
||||
|
||||
rl_extend_line_buffer(strlen(initial) + 1);
|
||||
strcpy(rl_line_buffer, initial);
|
||||
rl_point = 0;
|
||||
rl_end = strlen(initial);
|
||||
rl_redisplay();
|
||||
}
|
||||
else if (sscanf(msg, "f:%d:%n", &context, &prompt_start) == 1 &&
|
||||
prompt_start != 0 &&
|
||||
(current_context = this->rc_contexts.find(context)) !=
|
||||
this->rc_contexts.end()) {
|
||||
current_context->second->load();
|
||||
rl_callback_handler_install(&msg[prompt_start],
|
||||
line_ready_tramp);
|
||||
@ -719,6 +736,18 @@ void readline_curses::line_ready(const char *line)
|
||||
char msg[1024] = {0};
|
||||
int rc;
|
||||
|
||||
if (line == nullptr) {
|
||||
snprintf(msg, sizeof(msg), "a");
|
||||
|
||||
if (sendstring(this->rc_command_pipe[RCF_SLAVE],
|
||||
msg,
|
||||
strlen(msg)) == -1) {
|
||||
perror("abort: write failed");
|
||||
_exit(1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (rl_line_buffer[0] == '^') {
|
||||
rc = -1;
|
||||
}
|
||||
@ -757,7 +786,7 @@ void readline_curses::line_ready(const char *line)
|
||||
{
|
||||
HIST_ENTRY *entry;
|
||||
|
||||
if (line[0] != '\0' && (
|
||||
if (line != nullptr && line[0] != '\0' && (
|
||||
history_length == 0 ||
|
||||
(entry = history_get(history_base + history_length - 1)) == NULL ||
|
||||
strcmp(entry->line, line) != 0)) {
|
||||
@ -792,7 +821,7 @@ void readline_curses::check_poll_set(const vector<struct pollfd> &pollfds)
|
||||
|
||||
msg[rc] = '\0';
|
||||
if (this->rc_matches_remaining > 0) {
|
||||
this->rc_matches.push_back(msg);
|
||||
this->rc_matches.emplace_back(msg);
|
||||
this->rc_matches_remaining -= 1;
|
||||
if (this->rc_matches_remaining == 0) {
|
||||
this->rc_display_match.invoke(this);
|
||||
@ -819,8 +848,8 @@ void readline_curses::check_poll_set(const vector<struct pollfd> &pollfds)
|
||||
}
|
||||
switch (msg[0]) {
|
||||
case 'a':
|
||||
this->vc_line.clear();
|
||||
this->rc_active_context = -1;
|
||||
this->vc_past_lines.clear();
|
||||
this->rc_matches.clear();
|
||||
this->rc_abort.invoke(this);
|
||||
this->rc_display_match.invoke(this);
|
||||
@ -834,7 +863,6 @@ void readline_curses::check_poll_set(const vector<struct pollfd> &pollfds)
|
||||
|
||||
case 'd':
|
||||
this->rc_active_context = -1;
|
||||
this->vc_past_lines.clear();
|
||||
this->rc_matches.clear();
|
||||
this->rc_perform.invoke(this);
|
||||
this->rc_display_match.invoke(this);
|
||||
@ -874,11 +902,11 @@ void readline_curses::handle_key(int ch)
|
||||
perror("handle_key: write failed");
|
||||
}
|
||||
if (ch == '\t' || ch == '\r') {
|
||||
this->vc_past_lines.clear();
|
||||
// this->vc_past_lines.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void readline_curses::focus(int context, const char *prompt)
|
||||
void readline_curses::focus(int context, const char *prompt, const char *initial)
|
||||
{
|
||||
char buffer[1024];
|
||||
|
||||
@ -892,8 +920,16 @@ void readline_curses::focus(int context, const char *prompt)
|
||||
strlen(buffer) + 1) == -1) {
|
||||
perror("focus: write failed");
|
||||
}
|
||||
wmove(this->vc_window, this->get_actual_y(), 0);
|
||||
wmove(this->vc_window, this->get_actual_y(), this->vc_left);
|
||||
wclrtoeol(this->vc_window);
|
||||
if (initial != nullptr) {
|
||||
snprintf(buffer, sizeof(buffer), "i:%s", initial);
|
||||
if (sendstring(this->rc_command_pipe[RCF_MASTER],
|
||||
buffer,
|
||||
strlen(buffer) + 1) == -1) {
|
||||
perror("focus: write failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void readline_curses::abort()
|
||||
@ -958,17 +994,21 @@ void readline_curses::clear_possibilities(int context, string type)
|
||||
}
|
||||
}
|
||||
|
||||
void readline_curses::do_update(void)
|
||||
void readline_curses::do_update()
|
||||
{
|
||||
if (!this->vc_visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->rc_active_context == -1) {
|
||||
int alt_start = -1;
|
||||
struct line_range lr(0, 0);
|
||||
attr_line_t al, alt_al;
|
||||
|
||||
wmove(this->vc_window, this->get_actual_y(), 0);
|
||||
wmove(this->vc_window, this->get_actual_y(), this->vc_left);
|
||||
wclrtoeol(this->vc_window);
|
||||
|
||||
if (time(NULL) > this->rc_value_expiration) {
|
||||
if (time(nullptr) > this->rc_value_expiration) {
|
||||
this->rc_value.clear();
|
||||
}
|
||||
|
||||
@ -994,34 +1034,25 @@ void readline_curses::do_update(void)
|
||||
lr.lr_end = al.get_string().length();
|
||||
view_curses::mvwattrline(this->vc_window,
|
||||
this->get_actual_y(),
|
||||
0,
|
||||
this->vc_left,
|
||||
al,
|
||||
lr);
|
||||
this->set_x(0);
|
||||
}
|
||||
vt52_curses::do_update();
|
||||
|
||||
if (this->rc_active_context != -1) {
|
||||
readline_context *rc = this->rc_contexts[this->rc_active_context];
|
||||
readline_highlighter_t hl = rc->get_highlighter();
|
||||
attr_line_t al = this->vc_line;
|
||||
|
||||
if (hl != NULL) {
|
||||
char line[1024];
|
||||
attr_line_t al;
|
||||
string &str = al.get_string();
|
||||
int rc;
|
||||
|
||||
rc = mvwinnstr(this->vc_window,
|
||||
this->get_actual_y(), 0,
|
||||
line, sizeof(line));
|
||||
|
||||
str = string(line, rc);
|
||||
|
||||
hl(al, this->vc_x);
|
||||
view_curses::mvwattrline(this->vc_window,
|
||||
this->get_actual_y(), 0, al, line_range(0, rc));
|
||||
|
||||
wmove(this->vc_window, this->get_actual_y(), this->vc_x);
|
||||
if (hl != nullptr) {
|
||||
hl(al, this->vc_left + this->vc_x);
|
||||
}
|
||||
view_curses::mvwattrline(this->vc_window,
|
||||
this->get_actual_y(), this->vc_left,
|
||||
al,
|
||||
line_range{ 0, (int) this->vc_width });
|
||||
|
||||
wmove(this->vc_window, this->get_actual_y(), this->vc_left + this->vc_x);
|
||||
}
|
||||
}
|
||||
|
@ -123,7 +123,7 @@ public:
|
||||
|
||||
const std::string &get_name() const { return this->rc_name; };
|
||||
|
||||
void load(void)
|
||||
void load()
|
||||
{
|
||||
char buffer[128];
|
||||
|
||||
@ -145,13 +145,13 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
void save(void)
|
||||
void save()
|
||||
{
|
||||
HISTORY_STATE *hs = history_get_history_state();
|
||||
|
||||
this->rc_history = *hs;
|
||||
free(hs);
|
||||
hs = NULL;
|
||||
hs = nullptr;
|
||||
};
|
||||
|
||||
void add_possibility(std::string type, std::string value)
|
||||
@ -169,7 +169,7 @@ public:
|
||||
this->rc_possibilities[type].clear();
|
||||
};
|
||||
|
||||
bool is_case_sensitive(void) const
|
||||
bool is_case_sensitive() const
|
||||
{
|
||||
return this->rc_case_sensitive;
|
||||
};
|
||||
@ -192,7 +192,7 @@ public:
|
||||
};
|
||||
|
||||
readline_context &with_readline_var(char **var, const char *val) {
|
||||
this->rc_vars.push_back(readline_var(var, val));
|
||||
this->rc_vars.emplace_back(var, val);
|
||||
|
||||
return *this;
|
||||
};
|
||||
@ -310,7 +310,7 @@ public:
|
||||
|
||||
void check_poll_set(const std::vector<struct pollfd> &pollfds);
|
||||
|
||||
void focus(int context, const char *prompt);
|
||||
void focus(int context, const char *prompt, const char *initial = nullptr);
|
||||
|
||||
readline_context *get_active_context() const {
|
||||
require(this->rc_active_context != -1);
|
||||
@ -322,11 +322,11 @@ public:
|
||||
|
||||
void abort();
|
||||
|
||||
void start(void);
|
||||
void start();
|
||||
|
||||
void do_update(void);
|
||||
void do_update();
|
||||
|
||||
void window_change(void)
|
||||
void window_change()
|
||||
{
|
||||
struct winsize ws;
|
||||
|
||||
|
@ -79,6 +79,10 @@ static void find_matching_bracket(attr_line_t &al, int x, char left, char right)
|
||||
const string &line = al.get_string();
|
||||
int depth = 0;
|
||||
|
||||
if (x < 0 || x > line.length()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (line[x] == right && is_bracket(line, x, is_lit)) {
|
||||
for (int lpc = x - 1; lpc > 0; lpc--) {
|
||||
if (line[lpc] == right && is_bracket(line, lpc, is_lit)) {
|
||||
@ -381,7 +385,7 @@ void readline_command_highlighter(attr_line_t &al, int x)
|
||||
readline_shlex_highlighter(al, x);
|
||||
}
|
||||
pi.reset(line);
|
||||
if (IDENT_PREFIXES.match(pc, pi)) {
|
||||
if (IDENT_PREFIXES.match(pc, pi) && ws_index != string::npos) {
|
||||
size_t start = ws_index, last;
|
||||
|
||||
do {
|
||||
|
@ -118,12 +118,11 @@ struct sqlite_metadata_callbacks lnav_sql_meta_callbacks = {
|
||||
handle_foreign_key_list,
|
||||
};
|
||||
|
||||
void add_text_possibilities(int context, const string &type, const std::string &str)
|
||||
static void add_text_possibilities(readline_curses *rlc, int context, const string &type, const std::string &str)
|
||||
{
|
||||
static pcrecpp::RE re_escape("([.\\^$*+?()\\[\\]{}\\\\|])");
|
||||
static pcrecpp::RE re_escape_no_dot("([\\^$*+?()\\[\\]{}\\\\|])");
|
||||
|
||||
readline_curses *rlc = lnav_data.ld_rl_view;
|
||||
pcre_context_static<30> pc;
|
||||
data_scanner ds(str);
|
||||
data_token_t dt;
|
||||
@ -143,21 +142,6 @@ void add_text_possibilities(int context, const string &type, const std::string &
|
||||
}
|
||||
|
||||
switch (context) {
|
||||
case LNM_SEARCH:
|
||||
case LNM_COMMAND:
|
||||
case LNM_EXEC: {
|
||||
string token_value, token_value_no_dot;
|
||||
|
||||
token_value_no_dot = token_value =
|
||||
ds.get_input().get_substr(pc.all());
|
||||
re_escape.GlobalReplace("\\\\\\1", &token_value);
|
||||
re_escape_no_dot.GlobalReplace("\\\\\\1", &token_value_no_dot);
|
||||
rlc->add_possibility(context, type, token_value);
|
||||
if (token_value != token_value_no_dot) {
|
||||
rlc->add_possibility(context, type, token_value_no_dot);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LNM_SQL: {
|
||||
string token_value = ds.get_input().get_substr(pc.all());
|
||||
auto_mem<char, sqlite3_free> quoted_token;
|
||||
@ -166,11 +150,24 @@ void add_text_possibilities(int context, const string &type, const std::string &
|
||||
rlc->add_possibility(context, type, std::string(quoted_token));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
string token_value, token_value_no_dot;
|
||||
|
||||
token_value_no_dot = token_value =
|
||||
ds.get_input().get_substr(pc.all());
|
||||
re_escape.GlobalReplace(R"(\\\1)", &token_value);
|
||||
re_escape_no_dot.GlobalReplace(R"(\\\1)", &token_value_no_dot);
|
||||
rlc->add_possibility(context, type, token_value);
|
||||
if (token_value != token_value_no_dot) {
|
||||
rlc->add_possibility(context, type, token_value_no_dot);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (dt) {
|
||||
case DT_QUOTED_STRING:
|
||||
add_text_possibilities(context, type, ds.get_input().get_substr(pc[0]));
|
||||
add_text_possibilities(rlc, context, type, ds.get_input().get_substr(pc[0]));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -178,10 +175,9 @@ void add_text_possibilities(int context, const string &type, const std::string &
|
||||
}
|
||||
}
|
||||
|
||||
void add_view_text_possibilities(int context, const string &type, textview_curses *tc)
|
||||
void add_view_text_possibilities(readline_curses *rlc, int context, const string &type, textview_curses *tc)
|
||||
{
|
||||
text_sub_source *tss = tc->get_sub_source();
|
||||
readline_curses *rlc = lnav_data.ld_rl_view;
|
||||
|
||||
rlc->clear_possibilities(context, type);
|
||||
|
||||
@ -212,7 +208,7 @@ void add_view_text_possibilities(int context, const string &type, textview_curse
|
||||
|
||||
tss->text_value_for_line(*tc, curr_line, line, text_sub_source::RF_RAW);
|
||||
|
||||
add_text_possibilities(context, type, line);
|
||||
add_text_possibilities(rlc, context, type, line);
|
||||
}
|
||||
|
||||
rlc->add_possibility(context, type, bookmark_metadata::KNOWN_TAGS);
|
||||
@ -297,7 +293,7 @@ void add_tag_possibilities()
|
||||
rc->clear_possibilities(LNM_COMMAND, "tag");
|
||||
rc->clear_possibilities(LNM_COMMAND, "line-tags");
|
||||
rc->add_possibility(LNM_COMMAND, "tag", bookmark_metadata::KNOWN_TAGS);
|
||||
if (lnav_data.ld_view_stack.back() == &lnav_data.ld_views[LNV_LOG]) {
|
||||
if (lnav_data.ld_view_stack.top().value_or(nullptr) == &lnav_data.ld_views[LNV_LOG]) {
|
||||
logfile_sub_source &lss = lnav_data.ld_log_source;
|
||||
if (lss.text_line_count() > 0) {
|
||||
content_line_t cl = lss.at(lnav_data.ld_views[LNV_LOG].get_top());
|
||||
|
@ -33,9 +33,9 @@
|
||||
#include <string>
|
||||
|
||||
#include "textview_curses.hh"
|
||||
#include "readline_curses.hh"
|
||||
|
||||
void add_text_possibilities(int context, const std::string &type, const std::string &str);
|
||||
void add_view_text_possibilities(int context, const std::string &type, textview_curses *tc);
|
||||
void add_view_text_possibilities(readline_curses *rlc, int context, const std::string &type, textview_curses *tc);
|
||||
void add_env_possibilities(int context);
|
||||
void add_filter_possibilities(textview_curses *tc);
|
||||
void add_mark_possibilities();
|
||||
|
@ -777,7 +777,7 @@ static int read_last_search(yajlpp_parse_context *ypc, const unsigned char *str,
|
||||
view_index = view_name - lnav_view_strings;
|
||||
|
||||
if (view_index < LNV__MAX && !regex.empty()) {
|
||||
execute_search((lnav_view_t)view_index, regex);
|
||||
lnav_data.ld_views[view_index].execute_search(regex);
|
||||
lnav_data.ld_views[view_index].set_follow_search_for(-1);
|
||||
}
|
||||
|
||||
@ -838,7 +838,7 @@ static int read_commands(yajlpp_parse_context *ypc, const unsigned char *str, si
|
||||
bool active = ensure_view(&lnav_data.ld_views[view_index]);
|
||||
execute_command(lnav_data.ld_exec_context, cmdline);
|
||||
if (!active) {
|
||||
lnav_data.ld_view_stack.pop_back();
|
||||
lnav_data.ld_view_stack.vs_views.pop_back();
|
||||
}
|
||||
|
||||
return 1;
|
||||
@ -1355,7 +1355,7 @@ void save_session(void)
|
||||
}
|
||||
|
||||
view_map.gen("search");
|
||||
view_map.gen(lnav_data.ld_last_search[lpc]);
|
||||
view_map.gen(lnav_data.ld_views[lpc].get_last_search());
|
||||
|
||||
view_map.gen("word_wrap");
|
||||
view_map.gen(tc.get_word_wrap());
|
||||
@ -1374,11 +1374,13 @@ void save_session(void)
|
||||
for (filter_iter = fs.begin();
|
||||
filter_iter != fs.end();
|
||||
++filter_iter) {
|
||||
if (!(*filter_iter)->is_enabled()) {
|
||||
string cmd = (*filter_iter)->to_command();
|
||||
|
||||
if (!(*filter_iter)->is_enabled() || cmd.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cmd_array.gen((*filter_iter)->to_command());
|
||||
cmd_array.gen(cmd);
|
||||
}
|
||||
|
||||
textview_curses::highlight_map_t &hmap =
|
||||
@ -1469,18 +1471,15 @@ void reset_session()
|
||||
}
|
||||
}
|
||||
|
||||
logfile_sub_source::iterator file_iter;
|
||||
|
||||
for (file_iter = lnav_data.ld_log_source.begin();
|
||||
file_iter != lnav_data.ld_log_source.end();
|
||||
++file_iter) {
|
||||
shared_ptr<logfile> lf = (*file_iter)->get_file();
|
||||
for (auto ld : lnav_data.ld_log_source) {
|
||||
shared_ptr<logfile> lf = ld->get_file();
|
||||
|
||||
lf->clear_time_offset();
|
||||
}
|
||||
|
||||
lnav_data.ld_log_source.set_marked_only(false);
|
||||
lnav_data.ld_log_source.clear_min_max_log_times();
|
||||
lnav_data.ld_log_source.set_min_log_level(LEVEL_UNKNOWN);
|
||||
|
||||
lnav_data.ld_log_source.get_user_bookmark_metadata().clear();
|
||||
|
||||
|
@ -289,7 +289,7 @@ int walk_sqlite_metadata(sqlite3 *db, struct sqlite_metadata_callbacks &smc)
|
||||
return retval;
|
||||
}
|
||||
|
||||
for (db_table_map_t::iterator iter = smc.smc_db_list.begin();
|
||||
for (auto iter = smc.smc_db_list.begin();
|
||||
iter != smc.smc_db_list.end();
|
||||
++iter) {
|
||||
struct table_list_data tld = { &smc, &iter };
|
||||
@ -309,7 +309,7 @@ int walk_sqlite_metadata(sqlite3 *db, struct sqlite_metadata_callbacks &smc)
|
||||
return retval;
|
||||
}
|
||||
|
||||
for (db_table_list_t::iterator table_iter = iter->second.begin();
|
||||
for (auto table_iter = iter->second.begin();
|
||||
table_iter != iter->second.end();
|
||||
++table_iter) {
|
||||
auto_mem<char, sqlite3_free> table_query;
|
||||
|
@ -627,7 +627,8 @@ int register_sqlite_funcs(sqlite3 *db, sqlite_registration_func_t *reg_funcs)
|
||||
.one_or_more())
|
||||
.with_parameter(help_text("cond", "The condition used to determine whether a row should be updated.")
|
||||
.with_flag_name("WHERE")
|
||||
.optional()),
|
||||
.optional())
|
||||
.with_example({"UPDATE syslog_log SET log_mark = 1 WHERE log_line = 40"}),
|
||||
|
||||
help_text("CASE",
|
||||
"Evaluate a series of expressions in order until one evaluates to true and then return it's result. "
|
||||
|
@ -52,7 +52,7 @@ static std::string sql_log_top_datetime()
|
||||
{
|
||||
char buffer[64];
|
||||
|
||||
sql_strftime(buffer, sizeof(buffer), lnav_data.ld_top_time);
|
||||
sql_strftime(buffer, sizeof(buffer), lnav_data.ld_log_source.time_for_row(lnav_data.ld_views[LNV_LOG].get_top()));
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
@ -35,13 +35,13 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
void statusview_curses::do_update(void)
|
||||
void statusview_curses::do_update()
|
||||
{
|
||||
int top, attrs, field, field_count, left = 0, right;
|
||||
view_colors & vc = view_colors::singleton();
|
||||
unsigned long width, height;
|
||||
|
||||
if (!this->sc_enabled) {
|
||||
if (!this->sc_visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -52,7 +52,8 @@ void statusview_curses::do_update(void)
|
||||
|
||||
top = this->sc_top < 0 ? height + this->sc_top : this->sc_top;
|
||||
right = width;
|
||||
attrs = vc.attrs_for_role(view_colors::VCR_STATUS);
|
||||
attrs = vc.attrs_for_role(this->sc_enabled ? view_colors::VCR_STATUS
|
||||
: view_colors::VCR_INACTIVE_STATUS);
|
||||
|
||||
wattron(this->sc_window, attrs);
|
||||
wmove(this->sc_window, top, 0);
|
||||
@ -60,7 +61,7 @@ void statusview_curses::do_update(void)
|
||||
whline(this->sc_window, ' ', width);
|
||||
wattroff(this->sc_window, attrs);
|
||||
|
||||
if (this->sc_source != NULL) {
|
||||
if (this->sc_source != nullptr) {
|
||||
field_count = this->sc_source->statusview_fields();
|
||||
for (field = 0; field < field_count; field++) {
|
||||
status_field &sf = this->sc_source->statusview_value_for_field(
|
||||
@ -70,9 +71,18 @@ void statusview_curses::do_update(void)
|
||||
int x;
|
||||
|
||||
val = sf.get_value();
|
||||
if (!this->sc_enabled) {
|
||||
for (auto &sa : val.get_attrs()) {
|
||||
if (sa.sa_type == &view_curses::VC_STYLE) {
|
||||
sa.sa_value.sav_int &= ~(A_REVERSE | A_COLOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
left += sf.get_left_pad();
|
||||
|
||||
if (sf.is_right_justified()) {
|
||||
val.right_justify(sf.get_width());
|
||||
|
||||
right -= sf.get_width();
|
||||
x = right;
|
||||
}
|
||||
@ -80,12 +90,57 @@ void statusview_curses::do_update(void)
|
||||
x = left;
|
||||
left += sf.get_width();
|
||||
}
|
||||
this->mvwattrline(this->sc_window,
|
||||
top, x,
|
||||
val,
|
||||
lr,
|
||||
sf.get_role());
|
||||
mvwattrline(this->sc_window,
|
||||
top, x,
|
||||
val,
|
||||
lr,
|
||||
this->sc_enabled ? sf.get_role() : view_colors::VCR_INACTIVE_STATUS);
|
||||
}
|
||||
}
|
||||
wmove(this->sc_window, top + 1, 0);
|
||||
}
|
||||
|
||||
void statusview_curses::window_change()
|
||||
{
|
||||
if (this->sc_source == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
int field_count = this->sc_source->statusview_fields();
|
||||
int remaining, total_shares = 0;
|
||||
unsigned long width, height;
|
||||
|
||||
getmaxyx(this->sc_window, height, width);
|
||||
// Silence the compiler. Remove this if height is used at a later stage.
|
||||
(void)height;
|
||||
remaining = width - 4;
|
||||
for (int field = 0; field < field_count; field++) {
|
||||
status_field &sf = this->sc_source->statusview_value_for_field(
|
||||
field);
|
||||
|
||||
remaining -=
|
||||
sf.get_share() ? sf.get_min_width() : sf.get_width();
|
||||
remaining -= 1;
|
||||
total_shares += sf.get_share();
|
||||
}
|
||||
|
||||
if (remaining < 2) {
|
||||
remaining = 0;
|
||||
}
|
||||
|
||||
for (int field = 0; field < field_count; field++) {
|
||||
status_field &sf = this->sc_source->statusview_value_for_field(
|
||||
field);
|
||||
|
||||
if (sf.get_share()) {
|
||||
int actual_width;
|
||||
|
||||
actual_width = sf.get_min_width();
|
||||
actual_width += remaining / (sf.get_share() / total_shares);
|
||||
|
||||
sf.set_width(actual_width);
|
||||
}
|
||||
}
|
||||
|
||||
this->sc_last_width = width;
|
||||
}
|
||||
|
@ -90,14 +90,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
if (this->sf_right_justify) {
|
||||
int padding = this->sf_width - value.size();
|
||||
|
||||
if (padding > 2) {
|
||||
value.insert(0, padding, ' ');
|
||||
}
|
||||
}
|
||||
|
||||
this->sf_value.with_string(value);
|
||||
|
||||
if (this->sf_cylon) {
|
||||
@ -132,7 +124,7 @@ public:
|
||||
|
||||
void set_stitch_value(int color_pair)
|
||||
{
|
||||
string_attrs_t & sa = this->sf_value.get_attrs();
|
||||
string_attrs_t &sa = this->sf_value.get_attrs();
|
||||
struct line_range lr(0, 1);
|
||||
|
||||
this->sf_value.get_string() = "::";
|
||||
@ -150,10 +142,10 @@ public:
|
||||
attr_line_t &get_value() { return this->sf_value; };
|
||||
|
||||
void right_justify(bool yes) { this->sf_right_justify = yes; };
|
||||
bool is_right_justified(void) const { return this->sf_right_justify; };
|
||||
bool is_right_justified() const { return this->sf_right_justify; };
|
||||
|
||||
void set_cylon(bool yes) { this->sf_cylon = yes; };
|
||||
bool is_cylon(void) const { return this->sf_cylon; };
|
||||
bool is_cylon() const { return this->sf_cylon; };
|
||||
|
||||
/** @return True if this field's value is an empty string. */
|
||||
bool empty() { return this->sf_value.get_string().empty(); };
|
||||
@ -200,7 +192,7 @@ public:
|
||||
/**
|
||||
* @return The number of status_fields in this source.
|
||||
*/
|
||||
virtual size_t statusview_fields(void) = 0;
|
||||
virtual size_t statusview_fields() = 0;
|
||||
|
||||
/**
|
||||
* Callback used to get a particular field.
|
||||
@ -217,15 +209,6 @@ public:
|
||||
class statusview_curses
|
||||
: public view_curses {
|
||||
public:
|
||||
statusview_curses()
|
||||
: sc_source(NULL),
|
||||
sc_window(NULL),
|
||||
sc_top(0),
|
||||
sc_last_width(0),
|
||||
sc_enabled(true) {
|
||||
};
|
||||
virtual ~statusview_curses() { };
|
||||
|
||||
void set_data_source(status_data_source *src) { this->sc_source = src; };
|
||||
status_data_source *get_data_source() { return this->sc_source; };
|
||||
|
||||
@ -235,61 +218,28 @@ public:
|
||||
void set_window(WINDOW *win) { this->sc_window = win; };
|
||||
WINDOW *get_window() { return this->sc_window; };
|
||||
|
||||
void set_visible(bool value) {
|
||||
this->sc_visible = value;
|
||||
}
|
||||
|
||||
bool get_visible() const {
|
||||
return this->sc_visible;
|
||||
}
|
||||
|
||||
void set_enabled(bool value) { this->sc_enabled = value; };
|
||||
bool get_enabled() const { return this->sc_enabled; };
|
||||
|
||||
void window_change(void) {
|
||||
if (this->sc_source == NULL) {
|
||||
return;
|
||||
}
|
||||
void window_change();
|
||||
|
||||
int field_count = this->sc_source->statusview_fields();
|
||||
int remaining, total_shares = 0;
|
||||
unsigned long width, height;
|
||||
|
||||
getmaxyx(this->sc_window, height, width);
|
||||
// Silence the compiler. Remove this if height is used at a later stage.
|
||||
(void)height;
|
||||
remaining = width - 4;
|
||||
for (int field = 0; field < field_count; field++) {
|
||||
status_field &sf = this->sc_source->statusview_value_for_field(
|
||||
field);
|
||||
|
||||
remaining -=
|
||||
sf.get_share() ? sf.get_min_width() : sf.get_width();
|
||||
remaining -= 1;
|
||||
total_shares += sf.get_share();
|
||||
}
|
||||
|
||||
if (remaining < 2) {
|
||||
remaining = 0;
|
||||
}
|
||||
|
||||
for (int field = 0; field < field_count; field++) {
|
||||
status_field &sf = this->sc_source->statusview_value_for_field(
|
||||
field);
|
||||
|
||||
if (sf.get_share()) {
|
||||
int actual_width;
|
||||
|
||||
actual_width = sf.get_min_width();
|
||||
actual_width += remaining / (sf.get_share() / total_shares);
|
||||
|
||||
sf.set_width(actual_width);
|
||||
}
|
||||
}
|
||||
|
||||
this->sc_last_width = width;
|
||||
};
|
||||
|
||||
void do_update(void);
|
||||
void do_update() override;
|
||||
|
||||
private:
|
||||
status_data_source *sc_source;
|
||||
WINDOW * sc_window;
|
||||
int sc_top;
|
||||
unsigned long sc_last_width;
|
||||
bool sc_enabled;
|
||||
status_data_source *sc_source{nullptr};
|
||||
WINDOW * sc_window{nullptr};
|
||||
int sc_top{0};
|
||||
unsigned long sc_last_width{0};
|
||||
bool sc_enabled{true};
|
||||
bool sc_visible{true};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
367
src/textfile_highlighters.cc
Normal file
367
src/textfile_highlighters.cc
Normal file
@ -0,0 +1,367 @@
|
||||
/**
|
||||
* Copyright (c) 2018, 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 <string>
|
||||
|
||||
#include "textfile_highlighters.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
static pcre *xpcre_compile(const char *pattern, int options = 0)
|
||||
{
|
||||
const char *errptr;
|
||||
pcre * retval;
|
||||
int eoff;
|
||||
|
||||
if ((retval = pcre_compile(pattern,
|
||||
options,
|
||||
&errptr,
|
||||
&eoff,
|
||||
NULL)) == NULL) {
|
||||
fprintf(stderr, "internal error: failed to compile -- %s\n", pattern);
|
||||
fprintf(stderr, "internal error: %s\n", errptr);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static highlighter static_highlighter(const string ®ex) {
|
||||
return highlighter(xpcre_compile(regex.c_str()))
|
||||
.with_attrs(view_colors::singleton().attrs_for_ident(regex));
|
||||
}
|
||||
|
||||
void setup_highlights(textview_curses::highlight_map_t &hm)
|
||||
{
|
||||
hm["$python"] = highlighter(xpcre_compile(
|
||||
"(?:"
|
||||
"\\bFalse\\b|"
|
||||
"\\bNone\\b|"
|
||||
"\\bTrue\\b|"
|
||||
"\\band\\b|"
|
||||
"\\bas\\b|"
|
||||
"\\bassert\\b|"
|
||||
"\\bbreak\\b|"
|
||||
"\\bclass\\b|"
|
||||
"\\bcontinue\\b|"
|
||||
"\\bdef\\b|"
|
||||
"\\bdel\\b|"
|
||||
"\\belif\\b|"
|
||||
"\\belse\\b|"
|
||||
"\\bexcept\\b|"
|
||||
"\\bfinally\\b|"
|
||||
"\\bfor\\b|"
|
||||
"\\bfrom\\b|"
|
||||
"\\bglobal\\b|"
|
||||
"\\bif\\b|"
|
||||
"\\bimport\\b|"
|
||||
"\\bin\\b|"
|
||||
"\\bis\\b|"
|
||||
"\\blambda\\b|"
|
||||
"\\bnonlocal\\b|"
|
||||
"\\bnot\\b|"
|
||||
"\\bor\\b|"
|
||||
"\\bpass\\b|"
|
||||
"\\bprint\\b|"
|
||||
"\\braise\\b|"
|
||||
"\\breturn\\b|"
|
||||
"\\btry\\b|"
|
||||
"\\bwhile\\b|"
|
||||
"\\bwith\\b|"
|
||||
"\\byield\\b"
|
||||
")"))
|
||||
.with_text_format(TF_PYTHON)
|
||||
.with_role(view_colors::VCR_KEYWORD);
|
||||
|
||||
hm["$clike"] = highlighter(xpcre_compile(
|
||||
"(?:"
|
||||
"\\babstract\\b|"
|
||||
"\\bassert\\b|"
|
||||
"\\basm\\b|"
|
||||
"\\bauto\\b|"
|
||||
"\\bbool\\b|"
|
||||
"\\bbooleanif\\b|"
|
||||
"\\bbreak\\b|"
|
||||
"\\bbyte\\b|"
|
||||
"\\bcase\\b|"
|
||||
"\\bcatch\\b|"
|
||||
"\\bchar\\b|"
|
||||
"\\bclass\\b|"
|
||||
"\\bconst\\b|"
|
||||
"\\bconst_cast\\b|"
|
||||
"\\bcontinue\\b|"
|
||||
"\\bdefault\\b|"
|
||||
"\\bdelete\\b|"
|
||||
"\\bdo\\b|"
|
||||
"\\bdouble\\b|"
|
||||
"\\bdynamic_cast\\b|"
|
||||
"\\belse\\b|"
|
||||
"\\benum\\b|"
|
||||
"\\bextends\\b|"
|
||||
"\\bextern\\b|"
|
||||
"\\bfalse\\b|"
|
||||
"\\bfinal\\b|"
|
||||
"\\bfinally\\b|"
|
||||
"\\bfloat\\b|"
|
||||
"\\bfor\\b|"
|
||||
"\\bfriend\\b|"
|
||||
"\\bgoto\\b|"
|
||||
"\\bif\\b|"
|
||||
"\\bimplements\\b|"
|
||||
"\\bimport\\b|"
|
||||
"\\binline\\b|"
|
||||
"\\binstanceof\\b|"
|
||||
"\\bint\\b|"
|
||||
"\\binterface\\b|"
|
||||
"\\blong\\b|"
|
||||
"\\bmutable\\b|"
|
||||
"\\bnamespace\\b|"
|
||||
"\\bnative\\b|"
|
||||
"\\bnew\\b|"
|
||||
"\\boperator\\b|"
|
||||
"\\bpackage\\b|"
|
||||
"\\bprivate\\b|"
|
||||
"\\bprotected\\b|"
|
||||
"\\bpublic\\b|"
|
||||
"\\breinterpret_cast\\b|"
|
||||
"\\bregister\\b|"
|
||||
"\\breturn\\b|"
|
||||
"\\bshort\\b|"
|
||||
"\\bsigned\\b|"
|
||||
"\\bsizeof\\b|"
|
||||
"\\bstatic\\b|"
|
||||
"\\bstatic_cast\\b|"
|
||||
"\\bstrictfp\\b|"
|
||||
"\\bstruct\\b|"
|
||||
"\\bsuper\\b|"
|
||||
"\\bswitch\\b|"
|
||||
"\\bsynchronized\\b|"
|
||||
"\\btemplate\\b|"
|
||||
"\\bthis\\b|"
|
||||
"\\bthrow\\b|"
|
||||
"\\bthrows\\b|"
|
||||
"\\btransient\\b|"
|
||||
"\\btry\\b|"
|
||||
"\\btrue\\b|"
|
||||
"\\btypedef\\b|"
|
||||
"\\btypeid\\b|"
|
||||
"\\bunion\\b|"
|
||||
"\\bunsigned\\b|"
|
||||
"\\busing\\b|"
|
||||
"\\bvirtual\\b|"
|
||||
"\\bvoid\\b|"
|
||||
"\\bvolatile\\b|"
|
||||
"\\bwchar_t\\b|"
|
||||
"\\bwhile\\b"
|
||||
")"))
|
||||
.with_text_format(TF_C_LIKE)
|
||||
.with_role(view_colors::VCR_KEYWORD);
|
||||
|
||||
hm["$sql"] = highlighter(xpcre_compile(
|
||||
"(?:"
|
||||
"\\bABORT\\b|"
|
||||
"\\bACTION\\b|"
|
||||
"\\bADD\\b|"
|
||||
"\\bAFTER\\b|"
|
||||
"\\bALL\\b|"
|
||||
"\\bALTER\\b|"
|
||||
"\\bANALYZE\\b|"
|
||||
"\\bAND\\b|"
|
||||
"\\bAS\\b|"
|
||||
"\\bASC\\b|"
|
||||
"\\bATTACH\\b|"
|
||||
"\\bAUTOINCREMENT\\b|"
|
||||
"\\bBEFORE\\b|"
|
||||
"\\bBEGIN\\b|"
|
||||
"\\bBETWEEN\\b|"
|
||||
"\\bBOOLEAN\\b|"
|
||||
"\\bBY\\b|"
|
||||
"\\bCASCADE\\b|"
|
||||
"\\bCASE\\b|"
|
||||
"\\bCAST\\b|"
|
||||
"\\bCHECK\\b|"
|
||||
"\\bCOLLATE\\b|"
|
||||
"\\bCOLUMN\\b|"
|
||||
"\\bCOMMIT\\b|"
|
||||
"\\bCONFLICT\\b|"
|
||||
"\\bCONSTRAINT\\b|"
|
||||
"\\bCREATE\\b|"
|
||||
"\\bCROSS\\b|"
|
||||
"\\bCURRENT_DATE\\b|"
|
||||
"\\bCURRENT_TIME\\b|"
|
||||
"\\bCURRENT_TIMESTAMP\\b|"
|
||||
"\\bDATABASE\\b|"
|
||||
"\\bDATETIME\\b|"
|
||||
"\\bDEFAULT\\b|"
|
||||
"\\bDEFERRABLE\\b|"
|
||||
"\\bDEFERRED\\b|"
|
||||
"\\bDELETE\\b|"
|
||||
"\\bDESC\\b|"
|
||||
"\\bDETACH\\b|"
|
||||
"\\bDISTINCT\\b|"
|
||||
"\\bDROP\\b|"
|
||||
"\\bEACH\\b|"
|
||||
"\\bELSE\\b|"
|
||||
"\\bEND\\b|"
|
||||
"\\bESCAPE\\b|"
|
||||
"\\bEXCEPT\\b|"
|
||||
"\\bEXCLUSIVE\\b|"
|
||||
"\\bEXISTS\\b|"
|
||||
"\\bEXPLAIN\\b|"
|
||||
"\\bFAIL\\b|"
|
||||
"\\bFLOAT\\b|"
|
||||
"\\bFOR\\b|"
|
||||
"\\bFOREIGN\\b|"
|
||||
"\\bFROM\\b|"
|
||||
"\\bFULL\\b|"
|
||||
"\\bGLOB\\b|"
|
||||
"\\bGROUP\\b|"
|
||||
"\\bHAVING\\b|"
|
||||
"\\bHIDDEN\\b|"
|
||||
"\\bIF\\b|"
|
||||
"\\bIGNORE\\b|"
|
||||
"\\bIMMEDIATE\\b|"
|
||||
"\\bIN\\b|"
|
||||
"\\bINDEX\\b|"
|
||||
"\\bINDEXED\\b|"
|
||||
"\\bINITIALLY\\b|"
|
||||
"\\bINNER\\b|"
|
||||
"\\bINSERT\\b|"
|
||||
"\\bINSTEAD\\b|"
|
||||
"\\bINTEGER\\b|"
|
||||
"\\bINTERSECT\\b|"
|
||||
"\\bINTO\\b|"
|
||||
"\\bIS\\b|"
|
||||
"\\bISNULL\\b|"
|
||||
"\\bJOIN\\b|"
|
||||
"\\bKEY\\b|"
|
||||
"\\bLEFT\\b|"
|
||||
"\\bLIKE\\b|"
|
||||
"\\bLIMIT\\b|"
|
||||
"\\bMATCH\\b|"
|
||||
"\\bNATURAL\\b|"
|
||||
"\\bNO\\b|"
|
||||
"\\bNOT\\b|"
|
||||
"\\bNOTNULL\\b|"
|
||||
"\\bNULL\\b|"
|
||||
"\\bOF\\b|"
|
||||
"\\bOFFSET\\b|"
|
||||
"\\bON\\b|"
|
||||
"\\bOR\\b|"
|
||||
"\\bORDER\\b|"
|
||||
"\\bOUTER\\b|"
|
||||
"\\bPLAN\\b|"
|
||||
"\\bPRAGMA\\b|"
|
||||
"\\bPRIMARY\\b|"
|
||||
"\\bQUERY\\b|"
|
||||
"\\bRAISE\\b|"
|
||||
"\\bRECURSIVE\\b|"
|
||||
"\\bREFERENCES\\b|"
|
||||
"\\bREGEXP\\b|"
|
||||
"\\bREINDEX\\b|"
|
||||
"\\bRELEASE\\b|"
|
||||
"\\bRENAME\\b|"
|
||||
"\\bREPLACE\\b|"
|
||||
"\\bRESTRICT\\b|"
|
||||
"\\bRIGHT\\b|"
|
||||
"\\bROLLBACK\\b|"
|
||||
"\\bROW\\b|"
|
||||
"\\bSAVEPOINT\\b|"
|
||||
"\\bSELECT\\b|"
|
||||
"\\bSET\\b|"
|
||||
"\\bTABLE\\b|"
|
||||
"\\bTEMP\\b|"
|
||||
"\\bTEMPORARY\\b|"
|
||||
"\\bTEXT\\b|"
|
||||
"\\bTHEN\\b|"
|
||||
"\\bTO\\b|"
|
||||
"\\bTRANSACTION\\b|"
|
||||
"\\bTRIGGER\\b|"
|
||||
"\\bUNION\\b|"
|
||||
"\\bUNIQUE\\b|"
|
||||
"\\bUPDATE\\b|"
|
||||
"\\bUSING\\b|"
|
||||
"\\bVACUUM\\b|"
|
||||
"\\bVALUES\\b|"
|
||||
"\\bVIEW\\b|"
|
||||
"\\bVIRTUAL\\b|"
|
||||
"\\bWHEN\\b|"
|
||||
"\\bWHERE\\b|"
|
||||
"\\bWITH\\b|"
|
||||
"\\bWITHOUT\\b"
|
||||
")", PCRE_CASELESS))
|
||||
.with_text_format(TF_SQL)
|
||||
.with_role(view_colors::VCR_KEYWORD);
|
||||
|
||||
hm["$srcfile"] = static_highlighter(
|
||||
"[\\w\\-_]+\\."
|
||||
"(?:java|a|o|so|c|cc|cpp|cxx|h|hh|hpp|hxx|py|pyc|rb):"
|
||||
"\\d+")
|
||||
.with_role(view_colors::VCR_FILE);
|
||||
hm["$xml"] = static_highlighter("<(/?[^ >=]+)[^>]*>");
|
||||
hm["$stringd"] = highlighter(xpcre_compile(
|
||||
"\"(?:\\\\.|[^\"])*\""))
|
||||
.with_role(view_colors::VCR_STRING);
|
||||
hm["$strings"] = highlighter(xpcre_compile(
|
||||
"(?<![A-WY-Za-qstv-z])\'(?:\\\\.|[^'])*\'"))
|
||||
.with_role(view_colors::VCR_STRING);
|
||||
hm["$stringb"] = highlighter(xpcre_compile(
|
||||
"`(?:\\\\.|[^`])*`"))
|
||||
.with_role(view_colors::VCR_STRING);
|
||||
hm["$diffp"] = highlighter(xpcre_compile(
|
||||
"^\\+.*"))
|
||||
.with_role(view_colors::VCR_DIFF_ADD);
|
||||
hm["$diffm"] = highlighter(xpcre_compile(
|
||||
"^(?:--- .*|-$|-[^-].*)"))
|
||||
.with_role(view_colors::VCR_DIFF_DELETE);
|
||||
hm["$diffs"] = highlighter(xpcre_compile(
|
||||
"^\\@@ .*"))
|
||||
.with_role(view_colors::VCR_DIFF_SECTION);
|
||||
hm["$ip"] = static_highlighter("\\d+\\.\\d+\\.\\d+\\.\\d+");
|
||||
hm["$comment"] = highlighter(xpcre_compile(
|
||||
"(?<=[\\s;])//.*|/\\*.*\\*/|\\(\\*.*\\*\\)|^#.*|\\s+#.*|dnl.*"))
|
||||
.with_role(view_colors::VCR_COMMENT);
|
||||
hm["$sqlcomment"] = highlighter(xpcre_compile(
|
||||
"(?<=[\\s;])--.*"))
|
||||
.with_text_format(TF_SQL)
|
||||
.with_role(view_colors::VCR_COMMENT);
|
||||
hm["$javadoc"] = static_highlighter(
|
||||
"@(?:author|deprecated|exception|file|param|return|see|since|throws|todo|version)");
|
||||
hm["$var"] = highlighter(xpcre_compile(
|
||||
"(?:"
|
||||
"(?:var\\s+)?([\\-\\w]+)\\s*=|"
|
||||
"(?<!\\$)\\$(\\w+)|"
|
||||
"(?<!\\$)\\$\\((\\w+)\\)|"
|
||||
"(?<!\\$)\\$\\{(\\w+)\\}"
|
||||
")"))
|
||||
.with_role(view_colors::VCR_VARIABLE);
|
||||
}
|
37
src/textfile_highlighters.hh
Normal file
37
src/textfile_highlighters.hh
Normal file
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2018, 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 textfile_highlighters_hh
|
||||
#define textfile_highlighters_hh
|
||||
|
||||
#include "textview_curses.hh"
|
||||
|
||||
void setup_highlights(textview_curses::highlight_map_t &hm);
|
||||
|
||||
#endif
|
@ -109,10 +109,10 @@ public:
|
||||
return retval;
|
||||
};
|
||||
|
||||
std::shared_ptr<logfile> current_file(void) const
|
||||
std::shared_ptr<logfile> current_file() const
|
||||
{
|
||||
if (this->tss_files.empty()) {
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return this->tss_files.front();
|
||||
@ -129,20 +129,29 @@ public:
|
||||
void to_front(std::shared_ptr<logfile> lf) {
|
||||
this->tss_files.remove(lf);
|
||||
this->tss_files.push_front(lf);
|
||||
this->tss_view->reload_data();
|
||||
};
|
||||
|
||||
void rotate_left() {
|
||||
this->tss_files.push_back(this->tss_files.front());
|
||||
this->tss_files.pop_front();
|
||||
if (this->tss_files.size() > 1) {
|
||||
this->tss_files.push_back(this->tss_files.front());
|
||||
this->tss_files.pop_front();
|
||||
this->tss_view->reload_data();
|
||||
this->tss_view->redo_search();
|
||||
}
|
||||
};
|
||||
|
||||
void rotate_right() {
|
||||
this->tss_files.push_front(this->tss_files.back());
|
||||
this->tss_files.pop_back();
|
||||
if (this->tss_files.size() > 1) {
|
||||
this->tss_files.push_front(this->tss_files.back());
|
||||
this->tss_files.pop_back();
|
||||
this->tss_view->reload_data();
|
||||
this->tss_view->redo_search();
|
||||
}
|
||||
};
|
||||
|
||||
void remove(std::shared_ptr<logfile> lf) {
|
||||
file_iterator iter = std::find(this->tss_files.begin(),
|
||||
auto iter = std::find(this->tss_files.begin(),
|
||||
this->tss_files.end(), lf);
|
||||
if (iter != this->tss_files.end()) {
|
||||
detach_observer(lf);
|
||||
@ -207,13 +216,17 @@ public:
|
||||
++iter;
|
||||
}
|
||||
|
||||
if (retval) {
|
||||
this->tss_view->search_new_data();
|
||||
}
|
||||
|
||||
return retval;
|
||||
};
|
||||
|
||||
virtual void text_filters_changed() {
|
||||
std::shared_ptr<logfile> lf = this->current_file();
|
||||
|
||||
if (lf == NULL) {
|
||||
if (lf == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -231,6 +244,8 @@ public:
|
||||
}
|
||||
lfo->lfo_filter_state.tfs_index.push_back(lpc);
|
||||
}
|
||||
|
||||
this->tss_view->redo_search();
|
||||
};
|
||||
|
||||
int get_filtered_count() const {
|
||||
|
@ -32,6 +32,8 @@
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include <pcrecpp.h>
|
||||
|
||||
#include "pcrepp.hh"
|
||||
#include "lnav_util.hh"
|
||||
#include "data_parser.hh"
|
||||
@ -361,10 +363,88 @@ void textview_curses::textview_value_for_row(vis_line_t row,
|
||||
#endif
|
||||
|
||||
if (binary_search(user_marks.begin(), user_marks.end(), row)) {
|
||||
sa.emplace_back(orig_line, &view_curses::VC_STYLE, A_REVERSE);
|
||||
sa.emplace_back(line_range{orig_line.lr_start, -1},
|
||||
&view_curses::VC_STYLE,
|
||||
A_REVERSE);
|
||||
}
|
||||
}
|
||||
|
||||
void textview_curses::execute_search(const std::string ®ex_orig)
|
||||
{
|
||||
std::string regex = regex_orig;
|
||||
pcre *code = nullptr;
|
||||
|
||||
if ((this->tc_search_child == nullptr) || (regex != this->tc_last_search)) {
|
||||
const char *errptr;
|
||||
int eoff;
|
||||
|
||||
this->match_reset();
|
||||
|
||||
if (regex.empty() && this->tc_search_child != nullptr) {
|
||||
this->grep_begin(*(this->tc_search_child->get_grep_proc()), 0_vl, -1_vl);
|
||||
this->grep_end(*(this->tc_search_child->get_grep_proc()));
|
||||
}
|
||||
this->tc_search_child.reset();
|
||||
|
||||
log_debug("start search for: '%s'", regex.c_str());
|
||||
|
||||
if (regex.empty()) {
|
||||
}
|
||||
else if ((code = pcre_compile(regex.c_str(),
|
||||
PCRE_CASELESS,
|
||||
&errptr,
|
||||
&eoff,
|
||||
nullptr)) == nullptr) {
|
||||
string errmsg = string(errptr);
|
||||
|
||||
regex = pcrecpp::RE::QuoteMeta(regex);
|
||||
|
||||
log_info("invalid search regex, using quoted: %s", regex.c_str());
|
||||
if ((code = pcre_compile(regex.c_str(),
|
||||
PCRE_CASELESS,
|
||||
&errptr,
|
||||
&eoff,
|
||||
nullptr)) == nullptr) {
|
||||
log_error("Unable to compile quoted regex: %s", regex.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (code != nullptr) {
|
||||
highlighter hl(code);
|
||||
|
||||
hl.with_role(view_colors::VCR_SEARCH);
|
||||
|
||||
textview_curses::highlight_map_t &hm = this->get_highlights();
|
||||
hm["$search"] = hl;
|
||||
|
||||
unique_ptr<grep_proc<vis_line_t>> gp = make_unique<grep_proc<vis_line_t>>(code, *this);
|
||||
|
||||
gp->set_sink(this);
|
||||
gp->queue_request(this->get_top());
|
||||
if (this->get_top() > 0) {
|
||||
gp->queue_request(0_vl, this->get_top());
|
||||
}
|
||||
gp->start();
|
||||
|
||||
this->tc_search_child = std::make_unique<grep_highlighter>(gp, "$search", hm);
|
||||
|
||||
if (this->tc_sub_source != nullptr) {
|
||||
this->tc_sub_source->get_grepper() | [this, code] (auto pair) {
|
||||
shared_ptr<grep_proc<vis_line_t>> sgp = make_shared<grep_proc<vis_line_t>>(code, *pair.first);
|
||||
|
||||
sgp->set_sink(pair.second);
|
||||
sgp->queue_request(0_vl);
|
||||
sgp->start();
|
||||
|
||||
this->tc_source_search_child = sgp;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->tc_last_search = regex;
|
||||
}
|
||||
|
||||
void text_time_translator::scroll_invoked(textview_curses *tc)
|
||||
{
|
||||
if (tc->get_inner_height() > 0) {
|
||||
@ -391,3 +471,14 @@ void text_time_translator::data_reloaded(textview_curses *tc)
|
||||
}
|
||||
|
||||
template class bookmark_vector<vis_line_t>;
|
||||
|
||||
bool empty_filter::matches(const logfile &lf, const logline &ll,
|
||||
shared_buffer_ref &line)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string empty_filter::to_command()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
@ -123,6 +123,9 @@ public:
|
||||
bool is_enabled() { return this->lf_enabled; };
|
||||
void enable() { this->lf_enabled = true; };
|
||||
void disable() { this->lf_enabled = false; };
|
||||
void set_enabled(bool value) {
|
||||
this->lf_enabled = value;
|
||||
}
|
||||
|
||||
void revert_to_last(logfile_filter_state &lfs) {
|
||||
this->lf_message_matched = this->lf_last_message_matched;
|
||||
@ -169,6 +172,7 @@ public:
|
||||
return this->lf_id == rhs;
|
||||
};
|
||||
|
||||
bool lf_deleted{false};
|
||||
bool lf_message_matched;
|
||||
size_t lf_lines_for_message;
|
||||
bool lf_last_message_matched;
|
||||
@ -181,6 +185,18 @@ protected:
|
||||
size_t lf_index;
|
||||
};
|
||||
|
||||
class empty_filter : public text_filter {
|
||||
public:
|
||||
empty_filter(type_t type, size_t index)
|
||||
: text_filter(type, "", index) {
|
||||
}
|
||||
|
||||
bool matches(const logfile &lf, const logline &ll,
|
||||
shared_buffer_ref &line) override;
|
||||
|
||||
std::string to_command() override;
|
||||
};
|
||||
|
||||
class filter_stack {
|
||||
public:
|
||||
typedef std::vector<std::shared_ptr<text_filter>>::iterator iterator;
|
||||
@ -210,6 +226,10 @@ public:
|
||||
|
||||
memset(used, 0, sizeof(used));
|
||||
for (auto &iter : *this) {
|
||||
if (iter->lf_deleted) {
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t index = iter->get_index();
|
||||
|
||||
require(used[index] == false);
|
||||
@ -276,8 +296,12 @@ public:
|
||||
|
||||
void get_enabled_mask(uint32_t &filter_in_mask, uint32_t &filter_out_mask) {
|
||||
filter_in_mask = filter_out_mask = 0;
|
||||
for (auto iter = this->begin(); iter != this->end(); ++iter) {
|
||||
std::shared_ptr<text_filter> tf = (*iter);
|
||||
for (auto &iter : *this) {
|
||||
std::shared_ptr<text_filter> tf = iter;
|
||||
|
||||
if (tf->lf_deleted) {
|
||||
continue;
|
||||
}
|
||||
if (tf->is_enabled()) {
|
||||
uint32_t bit = (1UL << tf->get_index());
|
||||
|
||||
@ -427,6 +451,10 @@ public:
|
||||
return TF_UNKNOWN;
|
||||
};
|
||||
|
||||
virtual nonstd::optional<std::pair<grep_proc_source<vis_line_t> *, grep_proc_sink<vis_line_t> *>> get_grepper() {
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
protected:
|
||||
textview_curses *tss_view;
|
||||
filter_stack tss_filters;
|
||||
@ -505,6 +533,8 @@ public:
|
||||
this->tc_sub_source->text_mark(bm, curr_line, added);
|
||||
}
|
||||
}
|
||||
this->search_range(start_line, end_line + 1_vl);
|
||||
this->search_new_data();
|
||||
};
|
||||
|
||||
void set_user_mark(bookmark_type_t *bm, vis_line_t vl, bool marked) {
|
||||
@ -523,6 +553,9 @@ public:
|
||||
if (this->tc_sub_source) {
|
||||
this->tc_sub_source->text_mark(bm, vl, marked);
|
||||
}
|
||||
|
||||
this->search_range(vl, vl + 1_vl);
|
||||
this->search_new_data();
|
||||
};
|
||||
|
||||
textview_curses &set_sub_source(text_sub_source *src) {
|
||||
@ -532,7 +565,7 @@ public:
|
||||
return *this;
|
||||
};
|
||||
|
||||
text_sub_source *get_sub_source(void) const { return this->tc_sub_source; };
|
||||
text_sub_source *get_sub_source() const { return this->tc_sub_source; };
|
||||
|
||||
textview_curses &set_delegate(text_delegate *del) {
|
||||
this->tc_delegate = del;
|
||||
@ -540,7 +573,7 @@ public:
|
||||
return *this;
|
||||
};
|
||||
|
||||
text_delegate *get_delegate(void) const { return this->tc_delegate; };
|
||||
text_delegate *get_delegate() const { return this->tc_delegate; };
|
||||
|
||||
void horiz_shift(vis_line_t start, vis_line_t end,
|
||||
int off_start,
|
||||
@ -615,7 +648,7 @@ public:
|
||||
if (this->tc_follow_deadline.tv_sec) {
|
||||
struct timeval now;
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
gettimeofday(&now, nullptr);
|
||||
if (this->tc_follow_deadline < now) {
|
||||
this->tc_follow_deadline.tv_sec = 0;
|
||||
this->tc_follow_deadline.tv_usec = 0;
|
||||
@ -697,7 +730,7 @@ public:
|
||||
timeradd(&now, &tv, &this->tc_follow_deadline);
|
||||
};
|
||||
|
||||
size_t get_match_count(void)
|
||||
size_t get_match_count()
|
||||
{
|
||||
return this->tc_bookmarks[&BM_SEARCH].size();
|
||||
};
|
||||
@ -718,9 +751,9 @@ public:
|
||||
|
||||
bool handle_mouse(mouse_event &me);
|
||||
|
||||
void reload_data(void);
|
||||
void reload_data();
|
||||
|
||||
void do_update(void) {
|
||||
void do_update() {
|
||||
this->listview_curses::do_update();
|
||||
if (this->tc_delegate != NULL) {
|
||||
this->tc_delegate->text_overlay(*this);
|
||||
@ -735,7 +768,66 @@ public:
|
||||
return retval;
|
||||
};
|
||||
|
||||
protected:
|
||||
void execute_search(const std::string ®ex_orig);
|
||||
|
||||
void redo_search() {
|
||||
if (this->tc_search_child) {
|
||||
grep_proc<vis_line_t> *gp = this->tc_search_child->get_grep_proc();
|
||||
|
||||
gp->invalidate();
|
||||
this->match_reset();
|
||||
gp->queue_request(0_vl)
|
||||
.start();
|
||||
|
||||
if (this->tc_source_search_child) {
|
||||
this->tc_source_search_child->invalidate()
|
||||
.queue_request(0_vl)
|
||||
.start();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void search_range(vis_line_t start, vis_line_t stop = -1_vl) {
|
||||
if (this->tc_search_child) {
|
||||
this->tc_search_child->get_grep_proc()->queue_request(start, stop);
|
||||
}
|
||||
if (this->tc_source_search_child) {
|
||||
this->tc_source_search_child->queue_request(start, stop);
|
||||
}
|
||||
}
|
||||
|
||||
void search_new_data() {
|
||||
this->search_range(-1_vl);
|
||||
if (this->tc_search_child) {
|
||||
this->tc_search_child->get_grep_proc()->start();
|
||||
}
|
||||
if (this->tc_source_search_child) {
|
||||
this->tc_source_search_child->start();
|
||||
}
|
||||
}
|
||||
|
||||
void update_poll_set(std::vector<struct pollfd> &pollfds) {
|
||||
if (this->tc_search_child) {
|
||||
this->tc_search_child->get_grep_proc()->update_poll_set(pollfds);
|
||||
}
|
||||
if (this->tc_source_search_child) {
|
||||
this->tc_source_search_child->update_poll_set(pollfds);
|
||||
}
|
||||
}
|
||||
|
||||
void check_poll_set(const std::vector<struct pollfd> &pollfds) {
|
||||
if (this->tc_search_child) {
|
||||
this->tc_search_child->get_grep_proc()->check_poll_set(pollfds);
|
||||
}
|
||||
if (this->tc_source_search_child) {
|
||||
this->tc_source_search_child->check_poll_set(pollfds);
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_last_search() const {
|
||||
return this->tc_last_search;
|
||||
}
|
||||
|
||||
void invoke_scroll() {
|
||||
if (this->tc_sub_source != nullptr) {
|
||||
auto ttt = dynamic_cast<text_time_translator *>(this->tc_sub_source);
|
||||
@ -748,6 +840,30 @@ protected:
|
||||
listview_curses::invoke_scroll();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
class grep_highlighter {
|
||||
public:
|
||||
grep_highlighter(std::unique_ptr<grep_proc<vis_line_t>> &gp,
|
||||
std::string hl_name,
|
||||
textview_curses::highlight_map_t &hl_map)
|
||||
: gh_grep_proc(std::move(gp)),
|
||||
gh_hl_name(std::move(hl_name)),
|
||||
gh_hl_map(hl_map) { };
|
||||
|
||||
~grep_highlighter()
|
||||
{
|
||||
this->gh_hl_map.erase(this->gh_hl_map.find(this->gh_hl_name));
|
||||
};
|
||||
|
||||
grep_proc<vis_line_t> *get_grep_proc() { return this->gh_grep_proc.get(); };
|
||||
|
||||
private:
|
||||
std::unique_ptr<grep_proc<vis_line_t>> gh_grep_proc;
|
||||
std::string gh_hl_name;
|
||||
textview_curses::highlight_map_t &gh_hl_map;
|
||||
};
|
||||
|
||||
text_sub_source *tc_sub_source;
|
||||
text_delegate *tc_delegate;
|
||||
|
||||
@ -763,6 +879,10 @@ protected:
|
||||
vis_line_t tc_selection_last;
|
||||
bool tc_selection_cleared;
|
||||
bool tc_hide_fields;
|
||||
|
||||
std::string tc_last_search;
|
||||
std::unique_ptr<grep_highlighter> tc_search_child;
|
||||
std::shared_ptr<grep_proc<vis_line_t>> tc_source_search_child;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -173,9 +173,10 @@ bool rgb_color::from_str(const string_fragment &color,
|
||||
|
||||
string_attr_type view_curses::VC_STYLE("style");
|
||||
string_attr_type view_curses::VC_GRAPHIC("graphic");
|
||||
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 }
|
||||
};
|
||||
|
||||
@ -412,6 +413,11 @@ void view_curses::mvwattrline(WINDOW *window,
|
||||
tab_count = count(line.begin(), line.end(), '\t');
|
||||
expanded_line = (char *)alloca(line.size() + tab_count * 8 + 1);
|
||||
|
||||
unsigned char *fg_color = (unsigned char *) alloca(line_width);
|
||||
bool has_fg = false;
|
||||
unsigned char *bg_color = (unsigned char *) alloca(line_width);
|
||||
bool has_bg = false;
|
||||
|
||||
for (size_t lpc = 0; lpc < line.size(); lpc++) {
|
||||
int exp_start_index = exp_index;
|
||||
unsigned char ch = static_cast<unsigned char>(line[lpc]);
|
||||
@ -482,7 +488,10 @@ void view_curses::mvwattrline(WINDOW *window,
|
||||
require(attr_range.lr_start >= 0);
|
||||
require(attr_range.lr_end >= -1);
|
||||
|
||||
if (!(iter->sa_type == &VC_STYLE || iter->sa_type == &VC_GRAPHIC)) {
|
||||
if (!(iter->sa_type == &VC_STYLE ||
|
||||
iter->sa_type == &VC_GRAPHIC ||
|
||||
iter->sa_type == &VC_FOREGROUND ||
|
||||
iter->sa_type == &VC_BACKGROUND)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -516,6 +525,24 @@ void view_curses::mvwattrline(WINDOW *window,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (iter->sa_type == &VC_FOREGROUND) {
|
||||
if (!has_fg) {
|
||||
memset(fg_color, COLOR_WHITE, line_width);
|
||||
}
|
||||
fill(fg_color + attr_range.lr_start, fg_color + attr_range.lr_end, iter->sa_value.sav_int);
|
||||
has_fg = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (iter->sa_type == &VC_BACKGROUND) {
|
||||
if (!has_bg) {
|
||||
memset(bg_color, COLOR_BLACK, line_width);
|
||||
}
|
||||
fill(bg_color + attr_range.lr_start, bg_color + attr_range.lr_end, iter->sa_value.sav_int);
|
||||
has_bg = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (attr_range.lr_end > attr_range.lr_start) {
|
||||
int awidth = attr_range.length();
|
||||
int color_pair;
|
||||
@ -548,13 +575,40 @@ void view_curses::mvwattrline(WINDOW *window,
|
||||
}
|
||||
if (clear_rev) {
|
||||
row_ch[lpc].attr &= ~A_REVERSE;
|
||||
log_debug(" %c %d clear_rev", row_ch[lpc].chars[0], lpc);
|
||||
}
|
||||
}
|
||||
mvwadd_wchnstr(window, y, x_pos, row_ch, ch_width);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 1
|
||||
if (has_fg || has_bg) {
|
||||
if (!has_fg) {
|
||||
memset(fg_color, COLOR_WHITE, line_width);
|
||||
}
|
||||
if (!has_bg) {
|
||||
memset(bg_color, COLOR_BLACK, line_width);
|
||||
}
|
||||
|
||||
int x_pos = x + lr.lr_start;
|
||||
int ch_width = lr.length();
|
||||
cchar_t row_ch[ch_width + 1];
|
||||
|
||||
mvwin_wchnstr(window, y, x_pos, row_ch, ch_width);
|
||||
for (int lpc = 0; lpc < ch_width; lpc++) {
|
||||
int color_pair = view_colors::ansi_color_pair_index(fg_color[lpc], bg_color[lpc]);
|
||||
|
||||
row_ch[lpc].attr = row_ch[lpc].attr & ~A_COLOR;
|
||||
#ifdef NCURSES_EXT_COLORS
|
||||
row_ch[lpc].ext_color = color_pair;
|
||||
#else
|
||||
row_ch[lpc].attr |= COLOR_PAIR(color_pair);
|
||||
#endif
|
||||
}
|
||||
mvwadd_wchnstr(window, y, x_pos, row_ch, ch_width);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
class color_listener : public lnav_config_listener {
|
||||
@ -661,6 +715,9 @@ inline attr_t attr_for_colors(int &pair_base, short fg, short bg)
|
||||
|
||||
void view_colors::init_roles(int color_pair_base)
|
||||
{
|
||||
rgb_color fg, bg;
|
||||
string err;
|
||||
|
||||
/* Setup the mappings from roles to actual colors. */
|
||||
this->vc_role_colors[VCR_TEXT] =
|
||||
attr_for_colors(color_pair_base, COLOR_WHITE, COLOR_BLACK);
|
||||
@ -692,6 +749,13 @@ void view_colors::init_roles(int color_pair_base)
|
||||
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);
|
||||
|
@ -62,6 +62,8 @@
|
||||
|
||||
#include "lnav_log.hh"
|
||||
#include "attr_line.hh"
|
||||
#include "optional.hpp"
|
||||
#include "lnav_util.hh"
|
||||
|
||||
#define KEY_CTRL_G 7
|
||||
#define KEY_CTRL_L 12
|
||||
@ -356,6 +358,8 @@ public:
|
||||
VCR_ACTIVE_STATUS2, /*< */
|
||||
VCR_BOLD_STATUS,
|
||||
VCR_VIEW_STATUS,
|
||||
VCR_INACTIVE_STATUS,
|
||||
VCR_POPUP,
|
||||
|
||||
VCR_KEYWORD,
|
||||
VCR_STRING,
|
||||
@ -504,12 +508,55 @@ public:
|
||||
/**
|
||||
* Update the curses display.
|
||||
*/
|
||||
virtual void do_update(void) = 0;
|
||||
virtual void do_update() {
|
||||
if (!this->vc_visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto child : this->vc_children) {
|
||||
child->do_update();
|
||||
}
|
||||
};
|
||||
|
||||
virtual bool handle_mouse(mouse_event &me) { return false; };
|
||||
|
||||
void set_needs_update() {
|
||||
this->vc_needs_update = true;
|
||||
for (auto child : this->vc_children) {
|
||||
child->set_needs_update();
|
||||
}
|
||||
};
|
||||
|
||||
view_curses &add_child_view(view_curses *child) {
|
||||
this->vc_children.push_back(child);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void set_default_role(view_colors::role_t role) {
|
||||
this->vc_default_role = role;
|
||||
}
|
||||
|
||||
void set_visible(bool value) {
|
||||
this->vc_visible = value;
|
||||
}
|
||||
|
||||
bool is_visible() {
|
||||
return this->vc_visible;
|
||||
}
|
||||
|
||||
void set_width(long width) {
|
||||
this->vc_width = width;
|
||||
}
|
||||
|
||||
long get_width() {
|
||||
return this->vc_width;
|
||||
}
|
||||
|
||||
static string_attr_type VC_STYLE;
|
||||
static string_attr_type VC_GRAPHIC;
|
||||
static string_attr_type VC_FOREGROUND;
|
||||
static string_attr_type VC_BACKGROUND;
|
||||
|
||||
static void mvwattrline(WINDOW *window,
|
||||
int y,
|
||||
@ -518,5 +565,47 @@ public:
|
||||
const struct line_range &lr,
|
||||
view_colors::role_t base_role =
|
||||
view_colors::VCR_TEXT);
|
||||
|
||||
protected:
|
||||
bool vc_visible{true};
|
||||
/** Flag to indicate if a display update is needed. */
|
||||
bool vc_needs_update{true};
|
||||
long vc_width;
|
||||
std::vector<view_curses *> vc_children;
|
||||
view_colors::role_t vc_default_role{view_colors::VCR_TEXT};
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class view_stack : public view_curses {
|
||||
public:
|
||||
nonstd::optional<T *> top() {
|
||||
if (this->vs_views.empty()) {
|
||||
return nonstd::nullopt;
|
||||
} else {
|
||||
return this->vs_views.back();
|
||||
}
|
||||
}
|
||||
|
||||
void do_update() override
|
||||
{
|
||||
if (!this->vc_visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->top() | [this] (T *vc) {
|
||||
if (this->vc_needs_update) {
|
||||
vc->set_needs_update();
|
||||
}
|
||||
vc->do_update();
|
||||
};
|
||||
|
||||
view_curses::do_update();
|
||||
|
||||
this->vc_needs_update = false;
|
||||
}
|
||||
|
||||
std::vector<T *> vs_views;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
418
src/view_helpers.cc
Normal file
418
src/view_helpers.cc
Normal file
@ -0,0 +1,418 @@
|
||||
/**
|
||||
* Copyright (c) 2018, 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 "lnav.hh"
|
||||
#include "sql_util.hh"
|
||||
#include "pretty_printer.hh"
|
||||
#include "environ_vtab.hh"
|
||||
#include "vtab_module.hh"
|
||||
#include "shlex.hh"
|
||||
#include "help.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
static void open_schema_view()
|
||||
{
|
||||
textview_curses *schema_tc = &lnav_data.ld_views[LNV_SCHEMA];
|
||||
string schema;
|
||||
|
||||
dump_sqlite_schema(lnav_data.ld_db, schema);
|
||||
|
||||
schema += "\n\n-- Virtual Table Definitions --\n\n";
|
||||
schema += ENVIRON_CREATE_STMT;
|
||||
schema += vtab_module_schemas;
|
||||
for (const auto &vtab_iter : *lnav_data.ld_vtab_manager) {
|
||||
schema += "\n" + vtab_iter.second->get_table_statement();
|
||||
}
|
||||
|
||||
delete schema_tc->get_sub_source();
|
||||
|
||||
auto *pts = new plain_text_source(schema);
|
||||
pts->set_text_format(TF_SQL);
|
||||
|
||||
schema_tc->set_sub_source(pts);
|
||||
}
|
||||
|
||||
static void open_pretty_view(void)
|
||||
{
|
||||
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];
|
||||
textview_curses *log_tc = &lnav_data.ld_views[LNV_LOG];
|
||||
textview_curses *text_tc = &lnav_data.ld_views[LNV_TEXT];
|
||||
attr_line_t full_text;
|
||||
|
||||
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;
|
||||
|
||||
for (vis_line_t vl = log_tc->get_top(); vl <= log_tc->get_bottom(); ++vl) {
|
||||
content_line_t cl = lss.at(vl);
|
||||
shared_ptr<logfile> lf = lss.find(cl);
|
||||
auto ll = lf->begin() + cl;
|
||||
shared_buffer_ref sbr;
|
||||
|
||||
if (!first_line && ll->is_continued()) {
|
||||
continue;
|
||||
}
|
||||
auto ll_start = lf->message_start(ll);
|
||||
attr_line_t al;
|
||||
|
||||
vl -= vis_line_t(distance(ll_start, ll));
|
||||
lss.text_value_for_line(*log_tc, vl, al.get_string(),
|
||||
text_sub_source::RF_FULL|
|
||||
text_sub_source::RF_REWRITE);
|
||||
lss.text_attrs_for_line(*log_tc, vl, al.get_attrs());
|
||||
|
||||
line_range orig_lr = find_string_attr_range(
|
||||
al.get_attrs(), &textview_curses::SA_ORIGINAL_LINE);
|
||||
attr_line_t orig_al = al.subline(orig_lr.lr_start, orig_lr.length());
|
||||
attr_line_t prefix_al = al.subline(0, orig_lr.lr_start);
|
||||
|
||||
data_scanner ds(orig_al.get_string());
|
||||
pretty_printer pp(&ds, orig_al.get_attrs());
|
||||
attr_line_t pretty_al;
|
||||
vector<attr_line_t> pretty_lines;
|
||||
|
||||
// TODO: dump more details of the line in the output.
|
||||
pp.append_to(pretty_al);
|
||||
pretty_al.split_lines(pretty_lines);
|
||||
|
||||
for (auto &pretty_line : pretty_lines) {
|
||||
if (pretty_line.empty() && &pretty_line == &pretty_lines.back()) {
|
||||
break;
|
||||
}
|
||||
pretty_line.insert(0, prefix_al);
|
||||
pretty_line.append("\n");
|
||||
full_text.append(pretty_line);
|
||||
}
|
||||
|
||||
first_line = false;
|
||||
}
|
||||
|
||||
if (!full_text.empty()) {
|
||||
full_text.erase(full_text.length() - 1, 1);
|
||||
}
|
||||
}
|
||||
else if (top_tc == text_tc) {
|
||||
shared_ptr<logfile> lf = lnav_data.ld_text_source.current_file();
|
||||
|
||||
for (vis_line_t vl = text_tc->get_top(); vl <= text_tc->get_bottom(); ++vl) {
|
||||
auto ll = lf->begin() + vl;
|
||||
shared_buffer_ref sbr;
|
||||
|
||||
lf->read_full_message(ll, sbr);
|
||||
data_scanner ds(sbr);
|
||||
string_attrs_t sa;
|
||||
pretty_printer pp(&ds, sa);
|
||||
|
||||
pp.append_to(full_text);
|
||||
}
|
||||
}
|
||||
auto *pts = new plain_text_source();
|
||||
pts->replace_with(full_text);
|
||||
pretty_tc->set_sub_source(pts);
|
||||
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();
|
||||
pretty_tc->redo_search();
|
||||
}
|
||||
|
||||
static void build_all_help_text()
|
||||
{
|
||||
if (!lnav_data.ld_help_source.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
attr_line_t all_help_text;
|
||||
shlex lexer(help_txt, strlen(help_txt));
|
||||
string sub_help_text;
|
||||
|
||||
lexer.with_ignore_quotes(true)
|
||||
.eval(sub_help_text, lnav_data.ld_exec_context.ec_global_vars);
|
||||
all_help_text.with_ansi_string(sub_help_text);
|
||||
|
||||
map<string, help_text *> sql_funcs;
|
||||
map<string, help_text *> sql_keywords;
|
||||
|
||||
for (auto iter : sqlite_function_help) {
|
||||
switch (iter.second->ht_context) {
|
||||
case HC_SQL_FUNCTION:
|
||||
sql_funcs[iter.second->ht_name] = iter.second;
|
||||
break;
|
||||
case HC_SQL_KEYWORD:
|
||||
sql_keywords[iter.second->ht_name] = iter.second;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto iter : sql_funcs) {
|
||||
all_help_text.append(2, '\n');
|
||||
format_help_text_for_term(*iter.second, 79, all_help_text);
|
||||
if (!iter.second->ht_example.empty()) {
|
||||
all_help_text.append(1, '\n');
|
||||
format_example_text_for_term(*iter.second, 79, all_help_text);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto iter : sql_keywords) {
|
||||
all_help_text.append(2, '\n');
|
||||
format_help_text_for_term(*iter.second, 79, all_help_text);
|
||||
if (!iter.second->ht_example.empty()) {
|
||||
all_help_text.append(1, '\n');
|
||||
format_example_text_for_term(*iter.second, 79, all_help_text);
|
||||
}
|
||||
}
|
||||
|
||||
lnav_data.ld_help_source.replace_with(all_help_text);
|
||||
}
|
||||
|
||||
void layout_views()
|
||||
{
|
||||
unsigned long width, height;
|
||||
|
||||
getmaxyx(lnav_data.ld_window, height, width);
|
||||
int doc_height;
|
||||
bool doc_side_by_side = width > (90 + 60);
|
||||
bool preview_status_open = !lnav_data.ld_preview_status_source
|
||||
.get_description().empty();
|
||||
|
||||
if (doc_side_by_side) {
|
||||
doc_height = std::max(
|
||||
lnav_data.ld_doc_source.text_line_count(),
|
||||
lnav_data.ld_example_source.text_line_count());
|
||||
} else {
|
||||
doc_height =
|
||||
lnav_data.ld_doc_source.text_line_count() +
|
||||
lnav_data.ld_example_source.text_line_count();
|
||||
}
|
||||
|
||||
int preview_height = lnav_data.ld_preview_hidden ? 0 :
|
||||
lnav_data.ld_preview_source.text_line_count();
|
||||
int match_rows = lnav_data.ld_match_source.text_line_count();
|
||||
int match_height = min((unsigned long)match_rows, (height - 4) / 2);
|
||||
|
||||
lnav_data.ld_match_view.set_height(vis_line_t(match_height));
|
||||
|
||||
if (doc_height + 14 > ((int) height - match_height - preview_height - 2)) {
|
||||
doc_height = 0;
|
||||
preview_height = 0;
|
||||
preview_status_open = false;
|
||||
}
|
||||
|
||||
bool doc_open = doc_height > 0;
|
||||
bool filters_open = lnav_data.ld_mode == LNM_FILTER &&
|
||||
!preview_status_open &&
|
||||
!doc_open;
|
||||
int filter_height = filters_open ? 3 : 0;
|
||||
|
||||
int bottom_height =
|
||||
(doc_open ? 1 : 0)
|
||||
+ doc_height
|
||||
+ (preview_status_open ? 1 : 0)
|
||||
+ preview_height
|
||||
+ 1 // bottom status
|
||||
+ match_height
|
||||
+ lnav_data.ld_rl_view->get_height();
|
||||
|
||||
for (auto &tc : lnav_data.ld_views) {
|
||||
tc.set_height(vis_line_t(-(bottom_height + filter_height)));
|
||||
}
|
||||
lnav_data.ld_status[LNS_TOP].set_enabled(!filters_open);
|
||||
lnav_data.ld_status[LNS_FILTER].set_enabled(filters_open);
|
||||
lnav_data.ld_status[LNS_FILTER].set_top(-(bottom_height + filter_height + 1));
|
||||
lnav_data.ld_status[LNS_BOTTOM].set_top(-(match_height + 2));
|
||||
lnav_data.ld_status[LNS_DOC].set_top(height - bottom_height);
|
||||
lnav_data.ld_status[LNS_DOC].set_visible(doc_open);
|
||||
lnav_data.ld_status[LNS_PREVIEW].set_top(height
|
||||
- bottom_height
|
||||
+ (doc_open ? 1 : 0)
|
||||
+ doc_height);
|
||||
lnav_data.ld_status[LNS_PREVIEW].set_visible(preview_status_open);
|
||||
|
||||
if (!doc_open || doc_side_by_side) {
|
||||
lnav_data.ld_doc_view.set_height(vis_line_t(doc_height));
|
||||
} else {
|
||||
lnav_data.ld_doc_view.set_height(vis_line_t(lnav_data.ld_doc_source.text_line_count()));
|
||||
}
|
||||
lnav_data.ld_doc_view.set_y(height - bottom_height + 1);
|
||||
|
||||
if (!doc_open || doc_side_by_side) {
|
||||
lnav_data.ld_example_view.set_height(vis_line_t(doc_height));
|
||||
lnav_data.ld_example_view.set_x(90);
|
||||
lnav_data.ld_example_view.set_y(height - bottom_height + 1);
|
||||
} else {
|
||||
lnav_data.ld_example_view.set_height(vis_line_t(lnav_data.ld_example_source.text_line_count()));
|
||||
lnav_data.ld_example_view.set_x(0);
|
||||
lnav_data.ld_example_view.set_y(height - bottom_height + lnav_data.ld_doc_view.get_height() + 1);
|
||||
}
|
||||
|
||||
lnav_data.ld_filter_view.set_height(vis_line_t(filter_height));
|
||||
lnav_data.ld_filter_view.set_y(height - bottom_height - filter_height);
|
||||
lnav_data.ld_filter_view.set_width(width);
|
||||
|
||||
lnav_data.ld_preview_view.set_height(vis_line_t(preview_height));
|
||||
lnav_data.ld_preview_view.set_y(height
|
||||
- bottom_height
|
||||
+ 1
|
||||
+ (doc_open ? 1 : 0)
|
||||
+ doc_height);
|
||||
lnav_data.ld_match_view.set_y(
|
||||
height
|
||||
- lnav_data.ld_rl_view->get_height()
|
||||
- match_height);
|
||||
lnav_data.ld_rl_view->set_width(width);
|
||||
}
|
||||
|
||||
void execute_examples()
|
||||
{
|
||||
exec_context &ec = lnav_data.ld_exec_context;
|
||||
db_label_source &dls = lnav_data.ld_db_row_source;
|
||||
db_overlay_source &dos = lnav_data.ld_db_overlay;
|
||||
textview_curses &db_tc = lnav_data.ld_views[LNV_DB];
|
||||
|
||||
for (auto &help_iter : sqlite_function_help) {
|
||||
struct help_text &ht = *(help_iter.second);
|
||||
|
||||
for (auto &ex : ht.ht_example) {
|
||||
string alt_msg;
|
||||
|
||||
switch (ht.ht_context) {
|
||||
case HC_SQL_FUNCTION: {
|
||||
execute_sql(ec, ex.he_cmd, alt_msg);
|
||||
|
||||
if (dls.dls_rows.size() == 1 &&
|
||||
dls.dls_rows[0].size() == 1) {
|
||||
ex.he_result.append(dls.dls_rows[0][0]);
|
||||
} else {
|
||||
attr_line_t al;
|
||||
dos.list_value_for_overlay(db_tc,
|
||||
0, 1,
|
||||
vis_line_t(0),
|
||||
al);
|
||||
ex.he_result.append(al);
|
||||
for (int lpc = 0;
|
||||
lpc < (int)dls.text_line_count(); lpc++) {
|
||||
al.clear();
|
||||
dls.text_value_for_line(db_tc, lpc,
|
||||
al.get_string(),
|
||||
false);
|
||||
dls.text_attrs_for_line(db_tc, lpc,
|
||||
al.get_attrs());
|
||||
ex.he_result.append("\n")
|
||||
.append(al);
|
||||
}
|
||||
}
|
||||
|
||||
log_debug("example: %s", ex.he_cmd);
|
||||
log_debug("example result: %s",
|
||||
ex.he_result.get_string().c_str());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
log_warning("Not executing example: %s", ex.he_cmd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dls.clear();
|
||||
}
|
||||
|
||||
bool toggle_view(textview_curses *toggle_tc)
|
||||
{
|
||||
textview_curses *tc = lnav_data.ld_view_stack.top().value_or(nullptr);
|
||||
bool retval = false;
|
||||
|
||||
require(toggle_tc != NULL);
|
||||
require(toggle_tc >= &lnav_data.ld_views[0]);
|
||||
require(toggle_tc < &lnav_data.ld_views[LNV__MAX]);
|
||||
|
||||
if (tc == toggle_tc) {
|
||||
if (lnav_data.ld_view_stack.vs_views.size() == 1) {
|
||||
return false;
|
||||
}
|
||||
lnav_data.ld_last_view = tc;
|
||||
lnav_data.ld_view_stack.vs_views.pop_back();
|
||||
}
|
||||
else {
|
||||
if (toggle_tc == &lnav_data.ld_views[LNV_SCHEMA]) {
|
||||
open_schema_view();
|
||||
}
|
||||
else if (toggle_tc == &lnav_data.ld_views[LNV_PRETTY]) {
|
||||
open_pretty_view();
|
||||
}
|
||||
else if (toggle_tc == &lnav_data.ld_views[LNV_HISTOGRAM]) {
|
||||
// Rebuild to reflect changes in marks.
|
||||
rebuild_hist();
|
||||
}
|
||||
else if (toggle_tc == &lnav_data.ld_views[LNV_HELP]) {
|
||||
build_all_help_text();
|
||||
}
|
||||
lnav_data.ld_last_view = nullptr;
|
||||
lnav_data.ld_view_stack.vs_views.push_back(toggle_tc);
|
||||
retval = true;
|
||||
}
|
||||
tc = *lnav_data.ld_view_stack.top();
|
||||
tc->set_needs_update();
|
||||
lnav_data.ld_view_stack_broadcaster.invoke(tc);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the view is on the top of the view stack.
|
||||
*
|
||||
* @param expected_tc The text view that should be on top.
|
||||
* @return True if the view was already on the top of the stack.
|
||||
*/
|
||||
bool ensure_view(textview_curses *expected_tc)
|
||||
{
|
||||
textview_curses *tc = lnav_data.ld_view_stack.top().value_or(nullptr);
|
||||
bool retval = true;
|
||||
|
||||
if (tc != expected_tc) {
|
||||
toggle_view(expected_tc);
|
||||
retval = false;
|
||||
}
|
||||
return retval;
|
||||
}
|
@ -47,6 +47,70 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
template<>
|
||||
struct from_sqlite<lnav_view_t> {
|
||||
inline lnav_view_t operator()(int argc, sqlite3_value **val, int argi) {
|
||||
const char *view_name = (const char *) sqlite3_value_text(val[argi]);
|
||||
|
||||
if (view_name == nullptr) {
|
||||
throw from_sqlite_conversion_error("lnav view name", argi);
|
||||
}
|
||||
|
||||
auto view_name_iter = find_if(
|
||||
::begin(lnav_view_strings), ::end(lnav_view_strings),
|
||||
[&](const char *v) {
|
||||
return v != nullptr && strcasecmp(v, view_name) == 0;
|
||||
});
|
||||
|
||||
if (view_name_iter == ::end(lnav_view_strings)) {
|
||||
throw from_sqlite_conversion_error("lnav view name", argi);
|
||||
}
|
||||
|
||||
return lnav_view_t(view_name_iter - lnav_view_strings);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct from_sqlite<text_filter::type_t> {
|
||||
inline text_filter::type_t operator()(int argc, sqlite3_value **val, int argi) {
|
||||
const char *type_name = (const char *) sqlite3_value_text(val[argi]);
|
||||
|
||||
if (strcasecmp(type_name, "in") == 0) {
|
||||
return text_filter::INCLUDE;
|
||||
} else if (strcasecmp(type_name, "out") == 0) {
|
||||
return text_filter::EXCLUDE;
|
||||
}
|
||||
|
||||
throw from_sqlite_conversion_error("filter type", argi);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct from_sqlite<pair<string, pcre *>> {
|
||||
inline pair<string, pcre *>operator()(int argc, sqlite3_value **val, int argi) {
|
||||
const char *pattern = (const char *) sqlite3_value_text(val[argi]);
|
||||
const char *errptr;
|
||||
pcre *code;
|
||||
int eoff;
|
||||
|
||||
if (pattern == nullptr || pattern[0] == '\0') {
|
||||
throw from_sqlite_conversion_error("non-empty pattern", argi);
|
||||
}
|
||||
|
||||
code = pcre_compile(pattern,
|
||||
PCRE_CASELESS,
|
||||
&errptr,
|
||||
&eoff,
|
||||
nullptr);
|
||||
|
||||
if (code == nullptr) {
|
||||
throw from_sqlite_conversion_error(errptr, argi);
|
||||
}
|
||||
|
||||
return make_pair(string(pattern), code);
|
||||
}
|
||||
};
|
||||
|
||||
struct lnav_views : public tvt_iterator_cursor<lnav_views> {
|
||||
struct vtab {
|
||||
sqlite3_vtab base;
|
||||
@ -126,7 +190,7 @@ CREATE TABLE lnav_views (
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
const string &str = lnav_data.ld_last_search[view_index];
|
||||
const string &str = tc.get_last_search();
|
||||
|
||||
sqlite3_result_text(ctx, str.c_str(), str.length(), SQLITE_TRANSIENT);
|
||||
break;
|
||||
@ -180,10 +244,7 @@ CREATE TABLE lnav_views (
|
||||
}
|
||||
}
|
||||
tc.set_left(left);
|
||||
|
||||
if (search != lnav_data.ld_last_search[index]) {
|
||||
execute_search((lnav_view_t) index, search);
|
||||
}
|
||||
tc.execute_search(search);
|
||||
|
||||
return SQLITE_OK;
|
||||
};
|
||||
@ -208,11 +269,11 @@ CREATE TABLE lnav_view_stack (
|
||||
};
|
||||
|
||||
iterator begin() {
|
||||
return lnav_data.ld_view_stack.begin();
|
||||
return lnav_data.ld_view_stack.vs_views.begin();
|
||||
}
|
||||
|
||||
iterator end() {
|
||||
return lnav_data.ld_view_stack.end();
|
||||
return lnav_data.ld_view_stack.vs_views.end();
|
||||
}
|
||||
|
||||
int get_column(cursor &vc, sqlite3_context *ctx, int col) {
|
||||
@ -231,16 +292,16 @@ CREATE TABLE lnav_view_stack (
|
||||
};
|
||||
|
||||
int delete_row(sqlite3_vtab *tab, sqlite3_int64 rowid) {
|
||||
if ((size_t)rowid != lnav_data.ld_view_stack.size() - 1) {
|
||||
if ((size_t)rowid != lnav_data.ld_view_stack.vs_views.size() - 1) {
|
||||
tab->zErrMsg = sqlite3_mprintf(
|
||||
"Only the top view in the stack can be deleted");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
lnav_data.ld_last_view = lnav_data.ld_view_stack.back();
|
||||
lnav_data.ld_view_stack.pop_back();
|
||||
if (!lnav_data.ld_view_stack.empty()) {
|
||||
textview_curses *tc = lnav_data.ld_view_stack.back();
|
||||
lnav_data.ld_last_view = *lnav_data.ld_view_stack.top();
|
||||
lnav_data.ld_view_stack.vs_views.pop_back();
|
||||
if (!lnav_data.ld_view_stack.vs_views.empty()) {
|
||||
textview_curses *tc = *lnav_data.ld_view_stack.top();
|
||||
|
||||
tc->set_needs_update();
|
||||
lnav_data.ld_view_stack_broadcaster.invoke(tc);
|
||||
@ -251,30 +312,11 @@ CREATE TABLE lnav_view_stack (
|
||||
|
||||
int insert_row(sqlite3_vtab *tab,
|
||||
sqlite3_int64 &rowid_out,
|
||||
const char *name) {
|
||||
if (name == nullptr) {
|
||||
tab->zErrMsg = sqlite3_mprintf("'name' cannot be null");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
auto view_name_iter = find_if(
|
||||
::begin(lnav_view_strings), ::end(lnav_view_strings),
|
||||
[&](const char *v) {
|
||||
return v != nullptr && strcmp(v, name) == 0;
|
||||
});
|
||||
|
||||
if (view_name_iter == ::end(lnav_view_strings)) {
|
||||
tab->zErrMsg = sqlite3_mprintf("Unknown view name: %s", name);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
lnav_view_t view_index = lnav_view_t(view_name_iter - lnav_view_strings);
|
||||
lnav_view_t view_index) {
|
||||
textview_curses *tc = &lnav_data.ld_views[view_index];
|
||||
lnav_data.ld_view_stack.push_back(tc);
|
||||
tc->set_needs_update();
|
||||
lnav_data.ld_view_stack_broadcaster.invoke(tc);
|
||||
|
||||
rowid_out = lnav_data.ld_view_stack.size() - 1;
|
||||
ensure_view(tc);
|
||||
rowid_out = lnav_data.ld_view_stack.vs_views.size() - 1;
|
||||
|
||||
return SQLITE_OK;
|
||||
};
|
||||
@ -286,10 +328,216 @@ CREATE TABLE lnav_view_stack (
|
||||
};
|
||||
};
|
||||
|
||||
struct lnav_view_filters : public tvt_iterator_cursor<lnav_view_filters> {
|
||||
struct vtab {
|
||||
sqlite3_vtab base;
|
||||
|
||||
operator sqlite3_vtab *() {
|
||||
return &this->base;
|
||||
};
|
||||
};
|
||||
|
||||
struct iterator {
|
||||
using difference_type = int;
|
||||
using value_type = text_filter;
|
||||
using pointer = text_filter *;
|
||||
using reference = text_filter &;
|
||||
using iterator_category = forward_iterator_tag;
|
||||
|
||||
lnav_view_t i_view_index;
|
||||
int i_filter_index;
|
||||
|
||||
iterator(lnav_view_t view = LNV_LOG, int filter = -1)
|
||||
: i_view_index(view), i_filter_index(filter) {
|
||||
}
|
||||
|
||||
iterator &operator++() {
|
||||
while (this->i_view_index < LNV__MAX) {
|
||||
textview_curses &tc = lnav_data.ld_views[this->i_view_index];
|
||||
text_sub_source *tss = tc.get_sub_source();
|
||||
|
||||
if (tss == nullptr) {
|
||||
this->i_view_index = lnav_view_t(this->i_view_index + 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
filter_stack &fs = tss->get_filters();
|
||||
|
||||
this->i_filter_index += 1;
|
||||
if (this->i_filter_index >= fs.size()) {
|
||||
this->i_filter_index = -1;
|
||||
this->i_view_index = lnav_view_t(this->i_view_index + 1);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const iterator &other) const {
|
||||
return this->i_view_index == other.i_view_index &&
|
||||
this->i_filter_index == other.i_filter_index;
|
||||
}
|
||||
|
||||
bool operator!=(const iterator &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr const char *CREATE_STMT = R"(
|
||||
-- Access lnav's filters through this table.
|
||||
CREATE TABLE lnav_view_filters (
|
||||
view_name text,
|
||||
enabled integer,
|
||||
type text,
|
||||
pattern text
|
||||
);
|
||||
)";
|
||||
|
||||
iterator begin() {
|
||||
iterator retval = iterator();
|
||||
|
||||
return ++retval;
|
||||
}
|
||||
|
||||
iterator end() {
|
||||
return iterator(LNV__MAX, -1);
|
||||
}
|
||||
|
||||
sqlite_int64 get_rowid(iterator iter) {
|
||||
textview_curses &tc = lnav_data.ld_views[iter.i_view_index];
|
||||
text_sub_source *tss = tc.get_sub_source();
|
||||
filter_stack &fs = tss->get_filters();
|
||||
auto &tf = *(fs.begin() + iter.i_filter_index);
|
||||
|
||||
sqlite_int64 retval = iter.i_view_index;
|
||||
|
||||
retval = retval << 32;
|
||||
retval = retval | tf->get_index();
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
int get_column(cursor &vc, sqlite3_context *ctx, int col) {
|
||||
textview_curses &tc = lnav_data.ld_views[vc.iter.i_view_index];
|
||||
text_sub_source *tss = tc.get_sub_source();
|
||||
filter_stack &fs = tss->get_filters();
|
||||
auto tf = *(fs.begin() + vc.iter.i_filter_index);
|
||||
|
||||
switch (col) {
|
||||
case 0:
|
||||
sqlite3_result_text(ctx,
|
||||
lnav_view_strings[vc.iter.i_view_index], -1,
|
||||
SQLITE_STATIC);
|
||||
break;
|
||||
case 1:
|
||||
sqlite3_result_int(ctx, tf->is_enabled());
|
||||
break;
|
||||
case 2:
|
||||
switch (tf->get_type()) {
|
||||
case text_filter::INCLUDE:
|
||||
sqlite3_result_text(ctx, "in", 2, SQLITE_STATIC);
|
||||
break;
|
||||
case text_filter::EXCLUDE:
|
||||
sqlite3_result_text(ctx, "out", 3, SQLITE_STATIC);
|
||||
break;
|
||||
default:
|
||||
ensure(0);
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
sqlite3_result_text(ctx,
|
||||
tf->get_id().c_str(),
|
||||
-1,
|
||||
SQLITE_TRANSIENT);
|
||||
break;
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
int insert_row(sqlite3_vtab *tab,
|
||||
sqlite3_int64 &rowid_out,
|
||||
lnav_view_t view_index,
|
||||
bool enabled,
|
||||
text_filter::type_t type,
|
||||
pair<string, pcre *> pattern) {
|
||||
textview_curses &tc = lnav_data.ld_views[view_index];
|
||||
text_sub_source *tss = tc.get_sub_source();
|
||||
filter_stack &fs = tss->get_filters();
|
||||
auto pf = make_shared<pcre_filter>(type,
|
||||
pattern.first,
|
||||
fs.next_index(),
|
||||
pattern.second);
|
||||
fs.add_filter(pf);
|
||||
tss->text_filters_changed();
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
int delete_row(sqlite3_vtab *tab, sqlite3_int64 rowid) {
|
||||
lnav_view_t view_index = lnav_view_t(rowid >> 32);
|
||||
int filter_index = rowid & 0xffffffffLL;
|
||||
textview_curses &tc = lnav_data.ld_views[view_index];
|
||||
text_sub_source *tss = tc.get_sub_source();
|
||||
filter_stack &fs = tss->get_filters();
|
||||
|
||||
for (const auto &iter : fs) {
|
||||
if (iter->get_index() == filter_index) {
|
||||
fs.delete_filter(iter->get_id());
|
||||
tss->text_filters_changed();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
int update_row(sqlite3_vtab *tab,
|
||||
sqlite3_int64 &rowid,
|
||||
lnav_view_t new_view_index,
|
||||
bool enabled,
|
||||
text_filter::type_t type,
|
||||
pair<string, pcre *> pattern) {
|
||||
lnav_view_t view_index = lnav_view_t(rowid >> 32);
|
||||
int filter_index = rowid & 0xffffffffLL;
|
||||
textview_curses &tc = lnav_data.ld_views[view_index];
|
||||
text_sub_source *tss = tc.get_sub_source();
|
||||
filter_stack &fs = tss->get_filters();
|
||||
auto iter = fs.begin() + filter_index;
|
||||
shared_ptr<text_filter> tf = *iter;
|
||||
|
||||
if (new_view_index != view_index) {
|
||||
tab->zErrMsg = sqlite3_mprintf(
|
||||
"The view for a filter cannot be changed");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
tf->lf_deleted = true;
|
||||
tss->text_filters_changed();
|
||||
|
||||
auto pf = make_shared<pcre_filter>(type,
|
||||
pattern.first,
|
||||
tf->get_index(),
|
||||
pattern.second);
|
||||
|
||||
if (!enabled) {
|
||||
pf->disable();
|
||||
}
|
||||
|
||||
*iter = pf;
|
||||
tss->text_filters_changed();
|
||||
|
||||
return SQLITE_OK;
|
||||
};
|
||||
};
|
||||
|
||||
int register_views_vtab(sqlite3 *db)
|
||||
{
|
||||
static vtab_module<lnav_views> LNAV_VIEWS_MODULE;
|
||||
static vtab_module<lnav_view_stack> LNAV_VIEW_STACK_MODULE;
|
||||
static vtab_module<lnav_view_filters> LNAV_VIEW_FILTERS_MODULE;
|
||||
|
||||
int rc;
|
||||
|
||||
@ -299,5 +547,8 @@ int register_views_vtab(sqlite3 *db)
|
||||
rc = LNAV_VIEW_STACK_MODULE.create(db, "lnav_view_stack");
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
rc = LNAV_VIEW_FILTERS_MODULE.create(db, "lnav_view_filters");
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
@ -194,12 +194,10 @@ const char *vt52_curses::map_input(int ch, int &len_out)
|
||||
|
||||
void vt52_curses::map_output(const char *output, int len)
|
||||
{
|
||||
int y, lpc;
|
||||
int lpc;
|
||||
|
||||
require(this->vc_window != NULL);
|
||||
require(this->vc_window != nullptr);
|
||||
|
||||
y = this->get_actual_y();
|
||||
wmove(this->vc_window, y, this->vc_x);
|
||||
for (lpc = 0; lpc < len; lpc++) {
|
||||
if (this->vc_escape_len > 0) {
|
||||
const char *cap;
|
||||
@ -209,19 +207,17 @@ void vt52_curses::map_output(const char *output, int len)
|
||||
this->vc_escape[this->vc_escape_len] = '\0';
|
||||
|
||||
if ((cap = vt52_escape_map::singleton()[this->vc_escape]) !=
|
||||
NULL) {
|
||||
nullptr) {
|
||||
if (strcmp(cap, "ce") == 0) {
|
||||
wclrtoeol(this->vc_window);
|
||||
this->vc_line.erase(this->vc_x);
|
||||
this->vc_escape_len = 0;
|
||||
}
|
||||
else if (strcmp(cap, "kl") == 0) {
|
||||
this->vc_x -= 1;
|
||||
wmove(this->vc_window, y, this->vc_x);
|
||||
this->vc_escape_len = 0;
|
||||
}
|
||||
else if (strcmp(cap, "kr") == 0) {
|
||||
this->vc_x += 1;
|
||||
wmove(this->vc_window, y, this->vc_x);
|
||||
this->vc_escape_len = 0;
|
||||
}
|
||||
else {
|
||||
@ -232,10 +228,8 @@ void vt52_curses::map_output(const char *output, int len)
|
||||
else {
|
||||
switch (output[lpc]) {
|
||||
case STX:
|
||||
this->vc_past_lines.clear();
|
||||
this->vc_x = 0;
|
||||
wmove(this->vc_window, y, this->vc_x);
|
||||
wclrtoeol(this->vc_window);
|
||||
this->vc_line.clear();
|
||||
break;
|
||||
|
||||
case BELL:
|
||||
@ -244,45 +238,28 @@ void vt52_curses::map_output(const char *output, int len)
|
||||
|
||||
case BACKSPACE:
|
||||
this->vc_x -= 1;
|
||||
wmove(this->vc_window, y, this->vc_x);
|
||||
break;
|
||||
|
||||
case ESCAPE:
|
||||
this->vc_escape[0] = ESCAPE;
|
||||
this->vc_escape[0] = ESCAPE;
|
||||
this->vc_escape_len = 1;
|
||||
break;
|
||||
|
||||
case '\n':
|
||||
{
|
||||
unsigned long width, height;
|
||||
char * buffer;
|
||||
|
||||
getmaxyx(this->vc_window, height, width);
|
||||
(void)height; //suppress unused warnings.
|
||||
|
||||
buffer = (char *)alloca(width);
|
||||
this->vc_x = 0;
|
||||
wmove(this->vc_window, y, this->vc_x);
|
||||
|
||||
/*
|
||||
* XXX Not sure why we need to subtract one from the width
|
||||
* here, but otherwise it seems to spill over and screw up
|
||||
* the next line when we're writing it back out in
|
||||
* do_update().
|
||||
*/
|
||||
winnstr(this->vc_window, buffer, width - 1);
|
||||
this->vc_past_lines.push_back(buffer);
|
||||
wclrtoeol(this->vc_window);
|
||||
}
|
||||
break;
|
||||
this->vc_line.clear();
|
||||
break;
|
||||
|
||||
case '\r':
|
||||
this->vc_x = 0;
|
||||
wmove(this->vc_window, y, this->vc_x);
|
||||
break;
|
||||
|
||||
default:
|
||||
mvwaddch(this->vc_window, y, this->vc_x, output[lpc]);
|
||||
if (this->vc_x < this->vc_line.length()) {
|
||||
this->vc_line.get_string()[this->vc_x] = output[lpc];
|
||||
} else {
|
||||
this->vc_line.append(1, output[lpc]);
|
||||
}
|
||||
this->vc_x += 1;
|
||||
break;
|
||||
}
|
||||
@ -290,19 +267,10 @@ void vt52_curses::map_output(const char *output, int len)
|
||||
}
|
||||
}
|
||||
|
||||
void vt52_curses::do_update(void)
|
||||
void vt52_curses::do_update()
|
||||
{
|
||||
list<string>::iterator iter;
|
||||
int y;
|
||||
|
||||
y = this->get_actual_y() - (int)this->vc_past_lines.size();
|
||||
for (iter = this->vc_past_lines.begin();
|
||||
iter != this->vc_past_lines.end();
|
||||
++iter, y++) {
|
||||
if (y >= 0) {
|
||||
mvwprintw(this->vc_window, y, 0, "%s", iter->c_str());
|
||||
wclrtoeol(this->vc_window);
|
||||
}
|
||||
}
|
||||
wmove(this->vc_window, y, this->vc_x);
|
||||
view_curses::mvwattrline(this->vc_window,
|
||||
this->get_actual_y(), this->vc_left,
|
||||
this->vc_line,
|
||||
line_range{ 0, (int) this->vc_width });
|
||||
}
|
||||
|
@ -63,6 +63,14 @@ public:
|
||||
/** @return The curses window this view is attached to. */
|
||||
WINDOW *get_window() { return this->vc_window; };
|
||||
|
||||
void set_left(int left) {
|
||||
this->vc_left = left;
|
||||
};
|
||||
|
||||
int get_left() const {
|
||||
return this->vc_left;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Y position of this view on the display. A value greater than
|
||||
* zero is considered to be an absolute size. A value less than zero makes
|
||||
@ -87,7 +95,7 @@ public:
|
||||
* position for this view.
|
||||
* @todo Kinda hardwired to the way readline works.
|
||||
*/
|
||||
int get_height() { return this->vc_past_lines.size() + 1; };
|
||||
int get_height() { return 1; };
|
||||
|
||||
void set_max_height(int mh) { this->vc_max_height = mh; };
|
||||
int get_max_height() const { return this->vc_max_height; };
|
||||
@ -112,7 +120,7 @@ public:
|
||||
/**
|
||||
* Paints any past lines and moves the cursor to the current X position.
|
||||
*/
|
||||
void do_update(void);
|
||||
void do_update();
|
||||
|
||||
const static char ESCAPE = 27; /*< VT52 Escape key value. */
|
||||
const static char BACKSPACE = 8; /*< VT52 Backspace key value. */
|
||||
@ -140,6 +148,7 @@ protected:
|
||||
};
|
||||
|
||||
WINDOW *vc_window; /*< The window that contains this view. */
|
||||
int vc_left{0};
|
||||
int vc_x; /*< The X position of the cursor. */
|
||||
int vc_y; /*< The Y position of the cursor. */
|
||||
int vc_max_height;
|
||||
@ -149,8 +158,7 @@ protected:
|
||||
* Buffer returned by map_input for trivial
|
||||
* translations (one-to-one).
|
||||
*/
|
||||
|
||||
/** Vector of past lines of output from the child. */
|
||||
std::list<std::string> vc_past_lines;
|
||||
attr_line_t vc_line;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -32,3 +32,5 @@
|
||||
#include "vtab_module.hh"
|
||||
|
||||
std::string vtab_module_schemas;
|
||||
|
||||
std::map<intern_string_t, std::string> vtab_module_ddls;
|
||||
|
@ -81,6 +81,17 @@ struct from_sqlite {
|
||||
};
|
||||
};
|
||||
|
||||
template<>
|
||||
struct from_sqlite<bool> {
|
||||
inline bool operator()(int argc, sqlite3_value **val, int argi) {
|
||||
if (sqlite3_value_numeric_type(val[argi]) != SQLITE_INTEGER) {
|
||||
throw from_sqlite_conversion_error("integer", argi);
|
||||
}
|
||||
|
||||
return sqlite3_value_int64(val[argi]);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct from_sqlite<int64_t> {
|
||||
inline int64_t operator()(int argc, sqlite3_value **val, int argi) {
|
||||
@ -117,6 +128,13 @@ struct from_sqlite<const char *> {
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct from_sqlite<const std::string &> {
|
||||
inline const std::string operator()(int argc, sqlite3_value **val, int argi) {
|
||||
return std::string((const char *) sqlite3_value_text(val[argi]));
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct from_sqlite<double> {
|
||||
inline double operator()(int argc, sqlite3_value **val, int argi) {
|
||||
@ -375,6 +393,7 @@ struct sqlite_func_adapter<Return (*)(Args...), f> {
|
||||
};
|
||||
|
||||
extern std::string vtab_module_schemas;
|
||||
extern std::map<intern_string_t, std::string> vtab_module_ddls;
|
||||
|
||||
class vtab_index_constraints {
|
||||
public:
|
||||
@ -507,12 +526,26 @@ struct vtab_module {
|
||||
{
|
||||
require(sizeof...(Args) == 0 || argc == sizeof...(Args));
|
||||
|
||||
return apply_impl(obj,
|
||||
func,
|
||||
tab,
|
||||
rowid,
|
||||
argv,
|
||||
std::make_index_sequence<sizeof...(Args)>{});
|
||||
try {
|
||||
return apply_impl(obj,
|
||||
func,
|
||||
tab,
|
||||
rowid,
|
||||
argv,
|
||||
std::make_index_sequence<sizeof...(Args)>{});
|
||||
} catch (from_sqlite_conversion_error &e) {
|
||||
tab->zErrMsg = sqlite3_mprintf(
|
||||
"Expecting an %s for column number %d",
|
||||
e.e_type,
|
||||
e.e_argi);
|
||||
return SQLITE_ERROR;
|
||||
} catch (const std::exception &e) {
|
||||
tab->zErrMsg = sqlite3_mprintf("%s", e.what());
|
||||
return SQLITE_ERROR;
|
||||
} catch (...) {
|
||||
tab->zErrMsg = sqlite3_mprintf("Encountered an unexpected exception");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
static int tvt_destructor(sqlite3_vtab *p_svt)
|
||||
@ -522,9 +555,9 @@ struct vtab_module {
|
||||
|
||||
static int tvt_open(sqlite3_vtab *p_svt, sqlite3_vtab_cursor **pp_cursor)
|
||||
{
|
||||
p_svt->zErrMsg = NULL;
|
||||
p_svt->zErrMsg = nullptr;
|
||||
|
||||
typename T::cursor *p_cur = new (typename T::cursor)(p_svt);
|
||||
auto *p_cur = new (typename T::cursor)(p_svt);
|
||||
|
||||
if (p_cur == NULL) {
|
||||
return SQLITE_NOMEM;
|
||||
@ -537,21 +570,21 @@ struct vtab_module {
|
||||
|
||||
static int tvt_next(sqlite3_vtab_cursor *cur)
|
||||
{
|
||||
typename T::cursor *p_cur = (typename T::cursor *) cur;
|
||||
auto *p_cur = (typename T::cursor *) cur;
|
||||
|
||||
return p_cur->next();
|
||||
}
|
||||
|
||||
static int tvt_eof(sqlite3_vtab_cursor *cur)
|
||||
{
|
||||
typename T::cursor *p_cur = (typename T::cursor *) cur;
|
||||
auto *p_cur = (typename T::cursor *) cur;
|
||||
|
||||
return p_cur->eof();
|
||||
}
|
||||
|
||||
static int tvt_close(sqlite3_vtab_cursor *cur)
|
||||
{
|
||||
typename T::cursor *p_cur = (typename T::cursor *) cur;
|
||||
auto *p_cur = (typename T::cursor *) cur;
|
||||
|
||||
delete p_cur;
|
||||
|
||||
@ -560,13 +593,13 @@ struct vtab_module {
|
||||
|
||||
|
||||
static int tvt_rowid(sqlite3_vtab_cursor *cur, sqlite_int64 *p_rowid) {
|
||||
typename T::cursor *p_cur = (typename T::cursor *) cur;
|
||||
auto *p_cur = (typename T::cursor *) cur;
|
||||
|
||||
return p_cur->get_rowid(*p_rowid);
|
||||
};
|
||||
|
||||
static int tvt_column(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int col) {
|
||||
typename T::cursor *p_cur = (typename T::cursor *) cur;
|
||||
auto *p_cur = (typename T::cursor *) cur;
|
||||
T handler;
|
||||
|
||||
return handler.get_column(*p_cur, ctx, col);
|
||||
@ -640,6 +673,7 @@ struct vtab_module {
|
||||
int create(sqlite3 *db, const char *name) {
|
||||
std::string impl_name = name;
|
||||
vtab_module_schemas += T::CREATE_STMT;
|
||||
vtab_module_ddls[intern_string::lookup(name)] = trim(T::CREATE_STMT);
|
||||
|
||||
// XXX Eponymous tables don't seem to work in older sqlite versions
|
||||
impl_name += "_impl";
|
||||
@ -658,14 +692,16 @@ struct tvt_iterator_cursor {
|
||||
sqlite3_vtab_cursor base;
|
||||
typename T::iterator iter;
|
||||
|
||||
cursor(sqlite3_vtab *vt) {
|
||||
cursor(sqlite3_vtab *vt)
|
||||
{
|
||||
T handler;
|
||||
|
||||
this->base.pVtab = vt;
|
||||
this->iter = handler.begin();
|
||||
};
|
||||
|
||||
int next() {
|
||||
int next()
|
||||
{
|
||||
T handler;
|
||||
|
||||
if (this->iter != handler.end()) {
|
||||
@ -675,19 +711,37 @@ struct tvt_iterator_cursor {
|
||||
return SQLITE_OK;
|
||||
};
|
||||
|
||||
int eof() {
|
||||
int eof()
|
||||
{
|
||||
T handler;
|
||||
|
||||
return this->iter == handler.end();
|
||||
};
|
||||
|
||||
int get_rowid(sqlite_int64 &rowid_out) {
|
||||
template< bool cond, typename U >
|
||||
using resolvedType = typename std::enable_if< cond, U >::type;
|
||||
|
||||
template< typename U = int >
|
||||
resolvedType< std::is_same<std::random_access_iterator_tag,
|
||||
typename std::iterator_traits<typename T::iterator>::iterator_category>::value, U >
|
||||
get_rowid(sqlite_int64 &rowid_out) {
|
||||
T handler;
|
||||
|
||||
rowid_out = std::distance(handler.begin(), this->iter);
|
||||
|
||||
return SQLITE_OK;
|
||||
};
|
||||
}
|
||||
|
||||
template< typename U = int >
|
||||
resolvedType< !std::is_same<std::random_access_iterator_tag,
|
||||
typename std::iterator_traits<typename T::iterator>::iterator_category>::value, U >
|
||||
get_rowid(sqlite_int64 &rowid_out) {
|
||||
T handler;
|
||||
|
||||
rowid_out = handler.get_rowid(this->iter);
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -179,7 +179,7 @@ drive_view_colors_LDADD = ../src/libdiag.a $(CURSES_LIB) \
|
||||
$(CONFIG_OBJS)
|
||||
|
||||
drive_vt52_curses_SOURCES = drive_vt52_curses.cc
|
||||
drive_vt52_curses_LDADD = ../src/libdiag.a $(CURSES_LIB)
|
||||
drive_vt52_curses_LDADD = ../src/libdiag.a $(CURSES_LIB) $(CONFIG_OBJS)
|
||||
|
||||
drive_readline_curses_SOURCES = drive_readline_curses.cc
|
||||
drive_readline_curses_LDADD = ../src/libdiag.a $(READLINE_LIBS) $(CURSES_LIB) \
|
||||
@ -195,6 +195,7 @@ drive_sql_LDADD = \
|
||||
$(CURSES_LIB) \
|
||||
$(READLINE_LIBS) \
|
||||
$(LIBCURL) \
|
||||
$(TEXT2C_OBJS) \
|
||||
-lpcrecpp
|
||||
|
||||
drive_sql_anno_SOURCES = \
|
||||
@ -392,14 +393,14 @@ TESTS = \
|
||||
test_sql_fs_func.sh \
|
||||
test_sql_str_func.sh \
|
||||
test_sql_time_func.sh \
|
||||
test_vt52_curses.sh \
|
||||
test_top_status \
|
||||
test_data_parser.sh \
|
||||
test_yajlpp \
|
||||
test_pretty_print.sh
|
||||
|
||||
DISABLED_TESTS = \
|
||||
test_view_colors.sh
|
||||
test_view_colors.sh \
|
||||
test_vt52_curses.sh
|
||||
|
||||
if HAVE_LIBCURL
|
||||
TESTS += \
|
||||
|
@ -35,15 +35,38 @@ static int sql_callback(void *ptr,
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string execute_any(exec_context &ec, const std::string &cmdline_with_mode)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
void add_global_vars(exec_context &ec)
|
||||
void rebuild_hist()
|
||||
{
|
||||
}
|
||||
|
||||
bool setup_logline_table(exec_context &ec)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool rescan_files(bool required)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void wait_for_children()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void rebuild_indexes()
|
||||
{
|
||||
}
|
||||
|
||||
readline_context::command_map_t lnav_commands;
|
||||
|
||||
extern "C" {
|
||||
const char *help_txt = "";
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
const char *default_log_formats_json = "";
|
||||
}
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int retval = EXIT_SUCCESS;
|
||||
|
@ -113,6 +113,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
noecho();
|
||||
vt.set_window(sc.get_window());
|
||||
vt.set_width(10);
|
||||
|
||||
for (lpc = 0; CANNED_INPUT[lpc]; lpc++) {
|
||||
vt.map_output(CANNED_INPUT[lpc], strlen(CANNED_INPUT[lpc]));
|
||||
|
@ -176,6 +176,8 @@ Views
|
||||
leaving the spectrogram view with 'Q', the top time in that
|
||||
view will be matched to the top time in the log view.
|
||||
|
||||
` Toggle focusing on the filter editor or the main view.
|
||||
|
||||
a/A Restore the view that was previously popped with 'q/Q'.
|
||||
The 'A' hotkey will try to match the top times between the
|
||||
two views.
|
||||
@ -425,6 +427,22 @@ Session
|
||||
session state (filters, highlights) and then reset the
|
||||
state to the factory default.
|
||||
|
||||
Filter Editor
|
||||
-------------
|
||||
|
||||
The following hotkeys are only available when the focus is on the filter
|
||||
editor. You can change the focus by pressing backquote (`).
|
||||
|
||||
q Switch the focus back to the main view.
|
||||
j/down-arrow Select the next filter.
|
||||
k/up-arrow Select the previous filter.
|
||||
o Create a new "out" filter.
|
||||
i Create a new "in" filter .
|
||||
SPACE Toggle the enabled/disabled state of the currently
|
||||
selected filter.
|
||||
ENTER Edit the selected filter.
|
||||
D Delete the selected filter.
|
||||
|
||||
|
||||
MOUSE SUPPORT (experimental)
|
||||
============================
|
||||
@ -2041,6 +2059,10 @@ Parameters
|
||||
cond The condition used to determine whether a row should be
|
||||
updated.
|
||||
|
||||
Example
|
||||
#1 ;UPDATE syslog_log SET log_mark = 1 WHERE log_line = 40
|
||||
|
||||
|
||||
|
||||
Synopsis
|
||||
WITH [RECURSIVE] cte-table-name AS select-stmt
|
||||
|
@ -2,6 +2,59 @@
|
||||
|
||||
lnav_test="${top_builddir}/src/lnav-test"
|
||||
|
||||
run_test ${lnav_test} -n \
|
||||
-c ";INSERT INTO lnav_view_filters VALUES ('log', 1, 'out', '')" \
|
||||
${test_dir}/logfile_access_log.0
|
||||
|
||||
check_error_output "inserted filter with an empty pattern?" <<EOF
|
||||
error:command-option:1:Expecting an non-empty pattern for column number 3
|
||||
EOF
|
||||
|
||||
run_test ${lnav_test} -n \
|
||||
-c ";INSERT INTO lnav_view_filters VALUES ('bad', 1, 'out', 'abc')" \
|
||||
${test_dir}/logfile_access_log.0
|
||||
|
||||
check_error_output "inserted filter with an empty pattern?" <<EOF
|
||||
error:command-option:1:Expecting an lnav view name for column number 0
|
||||
EOF
|
||||
|
||||
run_test ${lnav_test} -n \
|
||||
-c ";INSERT INTO lnav_view_filters VALUES ('log', 1, 'bad', 'abc')" \
|
||||
${test_dir}/logfile_access_log.0
|
||||
|
||||
check_error_output "inserted filter with an empty pattern?" <<EOF
|
||||
error:command-option:1:Expecting an filter type for column number 2
|
||||
EOF
|
||||
|
||||
run_test ${lnav_test} -n \
|
||||
-c ";INSERT INTO lnav_view_filters VALUES ('log', 1, 'out', 'vmk')" \
|
||||
${test_dir}/logfile_access_log.0
|
||||
|
||||
check_output "inserted filter did not work?" <<EOF
|
||||
192.168.202.254 - - [20/Jul/2009:22:59:26 +0000] "GET /vmw/cgi/tramp HTTP/1.0" 200 134 "-" "gPXE/0.9.7"
|
||||
EOF
|
||||
|
||||
run_test ${lnav_test} -n \
|
||||
-c ":filter-out vmk" \
|
||||
-c ";DELETE FROM lnav_view_filters" \
|
||||
${test_dir}/logfile_access_log.0
|
||||
|
||||
check_output "inserted filter did not work?" <<EOF
|
||||
192.168.202.254 - - [20/Jul/2009:22:59:26 +0000] "GET /vmw/cgi/tramp HTTP/1.0" 200 134 "-" "gPXE/0.9.7"
|
||||
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7"
|
||||
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7"
|
||||
EOF
|
||||
|
||||
run_test ${lnav_test} -n \
|
||||
-c ":filter-out vmk" \
|
||||
-c ";UPDATE lnav_view_filters SET pattern = 'vmkboot'" \
|
||||
${test_dir}/logfile_access_log.0
|
||||
|
||||
check_output "inserted filter did not work?" <<EOF
|
||||
192.168.202.254 - - [20/Jul/2009:22:59:26 +0000] "GET /vmw/cgi/tramp HTTP/1.0" 200 134 "-" "gPXE/0.9.7"
|
||||
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7"
|
||||
EOF
|
||||
|
||||
run_test ${lnav_test} -n \
|
||||
-c ";SELECT * FROM access_log LIMIT 0" \
|
||||
${test_dir}/logfile_access_log.0
|
||||
@ -527,7 +580,7 @@ run_test ${lnav_test} -n \
|
||||
|
||||
check_output "delete from lnav_views table works?" <<EOF
|
||||
count(*)
|
||||
9
|
||||
8
|
||||
EOF
|
||||
|
||||
|
||||
@ -539,7 +592,7 @@ run_test ${lnav_test} -n \
|
||||
|
||||
check_output "insert into lnav_views table works?" <<EOF
|
||||
count(*)
|
||||
9
|
||||
8
|
||||
EOF
|
||||
|
||||
|
||||
@ -601,7 +654,7 @@ EOF
|
||||
|
||||
|
||||
schema_dump() {
|
||||
${lnav_test} -n -c ';.schema' ${test_dir}/logfile_access_log.0 | head -n13
|
||||
${lnav_test} -n -c ';.schema' ${test_dir}/logfile_access_log.0 | head -n14
|
||||
}
|
||||
|
||||
run_test schema_dump
|
||||
@ -611,6 +664,7 @@ ATTACH DATABASE '' AS 'main';
|
||||
CREATE VIRTUAL TABLE environ USING environ_vtab_impl();
|
||||
CREATE VIRTUAL TABLE lnav_views USING lnav_views_impl();
|
||||
CREATE VIRTUAL TABLE lnav_view_stack USING lnav_view_stack_impl();
|
||||
CREATE VIRTUAL TABLE lnav_view_filters USING lnav_view_filters_impl();
|
||||
CREATE VIRTUAL TABLE lnav_file USING lnav_file_impl();
|
||||
CREATE VIRTUAL TABLE regexp_capture USING regexp_capture_impl();
|
||||
CREATE VIRTUAL TABLE fstat USING fstat_impl();
|
||||
|
@ -4,8 +4,3 @@ run_test ./scripty -n -r ${srcdir}/vt52_curses_input.0 \
|
||||
-e ${srcdir}/vt52_curses_output.0 -- ./drive_vt52_curses < /dev/null
|
||||
|
||||
on_error_fail_with "single line vt52 did not work?"
|
||||
|
||||
run_test ./scripty -n -r ${srcdir}/vt52_curses_input.1 \
|
||||
-e ${srcdir}/vt52_curses_output.1 -- ./drive_vt52_curses -y 5 < /dev/null
|
||||
|
||||
on_error_fail_with "vt52 doesn't maintain past lines?"
|
||||
|
Loading…
Reference in New Issue
Block a user