• 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

73.89
/src/textview_curses.hh
1
/**
2
 * Copyright (c) 2007-2012, 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
 * @file textview_curses.hh
30
 */
31

32
#ifndef textview_curses_hh
33
#define textview_curses_hh
34

35
#include <memory>
36
#include <utility>
37
#include <vector>
38

39
#include "base/func_util.hh"
40
#include "base/lnav_log.hh"
41
#include "bookmarks.hh"
42
#include "breadcrumb.hh"
43
#include "grep_proc.hh"
44
#include "hasher.hh"
45
#include "highlighter.hh"
46
#include "listview_curses.hh"
47
#include "lnav_config_fwd.hh"
48
#include "log_accel.hh"
49
#include "logfile_fwd.hh"
50
#include "ring_span.hh"
51
#include "text_format.hh"
52
#include "textview_curses_fwd.hh"
53
#include "vis_line.hh"
54

55
class textview_curses;
56

57
using vis_bookmarks_t = bookmarks<vis_line_t>;
58
using vis_bookmarks = bookmarks<vis_line_t>::type;
59

60
class logfile_filter_state {
61
public:
62
    logfile_filter_state(std::shared_ptr<logfile> lf = nullptr);
63

64
    void clear();
65

66
    void clear_filter_state(size_t index);
67

68
    void clear_deleted_filter_state(uint32_t used_mask);
69

70
    void resize(size_t newsize);
71

72
    void reserve(size_t expected);
73

74
    std::optional<size_t> content_line_to_vis_line(uint32_t line);
75

76
    const static int MAX_FILTERS = 32;
77

78
    std::shared_ptr<logfile> tfs_logfile;
79
    size_t tfs_filter_count[MAX_FILTERS];
80
    int tfs_filter_hits[MAX_FILTERS];
81
    bool tfs_message_matched[MAX_FILTERS];
82
    size_t tfs_lines_for_message[MAX_FILTERS];
83
    bool tfs_last_message_matched[MAX_FILTERS];
84
    size_t tfs_last_lines_for_message[MAX_FILTERS];
85
    std::vector<uint32_t> tfs_mask;
86
    std::vector<uint32_t> tfs_index;
87
};
88

89
enum class filter_lang_t : int {
90
    NONE,
91
    REGEX,
92
    SQL,
93
};
94

95
class text_filter {
96
public:
97
    typedef enum {
98
        INCLUDE,
99
        EXCLUDE,
100
    } type_t;
101

102
    text_filter(type_t type, filter_lang_t lang, std::string id, size_t index)
97✔
103
        : lf_type(type), lf_lang(lang), lf_id(std::move(id)), lf_index(index)
97✔
104
    {
105
    }
97✔
106
    virtual ~text_filter() = default;
97✔
107

108
    type_t get_type() const { return this->lf_type; }
116✔
109
    filter_lang_t get_lang() const { return this->lf_lang; }
17✔
UNCOV
110
    void set_type(type_t t) { this->lf_type = t; }
×
111
    std::string get_id() const { return this->lf_id; }
31✔
112
    void set_id(std::string id) { this->lf_id = std::move(id); }
113
    size_t get_index() const { return this->lf_index; }
1,853✔
114

115
    bool is_enabled() const { return this->lf_enabled; }
428✔
116
    void enable() { this->lf_enabled = true; }
1✔
117
    void disable() { this->lf_enabled = false; }
51✔
UNCOV
118
    void set_enabled(bool value) { this->lf_enabled = value; }
×
119

120
    void revert_to_last(logfile_filter_state& lfs, size_t rollback_size);
121

122
    bool add_line(logfile_filter_state& lfs,
123
                  logfile_const_iterator ll,
124
                  const shared_buffer_ref& line);
125

126
    void end_of_message(logfile_filter_state& lfs);
127

128
    struct line_source {
129
        const logfile& ls_file;
130
        logfile_const_iterator ls_line;
131
    };
132

133
    virtual bool matches(std::optional<line_source> ls,
134
                         const shared_buffer_ref& line)
135
        = 0;
136

137
    virtual std::string to_command() const = 0;
138

139
    bool operator==(const std::string& rhs) const { return this->lf_id == rhs; }
140

141
    bool lf_deleted{false};
142

143
protected:
144
    bool lf_enabled{true};
145
    type_t lf_type;
146
    filter_lang_t lf_lang;
147
    std::string lf_id;
148
    size_t lf_index;
149
};
150

151
class empty_filter : public text_filter {
152
public:
UNCOV
153
    empty_filter(type_t type, size_t index)
×
UNCOV
154
        : text_filter(type, filter_lang_t::REGEX, "", index)
×
155
    {
156
    }
157

158
    bool matches(std::optional<line_source> ls,
159
                 const shared_buffer_ref& line) override;
160

161
    std::string to_command() const override;
162
};
163

164
class pcre_filter : public text_filter {
165
public:
166
    pcre_filter(type_t type,
92✔
167
                const std::string& id,
168
                size_t index,
169
                std::shared_ptr<lnav::pcre2pp::code> code)
170
        : text_filter(type, filter_lang_t::REGEX, id, index),
92✔
171
          pf_pcre(std::move(code))
92✔
172
    {
173
    }
92✔
174

175
    ~pcre_filter() override = default;
92✔
176

177
    bool matches(std::optional<line_source> ls,
1,514✔
178
                 const shared_buffer_ref& line) override
179
    {
180
        return this->pf_pcre->find_in(line.to_string_fragment())
3,028✔
181
            .ignore_error()
3,028✔
182
            .has_value();
3,028✔
183
    }
184

185
    std::string to_command() const override
68✔
186
    {
187
        return (this->lf_type == text_filter::INCLUDE ? "filter-in "
68✔
188
                                                      : "filter-out ")
189
            + this->lf_id;
68✔
190
    }
191

192
protected:
193
    std::shared_ptr<lnav::pcre2pp::code> pf_pcre;
194
};
195

196
class filter_stack {
197
public:
198
    using iterator = std::vector<std::shared_ptr<text_filter>>::iterator;
199
    using const_iterator
200
        = std::vector<std::shared_ptr<text_filter>>::const_iterator;
201
    using value_type = std::shared_ptr<text_filter>;
202

203
    explicit filter_stack(size_t reserved = 0) : fs_reserved(reserved) {}
13,078✔
204

205
    iterator begin() { return this->fs_filters.begin(); }
9,478✔
206

207
    iterator end() { return this->fs_filters.end(); }
9,692✔
208

209
    const_iterator begin() const { return this->fs_filters.begin(); }
1✔
210

211
    const_iterator end() const { return this->fs_filters.end(); }
1✔
212

213
    size_t size() const { return this->fs_filters.size(); }
133✔
214

215
    bool empty() const { return this->fs_filters.empty(); };
16,500✔
216

217
    bool full() const
31✔
218
    {
219
        return (this->fs_reserved + this->fs_filters.size())
31✔
220
            == logfile_filter_state::MAX_FILTERS;
31✔
221
    }
222

223
    std::optional<size_t> next_index();
224

225
    void add_filter(const std::shared_ptr<text_filter>& filter);
226

227
    void clear_filters()
42✔
228
    {
229
        while (!this->fs_filters.empty()) {
44✔
230
            this->fs_filters.pop_back();
2✔
231
        }
232
        this->fs_generation += 1;
42✔
233
    }
42✔
234

235
    void set_filter_enabled(const std::shared_ptr<text_filter>& filter,
3✔
236
                            bool enabled)
237
    {
238
        if (enabled) {
3✔
239
            filter->enable();
1✔
240
        } else {
241
            filter->disable();
2✔
242
        }
243
        this->fs_generation += 1;
3✔
244
    }
3✔
245

246
    std::shared_ptr<text_filter> get_filter(const std::string& id);
247

248
    bool delete_filter(const std::string& id);
249

250
    void get_mask(uint32_t& filter_mask);
251

252
    void get_enabled_mask(uint32_t& filter_in_mask, uint32_t& filter_out_mask);
253

254
    uint32_t fs_generation{0};
255

256
private:
257
    const size_t fs_reserved;
258
    std::vector<std::shared_ptr<text_filter>> fs_filters;
259
};
260

261
class text_time_translator {
262
public:
263
    struct row_info {
264
        row_info() = default;
265

266
        row_info(timeval tv, int64_t id) : ri_time(tv), ri_id(id) {}
1,587✔
267

268
        timeval ri_time{0, 0};
269
        int64_t ri_id{-1};
270
    };
271

272
    virtual ~text_time_translator() = default;
6,059✔
273

274
    virtual std::optional<vis_line_t> row_for_time(timeval time_bucket) = 0;
275

276
    virtual std::optional<vis_line_t> row_for(const row_info& ri)
152✔
277
    {
278
        return this->row_for_time(ri.ri_time);
152✔
279
    }
280

281
    virtual std::optional<row_info> time_for_row(vis_line_t row) = 0;
282

283
    void data_reloaded(textview_curses* tc);
284

285
    void ttt_scroll_invoked(textview_curses* tc);
286

287
    std::optional<timeval> get_min_row_time() const
145✔
288
    {
289
        if (this->ttt_min_row_time == min_time_init) {
145✔
290
            return std::nullopt;
140✔
291
        }
292

293
        return this->ttt_min_row_time;
5✔
294
    }
295

296
    void set_min_row_time(const timeval& tv)
8✔
297
    {
298
        if (this->ttt_min_row_time != tv) {
8✔
299
            this->ttt_min_row_time = tv;
8✔
300
            this->ttt_time_filter_generation += 1;
8✔
301
        }
302
    }
8✔
303

304
    std::optional<timeval> get_max_row_time() const
145✔
305
    {
306
        if (this->ttt_max_row_time == max_time_init) {
145✔
307
            return std::nullopt;
141✔
308
        }
309

310
        return this->ttt_max_row_time;
4✔
311
    }
312

313
    void set_max_row_time(const timeval& tv)
7✔
314
    {
315
        if (this->ttt_max_row_time != tv) {
7✔
316
            this->ttt_max_row_time = tv;
7✔
317
            this->ttt_time_filter_generation += 1;
7✔
318
        }
319
    }
7✔
320

321
    void clear_min_max_row_times()
1,128✔
322
    {
323
        if (this->ttt_min_row_time != min_time_init
1,128✔
324
            || this->ttt_max_row_time != max_time_init)
1,128✔
325
        {
326
            this->ttt_min_row_time = min_time_init;
1✔
327
            this->ttt_max_row_time = max_time_init;
1✔
328
            this->ttt_time_filter_generation += 1;
1✔
329
        }
330
    }
1,128✔
331

332
protected:
333
    static constexpr auto min_time_init = timeval{0, 0};
334
    static constexpr auto max_time_init
335
        = timeval{std::numeric_limits<time_t>::max(), 0};
336

337
    timeval ttt_min_row_time = min_time_init;
338
    timeval ttt_max_row_time = max_time_init;
339
    uint32_t ttt_time_filter_generation{0};
340
    std::optional<row_info> ttt_top_row_info;
341
};
342

343
class text_accel_source {
344
public:
345
    virtual ~text_accel_source() = default;
1,386✔
346

347
    virtual log_accel::direction_t get_line_accel_direction(vis_line_t vl);
348

349
    void toggle_time_offset()
350
    {
351
        this->tas_display_time_offset = !this->tas_display_time_offset;
352
        this->text_accel_display_changed();
353
    }
354

355
    void set_time_offset(bool enabled)
491✔
356
    {
357
        if (this->tas_display_time_offset != enabled) {
491✔
358
            this->tas_display_time_offset = enabled;
2✔
359
            this->text_accel_display_changed();
2✔
360
        }
361
    }
491✔
362

363
    bool is_time_offset_enabled() const
31✔
364
    {
365
        return this->tas_display_time_offset;
31✔
366
    }
367

UNCOV
368
    virtual bool is_time_offset_supported() const { return true; }
×
369

370
    virtual logline* text_accel_get_line(vis_line_t vl) = 0;
371

372
    std::string get_time_offset_for_line(textview_curses& tc, vis_line_t vl);
373

374
protected:
375
    virtual void text_accel_display_changed() {}
1✔
376

377
    bool tas_display_time_offset{false};
378
};
379

380
class text_anchors {
381
public:
382
    virtual ~text_anchors() = default;
7,695✔
383

384
    static std::string to_anchor_string(const std::string& raw);
385

386
    virtual std::optional<vis_line_t> row_for_anchor(const std::string& id) = 0;
387

388
    enum class direction {
389
        prev,
390
        next,
391
    };
392

UNCOV
393
    virtual std::optional<vis_line_t> adjacent_anchor(vis_line_t vl,
×
394
                                                      direction dir)
395
    {
396
        return std::nullopt;
×
397
    }
398

399
    virtual std::optional<std::string> anchor_for_row(vis_line_t vl) = 0;
400

401
    virtual std::unordered_set<std::string> get_anchors() = 0;
402
};
403

404
class location_history {
405
public:
406
    virtual ~location_history() = default;
7,695✔
407

408
    virtual void loc_history_append(vis_line_t top) = 0;
409

410
    virtual std::optional<vis_line_t> loc_history_back(vis_line_t current_top)
411
        = 0;
412

413
    virtual std::optional<vis_line_t> loc_history_forward(
414
        vis_line_t current_top)
415
        = 0;
416

417
    const static int MAX_SIZE = 100;
418

419
protected:
420
    size_t lh_history_position{0};
421
};
422

423
/**
424
 * Source for the text to be shown in a textview_curses view.
425
 */
426
class text_sub_source {
427
public:
428
    virtual ~text_sub_source() = default;
13,061✔
429

430
    enum {
431
        RB_RAW,
432
        RB_FULL,
433
        RB_REWRITE,
434
    };
435

436
    enum {
437
        RF_RAW = (1UL << RB_RAW),
438
        RF_FULL = (1UL << RB_FULL),
439
        RF_REWRITE = (1UL << RB_REWRITE),
440
    };
441

442
    typedef long line_flags_t;
443

444
    text_sub_source(size_t reserved_filters = 0) : tss_filters(reserved_filters)
13,078✔
445
    {
446
    }
13,078✔
447

448
    virtual void register_view(textview_curses* tc) { this->tss_view = tc; }
7,616✔
449

450
    virtual bool empty() const = 0;
451

452
    /**
453
     * @return The total number of lines available from the source.
454
     */
455
    virtual size_t text_line_count() = 0;
456

457
    virtual size_t text_line_width(textview_curses& curses) { return INT_MAX; }
×
458

459
    virtual bool text_is_row_selectable(textview_curses& tc, vis_line_t row)
×
460
    {
UNCOV
461
        return true;
×
462
    }
463

464
    virtual void text_selection_changed(textview_curses& tc) {}
88✔
465

466
    /**
467
     * Get the value for a line.
468
     *
469
     * @param tc The textview_curses object that is delegating control.
470
     * @param line The line number to retrieve.
471
     * @param value_out The string object that should be set to the line
472
     *   contents.
473
     * @param raw Indicates that the raw contents of the line should be returned
474
     *   without any post processing.
475
     */
476
    virtual line_info text_value_for_line(textview_curses& tc,
477
                                          int line,
478
                                          std::string& value_out,
479
                                          line_flags_t flags = 0)
480
        = 0;
481

482
    virtual size_t text_size_for_line(textview_curses& tc,
483
                                      int line,
484
                                      line_flags_t raw = 0)
485
        = 0;
486

487
    /**
488
     * Inform the source that the given line has been marked/unmarked.  This
489
     * callback function can be used to translate between between visible line
490
     * numbers and content line numbers.  For example, when viewing a log file
491
     * with filters being applied, we want the bookmarked lines to be stable
492
     * across changes in the filters.
493
     *
494
     * @param bm    The type of bookmark.
495
     * @param line  The line that has been marked/unmarked.
496
     * @param added True if the line was bookmarked and false if it was
497
     *   unmarked.
498
     */
499
    virtual void text_mark(const bookmark_type_t* bm,
×
500
                           vis_line_t line,
501
                           bool added)
502
    {
503
    }
504

505
    /**
506
     * Clear the bookmarks for a particular type in the text source.
507
     *
508
     * @param bm The type of bookmarks to clear.
509
     */
510
    virtual void text_clear_marks(const bookmark_type_t* bm) {}
74✔
511

512
    /**
513
     * Get the attributes for a line of text.
514
     *
515
     * @param tc The textview_curses object that is delegating control.
516
     * @param line The line number to retrieve.
517
     * @param value_out A string_attrs_t object that should be updated with the
518
     *   attributes for the line.
519
     */
UNCOV
520
    virtual void text_attrs_for_line(textview_curses& tc,
×
521
                                     int line,
522
                                     string_attrs_t& value_out)
523
    {
524
    }
525

526
    /**
527
     * Update the bookmarks used by the text view based on the bookmarks
528
     * maintained by the text source.
529
     *
530
     * @param bm The bookmarks data structure used by the text view.
531
     */
532
    virtual void text_update_marks(vis_bookmarks& bm) {}
17,719✔
533

534
    filter_stack& get_filters() { return this->tss_filters; }
3,290✔
535

536
    virtual void text_filters_changed() {}
24✔
537

538
    virtual int get_filtered_count() const { return 0; }
831✔
539

UNCOV
540
    virtual int get_filtered_count_for(size_t filter_index) const { return 0; }
×
541

542
    virtual void update_filter_hash_state(hasher& h) const;
543

544
    virtual text_format_t get_text_format() const
4,037✔
545
    {
546
        return text_format_t::TF_UNKNOWN;
4,037✔
547
    }
548

549
    virtual std::optional<
550
        std::pair<grep_proc_source<vis_line_t>*, grep_proc_sink<vis_line_t>*>>
UNCOV
551
    get_grepper()
×
552
    {
UNCOV
553
        return std::nullopt;
×
554
    }
555

556
    virtual std::optional<location_history*> get_location_history()
1✔
557
    {
558
        return std::nullopt;
1✔
559
    }
560

561
    void toggle_apply_filters();
562

563
    virtual void text_crumbs_for_line(int line,
564
                                      std::vector<breadcrumb::crumb>& crumbs);
565

UNCOV
566
    virtual void quiesce() {}
×
567

568
    virtual void scroll_invoked(textview_curses* tc);
569

570
    bool tss_supports_filtering{false};
571
    bool tss_apply_filters{true};
572

573
protected:
574
    textview_curses* tss_view{nullptr};
575
    filter_stack tss_filters;
576
};
577

578
class vis_location_history : public location_history {
579
public:
580
    vis_location_history()
7,019✔
581
        : vlh_history(std::begin(this->vlh_backing),
14,038✔
582
                      std::end(this->vlh_backing))
7,019✔
583
    {
584
    }
7,019✔
585

586
    void loc_history_append(vis_line_t top) override;
587

588
    std::optional<vis_line_t> loc_history_back(vis_line_t current_top) override;
589

590
    std::optional<vis_line_t> loc_history_forward(
591
        vis_line_t current_top) override;
592

593
    nonstd::ring_span<vis_line_t> vlh_history;
594

595
private:
UNCOV
596
    vis_line_t current_position()
×
597
    {
UNCOV
598
        auto iter = this->vlh_history.rbegin();
×
599

UNCOV
600
        iter += this->lh_history_position;
×
601

602
        return *iter;
×
603
    }
604

605
    vis_line_t vlh_backing[MAX_SIZE];
606
};
607

608
class text_delegate {
609
public:
610
    virtual ~text_delegate() = default;
5,394✔
611

UNCOV
612
    virtual bool text_handle_mouse(
×
613
        textview_curses& tc,
614
        const listview_curses::display_line_content_t&,
615
        mouse_event& me)
616
    {
UNCOV
617
        return false;
×
618
    }
619
};
620

621
class text_detail_provider {
622
public:
623
    virtual ~text_detail_provider() = default;
3,515✔
624

625
    virtual std::optional<json_string> text_row_details(
626
        const textview_curses& tc)
627
        = 0;
628
};
629

630
/**
631
 * The textview_curses class adds user bookmarks and searching to the standard
632
 * list view interface.
633
 */
634
class textview_curses
635
    : public listview_curses
636
    , public list_data_source
637
    , public grep_proc_source<vis_line_t>
638
    , public grep_proc_sink<vis_line_t>
639
    , public lnav_config_listener {
640
public:
641
    using action = std::function<void(textview_curses*)>;
642

643
    const static bookmark_type_t BM_ERRORS;
644
    const static bookmark_type_t BM_WARNINGS;
645
    const static bookmark_type_t BM_USER;
646
    const static bookmark_type_t BM_USER_EXPR;
647
    const static bookmark_type_t BM_SEARCH;
648
    const static bookmark_type_t BM_META;
649
    const static bookmark_type_t BM_PARTITION;
650

651
    textview_curses();
652

653
    ~textview_curses();
654

655
    void reload_config(error_reporter& reporter);
656

657
    void set_paused(bool paused)
17✔
658
    {
659
        this->tc_paused = paused;
17✔
660
        if (this->tc_state_event_handler) {
17✔
UNCOV
661
            this->tc_state_event_handler(*this);
×
662
        }
663
    }
17✔
664

665
    bool is_paused() const { return this->tc_paused; }
6,731✔
666

667
    vis_bookmarks& get_bookmarks() { return this->tc_bookmarks; }
15,680✔
668

669
    const vis_bookmarks& get_bookmarks() const { return this->tc_bookmarks; }
670

671
    void toggle_user_mark(const bookmark_type_t* bm,
672
                          vis_line_t start_line,
673
                          vis_line_t end_line = vis_line_t(-1));
674

675
    void set_user_mark(const bookmark_type_t* bm, vis_line_t vl, bool marked);
676

677
    textview_curses& set_sub_source(text_sub_source* src);
678

679
    text_sub_source* get_sub_source() const { return this->tc_sub_source; }
9,947✔
680

681
    textview_curses& set_supports_marks(bool m)
×
682
    {
UNCOV
683
        this->tc_supports_marks = m;
×
UNCOV
684
        return *this;
×
685
    }
686

687
    textview_curses& set_delegate(std::shared_ptr<text_delegate> del)
28✔
688
    {
689
        this->tc_delegate = del;
28✔
690

691
        return *this;
28✔
692
    }
693

694
    std::shared_ptr<text_delegate> get_delegate() const
695
    {
696
        return this->tc_delegate;
697
    }
698

699
    std::optional<std::pair<int, int>> horiz_shift(vis_line_t start,
700
                                                   vis_line_t end,
701
                                                   int off_start);
702

UNCOV
703
    void set_search_action(action sa)
×
704
    {
705
        this->tc_search_action = std::move(sa);
×
706
    }
707

708
    void grep_end_batch(grep_proc<vis_line_t>& gp);
709
    void grep_end(grep_proc<vis_line_t>& gp);
710

711
    size_t listview_rows(const listview_curses& lv)
115,187✔
712
    {
713
        return this->tc_sub_source == nullptr
115,187✔
714
            ? 0
115,187✔
715
            : this->tc_sub_source->text_line_count();
115,187✔
716
    }
717

718
    size_t listview_width(const listview_curses& lv)
50,254✔
719
    {
720
        return this->tc_sub_source == nullptr
50,254✔
721
            ? 0
50,254✔
722
            : this->tc_sub_source->text_line_width(*this);
50,254✔
723
    }
724

725
    void listview_value_for_rows(const listview_curses& lv,
726
                                 vis_line_t line,
727
                                 std::vector<attr_line_t>& rows_out);
728

729
    void textview_value_for_row(vis_line_t line, attr_line_t& value_out);
730

731
    bool listview_is_row_selectable(const listview_curses& lv, vis_line_t row);
732

733
    void listview_selection_changed(const listview_curses& lv);
734

UNCOV
735
    size_t listview_size_for_row(const listview_curses& lv, vis_line_t row)
×
736
    {
UNCOV
737
        return this->tc_sub_source->text_size_for_line(*this, row);
×
738
    }
739

740
    std::optional<line_info> grep_value_for_line(vis_line_t line,
741
                                                 std::string& value_out);
742

743
    void grep_quiesce()
28✔
744
    {
745
        if (this->tc_sub_source != nullptr) {
28✔
746
            this->tc_sub_source->quiesce();
28✔
747
        }
748
    }
28✔
749

750
    void grep_begin(grep_proc<vis_line_t>& gp,
751
                    vis_line_t start,
752
                    vis_line_t stop);
753
    void grep_match(grep_proc<vis_line_t>& gp, vis_line_t line);
754

UNCOV
755
    bool is_searching() const { return this->tc_searching > 0; }
×
756

757
    void set_follow_search_for(int64_t ms_to_deadline,
×
758
                               std::function<bool()> func)
759
    {
760
        struct timeval now, tv;
761

UNCOV
762
        tv.tv_sec = ms_to_deadline / 1000;
×
UNCOV
763
        tv.tv_usec = (ms_to_deadline % 1000) * 1000;
×
UNCOV
764
        gettimeofday(&now, nullptr);
×
UNCOV
765
        timeradd(&now, &tv, &this->tc_follow_deadline);
×
UNCOV
766
        this->tc_follow_selection = this->get_selection().value_or(-1_vl);
×
UNCOV
767
        this->tc_follow_func = func;
×
768
    }
769

UNCOV
770
    size_t get_match_count() { return this->tc_bookmarks[&BM_SEARCH].size(); }
×
771

772
    void match_reset()
36✔
773
    {
774
        this->tc_bookmarks[&BM_SEARCH].clear();
36✔
775
        if (this->tc_sub_source != nullptr) {
36✔
776
            this->tc_sub_source->text_clear_marks(&BM_SEARCH);
36✔
777
        }
778
    }
36✔
779

780
    highlight_map_t& get_highlights() { return this->tc_highlights; }
768✔
781

782
    const highlight_map_t& get_highlights() const
174✔
783
    {
784
        return this->tc_highlights;
174✔
785
    }
786

UNCOV
787
    std::set<highlight_source_t>& get_disabled_highlights()
×
788
    {
789
        return this->tc_disabled_highlights;
×
790
    }
791

792
    bool handle_mouse(mouse_event& me);
793

794
    void reload_data();
795

796
    bool toggle_hide_fields()
797
    {
798
        bool retval = this->tc_hide_fields;
799

800
        this->tc_hide_fields = !this->tc_hide_fields;
801

802
        return retval;
803
    }
804

805
    bool get_hide_fields() const { return this->tc_hide_fields; }
74✔
806

807
    void set_hide_fields(bool val)
7✔
808
    {
809
        if (this->tc_hide_fields != val) {
7✔
UNCOV
810
            this->tc_hide_fields = val;
×
UNCOV
811
            this->set_needs_update();
×
812
        }
813
    }
7✔
814

815
    void execute_search(const std::string& regex_orig);
816

817
    void redo_search();
818

819
    void search_range(vis_line_t start, vis_line_t stop = -1_vl)
198✔
820
    {
821
        if (this->tc_search_child) {
198✔
822
            this->tc_search_child->get_grep_proc()->queue_request(start, stop);
11✔
823
        }
824
        if (this->tc_source_search_child) {
198✔
825
            this->tc_source_search_child->queue_request(start, stop);
11✔
826
        }
827
    }
198✔
828

829
    void search_new_data(vis_line_t start = -1_vl)
148✔
830
    {
831
        this->search_range(start);
148✔
832
        if (this->tc_search_child) {
148✔
833
            this->tc_search_child->get_grep_proc()->start();
6✔
834
        }
835
        if (this->tc_source_search_child) {
148✔
836
            this->tc_source_search_child->start();
6✔
837
        }
838
    }
148✔
839

840
    std::string get_current_search() const { return this->tc_current_search; }
5,434✔
841

UNCOV
842
    void save_current_search()
×
843
    {
UNCOV
844
        this->tc_previous_search = this->tc_current_search;
×
845
    }
846

847
    std::string get_input_suggestion() const
×
848
    {
849
        std::string retval;
×
UNCOV
850
        if (this->tc_selected_text) {
×
UNCOV
851
            retval = this->tc_selected_text->sti_value;
×
852
        } else {
853
            retval = this->tc_current_search;
×
854
        }
855

UNCOV
856
        if (this->tc_sub_source != nullptr) {
×
UNCOV
857
            if (this->tc_sub_source->get_filters().get_filter(retval)
×
UNCOV
858
                != nullptr)
×
859
            {
UNCOV
860
                retval.clear();
×
861
            }
862
        }
863

UNCOV
864
        return retval;
×
UNCOV
865
    }
×
866

UNCOV
867
    void revert_search() { this->execute_search(this->tc_previous_search); }
×
868

869
    void invoke_scroll();
870

871
    textview_curses& set_reload_config_delegate(
3,474✔
872
        std::function<void(textview_curses&)> func)
873
    {
874
        this->tc_reload_config_delegate = std::move(func);
3,474✔
875
        if (this->tc_reload_config_delegate) {
3,474✔
876
            this->tc_reload_config_delegate(*this);
3,474✔
877
        }
878
        return *this;
3,474✔
879
    }
880

UNCOV
881
    std::optional<std::chrono::milliseconds> consume_search_duration()
×
882
    {
UNCOV
883
        return std::exchange(this->tc_search_duration, std::nullopt);
×
884
    }
885

886
    void apply_highlights(attr_line_t& al,
887
                          const line_range& body,
888
                          const line_range& orig_line);
889

890
    bool tc_interactive{false};
891
    std::function<void(textview_curses&)> tc_state_event_handler;
892

893
    std::optional<role_t> tc_cursor_role;
894
    std::optional<role_t> tc_disabled_cursor_role;
895

896
    struct selected_text_info {
897
        int sti_x;
898
        int64_t sti_line;
899
        line_range sti_range;
900
        string_attrs_t sti_attrs;
901
        std::string sti_value;
902
        std::string sti_href;
903
    };
904

905
    std::optional<selected_text_info> tc_selected_text;
906
    bool tc_text_selection_active{false};
907
    display_line_content_t tc_press_line;
908
    std::optional<vis_line_t> tc_selection_at_press;
909
    int tc_press_left{0};
910
    std::function<void(textview_curses&, const attr_line_t&, int x)>
911
        tc_on_click;
912

913
protected:
914
    class grep_highlighter {
915
    public:
916
        grep_highlighter(std::shared_ptr<grep_proc<vis_line_t>>& gp,
9✔
917
                         highlight_source_t source,
918
                         std::string hl_name,
919
                         highlight_map_t& hl_map)
920
            : gh_grep_proc(std::move(gp)), gh_hl_source(source),
9✔
921
              gh_hl_name(std::move(hl_name)), gh_hl_map(hl_map)
9✔
922
        {
923
        }
9✔
924

925
        ~grep_highlighter()
9✔
926
        {
927
            this->gh_hl_map.erase(
9✔
928
                this->gh_hl_map.find({this->gh_hl_source, this->gh_hl_name}));
9✔
929
        }
9✔
930

931
        grep_proc<vis_line_t>* get_grep_proc()
29✔
932
        {
933
            return this->gh_grep_proc.get();
29✔
934
        }
935

936
    private:
937
        std::shared_ptr<grep_proc<vis_line_t>> gh_grep_proc;
938
        highlight_source_t gh_hl_source;
939
        std::string gh_hl_name;
940
        highlight_map_t& gh_hl_map;
941
    };
942

943
    text_sub_source* tc_sub_source{nullptr};
944
    std::shared_ptr<text_delegate> tc_delegate;
945

946
    vis_bookmarks tc_bookmarks{vis_bookmarks_t::create_array()};
947

948
    int tc_searching{0};
949
    struct timeval tc_follow_deadline{0, 0};
950
    vis_line_t tc_follow_selection{-1_vl};
951
    std::function<bool()> tc_follow_func;
952
    action tc_search_action;
953

954
    highlight_map_t tc_highlights;
955
    std::set<highlight_source_t> tc_disabled_highlights;
956

957
    std::optional<vis_line_t> tc_selection_start;
958
    mouse_event tc_press_event;
959
    bool tc_hide_fields{true};
960
    bool tc_paused{false};
961
    bool tc_supports_marks{false};
962

963
    std::string tc_current_search;
964
    std::string tc_previous_search;
965
    std::shared_ptr<grep_highlighter> tc_search_child;
966
    std::shared_ptr<grep_proc<vis_line_t>> tc_source_search_child;
967
    std::optional<std::chrono::steady_clock::time_point> tc_search_start_time;
968
    std::optional<std::chrono::milliseconds> tc_search_duration;
969
    std::function<void(textview_curses&)> tc_reload_config_delegate;
970
};
971

972
#endif
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