11#include "gsl/pointers"
21constexpr std::size_t base_prefix_width = 3;
31compute_window_bounds(std::size_t min_line, std::size_t max_line, std::size_t window_size, std::size_t total_lines)
33 const std::size_t half_window = (window_size - 1) / 2;
34 const std::size_t
start = (min_line >= half_window) ? min_line - half_window : 0;
35 const std::size_t
end = std::min(max_line + half_window + 1, total_lines);
40std::size_t compute_prefix_width(std::size_t line_num_display, std::size_t
max_digits)
42 const std::size_t current_digits = std::to_string(line_num_display).length();
43 return base_prefix_width + (
max_digits - current_digits);
46std::string format_prefix(
bool is_highlighted, std::size_t prefix_width,
const TextFormatter *format)
49 const std::string arrow_with_spaces =
"➞" + std::string(prefix_width - 1,
' ');
50 return format->format(
"{}", FormatParam{arrow_with_spaces, highlight_style});
52 return std::string(prefix_width,
' ');
55std::string format_plain_line(
const std::string &prefix, std::size_t line_num,
const std::string &content)
57 return prefix + std::to_string(line_num) +
": " + content;
60std::string format_highlighted_line(
61 const std::string &prefix, std::size_t line_num,
const std::string &content,
const TextFormatter *format)
63 const auto styled_content = format->format(
"{}", FormatParam{content, highlight_style});
64 return prefix + std::to_string(line_num) +
": " + styled_content;
67std::vector<std::string> read_file_lines(
const std::filesystem::path &file)
70 panic(
"file does not exist: " + file.string());
73 std::vector<std::string> lines{};
74 std::ifstream stream{file};
75 std::string line_buf{};
76 while (std::getline(stream, line_buf)) {
78 lines.push_back(line_buf);
88 const std::vector<std::string> &lines,
89 const std::vector<std::size_t> &line_indices_to_highlight,
90 std::size_t window_size)
const
92 std::vector<std::string> result;
99 std::set<std::size_t> highlight_set;
100 std::size_t min_line = std::numeric_limits<std::size_t>::max();
101 std::size_t max_line = 0;
103 for (
const auto line_index : line_indices_to_highlight) {
104 if (line_index >= lines.size()) {
105 panic(
"invalid line index " + std::to_string(line_index) +
": index out of bounds");
107 highlight_set.insert(line_index);
108 min_line = std::min(min_line, line_index);
109 max_line = std::max(max_line, line_index);
112 if (highlight_set.empty()) {
116 const auto bounds = compute_window_bounds(min_line, max_line, window_size, lines.size());
118 for (std::size_t i = bounds.start; i < bounds.end; ++i) {
119 const bool is_highlighted = highlight_set.contains(i);
120 const std::size_t line_num = i + 1;
121 const std::size_t prefix_width = compute_prefix_width(line_num, bounds.max_digits);
122 const std::string prefix = format_prefix(is_highlighted, prefix_width, format_);
124 if (is_highlighted) {
125 result.push_back(format_highlighted_line(prefix, line_num, lines[i], format_));
128 result.push_back(format_plain_line(prefix, line_num, lines[i]));
136 const std::vector<std::string> &lines,
137 std::size_t line_index_to_highlight,
138 std::size_t col_to_highlight,
139 std::size_t window_size)
const
141 std::vector<std::string> result;
147 if (line_index_to_highlight >= lines.size()) {
148 panic(
"invalid line index " + std::to_string(line_index_to_highlight) +
": index out of bounds");
151 const std::string &target_line = lines[line_index_to_highlight];
152 if (col_to_highlight >= target_line.size()) {
154 "invalid column index " + std::to_string(col_to_highlight) +
": index out of bounds for line " +
155 std::to_string(line_index_to_highlight));
159 compute_window_bounds(line_index_to_highlight, line_index_to_highlight, window_size, lines.size());
161 for (std::size_t i = bounds.start; i < bounds.end; ++i) {
162 const bool is_highlighted = (i == line_index_to_highlight);
163 const std::size_t line_num = i + 1;
164 const std::size_t prefix_width = compute_prefix_width(line_num, bounds.max_digits);
165 const std::string prefix = format_prefix(is_highlighted, prefix_width, format_);
167 if (is_highlighted) {
169 const std::string before = target_line.substr(0, col_to_highlight);
170 const std::string at_col = target_line.substr(col_to_highlight, 1);
171 const std::string after =
172 (col_to_highlight + 1 < target_line.size()) ? target_line.substr(col_to_highlight + 1) :
"";
176 const auto formatted_before = format_->
format(
"{}",
FormatParam{before, highlight_style});
178 const auto formatted_after = format_->
format(
"{}",
FormatParam{after, highlight_style});
181 prefix + std::to_string(line_num) +
": " + formatted_before + formatted_at + formatted_after);
184 const std::size_t indent = prefix_width + std::to_string(line_num).length() + 4 + col_to_highlight;
185 const std::string
caret =
187 result.push_back(
caret);
190 result.push_back(format_plain_line(prefix, line_num, lines[i]));
198 const std::filesystem::path &file,
199 const std::vector<std::size_t> &line_indices_to_highlight,
200 std::size_t window_size)
const
202 const auto lines = read_file_lines(file);
203 return print(lines, line_indices_to_highlight, window_size);
207 const std::filesystem::path &file,
208 std::size_t line_index_to_highlight,
209 std::size_t col_to_highlight,
210 std::size_t window_size)
const
212 const auto lines = read_file_lines(file);
213 return print(lines, line_index_to_highlight, col_to_highlight, window_size);
std::vector< std::string > print(const std::vector< std::string > &lines, const std::vector< std::size_t > &line_indices_to_highlight, std::size_t window_size=9) const
Prints lines with specified lines highlighted and line numbers shown.
FileHighlightPrinter(gsl::not_null< const TextFormatter * > format)
Constructs a FileHighlightPrinter with a text formatter for styling.
Text styling class supporting formatting, foreground colors, and background colors.
static const Style underline
Underline text formatting.
static const Style italic
Italic text formatting.
static const Style yellow
Yellow foreground color.
static const Style bold
Bold text formatting.
static const Style green
Green foreground color.
virtual std::string format(const std::string &format_str, const std::vector< FormatParam > ¶ms) const
Formats a string with styled parameters using fmtlib syntax.
void panic(const StringViewSourceLoc &s)
Unconditionally terminates the program with a panic message.
std::string & trim_line_ending(std::string &line)
Removes line ending characters from a string in-place.
Utility functions for string manipulation and formatting.