diff options
Diffstat (limited to '')
-rw-r--r-- | src/views_vtab.cc | 302 |
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) |