Skip to content

Commit f3f6258

Browse files
authored
add get_line(doc, line_num) utility (#255)
1 parent cbc00d6 commit f3f6258

File tree

3 files changed

+158
-0
lines changed

3 files changed

+158
-0
lines changed

include/toml++/impl/source_region.hpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,78 @@ TOML_NAMESPACE_START
217217
return lhs;
218218
}
219219
};
220+
221+
/// \brief Returns the line at the specified line number, from the specified document.
222+
///
223+
/// \detail \cpp
224+
/// auto doc = "alpha = 1\nbeta = 2"sv;
225+
/// auto second_line = toml::get_line(doc, 2);
226+
///
227+
/// std::cout << "The second line says \"" << second_line << "\"\n";
228+
/// \ecpp
229+
///
230+
/// \out
231+
/// The second line says "beta = 2"
232+
/// \eout
233+
///
234+
/// \param doc The document.
235+
/// \param line_num The line number (1-based).
236+
///
237+
/// \returns The specified line, excluding any possible trailing carriage return or line feed character.
238+
/// \remarks Returns an empty string_view when there is no line at the specified line number, in the specified document.
239+
TOML_NODISCARD
240+
inline std::string_view get_line(std::string_view doc, source_index line_num) noexcept
241+
{
242+
if (line_num == 0)
243+
{
244+
// Invalid line number. Should be greater than zero.
245+
return {};
246+
}
247+
248+
// The position of the first character of the specified line.
249+
const auto begin_of_line = [doc, line_num]() -> std::size_t
250+
{
251+
if (line_num == 1)
252+
{
253+
return 0;
254+
}
255+
256+
const auto num_chars_of_doc = doc.size();
257+
std::size_t current_line_num{ 1 };
258+
259+
for (std::size_t i{}; i < num_chars_of_doc; ++i)
260+
{
261+
if (doc[i] == '\n')
262+
{
263+
++current_line_num;
264+
265+
if (current_line_num == line_num)
266+
{
267+
return i + 1;
268+
}
269+
}
270+
}
271+
return std::string_view::npos;
272+
}();
273+
274+
if (begin_of_line >= doc.size())
275+
{
276+
return {};
277+
}
278+
279+
if (const auto end_of_line = doc.find('\n', begin_of_line); end_of_line != std::string_view::npos)
280+
{
281+
const auto num_chars_of_line = end_of_line - begin_of_line;
282+
283+
// Trim an optional trailing carriage return.
284+
return doc.substr(begin_of_line,
285+
((num_chars_of_line > 0) && (doc[end_of_line - 1] == '\r')) ? num_chars_of_line - 1
286+
: num_chars_of_line);
287+
}
288+
289+
// Return the last line. Apparently this doc has no trailing line break character at the end.
290+
return doc.substr(begin_of_line);
291+
}
220292
}
221293
TOML_NAMESPACE_END;
222294

tests/user_feedback.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,4 +451,36 @@ b = []
451451
oss << toml::source_region{ { 1, 2 }, { 3, 4 }, nullptr };
452452
CHECK(oss.str() == "line 1, column 2 to line 3, column 4");
453453
}
454+
455+
SECTION("tomlplusplus/issues/254") // https://github.com/marzer/tomlplusplus/issues/254
456+
{
457+
for (const toml::source_index line_num: { 0u, 1u, 2u })
458+
{
459+
CHECK(toml::get_line(""sv, line_num) == std::string_view{});
460+
}
461+
462+
for (const auto input : {
463+
"# \r (embedded carriage return)"sv,
464+
"# \r (embedded carriage return)\n"sv,
465+
"# \r (embedded carriage return)\r\n"sv,
466+
})
467+
{
468+
CHECK(toml::get_line(input, 0) == std::string_view{});
469+
CHECK(toml::get_line(input, 1) == "# \r (embedded carriage return)"sv);
470+
CHECK(toml::get_line(input, 2) == std::string_view{});
471+
}
472+
473+
for (const auto input : {
474+
"alpha = 1\nbeta = 2\n # last line # "sv,
475+
"alpha = 1\nbeta = 2\n # last line # \n"sv,
476+
"alpha = 1\r\nbeta = 2\r\n # last line # \r\n"sv,
477+
})
478+
{
479+
CHECK(toml::get_line(input, 0) == std::string_view{});
480+
CHECK(toml::get_line(input, 1) == "alpha = 1"sv);
481+
CHECK(toml::get_line(input, 2) == "beta = 2"sv);
482+
CHECK(toml::get_line(input, 3) == " # last line # "sv);
483+
CHECK(toml::get_line(input, 4) == std::string_view{});
484+
}
485+
}
454486
}

toml.hpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2634,6 +2634,60 @@ TOML_NAMESPACE_START
26342634
return lhs;
26352635
}
26362636
};
2637+
2638+
TOML_NODISCARD
2639+
inline std::string_view get_line(std::string_view doc, source_index line_num) noexcept
2640+
{
2641+
if (line_num == 0)
2642+
{
2643+
// Invalid line number. Should be greater than zero.
2644+
return {};
2645+
}
2646+
2647+
// The position of the first character of the specified line.
2648+
const auto begin_of_line = [doc, line_num]() -> std::size_t
2649+
{
2650+
if (line_num == 1)
2651+
{
2652+
return 0;
2653+
}
2654+
2655+
const auto num_chars_of_doc = doc.size();
2656+
std::size_t current_line_num{ 1 };
2657+
2658+
for (std::size_t i{}; i < num_chars_of_doc; ++i)
2659+
{
2660+
if (doc[i] == '\n')
2661+
{
2662+
++current_line_num;
2663+
2664+
if (current_line_num == line_num)
2665+
{
2666+
return i + 1;
2667+
}
2668+
}
2669+
}
2670+
return std::string_view::npos;
2671+
}();
2672+
2673+
if (begin_of_line >= doc.size())
2674+
{
2675+
return {};
2676+
}
2677+
2678+
if (const auto end_of_line = doc.find('\n', begin_of_line); end_of_line != std::string_view::npos)
2679+
{
2680+
const auto num_chars_of_line = end_of_line - begin_of_line;
2681+
2682+
// Trim an optional trailing carriage return.
2683+
return doc.substr(begin_of_line,
2684+
((num_chars_of_line > 0) && (doc[end_of_line - 1] == '\r')) ? num_chars_of_line - 1
2685+
: num_chars_of_line);
2686+
}
2687+
2688+
// Return the last line. Apparently this doc has no trailing line break character at the end.
2689+
return doc.substr(begin_of_line);
2690+
}
26372691
}
26382692
TOML_NAMESPACE_END;
26392693

0 commit comments

Comments
 (0)