summaryrefslogtreecommitdiffstats
path: root/src/views_vtab.cc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/views_vtab.cc302
1 files changed, 260 insertions, 42 deletions
diff --git a/src/views_vtab.cc b/src/views_vtab.cc
index 6dee241..7c399af 100644
--- a/src/views_vtab.cc
+++ b/src/views_vtab.cc
@@ -34,17 +34,16 @@
#include <unistd.h>
#include "base/injector.bind.hh"
-#include "base/itertools.hh"
#include "base/lnav_log.hh"
#include "base/opt_util.hh"
#include "config.h"
#include "lnav.hh"
-#include "lnav_util.hh"
#include "sql_util.hh"
-#include "view_curses.hh"
#include "vtab_module_json.hh"
#include "yajlpp/yajlpp_def.hh"
+using namespace lnav::roles::literals;
+
template<>
struct from_sqlite<lnav_view_t> {
inline lnav_view_t operator()(int argc, sqlite3_value** val, int argi)
@@ -123,6 +122,8 @@ struct from_sqlite<std::shared_ptr<lnav::pcre2pp::code>> {
}
};
+namespace {
+
static const typed_json_path_container<breadcrumb::possibility>
breadcrumb_possibility_handlers = {
yajlpp::property_handler("display_value")
@@ -174,6 +175,88 @@ static const typed_json_path_container<top_line_meta> top_line_meta_handlers = {
.with_children(breadcrumb_crumb_handlers),
};
+static const typed_json_path_container<line_range> line_range_handlers = {
+ yajlpp::property_handler("start").for_field(&line_range::lr_start),
+ yajlpp::property_handler("end").for_field(&line_range::lr_end),
+};
+
+static const typed_json_path_container<textview_curses::selected_text_info>
+ selected_text_handlers = {
+ yajlpp::property_handler("line").for_field(
+ &textview_curses::selected_text_info::sti_line),
+ yajlpp::property_handler("range")
+ .for_child(&textview_curses::selected_text_info::sti_range)
+ .with_children(line_range_handlers),
+ yajlpp::property_handler("value").for_field(
+ &textview_curses::selected_text_info::sti_value),
+};
+
+enum class row_details_t {
+ hide,
+ show,
+};
+
+enum class word_wrap_t {
+ none,
+ normal,
+};
+
+struct view_options {
+ nonstd::optional<row_details_t> vo_row_details;
+ nonstd::optional<row_details_t> vo_row_time_offset;
+ nonstd::optional<int32_t> vo_overlay_focus;
+ nonstd::optional<word_wrap_t> vo_word_wrap;
+ nonstd::optional<row_details_t> vo_hidden_fields;
+
+ bool empty() const
+ {
+ return !this->vo_row_details.has_value()
+ && !this->vo_row_time_offset.has_value()
+ && !this->vo_overlay_focus.has_value()
+ && !this->vo_word_wrap.has_value()
+ && !this->vo_hidden_fields.has_value();
+ }
+};
+
+static const json_path_handler_base::enum_value_t ROW_DETAILS_ENUM[] = {
+ {"hide", row_details_t::hide},
+ {"show", row_details_t::show},
+
+ json_path_handler_base::ENUM_TERMINATOR,
+};
+
+static const json_path_handler_base::enum_value_t WORD_WRAP_ENUM[] = {
+ {"none", word_wrap_t::none},
+ {"normal", word_wrap_t::normal},
+
+ json_path_handler_base::ENUM_TERMINATOR,
+};
+
+static const typed_json_path_container<view_options> view_options_handlers = {
+ yajlpp::property_handler("row-details")
+ .with_enum_values(ROW_DETAILS_ENUM)
+ .with_description(
+ "Show or hide the details overlay for the focused row")
+ .for_field(&view_options::vo_row_details),
+ yajlpp::property_handler("row-time-offset")
+ .with_enum_values(ROW_DETAILS_ENUM)
+ .with_description(
+ "Show or hide the time-offset from a row to the previous mark")
+ .for_field(&view_options::vo_row_time_offset),
+ yajlpp::property_handler("hidden-fields")
+ .with_enum_values(ROW_DETAILS_ENUM)
+ .with_description(
+ "Show or hide fields that have been hidden by the user")
+ .for_field(&view_options::vo_hidden_fields),
+ yajlpp::property_handler("overlay-focused-line")
+ .with_description("The focused line in an overlay")
+ .for_field(&view_options::vo_overlay_focus),
+ yajlpp::property_handler("word-wrap")
+ .with_enum_values(WORD_WRAP_ENUM)
+ .with_description("How to break long lines")
+ .for_field(&view_options::vo_word_wrap),
+};
+
struct lnav_views : public tvt_iterator_cursor<lnav_views> {
static constexpr const char* NAME = "lnav_views";
static constexpr const char* CREATE_STMT = R"(
@@ -191,7 +274,9 @@ CREATE TABLE lnav_views (
filtering INTEGER, -- Indicates if the view is applying filters.
movement TEXT, -- The movement mode, either 'top' or 'cursor'.
top_meta TEXT, -- A JSON object that contains metadata related to the top line in the view.
- selection INTEGER -- The number of the line that is focused for selection.
+ selection INTEGER, -- The number of the line that is focused for selection.
+ options TEXT, -- A JSON object that contains optional settings for this view.
+ selected_text TEXT -- A JSON object that contains information about the text selected by the mouse in the view.
);
)";
@@ -205,7 +290,7 @@ CREATE TABLE lnav_views (
{
lnav_view_t view_index = (lnav_view_t) std::distance(
std::begin(lnav_data.ld_views), vc.iter);
- textview_curses& tc = *vc.iter;
+ const auto& tc = *vc.iter;
unsigned long width;
vis_line_t height;
@@ -232,14 +317,15 @@ CREATE TABLE lnav_views (
= dynamic_cast<text_time_translator*>(tc.get_sub_source());
if (time_source != nullptr && tc.get_inner_height() > 0) {
- auto top_time_opt = time_source->time_for_row(tc.get_top());
+ auto top_ri_opt
+ = time_source->time_for_row(tc.get_selection());
- if (top_time_opt) {
+ if (top_ri_opt) {
char timestamp[64];
sql_strftime(timestamp,
sizeof(timestamp),
- top_time_opt.value(),
+ top_ri_opt->ri_time,
' ');
sqlite3_result_text(
ctx, timestamp, -1, SQLITE_TRANSIENT);
@@ -288,6 +374,10 @@ CREATE TABLE lnav_views (
case 11: {
static const size_t MAX_POSSIBILITIES = 128;
+ if (sqlite3_vtab_nochange(ctx)) {
+ return SQLITE_OK;
+ }
+
auto* tss = tc.get_sub_source();
if (tss != nullptr && tss->text_line_count() > 0) {
@@ -300,15 +390,15 @@ CREATE TABLE lnav_views (
top_line_meta tlm;
if (time_source != nullptr) {
- auto top_time_opt
- = time_source->time_for_row(tc.get_top());
+ auto top_ri_opt
+ = time_source->time_for_row(tc.get_selection());
- if (top_time_opt) {
+ if (top_ri_opt) {
char timestamp[64];
sql_strftime(timestamp,
sizeof(timestamp),
- top_time_opt.value(),
+ top_ri_opt->ri_time,
' ');
tlm.tlm_time = timestamp;
}
@@ -344,6 +434,55 @@ CREATE TABLE lnav_views (
case 12:
sqlite3_result_int(ctx, (int) tc.get_selection());
break;
+ case 13: {
+ if (sqlite3_vtab_nochange(ctx)) {
+ return SQLITE_OK;
+ }
+
+ auto* text_accel_p
+ = dynamic_cast<text_accel_source*>(tc.get_sub_source());
+ auto vo = view_options{};
+
+ vo.vo_word_wrap = tc.get_word_wrap() ? word_wrap_t::normal
+ : word_wrap_t::none;
+ vo.vo_hidden_fields = tc.get_hide_fields()
+ ? row_details_t::hide
+ : row_details_t::show;
+ if (tc.get_overlay_source()) {
+ auto ov_sel = tc.get_overlay_selection();
+
+ vo.vo_row_details
+ = tc.get_overlay_source()->get_show_details_in_overlay()
+ ? row_details_t::show
+ : row_details_t::hide;
+ if (ov_sel) {
+ vo.vo_overlay_focus = ov_sel.value();
+ }
+ }
+ if (text_accel_p != nullptr) {
+ vo.vo_row_time_offset
+ = text_accel_p->is_time_offset_enabled()
+ ? row_details_t::show
+ : row_details_t::hide;
+ }
+
+ if (vo.empty()) {
+ sqlite3_result_null(ctx);
+ } else {
+ to_sqlite(ctx, view_options_handlers.to_json_string(vo));
+ }
+ break;
+ }
+ case 14: {
+ if (tc.tc_selected_text) {
+ to_sqlite(ctx,
+ selected_text_handlers.to_json_string(
+ tc.tc_selected_text.value()));
+ } else {
+ sqlite3_result_null(ctx);
+ }
+ break;
+ }
}
return SQLITE_OK;
@@ -377,13 +516,36 @@ CREATE TABLE lnav_views (
bool do_filtering,
string_fragment movement,
const char* top_meta,
- int64_t selection)
+ int64_t selection,
+ nonstd::optional<string_fragment> options,
+ nonstd::optional<string_fragment> selected_text)
{
auto& tc = lnav_data.ld_views[index];
auto* time_source
= dynamic_cast<text_time_translator*>(tc.get_sub_source());
+ auto* text_accel_p
+ = dynamic_cast<text_accel_source*>(tc.get_sub_source());
+ view_options vo;
+
+ if (options) {
+ static const intern_string_t OPTIONS_SRC
+ = intern_string::lookup("options");
+
+ auto parse_res = view_options_handlers.parser_for(OPTIONS_SRC)
+ .of(options.value());
+ if (parse_res.isErr()) {
+ auto errmsg = parse_res.unwrapErr();
+
+ set_vtable_errmsg(tab, errmsg[0]);
+ return SQLITE_ERROR;
+ }
+
+ vo = parse_res.unwrap();
+ }
if (tc.get_top() != top_row) {
+ log_debug(
+ "setting top for %s to %d", tc.get_title().c_str(), top_row);
tc.set_top(vis_line_t(top_row));
if (!tc.is_selectable()) {
selection = top_row;
@@ -392,21 +554,41 @@ CREATE TABLE lnav_views (
date_time_scanner dts;
struct timeval tv;
+ log_debug("setting top time for %s to %s",
+ tc.get_title().c_str(),
+ top_time);
if (dts.convert_to_timeval(top_time, -1, nullptr, tv)) {
- auto last_time_opt = time_source->time_for_row(tc.get_top());
+ auto last_ri_opt
+ = time_source->time_for_row(tc.get_selection());
- if (last_time_opt) {
- auto last_time = last_time_opt.value();
+ if (last_ri_opt) {
+ auto last_time = last_ri_opt->ri_time;
if (tv != last_time) {
time_source->row_for_time(tv) |
- [&tc](auto row) { tc.set_top(row); };
+ [&tc, &selection](auto row) {
+ log_debug("setting top for %s to %d from time",
+ tc.get_title().c_str(),
+ row);
+ selection = row;
+ tc.set_selection(row);
+ };
if (!tc.is_selectable()) {
selection = tc.get_top();
}
}
+ } else {
+ log_warning(" could not get for time top row of %s",
+ tc.get_title().c_str());
}
} else {
- tab->zErrMsg = sqlite3_mprintf("Invalid time: %s", top_time);
+ auto um = lnav::console::user_message::error(
+ attr_line_t("Invalid ")
+ .append_quoted("top_time"_symbol)
+ .append(" value"))
+ .with_reason(
+ attr_line_t("Unrecognized time value: ")
+ .append(lnav::roles::string(top_time)));
+ set_vtable_errmsg(tab, um);
return SQLITE_ERROR;
}
}
@@ -421,9 +603,8 @@ CREATE TABLE lnav_views (
string_fragment::from_c_str(top_meta));
if (parse_res.isErr()) {
auto errmsg = parse_res.unwrapErr();
- tab->zErrMsg = sqlite3_mprintf(
- "Invalid top_meta: %s",
- errmsg[0].to_attr_line().get_string().c_str());
+
+ set_vtable_errmsg(tab, errmsg[0]);
return SQLITE_ERROR;
}
@@ -431,9 +612,15 @@ CREATE TABLE lnav_views (
if (index == LNV_TEXT && tlm.tlm_file) {
if (!lnav_data.ld_text_source.to_front(tlm.tlm_file.value())) {
- auto errmsg = parse_res.unwrapErr();
- tab->zErrMsg = sqlite3_mprintf("unknown top_meta.file: %s",
- tlm.tlm_file->c_str());
+ auto um
+ = lnav::console::user_message::error(
+ attr_line_t("Invalid ")
+ .append_quoted("top_meta.file"_symbol)
+ .append(" value"))
+ .with_reason(attr_line_t("Unknown text file: ")
+ .append(lnav::roles::file(
+ tlm.tlm_file.value())));
+ set_vtable_errmsg(tab, um);
return SQLITE_ERROR;
}
}
@@ -451,8 +638,15 @@ CREATE TABLE lnav_views (
tc.set_selection(req_anchor_top.value());
}
} else {
- tab->zErrMsg = sqlite3_mprintf(
- "unknown top_meta.anchor: %s", req_anchor.c_str());
+ auto um
+ = lnav::console::user_message::error(
+ attr_line_t("Invalid ")
+ .append_quoted("top_meta.anchor"_symbol)
+ .append(" value"))
+ .with_reason(
+ attr_line_t("Unknown anchor: ")
+ .append(lnav::roles::symbol(req_anchor)));
+ set_vtable_errmsg(tab, um);
return SQLITE_ERROR;
}
}
@@ -473,6 +667,31 @@ CREATE TABLE lnav_views (
tc.set_selection(cur_bot);
}
}
+ if (vo.vo_row_details && tc.get_overlay_source()) {
+ tc.get_overlay_source()->set_show_details_in_overlay(
+ vo.vo_row_details.value() == row_details_t::show);
+ tc.set_needs_update();
+ }
+ if (vo.vo_overlay_focus && tc.get_overlay_source()) {
+ tc.set_overlay_selection(vis_line_t(vo.vo_overlay_focus.value()));
+ }
+ if (vo.vo_word_wrap) {
+ tc.set_word_wrap(vo.vo_word_wrap.value() == word_wrap_t::normal);
+ }
+ if (vo.vo_hidden_fields) {
+ tc.set_hide_fields(vo.vo_hidden_fields.value()
+ == row_details_t::hide);
+ }
+ if (text_accel_p != nullptr && vo.vo_row_time_offset) {
+ switch (vo.vo_row_time_offset.value()) {
+ case row_details_t::show:
+ text_accel_p->set_time_offset(true);
+ break;
+ case row_details_t::hide:
+ text_accel_p->set_time_offset(false);
+ break;
+ }
+ }
tc.set_left(left);
tc.set_paused(is_paused);
tc.execute_search(search);
@@ -528,6 +747,8 @@ CREATE TABLE lnav_view_stack (
lnav_data.ld_last_view = *lnav_data.ld_view_stack.top();
lnav_data.ld_view_stack.pop_back();
+ clear_preview();
+
return SQLITE_OK;
}
@@ -570,15 +791,15 @@ struct lnav_view_filter_base {
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();
+ const auto& tc = lnav_data.ld_views[this->i_view_index];
+ auto* 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();
+ const auto& fs = tss->get_filters();
this->i_filter_index += 1;
if (this->i_filter_index >= (ssize_t) fs.size()) {
@@ -802,10 +1023,7 @@ CREATE TABLE lnav_view_filters (
auto set_res = lnav_data.ld_log_source.set_sql_filter(
clause, stmt.release());
if (set_res.isErr()) {
- tab->zErrMsg = sqlite3_mprintf(
- "%s%s",
- sqlitepp::ERROR_PREFIX,
- lnav::to_json(set_res.unwrapErr()).c_str());
+ set_vtable_errmsg(tab, set_res.unwrapErr());
return SQLITE_ERROR;
}
tf = lnav_data.ld_log_source.get_sql_filter().value();
@@ -903,10 +1121,7 @@ CREATE TABLE lnav_view_filters (
auto set_res = lnav_data.ld_log_source.set_sql_filter(
clause, stmt.release());
if (set_res.isErr()) {
- tab->zErrMsg = sqlite3_mprintf(
- "%s%s",
- sqlitepp::ERROR_PREFIX,
- lnav::to_json(set_res.unwrapErr()).c_str());
+ set_vtable_errmsg(tab, set_res.unwrapErr());
return SQLITE_ERROR;
}
*iter = lnav_data.ld_log_source.get_sql_filter().value();
@@ -1079,6 +1294,7 @@ CREATE TABLE lnav_view_files (
auto& ld = *iter;
if (ld->ld_visible != visible) {
+ ld->get_file_ptr()->set_indexing(visible);
ld->set_visibility(visible);
lss.text_filters_changed();
}
@@ -1087,11 +1303,6 @@ CREATE TABLE lnav_view_files (
}
};
-static const char* CREATE_FILTER_VIEW = R"(
-CREATE VIEW lnav_view_filters_and_stats AS
- SELECT * FROM lnav_view_filters LEFT NATURAL JOIN lnav_view_filter_stats
-)";
-
static auto a = injector::bind_multiple<vtab_module_base>()
.add<vtab_module<lnav_views>>()
.add<vtab_module<lnav_view_stack>>()
@@ -1099,9 +1310,16 @@ static auto a = injector::bind_multiple<vtab_module_base>()
.add<vtab_module<tvt_no_update<lnav_view_filter_stats>>>()
.add<vtab_module<lnav_view_files>>();
+} // namespace
+
int
register_views_vtab(sqlite3* db)
{
+ static const char* CREATE_FILTER_VIEW = R"(
+CREATE VIEW lnav_view_filters_and_stats AS
+ SELECT * FROM lnav_view_filters LEFT NATURAL JOIN lnav_view_filter_stats
+)";
+
auto_mem<char> errmsg(sqlite3_free);
if (sqlite3_exec(db, CREATE_FILTER_VIEW, nullptr, nullptr, errmsg.out())
!= SQLITE_OK)