fix: consider comments while serialization

This commit is contained in:
ToruNiina
2019-06-17 22:13:58 +09:00
parent d10c0725fd
commit 6399d44e3b

View File

@@ -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;