Skip to content

Commit 7040e5d

Browse files
committed
add get_line(doc, line_num) utility
As discussed at issue #254
1 parent cbc00d6 commit 7040e5d

File tree

3 files changed

+135
-0
lines changed

3 files changed

+135
-0
lines changed

include/toml++/impl/source_region.hpp

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

223290
#include "header_end.hpp"

tests/user_feedback.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,4 +451,25 @@ 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: { 0, 1, 2 })
458+
{
459+
CHECK(toml::get_line(""sv, line_num) == std::string_view{});
460+
}
461+
462+
for (const auto input : {
463+
"alpha = 1\nbeta = 2\n # last line # "sv,
464+
"alpha = 1\nbeta = 2\n # last line # \n"sv,
465+
"alpha = 1\r\nbeta = 2\r\n # last line # \r\n"sv,
466+
})
467+
{
468+
CHECK(toml::get_line(input, 0) == std::string_view{});
469+
CHECK(toml::get_line(input, 1) == "alpha = 1"sv);
470+
CHECK(toml::get_line(input, 2) == "beta = 2"sv);
471+
CHECK(toml::get_line(input, 3) == " # last line # "sv);
472+
CHECK(toml::get_line(input, 4) == std::string_view{});
473+
}
474+
}
454475
}

toml.hpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2634,7 +2634,54 @@ TOML_NAMESPACE_START
26342634
return lhs;
26352635
}
26362636
};
2637+
2638+
inline std::string_view get_line(std::string_view doc, source_index line_num)
2639+
{
2640+
if (line_num == 0)
2641+
{
2642+
// Invalid line number. Should be greater than zero.
2643+
return {};
2644+
}
2645+
2646+
// The position of the first character of the specified line.
2647+
const auto begin_of_line = [doc, line_num]() -> std::size_t
2648+
{
2649+
if (line_num == 1)
2650+
{
2651+
return 0;
2652+
}
2653+
2654+
const auto num_chars = doc.size();
2655+
std::size_t current_line_num{ 1 };
2656+
2657+
for (std::size_t i{}; i < num_chars; ++i)
2658+
{
2659+
if (doc[i] == '\n')
2660+
{
2661+
++current_line_num;
2662+
2663+
if (current_line_num == line_num)
2664+
{
2665+
return i + 1;
2666+
}
2667+
}
2668+
}
2669+
return std::string_view::npos;
2670+
}();
2671+
2672+
if (begin_of_line >= doc.size())
2673+
{
2674+
return {};
2675+
}
2676+
2677+
const auto end_of_line = doc.find_first_of("\r\n", begin_of_line);
2678+
2679+
return doc.substr(begin_of_line,
2680+
(end_of_line == std::string_view::npos) ? std::string_view::npos
2681+
: (end_of_line - begin_of_line));
2682+
}
26372683
}
2684+
26382685
TOML_NAMESPACE_END;
26392686

26402687
#ifdef _MSC_VER

0 commit comments

Comments
 (0)