diff options
Diffstat (limited to '')
-rw-r--r-- | src/session_data.cc | 719 |
1 files changed, 428 insertions, 291 deletions
diff --git a/src/session_data.cc b/src/session_data.cc index 73cb7f8..1b57da4 100644 --- a/src/session_data.cc +++ b/src/session_data.cc @@ -34,7 +34,6 @@ #include "session_data.hh" -#include <fcntl.h> #include <glob.h> #include <stdio.h> #include <sys/types.h> @@ -44,11 +43,13 @@ #include "base/isc.hh" #include "base/opt_util.hh" #include "base/paths.hh" +#include "bookmarks.json.hh" +#include "bound_tags.hh" #include "command_executor.hh" #include "config.h" +#include "hasher.hh" #include "lnav.events.hh" #include "lnav.hh" -#include "lnav_util.hh" #include "log_format_ext.hh" #include "logfile.hh" #include "service_tags.hh" @@ -59,7 +60,8 @@ #include "yajlpp/yajlpp.hh" #include "yajlpp/yajlpp_def.hh" -struct session_data_t session_data; +session_data_t session_data; +recent_refs_t recent_refs; static const char* LOG_METADATA_NAME = "log_metadata.db"; @@ -73,6 +75,7 @@ CREATE TABLE IF NOT EXISTS bookmarks ( access_time datetime DEFAULT CURRENT_TIMESTAMP, comment text DEFAULT '', tags text DEFAULT '', + annotations text DEFAULT NULL, PRIMARY KEY (log_time, log_format, log_hash, session_time) ); @@ -125,13 +128,28 @@ static const char* NETLOC_LRU_STMT static const char* UPGRADE_STMTS[] = { R"(ALTER TABLE bookmarks ADD COLUMN comment text DEFAULT '';)", R"(ALTER TABLE bookmarks ADD COLUMN tags text DEFAULT '';)", + R"(ALTER TABLE bookmarks ADD COLUMN annotations text DEFAULT NULL;)", }; static const size_t MAX_SESSIONS = 8; static const size_t MAX_SESSION_FILE_COUNT = 256; -static std::vector<content_line_t> marked_session_lines; -static std::vector<content_line_t> offset_session_lines; +struct session_line { + session_line(struct timeval tv, + intern_string_t format_name, + std::string line_hash) + : sl_time(tv), sl_format_name(format_name), + sl_line_hash(std::move(line_hash)) + { + } + + struct timeval sl_time; + intern_string_t sl_format_name; + std::string sl_line_hash; +}; + +static std::vector<session_line> marked_session_lines; +static std::vector<session_line> offset_session_lines; static bool bind_line(sqlite3* db, @@ -139,7 +157,7 @@ bind_line(sqlite3* db, content_line_t cl, time_t session_time) { - logfile_sub_source& lss = lnav_data.ld_log_source; + auto& lss = lnav_data.ld_log_source; auto lf = lss.find(cl); if (lf == nullptr) { @@ -175,7 +193,9 @@ bind_line(sqlite3* db, struct session_file_info { session_file_info(int timestamp, std::string id, std::string path) : sfi_timestamp(timestamp), sfi_id(std::move(id)), - sfi_path(std::move(path)){}; + sfi_path(std::move(path)) + { + } bool operator<(const session_file_info& other) const { @@ -186,7 +206,7 @@ struct session_file_info { return true; } return false; - }; + } int sfi_timestamp; std::string sfi_id; @@ -279,6 +299,7 @@ init_session() { lnav_data.ld_session_time = time(nullptr); lnav_data.ld_session_id.clear(); + session_data.sd_view_states[LNV_LOG].vs_top = -1; } static nonstd::optional<std::string> @@ -294,6 +315,17 @@ compute_session_id() has_files = true; h.update(ld_file_name.first); } + for (auto& lf : lnav_data.ld_active_files.fc_files) { + if (lf->is_valid_filename()) { + continue; + } + if (!lf->get_open_options().loo_include_in_session) { + continue; + } + + has_files = true; + h.update(lf->get_filename()); + } if (!has_files) { return nonstd::nullopt; } @@ -371,6 +403,24 @@ scan_sessions() void load_time_bookmarks() { + static const char* BOOKMARK_STMT = R"( + SELECT + log_time, + log_format, + log_hash, + session_time, + part_name, + access_time, + comment, + tags, + annotations, + session_time=? AS same_session + FROM bookmarks WHERE + log_time BETWEEN ? AND ? AND + log_format = ? + ORDER BY same_session DESC, session_time DESC +)"; + logfile_sub_source& lss = lnav_data.ld_log_source; auto_sqlite3 db; auto db_path = lnav::paths::dotlnav() / LOG_METADATA_NAME; @@ -412,7 +462,7 @@ load_time_bookmarks() while (!done) { done = netloc_stmt.fetch_row<std::string>().match( [](const std::string& netloc) { - session_data.sd_recent_netlocs.insert(netloc); + recent_refs.rr_netlocs.insert(netloc); return false; }, [](const prepared_stmt::fetch_error& fe) { @@ -424,16 +474,7 @@ load_time_bookmarks() } } - if (sqlite3_prepare_v2( - db.in(), - "SELECT log_time, log_format, log_hash, session_time, part_name, " - "access_time, comment," - " tags, session_time=? as same_session FROM bookmarks WHERE " - " log_time between ? and ? and log_format = ? " - " ORDER BY same_session DESC, session_time DESC", - -1, - stmt.out(), - nullptr) + if (sqlite3_prepare_v2(db.in(), BOOKMARK_STMT, -1, stmt.out(), nullptr) != SQLITE_OK) { log_error("could not prepare bookmark select statement -- %s", @@ -446,6 +487,7 @@ load_time_bookmarks() ++file_iter) { auto lf = (*file_iter)->get_file(); + const auto* format = lf->get_format_ptr(); content_line_t base_content_line; if (lf == nullptr) { @@ -471,7 +513,6 @@ load_time_bookmarks() date_time_scanner dts; bool done = false; - std::string line; int64_t last_mark_time = -1; while (!done) { @@ -494,6 +535,7 @@ load_time_bookmarks() = (const char*) sqlite3_column_text(stmt.in(), 6); const char* tags = (const char*) sqlite3_column_text(stmt.in(), 7); + const auto annotations = sqlite3_column_text(stmt.in(), 8); int64_t mark_time = sqlite3_column_int64(stmt.in(), 3); struct timeval log_tv; struct exttm log_tm; @@ -509,21 +551,29 @@ load_time_bookmarks() continue; } - if (!dts.scan( - log_time, strlen(log_time), NULL, &log_tm, log_tv)) + if (dts.scan(log_time, + strlen(log_time), + nullptr, + &log_tm, + log_tv) + == nullptr) { + log_warning("bad log time: %s", log_time); continue; } - auto line_iter - = lower_bound(lf->begin(), lf->end(), log_tv); + auto line_iter = format->lf_time_ordered + ? std::lower_bound(lf->begin(), lf->end(), log_tv) + : lf->begin(); while (line_iter != lf->end()) { - struct timeval line_tv = line_iter->get_timeval(); + const auto line_tv = line_iter->get_timeval(); - if ((line_tv.tv_sec != log_tv.tv_sec) - || (line_tv.tv_usec != log_tv.tv_usec)) - { - break; + if (line_tv != log_tv) { + if (format->lf_time_ordered) { + break; + } + ++line_iter; + continue; } auto cl = content_line_t( @@ -542,65 +592,93 @@ load_time_bookmarks() .update(cl) .to_string(); - if (line_hash == log_hash) { - auto& bm_meta = lf->get_bookmark_metadata(); - auto line_number = static_cast<uint32_t>( - std::distance(lf->begin(), line_iter)); - content_line_t line_cl = content_line_t( - base_content_line + line_number); - bool meta = false; - - if (part_name != nullptr && part_name[0] != '\0') { - lss.set_user_mark(&textview_curses::BM_META, - line_cl); - bm_meta[line_number].bm_name = part_name; - meta = true; - } - if (comment != nullptr && comment[0] != '\0') { + if (line_hash != log_hash) { + ++line_iter; + continue; + } + auto& bm_meta = lf->get_bookmark_metadata(); + auto line_number = static_cast<uint32_t>( + std::distance(lf->begin(), line_iter)); + content_line_t line_cl + = content_line_t(base_content_line + line_number); + bool meta = false; + + if (part_name != nullptr && part_name[0] != '\0') { + lss.set_user_mark(&textview_curses::BM_PARTITION, + line_cl); + bm_meta[line_number].bm_name = part_name; + meta = true; + } + if (comment != nullptr && comment[0] != '\0') { + lss.set_user_mark(&textview_curses::BM_META, + line_cl); + bm_meta[line_number].bm_comment = comment; + meta = true; + } + if (tags != nullptr && tags[0] != '\0') { + auto_mem<yajl_val_s> tag_list(yajl_tree_free); + char error_buffer[1024]; + + tag_list = yajl_tree_parse( + tags, error_buffer, sizeof(error_buffer)); + if (!YAJL_IS_ARRAY(tag_list.in())) { + log_error("invalid tags column: %s", tags); + } else { lss.set_user_mark(&textview_curses::BM_META, line_cl); - bm_meta[line_number].bm_comment = comment; - meta = true; - } - if (tags != nullptr && tags[0] != '\0') { - auto_mem<yajl_val_s> tag_list(yajl_tree_free); - char error_buffer[1024]; - - tag_list = yajl_tree_parse( - tags, error_buffer, sizeof(error_buffer)); - if (!YAJL_IS_ARRAY(tag_list.in())) { - log_error("invalid tags column: %s", tags); - } else { - lss.set_user_mark(&textview_curses::BM_META, - line_cl); - for (size_t lpc = 0; - lpc < tag_list.in()->u.array.len; - lpc++) - { - yajl_val elem - = tag_list.in() - ->u.array.values[lpc]; - - if (!YAJL_IS_STRING(elem)) { - continue; - } - bookmark_metadata::KNOWN_TAGS.insert( - elem->u.string); - bm_meta[line_number].add_tag( - elem->u.string); + for (size_t lpc = 0; + lpc < tag_list.in()->u.array.len; + lpc++) + { + yajl_val elem + = tag_list.in()->u.array.values[lpc]; + + if (!YAJL_IS_STRING(elem)) { + continue; } + bookmark_metadata::KNOWN_TAGS.insert( + elem->u.string); + bm_meta[line_number].add_tag( + elem->u.string); } - meta = true; } - if (!meta) { - marked_session_lines.push_back(line_cl); - lss.set_user_mark(&textview_curses::BM_USER, + meta = true; + } + if (annotations != nullptr && annotations[0] != '\0') { + static const intern_string_t SRC + = intern_string::lookup("annotations"); + + const auto anno_sf + = string_fragment::from_c_str(annotations); + auto parse_res + = logmsg_annotations_handlers.parser_for(SRC) + .of(anno_sf); + if (parse_res.isErr()) { + log_error( + "unable to parse annotations JSON -- " + "%s", + parse_res.unwrapErr()[0] + .to_attr_line() + .get_string() + .c_str()); + } else { + lss.set_user_mark(&textview_curses::BM_META, line_cl); + bm_meta[line_number].bm_annotations + = parse_res.unwrap(); + meta = true; } - reload_needed = true; } - - ++line_iter; + if (!meta) { + marked_session_lines.emplace_back( + lf->original_line_time(line_iter), + format->get_name(), + line_hash); + lss.set_user_mark(&textview_curses::BM_USER, + line_cl); + } + reload_needed = true; + break; } break; } @@ -719,11 +797,12 @@ load_time_bookmarks() if (lf->get_content_id() == log_hash) { int file_line = std::distance(lf->begin(), line_iter); - content_line_t line_cl - = content_line_t(base_content_line + file_line); struct timeval offset; - offset_session_lines.push_back(line_cl); + offset_session_lines.emplace_back( + lf->original_line_time(line_iter), + lf->get_format_ptr()->get_name(), + log_hash); offset.tv_sec = sqlite3_column_int64(stmt.in(), 4); offset.tv_usec = sqlite3_column_int64(stmt.in(), 5); lf->adjust_content_time(file_line, offset); @@ -761,136 +840,32 @@ read_files(yajlpp_parse_context* ypc, const unsigned char* str, size_t len) return 1; } -static int -read_current_search(yajlpp_parse_context* ypc, - const unsigned char* str, - size_t len) -{ - const auto regex = std::string((const char*) str, len); - const char** view_name; - int view_index; - - view_name = find(lnav_view_strings, - lnav_view_strings + LNV__MAX, - ypc->get_path_fragment(-2)); - view_index = view_name - lnav_view_strings; - - if (view_index < LNV__MAX && !regex.empty()) { - lnav_data.ld_views[view_index].execute_search(regex); - lnav_data.ld_views[view_index].set_follow_search_for(-1, {}); - } - - return 1; -} - -static int -read_top_line(yajlpp_parse_context* ypc, long long value) -{ - const char** view_name; - int view_index; - - view_name = find(lnav_view_strings, - lnav_view_strings + LNV__MAX, - ypc->get_path_fragment(-2)); - view_index = view_name - lnav_view_strings; - if (view_index < LNV__MAX) { - session_data.sd_view_states[view_index].vs_top = value; - } - - return 1; -} - -static int -read_focused_line(yajlpp_parse_context* ypc, long long value) -{ - const char** view_name; - int view_index; - - view_name = find(lnav_view_strings, - lnav_view_strings + LNV__MAX, - ypc->get_path_fragment(-2)); - view_index = view_name - lnav_view_strings; - if (view_index < LNV__MAX) { - session_data.sd_view_states[view_index].vs_selection = value; - } - - return 1; -} - -static int -read_word_wrap(yajlpp_parse_context* ypc, int value) -{ - const char** view_name; - int view_index; - - view_name = find(lnav_view_strings, - lnav_view_strings + LNV__MAX, - ypc->get_path_fragment(-2)); - view_index = view_name - lnav_view_strings; - if (view_index == LNV_HELP) { - } else if (view_index < LNV__MAX) { - textview_curses& tc = lnav_data.ld_views[view_index]; - - tc.set_word_wrap(value); - } - - return 1; -} - -static int -read_filtering(yajlpp_parse_context* ypc, int value) -{ - const char** view_name; - int view_index; - - view_name = find(lnav_view_strings, - lnav_view_strings + LNV__MAX, - ypc->get_path_fragment(-2)); - view_index = view_name - lnav_view_strings; - if (view_index == LNV_HELP) { - } else if (view_index < LNV__MAX) { - textview_curses& tc = lnav_data.ld_views[view_index]; - - if (tc.get_sub_source() != nullptr) { - tc.get_sub_source()->tss_apply_filters = value; - } - } - - return 1; -} - -static int -read_commands(yajlpp_parse_context* ypc, const unsigned char* str, size_t len) -{ - std::string cmdline = std::string((const char*) str, len); - const char** view_name; - int view_index; - - view_name = find(lnav_view_strings, - lnav_view_strings + LNV__MAX, - ypc->get_path_fragment(-3)); - view_index = view_name - lnav_view_strings; - 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(); - } - - return 1; -} - static const struct json_path_container view_def_handlers = { - json_path_handler("top_line", read_top_line), - json_path_handler("focused_line", read_focused_line), - json_path_handler("search", read_current_search), - json_path_handler("word_wrap", read_word_wrap), - json_path_handler("filtering", read_filtering), - json_path_handler("commands#", read_commands), + json_path_handler("top_line").for_field(&view_state::vs_top), + json_path_handler("focused_line").for_field(&view_state::vs_selection), + json_path_handler("search").for_field(&view_state::vs_search), + json_path_handler("word_wrap").for_field(&view_state::vs_word_wrap), + json_path_handler("filtering").for_field(&view_state::vs_filtering), + json_path_handler("commands#").for_field(&view_state::vs_commands), }; static const struct json_path_container view_handlers = { - yajlpp::pattern_property_handler("([^/]+)").with_children( - view_def_handlers), + yajlpp::pattern_property_handler("(?<view_name>[\\w\\-]+)") + .with_obj_provider<view_state, session_data_t>( + +[](const yajlpp_provider_context& ypc, session_data_t* root) { + const char** view_name; + int view_index; + + view_name = find(lnav_view_strings, + lnav_view_strings + LNV__MAX, + ypc.get_substr("view_name")); + view_index = view_name - lnav_view_strings; + if (view_index < LNV__MAX) { + return &root->sd_view_states[view_index]; + } + return (view_state*) nullptr; + }) + .with_children(view_def_handlers), }; static const struct json_path_container file_state_handlers = { @@ -902,14 +877,15 @@ static const struct json_path_container file_state_handlers = { static const struct json_path_container file_states_handlers = { yajlpp::pattern_property_handler(R"((?<filename>[^/]+))") .with_description("Map of file names to file state objects") - .with_obj_provider<file_state, void>([](const auto& ypc, auto* root) { - auto fn = ypc.get_substr("filename"); - return &session_data.sd_file_states[fn]; - }) + .with_obj_provider<file_state, session_data_t>( + [](const auto& ypc, session_data_t* root) { + auto fn = ypc.get_substr("filename"); + return &root->sd_file_states[fn]; + }) .with_children(file_state_handlers), }; -static const struct json_path_container view_info_handlers = { +static const typed_json_path_container<session_data_t> view_info_handlers = { yajlpp::property_handler("save-time") .for_field(&session_data_t::sd_save_time), yajlpp::property_handler("time-offset") @@ -922,35 +898,54 @@ static const struct json_path_container view_info_handlers = { void load_session() { + log_info("BEGIN load_session"); load_time_bookmarks(); scan_sessions() | [](const auto pair) { - yajl_handle handle; - auto_fd fd; - lnav_data.ld_session_load_time = pair.first.second; - session_data.sd_save_time = pair.first.second; const auto& view_info_path = pair.second; - - yajlpp_parse_context ypc(intern_string::lookup(view_info_path.string()), - &view_info_handlers); - ypc.with_obj(session_data); - handle = yajl_alloc(&ypc.ypc_callbacks, nullptr, &ypc); + auto view_info_src = intern_string::lookup(view_info_path.string()); load_time_bookmarks(); - if ((fd = lnav::filesystem::openp(view_info_path, O_RDONLY)) < 0) { - perror("cannot open session file"); - } else { - unsigned char buffer[1024]; - ssize_t rc; + auto open_res = lnav::filesystem::open_file(view_info_path, O_RDONLY); + if (open_res.isErr()) { + log_error("cannot open session file: %s -- %s", + view_info_path.c_str(), + open_res.unwrapErr().c_str()); + return; + } - log_info("loading session file: %s", view_info_path.c_str()); - while ((rc = read(fd, buffer, sizeof(buffer))) > 0) { - yajl_parse(handle, buffer, rc); + auto fd = open_res.unwrap(); + unsigned char buffer[1024]; + ssize_t rc; + + log_info("loading session file: %s", view_info_path.c_str()); + auto parser = view_info_handlers.parser_for(view_info_src); + while ((rc = read(fd, buffer, sizeof(buffer))) > 0) { + auto buf_frag = string_fragment::from_bytes(buffer, rc); + auto parse_res = parser.consume(buf_frag); + if (parse_res.isErr()) { + log_error("failed to load session: %s -- %s", + view_info_path.c_str(), + parse_res.unwrapErr()[0] + .to_attr_line() + .get_string() + .c_str()); + return; } - yajl_complete_parse(handle); } - yajl_free(handle); + + auto complete_res = parser.complete(); + if (complete_res.isErr()) { + log_error("failed to load session: %s -- %s", + view_info_path.c_str(), + complete_res.unwrapErr()[0] + .to_attr_line() + .get_string() + .c_str()); + return; + } + session_data = complete_res.unwrap(); bool log_changes = false, text_changes = false; @@ -964,16 +959,15 @@ load_session() log_debug("found state for file: %s %d", lf->get_content_id().c_str(), iter->second.fs_is_visible); - lnav_data.ld_log_source.find_data(lf) | [iter](auto ld) { - ld->set_visibility(iter->second.fs_is_visible); - }; - if (!iter->second.fs_is_visible) { - if (lf->get_format() != nullptr) { - log_changes = true; - } else { - text_changes = true; - } - } + lnav_data.ld_log_source.find_data(lf) | + [iter, &log_changes](auto ld) { + if (ld->ld_visible != iter->second.fs_is_visible) { + ld->get_file_ptr()->set_indexing( + iter->second.fs_is_visible); + ld->set_visibility(iter->second.fs_is_visible); + log_changes = true; + } + }; } if (log_changes) { @@ -986,6 +980,8 @@ load_session() lnav::events::publish(lnav_data.ld_db.in(), lnav::events::session::loaded{}); + + log_info("END load_session"); } static void @@ -1001,13 +997,41 @@ save_user_bookmarks(sqlite3* db, sqlite3_stmt* stmt, bookmark_vector<content_line_t>& user_marks) { - logfile_sub_source& lss = lnav_data.ld_log_source; - bookmark_vector<content_line_t>::iterator iter; + auto& lss = lnav_data.ld_log_source; - for (iter = user_marks.begin(); iter != user_marks.end(); ++iter) { + for (auto iter = user_marks.begin(); iter != user_marks.end(); ++iter) { content_line_t cl = *iter; auto line_meta_opt = lss.find_bookmark_metadata(cl); - if (!bind_line(db, stmt, cl, lnav_data.ld_session_time)) { + auto lf = lss.find(cl); + if (lf == nullptr) { + continue; + } + + sqlite3_clear_bindings(stmt); + + auto line_iter = lf->begin() + cl; + auto read_result = lf->read_line(line_iter); + + if (read_result.isErr()) { + continue; + } + + auto line_hash = read_result + .map([cl](auto sbr) { + return hasher() + .update(sbr.get_data(), sbr.length()) + .update(cl) + .to_string(); + }) + .unwrap(); + + if (bind_values(stmt, + lf->original_line_time(line_iter), + lf->get_format()->get_name(), + line_hash, + lnav_data.ld_session_time) + != SQLITE_OK) + { continue; } @@ -1019,8 +1043,8 @@ save_user_bookmarks(sqlite3* db, return; } } else { - bookmark_metadata& line_meta = *(line_meta_opt.value()); - if (line_meta.empty()) { + const auto& line_meta = *(line_meta_opt.value()); + if (line_meta.empty(bookmark_metadata::categories::any)) { continue; } @@ -1071,6 +1095,25 @@ save_user_bookmarks(sqlite3* db, log_error("could not bind tags -- %s", sqlite3_errmsg(db)); return; } + + if (!line_meta.bm_annotations.la_pairs.empty()) { + auto anno_str = logmsg_annotations_handlers.to_string( + line_meta.bm_annotations); + + if (sqlite3_bind_text(stmt, + 8, + anno_str.c_str(), + anno_str.length(), + SQLITE_TRANSIENT) + != SQLITE_OK) + { + log_error("could not bind annotations -- %s", + sqlite3_errmsg(db)); + return; + } + } else { + sqlite3_bind_null(stmt, 8); + } } if (sqlite3_step(stmt) != SQLITE_DONE) { @@ -1079,7 +1122,9 @@ save_user_bookmarks(sqlite3* db, return; } - marked_session_lines.push_back(cl); + marked_session_lines.emplace_back(lf->original_line_time(line_iter), + lf->get_format_ptr()->get_name(), + line_hash); sqlite3_reset(stmt); } @@ -1142,7 +1187,7 @@ save_time_bookmarks() sqlite3_reset(stmt.in()); } - session_data.sd_recent_netlocs.insert(netlocs.begin(), netlocs.end()); + recent_refs.rr_netlocs.insert(netlocs.begin(), netlocs.end()); } logfile_sub_source& lss = lnav_data.ld_log_source; @@ -1163,10 +1208,14 @@ save_time_bookmarks() } for (auto& marked_session_line : marked_session_lines) { - if (!bind_line(db.in(), - stmt.in(), - marked_session_line, - lnav_data.ld_session_time)) + sqlite3_clear_bindings(stmt.in()); + + if (bind_values(stmt, + marked_session_line.sl_time, + marked_session_line.sl_format_name, + marked_session_line.sl_line_hash, + lnav_data.ld_session_time) + != SQLITE_OK) { continue; } @@ -1185,8 +1234,8 @@ save_time_bookmarks() if (sqlite3_prepare_v2(db.in(), "REPLACE INTO bookmarks" " (log_time, log_format, log_hash, session_time, " - "part_name, comment, tags)" - " VALUES (?, ?, ?, ?, ?, ?, ?)", + "part_name, comment, tags, annotations)" + " VALUES (?, ?, ?, ?, ?, ?, ?, ?)", -1, stmt.out(), nullptr) @@ -1240,7 +1289,11 @@ save_time_bookmarks() } save_user_bookmarks(db.in(), stmt.in(), bm[&textview_curses::BM_USER]); - save_user_bookmarks(db.in(), stmt.in(), bm[&textview_curses::BM_META]); + auto all_meta_marks = bm[&textview_curses::BM_META]; + const auto& bm_parts = bm[&textview_curses::BM_PARTITION]; + all_meta_marks.insert( + all_meta_marks.end(), bm_parts.begin(), bm_parts.end()); + save_user_bookmarks(db.in(), stmt.in(), all_meta_marks); if (sqlite3_prepare_v2(db.in(), "DELETE FROM time_offset WHERE " @@ -1257,10 +1310,14 @@ save_time_bookmarks() } for (auto& offset_session_line : offset_session_lines) { - if (!bind_line(db.in(), - stmt.in(), - offset_session_line, - lnav_data.ld_session_time)) + sqlite3_clear_bindings(stmt.in()); + + if (bind_values(stmt, + offset_session_line.sl_time, + offset_session_line.sl_format_name, + offset_session_line.sl_line_hash, + lnav_data.ld_session_time) + != SQLITE_OK) { continue; } @@ -1452,7 +1509,7 @@ save_session_with_id(const std::string& session_id) for (auto& lf : lnav_data.ld_active_files.fc_files) { auto ld_opt = lnav_data.ld_log_source.find_data(lf); - file_states.gen(lf->get_filename()); + file_states.gen(lf->get_filename().native()); { yajlpp_map file_state(handle); @@ -1469,7 +1526,7 @@ save_session_with_id(const std::string& session_id) yajlpp_map top_view_map(handle); for (int lpc = 0; lpc < LNV__MAX; lpc++) { - textview_curses& tc = lnav_data.ld_views[lpc]; + auto& tc = lnav_data.ld_views[lpc]; unsigned long width; vis_line_t height; @@ -1540,47 +1597,44 @@ save_session_with_id(const std::string& session_id) for (const auto& format : log_format::get_root_formats()) { - auto* elf = dynamic_cast<external_log_format*>( - format.get()); - - if (elf == nullptr) { - continue; - } + auto field_states = format->get_field_states(); - for (const auto& vd : elf->elf_value_defs) { - if (!vd.second->vd_meta.lvm_user_hidden) { + for (const auto& fs_pair : field_states) { + if (!fs_pair.second.lvm_user_hidden) { continue; } - if (vd.second->vd_meta.lvm_user_hidden.value()) - { - cmd_array.gen("hide-fields " - + elf->get_name().to_string() - + "." + vd.first.to_string()); - } else if (vd.second->vd_meta.lvm_hidden) { - cmd_array.gen("show-fields " - + elf->get_name().to_string() - + "." + vd.first.to_string()); + if (fs_pair.second.lvm_user_hidden.value()) { + cmd_array.gen( + "hide-fields " + + format->get_name().to_string() + "." + + fs_pair.first.to_string()); + } else if (fs_pair.second.lvm_hidden) { + cmd_array.gen( + "show-fields " + + format->get_name().to_string() + "." + + fs_pair.first.to_string()); } } } - logfile_sub_source& lss = lnav_data.ld_log_source; + auto& lss = lnav_data.ld_log_source; - struct timeval min_time, max_time; - bool have_min_time = lss.get_min_log_time(min_time); - bool have_max_time = lss.get_max_log_time(max_time); + auto min_time_opt = lss.get_min_log_time(); + auto max_time_opt = lss.get_max_log_time(); char min_time_str[32], max_time_str[32]; - sql_strftime( - min_time_str, sizeof(min_time_str), min_time); - if (have_min_time) { + if (min_time_opt) { + sql_strftime(min_time_str, + sizeof(min_time_str), + min_time_opt.value()); cmd_array.gen("hide-lines-before " + std::string(min_time_str)); } - if (have_max_time) { - sql_strftime( - max_time_str, sizeof(max_time_str), max_time); + if (max_time_opt) { + sql_strftime(max_time_str, + sizeof(max_time_str), + max_time_opt.value()); cmd_array.gen("hide-lines-after " + std::string(max_time_str)); } @@ -1701,6 +1755,89 @@ reset_session() } void +lnav::session::restore_view_states() +{ + log_debug("restoring view states"); + for (size_t view_index = 0; view_index < LNV__MAX; view_index++) { + const auto& vs = session_data.sd_view_states[view_index]; + auto& tview = lnav_data.ld_views[view_index]; + bool has_loc = false; + + if (view_index == LNV_TEXT) { + auto lf = lnav_data.ld_text_source.current_file(); + if (lf != nullptr) { + has_loc = lf->get_open_options().loo_init_location.valid(); + if (!has_loc) { + switch (lf->get_text_format()) { + case text_format_t::TF_UNKNOWN: + case text_format_t::TF_LOG: + break; + default: + if (vs.vs_top == 0 && tview.get_top() > 0) { + log_debug("setting to 0"); + tview.set_top(0_vl); + } + break; + } + } + } + } + + if (!has_loc && vs.vs_top >= 0 + && (view_index == LNV_LOG || tview.get_top() == 0_vl + || tview.get_top() == tview.get_top_for_last_row())) + { + log_info("restoring %s view top: %d", + lnav_view_strings[view_index], + vs.vs_top); + lnav_data.ld_views[view_index].set_top(vis_line_t(vs.vs_top), true); + lnav_data.ld_views[view_index].set_selection(-1_vl); + } + if (!has_loc && vs.vs_selection) { + log_info("restoring %s view selection: %d", + lnav_view_strings[view_index], + vs.vs_selection.value()); + lnav_data.ld_views[view_index].set_selection( + vis_line_t(vs.vs_selection.value())); + } + + if (!vs.vs_search.empty()) { + tview.execute_search(vs.vs_search); + tview.set_follow_search_for(-1, {}); + } + tview.set_word_wrap(vs.vs_word_wrap); + if (tview.get_sub_source() != nullptr) { + tview.get_sub_source()->tss_apply_filters = vs.vs_filtering; + } + for (const auto& cmdline : vs.vs_commands) { + auto active = ensure_view(&tview); + auto exec_cmd_res + = execute_command(lnav_data.ld_exec_context, cmdline); + if (exec_cmd_res.isOk()) { + log_info("Result: %s", exec_cmd_res.unwrap().c_str()); + } else { + log_error("Result: %s", + exec_cmd_res.unwrapErr() + .to_attr_line() + .get_string() + .c_str()); + } + if (!active) { + lnav_data.ld_view_stack.pop_back(); + lnav_data.ld_view_stack.top() | [](auto* tc) { + // XXX + if (tc == &lnav_data.ld_views[LNV_GANTT]) { + auto tss = tc->get_sub_source(); + tss->text_filters_changed(); + tc->reload_data(); + } + }; + } + } + } +} + +void lnav::session::regex101::insert_entry(const lnav::session::regex101::entry& ei) { constexpr const char* STMT = R"( |