• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tstack / lnav / 20284884426-2753

16 Dec 2025 10:23PM UTC coverage: 68.23% (-0.7%) from 68.903%
20284884426-2753

push

github

tstack
[log] show invalid utf hex dump in log view too

25 of 25 new or added lines in 2 files covered. (100.0%)

503 existing lines in 33 files now uncovered.

51170 of 74996 relevant lines covered (68.23%)

433797.6 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/text_overlay_menu.cc
1
/**
2
 * Copyright (c) 2024, Timothy Stack
3
 *
4
 * All rights reserved.
5
 *
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions are met:
8
 *
9
 * * Redistributions of source code must retain the above copyright notice, this
10
 * list of conditions and the following disclaimer.
11
 * * Redistributions in binary form must reproduce the above copyright notice,
12
 * this list of conditions and the following disclaimer in the documentation
13
 * and/or other materials provided with the distribution.
14
 * * Neither the name of Timothy Stack nor the names of its contributors
15
 * may be used to endorse or promote products derived from this software
16
 * without specific prior written permission.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
19
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
22
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
 */
29

30
#include <vector>
31

32
#include "text_overlay_menu.hh"
33

34
#include "command_executor.hh"
35
#include "config.h"
36
#include "lnav.hh"
37
#include "lnav.prompt.hh"
38
#include "md4cpp.hh"
39
#include "readline_highlighters.hh"
40
#include "sysclip.hh"
41
#include "textview_curses.hh"
42

43
using namespace md4cpp::literals;
44
using namespace lnav::roles::literals;
45

46
std::vector<attr_line_t>
UNCOV
47
text_overlay_menu::list_overlay_menu(const listview_curses& lv, vis_line_t row)
×
48
{
49
    static constexpr auto MENU_WIDTH = 25;
50

UNCOV
51
    const auto* tc = dynamic_cast<const textview_curses*>(&lv);
×
UNCOV
52
    std::vector<attr_line_t> retval;
×
53

UNCOV
54
    if (tc->tc_text_selection_active || !tc->tc_selected_text) {
×
UNCOV
55
        return retval;
×
56
    }
57

58
    const auto* tss = tc->get_sub_source();
×
59
    const auto& sti = tc->tc_selected_text.value();
×
60
    const auto supports_filtering
×
61
        = tss != nullptr && tss->tss_supports_filtering;
×
62

63
    if (sti.sti_line != row) {
×
64
        return retval;
×
65
    }
66
    auto title = " Actions "_status_title;
×
67
    auto left = std::max(0, sti.sti_x - 2);
×
68
    auto [height, width] = lv.get_dimensions();
×
69

70
    if (left + MENU_WIDTH >= (ssize_t) width) {
×
71
        left = width - MENU_WIDTH;
×
72
    }
73

74
    this->los_menu_items.clear();
×
75

76
    auto is_link = !sti.sti_href.empty();
×
77
    auto menu_line = vis_line_t{1};
×
78
    attr_line_t link_cmd;
×
79
    if (is_link) {
×
80
        auto href_al
81
            = attr_line_t(" Link: ")
×
82
                  .append(lnav::roles::table_header(sti.sti_href))
×
83
                  .with_attr_for_all(VC_ROLE.value(role_t::VCR_STATUS_INFO))
×
84
                  .move();
×
85
        retval.emplace_back(href_al);
×
86
        menu_line += 1_vl;
×
87

88
        std::string filepath;
×
89
        auto file_attr_opt = get_string_attr(sti.sti_attrs, &L_FILE);
×
90
        if (file_attr_opt) {
×
91
            filepath = file_attr_opt.value()
×
92
                           ->sa_value.get<std::shared_ptr<logfile>>()
×
93
                           ->get_filename();
×
94
        }
95

96
        {
97
            logline_value_vector values;
×
98
            exec_context ec(&values, internal_sql_callback, pipe_callback);
×
99

100
            auto exec_res
101
                = ec.execute_with(INTERNAL_SRC_LOC,
×
102
                                  "|lnav-link-callback $href $filepath",
103
                                  std::make_pair("href", sti.sti_href),
×
104
                                  std::make_pair("filepath", filepath));
×
105
            if (exec_res.isOk()) {
×
106
                link_cmd = exec_res.unwrap();
×
107
            }
108
        }
109
        if (link_cmd.empty()) {
×
110
            link_cmd = ":open $href";
×
111
        }
112
        readline_lnav_highlighter(link_cmd, std::nullopt);
×
113

114
        auto cmd_al
115
            = attr_line_t(" ")
×
116
                  .append("Command"_table_header)
×
117
                  .append(": ")
×
118
                  .append(link_cmd)
×
119
                  .with_attr_for_all(VC_ROLE.value(role_t::VCR_STATUS_INFO))
×
120
                  .with_attr_for_all(
×
121
                      VC_STYLE.value(text_attrs::with_underline()))
×
122
                  .move();
×
123
        retval.emplace_back(cmd_al);
×
124
        menu_line += 1_vl;
×
125
    }
126

127
    retval.emplace_back(attr_line_t().pad_to(left).append(title));
×
128
    {
129
        attr_line_t al;
×
130

131
        int start = left;
×
132
        if (is_link || supports_filtering) {
×
133
            if (is_link) {
×
134
                if (startswith(link_cmd.al_string, ":open")) {
×
135
                    al.append(":floppy_disk:"_emoji)
×
136
                        .append(" Open in lnav")
×
137
                        .append("  ");
×
138
                } else {
139
                    al.append(":play_button:"_emoji)
×
140
                        .append(" Execute")
×
141
                        .append("        ");
×
142
                }
143
            } else {
144
                al.append(" ").append("\u2714 Filter-in"_ok).append("   ");
×
145
            }
146
            this->los_menu_items.emplace_back(
×
147
                menu_line,
148
                line_range{start, start + (int) al.length()},
×
149
                [is_link, link_cmd, sti](const std::string& value) {
×
150
                    const auto cmd = is_link
×
151
                        ? link_cmd.get_string()
×
152
                        : fmt::format(FMT_STRING(":filter-in {}"),
×
153
                                      lnav::pcre2pp::quote(value));
×
154
                    auto& dls = lnav_data.ld_db_row_source;
×
155
                    auto previous_db_gen = dls.dls_generation;
×
156
                    auto exec_res
157
                        = lnav_data.ld_exec_context
158
                              .with_provenance(exec_context::mouse_input{})
×
159
                              ->execute_with(
160
                                  INTERNAL_SRC_LOC,
×
161
                                  cmd,
162
                                  std::make_pair("href", sti.sti_href));
×
163
                    if (exec_res.isOk()) {
×
164
                        lnav::prompt::get().p_editor.set_inactive_value(
×
165
                            exec_res.unwrap());
×
166
                        if (dls.dls_generation != previous_db_gen
×
167
                            && dls.dls_row_cursors.size() > 1)
×
168
                        {
169
                            ensure_view(&lnav_data.ld_views[LNV_DB]);
×
170
                        }
171
                    }
172
                });
×
173
            start += al.length();
×
174
        }
175

176
        if (is_link) {
×
177
            al.append("      ");
×
178
        } else {
179
            al.append(":mag_right:"_emoji).append(" Search ");
×
180
        }
181
        al.with_attr_for_all(VC_ROLE.value(role_t::VCR_STATUS));
×
182
        if (!is_link) {
×
183
            this->los_menu_items.emplace_back(
×
184
                menu_line,
185
                line_range{start, start + (int) al.length()},
×
186
                [](const std::string& value) {
×
187
                    auto cmd = fmt::format(FMT_STRING("/{}"),
×
188
                                           lnav::pcre2pp::quote(value));
×
189
                    lnav_data.ld_exec_context
190
                        .with_provenance(exec_context::mouse_input{})
×
191
                        ->execute(INTERNAL_SRC_LOC, cmd);
×
192
                });
×
193
        }
194
        retval.emplace_back(attr_line_t().pad_to(left).append(al));
×
195
    }
196
    menu_line += 1_vl;
×
197
    {
198
        attr_line_t al;
×
199

200
        int start = left;
×
201
        if (is_link || supports_filtering) {
×
202
            if (is_link) {
×
203
                al.append(":globe_with_meridians:"_emoji).append(" Open   ");
×
204
            } else {
205
                al.append(" ").append("\u2718 Filter-out"_error).append("  ");
×
206
            }
207
            this->los_menu_items.emplace_back(
×
208
                menu_line,
209
                line_range{start, start + (int) al.length()},
×
210
                [is_link, sti](const std::string& value) {
×
211
                    auto cmd = is_link
×
212
                        ? ":xopen $href"
×
213
                        : fmt::format(FMT_STRING(":filter-out {}"),
×
214
                                      lnav::pcre2pp::quote(value));
×
215
                    auto exec_res
216
                        = lnav_data.ld_exec_context
217
                              .with_provenance(exec_context::mouse_input{})
×
218
                              ->execute_with(
219
                                  INTERNAL_SRC_LOC,
×
220
                                  cmd,
221
                                  std::make_pair("href", sti.sti_href));
×
222
                    if (exec_res.isOk()) {
×
223
                        lnav::prompt::get().p_editor.set_inactive_value(
×
224
                            exec_res.unwrap());
×
225
                    }
226
                });
×
227
            start += al.length();
×
228
        }
229
        al.append(":clipboard:"_emoji)
×
230
            .append(is_link ? " Copy link " : " Copy   ")
×
231
            .with_attr_for_all(VC_ROLE.value(role_t::VCR_STATUS));
×
232
        this->los_menu_items.emplace_back(
×
233
            menu_line,
234
            line_range{start, start + (int) al.length()},
×
235
            [is_link, sti](const std::string& value) {
×
236
                auto clip_res = sysclip::open(sysclip::type_t::GENERAL);
×
237
                if (clip_res.isErr()) {
×
238
                    log_error("unable to open clipboard: %s",
×
239
                              clip_res.unwrapErr().c_str());
240
                    return;
×
241
                }
242

243
                auto clip_pipe = clip_res.unwrap();
×
244
                if (is_link) {
×
245
                    fwrite(sti.sti_href.c_str(),
×
246
                           1,
247
                           sti.sti_href.length(),
248
                           clip_pipe.in());
249
                } else {
250
                    fwrite(value.c_str(), 1, value.length(), clip_pipe.in());
×
251
                }
252
            });
×
253
        retval.emplace_back(attr_line_t().pad_to(left).append(al));
×
254
    }
255

256
    return retval;
×
257
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc