in src/SyntaxTree.cpp [49:184]
std::string_view ast_node::unescaped_view() const
{
if (!_unescaped)
{
if (is_type<block_quote_content_lines>())
{
// Trim leading and trailing empty lines
const auto isNonEmptyLine = [](const std::unique_ptr<ast_node>& child) noexcept {
return child->is_type<block_quote_line>();
};
const auto itrEndRev = std::make_reverse_iterator(
std::find_if(children.cbegin(), children.cend(), isNonEmptyLine));
const auto itrRev = std::find_if(children.crbegin(), itrEndRev, isNonEmptyLine);
std::vector<std::optional<std::pair<std::string_view, std::string_view>>> lines(
std::distance(itrRev, itrEndRev));
std::transform(itrRev,
itrEndRev,
lines.rbegin(),
[](const std::unique_ptr<ast_node>& child) noexcept {
return (child->is_type<block_quote_line>() && !child->children.empty()
&& child->children.front()->is_type<block_quote_empty_line>()
&& child->children.back()->is_type<block_quote_line_content>())
? std::make_optional(std::make_pair(child->children.front()->string_view(),
child->children.back()->unescaped_view()))
: std::nullopt;
});
// Calculate the common indent
const auto commonIndent = std::accumulate(lines.cbegin(),
lines.cend(),
std::optional<size_t> {},
[](auto value, const auto& line) noexcept {
if (line)
{
const auto indent = line->first.size();
if (!value || indent < *value)
{
value = indent;
}
}
return value;
});
const auto trimIndent = commonIndent ? *commonIndent : 0;
std::string joined;
if (!lines.empty())
{
joined.reserve(std::accumulate(lines.cbegin(),
lines.cend(),
size_t {},
[trimIndent](auto value, const auto& line) noexcept {
if (line)
{
value += line->first.size() - trimIndent;
value += line->second.size();
}
return value;
})
+ lines.size() - 1);
bool firstLine = true;
for (const auto& line : lines)
{
if (!firstLine)
{
joined.append(1, '\n');
}
if (line)
{
joined.append(line->first.substr(trimIndent));
joined.append(line->second);
}
firstLine = false;
}
}
const_cast<ast_node*>(this)->_unescaped =
std::make_unique<unescaped_t>(std::move(joined));
}
else if (children.size() > 1)
{
std::string joined;
joined.reserve(std::accumulate(children.cbegin(),
children.cend(),
size_t(0),
[](size_t total, const std::unique_ptr<ast_node>& child) {
return total + child->string_view().size();
}));
for (const auto& child : children)
{
joined.append(child->string_view());
}
const_cast<ast_node*>(this)->_unescaped =
std::make_unique<unescaped_t>(std::move(joined));
}
else if (!children.empty())
{
const_cast<ast_node*>(this)->_unescaped =
std::make_unique<unescaped_t>(children.front()->string_view());
}
else if (has_content() && is_type<escaped_unicode>())
{
const auto content = string_view();
memory_input<> in(content.data(), content.size(), "escaped unicode");
std::string utf8;
utf8.reserve((content.size() + 1) / 2);
unescape::unescape_j::apply(in, utf8);
const_cast<ast_node*>(this)->_unescaped =
std::make_unique<unescaped_t>(std::move(utf8));
}
else
{
const_cast<ast_node*>(this)->_unescaped =
std::make_unique<unescaped_t>(std::string_view {});
}
}
return std::visit(
[](const auto& value) noexcept {
return std::string_view { value };
},
*_unescaped);
}