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

tstack / lnav / 11872214087-1756

16 Nov 2024 06:12PM UTC coverage: 70.243% (+0.5%) from 69.712%
11872214087-1756

push

github

tstack
[build] disable regex101

46266 of 65866 relevant lines covered (70.24%)

467515.63 hits per line

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

45.43
/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 "filter_sub_source.hh"
31

32
#include "base/attr_line.builder.hh"
33
#include "base/enum_util.hh"
34
#include "base/func_util.hh"
35
#include "base/opt_util.hh"
36
#include "bound_tags.hh"
37
#include "config.h"
38
#include "lnav.hh"
39
#include "readline_highlighters.hh"
40
#include "readline_possibilities.hh"
41
#include "sql_util.hh"
42

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

45
filter_sub_source::
636✔
46
filter_sub_source(std::shared_ptr<readline_curses> editor)
636✔
47
    : fss_editor(editor)
636✔
48
{
49
    this->fss_editor->set_x(25);
636✔
50
    this->fss_editor->set_width(-1);
636✔
51
    this->fss_editor->set_save_history(!(lnav_data.ld_flags & LNF_SECURE_MODE));
636✔
52
    this->fss_regex_context.set_highlighter(readline_regex_highlighter)
636✔
53
        .set_append_character(0);
636✔
54
    this->fss_editor->add_context(filter_lang_t::REGEX,
636✔
55
                                  this->fss_regex_context);
636✔
56
    this->fss_sql_context.set_highlighter(readline_sqlite_highlighter)
636✔
57
        .set_append_character(0);
636✔
58
    this->fss_editor->add_context(filter_lang_t::SQL, this->fss_sql_context);
636✔
59
    this->fss_editor->set_change_action(
1,272✔
60
        bind_mem(&filter_sub_source::rl_change, this));
636✔
61
    this->fss_editor->set_perform_action(
1,272✔
62
        bind_mem(&filter_sub_source::rl_perform, this));
636✔
63
    this->fss_editor->set_abort_action(
1,272✔
64
        bind_mem(&filter_sub_source::rl_abort, this));
636✔
65
    this->fss_editor->set_display_match_action(
1,272✔
66
        bind_mem(&filter_sub_source::rl_display_matches, this));
636✔
67
    this->fss_editor->set_display_next_action(
1,272✔
68
        bind_mem(&filter_sub_source::rl_display_next, this));
636✔
69
    this->fss_match_view.set_sub_source(&this->fss_match_source);
636✔
70
    this->fss_match_view.set_height(0_vl);
636✔
71
    this->fss_match_view.set_show_scrollbar(true);
636✔
72
    this->fss_match_view.set_default_role(role_t::VCR_POPUP);
636✔
73
}
636✔
74

75
void
76
filter_sub_source::register_view(textview_curses* tc)
518✔
77
{
78
    text_sub_source::register_view(tc);
518✔
79
    tc->add_child_view(this->fss_editor.get());
518✔
80
    tc->add_child_view(&this->fss_match_view);
518✔
81
}
518✔
82

83
bool
84
filter_sub_source::list_input_handle_key(listview_curses& lv, int ch)
7✔
85
{
86
    if (this->fss_editing) {
7✔
87
        switch (ch) {
4✔
88
            case KEY_ESCAPE:
×
89
            case KEY_CTRL(']'):
90
                this->fss_editor->abort();
×
91
                return true;
×
92
            default:
4✔
93
                this->fss_editor->handle_key(ch);
4✔
94
                return true;
4✔
95
        }
96
    }
97

98
    switch (ch) {
3✔
99
        case 'f': {
×
100
            auto* top_view = *lnav_data.ld_view_stack.top();
×
101
            auto* tss = top_view->get_sub_source();
×
102

103
            tss->toggle_apply_filters();
×
104
            top_view->reload_data();
×
105
            break;
×
106
        }
107
        case ' ': {
×
108
            textview_curses* top_view = *lnav_data.ld_view_stack.top();
×
109
            text_sub_source* tss = top_view->get_sub_source();
×
110
            filter_stack& fs = tss->get_filters();
×
111

112
            if (fs.empty()) {
×
113
                return true;
×
114
            }
115

116
            auto tf = *(fs.begin() + lv.get_selection());
×
117

118
            fs.set_filter_enabled(tf, !tf->is_enabled());
×
119
            tss->text_filters_changed();
×
120
            lv.reload_data();
×
121
            top_view->reload_data();
×
122
            return true;
×
123
        }
124
        case 't': {
×
125
            textview_curses* top_view = *lnav_data.ld_view_stack.top();
×
126
            text_sub_source* tss = top_view->get_sub_source();
×
127
            filter_stack& fs = tss->get_filters();
×
128

129
            if (fs.empty()) {
×
130
                return true;
×
131
            }
132

133
            auto tf = *(fs.begin() + lv.get_selection());
×
134

135
            if (tf->get_type() == text_filter::INCLUDE) {
×
136
                tf->set_type(text_filter::EXCLUDE);
×
137
            } else {
138
                tf->set_type(text_filter::INCLUDE);
×
139
            }
140

141
            tss->text_filters_changed();
×
142
            lv.reload_data();
×
143
            top_view->reload_data();
×
144
            return true;
×
145
        }
146
        case 'D': {
×
147
            textview_curses* top_view = *lnav_data.ld_view_stack.top();
×
148
            text_sub_source* tss = top_view->get_sub_source();
×
149
            filter_stack& fs = tss->get_filters();
×
150

151
            if (fs.empty()) {
×
152
                return true;
×
153
            }
154

155
            auto tf = *(fs.begin() + lv.get_selection());
×
156

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

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

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

181
            this->fss_editing = true;
×
182
            this->tss_view->vc_enabled = false;
×
183

184
            add_view_text_possibilities(this->fss_editor.get(),
×
185
                                        filter_lang_t::REGEX,
186
                                        "*",
187
                                        top_view,
188
                                        text_quoting::regex);
189
            this->fss_editor->set_window(lv.get_window());
×
190
            this->fss_editor->set_visible(true);
×
191
            this->fss_editor->set_y(
×
192
                lv.get_y() + (int) (lv.get_selection() - lv.get_top()));
×
193
            this->fss_editor->window_change();
×
194
            this->fss_editor->focus(filter_lang_t::REGEX, "", "");
×
195
            this->fss_filter_state = true;
×
196
            ef->disable();
×
197
            return true;
×
198
        }
199
        case 'o': {
1✔
200
            auto* top_view = *lnav_data.ld_view_stack.top();
1✔
201
            auto* tss = top_view->get_sub_source();
1✔
202
            auto& fs = tss->get_filters();
1✔
203
            auto filter_index = fs.next_index();
1✔
204

205
            if (!filter_index) {
1✔
206
                lnav_data.ld_filter_help_status_source.fss_error.set_value(
×
207
                    "error: too many filters");
208
                return true;
×
209
            }
210

211
            auto ef = std::make_shared<empty_filter>(
212
                text_filter::type_t::EXCLUDE, *filter_index);
1✔
213
            fs.add_filter(ef);
1✔
214
            lv.set_selection(vis_line_t(fs.size() - 1));
1✔
215
            lv.reload_data();
1✔
216

217
            this->fss_editing = true;
1✔
218
            this->tss_view->vc_enabled = false;
1✔
219

220
            add_view_text_possibilities(this->fss_editor.get(),
1✔
221
                                        filter_lang_t::REGEX,
222
                                        "*",
223
                                        top_view,
224
                                        text_quoting::regex);
225
            this->fss_editor->set_window(lv.get_window());
1✔
226
            this->fss_editor->set_visible(true);
1✔
227
            this->fss_editor->set_y(
1✔
228
                lv.get_y() + (int) (lv.get_selection() - lv.get_top()));
1✔
229
            this->fss_editor->window_change();
1✔
230
            this->fss_editor->focus(filter_lang_t::REGEX, "", "");
1✔
231
            this->fss_filter_state = true;
1✔
232
            ef->disable();
1✔
233
            return true;
1✔
234
        }
1✔
235
        case '\r':
×
236
        case KEY_ENTER: {
237
            textview_curses* top_view = *lnav_data.ld_view_stack.top();
×
238
            text_sub_source* tss = top_view->get_sub_source();
×
239
            filter_stack& fs = tss->get_filters();
×
240

241
            if (fs.empty()) {
×
242
                return true;
×
243
            }
244

245
            auto tf = *(fs.begin() + lv.get_selection());
×
246

247
            this->fss_editing = true;
×
248
            this->tss_view->vc_enabled = false;
×
249

250
            auto tq = tf->get_lang() == filter_lang_t::SQL
×
251
                ? text_quoting::sql
×
252
                : text_quoting::regex;
×
253
            add_view_text_possibilities(
×
254
                this->fss_editor.get(), tf->get_lang(), "*", top_view, tq);
255
            if (top_view == &lnav_data.ld_views[LNV_LOG]) {
×
256
                add_filter_expr_possibilities(
×
257
                    this->fss_editor.get(), filter_lang_t::SQL, "*");
258
            }
259
            this->fss_editor->set_window(lv.get_window());
×
260
            this->fss_editor->set_visible(true);
×
261
            this->fss_editor->set_y(
×
262
                lv.get_y() + (int) (lv.get_selection() - lv.get_top()));
×
263
            this->fss_editor->focus(tf->get_lang(), "");
×
264
            this->fss_editor->rewrite_line(0, tf->get_id().c_str());
×
265
            this->fss_filter_state = tf->is_enabled();
×
266
            tf->disable();
×
267
            tss->text_filters_changed();
×
268
            return true;
×
269
        }
270
        case 'n': {
×
271
            execute_command(lnav_data.ld_exec_context, "next-mark search");
×
272
            return true;
×
273
        }
274
        case 'N': {
×
275
            execute_command(lnav_data.ld_exec_context, "prev-mark search");
×
276
            return true;
×
277
        }
278
        case '/': {
×
279
            execute_command(lnav_data.ld_exec_context, "prompt search-filters");
×
280
            return true;
×
281
        }
282
        default:
2✔
283
            log_debug("unhandled %x", ch);
2✔
284
            break;
2✔
285
    }
286

287
    return false;
2✔
288
}
289

290
size_t
291
filter_sub_source::text_line_count()
5,841✔
292
{
293
    return (lnav_data.ld_view_stack.top() |
5,841✔
294
                [](auto tc) -> std::optional<size_t> {
707✔
295
               text_sub_source* tss = tc->get_sub_source();
707✔
296

297
               if (tss == nullptr) {
707✔
298
                   return std::nullopt;
×
299
               }
300
               auto& fs = tss->get_filters();
707✔
301
               return std::make_optional(fs.size());
707✔
302
           })
5,841✔
303
        .value_or(0);
5,841✔
304
}
305

306
size_t
307
filter_sub_source::text_line_width(textview_curses& curses)
×
308
{
309
    textview_curses* top_view = *lnav_data.ld_view_stack.top();
×
310
    text_sub_source* tss = top_view->get_sub_source();
×
311
    filter_stack& fs = tss->get_filters();
×
312
    size_t retval = 0;
×
313

314
    for (auto& filter : fs) {
×
315
        retval = std::max(filter->get_id().size() + 8, retval);
×
316
    }
317

318
    return retval;
×
319
}
320

321
void
322
filter_sub_source::text_value_for_line(textview_curses& tc,
16✔
323
                                       int line,
324
                                       std::string& value_out,
325
                                       text_sub_source::line_flags_t flags)
326
{
327
    auto* top_view = *lnav_data.ld_view_stack.top();
16✔
328
    auto* tss = top_view->get_sub_source();
16✔
329
    auto& fs = tss->get_filters();
16✔
330
    auto tf = *(fs.begin() + line);
16✔
331
    bool selected
332
        = lnav_data.ld_mode == ln_mode_t::FILTER && line == tc.get_selection();
16✔
333

334
    this->fss_curr_line.clear();
16✔
335
    auto& al = this->fss_curr_line;
16✔
336
    attr_line_builder alb(al);
16✔
337

338
    if (selected) {
16✔
339
        al.append(" ", VC_GRAPHIC.value(ACS_RARROW));
16✔
340
    } else {
341
        al.append(" ");
×
342
    }
343
    al.append(" ");
16✔
344
    if (tf->is_enabled()) {
16✔
345
        al.append("\u25c6"_ok);
3✔
346
    } else {
347
        al.append("\u25c7"_comment);
13✔
348
    }
349
    al.append(" ");
16✔
350
    switch (tf->get_type()) {
16✔
351
        case text_filter::INCLUDE:
×
352
            al.append(" ").append(lnav::roles::ok("IN")).append(" ");
×
353
            break;
×
354
        case text_filter::EXCLUDE:
16✔
355
            if (tf->get_lang() == filter_lang_t::REGEX) {
16✔
356
                al.append(lnav::roles::error("OUT")).append(" ");
16✔
357
            } else {
358
                al.append("    ");
×
359
            }
360
            break;
16✔
361
        default:
×
362
            ensure(0);
×
363
            break;
364
    }
365

366
    {
367
        auto ag = alb.with_attr(VC_ROLE.value(role_t::VCR_NUMBER));
16✔
368
        if (this->fss_editing && line == tc.get_selection()) {
16✔
369
            alb.appendf(FMT_STRING("{:>9}"), "-");
39✔
370
        } else {
13✔
371
            alb.appendf(FMT_STRING("{:>9}"),
13✔
372
                        tss->get_filtered_count_for(tf->get_index()));
373
        }
9✔
374
    }
3✔
375

3✔
376
    al.append(" hits ").append("|", VC_GRAPHIC.value(ACS_VLINE)).append(" ");
3✔
377

378
    attr_line_t content{tf->get_id()};
16✔
379
    switch (tf->get_lang()) {
380
        case filter_lang_t::REGEX:
16✔
381
            readline_regex_highlighter(content, std::nullopt);
382
            break;
32✔
383
        case filter_lang_t::SQL:
16✔
384
            readline_sqlite_highlighter(content, std::nullopt);
16✔
385
            break;
16✔
386
        case filter_lang_t::NONE:
16✔
387
            break;
×
388
    }
×
389
    al.append(content);
×
390

×
391
    if (selected) {
×
392
        al.with_attr_for_all(VC_ROLE.value(role_t::VCR_FOCUSED));
393
    }
16✔
394

395
    value_out = al.get_string();
16✔
396
}
16✔
397

398
void
399
filter_sub_source::text_attrs_for_line(textview_curses& tc,
16✔
400
                                       int line,
16✔
401
                                       string_attrs_t& value_out)
402
{
403
    value_out = this->fss_curr_line.get_attrs();
16✔
404
}
405

406
size_t
407
filter_sub_source::text_size_for_line(textview_curses& tc,
16✔
408
                                      int line,
16✔
409
                                      text_sub_source::line_flags_t raw)
410
{
411
    textview_curses* top_view = *lnav_data.ld_view_stack.top();
×
412
    text_sub_source* tss = top_view->get_sub_source();
413
    filter_stack& fs = tss->get_filters();
414
    auto tf = *(fs.begin() + line);
415

×
416
    return 8 + tf->get_id().size();
×
417
}
×
418

×
419
void
420
filter_sub_source::rl_change(readline_curses* rc)
×
421
{
422
    textview_curses* top_view = *lnav_data.ld_view_stack.top();
423
    text_sub_source* tss = top_view->get_sub_source();
424
    filter_stack& fs = tss->get_filters();
8✔
425
    if (fs.empty()) {
426
        return;
8✔
427
    }
8✔
428

8✔
429
    auto iter = fs.begin() + this->tss_view->get_selection();
8✔
430
    auto tf = *iter;
×
431
    auto new_value = rc->get_line_buffer();
432

433
    switch (tf->get_lang()) {
8✔
434
        case filter_lang_t::NONE:
8✔
435
            break;
8✔
436
        case filter_lang_t::REGEX: {
437
            if (new_value.empty()) {
8✔
438
                auto sugg = top_view->get_current_search();
×
439
                if (top_view->tc_selected_text) {
×
440
                    sugg = top_view->tc_selected_text->sti_value;
8✔
441
                }
8✔
442
                if (fs.get_filter(sugg) == nullptr) {
2✔
443
                    this->fss_editor->set_suggestion(sugg);
2✔
444
                }
×
445
            } else {
446
                auto regex_res
2✔
447
                    = lnav::pcre2pp::code::from(new_value, PCRE2_CASELESS);
×
448

449
                if (regex_res.isErr()) {
2✔
450
                    auto pe = regex_res.unwrapErr();
451
                    lnav_data.ld_filter_help_status_source.fss_error.set_value(
6✔
452
                        "error: %s", pe.get_message().c_str());
453
                } else {
6✔
454
                    auto& hm = top_view->get_highlights();
×
455
                    highlighter hl(regex_res.unwrap().to_shared());
×
456
                    auto role = tf->get_type() == text_filter::EXCLUDE
×
457
                        ? role_t::VCR_DIFF_DELETE
×
458
                        : role_t::VCR_DIFF_ADD;
6✔
459
                    hl.with_role(role);
12✔
460
                    hl.with_attrs(text_attrs{A_BLINK | A_REVERSE});
6✔
461

6✔
462
                    hm[{highlight_source_t::PREVIEW, "preview"}] = hl;
6✔
463
                    top_view->set_needs_update();
6✔
464
                    lnav_data.ld_filter_help_status_source.fss_error.clear();
6✔
465
                }
466
            }
6✔
467
            break;
6✔
468
        }
6✔
469
        case filter_lang_t::SQL: {
6✔
470
            auto full_sql
6✔
471
                = fmt::format(FMT_STRING("SELECT 1 WHERE {}"), new_value);
8✔
472
            auto_mem<sqlite3_stmt> stmt(sqlite3_finalize);
473
#ifdef SQLITE_PREPARE_PERSISTENT
×
474
            auto retcode = sqlite3_prepare_v3(lnav_data.ld_db.in(),
475
                                              full_sql.c_str(),
×
476
                                              full_sql.size(),
×
477
                                              SQLITE_PREPARE_PERSISTENT,
×
478
                                              stmt.out(),
×
479
                                              nullptr);
480
#else
×
481
            auto retcode = sqlite3_prepare_v2(lnav_data.ld_db.in(),
482
                                              full_sql.c_str(),
×
483
                                              full_sql.size(),
484
                                              stmt.out(),
485
                                              nullptr);
486
#endif
487
            if (retcode != SQLITE_OK) {
488
                lnav_data.ld_filter_help_status_source.fss_error.set_value(
489
                    "error: %s", sqlite3_errmsg(lnav_data.ld_db));
490
            } else {
491
                auto set_res = lnav_data.ld_log_source.set_preview_sql_filter(
492
                    stmt.release());
493

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

511
void
512
filter_sub_source::rl_perform(readline_curses* rc)
×
513
{
514
    static const intern_string_t INPUT_SRC = intern_string::lookup("input");
515

8✔
516
    textview_curses* top_view = *lnav_data.ld_view_stack.top();
517
    text_sub_source* tss = top_view->get_sub_source();
518
    filter_stack& fs = tss->get_filters();
1✔
519
    auto iter = fs.begin() + this->tss_view->get_selection();
520
    auto tf = *iter;
1✔
521
    auto new_value = rc->get_value().get_string();
522

1✔
523
    if (new_value.empty()) {
1✔
524
        this->rl_abort(rc);
1✔
525
    } else {
1✔
526
        top_view->get_highlights().erase(
1✔
527
            {highlight_source_t::PREVIEW, "preview"});
1✔
528
        switch (tf->get_lang()) {
529
            case filter_lang_t::NONE:
1✔
530
            case filter_lang_t::REGEX: {
×
531
                auto compile_res
532
                    = lnav::pcre2pp::code::from(new_value, PCRE2_CASELESS);
2✔
533

1✔
534
                if (compile_res.isErr()) {
1✔
535
                    auto ce = compile_res.unwrapErr();
1✔
536
                    auto um = lnav::console::to_user_message(INPUT_SRC, ce);
537
                    lnav_data.ld_exec_context.ec_error_callback_stack.back()(
538
                        um);
1✔
539
                    this->rl_abort(rc);
540
                } else {
1✔
541
                    tf->lf_deleted = true;
×
542
                    tss->text_filters_changed();
×
543

×
544
                    auto pf = std::make_shared<pcre_filter>(
545
                        tf->get_type(),
×
546
                        new_value,
×
547
                        tf->get_index(),
1✔
548
                        compile_res.unwrap().to_shared());
1✔
549

550
                    *iter = pf;
551
                    tss->text_filters_changed();
1✔
552
                }
553
                break;
2✔
554
            }
3✔
555
            case filter_lang_t::SQL: {
556
                auto full_sql
1✔
557
                    = fmt::format(FMT_STRING("SELECT 1 WHERE {}"), new_value);
1✔
558
                auto_mem<sqlite3_stmt> stmt(sqlite3_finalize);
1✔
559
#ifdef SQLITE_PREPARE_PERSISTENT
1✔
560
                auto retcode = sqlite3_prepare_v3(lnav_data.ld_db.in(),
1✔
561
                                                  full_sql.c_str(),
×
562
                                                  full_sql.size(),
563
                                                  SQLITE_PREPARE_PERSISTENT,
×
564
                                                  stmt.out(),
×
565
                                                  nullptr);
×
566
#else
×
567
                auto retcode = sqlite3_prepare_v2(lnav_data.ld_db.in(),
568
                                                  full_sql.c_str(),
×
569
                                                  full_sql.size(),
570
                                                  stmt.out(),
×
571
                                                  nullptr);
572
#endif
573
                if (retcode != SQLITE_OK) {
574
                    auto sqlerr = annotate_sql_with_error(
575
                        lnav_data.ld_db.in(), full_sql.c_str(), nullptr);
576
                    auto um
577
                        = lnav::console::user_message::error(
578
                              "invalid SQL expression")
579
                              .with_reason(sqlite3_errmsg(lnav_data.ld_db.in()))
580
                              .with_snippet(lnav::console::snippet::from(
581
                                  INPUT_SRC, sqlerr));
×
582
                    lnav_data.ld_exec_context.ec_error_callback_stack.back()(
583
                        um);
×
584
                    this->rl_abort(rc);
585
                } else {
×
586
                    lnav_data.ld_log_source.set_sql_filter(new_value,
587
                                                           stmt.release());
×
588
                    tss->text_filters_changed();
×
589
                }
×
590
                break;
×
591
            }
592
        }
×
593
    }
×
594

×
595
    lnav_data.ld_log_source.set_preview_sql_filter(nullptr);
596
    lnav_data.ld_filter_help_status_source.fss_prompt.clear();
×
597
    this->fss_editing = false;
598
    this->tss_view->vc_enabled = true;
×
599
    this->fss_editor->set_visible(false);
600
    top_view->reload_data();
601
    this->tss_view->reload_data();
602
}
603

1✔
604
void
1✔
605
filter_sub_source::rl_abort(readline_curses* rc)
1✔
606
{
1✔
607
    textview_curses* top_view = *lnav_data.ld_view_stack.top();
1✔
608
    text_sub_source* tss = top_view->get_sub_source();
1✔
609
    filter_stack& fs = tss->get_filters();
1✔
610
    auto iter = fs.begin() + this->tss_view->get_selection();
1✔
611
    auto tf = *iter;
612

613
    lnav_data.ld_log_source.set_preview_sql_filter(nullptr);
×
614
    lnav_data.ld_filter_help_status_source.fss_prompt.clear();
615
    lnav_data.ld_filter_help_status_source.fss_error.clear();
×
616
    top_view->get_highlights().erase({highlight_source_t::PREVIEW, "preview"});
×
617
    top_view->reload_data();
×
618
    fs.delete_filter("");
×
619
    this->tss_view->reload_data();
×
620
    this->fss_editor->set_visible(false);
621
    this->fss_editing = false;
×
622
    this->tss_view->vc_enabled = true;
×
623
    this->tss_view->set_needs_update();
×
624
    tf->set_enabled(this->fss_filter_state);
×
625
    tss->text_filters_changed();
×
626
    this->tss_view->reload_data();
×
627
}
×
628

×
629
void
×
630
filter_sub_source::rl_display_matches(readline_curses* rc)
×
631
{
×
632
    const std::vector<std::string>& matches = rc->get_matches();
×
633
    unsigned long width = 0;
×
634

×
635
    if (matches.empty()) {
636
        this->fss_match_source.clear();
637
        this->fss_match_view.set_height(0_vl);
638
        this->tss_view->set_needs_update();
5✔
639
    } else {
640
        auto current_match = rc->get_match_string();
5✔
641
        attr_line_t al;
5✔
642
        vis_line_t line, selected_line;
643

5✔
644
        for (const auto& match : matches) {
5✔
645
            if (match == current_match) {
5✔
646
                al.append(match, VC_STYLE.value(text_attrs{A_REVERSE}));
5✔
647
                selected_line = line;
648
            } else {
×
649
                al.append(match);
×
650
            }
×
651
            al.append(1, '\n');
652
            width = std::max(width, (unsigned long) match.size());
×
653
            line += 1_vl;
×
654
        }
×
655

×
656
        this->fss_match_view.set_selection(selected_line);
657
        this->fss_match_source.replace_with(al);
×
658
        this->fss_match_view.set_height(
659
            std::min(vis_line_t(matches.size()), 3_vl));
×
660
    }
×
661

×
662
    this->fss_match_view.set_window(this->tss_view->get_window());
663
    this->fss_match_view.set_y(rc->get_y() + 1);
664
    this->fss_match_view.set_x(rc->get_x() + rc->get_match_start());
×
665
    this->fss_match_view.set_width(width + 3);
×
666
    this->fss_match_view.set_needs_update();
×
667
    this->fss_match_view.reload_data();
×
668
}
669

670
void
5✔
671
filter_sub_source::rl_display_next(readline_curses* rc)
5✔
672
{
5✔
673
    textview_curses& tc = this->fss_match_view;
5✔
674

5✔
675
    if (tc.get_top() >= (tc.get_top_for_last_row() - 1)) {
5✔
676
        tc.set_top(0_vl);
5✔
677
    } else {
678
        tc.shift_top(tc.get_height());
679
    }
×
680
}
681

×
682
void
683
filter_sub_source::list_input_handle_scroll_out(listview_curses& lv)
×
684
{
×
685
    set_view_mode(ln_mode_t::PAGING);
686
    lnav_data.ld_filter_view.reload_data();
×
687
}
688

689
bool
690
filter_sub_source::text_handle_mouse(
691
    textview_curses& tc,
×
692
    const listview_curses::display_line_content_t&,
693
    mouse_event& me)
×
694
{
×
695
    if (this->fss_editing) {
696
        return true;
697
    }
698
    if (me.is_click_in(mouse_button_t::BUTTON_LEFT, 1, 3)) {
×
699
        this->list_input_handle_key(tc, ' ');
700
    }
701
    if (me.is_click_in(mouse_button_t::BUTTON_LEFT, 4, 7)) {
702
        this->list_input_handle_key(tc, 't');
703
    }
×
704
    if (me.is_double_click_in(mouse_button_t::BUTTON_LEFT, line_range{25, -1}))
×
705
    {
706
        this->list_input_handle_key(tc, '\r');
×
707
    }
×
708
    return true;
709
}
×
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