• 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

97.5
/src/styling.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 <string>
31

32
#include "styling.hh"
33

34
#include "ansi-palette-json.h"
35
#include "base/from_trait.hh"
36
#include "config.h"
37
#include "css-color-names-json.h"
38
#include "fmt/format.h"
39
#include "xterm-palette-json.h"
40
#include "yajlpp/yajlpp.hh"
41
#include "yajlpp/yajlpp_def.hh"
42

43
static const json_path_container term_color_rgb_handler = {
44
    yajlpp::property_handler("r").for_field(&rgb_color::rc_r),
45
    yajlpp::property_handler("g").for_field(&rgb_color::rc_g),
46
    yajlpp::property_handler("b").for_field(&rgb_color::rc_b),
47
};
48

49
static const json_path_container term_color_handler = {
50
    yajlpp::property_handler("colorId").for_field(&term_color::xc_id),
51
    yajlpp::property_handler("name").for_field(&term_color::xc_name),
52
    yajlpp::property_handler("hexString").for_field(&term_color::xc_hex),
53
    yajlpp::property_handler("rgb")
54
        .for_child(&term_color::xc_color)
55
        .with_children(term_color_rgb_handler),
56
};
57

58
static constexpr json_path_handler_base::enum_value_t _align_values[] = {
59
    {"start"_frag, text_align_t::start},
60
    {"center"_frag, text_align_t::center},
61
    {"end"_frag, text_align_t::end},
62

63
    json_path_handler_base::ENUM_TERMINATOR,
64
};
65

66
const json_path_container style_config_handlers =
67
    json_path_container{
68
        yajlpp::property_handler("text-align")
69
        .with_synopsis("start|center|end")
70
        .with_enum_values(_align_values)
71
        .with_description("How to align text within a cell")
72
        .for_field(&style_config::sc_text_align),
73
        yajlpp::property_handler("color")
74
            .with_synopsis("#hex|color_name")
75
            .with_description(
76
                "The foreground color value for this style. The value can be "
77
                "the name of an xterm color, the hexadecimal value, or a theme "
78
                "variable reference.")
79
            .with_example("#fff")
80
            .with_example("Green")
81
            .with_example("$black")
82
            .for_field(&style_config::sc_color),
83
        yajlpp::property_handler("background-color")
84
            .with_synopsis("#hex|color_name")
85
            .with_description(
86
                "The background color value for this style. The value can be "
87
                "the name of an xterm color, the hexadecimal value, or a theme "
88
                "variable reference.")
89
            .with_example("#2d2a2e")
90
            .with_example("Green")
91
            .for_field(&style_config::sc_background_color),
92
        yajlpp::property_handler("underline")
93
            .with_description("Indicates that the text should be underlined.")
94
            .for_field(&style_config::sc_underline),
95
        yajlpp::property_handler("bold")
96
            .with_description("Indicates that the text should be bolded.")
97
            .for_field(&style_config::sc_bold),
98
        yajlpp::property_handler("italic")
99
            .with_description("Indicates that the text should be italicized.")
100
            .for_field(&style_config::sc_italic),
101
        yajlpp::property_handler("strike")
102
            .with_description("Indicates that the text should be struck.")
103
            .for_field(&style_config::sc_strike),
104
    }
105
.with_definition_id("style");
106

107
static const typed_json_path_container<std::vector<term_color>>
108
    root_color_handler = {
109
        yajlpp::property_handler("#")
110
            .with_obj_provider<term_color, std::vector<term_color>>(
111
                [](const yajlpp_provider_context& ypc,
2,262,312✔
112
                   std::vector<term_color>* palette) {
113
                    if (ypc.ypc_index >= palette->size()) {
2,262,312✔
114
                        palette->resize(ypc.ypc_index + 1);
174,024✔
115
                    }
116
                    return &((*palette)[ypc.ypc_index]);
2,262,312✔
117
                })
118
            .with_children(term_color_handler),
119
};
120

121
struct css_color_names {
122
    std::map<std::string, std::string> ccn_name_to_color;
123
};
124

125
static const typed_json_path_container<css_color_names> css_color_names_handlers
126
    = {
127
        yajlpp::pattern_property_handler("(?<css_color_name>.*)")
128
            .for_field(&css_color_names::ccn_name_to_color),
129
};
130

131
static const css_color_names&
132
get_css_color_names()
7✔
133
{
134
    static const intern_string_t iname
135
        = intern_string::lookup(css_color_names_json.get_name());
19✔
136
    static auto sfp = css_color_names_json.to_string_fragment_producer();
7✔
137
    static const auto INSTANCE
138
        = css_color_names_handlers.parser_for(iname).of(*sfp).unwrap();
7✔
139

140
    return INSTANCE;
7✔
141
}
142

143
term_color_palette*
144
xterm_colors()
1,045,072✔
145
{
146
    static term_color_palette retval(
147
        xterm_palette_json.get_name(),
148
        *xterm_palette_json.to_string_fragment_producer());
1,045,072✔
149

150
    return &retval;
1,045,072✔
151
}
152

153
term_color_palette*
154
ansi_colors()
1,164✔
155
{
156
    static term_color_palette retval(
157
        ansi_palette_json.get_name(),
158
        *ansi_palette_json.to_string_fragment_producer());
1,164✔
159

160
    return &retval;
1,164✔
161
}
162

163
template<>
164
Result<rgb_color, std::string>
165
from(string_fragment sf)
2,022,316✔
166
{
167
    if (sf.empty()) {
2,022,316✔
168
        return Ok(rgb_color());
877,364✔
169
    }
170

171
    if (sf[0] != '#') {
1,144,952✔
172
        const auto& css_colors = get_css_color_names();
7✔
173
        const auto& iter = css_colors.ccn_name_to_color.find(sf.to_string());
7✔
174

175
        if (iter != css_colors.ccn_name_to_color.end()) {
7✔
176
            sf = string_fragment::from_str(iter->second);
4✔
177
        }
178
    }
179

180
    rgb_color rgb_out;
1,144,952✔
181

182
    if (sf[0] == '#') {
1,144,952✔
183
        switch (sf.length()) {
1,144,949✔
184
            case 4:
184,955✔
185
                if (sscanf(sf.data(),
184,955✔
186
                           "#%1hx%1hx%1hx",
187
                           &rgb_out.rc_r,
188
                           &rgb_out.rc_g,
189
                           &rgb_out.rc_b)
190
                    == 3)
184,955✔
191
                {
192
                    rgb_out.rc_r |= rgb_out.rc_r << 4;
184,890✔
193
                    rgb_out.rc_g |= rgb_out.rc_g << 4;
184,890✔
194
                    rgb_out.rc_b |= rgb_out.rc_b << 4;
184,890✔
195
                    return Ok(rgb_out);
184,890✔
196
                }
197
                break;
65✔
198
            case 7:
959,992✔
199
                if (sscanf(sf.data(),
959,992✔
200
                           "#%2hx%2hx%2hx",
201
                           &rgb_out.rc_r,
202
                           &rgb_out.rc_g,
203
                           &rgb_out.rc_b)
204
                    == 3)
959,992✔
205
                {
206
                    return Ok(rgb_out);
959,992✔
207
                }
UNCOV
208
                break;
×
209
        }
210

211
        return Err(fmt::format(FMT_STRING("Could not parse color: {}"), sf));
268✔
212
    }
213

214
    return Err(fmt::format(
6✔
215
        FMT_STRING(
12✔
216
            "Unknown color: '{}'.  "
217
            "See https://jonasjacek.github.io/colors/ for a list of supported "
218
            "color names"),
219
        sf));
3✔
220
}
221

222
term_color_palette::term_color_palette(const char* name,
1,262✔
223
                                       string_fragment_producer& json)
1,262✔
224
{
225
    intern_string_t iname = intern_string::lookup(name);
1,262✔
226
    auto parse_res
227
        = root_color_handler.parser_for(iname).with_ignore_unused(true).of(
2,524✔
228
            json);
1,262✔
229

230
    if (parse_res.isErr()) {
1,262✔
UNCOV
231
        log_error("failed to parse palette: %s -- %s",
×
232
                  name,
233
                  parse_res.unwrapErr()[0].to_attr_line().get_string().c_str());
234
    }
235
    require(parse_res.isOk());
1,262✔
236

237
    this->tc_palette = parse_res.unwrap();
1,262✔
238
    for (auto& xc : this->tc_palette) {
175,286✔
239
        xc.xc_lab_color = lab_color(xc.xc_color);
174,024✔
240
    }
241
}
1,262✔
242

243
uint8_t
244
term_color_palette::match_color(const lab_color& to_match) const
77,931✔
245
{
246
    double lowest = 1000.0;
77,931✔
247
    short lowest_id = -1;
77,931✔
248

249
    for (const auto& xc : this->tc_palette) {
701,379✔
250
        const double xc_delta = xc.xc_lab_color.deltaE(to_match);
623,448✔
251

252
        if (lowest_id == -1) {
623,448✔
253
            lowest = xc_delta;
77,931✔
254
            lowest_id = xc.xc_id;
77,931✔
255
            continue;
77,931✔
256
        }
257

258
        if (xc_delta < lowest) {
545,517✔
259
            lowest = xc_delta;
187,200✔
260
            lowest_id = xc.xc_id;
187,200✔
261
        }
262
    }
263

264
    require_ge(lowest_id, -100);
77,931✔
265

266
    return lowest_id;
77,931✔
267
}
268

269
namespace styling {
270

271
Result<color_unit, std::string>
272
color_unit::from_str(const string_fragment& sf)
2,237,972✔
273
{
274
    if (sf == "semantic()") {
2,237,972✔
275
        return Ok(color_unit{semantic{}});
111,162✔
276
    }
277

278
    if (!sf.startswith("#")) {
2,126,810✔
279
        for (const auto& xc : xterm_colors()->tc_palette) {
226,130,009✔
280
            if (sf.iequal(xc.xc_name)) {
225,267,389✔
281
                return Ok(from_palette(xc.xc_id));
182,426✔
282
            }
283
        }
284
    }
285
    auto retval = TRY(from<rgb_color>(sf));
1,944,384✔
286
    if (retval.empty()) {
1,944,314✔
287
        return Ok(EMPTY);
862,613✔
288
    }
289

290
    return Ok(color_unit{retval});
1,081,701✔
291
}
292

293
}  // namespace styling
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