mirror of
https://github.com/ToruNiina/toml11.git
synced 2025-09-18 02:08:09 +08:00
fix: consider comments while serialization
This commit is contained in:
@@ -177,7 +177,14 @@ struct serializer
|
|||||||
if(!v.empty() && v.front().is_table())// v is an array of tables
|
if(!v.empty() && v.front().is_table())// v is an array of tables
|
||||||
{
|
{
|
||||||
// if it's not inlined, we need to add `[[table.key]]`.
|
// if it's not inlined, we need to add `[[table.key]]`.
|
||||||
// but if it can be inlined, we need `table.key = [...]`.
|
// but if it can be inlined,
|
||||||
|
// ```
|
||||||
|
// table.key = [
|
||||||
|
// {...},
|
||||||
|
// # comment
|
||||||
|
// {...},
|
||||||
|
// ]
|
||||||
|
// ```
|
||||||
if(this->can_be_inlined_)
|
if(this->can_be_inlined_)
|
||||||
{
|
{
|
||||||
std::string token;
|
std::string token;
|
||||||
@@ -186,32 +193,52 @@ struct serializer
|
|||||||
token += this->serialize_key(keys_.back());
|
token += this->serialize_key(keys_.back());
|
||||||
token += " = ";
|
token += " = ";
|
||||||
}
|
}
|
||||||
bool width_exceeds = false;
|
bool failed = false;
|
||||||
token += "[\n";
|
token += "[\n";
|
||||||
for(const auto& item : v)
|
for(const auto& item : v)
|
||||||
{
|
{
|
||||||
|
// if an element of the table has a comment, the table
|
||||||
|
// cannot be inlined.
|
||||||
|
if(this->has_comment_inside(item.as_table()))
|
||||||
|
{
|
||||||
|
failed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for(const auto& c : item.comments())
|
||||||
|
{
|
||||||
|
token += '#';
|
||||||
|
token += c;
|
||||||
|
token += '\n';
|
||||||
|
}
|
||||||
|
|
||||||
const auto t = this->make_inline_table(item.as_table());
|
const auto t = this->make_inline_table(item.as_table());
|
||||||
|
|
||||||
if(t.size() + 1 > width_ || // +1 for the last comma {...},
|
if(t.size() + 1 > width_ || // +1 for the last comma {...},
|
||||||
std::find(t.cbegin(), t.cend(), '\n') != t.cend())
|
std::find(t.cbegin(), t.cend(), '\n') != t.cend())
|
||||||
{
|
{
|
||||||
width_exceeds = true;
|
failed = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
token += t;
|
token += t;
|
||||||
token += ",\n";
|
token += ",\n";
|
||||||
}
|
}
|
||||||
if(!width_exceeds)
|
if(!failed)
|
||||||
{
|
{
|
||||||
token += "]\n";
|
token += "]\n";
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
// if width_exceeds, serialize it as [[array.of.tables]].
|
// if failed, serialize them as [[array.of.tables]].
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string token;
|
std::string token;
|
||||||
for(const auto& item : v)
|
for(const auto& item : v)
|
||||||
{
|
{
|
||||||
|
for(const auto& c : item.comments())
|
||||||
|
{
|
||||||
|
token += '#';
|
||||||
|
token += c;
|
||||||
|
token += '\n';
|
||||||
|
}
|
||||||
token += "[[";
|
token += "[[";
|
||||||
token += this->serialize_dotted_key(keys_);
|
token += this->serialize_dotted_key(keys_);
|
||||||
token += "]]\n";
|
token += "]]\n";
|
||||||
@@ -224,7 +251,9 @@ struct serializer
|
|||||||
return std::string("[]");
|
return std::string("[]");
|
||||||
}
|
}
|
||||||
|
|
||||||
// not an array of tables. normal array. first, try to make it inline.
|
// not an array of tables. normal array.
|
||||||
|
// first, try to make it inline if none of the elements have a comment.
|
||||||
|
if(!this->has_comment_inside(v))
|
||||||
{
|
{
|
||||||
const auto inl = this->make_inline_array(v);
|
const auto inl = this->make_inline_array(v);
|
||||||
if(inl.size() < this->width_ &&
|
if(inl.size() < this->width_ &&
|
||||||
@@ -234,16 +263,54 @@ struct serializer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the length exceeds this->width_, print multiline array
|
// if the length exceeds this->width_, print multiline array.
|
||||||
|
// key = [
|
||||||
|
// # ...
|
||||||
|
// 42,
|
||||||
|
// ...
|
||||||
|
// ]
|
||||||
std::string token;
|
std::string token;
|
||||||
std::string current_line;
|
std::string current_line;
|
||||||
token += "[\n";
|
token += "[\n";
|
||||||
for(const auto& item : v)
|
for(const auto& item : v)
|
||||||
{
|
{
|
||||||
auto next_elem = toml::visit(*this, item);
|
if(!item.comments().empty())
|
||||||
// newline between array-value and comma is not allowed
|
{
|
||||||
if(next_elem.back() == '\n'){next_elem.pop_back();}
|
// if comment exists, the element must be the only element in the line.
|
||||||
|
// e.g. the following is not allowed.
|
||||||
|
// ```toml
|
||||||
|
// array = [
|
||||||
|
// # comment for what?
|
||||||
|
// 1, 2, 3, 4, 5
|
||||||
|
// ]
|
||||||
|
// ```
|
||||||
|
if(!current_line.empty())
|
||||||
|
{
|
||||||
|
if(current_line.back() != '\n')
|
||||||
|
{
|
||||||
|
current_line += '\n';
|
||||||
|
}
|
||||||
|
token += current_line;
|
||||||
|
current_line.clear();
|
||||||
|
}
|
||||||
|
for(const auto& c : item.comments())
|
||||||
|
{
|
||||||
|
token += '#';
|
||||||
|
token += c;
|
||||||
|
token += '\n';
|
||||||
|
}
|
||||||
|
token += toml::visit(*this, item);
|
||||||
|
if(token.back() == '\n') {token.pop_back();}
|
||||||
|
token += ",\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::string next_elem;
|
||||||
|
next_elem += toml::visit(*this, item);
|
||||||
|
|
||||||
|
// comma before newline.
|
||||||
|
if(next_elem.back() == '\n') {next_elem.pop_back();}
|
||||||
|
|
||||||
|
// if current line does not exceeds the width limit, continue.
|
||||||
if(current_line.size() + next_elem.size() + 1 < this->width_)
|
if(current_line.size() + next_elem.size() + 1 < this->width_)
|
||||||
{
|
{
|
||||||
current_line += next_elem;
|
current_line += next_elem;
|
||||||
@@ -251,12 +318,13 @@ struct serializer
|
|||||||
}
|
}
|
||||||
else if(current_line.empty())
|
else if(current_line.empty())
|
||||||
{
|
{
|
||||||
// the next elem cannot be within the width.
|
// if current line was empty, force put the next_elem because
|
||||||
|
// next_elem is not splittable
|
||||||
token += next_elem;
|
token += next_elem;
|
||||||
token += ",\n";
|
token += ",\n";
|
||||||
// keep current line empty
|
// current_line is kept empty
|
||||||
}
|
}
|
||||||
else // current_line has some tokens and it exceeds width
|
else // reset current_line
|
||||||
{
|
{
|
||||||
assert(current_line.back() == ',');
|
assert(current_line.back() == ',');
|
||||||
token += current_line;
|
token += current_line;
|
||||||
@@ -265,11 +333,6 @@ struct serializer
|
|||||||
current_line += ',';
|
current_line += ',';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!current_line.empty())
|
|
||||||
{
|
|
||||||
if(current_line.back() != '\n') {current_line += '\n';}
|
|
||||||
token += current_line;
|
|
||||||
}
|
|
||||||
token += "]\n";
|
token += "]\n";
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
@@ -277,7 +340,9 @@ struct serializer
|
|||||||
// templatize for any table-like container
|
// templatize for any table-like container
|
||||||
std::string operator()(const table_type& v) const
|
std::string operator()(const table_type& v) const
|
||||||
{
|
{
|
||||||
if(this->can_be_inlined_)
|
// if an element has a comment, then it can't be inlined.
|
||||||
|
// table = {# how can we write a comment for this? key = "value"}
|
||||||
|
if(this->can_be_inlined_ && !(this->has_comment_inside(v)))
|
||||||
{
|
{
|
||||||
std::string token;
|
std::string token;
|
||||||
if(!this->keys_.empty())
|
if(!this->keys_.empty())
|
||||||
@@ -386,8 +451,27 @@ struct serializer
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if an element of a table or an array has a comment, it cannot be inlined.
|
||||||
|
bool has_comment_inside(const array_type& a) const noexcept
|
||||||
|
{
|
||||||
|
for(const auto& v : a)
|
||||||
|
{
|
||||||
|
if(!v.comments().empty()) {return true;}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool has_comment_inside(const table_type& t) const noexcept
|
||||||
|
{
|
||||||
|
for(const auto& kv : t)
|
||||||
|
{
|
||||||
|
if(!kv.second.comments().empty()) {return true;}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
std::string make_inline_array(const array_type& v) const
|
std::string make_inline_array(const array_type& v) const
|
||||||
{
|
{
|
||||||
|
assert(!has_comment_inside(v));
|
||||||
std::string token;
|
std::string token;
|
||||||
token += '[';
|
token += '[';
|
||||||
bool is_first = true;
|
bool is_first = true;
|
||||||
@@ -403,6 +487,7 @@ struct serializer
|
|||||||
|
|
||||||
std::string make_inline_table(const table_type& v) const
|
std::string make_inline_table(const table_type& v) const
|
||||||
{
|
{
|
||||||
|
assert(!has_comment_inside(v));
|
||||||
assert(this->can_be_inlined_);
|
assert(this->can_be_inlined_);
|
||||||
std::string token;
|
std::string token;
|
||||||
token += '{';
|
token += '{';
|
||||||
@@ -433,6 +518,15 @@ struct serializer
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!kv.second.comments().empty())
|
||||||
|
{
|
||||||
|
for(const auto& c : kv.second.comments())
|
||||||
|
{
|
||||||
|
token += '#';
|
||||||
|
token += c;
|
||||||
|
token += '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
const auto key_and_sep = this->serialize_key(kv.first) + " = ";
|
const auto key_and_sep = this->serialize_key(kv.first) + " = ";
|
||||||
const auto residual_width = (this->width_ > key_and_sep.size()) ?
|
const auto residual_width = (this->width_ > key_and_sep.size()) ?
|
||||||
this->width_ - key_and_sep.size() : 0;
|
this->width_ - key_and_sep.size() : 0;
|
||||||
@@ -477,6 +571,16 @@ struct serializer
|
|||||||
// still inline tables only.
|
// still inline tables only.
|
||||||
tmp += '\n';
|
tmp += '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!kv.second.comments().empty())
|
||||||
|
{
|
||||||
|
for(const auto& c : kv.second.comments())
|
||||||
|
{
|
||||||
|
token += '#';
|
||||||
|
token += c;
|
||||||
|
token += '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
token += tmp;
|
token += tmp;
|
||||||
}
|
}
|
||||||
return token;
|
return token;
|
||||||
|
Reference in New Issue
Block a user