mirror of
https://github.com/ToruNiina/toml11.git
synced 2024-11-27 23:00:08 +00:00
Merge pull request #59 from ToruNiina/preserve-comments
Preserve comments; related to #48
This commit is contained in:
commit
90f84000ba
48
README.md
48
README.md
@ -63,6 +63,7 @@ int main()
|
||||
- [Conversion between toml value and arbitrary types](#conversion-between-toml-value-and-arbitrary-types)
|
||||
- [Invalid UTF-8 Codepoints](#invalid-utf-8-codepoints)
|
||||
- [Formatting user-defined error messages](#formatting-user-defined-error-messages)
|
||||
- [Getting comments related to a value](#getting-comments)
|
||||
- [Serializing TOML data](#serializing-toml-data)
|
||||
- [Underlying types](#underlying-types)
|
||||
- [Running Tests](#running-tests)
|
||||
@ -831,6 +832,53 @@ you will get an error message like this.
|
||||
| ~~ maximum number here
|
||||
```
|
||||
|
||||
## Getting comments
|
||||
|
||||
Since toml11 keeps a file data until all the values are destructed, you can
|
||||
also extract a comment related to a value by calling `toml::value::comment()`.
|
||||
|
||||
If there is a comment just after a value (within the same line), you can get
|
||||
the specific comment by `toml::value::comment_inline()`.
|
||||
|
||||
If there are comments just before a value (without any newline between them),
|
||||
you can get the comments by `toml::value::comment_before()`.
|
||||
|
||||
`toml::value::comment()` returns the results of both functions after
|
||||
concatenating them.
|
||||
|
||||
```toml
|
||||
a = 42 # comment for a.
|
||||
|
||||
# comment for b.
|
||||
# this is also a comment for b.
|
||||
b = "foo"
|
||||
|
||||
c = [ # comment for c.
|
||||
3.14, # this is not a comment for c, but for 3.14.
|
||||
] # this is also a comment for c.
|
||||
```
|
||||
|
||||
```cpp
|
||||
// "# comment for a."
|
||||
const std::string com1 = toml::find(data, "a").comment();
|
||||
|
||||
// "# comment for b."
|
||||
const std::string com2 = toml::find(data, "b").comment();
|
||||
|
||||
// "# comment for c.\n# this is also a comment for c."
|
||||
const std::string com3 = toml::find(data, "c").comment();
|
||||
|
||||
// "# this is not a comment for c, but for 3.14."
|
||||
const std::string com3 = toml::find<toml::array>(data, "c").front().comment();
|
||||
```
|
||||
|
||||
Note that once a data in a value is modified, the related file region
|
||||
information would be deleted. Thus after modifying a data, you cannot find any
|
||||
comments.
|
||||
|
||||
Also note that currently it does not support any way to set a comment to a value.
|
||||
And currently, serializer does not take comments into account.
|
||||
|
||||
## Serializing TOML data
|
||||
|
||||
toml11 (after v2.1.0) enables you to serialize data into toml format.
|
||||
|
@ -21,6 +21,7 @@ set(TEST_NAMES
|
||||
test_parse_key
|
||||
test_parse_table_key
|
||||
test_literals
|
||||
test_comments
|
||||
test_get
|
||||
test_get_related_func
|
||||
test_from_toml
|
||||
|
125
tests/test_comments.cpp
Normal file
125
tests/test_comments.cpp
Normal file
@ -0,0 +1,125 @@
|
||||
#define BOOST_TEST_MODULE "test_comments"
|
||||
#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#else
|
||||
#define BOOST_TEST_NO_LIB
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
#endif
|
||||
|
||||
#include <toml.hpp>
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_comment_before)
|
||||
{
|
||||
using namespace toml::literals::toml_literals;
|
||||
{
|
||||
const toml::value v = u8R"(
|
||||
# comment for a.
|
||||
a = 42
|
||||
# comment for b.
|
||||
b = "baz"
|
||||
)"_toml;
|
||||
|
||||
BOOST_CHECK_EQUAL(toml::find(v, "a").comment_before(), u8"# comment for a.");
|
||||
BOOST_CHECK_EQUAL(toml::find(v, "b").comment_before(), u8"# comment for b.");
|
||||
BOOST_CHECK_EQUAL(toml::find(v, "a").comment_inline(), "");
|
||||
BOOST_CHECK_EQUAL(toml::find(v, "b").comment_inline(), "");
|
||||
|
||||
BOOST_CHECK_EQUAL(toml::find(v, "a").comment(), u8"# comment for a.");
|
||||
BOOST_CHECK_EQUAL(toml::find(v, "b").comment(), u8"# comment for b.");
|
||||
}
|
||||
|
||||
{
|
||||
const toml::value v = u8R"(
|
||||
# comment for a.
|
||||
# another comment for a.
|
||||
a = 42
|
||||
# comment for b.
|
||||
# also comment for b.
|
||||
b = "baz"
|
||||
)"_toml;
|
||||
|
||||
BOOST_CHECK_EQUAL(toml::find(v, "a").comment_before(), u8R"(# comment for a.
|
||||
# another comment for a.)");
|
||||
BOOST_CHECK_EQUAL(toml::find(v, "b").comment_before(), u8R"(# comment for b.
|
||||
# also comment for b.)");
|
||||
BOOST_CHECK_EQUAL(toml::find(v, "a").comment_inline(), u8"");
|
||||
BOOST_CHECK_EQUAL(toml::find(v, "b").comment_inline(), u8"");
|
||||
|
||||
BOOST_CHECK_EQUAL(toml::find(v, "a").comment(), u8R"(# comment for a.
|
||||
# another comment for a.)");
|
||||
BOOST_CHECK_EQUAL(toml::find(v, "b").comment(), u8R"(# comment for b.
|
||||
# also comment for b.)");
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_comment_inline)
|
||||
{
|
||||
using namespace toml::literals::toml_literals;
|
||||
{
|
||||
const toml::value v = u8R"(
|
||||
a = 42 # comment for a.
|
||||
b = "baz" # comment for b.
|
||||
)"_toml;
|
||||
|
||||
BOOST_CHECK_EQUAL(toml::find(v, "a").comment_before(), "");
|
||||
BOOST_CHECK_EQUAL(toml::find(v, "b").comment_before(), "");
|
||||
BOOST_CHECK_EQUAL(toml::find(v, "a").comment_inline(), u8"# comment for a.");
|
||||
BOOST_CHECK_EQUAL(toml::find(v, "b").comment_inline(), u8"# comment for b.");
|
||||
|
||||
BOOST_CHECK_EQUAL(toml::find(v, "a").comment(), u8"# comment for a.");
|
||||
BOOST_CHECK_EQUAL(toml::find(v, "b").comment(), u8"# comment for b.");
|
||||
}
|
||||
{
|
||||
const toml::value v = u8R"(
|
||||
a = [ # comment for a.
|
||||
42,
|
||||
] # this also.
|
||||
b = [ # comment for b.
|
||||
"bar",
|
||||
]
|
||||
c = [
|
||||
3.14, # this is not a comment for c, but 3.14.
|
||||
] # comment for c.
|
||||
)"_toml;
|
||||
|
||||
BOOST_CHECK_EQUAL(toml::find(v, "a").comment_before(), "");
|
||||
BOOST_CHECK_EQUAL(toml::find(v, "b").comment_before(), "");
|
||||
BOOST_CHECK_EQUAL(toml::find(v, "c").comment_before(), "");
|
||||
|
||||
BOOST_CHECK_EQUAL(toml::find(v, "a").comment_inline(), u8R"(# comment for a.
|
||||
# this also.)");
|
||||
BOOST_CHECK_EQUAL(toml::find(v, "b").comment_inline(), u8"# comment for b.");
|
||||
BOOST_CHECK_EQUAL(toml::find(v, "c").comment_inline(), u8"# comment for c.");
|
||||
|
||||
BOOST_CHECK_EQUAL(toml::find(v, "a").comment(), u8R"(# comment for a.
|
||||
# this also.)");
|
||||
BOOST_CHECK_EQUAL(toml::find(v, "b").comment(), u8"# comment for b.");
|
||||
BOOST_CHECK_EQUAL(toml::find(v, "c").comment(), u8"# comment for c.");
|
||||
|
||||
const auto& c0 = toml::find<toml::array>(v, "c").at(0);
|
||||
BOOST_CHECK_EQUAL(c0.comment(), u8"# this is not a comment for c, but 3.14.");
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_comment_both)
|
||||
{
|
||||
using namespace toml::literals::toml_literals;
|
||||
{
|
||||
const toml::value v = u8R"(
|
||||
# comment for a.
|
||||
a = 42 # inline comment for a.
|
||||
# comment for b.
|
||||
b = "baz" # inline comment for b.
|
||||
)"_toml;
|
||||
|
||||
BOOST_CHECK_EQUAL(toml::find(v, "a").comment_before(), "# comment for a.");
|
||||
BOOST_CHECK_EQUAL(toml::find(v, "b").comment_before(), "# comment for b.");
|
||||
BOOST_CHECK_EQUAL(toml::find(v, "a").comment_inline(), "# inline comment for a.");
|
||||
BOOST_CHECK_EQUAL(toml::find(v, "b").comment_inline(), "# inline comment for b.");
|
||||
|
||||
BOOST_CHECK_EQUAL(toml::find(v, "a").comment(), u8R"(# comment for a.
|
||||
# inline comment for a.)");
|
||||
BOOST_CHECK_EQUAL(toml::find(v, "b").comment(), u8R"(# comment for b.
|
||||
# inline comment for b.)");
|
||||
}
|
||||
}
|
@ -52,6 +52,14 @@ struct region_base
|
||||
virtual std::size_t before() const noexcept {return 0;}
|
||||
// number of characters in the line after the region
|
||||
virtual std::size_t after() const noexcept {return 0;}
|
||||
|
||||
virtual std::string comment_before() const {return "";} // just before
|
||||
virtual std::string comment_inline() const {return "";} // in the same line
|
||||
virtual std::string comment() const {return "";} // concatenate
|
||||
// ```toml
|
||||
// # comment_before
|
||||
// key = "value" # comment_inline
|
||||
// ```
|
||||
};
|
||||
|
||||
// location represents a position in a container, which contains a file content.
|
||||
@ -280,6 +288,92 @@ struct region final : public region_base
|
||||
|
||||
std::string name() const override {return source_name_;}
|
||||
|
||||
std::string comment_before() const override
|
||||
{
|
||||
auto iter = this->line_begin(); // points the first element
|
||||
std::vector<std::pair<decltype(iter), decltype(iter)>> comments;
|
||||
while(iter != this->begin())
|
||||
{
|
||||
iter = std::prev(iter);
|
||||
using rev_iter = std::reverse_iterator<decltype(iter)>;
|
||||
auto line_before = std::find(rev_iter(iter), rev_iter(this->begin()),
|
||||
'\n').base();
|
||||
// range [line_before, iter) represents the previous line
|
||||
|
||||
auto comment_found = std::find(line_before, iter, '#');
|
||||
if(iter != comment_found && std::all_of(line_before, comment_found,
|
||||
[](const char c) noexcept -> bool {
|
||||
return c == ' ' || c == '\t';
|
||||
}))
|
||||
{
|
||||
// the line before this range contains only a comment.
|
||||
comments.push_back(std::make_pair(comment_found, iter));
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
iter = line_before;
|
||||
}
|
||||
|
||||
std::string com;
|
||||
for(auto i = comments.crbegin(), e = comments.crend(); i!=e; ++i)
|
||||
{
|
||||
if(i != comments.crbegin()) {com += '\n';}
|
||||
com += std::string(i->first, i->second);
|
||||
}
|
||||
return com;
|
||||
}
|
||||
|
||||
std::string comment_inline() const override
|
||||
{
|
||||
if(this->contain_newline())
|
||||
{
|
||||
std::string com;
|
||||
// check both the first and the last line.
|
||||
const auto first_line_end =
|
||||
std::find(this->line_begin(), this->last(), '\n');
|
||||
const auto first_comment_found =
|
||||
std::find(this->line_begin(), first_line_end, '#');
|
||||
|
||||
if(first_comment_found != first_line_end)
|
||||
{
|
||||
com += std::string(first_comment_found, first_line_end);
|
||||
}
|
||||
|
||||
const auto last_comment_found =
|
||||
std::find(this->last(), this->line_end(), '#');
|
||||
if(last_comment_found != this->line_end())
|
||||
{
|
||||
if(!com.empty()){com += '\n';}
|
||||
com += std::string(last_comment_found, this->line_end());
|
||||
}
|
||||
return com;
|
||||
}
|
||||
const auto comment_found =
|
||||
std::find(this->line_begin(), this->line_end(), '#');
|
||||
return std::string(comment_found, this->line_end());
|
||||
}
|
||||
|
||||
std::string comment() const override
|
||||
{
|
||||
std::string com_bef = this->comment_before();
|
||||
std::string com_inl = this->comment_inline();
|
||||
if(!com_bef.empty() && !com_inl.empty())
|
||||
{
|
||||
com_bef += '\n';
|
||||
return com_bef + com_inl;
|
||||
}
|
||||
else if(com_bef.empty())
|
||||
{
|
||||
return com_inl;
|
||||
}
|
||||
else
|
||||
{
|
||||
return com_bef;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
source_ptr source_;
|
||||
|
@ -607,6 +607,19 @@ class value
|
||||
template<value_t T>
|
||||
typename detail::toml_default_type<T>::type&& cast() &&;
|
||||
|
||||
std::string comment() const
|
||||
{
|
||||
return this->region_info_->comment();
|
||||
}
|
||||
std::string comment_before() const
|
||||
{
|
||||
return this->region_info_->comment_before();
|
||||
}
|
||||
std::string comment_inline() const
|
||||
{
|
||||
return this->region_info_->comment_inline();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void cleanup() noexcept
|
||||
|
Loading…
Reference in New Issue
Block a user