1
1
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:
Timothy Stack 2018-11-09 09:45:19 -08:00
parent 80456791ac
commit 2345a32423
60 changed files with 3529 additions and 1917 deletions

6
NEWS
View File

@ -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

View File

@ -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
--------

View File

@ -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

View File

@ -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 \

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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
View 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());
}
}

View 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
View 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
View 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

View File

@ -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

View File

@ -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))

View File

@ -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)
============================

View File

@ -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) {

View File

@ -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;

View File

@ -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;

File diff suppressed because it is too large Load Diff

View File

@ -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 &regex);
void redo_search(lnav_view_t view_index);
bool rescan_files(bool required = false);
void wait_for_children();

View File

@ -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));

View File

@ -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);

View File

@ -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();

View File

@ -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
View 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
View 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

View File

@ -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
);
}

View File

@ -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;

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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 {

View File

@ -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());

View File

@ -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();

View File

@ -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();

View File

@ -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;

View File

@ -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. "

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View 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 &regex) {
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);
}

View 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

View File

@ -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 {

View File

@ -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 &regex_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 "";
}

View File

@ -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 &regex_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

View File

@ -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);

View File

@ -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
View 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;
}

View File

@ -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;
}

View File

@ -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 });
}

View File

@ -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

View File

@ -32,3 +32,5 @@
#include "vtab_module.hh"
std::string vtab_module_schemas;
std::map<intern_string_t, std::string> vtab_module_ddls;

View File

@ -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;
}
};
};

View File

@ -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 += \

View File

@ -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;

View File

@ -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]));

View File

@ -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

View File

@ -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();

View File

@ -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?"