[mouse] missed adding the view left position to the X coord

master
Tim Stack 1 month ago
parent c3f6887f38
commit c465cc6d54

@ -1,6 +1,19 @@
## lnav v0.12.2
Features:
* Added a `journald://` URL handler that will call `journalctl`
and pass any query parameters as options. For example, the
following command:
```
$ lnav 'journal://?since=yesterday'
```
Will execute the following and capture the output:
```
journalctl --output=json -f --since=yesterday
```
* Added the "last-word" line-format field shortening algorithm
from @flicus.
* Added a `stats.hist` PRQL transform that produces a histogram
@ -19,9 +32,9 @@ Features:
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;
- when text is selected: pressing `c` will copy the text to
the clipboard; the text will be used as the suggestion for
searching/filtering;
- when text is selected, a menu will pop up that can be used
to filter based on the current text, search for it, or copy
it to the clipboard;
- 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

@ -432,9 +432,9 @@ elements will respond to mouse inputs:
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;
* when text is selected, a menu will pop up that can be used
to filter based on the current text, search for it, or copy
it to the clipboard;
* 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
@ -457,7 +457,14 @@ elements will respond to mouse inputs:
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 <https://iterm2.com/documentation-preferences-profiles-terminal.html>_`
where pressing :kbd:`Option` will allow you to select
text and copy.
for switching while a modifier is pressed:
.. list-table::
:header-rows: 1
* - Key
- Terminal
* - :kbd:`Option`
- iTerm, Hyper
* - :kbd:`Fn`
- Terminal.app

@ -575,6 +575,13 @@ inline std::pair<std::string, string_attr_pair> operator"" _info(
VC_ROLE.template value(role_t::VCR_INFO));
}
inline std::pair<std::string, string_attr_pair> operator"" _status_title(
const char* str, std::size_t len)
{
return std::make_pair(std::string(str, len),
VC_ROLE.template value(role_t::VCR_STATUS_TITLE));
}
inline std::pair<std::string, string_attr_pair> operator"" _symbol(
const char* str, std::size_t len)
{

@ -130,8 +130,7 @@ public:
static const char* token2name(data_token_t token);
struct capture_t {
capture_t()
{ /* We don't initialize anything since it's a perf hit. */
capture_t() { /* We don't initialize anything since it's a perf hit. */
}
capture_t(int begin, int end) : c_begin(begin), c_end(end)
@ -189,6 +188,14 @@ public:
this->tr_capture.c_end);
}
string_fragment inner_string_fragment() const
{
return string_fragment::from_byte_range(
this->tr_data,
this->tr_inner_capture.c_begin,
this->tr_inner_capture.c_end);
}
std::string to_string() const
{
return {&this->tr_data[this->tr_capture.c_begin],

@ -31,6 +31,7 @@
#include "base/humanize.time.hh"
#include "base/snippet_highlighters.hh"
#include "command_executor.hh"
#include "config.h"
#include "log.annotate.hh"
#include "log_format_ext.hh"
@ -513,10 +514,10 @@ field_overlay_source::build_meta_line(const listview_curses& lv,
if (!line_meta_opt) {
return;
}
const auto* tc = dynamic_cast<const textview_curses*>(&lv);
auto& vc = view_colors::singleton();
const auto& line_meta = *(line_meta_opt.value());
size_t filename_width = this->fos_lss.get_filename_offset();
const auto* tc = dynamic_cast<const textview_curses*>(&lv);
if (!line_meta.bm_comment.empty()) {
const auto* lead = line_meta.bm_tags.empty() ? " \u2514 " : " \u251c ";
@ -666,6 +667,81 @@ field_overlay_source::list_value_for_overlay(
this->build_meta_line(lv, value_out, row);
}
std::vector<attr_line_t>
field_overlay_source::list_overlay_menu(const listview_curses& lv,
vis_line_t row)
{
const auto* tc = dynamic_cast<const textview_curses*>(&lv);
std::vector<attr_line_t> retval;
if (!tc->tc_text_selection_active && tc->tc_selected_text) {
const auto& sti = tc->tc_selected_text.value();
if (sti.sti_line == row) {
auto left = std::max(0, sti.sti_x - 2);
this->fos_menu_items.clear();
retval.emplace_back(attr_line_t().pad_to(left).append(
" Filter Other "_status_title));
{
attr_line_t al;
al.append(" ").append("\u2714 IN"_ok).append(" ");
int start = left;
this->fos_menu_items.emplace_back(
1_vl,
line_range{start, start + (int) al.length()},
[this](const std::string& value) {
auto cmd = fmt::format(FMT_STRING(":filter-in {}"),
lnav::pcre2pp::quote(value));
execute_any(*this->fos_lss.get_exec_context(), cmd);
});
start += al.length();
al.append(":mag_right:"_emoji)
.append(" Search ")
.with_attr_for_all(VC_ROLE.value(role_t::VCR_STATUS));
this->fos_menu_items.emplace_back(
1_vl,
line_range{start, start + (int) al.length()},
[this](const std::string& value) {
auto cmd = fmt::format(FMT_STRING("/{}"),
lnav::pcre2pp::quote(value));
execute_any(*this->fos_lss.get_exec_context(), cmd);
});
retval.emplace_back(attr_line_t().pad_to(left).append(al));
}
{
attr_line_t al;
al.append(" ").append("\u2718 OUT"_error).append(" ");
int start = left;
this->fos_menu_items.emplace_back(
2_vl,
line_range{start, start + (int) al.length()},
[this](const std::string& value) {
auto cmd = fmt::format(FMT_STRING(":filter-out {}"),
lnav::pcre2pp::quote(value));
execute_any(*this->fos_lss.get_exec_context(), cmd);
});
start += al.length();
al.append(":clipboard:"_emoji)
.append(" Copy ")
.with_attr_for_all(VC_ROLE.value(role_t::VCR_STATUS));
this->fos_menu_items.emplace_back(
2_vl,
line_range{start, start + (int) al.length()},
[this](const std::string& value) {
execute_any(*this->fos_lss.get_exec_context(),
"|lnav-copy-text");
});
retval.emplace_back(attr_line_t().pad_to(left).append(al));
}
}
}
return retval;
}
nonstd::optional<attr_line_t>
field_overlay_source::list_header_for_overlay(const listview_curses& lv,
vis_line_t vl)

@ -55,6 +55,9 @@ public:
this->fos_meta_lines_row = -1_vl;
}
std::vector<attr_line_t> list_overlay_menu(const listview_curses& lv,
vis_line_t row) override;
nonstd::optional<attr_line_t> list_header_for_overlay(
const listview_curses& lv, vis_line_t vl) override;
@ -103,6 +106,20 @@ public:
vis_line_t fos_meta_lines_row{0_vl};
std::vector<attr_line_t> fos_meta_lines;
std::map<size_t, intern_string_t> fos_row_to_field_name;
struct menu_item {
menu_item(vis_line_t line,
line_range range,
std::function<void(const std::string&)> action)
: mi_line(line), mi_range(range), mi_action(std::move(action))
{
}
vis_line_t mi_line;
line_range mi_range;
std::function<void(const std::string&)> mi_action;
};
std::vector<menu_item> fos_menu_items;
};
#endif // LNAV_FIELD_OVERLAY_SOURCE_H

@ -440,7 +440,10 @@ files_overlay_source::list_static_overlay(const listview_curses& lv,
}
bool
files_sub_source::text_handle_mouse(textview_curses& tc, mouse_event& me)
files_sub_source::text_handle_mouse(
textview_curses& tc,
const listview_curses::display_line_content_t&,
mouse_event& me)
{
if (me.is_click_in(mouse_button_t::BUTTON_LEFT, 1, 3)) {
this->list_input_handle_key(tc, ' ');

@ -61,7 +61,9 @@ public:
int line,
line_flags_t raw) override;
bool text_handle_mouse(textview_curses& tc, mouse_event& me) override;
bool text_handle_mouse(textview_curses& tc,
const listview_curses::display_line_content_t&,
mouse_event& me) override;
size_t fss_last_line_len{0};
attr_line_t fss_curr_line;

@ -686,7 +686,10 @@ filter_sub_source::list_input_handle_scroll_out(listview_curses& lv)
}
bool
filter_sub_source::text_handle_mouse(textview_curses& tc, mouse_event& me)
filter_sub_source::text_handle_mouse(
textview_curses& tc,
const listview_curses::display_line_content_t&,
mouse_event& me)
{
if (this->fss_editing) {
return true;

@ -72,7 +72,9 @@ public:
int line,
line_flags_t raw) override;
bool text_handle_mouse(textview_curses& tc, mouse_event& me) override;
bool text_handle_mouse(textview_curses& tc,
const listview_curses::display_line_content_t&,
mouse_event& me) override;
void rl_change(readline_curses* rc);

@ -475,6 +475,28 @@ listview_curses::do_update()
lr.lr_start = this->lv_left;
lr.lr_end = this->lv_left + wrap_width;
auto ov_menu = this->lv_overlay_source->list_overlay_menu(
*this, row);
auto ov_menu_row = 0_vl;
for (auto& ov_menu_line : ov_menu) {
if (y >= bottom) {
break;
}
this->lv_display_lines.push_back(overlay_menu{
ov_menu_row,
});
mvwattrline(this->lv_window,
y,
this->vc_x,
ov_menu_line,
lr,
role_t::VCR_ALT_ROW);
ov_menu_row += 1_vl;
++y;
}
this->lv_overlay_source->list_value_for_overlay(
*this, row, row_overlay_content);
auto overlay_height = this->get_overlay_height(

@ -114,6 +114,12 @@ public:
return false;
}
virtual std::vector<attr_line_t> list_overlay_menu(
const listview_curses& lv, vis_line_t line)
{
return {};
}
virtual nonstd::optional<attr_line_t> list_header_for_overlay(
const listview_curses& lv, vis_line_t line)
{
@ -528,6 +534,24 @@ public:
virtual void invoke_scroll() { this->lv_scroll(this); }
struct main_content {
vis_line_t mc_line;
};
struct static_overlay_content {};
struct overlay_menu {
vis_line_t om_line;
};
struct overlay_content {
vis_line_t oc_line;
};
struct empty_space {};
using display_line_content_t = mapbox::util::variant<main_content,
overlay_menu,
static_overlay_content,
overlay_content,
empty_space>;
protected:
void delegate_scroll_out()
{
@ -581,20 +605,6 @@ protected:
lv_mode_t lv_mouse_mode{lv_mode_t::NONE};
vis_line_t lv_tail_space{1};
struct main_content {
vis_line_t mc_line;
};
struct static_overlay_content {};
struct overlay_content {
vis_line_t oc_line;
};
struct empty_space {};
using display_line_content_t = mapbox::util::variant<main_content,
static_overlay_content,
overlay_content,
empty_space>;
std::vector<display_line_content_t> lv_display_lines;
unsigned int lv_scroll_top{0};
unsigned int lv_scroll_bottom{0};

@ -3022,8 +3022,27 @@ logfile_sub_source::get_anchors()
}
bool
logfile_sub_source::text_handle_mouse(textview_curses& tc, mouse_event& me)
logfile_sub_source::text_handle_mouse(
textview_curses& tc,
const listview_curses::display_line_content_t& mouse_line,
mouse_event& me)
{
auto* fos = dynamic_cast<field_overlay_source*>(tc.get_overlay_source());
if (mouse_line.is<listview_curses::overlay_menu>() && tc.tc_selected_text) {
auto& om = mouse_line.get<listview_curses::overlay_menu>();
auto& sti = tc.tc_selected_text.value();
for (const auto& mi : fos->fos_menu_items) {
if (om.om_line == mi.mi_line
&& me.is_click_in(mouse_button_t::BUTTON_LEFT, mi.mi_range))
{
mi.mi_action(sti.sti_value);
break;
}
}
}
if (tc.get_overlay_selection()
&& me.is_click_in(mouse_button_t::BUTTON_LEFT, 2, 4))
{

@ -689,7 +689,9 @@ public:
void text_crumbs_for_line(int line, std::vector<breadcrumb::crumb>& crumbs);
bool text_handle_mouse(textview_curses& tc, mouse_event& me);
bool text_handle_mouse(textview_curses& tc,
const listview_curses::display_line_content_t&,
mouse_event& me);
Result<bool, lnav::console::user_message> eval_sql_filter(
sqlite3_stmt* stmt, iterator ld, logfile::const_iterator ll);
@ -702,6 +704,8 @@ public:
void set_exec_context(exec_context* ec) { this->lss_exec_context = ec; }
exec_context* get_exec_context() const { return this->lss_exec_context; }
static const uint64_t MAX_CONTENT_LINES = (1ULL << 40) - 1;
static const uint64_t MAX_LINES_PER_FILE = 256 * 1024 * 1024;
static const uint64_t MAX_FILES = (MAX_CONTENT_LINES / MAX_LINES_PER_FILE);

@ -43,12 +43,24 @@ quote(const char* unquoted)
for (int lpc = 0; unquoted[lpc]; lpc++) {
if (isalnum(unquoted[lpc]) || unquoted[lpc] == '_'
|| unquoted[lpc] == '-' || unquoted[lpc] == ' '
|| unquoted[lpc] == ':' || unquoted[lpc] == ';'
|| unquoted[lpc] & 0x80)
{
retval.push_back(unquoted[lpc]);
} else {
retval.push_back('\\');
retval.push_back(unquoted[lpc]);
switch (unquoted[lpc]) {
case '\t':
retval.push_back('t');
break;
case '\n':
retval.push_back('n');
break;
default:
retval.push_back(unquoted[lpc]);
break;
}
}
}

@ -426,22 +426,24 @@ textview_curses::handle_mouse(mouse_event& me)
: this->lv_display_lines[me.me_y];
this->get_dimensions(height, width);
auto* sub_delegate = dynamic_cast<text_delegate*>(this->tc_sub_source);
if (me.me_button != mouse_button_t::BUTTON_LEFT
|| me.me_state != mouse_button_state_t::BUTTON_STATE_RELEASED)
if (!mouse_line.is<overlay_menu>()
&& (me.me_button != mouse_button_t::BUTTON_LEFT
|| me.me_state != mouse_button_state_t::BUTTON_STATE_RELEASED))
{
this->tc_selected_text = nonstd::nullopt;
this->set_needs_update();
}
auto* sub_delegate = dynamic_cast<text_delegate*>(this->tc_sub_source);
switch (me.me_state) {
case mouse_button_state_t::BUTTON_STATE_PRESSED: {
this->tc_text_selection_active = true;
if (!this->lv_selectable) {
this->set_selectable(true);
}
mouse_line.match(
[this, &me, sub_delegate](const main_content& mc) {
[this, &me, sub_delegate, &mouse_line](const main_content& mc) {
if (this->vc_enabled) {
if (this->tc_supports_marks
&& me.is_modifier_pressed(
@ -453,12 +455,14 @@ textview_curses::handle_mouse(mouse_event& me)
this->tc_press_event = me;
}
if (this->tc_delegate != nullptr) {
this->tc_delegate->text_handle_mouse(*this, me);
this->tc_delegate->text_handle_mouse(
*this, mouse_line, me);
}
if (sub_delegate != nullptr) {
sub_delegate->text_handle_mouse(*this, me);
sub_delegate->text_handle_mouse(*this, mouse_line, me);
}
},
[](const overlay_menu& om) {},
[](const static_overlay_content& soc) {
},
@ -472,8 +476,9 @@ textview_curses::handle_mouse(mouse_event& me)
if (!this->lv_selectable) {
this->set_selectable(true);
}
this->tc_text_selection_active = false;
mouse_line.match(
[this, &me, sub_delegate](const main_content& mc) {
[this, &me, &mouse_line, sub_delegate](const main_content& mc) {
if (this->vc_enabled) {
if (this->tc_supports_marks) {
attr_line_t al;
@ -481,8 +486,9 @@ textview_curses::handle_mouse(mouse_event& me)
this->textview_value_for_row(mc.mc_line, al);
auto line_sf
= string_fragment::from_str(al.get_string());
auto cursor_sf
= line_sf.sub_cell_range(me.me_x, me.me_x);
auto cursor_sf = line_sf.sub_cell_range(
this->lv_left + me.me_x,
this->lv_left + me.me_x);
auto ds = data_scanner(line_sf);
auto tf = this->tc_sub_source->get_text_format();
while (true) {
@ -492,9 +498,10 @@ textview_curses::handle_mouse(mouse_event& me)
}
auto tok = tok_res.value();
auto tok_sf = tok.to_string_fragment();
auto tok_sf = tok.inner_string_fragment();
if (tok_sf.contains(cursor_sf)) {
this->tc_selected_text = selected_text_info{
me.me_x,
mc.mc_line,
line_range{
tok_sf.sf_begin,
@ -510,14 +517,18 @@ textview_curses::handle_mouse(mouse_event& me)
this->set_selection_without_context(mc.mc_line);
}
if (this->tc_delegate != nullptr) {
this->tc_delegate->text_handle_mouse(*this, me);
this->tc_delegate->text_handle_mouse(
*this, mouse_line, me);
}
if (sub_delegate != nullptr) {
sub_delegate->text_handle_mouse(*this, me);
sub_delegate->text_handle_mouse(*this, mouse_line, me);
}
},
[](const static_overlay_content& soc) {
},
[](const overlay_menu& om) {
},
[](const overlay_content& oc) {
@ -526,19 +537,24 @@ textview_curses::handle_mouse(mouse_event& me)
break;
}
case mouse_button_state_t::BUTTON_STATE_DRAGGED: {
this->tc_text_selection_active = true;
if (!this->vc_enabled) {
} else if (me.me_y == me.me_press_y) {
if (mouse_line.is<main_content>()) {
auto& mc = mouse_line.get<main_content>();
attr_line_t al;
auto low_x = std::min(me.me_x, me.me_press_x);
auto high_x = std::max(me.me_x, me.me_press_x);
auto low_x = std::min(this->lv_left + me.me_x,
this->lv_left + me.me_press_x);
auto high_x = std::max(this->lv_left + me.me_x,
this->lv_left + me.me_press_x);
this->set_selection_without_context(mc.mc_line);
this->textview_value_for_row(mc.mc_line, al);
auto line_sf = string_fragment::from_str(al.get_string());
auto cursor_sf = line_sf.sub_cell_range(low_x, high_x);
if (!cursor_sf.empty()) {
this->tc_selected_text = {
me.me_press_x,
mc.mc_line,
line_range{
cursor_sf.sf_begin,
@ -561,6 +577,7 @@ textview_curses::handle_mouse(mouse_event& me)
break;
}
case mouse_button_state_t::BUTTON_STATE_RELEASED: {
this->tc_text_selection_active = false;
if (this->vc_enabled) {
if (this->tc_selection_start) {
this->toggle_user_mark(&BM_USER,
@ -571,10 +588,14 @@ textview_curses::handle_mouse(mouse_event& me)
this->tc_selection_start = nonstd::nullopt;
}
if (this->tc_delegate != nullptr) {
this->tc_delegate->text_handle_mouse(*this, me);
this->tc_delegate->text_handle_mouse(*this, mouse_line, me);
}
if (sub_delegate != nullptr) {
sub_delegate->text_handle_mouse(*this, me);
sub_delegate->text_handle_mouse(*this, mouse_line, me);
}
if (mouse_line.is<overlay_menu>()) {
this->tc_selected_text = nonstd::nullopt;
this->set_needs_update();
}
break;
}

@ -551,7 +551,10 @@ class text_delegate {
public:
virtual ~text_delegate() = default;
virtual bool text_handle_mouse(textview_curses& tc, mouse_event& me)
virtual bool text_handle_mouse(
textview_curses& tc,
const listview_curses::display_line_content_t&,
mouse_event& me)
{
return false;
}
@ -801,12 +804,14 @@ public:
nonstd::optional<role_t> tc_disabled_cursor_role;
struct selected_text_info {
int sti_x;
int64_t sti_line;
line_range sti_range;
std::string sti_value;
};
nonstd::optional<selected_text_info> tc_selected_text;
bool tc_text_selection_active{false};
protected:
class grep_highlighter {

Loading…
Cancel
Save