From 10eb9617b1022384d526928d0fd620f8e29b5cd0 Mon Sep 17 00:00:00 2001 From: Timothy Stack Date: Sat, 1 Mar 2014 23:40:12 -0800 Subject: [PATCH] [sql] add a bookmark column to the log tables --- NEWS | 3 +++ src/help.txt | 2 ++ src/lnav.cc | 7 ------- src/lnav_config.cc | 2 +- src/log_format.hh | 15 ++++++++++++--- src/log_vtab_impl.cc | 32 ++++++++++++++++++++++++++++++-- src/log_vtab_impl.hh | 2 ++ src/logfile.cc | 1 - src/logfile_sub_source.cc | 12 +++++++++--- src/logfile_sub_source.hh | 38 ++++++++++++++------------------------ src/textview_curses.hh | 18 ++++++++++++++++++ 11 files changed, 91 insertions(+), 41 deletions(-) diff --git a/NEWS b/NEWS index b0f8678d..381a5e6c 100644 --- a/NEWS +++ b/NEWS @@ -16,6 +16,9 @@ lnav v0.6.3: displayed can be tab-completed. * The '-H' option was added so you can view the internal help text. * Added the 'g/G' hotkeys to move to the top/bottom of the file. + * Added a 'log_mark' column to the log tables that indicates whether or + not a log message is bookmarked. The field is writable, so you can + bookmark lines using an SQL UPDATE query. Fixes: * Performance improvements. diff --git a/src/help.txt b/src/help.txt index 53195a7b..8e613579 100644 --- a/src/help.txt +++ b/src/help.txt @@ -458,6 +458,8 @@ All log tables contain at least the following columns: log_idle_msecs The amount of time, in milliseconds, between the current log message and the previous one. log_level The log level (e.g. info, error, etc...). + log_mark The bookmark status for the line. This column + can be written to using an UPDATE query. log_path The full path to the file. log_text The raw line of text. Note that this column is not included in the result of a 'select *', but diff --git a/src/lnav.cc b/src/lnav.cc index fa7e4e19..7548fa7d 100644 --- a/src/lnav.cc +++ b/src/lnav.cc @@ -1891,13 +1891,6 @@ static void handle_paging_key(int ch) } break; - case 'x': - if (tc == &lnav_data.ld_views[LNV_LOG]) { - lnav_data.ld_log_source.toggle_user_mark(&BM_EXAMPLE, - vis_line_t(tc->get_top())); - } - break; - case 'X': execute_command("close"); break; diff --git a/src/lnav_config.cc b/src/lnav_config.cc index 501f1e9e..38037066 100644 --- a/src/lnav_config.cc +++ b/src/lnav_config.cc @@ -103,7 +103,7 @@ void ensure_dotlnav(void) path += "/*"; if (glob(path.c_str(), GLOB_NOCHECK, NULL, gl.inout()) == 0) { for (int lpc = 0; - lpc < (gl->gl_pathc - MAX_CRASH_LOG_COUNT); + lpc < ((int)gl->gl_pathc - MAX_CRASH_LOG_COUNT); lpc++) { remove(gl->gl_pathv[lpc]); } diff --git a/src/log_format.hh b/src/log_format.hh index 34b18d11..f6c48236 100644 --- a/src/log_format.hh +++ b/src/log_format.hh @@ -119,11 +119,11 @@ public: LEVEL__MAX, - LEVEL_MULTILINE = 0x40, /*< Start of a multiline entry. */ + LEVEL_MARK = 0x40, /*< Bookmarked line. */ LEVEL_CONTINUED = 0x80, /*< Continuation of multiline entry. */ /** Mask of flags for the level field. */ - LEVEL__FLAGS = (LEVEL_MULTILINE | LEVEL_CONTINUED) + LEVEL__FLAGS = (LEVEL_MARK | LEVEL_CONTINUED) } level_t; static const char *level_names[LEVEL__MAX]; @@ -198,7 +198,16 @@ public: this->ll_millis = tv.tv_usec / 1000; }; - void set_multiline(void) { this->ll_level |= LEVEL_MULTILINE; }; + void set_mark(bool val) { + if (val) { + this->ll_level |= LEVEL_MARK; + } + else { + this->ll_level &= ~LEVEL_MARK; + } + }; + + bool is_marked(void) const { return this->ll_level & LEVEL_MARK; }; /** @param l The logging level. */ void set_level(level_t l) { this->ll_level = l; }; diff --git a/src/log_vtab_impl.cc b/src/log_vtab_impl.cc index 06e4df32..689a3c92 100644 --- a/src/log_vtab_impl.cc +++ b/src/log_vtab_impl.cc @@ -69,7 +69,8 @@ std::string log_vtab_impl::get_table_statement(void) << " log_part text collate naturalnocase,\n" << " log_time datetime,\n" << " log_idle_msecs int,\n" - << " log_level text collate loglevel,\n"; + << " log_level text collate loglevel,\n" + << " log_mark boolean,\n"; this->get_columns(cols); this->vi_column_count = cols.size(); for (iter = cols.begin(); iter != cols.end(); iter++) { @@ -328,6 +329,12 @@ static int vt_column(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int col) } break; + case VT_COL_MARK: + { + sqlite3_result_int(ctx, ll->is_marked()); + } + break; + default: if (col > (VT_COL_MAX + vt->vi->vi_column_count - 1)) { int post_col_number = col - @@ -430,6 +437,27 @@ static int vt_best_index(sqlite3_vtab *tab, sqlite3_index_info *p_info) return SQLITE_OK; } +static int vt_update(sqlite3_vtab *tab, + int argc, + sqlite3_value **argv, + sqlite_int64 *rowid) +{ + vtab *vt = (vtab *)tab; + int retval = SQLITE_READONLY; + + if (argc > 1 && sqlite3_value_type(argv[0]) != SQLITE_NULL && + sqlite3_value_int64(argv[0]) == sqlite3_value_int64(argv[1])) { + int64_t rowid = sqlite3_value_int64(argv[0]) >> 8; + int val = sqlite3_value_int(argv[2 + VT_COL_MARK]); + + vt->tc->set_user_mark(&textview_curses::BM_USER, vis_line_t(rowid), val); + + retval = SQLITE_OK; + } + + return retval; +} + static sqlite3_module vtab_module = { 0, /* iVersion */ vt_create, /* xCreate - create a vtable */ @@ -444,7 +472,7 @@ static sqlite3_module vtab_module = { vt_eof, /* xEof - inidicate end of result set*/ vt_column, /* xColumn - read data */ vt_rowid, /* xRowid - read data */ - NULL, /* xUpdate - write data */ + vt_update, /* xUpdate - write data */ NULL, /* xBegin - begin transaction */ NULL, /* xSync - sync transaction */ NULL, /* xCommit - commit transaction */ diff --git a/src/log_vtab_impl.hh b/src/log_vtab_impl.hh index fc86af53..8b9aa395 100644 --- a/src/log_vtab_impl.hh +++ b/src/log_vtab_impl.hh @@ -45,6 +45,7 @@ enum { VT_COL_LOG_TIME, VT_COL_IDLE_MSECS, VT_COL_LEVEL, + VT_COL_MARK, VT_COL_MAX }; @@ -113,6 +114,7 @@ public: virtual void get_foreign_keys(std::vector &keys_inout) { keys_inout.push_back("log_line"); + keys_inout.push_back("log_mark"); }; virtual void extract(logfile *lf, diff --git a/src/logfile.cc b/src/logfile.cc index 8b52c292..ff065f14 100644 --- a/src/logfile.cc +++ b/src/logfile.cc @@ -206,7 +206,6 @@ void logfile::process_prefix(off_t offset, char *prefix, int len) * Assume this line is part of the previous one(s) and copy the * metadata over. */ - ll.set_multiline(); last_time = ll.get_time(); last_millis = ll.get_millis(); if (this->lf_format.get() != NULL) { diff --git a/src/logfile_sub_source.cc b/src/logfile_sub_source.cc index 7a107c63..a5a98bdd 100644 --- a/src/logfile_sub_source.cc +++ b/src/logfile_sub_source.cc @@ -590,22 +590,28 @@ void logfile_sub_source::text_update_marks(vis_bookmarks &bm) content_line_t cl = this->lss_index[vl]; logfile * lf; + lf = this->find(cl); + for (bookmarks::type::iterator iter = this->lss_user_marks.begin(); iter != this->lss_user_marks.end(); ++iter) { if (binary_search(iter->second.begin(), iter->second.end(), cl)) { bm[iter->first].insert_once(vl); + + if (iter->first == &textview_curses::BM_USER) { + logfile::iterator ll = lf->begin() + cl; + + ll->set_mark(true); + } } } - lf = this->find(cl); - if (lf != last_file) { bm[&BM_FILES].insert_once(vl); } - switch ((*lf)[cl].get_level() & ~logline::LEVEL_MULTILINE) { + switch ((*lf)[cl].get_level() & ~logline::LEVEL_MARK) { case logline::LEVEL_WARNING: bm[&BM_WARNINGS].insert_once(vl); break; diff --git a/src/logfile_sub_source.hh b/src/logfile_sub_source.hh index c3476bdd..cd111e8c 100644 --- a/src/logfile_sub_source.hh +++ b/src/logfile_sub_source.hh @@ -154,6 +154,11 @@ public: content_line_t cl = this->lss_index[line]; std::vector::iterator lb; + if (bm == &textview_curses::BM_USER) { + logline *ll = this->find_line(cl); + + ll->set_mark(added); + } lb = std::lower_bound(this->lss_user_marks[bm].begin(), this->lss_user_marks[bm].end(), cl); @@ -171,6 +176,15 @@ public: void text_clear_marks(bookmark_type_t *bm) { + std::vector::iterator iter; + + if (bm == &textview_curses::BM_USER) { + for (iter = this->lss_user_marks[bm].begin(); + iter != this->lss_user_marks[bm].end(); + ++iter) { + this->find_line(*iter)->set_mark(false); + } + } this->lss_user_marks[bm].clear(); }; @@ -237,30 +251,6 @@ public: void text_update_marks(vis_bookmarks &bm); - void toggle_user_mark(bookmark_type_t *bm, - vis_line_t start_line, - vis_line_t end_line = vis_line_t(-1)) - { - if (end_line == -1) { - end_line = start_line; - } - if (start_line > end_line) { - std::swap(start_line, end_line); - } - for (vis_line_t curr_line = start_line; curr_line <= end_line; - ++curr_line) { - bookmark_vector &bv = - this->lss_user_marks[bm]; - bookmark_vector::iterator iter; - - iter = bv.insert_once(this->at(curr_line)); - if (iter == bv.end()) {} - else { - bv.erase(iter); - } - } - }; - void set_user_mark(bookmark_type_t *bm, content_line_t cl) { this->lss_user_marks[bm].insert_once(cl); diff --git a/src/textview_curses.hh b/src/textview_curses.hh index 3b217198..0d6cd7e4 100644 --- a/src/textview_curses.hh +++ b/src/textview_curses.hh @@ -248,6 +248,24 @@ public: } }; + void set_user_mark(bookmark_type_t *bm, vis_line_t vl, bool marked) { + bookmark_vector &bv = this->tc_bookmarks[bm]; + bookmark_vector::iterator iter; + + if (marked) { + bv.insert_once(vl); + } + else { + iter = std::lower_bound(bv.begin(), bv.end(), vl); + if (iter != bv.end() && *iter == vl) { + bv.erase(iter); + } + } + if (this->tc_sub_source != NULL) { + this->tc_sub_source->text_mark(bm, (int)vl, marked); + } + }; + void set_sub_source(text_sub_source *src) { this->tc_sub_source = src;