[Clang-doc] Display values and comments in MD (#183754)

Display enum members in a tabular format in markdown.
Support displaying enum member value and comments.
Output:
| Name | Value | Comments |
|---|---|---|
| Small | 0 | A pearl.<br>Pearls are quite small.<br><br>Pearls are used
in jewelry. |
| Medium | 1 | A tennis ball. |
| Large | 2 | A football. |
This commit is contained in:
Samrudh Nelli 2026-03-11 23:20:22 +05:30 committed by GitHub
parent ac2567c9f7
commit a60fc29236
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 156 additions and 57 deletions

View File

@ -8,6 +8,7 @@
#include "Generators.h"
#include "Representation.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FormatVariadic.h"
@ -67,6 +68,80 @@ static void writeSourceFileRef(const ClangDocContext &CDCtx, const Location &L,
OS << "\n\n";
}
/// Writer for writing comments to a table cell in MD.
///
/// The writer traverses the comments recursively and outputs the
/// comments into a stream.
/// The formatter inserts single/double line breaks to retain the comment
/// structure.
///
/// Usage :
/// Initialize an object with a llvm::raw_ostream to output into.
/// Call the write(C) function with an array of Comments 'C'.
class TableCommentWriter {
public:
explicit TableCommentWriter(llvm::raw_ostream &OS) : OS(OS) {}
void write(llvm::ArrayRef<CommentInfo> Comments) {
for (const auto &C : Comments)
writeTableSafeComment(C);
if (!Started)
OS << "--";
}
private:
/// This function inserts breaks into the stream.
///
/// We add a double break in between paragraphs.
/// Inside a paragraph, a single break between lines is maintained.
void insertSeparator() {
if (!Started)
return;
if (NeedsParagraphBreak) {
OS << "<br><br>";
NeedsParagraphBreak = false;
} else {
OS << "<br>";
}
}
/// This function processes every comment and its children recursively.
void writeTableSafeComment(const CommentInfo &I) {
switch (I.Kind) {
case CommentKind::CK_FullComment:
for (const auto &Child : I.Children)
writeTableSafeComment(*Child);
break;
case CommentKind::CK_ParagraphComment:
for (const auto &Child : I.Children)
writeTableSafeComment(*Child);
// Next content after a paragraph needs a break
NeedsParagraphBreak = true;
break;
case CommentKind::CK_TextComment:
if (!I.Text.empty()) {
insertSeparator();
OS << I.Text;
Started = true;
}
break;
// Handle other comment types (BlockCommand, InlineCommand, etc.)
default:
for (const auto &Child : I.Children)
writeTableSafeComment(*Child);
break;
}
}
llvm::raw_ostream &OS;
bool Started = false;
bool NeedsParagraphBreak = false;
};
static void maybeWriteSourceFileRef(llvm::raw_ostream &OS,
const ClangDocContext &CDCtx,
const std::optional<Location> &DefLoc) {
@ -163,14 +238,36 @@ static void genMarkdown(const ClangDocContext &CDCtx, const EnumInfo &I,
if (I.BaseType && !I.BaseType->Type.QualName.empty()) {
OS << ": " << I.BaseType->Type.QualName << " ";
}
OS << "|\n\n" << "--\n\n";
OS << "|\n\n";
std::string Buffer;
llvm::raw_string_ostream Members(Buffer);
if (!I.Members.empty())
for (const auto &N : I.Members)
Members << "| " << N.Name << " |\n";
writeLine(Members.str(), OS);
OS << "| Name | Value |";
if (!I.Members.empty()) {
bool HasComments = false;
for (const auto &Member : I.Members) {
if (!Member.Description.empty()) {
HasComments = true;
OS << " Comments |";
break;
}
}
OS << "\n|---|---|";
if (HasComments)
OS << "---|";
OS << "\n";
for (const auto &N : I.Members) {
OS << "| " << N.Name << " ";
if (!N.Value.empty())
OS << "| " << N.Value << " ";
if (HasComments) {
OS << "| ";
TableCommentWriter CommentWriter(OS);
CommentWriter.write(N.Description);
OS << " ";
}
OS << "|\n";
}
}
OS << "\n";
maybeWriteSourceFileRef(OS, CDCtx, I.DefLoc);

View File

@ -41,10 +41,11 @@ enum Color {
// MD-INDEX: ## Enums
// MD-INDEX: | enum Color |
// MD-INDEX: --
// MD-INDEX: | Red |
// MD-INDEX: | Green |
// MD-INDEX: | Blue |
// MD-INDEX: | Name | Value | Comments |
// MD-INDEX: |---|---|---|
// MD-INDEX: | Red | 0 | Comment 1 |
// MD-INDEX: | Green | 1 | Comment 2 |
// MD-INDEX: | Blue | 2 | Comment 3 |
// MD-INDEX: **brief** For specifying RGB colors
// HTML-INDEX-LABEL: <div id="{{([0-9A-F]{40})}}" class="delimiter-container">
@ -92,7 +93,7 @@ enum Color {
// HTML-INDEX-NEXT: <p>For specifying RGB colors</p>
// HTML-INDEX-NEXT: </div>
// HTML-INDEX-NEXT: </div>
// HTML-INDEX-NEXT: <p>Defined at line [[@LINE-62]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
// HTML-INDEX-NEXT: <p>Defined at line [[@LINE-63]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
// HTML-INDEX-NEXT: </div>
// MD-MUSTACHE-INDEX: ## Enums
@ -118,11 +119,13 @@ enum class Shapes {
/// Comment 3
Triangle
};
// MD-INDEX: | enum class Shapes |
// MD-INDEX: --
// MD-INDEX: | Circle |
// MD-INDEX: | Rectangle |
// MD-INDEX: | Triangle |
// MD-INDEX: | Name | Value | Comments |
// MD-INDEX: |---|---|---|
// MD-INDEX: | Circle | 0 | Comment 1 |
// MD-INDEX: | Rectangle | 1 | Comment 2 |
// MD-INDEX: | Triangle | 2 | Comment 3 |
// MD-INDEX: **brief** Shape Types
// HTML-INDEX-LABEL: <div id="{{([0-9A-F]{40})}}" class="delimiter-container">
@ -170,7 +173,7 @@ enum class Shapes {
// HTML-INDEX-NEXT: <p>Shape Types</p>
// HTML-INDEX-NEXT: </div>
// HTML-INDEX-NEXT: </div>
// HTML-INDEX-NEXT: <p>Defined at line [[@LINE-64]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
// HTML-INDEX-NEXT: <p>Defined at line [[@LINE-66]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
// HTML-INDEX-NEXT: </div>
typedef unsigned char uint8_t;
@ -195,10 +198,11 @@ enum Size : uint8_t {
};
// MD-INDEX: | enum Size : uint8_t |
// MD-INDEX: --
// MD-INDEX: | Small |
// MD-INDEX: | Medium |
// MD-INDEX: | Large |
// MD-INDEX: | Name | Value | Comments |
// MD-INDEX: |---|---|---|
// MD-INDEX: | Small | 0 | A pearl.<br>Pearls are quite small.<br><br>Pearls are used in jewelry. |
// MD-INDEX: | Medium | 1 | A tennis ball. |
// MD-INDEX: | Large | 2 | A football. |
// MD-INDEX: **brief** Specify the size
// HTML-INDEX-LABEL: <div id="{{([0-9A-F]{40})}}" class="delimiter-container">
@ -248,7 +252,7 @@ enum Size : uint8_t {
// HTML-INDEX-NEXT: <p>Specify the size</p>
// HTML-INDEX-NEXT: </div>
// HTML-INDEX-NEXT: </div>
// HTML-INDEX-NEXT: <p>Defined at line [[@LINE-71]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
// HTML-INDEX-NEXT: <p>Defined at line [[@LINE-72]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
// HTML-INDEX-NEXT: </div>
/**
@ -261,8 +265,9 @@ enum : long long {
};
// MD-INDEX: | enum (unnamed) : long long |
// MD-INDEX: --
// MD-INDEX: | BigVal |
// MD-INDEX: | Name | Value | Comments |
// MD-INDEX: |---|---|---|
// MD-INDEX: | BigVal | 999999999999 | A very large value |
// MD-INDEX: **brief** Very long number
// HTML-INDEX-LABEL: <div id="{{([0-9A-F]{40})}}" class="delimiter-container">
@ -292,7 +297,7 @@ enum : long long {
// HTML-INDEX-NEXT: <p>Very long number</p>
// HTML-INDEX-NEXT: </div>
// HTML-INDEX-NEXT: </div>
// HTML-INDEX-NEXT: <p>Defined at line [[@LINE-38]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
// HTML-INDEX-NEXT: <p>Defined at line [[@LINE-39]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
// HTML-INDEX-NEXT: </div>
class FilePermissions {
@ -312,10 +317,11 @@ public:
};
// MD-PERM: | enum (unnamed) |
// MD-PERM: --
// MD-PERM: | Read |
// MD-PERM: | Write |
// MD-PERM: | Execute |
// MD-PERM: | Name | Value | Comments |
// MD-PERM: |---|---|---|
// MD-PERM: | Read | 1 | Permission to READ r |
// MD-PERM: | Write | 2 | Permission to WRITE w |
// MD-PERM: | Execute | 4 | Permission to EXECUTE x |
// MD-PERM: **brief** File permission flags
// HTML-PERM-LABEL: <section id="Enums" class="section-container">
@ -365,7 +371,7 @@ public:
// HTML-PERM-NEXT: <p>File permission flags</p>
// HTML-PERM-NEXT: </div>
// HTML-PERM-NEXT: </div>
// HTML-PERM-NEXT: <p>Defined at line [[@LINE-63]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
// HTML-PERM-NEXT: <p>Defined at line [[@LINE-64]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
// HTML-PERM-NEXT: </div>
// HTML-PERM-NEXT: </section>
@ -442,10 +448,11 @@ public:
// MD-ANIMAL: # class Animals
// MD-ANIMAL: ## Enums
// MD-ANIMAL: | enum AnimalType |
// MD-ANIMAL: --
// MD-ANIMAL: | Dog |
// MD-ANIMAL: | Cat |
// MD-ANIMAL: | Iguana |
// MD-ANIMAL: | Name | Value | Comments |
// MD-ANIMAL: |---|---|---|
// MD-ANIMAL: | Dog | 0 | Man's best friend |
// MD-ANIMAL: | Cat | 1 | Man's other best friend |
// MD-ANIMAL: | Iguana | 2 | A lizard |
// MD-ANIMAL: **brief** specify what animal the class is
// MD-MUSTACHE-ANIMAL: # class Animals
@ -476,11 +483,12 @@ enum Car {
// MD-VEHICLES: # namespace Vehicles
// MD-VEHICLES: ## Enums
// MD-VEHICLES: | enum Car |
// MD-VEHICLES: --
// MD-VEHICLES: | Sedan |
// MD-VEHICLES: | SUV |
// MD-VEHICLES: | Pickup |
// MD-VEHICLES: | Hatchback |
// MD-VEHICLES: | Name | Value | Comments |
// MD-VEHICLES: |---|---|---|
// MD-VEHICLES: | Sedan | 0 | Comment 1 |
// MD-VEHICLES: | SUV | 1 | Comment 2 |
// MD-VEHICLES: | Pickup | 2 | -- |
// MD-VEHICLES: | Hatchback | 3 | Comment 4 |
// MD-VEHICLES: **brief** specify type of car
// HTML-VEHICLES-LABEL: <div id="{{([0-9A-F]{40})}}" class="delimiter-container">
@ -533,7 +541,7 @@ enum Car {
// HTML-VEHICLES-NEXT: <p>specify type of car</p>
// HTML-VEHICLES-NEXT: </div>
// HTML-VEHICLES-NEXT: </div>
// HTML-VEHICLES-NEXT: <p>Defined at line [[@LINE-72]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
// HTML-VEHICLES-NEXT: <p>Defined at line [[@LINE-73]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
// HTML-VEHICLES-NEXT: </div>
// MD-MUSTACHE-VEHICLES: # namespace Vehicles
@ -553,10 +561,11 @@ enum ColorUserSpecified {
};
// MD-INDEX: | enum ColorUserSpecified |
// MD-INDEX: --
// MD-INDEX: | RedUserSpecified |
// MD-INDEX: | GreenUserSpecified |
// MD-INDEX: | BlueUserSpecified |
// MD-INDEX: | Name | Value |
// MD-INDEX: |---|---|
// MD-INDEX: | RedUserSpecified | 65 |
// MD-INDEX: | GreenUserSpecified | 2 |
// MD-INDEX: | BlueUserSpecified | 67 |
// HTML-INDEX-LABEL: <div id="{{([0-9A-F]{40})}}" class="delimiter-container">
// HTML-INDEX-NEXT: <div>
@ -582,7 +591,7 @@ enum ColorUserSpecified {
// HTML-INDEX-NEXT: </tr>
// HTML-INDEX-NEXT: </tbody>
// HTML-INDEX-NEXT: </table>
// HTML-INDEX-NEXT: <p>Defined at line [[@LINE-36]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
// HTML-INDEX-NEXT: <p>Defined at line [[@LINE-37]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
// HTML-INDEX-NEXT: </div>
// MD-MUSTACHE-INDEX: | enum ColorUserSpecified |

View File

@ -69,10 +69,7 @@ TEST_F(MDGeneratorTest, emitNamespaceMD) {
| enum OneEnum |
--
| Name | Value |
)raw";
@ -136,10 +133,7 @@ ChildStruct
| enum OneEnum |
--
| Name | Value |
)raw";
@ -197,10 +191,9 @@ TEST_F(MDGeneratorTest, emitEnumMD) {
assert(!Err);
std::string Expected = R"raw(| enum class e |
--
| X |
| Name | Value |
|---|---|
| X | 0 |
*Defined at test.cpp#10*