diff --git a/NEWS.md b/NEWS.md index cece5f61..998a764a 100644 --- a/NEWS.md +++ b/NEWS.md @@ -51,6 +51,10 @@ Interface changes: running: `:config /ui/movement/mode top` +* In the parser details panel (opened by pressing `p`), you + can now hide/show fields by moving the cursor line to the + given field and pressing the space bar or by clicking on + the diamond with the mouse. Bug Fixes: * With the recent xz backdoor shenanigans, it seems like a good diff --git a/docs/source/intro.rst b/docs/source/intro.rst index 8bd6c03a..28172a1c 100644 --- a/docs/source/intro.rst +++ b/docs/source/intro.rst @@ -139,41 +139,7 @@ and can be set as the default by executing the following command: :config /ui/mouse/mode enabled -With mouse support enabled, many of the UI elements will respond to -mouse inputs: - -* clicking on the main view will move the cursor to the given - row and dragging will scroll the view as needed; -* shift + clicking/dragging in the main view will highlight - lines and then toggle their bookmark status on release; -* double-clicking will select the underlying token and - drag-selecting within a line will select the given text; -* with selected text, pressing :kbd:`c` will copy the text to - the clipboard and it will be used as the suggestion for - searching/filtering; -* clicking in the scroll area will move the view by a page and - dragging the scrollbar will move the view to the given spot; -* clicking on the breadcrumb bar will select a crumb and - selecting a possibility from the popup will move to that - location in the view; -* clicking on portions of the bottom status bar will trigger - a relevant action (e.g. clicking the line number will open - the command prompt with `:goto `); -* clicking on the configuration panel tabs (i.e. Files/Filters) - will open the selected panel and clicking parts of the - display in there will perform the relevant action (e.g. - clicking the diamond will enable/disable the file/filter); -* clicking in a prompt will move the cursor to the location. - -.. note:: - - A downside of enabling mouse support is that normal text - selection and copy will no longer work. You can press - :kbd:`F2` to quickly switch back-and-forth. Or, some - terminals have support for switching using a modifier - key, like `iTerm _` - where pressing :kbd:`Option` will allow you to select - text and copy. +See :ref:`ui_mouse` for more details. Log Formats ^^^^^^^^^^^ diff --git a/docs/source/ui.rst b/docs/source/ui.rst index de71e721..2666f4cd 100644 --- a/docs/source/ui.rst +++ b/docs/source/ui.rst @@ -416,3 +416,48 @@ range of values. The panel at the bottom of the view shows the data points themselves from the original source, the log file or the SQL query results. You can press :kbd:`TAB` to focus on the details panel so you can scroll around and get a closer look at the values. + +.. _ui_mouse: + +Mouse Support (v0.12.2+) +------------------------ + +With mouse support enabled, either through the `/ui/mouse/mode` +configuration option or by pressing :kbd:`F2`, many of the UI +elements will respond to mouse inputs: + +* clicking on the main view will move the cursor to the given + row and dragging will scroll the view as needed; +* shift + clicking/dragging in the main view will highlight + lines and then toggle their bookmark status on release; +* double-clicking will select the underlying token and + drag-selecting within a line will select the given text; +* with selected text, pressing :kbd:`c` will copy the text to + the clipboard and it will be used as the suggestion for + searching/filtering; +* clicking in the scroll area will move the view by a page and + dragging the scrollbar will move the view to the given spot; +* clicking on the breadcrumb bar will select a crumb and + selecting a possibility from the popup will move to that + location in the view; +* clicking on portions of the bottom status bar will trigger + a relevant action (e.g. clicking the line number will open + the command prompt with `:goto `); +* clicking on the configuration panel tabs (i.e. Files/Filters) + will open the selected panel and clicking parts of the + display in there will perform the relevant action (e.g. + clicking the diamond will enable/disable the file/filter); +* clicking in a prompt will move the cursor to the location. + +.. note:: + + A downside of enabling mouse support is that normal text + selection and copy will no longer work. While lnav has + some support for selection in the main view, there are + still likely to be cases where that is insufficient. + In those cases, you can press :kbd:`F2` to quickly + switch back-and-forth. Or, some terminals have support + for switching using a modifier key, like + `iTerm _` + where pressing :kbd:`Option` will allow you to select + text and copy. diff --git a/src/field_overlay_source.cc b/src/field_overlay_source.cc index 43717867..8a47f0f4 100644 --- a/src/field_overlay_source.cc +++ b/src/field_overlay_source.cc @@ -54,6 +54,7 @@ field_overlay_source::build_field_lines(const listview_curses& lv, auto& vc = view_colors::singleton(); this->fos_lines.clear(); + this->fos_row_to_field_name.clear(); if (lss.text_line_count() == 0) { this->fos_log_helper.clear(); @@ -292,7 +293,7 @@ field_overlay_source::build_field_lines(const listview_curses& lv, const log_format* last_format = nullptr; for (const auto& lv : this->fos_log_helper.ldh_line_values.lvv_values) { - auto& meta = lv.lv_meta; + const auto& meta = lv.lv_meta; if (!meta.lvm_format) { continue; } @@ -305,7 +306,7 @@ field_overlay_source::build_field_lines(const listview_curses& lv, auto* curr_elf = dynamic_cast(curr_format); const auto format_name = curr_format->get_name().to_string(); attr_line_t al; - std::string str, value_str = lv.to_string(); + std::string value_str = lv.to_string(); if (curr_format != last_format) { this->fos_lines.emplace_back(" Known message fields for table " @@ -318,6 +319,8 @@ field_overlay_source::build_field_lines(const listview_curses& lv, } std::string field_name, orig_field_name; + line_range hl_range; + al.append(" ").append("|", VC_GRAPHIC.value(ACS_LTEE)).append(" "); if (meta.lvm_struct_name.empty()) { if (curr_elf && curr_elf->elf_body_field == meta.lvm_name) { field_name = LOG_BODY; @@ -332,31 +335,32 @@ field_overlay_source::build_field_lines(const listview_curses& lv, if (!this->fos_contexts.empty()) { field_name = this->fos_contexts.top().c_prefix + field_name; } - str = " " + field_name; + if (meta.is_hidden()) { + al.append("\u25c7"_comment); + } else { + al.append("\u25c6"_ok); + } + al.append(" "); + auto prefix_len = al.utf8_length_or_length(); + hl_range.lr_start = al.get_string().length(); + al.append(field_name); + hl_range.lr_end = al.get_string().length(); + al.pad_to(prefix_len + this->fos_known_key_size); + + this->fos_row_to_field_name[this->fos_lines.size()] = meta.lvm_name; } else { - str = lnav::sql::mprintf(" jget(%s, '/%q')", - meta.lvm_struct_name.get(), - meta.lvm_name.get()); + auto jget_str = lnav::sql::mprintf("jget(%s, '/%q')", + meta.lvm_struct_name.get(), + meta.lvm_name.get()); + hl_range.lr_start = al.get_string().length(); + al.append(jget_str.in()); + hl_range.lr_end = al.get_string().length(); } - str.append(this->fos_known_key_size - (str.length() - 3), ' '); - - al.with_string(str); - readline_sqlite_highlighter(al, 0); + readline_sqlite_highlighter_int(al, -1, hl_range); al.append(" = ").append(scrub_ws(value_str.c_str())); - if (meta.lvm_struct_name.empty()) { - auto prefix_len = field_name.length() - orig_field_name.length(); - al.with_attr(string_attr( - line_range(3 + prefix_len, 3 + prefix_len + field_name.size()), - VC_STYLE.value(vc.attrs_for_ident(orig_field_name)))); - } else { - al.with_attr(string_attr( - line_range(8, 8 + meta.lvm_struct_name.size()), - VC_STYLE.value(vc.attrs_for_ident(meta.lvm_struct_name)))); - } this->fos_lines.emplace_back(al); - this->add_key_line_attrs(this->fos_known_key_size); if (meta.lvm_kind == value_kind_t::VALUE_STRUCT) { json_string js = extract(value_str.c_str()); @@ -684,9 +688,11 @@ field_overlay_source::list_header_for_overlay(const listview_curses& lv, } if (lv.get_overlay_selection()) { - retval.append(" Press ") + retval.append(" ") + .append("SPC"_hotkey) + .append(": hide/show field ") .append("Esc"_hotkey) - .append(" to exit this panel."); + .append(": exit this panel"); } else { retval.append(" Press ") .append("CTRL-]"_hotkey) diff --git a/src/field_overlay_source.hh b/src/field_overlay_source.hh index 5458ad5c..0005ac86 100644 --- a/src/field_overlay_source.hh +++ b/src/field_overlay_source.hh @@ -102,6 +102,7 @@ public: std::vector fos_lines; vis_line_t fos_meta_lines_row{0_vl}; std::vector fos_meta_lines; + std::map fos_row_to_field_name; }; #endif // LNAV_FIELD_OVERLAY_SOURCE_H diff --git a/src/logfile_sub_source.cc b/src/logfile_sub_source.cc index b0a60611..d848af9c 100644 --- a/src/logfile_sub_source.cc +++ b/src/logfile_sub_source.cc @@ -42,6 +42,7 @@ #include "bookmarks.json.hh" #include "command_executor.hh" #include "config.h" +#include "field_overlay_source.hh" #include "k_merge_tree.h" #include "lnav_util.hh" #include "log_accel.hh" @@ -1382,6 +1383,30 @@ bool logfile_sub_source::list_input_handle_key(listview_curses& lv, int ch) { switch (ch) { + case ' ': { + auto ov_vl = lv.get_overlay_selection(); + if (ov_vl) { + auto* fos = dynamic_cast( + lv.get_overlay_source()); + auto iter = fos->fos_row_to_field_name.find(ov_vl.value()); + if (iter != fos->fos_row_to_field_name.end()) { + auto find_res = this->find_line_with_file(lv.get_top()); + if (find_res) { + auto file_and_line = find_res.value(); + auto* format = file_and_line.first->get_format_ptr(); + auto fstates = format->get_field_states(); + auto state_iter = fstates.find(iter->second); + if (state_iter != fstates.end()) { + format->hide_field(iter->second, + !state_iter->second.is_hidden()); + lv.set_needs_update(); + } + } + } + return true; + } + return false; + } case 'h': case 'H': case KEY_SLEFT: @@ -2995,3 +3020,14 @@ logfile_sub_source::get_anchors() return retval; } + +bool +logfile_sub_source::text_handle_mouse(textview_curses& tc, mouse_event& me) +{ + if (tc.get_overlay_selection() + && me.is_click_in(mouse_button_t::BUTTON_LEFT, 2, 4)) + { + this->list_input_handle_key(tc, ' '); + } + return true; +} diff --git a/src/logfile_sub_source.hh b/src/logfile_sub_source.hh index 785ae031..5ef3dbff 100644 --- a/src/logfile_sub_source.hh +++ b/src/logfile_sub_source.hh @@ -201,7 +201,8 @@ class logfile_sub_source , public text_time_translator , public text_accel_source , public list_input_delegate - , public text_anchors { + , public text_anchors + , public text_delegate { public: const static bookmark_type_t BM_ERRORS; const static bookmark_type_t BM_WARNINGS; @@ -688,6 +689,8 @@ public: void text_crumbs_for_line(int line, std::vector& crumbs); + bool text_handle_mouse(textview_curses& tc, mouse_event& me); + Result eval_sql_filter( sqlite3_stmt* stmt, iterator ld, logfile::const_iterator ll); diff --git a/src/readline_highlighters.cc b/src/readline_highlighters.cc index be55d204..9fe84187 100644 --- a/src/readline_highlighters.cc +++ b/src/readline_highlighters.cc @@ -41,10 +41,6 @@ #include "sql_util.hh" #include "view_curses.hh" -static void readline_sqlite_highlighter_int(attr_line_t& al, - int x, - line_range sub); - static bool is_bracket(const std::string& str, int index, bool is_lit) { @@ -237,7 +233,7 @@ readline_command_highlighter(attr_line_t& al, int x) al, x, line_range{0, (int) al.get_string().length()}); } -static void +void readline_sqlite_highlighter_int(attr_line_t& al, int x, line_range sub) { static const char* brackets[] = { diff --git a/src/readline_highlighters.hh b/src/readline_highlighters.hh index e2b83dda..78b90759 100644 --- a/src/readline_highlighters.hh +++ b/src/readline_highlighters.hh @@ -38,6 +38,7 @@ void readline_regex_highlighter(attr_line_t& line, int x); void readline_command_highlighter(attr_line_t& line, int x); +void readline_sqlite_highlighter_int(attr_line_t& line, int x, line_range sub); void readline_sqlite_highlighter(attr_line_t& line, int x); void readline_shlex_highlighter_int(attr_line_t& al, int x, line_range sub); diff --git a/src/textview_curses.cc b/src/textview_curses.cc index f3169e9e..f64fa0f4 100644 --- a/src/textview_curses.cc +++ b/src/textview_curses.cc @@ -462,8 +462,8 @@ textview_curses::handle_mouse(mouse_event& me) [](const static_overlay_content& soc) { }, - [](const overlay_content& oc) { - + [this](const overlay_content& oc) { + this->set_overlay_selection(oc.oc_line); }, [](const empty_space& es) {}); break; diff --git a/test/expected/test_sql_views_vtab.sh_4363d60040424a573ed79ee4260a32e3cd72f62c.out b/test/expected/test_sql_views_vtab.sh_4363d60040424a573ed79ee4260a32e3cd72f62c.out index 438a981f..bdb8fe30 100644 --- a/test/expected/test_sql_views_vtab.sh_4363d60040424a573ed79ee4260a32e3cd72f62c.out +++ b/test/expected/test_sql_views_vtab.sh_4363d60040424a573ed79ee4260a32e3cd72f62c.out @@ -1,11 +1,11 @@ 2023-03-24T14:26:16.243Z renovate[7] INFO Dependency extraction complete Received Time: 2023-03-24T14:26:16.243 — in the future Format: %Y-%m-%dT%H:%M:%S.%L%z Known message fields for table bunyan_log: - name = renovate - hostname = renovate-gitlab-67c4bcb5-9ggbv - pid = 7 - level = 30 - v = 0 + | ◆ name = renovate + | ◇ hostname = renovate-gitlab-67c4bcb5-9ggbv + | ◆ pid = 7 + | ◇ level = 30 + | ◇ v = 0 JSON fields: jget(log_raw_text, '/baseBranch') = main jget(log_raw_text, '/logContext') = qjifsaDDI diff --git a/test/expected/test_sql_views_vtab.sh_45dbef06572b43cb997682436e753a13e003f792.out b/test/expected/test_sql_views_vtab.sh_45dbef06572b43cb997682436e753a13e003f792.out index 0e86cf4b..f1a563fa 100644 --- a/test/expected/test_sql_views_vtab.sh_45dbef06572b43cb997682436e753a13e003f792.out +++ b/test/expected/test_sql_views_vtab.sh_45dbef06572b43cb997682436e753a13e003f792.out @@ -2,9 +2,9 @@ Received Time: 2020-12-10T06:56:41.092 — in the future Format: %Y-%m-%d %H:%M:%S,%L Pattern: /xml_msg_log/regex/std = ^\[(?<timestamp>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d{3})\]\s+(?<level>\w+)\s+\[(?<module>[^:]*):(?<line>\d+)\]\s*(?<body>[^\n]*)\n?(?<msg_data>.*) Known message fields for table xml_msg_log: - module = connect.client - line = 69 - msg_data = ␊ ␊ x␊ ␊ ␊ x␊ ␊ ␊ x␊ ␊ + | ◆ module = connect.client + | ◆ line = 69 + | ◆ msg_data = ␊ ␊ x␊ ␊ ␊ x␊ ␊ ␊ x␊ ␊ XML fields: xpath('/a-request/head/text()', xml_msg_log.msg_data) = x xpath('/a-request/request/@id', xml_msg_log.msg_data) = 1