From 2235e98fd0f9d7b464063b5f1ee7834443101ea7 Mon Sep 17 00:00:00 2001 From: Colin Moy Date: Fri, 7 Aug 2026 20:59:17 +0000 Subject: [PATCH] fix(generator): preserve comment introducers across embedded newlines --- generator/internal/codegen_utils.cc | 50 +++++++++++++++++------- generator/internal/codegen_utils_test.cc | 12 +++++- 2 files changed, 46 insertions(+), 16 deletions(-) diff --git a/generator/internal/codegen_utils.cc b/generator/internal/codegen_utils.cc index 3e5be23b867d1..08005f8fc10ad 100644 --- a/generator/internal/codegen_utils.cc +++ b/generator/internal/codegen_utils.cc @@ -331,26 +331,46 @@ std::string FormatCommentBlock(std::string const& comment, if (offset >= line_length) GCP_LOG(FATAL) << "line_length is too small"; auto comment_width = line_length - offset; - std::vector lines; - std::size_t start_pos = 0; - while (start_pos != std::string::npos) { - std::size_t boundary = start_pos + comment_width; - std::size_t end_pos = boundary; - if (boundary < comment.length()) { - // Look backward from the boundary for the last word - end_pos = comment.rfind(' ', boundary); - // If there is only one word, find and use its boundary - if (end_pos == std::string::npos || end_pos < start_pos) { - end_pos = comment.find(' ', boundary); + std::vector lines; + std::vector paragraphs = absl::StrSplit(comment, '\n'); + for (auto const& paragraph : paragraphs) { + if (paragraph.empty()) { + lines.emplace_back(); + continue; + } + std::size_t start_pos = 0; + while (start_pos != std::string_view::npos) { + std::size_t boundary = start_pos + comment_width; + std::size_t end_pos = boundary; + if (boundary < paragraph.length()) { + // Look backward from the boundary for the last word + end_pos = paragraph.rfind(' ', boundary); + // If there is only one word, find and use its boundary + if (end_pos == std::string_view::npos || end_pos < start_pos) { + end_pos = paragraph.find(' ', boundary); + } } + lines.push_back(paragraph.substr(start_pos, end_pos - start_pos)); + start_pos = paragraph.find_first_not_of(' ', end_pos); } - lines.push_back(comment.substr(start_pos, end_pos - start_pos)); - start_pos = comment.find_first_not_of(' ', end_pos); } std::string indent(indent_level * indent_width, ' '); - std::string joiner = absl::StrCat("\n", indent, comment_introducer); - return absl::StrCat(indent, comment_introducer, absl::StrJoin(lines, joiner)); + std::string trimmed_introducer = comment_introducer; + while (!trimmed_introducer.empty() && trimmed_introducer.back() == ' ') { + trimmed_introducer.pop_back(); + } + + std::string result; + for (std::size_t i = 0; i < lines.size(); ++i) { + if (i > 0) result += "\n"; + if (lines[i].empty()) { + result += absl::StrCat(indent, trimmed_introducer); + } else { + result += absl::StrCat(indent, comment_introducer, lines[i]); + } + } + return result; } std::string FormatCommentKeyValueList( diff --git a/generator/internal/codegen_utils_test.cc b/generator/internal/codegen_utils_test.cc index de160074187a7..31bfabb8453b6 100644 --- a/generator/internal/codegen_utils_test.cc +++ b/generator/internal/codegen_utils_test.cc @@ -466,7 +466,17 @@ wordthatiswaytoolong)"""}, // internal)](https://cloud.google.com/compute/docs/reference/rest/v1/globalAddresses) // * [Regional (external and // internal)](https://cloud.google.com/compute/docs/reference/rest/v1/addresses) -// For more information, see Reserving a static external IP address.)"""})); +// For more information, see Reserving a static external IP address.)"""}, + FormatCommentBlockTestParams{"Line 1.\nLine 2 is here.", 1, "// ", 2, + 80, R"""( + // Line 1. + // Line 2 is here.)"""}, + FormatCommentBlockTestParams{ + "Paragraph 1.\n\nParagraph 2 with more text.", 1, "// ", 2, 80, + R"""( + // Paragraph 1. + // + // Paragraph 2 with more text.)"""})); struct FormatCommentKeyValueListTestParams { std::vector> comment;