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

tstack / lnav / 17589970077-2502

09 Sep 2025 05:00PM UTC coverage: 65.196% (-5.0%) from 70.225%
17589970077-2502

push

github

tstack
[format] add fields for source file/line

Knowing the source file/line context in a log
message can help find log messages when using
log2src.

56 of 70 new or added lines in 2 files covered. (80.0%)

13954 existing lines in 210 files now uncovered.

45516 of 69814 relevant lines covered (65.2%)

404154.37 hits per line

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

0.0
/src/filter_sub_source.cc
1
/**
2
 * Copyright (c) 2018, 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 <memory>
31
#include <utility>
32

33
#include "filter_sub_source.hh"
34

35
#include "base/attr_line.builder.hh"
36
#include "base/auto_mem.hh"
37
#include "base/func_util.hh"
38
#include "base/itertools.hh"
39
#include "base/opt_util.hh"
40
#include "cmd.parser.hh"
41
#include "config.h"
42
#include "data_scanner.hh"
43
#include "lnav.hh"
44
#include "lnav.prompt.hh"
45
#include "readline_highlighters.hh"
46
#include "readline_possibilities.hh"
47
#include "sql_util.hh"
48
#include "textinput_curses.hh"
49

50
using namespace lnav::roles::literals;
51

UNCOV
52
filter_sub_source::filter_sub_source(std::shared_ptr<textinput_curses> editor)
×
UNCOV
53
    : fss_editor(std::move(editor)),
×
54
      fss_regexp_history(
UNCOV
55
          lnav::textinput::history::for_context("regexp-filter"_frag)),
×
UNCOV
56
      fss_sql_history(lnav::textinput::history::for_context("sql-filter"_frag))
×
57
{
UNCOV
58
    this->fss_editor->set_visible(false);
×
UNCOV
59
    this->fss_editor->set_x(25);
×
UNCOV
60
    this->fss_editor->tc_popup.set_title("Pattern");
×
UNCOV
61
    this->fss_editor->tc_height = 1;
×
UNCOV
62
    this->fss_editor->tc_on_change
×
UNCOV
63
        = bind_mem(&filter_sub_source::rl_change, this);
×
UNCOV
64
    this->fss_editor->tc_on_history_search
×
UNCOV
65
        = bind_mem(&filter_sub_source::rl_history, this);
×
UNCOV
66
    this->fss_editor->tc_on_history_list
×
UNCOV
67
        = bind_mem(&filter_sub_source::rl_history, this);
×
UNCOV
68
    this->fss_editor->tc_on_completion_request
×
UNCOV
69
        = bind_mem(&filter_sub_source::rl_completion_request, this);
×
UNCOV
70
    this->fss_editor->tc_on_completion
×
UNCOV
71
        = bind_mem(&filter_sub_source::rl_completion, this);
×
UNCOV
72
    this->fss_editor->tc_on_perform
×
UNCOV
73
        = bind_mem(&filter_sub_source::rl_perform, this);
×
UNCOV
74
    this->fss_editor->tc_on_blur = bind_mem(&filter_sub_source::rl_blur, this);
×
UNCOV
75
    this->fss_editor->tc_on_abort
×
UNCOV
76
        = bind_mem(&filter_sub_source::rl_abort, this);
×
77
}
78

79
void
UNCOV
80
filter_sub_source::register_view(textview_curses* tc)
×
81
{
UNCOV
82
    text_sub_source::register_view(tc);
×
UNCOV
83
    tc->add_child_view(this->fss_editor.get());
×
84
}
85

86
bool
UNCOV
87
filter_sub_source::list_input_handle_key(listview_curses& lv, const ncinput& ch)
×
88
{
89
    static auto& prompt = lnav::prompt::get();
90

91
    if (this->fss_editing) {
×
UNCOV
92
        return this->fss_editor->handle_key(ch);
×
93
    }
94

UNCOV
95
    switch (ch.eff_text[0]) {
×
UNCOV
96
        case 'f': {
×
UNCOV
97
            auto* top_view = *lnav_data.ld_view_stack.top();
×
UNCOV
98
            auto* tss = top_view->get_sub_source();
×
99

100
            tss->toggle_apply_filters();
×
101
            top_view->reload_data();
×
UNCOV
102
            break;
×
103
        }
104
        case ' ': {
×
105
            auto* top_view = *lnav_data.ld_view_stack.top();
×
UNCOV
106
            auto* tss = top_view->get_sub_source();
×
107
            auto& fs = tss->get_filters();
×
108

109
            if (fs.empty() || !lv.get_selection()) {
×
110
                return true;
×
111
            }
112

113
            auto tf = *(fs.begin() + lv.get_selection().value());
×
114

UNCOV
115
            fs.set_filter_enabled(tf, !tf->is_enabled());
×
116
            tss->text_filters_changed();
×
UNCOV
117
            lv.reload_data();
×
118
            top_view->reload_data();
×
119
            return true;
×
120
        }
121
        case 't': {
×
122
            auto* top_view = *lnav_data.ld_view_stack.top();
×
UNCOV
123
            auto* tss = top_view->get_sub_source();
×
124
            auto& fs = tss->get_filters();
×
125

126
            if (fs.empty() || !lv.get_selection()) {
×
127
                return true;
×
128
            }
129

130
            auto tf = *(fs.begin() + lv.get_selection().value());
×
131

UNCOV
132
            if (tf->get_type() == text_filter::INCLUDE) {
×
133
                tf->set_type(text_filter::EXCLUDE);
×
134
            } else {
135
                tf->set_type(text_filter::INCLUDE);
×
136
            }
137

138
            tss->text_filters_changed();
×
UNCOV
139
            lv.reload_data();
×
UNCOV
140
            top_view->reload_data();
×
141
            return true;
×
142
        }
143
        case 'D': {
×
144
            auto* top_view = *lnav_data.ld_view_stack.top();
×
UNCOV
145
            auto* tss = top_view->get_sub_source();
×
146
            auto& fs = tss->get_filters();
×
147

148
            if (fs.empty() || !lv.get_selection()) {
×
149
                return true;
×
150
            }
151

152
            auto tf = *(fs.begin() + lv.get_selection().value());
×
153

UNCOV
154
            fs.delete_filter(tf->get_id());
×
155
            lv.reload_data();
×
UNCOV
156
            tss->text_filters_changed();
×
157
            top_view->reload_data();
×
158
            return true;
×
159
        }
160
        case 'i': {
×
161
            auto* top_view = *lnav_data.ld_view_stack.top();
×
UNCOV
162
            auto* tss = top_view->get_sub_source();
×
163
            auto& fs = tss->get_filters();
×
164
            auto filter_index = fs.next_index();
×
165

166
            if (!filter_index) {
×
167
                lnav_data.ld_filter_help_status_source.fss_error.set_value(
×
168
                    "error: too many filters");
169
                return true;
×
170
            }
171

172
            auto ef = std::make_shared<empty_filter>(
UNCOV
173
                text_filter::type_t::INCLUDE, *filter_index);
×
UNCOV
174
            fs.add_filter(ef);
×
UNCOV
175
            lv.set_selection(vis_line_t(fs.size() - 1));
×
176
            lv.reload_data();
×
177

178
            this->fss_editing = true;
×
179
            this->tss_view->set_enabled(false);
×
180
            this->fss_view_text_possibilities
181
                = view_text_possibilities(*top_view);
×
182
            this->fss_editor->tc_text_format = text_format_t::TF_PCRE;
×
UNCOV
183
            this->fss_editor->set_y(lv.get_y_for_selection());
×
184
            this->fss_editor->set_content("");
×
UNCOV
185
            this->fss_editor->tc_suggestion = top_view->get_input_suggestion();
×
UNCOV
186
            this->fss_editor->set_visible(true);
×
UNCOV
187
            this->fss_editor->focus();
×
UNCOV
188
            this->fss_filter_state = true;
×
189
            ef->disable();
×
190
            return true;
×
191
        }
192
        case 'o': {
×
193
            auto* top_view = *lnav_data.ld_view_stack.top();
×
194
            auto* tss = top_view->get_sub_source();
×
195
            auto& fs = tss->get_filters();
×
196
            auto filter_index = fs.next_index();
×
197

UNCOV
198
            if (!filter_index) {
×
UNCOV
199
                lnav_data.ld_filter_help_status_source.fss_error.set_value(
×
200
                    "error: too many filters");
UNCOV
201
                return true;
×
202
            }
203

204
            auto ef = std::make_shared<empty_filter>(
UNCOV
205
                text_filter::type_t::EXCLUDE, *filter_index);
×
206
            fs.add_filter(ef);
×
UNCOV
207
            lv.set_selection(vis_line_t(fs.size() - 1));
×
208
            lv.reload_data();
×
209

UNCOV
210
            this->fss_editing = true;
×
UNCOV
211
            this->tss_view->set_enabled(false);
×
212

UNCOV
213
            this->fss_editor->tc_text_format = text_format_t::TF_PCRE;
×
UNCOV
214
            this->fss_editor->set_y(lv.get_y_for_selection());
×
UNCOV
215
            this->fss_editor->set_visible(true);
×
UNCOV
216
            this->fss_editor->set_content("");
×
217
            this->fss_view_text_possibilities
UNCOV
218
                = view_text_possibilities(*top_view);
×
UNCOV
219
            this->fss_editor->tc_suggestion = top_view->get_input_suggestion();
×
UNCOV
220
            this->fss_editor->focus();
×
UNCOV
221
            this->fss_filter_state = true;
×
UNCOV
222
            ef->disable();
×
UNCOV
223
            return true;
×
224
        }
UNCOV
225
        case '\r':
×
226
        case NCKEY_ENTER: {
UNCOV
227
            auto* top_view = *lnav_data.ld_view_stack.top();
×
UNCOV
228
            auto* tss = top_view->get_sub_source();
×
UNCOV
229
            auto& fs = tss->get_filters();
×
230

UNCOV
231
            if (fs.empty() || !lv.get_selection()) {
×
UNCOV
232
                return true;
×
233
            }
234

235
            auto tf = *(fs.begin() + lv.get_selection().value());
×
236

237
            this->fss_editing = true;
×
238
            this->tss_view->set_enabled(false);
×
239

UNCOV
240
            this->fss_editor->tc_text_format
×
241
                = tf->get_lang() == filter_lang_t::SQL ? text_format_t::TF_SQL
×
242
                                                       : text_format_t::TF_PCRE;
UNCOV
243
            if (tf->get_lang() == filter_lang_t::SQL) {
×
UNCOV
244
                prompt.refresh_sql_completions(*top_view);
×
245
                prompt.refresh_sql_expr_completions(*top_view);
×
246
            }
247
            this->fss_editor->set_y(lv.get_y_for_selection());
×
248
            this->fss_editor->set_visible(true);
×
UNCOV
249
            this->fss_editor->tc_suggestion.clear();
×
250
            this->fss_editor->set_content(tf->get_id());
×
251
            this->fss_view_text_possibilities
252
                = view_text_possibilities(*top_view);
×
253
            this->fss_editor->focus();
×
UNCOV
254
            this->fss_filter_state = tf->is_enabled();
×
255
            tf->disable();
×
256
            tss->text_filters_changed();
×
UNCOV
257
            return true;
×
258
        }
259
        case 'n': {
×
260
            lnav_data.ld_exec_context.execute(INTERNAL_SRC_LOC,
×
261
                                              ":next-mark search");
262
            return true;
×
263
        }
264
        case 'N': {
×
265
            lnav_data.ld_exec_context.execute(INTERNAL_SRC_LOC,
×
266
                                              ":prev-mark search");
267
            return true;
×
268
        }
UNCOV
269
        case '/': {
×
270
            lnav_data.ld_exec_context.execute(INTERNAL_SRC_LOC,
×
271
                                              ":prompt search-filters");
272
            return true;
×
273
        }
274
        default:
×
275
            log_debug("unhandled %x", ch);
×
276
            break;
×
277
    }
278

279
    return false;
×
280
}
281

282
size_t
UNCOV
283
filter_sub_source::text_line_count()
×
284
{
UNCOV
285
    return (lnav_data.ld_view_stack.top() |
×
UNCOV
286
                [](auto tc) -> std::optional<size_t> {
×
UNCOV
287
               text_sub_source* tss = tc->get_sub_source();
×
288

UNCOV
289
               if (tss == nullptr) {
×
UNCOV
290
                   return std::nullopt;
×
291
               }
UNCOV
292
               auto& fs = tss->get_filters();
×
UNCOV
293
               return std::make_optional(fs.size());
×
UNCOV
294
           })
×
UNCOV
295
        .value_or(0);
×
296
}
297

298
size_t
UNCOV
299
filter_sub_source::text_line_width(textview_curses& curses)
×
300
{
UNCOV
301
    textview_curses* top_view = *lnav_data.ld_view_stack.top();
×
UNCOV
302
    text_sub_source* tss = top_view->get_sub_source();
×
UNCOV
303
    filter_stack& fs = tss->get_filters();
×
UNCOV
304
    size_t retval = 0;
×
305

UNCOV
306
    for (auto& filter : fs) {
×
307
        retval = std::max(filter->get_id().size() + 8, retval);
×
308
    }
309

310
    return retval;
×
311
}
312

313
line_info
314
filter_sub_source::text_value_for_line(textview_curses& tc,
×
315
                                       int line,
316
                                       std::string& value_out,
317
                                       line_flags_t flags)
318
{
UNCOV
319
    auto* top_view = *lnav_data.ld_view_stack.top();
×
UNCOV
320
    auto* tss = top_view->get_sub_source();
×
UNCOV
321
    auto& fs = tss->get_filters();
×
UNCOV
322
    auto tf = *(fs.begin() + line);
×
323
    bool selected
UNCOV
324
        = lnav_data.ld_mode == ln_mode_t::FILTER && line == tc.get_selection();
×
325

UNCOV
326
    this->fss_curr_line.clear();
×
UNCOV
327
    auto& al = this->fss_curr_line;
×
UNCOV
328
    attr_line_builder alb(al);
×
329

UNCOV
330
    if (selected) {
×
UNCOV
331
        al.append(" ", VC_GRAPHIC.value(NCACS_RARROW));
×
332
    } else {
UNCOV
333
        al.append(" ");
×
334
    }
UNCOV
335
    al.append(" ");
×
UNCOV
336
    if (tf->is_enabled()) {
×
UNCOV
337
        al.append("\u25c6"_ok);
×
338
    } else {
UNCOV
339
        al.append("\u25c7"_comment);
×
340
    }
341
    al.append(" ");
×
UNCOV
342
    switch (tf->get_type()) {
×
UNCOV
343
        case text_filter::INCLUDE:
×
UNCOV
344
            al.append(" ").append(lnav::roles::ok("IN")).append(" ");
×
UNCOV
345
            break;
×
UNCOV
346
        case text_filter::EXCLUDE:
×
UNCOV
347
            if (tf->get_lang() == filter_lang_t::REGEX) {
×
UNCOV
348
                al.append(lnav::roles::error("OUT")).append(" ");
×
349
            } else {
UNCOV
350
                al.append("    ");
×
351
            }
352
            break;
×
353
        default:
×
UNCOV
354
            ensure(0);
×
355
            break;
356
    }
357

358
    {
UNCOV
359
        auto ag = alb.with_attr(VC_ROLE.value(role_t::VCR_NUMBER));
×
UNCOV
360
        if (this->fss_editing && line == tc.get_selection()) {
×
361
            alb.appendf(FMT_STRING("{:>9}"), "-");
×
362
        } else {
UNCOV
363
            alb.appendf(FMT_STRING("{:>9L}"),
×
UNCOV
364
                        tss->get_filtered_count_for(tf->get_index()));
×
365
        }
366
    }
367

UNCOV
368
    al.append(" hits ").append("|", VC_GRAPHIC.value(NCACS_VLINE)).append(" ");
×
369

UNCOV
370
    attr_line_t content{tf->get_id()};
×
UNCOV
371
    switch (tf->get_lang()) {
×
UNCOV
372
        case filter_lang_t::REGEX:
×
UNCOV
373
            readline_regex_highlighter(content, std::nullopt);
×
UNCOV
374
            break;
×
UNCOV
375
        case filter_lang_t::SQL:
×
UNCOV
376
            readline_sql_highlighter(
×
377
                content, lnav::sql::dialect::sqlite, std::nullopt);
UNCOV
378
            break;
×
UNCOV
379
        case filter_lang_t::NONE:
×
UNCOV
380
            break;
×
381
    }
UNCOV
382
    al.append(content);
×
383

UNCOV
384
    if (selected) {
×
UNCOV
385
        al.with_attr_for_all(VC_ROLE.value(role_t::VCR_FOCUSED));
×
386
    }
387

388
    value_out = al.get_string();
×
389

390
    return {};
×
391
}
392

393
void
UNCOV
394
filter_sub_source::text_attrs_for_line(textview_curses& tc,
×
395
                                       int line,
396
                                       string_attrs_t& value_out)
397
{
UNCOV
398
    value_out = this->fss_curr_line.get_attrs();
×
399
}
400

401
size_t
UNCOV
402
filter_sub_source::text_size_for_line(textview_curses& tc,
×
403
                                      int line,
404
                                      text_sub_source::line_flags_t raw)
405
{
UNCOV
406
    auto* top_view = *lnav_data.ld_view_stack.top();
×
UNCOV
407
    auto* tss = top_view->get_sub_source();
×
UNCOV
408
    auto& fs = tss->get_filters();
×
UNCOV
409
    auto tf = *(fs.begin() + line);
×
410

411
    return 8 + tf->get_id().size();
×
412
}
413

414
void
415
filter_sub_source::rl_change(textinput_curses& rc)
×
416
{
417
    auto* top_view = *lnav_data.ld_view_stack.top();
×
418
    auto* tss = top_view->get_sub_source();
×
UNCOV
419
    auto& fs = tss->get_filters();
×
420
    if (fs.empty()) {
×
UNCOV
421
        return;
×
422
    }
423

UNCOV
424
    auto iter = fs.begin() + this->tss_view->get_selection().value();
×
UNCOV
425
    auto tf = *iter;
×
UNCOV
426
    auto new_value = rc.get_content();
×
427

UNCOV
428
    top_view->get_highlights().erase({highlight_source_t::PREVIEW, "preview"});
×
UNCOV
429
    top_view->set_needs_update();
×
430
    switch (tf->get_lang()) {
×
UNCOV
431
        case filter_lang_t::NONE:
×
UNCOV
432
            break;
×
UNCOV
433
        case filter_lang_t::REGEX: {
×
UNCOV
434
            if (new_value.empty()) {
×
UNCOV
435
                auto sugg = top_view->get_current_search();
×
UNCOV
436
                if (top_view->tc_selected_text) {
×
UNCOV
437
                    sugg = top_view->tc_selected_text->sti_value;
×
438
                }
439
                if (fs.get_filter(sugg) == nullptr) {
×
UNCOV
440
                    this->fss_editor->tc_suggestion = sugg;
×
441
                } else {
UNCOV
442
                    this->fss_editor->tc_suggestion.clear();
×
443
                }
444
            } else {
×
445
                auto regex_res
UNCOV
446
                    = lnav::pcre2pp::code::from(new_value, PCRE2_CASELESS);
×
447

UNCOV
448
                this->rl_completion_request_int(
×
449
                    rc, completion_request_type_t::partial);
UNCOV
450
                if (regex_res.isErr()) {
×
UNCOV
451
                    auto pe = regex_res.unwrapErr();
×
UNCOV
452
                    lnav_data.ld_filter_help_status_source.fss_error.set_value(
×
UNCOV
453
                        "error: %s", pe.get_message().c_str());
×
454
                } else {
×
455
                    auto& hm = top_view->get_highlights();
×
456
                    highlighter hl(regex_res.unwrap().to_shared());
×
457
                    auto role = tf->get_type() == text_filter::EXCLUDE
×
UNCOV
458
                        ? role_t::VCR_DIFF_DELETE
×
UNCOV
459
                        : role_t::VCR_DIFF_ADD;
×
UNCOV
460
                    hl.with_role(role);
×
UNCOV
461
                    hl.with_attrs(text_attrs::with_styles(
×
462
                        text_attrs::style::blink, text_attrs::style::reverse));
UNCOV
463
                    hm[{highlight_source_t::PREVIEW, "preview"}] = hl;
×
UNCOV
464
                    top_view->set_needs_update();
×
UNCOV
465
                    lnav_data.ld_filter_help_status_source.fss_error.clear();
×
466
                }
467
            }
UNCOV
468
            break;
×
469
        }
UNCOV
470
        case filter_lang_t::SQL: {
×
UNCOV
471
            this->rl_completion_request_int(rc,
×
472
                                            completion_request_type_t::partial);
473

474
            auto full_sql
475
                = fmt::format(FMT_STRING("SELECT 1 WHERE {}"), new_value);
×
476
            auto_mem<sqlite3_stmt> stmt(sqlite3_finalize);
×
477
#ifdef SQLITE_PREPARE_PERSISTENT
478
            auto retcode = sqlite3_prepare_v3(lnav_data.ld_db.in(),
×
479
                                              full_sql.c_str(),
480
                                              full_sql.size(),
×
481
                                              SQLITE_PREPARE_PERSISTENT,
482
                                              stmt.out(),
483
                                              nullptr);
484
#else
485
            auto retcode = sqlite3_prepare_v2(lnav_data.ld_db.in(),
486
                                              full_sql.c_str(),
487
                                              full_sql.size(),
488
                                              stmt.out(),
489
                                              nullptr);
490
#endif
UNCOV
491
            if (retcode != SQLITE_OK) {
×
UNCOV
492
                lnav_data.ld_filter_help_status_source.fss_error.set_value(
×
493
                    "error: %s", sqlite3_errmsg(lnav_data.ld_db));
494
            } else {
495
                auto set_res = lnav_data.ld_log_source.set_preview_sql_filter(
UNCOV
496
                    stmt.release());
×
497

498
                if (set_res.isErr()) {
×
UNCOV
499
                    lnav_data.ld_filter_help_status_source.fss_error.set_value(
×
500
                        "error: %s",
501
                        set_res.unwrapErr()
×
UNCOV
502
                            .to_attr_line()
×
503
                            .get_string()
×
504
                            .c_str());
505
                } else {
UNCOV
506
                    top_view->set_needs_update();
×
UNCOV
507
                    lnav_data.ld_filter_help_status_source.fss_error.clear();
×
508
                }
509
            }
UNCOV
510
            break;
×
511
        }
512
    }
513
}
514

515
void
UNCOV
516
filter_sub_source::rl_history(textinput_curses& tc)
×
517
{
UNCOV
518
    switch (tc.tc_text_format) {
×
UNCOV
519
        case text_format_t::TF_PCRE: {
×
UNCOV
520
            std::vector<attr_line_t> poss;
×
UNCOV
521
            this->fss_regexp_history.query_entries(
×
UNCOV
522
                tc.get_content(),
×
UNCOV
523
                [&poss](const auto& e) { poss.emplace_back(e.e_content); });
×
UNCOV
524
            tc.open_popup_for_history(poss);
×
UNCOV
525
            break;
×
526
        }
UNCOV
527
        case text_format_t::TF_SQL: {
×
UNCOV
528
            break;
×
529
        }
530
        default:
×
UNCOV
531
            ensure(false);
×
532
            break;
533
    }
534
}
535

536
void
UNCOV
537
filter_sub_source::rl_completion_request_int(textinput_curses& tc,
×
538
                                             completion_request_type_t crt)
539
{
540
    static const auto FILTER_HELP
541
        = help_text("filter", "filter the view by a pattern")
×
542
              .with_parameter(
×
543
                  help_text("pattern", "The pattern to filter by")
×
UNCOV
544
                      .with_format(help_parameter_format_t::HPF_REGEX));
×
545
    static const auto FILTER_EXPR_HELP
546
        = help_text("filter-expr", "filter the view by a SQL expression")
×
UNCOV
547
              .with_parameter(
×
UNCOV
548
                  help_text("expr", "The expression to evaluate")
×
UNCOV
549
                      .with_format(help_parameter_format_t::HPF_SQL_EXPR));
×
550
    static auto& prompt = lnav::prompt::get();
551

UNCOV
552
    auto* top_view = *lnav_data.ld_view_stack.top();
×
UNCOV
553
    auto& al = tc.tc_lines[tc.tc_cursor.y];
×
UNCOV
554
    auto al_sf = al.to_string_fragment().sub_cell_range(0, tc.tc_cursor.x);
×
UNCOV
555
    std::string prefix;
×
UNCOV
556
    auto is_regex = tc.tc_text_format == text_format_t::TF_PCRE;
×
557
    auto parse_res = lnav::command::parse_for_prompt(
558
        lnav_data.ld_exec_context,
559
        al_sf,
UNCOV
560
        is_regex ? FILTER_HELP : FILTER_EXPR_HELP);
×
561

UNCOV
562
    switch (crt) {
×
563
        case completion_request_type_t::partial: {
×
564
            if (al_sf.endswith(" ")) {
×
565
                if (tc.is_cursor_at_end_of_line()) {
×
566
                    tc.tc_suggestion
UNCOV
567
                        = prompt.get_regex_suggestion(*top_view, al.al_string);
×
568
                }
UNCOV
569
                return;
×
570
            }
UNCOV
571
            break;
×
572
        }
UNCOV
573
        case completion_request_type_t::full:
×
UNCOV
574
            break;
×
575
    }
576

UNCOV
577
    auto byte_x = al_sf.column_to_byte_index(tc.tc_cursor.x);
×
UNCOV
578
    auto arg_res_opt = parse_res.arg_at(byte_x);
×
UNCOV
579
    if (arg_res_opt) {
×
UNCOV
580
        auto arg_pair = arg_res_opt.value();
×
581
        if (crt == completion_request_type_t::full
×
UNCOV
582
            || tc.tc_popup_type != textinput_curses::popup_type_t::none)
×
583
        {
584
            auto poss = prompt.get_cmd_parameter_completion(
585
                *top_view,
586
                &FILTER_HELP,
587
                arg_pair.aar_help,
588
                arg_pair.aar_element.se_value);
×
589
            auto left = arg_pair.aar_element.se_value.empty()
×
590
                ? tc.tc_cursor.x
×
UNCOV
591
                : al_sf.byte_to_column_index(
×
592
                      arg_pair.aar_element.se_origin.sf_begin);
×
593
            tc.open_popup_for_completion(left, poss);
×
594
            tc.tc_popup.set_title(arg_pair.aar_help->ht_name);
×
UNCOV
595
        } else if (arg_pair.aar_element.se_value.empty()
×
596
                   && tc.is_cursor_at_end_of_line())
×
597
        {
598
            tc.tc_suggestion
UNCOV
599
                = prompt.get_regex_suggestion(*top_view, al.al_string);
×
600
        } else {
UNCOV
601
            log_debug("not at end of line");
×
UNCOV
602
            tc.tc_suggestion.clear();
×
603
        }
UNCOV
604
    } else {
×
UNCOV
605
        log_debug("no arg");
×
606
    }
607
}
608

609
void
UNCOV
610
filter_sub_source::rl_completion_request(textinput_curses& tc)
×
611
{
UNCOV
612
    this->rl_completion_request_int(tc, completion_request_type_t::full);
×
613
}
614

615
void
616
filter_sub_source::rl_completion(textinput_curses& tc)
×
617
{
618
    static auto& prompt = lnav::prompt::get();
619

UNCOV
620
    prompt.rl_completion(tc);
×
621
}
622

623
void
624
filter_sub_source::rl_perform(textinput_curses& rc)
×
625
{
626
    static const intern_string_t INPUT_SRC = intern_string::lookup("input");
627

628
    auto* top_view = *lnav_data.ld_view_stack.top();
×
629
    auto* tss = top_view->get_sub_source();
×
630
    auto& fs = tss->get_filters();
×
631
    auto iter = fs.begin() + this->tss_view->get_selection().value();
×
632
    auto tf = *iter;
×
633
    auto new_value = rc.get_content();
×
634

UNCOV
635
    fs.fs_generation += 1;
×
UNCOV
636
    if (new_value.empty()) {
×
UNCOV
637
        this->rl_abort(rc);
×
638
    } else {
UNCOV
639
        switch (tf->get_lang()) {
×
UNCOV
640
            case filter_lang_t::NONE:
×
641
            case filter_lang_t::REGEX: {
642
                auto compile_res
UNCOV
643
                    = lnav::pcre2pp::code::from(new_value, PCRE2_CASELESS);
×
644

UNCOV
645
                if (compile_res.isErr()) {
×
UNCOV
646
                    auto ce = compile_res.unwrapErr();
×
UNCOV
647
                    auto um = lnav::console::to_user_message(INPUT_SRC, ce);
×
648
                    lnav_data.ld_exec_context.ec_msg_callback_stack.back()(um);
×
649
                    this->rl_abort(rc);
×
650
                } else {
×
UNCOV
651
                    auto code_ptr = compile_res.unwrap().to_shared();
×
652
                    tf->lf_deleted = true;
×
653
                    tss->text_filters_changed();
×
654

655
                    auto pf = std::make_shared<pcre_filter>(
UNCOV
656
                        tf->get_type(), new_value, tf->get_index(), code_ptr);
×
657

UNCOV
658
                    this->fss_regexp_history.insert_plain_content(new_value);
×
659

660
                    *iter = pf;
×
661
                    tss->text_filters_changed();
×
662
                }
UNCOV
663
                break;
×
664
            }
665
            case filter_lang_t::SQL: {
×
666
                auto full_sql
667
                    = fmt::format(FMT_STRING("SELECT 1 WHERE {}"), new_value);
×
UNCOV
668
                auto_mem<sqlite3_stmt> stmt(sqlite3_finalize);
×
669
#ifdef SQLITE_PREPARE_PERSISTENT
UNCOV
670
                auto retcode = sqlite3_prepare_v3(lnav_data.ld_db.in(),
×
671
                                                  full_sql.c_str(),
UNCOV
672
                                                  full_sql.size(),
×
673
                                                  SQLITE_PREPARE_PERSISTENT,
674
                                                  stmt.out(),
675
                                                  nullptr);
676
#else
677
                auto retcode = sqlite3_prepare_v2(lnav_data.ld_db.in(),
678
                                                  full_sql.c_str(),
679
                                                  full_sql.size(),
680
                                                  stmt.out(),
681
                                                  nullptr);
682
#endif
683
                if (retcode != SQLITE_OK) {
×
684
                    auto sqlerr = annotate_sql_with_error(
UNCOV
685
                        lnav_data.ld_db.in(), full_sql.c_str(), nullptr);
×
686
                    auto um
UNCOV
687
                        = lnav::console::user_message::error(
×
688
                              "invalid SQL expression")
UNCOV
689
                              .with_reason(sqlite3_errmsg(lnav_data.ld_db.in()))
×
UNCOV
690
                              .with_snippet(lnav::console::snippet::from(
×
691
                                  INPUT_SRC, sqlerr));
×
UNCOV
692
                    lnav_data.ld_exec_context.ec_msg_callback_stack.back()(um);
×
693
                    this->rl_abort(rc);
×
694
                } else {
×
UNCOV
695
                    lnav_data.ld_log_source.set_sql_filter(new_value,
×
696
                                                           stmt.release());
UNCOV
697
                    tss->text_filters_changed();
×
698
                }
UNCOV
699
                break;
×
700
            }
701
        }
702
    }
703

704
    top_view->reload_data();
×
UNCOV
705
    this->tss_view->reload_data();
×
706
}
707

708
void
709
filter_sub_source::rl_blur(textinput_curses& tc)
×
710
{
UNCOV
711
    auto* top_view = *lnav_data.ld_view_stack.top();
×
712
    top_view->get_highlights().erase({highlight_source_t::PREVIEW, "preview"});
×
UNCOV
713
    lnav_data.ld_log_source.set_preview_sql_filter(nullptr);
×
714
    lnav_data.ld_filter_help_status_source.fss_prompt.clear();
×
UNCOV
715
    lnav_data.ld_filter_help_status_source.fss_error.clear();
×
716
    this->fss_editing = false;
×
UNCOV
717
    tc.set_visible(false);
×
UNCOV
718
    this->tss_view->set_enabled(true);
×
719
}
720

721
void
UNCOV
722
filter_sub_source::rl_abort(textinput_curses& rc)
×
723
{
UNCOV
724
    auto* top_view = *lnav_data.ld_view_stack.top();
×
UNCOV
725
    auto* tss = top_view->get_sub_source();
×
UNCOV
726
    auto& fs = tss->get_filters();
×
UNCOV
727
    auto iter = fs.begin() + this->tss_view->get_selection().value();
×
UNCOV
728
    auto tf = *iter;
×
729

UNCOV
730
    top_view->reload_data();
×
UNCOV
731
    fs.delete_filter("");
×
UNCOV
732
    this->tss_view->reload_data();
×
UNCOV
733
    this->tss_view->set_needs_update();
×
UNCOV
734
    tf->set_enabled(this->fss_filter_state);
×
UNCOV
735
    tss->text_filters_changed();
×
UNCOV
736
    this->tss_view->reload_data();
×
737
}
738

739
void
UNCOV
740
filter_sub_source::list_input_handle_scroll_out(listview_curses& lv)
×
741
{
UNCOV
742
    set_view_mode(ln_mode_t::PAGING);
×
UNCOV
743
    lnav_data.ld_filter_view.reload_data();
×
744
}
745

746
bool
UNCOV
747
filter_sub_source::text_handle_mouse(
×
748
    textview_curses& tc,
749
    const listview_curses::display_line_content_t&,
750
    mouse_event& me)
751
{
UNCOV
752
    if (this->fss_editing) {
×
UNCOV
753
        return true;
×
754
    }
UNCOV
755
    auto nci = ncinput{};
×
UNCOV
756
    if (me.is_click_in(mouse_button_t::BUTTON_LEFT, 1, 3)) {
×
UNCOV
757
        nci.id = ' ';
×
UNCOV
758
        nci.eff_text[0] = ' ';
×
UNCOV
759
        this->list_input_handle_key(tc, nci);
×
760
    }
UNCOV
761
    if (me.is_click_in(mouse_button_t::BUTTON_LEFT, 4, 7)) {
×
UNCOV
762
        nci.id = 't';
×
UNCOV
763
        nci.eff_text[0] = 't';
×
UNCOV
764
        this->list_input_handle_key(tc, nci);
×
765
    }
UNCOV
766
    if (me.is_double_click_in(mouse_button_t::BUTTON_LEFT, line_range{25, -1}))
×
767
    {
UNCOV
768
        nci.id = '\r';
×
UNCOV
769
        nci.eff_text[0] = '\r';
×
UNCOV
770
        this->list_input_handle_key(tc, nci);
×
771
    }
UNCOV
772
    return true;
×
773
}
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