|
8 | 8 | #include "slang/syntax/SyntaxPrinter.h" |
9 | 9 |
|
10 | 10 | #include "slang/parsing/ParserMetadata.h" |
| 11 | +#include "slang/parsing/Token.h" |
11 | 12 | #include "slang/syntax/SyntaxNode.h" |
12 | 13 | #include "slang/syntax/SyntaxTree.h" |
13 | 14 | #include "slang/text/SourceLocation.h" |
14 | 15 | #include "slang/text/SourceManager.h" |
| 16 | +#include "slang/util/Util.h" |
15 | 17 |
|
16 | 18 | namespace slang::syntax { |
17 | 19 |
|
@@ -118,6 +120,82 @@ SyntaxPrinter& SyntaxPrinter::print(const SyntaxNode& node) { |
118 | 120 | return *this; |
119 | 121 | } |
120 | 122 |
|
| 123 | +SyntaxPrinter& SyntaxPrinter::printLeadingComments(const SyntaxNode& node) { |
| 124 | + auto triviaSpan = node.getFirstToken().trivia(); |
| 125 | + using Iterator = std::span<const Trivia>::iterator; |
| 126 | + std::optional<Iterator> lastComment; |
| 127 | + std::optional<Iterator> leadingCommentStart; |
| 128 | + |
| 129 | + // Walk backwards through trivia until |
| 130 | + // - block comment |
| 131 | + // - double new line after seeing a comment |
| 132 | + // This misses leading trivia at first line, although that's typically for license/file |
| 133 | + auto findDocBoundary = [&]() { |
| 134 | + bool lastIsNewline = false; |
| 135 | + for (auto it = triviaSpan.rbegin(); it != triviaSpan.rend(); it++) { |
| 136 | + const auto& trivia = *it; |
| 137 | + switch (trivia.kind) { |
| 138 | + case TriviaKind::EndOfLine: |
| 139 | + if (lastIsNewline && lastComment) { |
| 140 | + // found a double newline after a comment, stop here |
| 141 | + return; |
| 142 | + } |
| 143 | + leadingCommentStart = lastComment; |
| 144 | + lastIsNewline = true; |
| 145 | + break; |
| 146 | + case TriviaKind::BlockComment: |
| 147 | + // the first block comment is the start |
| 148 | + leadingCommentStart = it.base() - 1; |
| 149 | + return; |
| 150 | + case TriviaKind::LineComment: |
| 151 | + lastComment = it.base() - 1; |
| 152 | + [[fallthrough]]; |
| 153 | + default: |
| 154 | + lastIsNewline = false; |
| 155 | + } |
| 156 | + } |
| 157 | + }; |
| 158 | + findDocBoundary(); |
| 159 | + |
| 160 | + if (leadingCommentStart) { |
| 161 | + for (auto it = *leadingCommentStart; it != triviaSpan.end(); it++) { |
| 162 | + print(*it); |
| 163 | + } |
| 164 | + } |
| 165 | + |
| 166 | + return *this; |
| 167 | +} |
| 168 | + |
| 169 | +SyntaxPrinter& SyntaxPrinter::printExcludingLeadingComments(const SyntaxNode& node) { |
| 170 | + if (!includeTrivia) { |
| 171 | + print(node); |
| 172 | + return *this; |
| 173 | + } |
| 174 | + |
| 175 | + auto it = node.tokens_begin(); |
| 176 | + auto end = node.tokens_end(); |
| 177 | + |
| 178 | + if (it == end) |
| 179 | + return *this; |
| 180 | + |
| 181 | + // Print first token without leading trivia |
| 182 | + includeTrivia = false; |
| 183 | + print(*it); |
| 184 | + includeTrivia = true; |
| 185 | + |
| 186 | + for (++it; it != end; ++it) { |
| 187 | + print(*it); |
| 188 | + } |
| 189 | + |
| 190 | + return *this; |
| 191 | +} |
| 192 | + |
| 193 | +SyntaxPrinter& SyntaxPrinter::printWithLeadingComments(const SyntaxNode& node) { |
| 194 | + printLeadingComments(node); |
| 195 | + printExcludingLeadingComments(node); |
| 196 | + return *this; |
| 197 | +} |
| 198 | + |
121 | 199 | SyntaxPrinter& SyntaxPrinter::print(const SyntaxTree& tree) { |
122 | 200 | print(tree.root()); |
123 | 201 | if (tree.root().kind != SyntaxKind::CompilationUnit && tree.getMetadata().eofToken) |
|
0 commit comments