• 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

25.19
/src/bottom_status_source.cc
1
/**
2
 * Copyright (c) 2019, 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 "bottom_status_source.hh"
31

32
#include "base/snippet_highlighters.hh"
33
#include "config.h"
34

35
bottom_status_source::bottom_status_source()
7,637✔
36
{
37
    this->bss_fields[BSF_LINE_NUMBER].set_min_width(10);
1,091✔
38
    this->bss_fields[BSF_LINE_NUMBER].set_share(1000);
1,091✔
39
    this->bss_fields[BSF_PERCENT].set_width(6);
1,091✔
40
    this->bss_fields[BSF_PERCENT].set_left_pad(1);
1,091✔
41
    this->bss_fields[BSF_HITS].set_min_width(10);
1,091✔
42
    this->bss_fields[BSF_HITS].set_share(5);
1,091✔
43
    this->bss_fields[BSF_SEARCH_TERM].set_min_width(10);
1,091✔
44
    this->bss_fields[BSF_SEARCH_TERM].set_share(1);
1,091✔
45
    this->bss_fields[BSF_LOADING].set_width(13);
1,091✔
46
    this->bss_fields[BSF_LOADING].right_justify(true);
1,091✔
47
    this->bss_fields[BSF_HELP].set_width(14);
1,091✔
48
    this->bss_fields[BSF_HELP].set_value("?:View Help"_frag);
1,091✔
49
    this->bss_fields[BSF_HELP].right_justify(true);
1,091✔
50
    this->bss_prompt.set_left_pad(1);
1,091✔
51
    this->bss_prompt.set_min_width(35);
1,091✔
52
    this->bss_prompt.set_share(1);
1,091✔
53
    this->bss_error.set_left_pad(1);
1,091✔
54
    this->bss_error.set_min_width(35);
1,091✔
55
    this->bss_error.set_share(1);
1,091✔
56
    this->bss_line_error.set_left_pad(1);
1,091✔
57
    this->bss_line_error.set_min_width(35);
1,091✔
58
    this->bss_line_error.set_share(1);
1,091✔
59
}
1,091✔
60

61
void
UNCOV
62
bottom_status_source::update_line_number(listview_curses* lc)
×
63
{
UNCOV
64
    auto& sf = this->bss_fields[BSF_LINE_NUMBER];
×
UNCOV
65
    auto sel = lc->get_selection();
×
66

UNCOV
67
    if (lc->get_inner_height() == 0) {
×
UNCOV
68
        sf.set_value(" L0"_frag);
×
UNCOV
69
    } else if (sel) {
×
UNCOV
70
        sf.set_value(" L%'d", (int) sel.value());
×
71
    } else {
UNCOV
72
        sf.set_value(" L-"_frag);
×
73
    }
74

UNCOV
75
    this->bss_line_error.set_value(
×
UNCOV
76
        lc->map_top_row([](const attr_line_t& top_row)
×
77
                            -> std::optional<std::string> {
78
              const auto& sa = top_row.get_attrs();
×
UNCOV
79
              auto error_wrapper = get_string_attr(sa, SA_ERROR);
×
UNCOV
80
              if (error_wrapper) {
×
UNCOV
81
                  return error_wrapper.value().get();
×
82
              }
UNCOV
83
              return std::nullopt;
×
UNCOV
84
          }).value_or(""));
×
85
}
86

87
void
UNCOV
88
bottom_status_source::update_search_term(textview_curses& tc)
×
89
{
UNCOV
90
    auto& sf = this->bss_fields[BSF_SEARCH_TERM];
×
UNCOV
91
    auto search_term = tc.get_current_search();
×
92

UNCOV
93
    sf.clear();
×
94
    if (!search_term.empty()) {
×
95
        auto search_term_al = attr_line_t(search_term);
×
96

UNCOV
97
        lnav::snippets::regex_highlighter(
×
UNCOV
98
            search_term_al, -1, line_range{0, (int) search_term_al.length()});
×
UNCOV
99
        sf.get_value().append_quoted(search_term_al);
×
100
    }
101

UNCOV
102
    this->bss_paused = tc.is_paused();
×
UNCOV
103
    this->update_loading(0, 0);
×
104
}
105

106
void
UNCOV
107
bottom_status_source::update_percent(listview_curses* lc)
×
108
{
UNCOV
109
    status_field& sf = this->bss_fields[BSF_PERCENT];
×
UNCOV
110
    vis_line_t top = lc->get_top();
×
UNCOV
111
    vis_line_t bottom, height;
×
112
    unsigned long width;
113
    double percent;
114

UNCOV
115
    lc->get_dimensions(height, width);
×
116

UNCOV
117
    if (lc->get_inner_height() > 0) {
×
UNCOV
118
        bottom = std::min(top + height - 1_vl, lc->get_inner_height() - 1_vl);
×
UNCOV
119
        percent = (double) (bottom + 1);
×
UNCOV
120
        percent /= (double) lc->get_inner_height();
×
UNCOV
121
        percent *= 100.0;
×
122
    } else {
UNCOV
123
        percent = 0.0;
×
124
    }
UNCOV
125
    sf.set_value("%3d%% ", (int) percent);
×
126
}
127

128
bool
UNCOV
129
bottom_status_source::update_marks(listview_curses* lc)
×
130
{
UNCOV
131
    auto* tc = static_cast<textview_curses*>(lc);
×
UNCOV
132
    auto& bm = tc->get_bookmarks();
×
UNCOV
133
    status_field& sf = this->bss_fields[BSF_HITS];
×
UNCOV
134
    auto retval = false;
×
135

UNCOV
136
    const auto& bv = bm[&textview_curses::BM_SEARCH];
×
137

UNCOV
138
    if (!bv.empty() || !tc->get_current_search().empty()) {
×
UNCOV
139
        auto vl = tc->get_selection();
×
UNCOV
140
        if (vl) {
×
UNCOV
141
            auto lb = bv.bv_tree.find(vl.value());
×
UNCOV
142
            if (lb != bv.bv_tree.end()) {
×
UNCOV
143
                retval = sf.set_value("  Hit %'d of %'d for ",
×
UNCOV
144
                                      (lb - bv.bv_tree.begin()) + 1,
×
145
                                      tc->get_match_count());
146
            } else {
UNCOV
147
                retval = sf.set_value("  %'d hits for ", tc->get_match_count());
×
148
            }
149
        }
150
    } else {
UNCOV
151
        retval = sf.clear();
×
152
    }
UNCOV
153
    return retval;
×
154
}
155

156
bool
UNCOV
157
bottom_status_source::update_hits(textview_curses* tc)
×
158
{
UNCOV
159
    auto& sf = this->bss_fields[BSF_HITS];
×
UNCOV
160
    bool retval = false;
×
161
    role_t new_role;
162

UNCOV
163
    if (tc->is_searching()) {
×
UNCOV
164
        this->bss_hit_spinner += 1;
×
UNCOV
165
        if (this->bss_hit_spinner % 2) {
×
UNCOV
166
            new_role = role_t::VCR_ACTIVE_STATUS;
×
167
        } else {
UNCOV
168
            new_role = role_t::VCR_ACTIVE_STATUS2;
×
169
        }
UNCOV
170
        if (!sf.is_cylon()) {
×
UNCOV
171
            sf.set_cylon(true);
×
UNCOV
172
            retval = true;
×
173
        }
174
    } else {
UNCOV
175
        new_role = role_t::VCR_STATUS;
×
UNCOV
176
        if (sf.is_cylon()) {
×
UNCOV
177
            sf.set_cylon(false);
×
UNCOV
178
            sf.clear();  // clear cylon style attribute
×
UNCOV
179
            retval = true;
×
180
        }
181
    }
182
    // this->bss_error.clear();
UNCOV
183
    sf.set_role(new_role);
×
UNCOV
184
    retval = this->update_marks(tc) || retval;
×
UNCOV
185
    return retval;
×
186
}
187

188
void
189
bottom_status_source::update_loading(file_off_t off,
125✔
190
                                     file_ssize_t total,
191
                                     const char* term)
192
{
193
    auto& sf = this->bss_fields[BSF_LOADING];
125✔
194

195
    require_ge(off, 0);
125✔
196
    require_ge(total, off);
125✔
197

198
    if (total == 0) {
125✔
199
        sf.set_cylon(false);
125✔
200
        sf.set_role(role_t::VCR_STATUS);
125✔
201
        if (this->bss_paused) {
125✔
202
            sf.set_value("\xE2\x80\x96 Paused"_frag);
×
203
        } else {
204
            sf.clear();
125✔
205
        }
206
    } else if (off == total) {
×
207
        static const std::vector<std::string> DOTS = {
208
            "   ",
209
            ".  ",
210
            ".. ",
211
            "...",
212
            ".. ",
213
            ".  ",
214
        };
215

UNCOV
216
        this->bss_load_percent += 1;
×
UNCOV
217
        sf.set_cylon(true);
×
UNCOV
218
        sf.set_role(role_t::VCR_ACTIVE_STATUS2);
×
UNCOV
219
        sf.set_value(" Working%s  ",
×
UNCOV
220
                     DOTS[this->bss_load_percent % DOTS.size()].c_str());
×
221
    } else {
UNCOV
222
        int pct = (int) (((double) off / (double) total) * 100.0);
×
223

UNCOV
224
        if (this->bss_load_percent != pct) {
×
UNCOV
225
            this->bss_load_percent = pct;
×
226

UNCOV
227
            sf.set_cylon(true);
×
UNCOV
228
            sf.set_role(role_t::VCR_ACTIVE_STATUS2);
×
UNCOV
229
            sf.set_value(" %s %2d%% ", term, pct);
×
230
        }
231
    }
232
}
125✔
233

234
size_t
UNCOV
235
bottom_status_source::statusview_fields()
×
236
{
237
    size_t retval;
238

UNCOV
239
    if (this->bss_prompt.empty() && this->bss_error.empty()
×
240
        && this->bss_line_error.empty())
×
241
    {
UNCOV
242
        retval = BSF__MAX;
×
243
    } else {
UNCOV
244
        retval = 1;
×
245
    }
246

UNCOV
247
    return retval;
×
248
}
249

250
status_field&
UNCOV
251
bottom_status_source::statusview_value_for_field(int field)
×
252
{
UNCOV
253
    if (!this->bss_error.empty()) {
×
UNCOV
254
        return this->bss_error;
×
255
    }
UNCOV
256
    if (!this->bss_prompt.empty()) {
×
UNCOV
257
        return this->bss_prompt;
×
258
    }
UNCOV
259
    if (!this->bss_line_error.empty()) {
×
UNCOV
260
        return this->bss_line_error;
×
261
    }
UNCOV
262
    return this->get_field((field_t) field);
×
263
}
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