Skip to content

Commit 7003f64

Browse files
committed
[clang-doc] Improving Markdown Output
This change has two components. The moves the generated file for a namespace to the directory named after the namespace in a file named 'index.<format>'. This greatly improves the browsing experience since the index page is shown by default for a directory. The second improves the markdown output by adding the links to the referenced pages for children objects and the link back to the source code. Patch By: Clayton Differential Revision: https://reviews.llvm.org/D72954
1 parent 4e7c686 commit 7003f64

File tree

9 files changed

+246
-65
lines changed

9 files changed

+246
-65
lines changed

clang-tools-extra/clang-doc/HTMLGenerator.cpp

+11-9
Original file line numberDiff line numberDiff line change
@@ -314,9 +314,9 @@ genReference(const Reference &Type, StringRef CurrentDirectory,
314314
else
315315
return genLink(Type.Name, "#" + JumpToSection.getValue());
316316
}
317-
llvm::SmallString<128> Path =
318-
computeRelativePath(Type.Path, CurrentDirectory);
319-
llvm::sys::path::append(Path, Type.Name + ".html");
317+
llvm::SmallString<64> Path = Type.getRelativeFilePath(CurrentDirectory);
318+
llvm::sys::path::append(Path, Type.getFileBaseName() + ".html");
319+
320320
// Paths in HTML must be in posix-style
321321
llvm::sys::path::native(Path, llvm::sys::path::Style::posix);
322322
if (JumpToSection)
@@ -730,15 +730,17 @@ genHTML(const NamespaceInfo &I, Index &InfoIndex, const ClangDocContext &CDCtx,
730730
if (!I.Description.empty())
731731
Out.emplace_back(genHTML(I.Description));
732732

733+
llvm::SmallString<64> BasePath = I.getRelativeFilePath("");
734+
733735
std::vector<std::unique_ptr<TagNode>> ChildNamespaces =
734-
genReferencesBlock(I.ChildNamespaces, "Namespaces", I.Path);
736+
genReferencesBlock(I.ChildNamespaces, "Namespaces", BasePath);
735737
AppendVector(std::move(ChildNamespaces), Out);
736738
std::vector<std::unique_ptr<TagNode>> ChildRecords =
737-
genReferencesBlock(I.ChildRecords, "Records", I.Path);
739+
genReferencesBlock(I.ChildRecords, "Records", BasePath);
738740
AppendVector(std::move(ChildRecords), Out);
739741

740742
std::vector<std::unique_ptr<TagNode>> ChildFunctions =
741-
genFunctionsBlock(I.ChildFunctions, CDCtx, I.Path);
743+
genFunctionsBlock(I.ChildFunctions, CDCtx, BasePath);
742744
AppendVector(std::move(ChildFunctions), Out);
743745
std::vector<std::unique_ptr<TagNode>> ChildEnums =
744746
genEnumsBlock(I.ChildEnums, CDCtx);
@@ -860,8 +862,8 @@ llvm::Error HTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
860862
"unexpected info type");
861863
}
862864

863-
HTMLFile F =
864-
genInfoFile(InfoTitle, I->Path, MainContentNodes, InfoIndex, CDCtx);
865+
HTMLFile F = genInfoFile(InfoTitle, I->getRelativeFilePath(""),
866+
MainContentNodes, InfoIndex, CDCtx);
865867
F.Render(OS);
866868

867869
return llvm::Error::success();
@@ -902,7 +904,7 @@ static llvm::Error SerializeIndex(ClangDocContext &CDCtx) {
902904
J.attribute("USR", toHex(llvm::toStringRef(I.USR)));
903905
J.attribute("Name", I.Name);
904906
J.attribute("RefType", getRefType(I.RefType));
905-
J.attribute("Path", I.Path);
907+
J.attribute("Path", I.getRelativeFilePath(""));
906908
J.attributeArray("Children", [&] {
907909
for (const Index &C : I.Children)
908910
IndexToJSON(C);

clang-tools-extra/clang-doc/MDGenerator.cpp

+143-25
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,20 @@ static void writeHeader(const Twine &Text, unsigned int Num, raw_ostream &OS) {
5050
OS << std::string(Num, '#') + " " + Text << "\n\n";
5151
}
5252

53-
static void writeFileDefinition(const Location &L, raw_ostream &OS) {
54-
OS << genItalic("Defined at line " + std::to_string(L.LineNumber) + " of " +
55-
L.Filename)
56-
<< "\n\n";
53+
static void writeFileDefinition(const ClangDocContext &CDCtx, const Location &L,
54+
raw_ostream &OS) {
55+
56+
if (!CDCtx.RepositoryUrl) {
57+
OS << "*Defined at " << L.Filename << "#" << std::to_string(L.LineNumber)
58+
<< "*";
59+
} else {
60+
OS << "*Defined at [" << L.Filename << "#" << std::to_string(L.LineNumber)
61+
<< "](" << StringRef{CDCtx.RepositoryUrl.getValue()}
62+
<< llvm::sys::path::relative_path(L.Filename) << "#"
63+
<< std::to_string(L.LineNumber) << ")"
64+
<< "*";
65+
}
66+
OS << "\n\n";
5767
}
5868

5969
static void writeDescription(const CommentInfo &I, raw_ostream &OS) {
@@ -104,7 +114,18 @@ static void writeDescription(const CommentInfo &I, raw_ostream &OS) {
104114
}
105115
}
106116

107-
static void genMarkdown(const EnumInfo &I, llvm::raw_ostream &OS) {
117+
static void writeNameLink(const StringRef &CurrentPath, const Reference &R,
118+
llvm::raw_ostream &OS) {
119+
llvm::SmallString<64> Path = R.getRelativeFilePath(CurrentPath);
120+
// Paths in Markdown use POSIX separators.
121+
llvm::sys::path::native(Path, llvm::sys::path::Style::posix);
122+
llvm::sys::path::append(Path, llvm::sys::path::Style::posix,
123+
R.getFileBaseName() + ".md");
124+
OS << "[" << R.Name << "](" << Path << ")";
125+
}
126+
127+
static void genMarkdown(const ClangDocContext &CDCtx, const EnumInfo &I,
128+
llvm::raw_ostream &OS) {
108129
if (I.Scoped)
109130
writeLine("| enum class " + I.Name + " |", OS);
110131
else
@@ -118,13 +139,14 @@ static void genMarkdown(const EnumInfo &I, llvm::raw_ostream &OS) {
118139
Members << "| " << N << " |\n";
119140
writeLine(Members.str(), OS);
120141
if (I.DefLoc)
121-
writeFileDefinition(I.DefLoc.getValue(), OS);
142+
writeFileDefinition(CDCtx, I.DefLoc.getValue(), OS);
122143

123144
for (const auto &C : I.Description)
124145
writeDescription(C, OS);
125146
}
126147

127-
static void genMarkdown(const FunctionInfo &I, llvm::raw_ostream &OS) {
148+
static void genMarkdown(const ClangDocContext &CDCtx, const FunctionInfo &I,
149+
llvm::raw_ostream &OS) {
128150
std::string Buffer;
129151
llvm::raw_string_ostream Stream(Buffer);
130152
bool First = true;
@@ -145,13 +167,14 @@ static void genMarkdown(const FunctionInfo &I, llvm::raw_ostream &OS) {
145167
Stream.str() + ")"),
146168
OS);
147169
if (I.DefLoc)
148-
writeFileDefinition(I.DefLoc.getValue(), OS);
170+
writeFileDefinition(CDCtx, I.DefLoc.getValue(), OS);
149171

150172
for (const auto &C : I.Description)
151173
writeDescription(C, OS);
152174
}
153175

154-
static void genMarkdown(const NamespaceInfo &I, llvm::raw_ostream &OS) {
176+
static void genMarkdown(const ClangDocContext &CDCtx, const NamespaceInfo &I,
177+
llvm::raw_ostream &OS) {
155178
if (I.Name == "")
156179
writeHeader("Global Namespace", 1, OS);
157180
else
@@ -164,36 +187,47 @@ static void genMarkdown(const NamespaceInfo &I, llvm::raw_ostream &OS) {
164187
writeNewLine(OS);
165188
}
166189

190+
llvm::SmallString<64> BasePath = I.getRelativeFilePath("");
191+
167192
if (!I.ChildNamespaces.empty()) {
168193
writeHeader("Namespaces", 2, OS);
169-
for (const auto &R : I.ChildNamespaces)
170-
writeLine(R.Name, OS);
194+
for (const auto &R : I.ChildNamespaces) {
195+
OS << "* ";
196+
writeNameLink(BasePath, R, OS);
197+
OS << "\n";
198+
}
171199
writeNewLine(OS);
172200
}
201+
173202
if (!I.ChildRecords.empty()) {
174203
writeHeader("Records", 2, OS);
175-
for (const auto &R : I.ChildRecords)
176-
writeLine(R.Name, OS);
204+
for (const auto &R : I.ChildRecords) {
205+
OS << "* ";
206+
writeNameLink(BasePath, R, OS);
207+
OS << "\n";
208+
}
177209
writeNewLine(OS);
178210
}
211+
179212
if (!I.ChildFunctions.empty()) {
180213
writeHeader("Functions", 2, OS);
181214
for (const auto &F : I.ChildFunctions)
182-
genMarkdown(F, OS);
215+
genMarkdown(CDCtx, F, OS);
183216
writeNewLine(OS);
184217
}
185218
if (!I.ChildEnums.empty()) {
186219
writeHeader("Enums", 2, OS);
187220
for (const auto &E : I.ChildEnums)
188-
genMarkdown(E, OS);
221+
genMarkdown(CDCtx, E, OS);
189222
writeNewLine(OS);
190223
}
191224
}
192225

193-
static void genMarkdown(const RecordInfo &I, llvm::raw_ostream &OS) {
226+
static void genMarkdown(const ClangDocContext &CDCtx, const RecordInfo &I,
227+
llvm::raw_ostream &OS) {
194228
writeHeader(getTagType(I.TagType) + " " + I.Name, 1, OS);
195229
if (I.DefLoc)
196-
writeFileDefinition(I.DefLoc.getValue(), OS);
230+
writeFileDefinition(CDCtx, I.DefLoc.getValue(), OS);
197231

198232
if (!I.Description.empty()) {
199233
for (const auto &C : I.Description)
@@ -234,24 +268,94 @@ static void genMarkdown(const RecordInfo &I, llvm::raw_ostream &OS) {
234268
if (!I.ChildFunctions.empty()) {
235269
writeHeader("Functions", 2, OS);
236270
for (const auto &F : I.ChildFunctions)
237-
genMarkdown(F, OS);
271+
genMarkdown(CDCtx, F, OS);
238272
writeNewLine(OS);
239273
}
240274
if (!I.ChildEnums.empty()) {
241275
writeHeader("Enums", 2, OS);
242276
for (const auto &E : I.ChildEnums)
243-
genMarkdown(E, OS);
277+
genMarkdown(CDCtx, E, OS);
244278
writeNewLine(OS);
245279
}
246280
}
247281

282+
static void serializeReference(llvm::raw_fd_ostream &OS, Index &I, int Level) {
283+
// Write out the heading level starting at ##
284+
OS << "##" << std::string(Level, '#') << " ";
285+
writeNameLink("", I, OS);
286+
OS << "\n";
287+
}
288+
289+
static llvm::Error serializeIndex(ClangDocContext &CDCtx) {
290+
std::error_code FileErr;
291+
llvm::SmallString<128> FilePath;
292+
llvm::sys::path::native(CDCtx.OutDirectory, FilePath);
293+
llvm::sys::path::append(FilePath, "all_files.md");
294+
llvm::raw_fd_ostream OS(FilePath, FileErr, llvm::sys::fs::OF_None);
295+
if (FileErr)
296+
return llvm::createStringError(llvm::inconvertibleErrorCode(),
297+
"error creating index file: " +
298+
FileErr.message());
299+
300+
CDCtx.Idx.sort();
301+
OS << "# All Files";
302+
if (!CDCtx.ProjectName.empty())
303+
OS << " for " << CDCtx.ProjectName;
304+
OS << "\n\n";
305+
306+
for (auto C : CDCtx.Idx.Children)
307+
serializeReference(OS, C, 0);
308+
309+
return llvm::Error::success();
310+
}
311+
312+
static llvm::Error genIndex(ClangDocContext &CDCtx) {
313+
std::error_code FileErr;
314+
llvm::SmallString<128> FilePath;
315+
llvm::sys::path::native(CDCtx.OutDirectory, FilePath);
316+
llvm::sys::path::append(FilePath, "index.md");
317+
llvm::raw_fd_ostream OS(FilePath, FileErr, llvm::sys::fs::OF_None);
318+
if (FileErr)
319+
return llvm::createStringError(llvm::inconvertibleErrorCode(),
320+
"error creating index file: " +
321+
FileErr.message());
322+
CDCtx.Idx.sort();
323+
OS << "# " << CDCtx.ProjectName << " C/C++ Reference\n\n";
324+
for (auto C : CDCtx.Idx.Children) {
325+
if (!C.Children.empty()) {
326+
const char *Type;
327+
switch (C.RefType) {
328+
case InfoType::IT_namespace:
329+
Type = "Namespace";
330+
break;
331+
case InfoType::IT_record:
332+
Type = "Type";
333+
break;
334+
case InfoType::IT_enum:
335+
Type = "Enum";
336+
break;
337+
case InfoType::IT_function:
338+
Type = "Function";
339+
break;
340+
case InfoType::IT_default:
341+
Type = "Other";
342+
}
343+
OS << "* " << Type << ": [" << C.Name << "](";
344+
if (!C.Path.empty())
345+
OS << C.Path << "/";
346+
OS << C.Name << ")\n";
347+
}
348+
}
349+
return llvm::Error::success();
350+
}
248351
/// Generator for Markdown documentation.
249352
class MDGenerator : public Generator {
250353
public:
251354
static const char *Format;
252355

253356
llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS,
254357
const ClangDocContext &CDCtx) override;
358+
llvm::Error createResources(ClangDocContext &CDCtx) override;
255359
};
256360

257361
const char *MDGenerator::Format = "md";
@@ -260,16 +364,16 @@ llvm::Error MDGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
260364
const ClangDocContext &CDCtx) {
261365
switch (I->IT) {
262366
case InfoType::IT_namespace:
263-
genMarkdown(*static_cast<clang::doc::NamespaceInfo *>(I), OS);
367+
genMarkdown(CDCtx, *static_cast<clang::doc::NamespaceInfo *>(I), OS);
264368
break;
265369
case InfoType::IT_record:
266-
genMarkdown(*static_cast<clang::doc::RecordInfo *>(I), OS);
370+
genMarkdown(CDCtx, *static_cast<clang::doc::RecordInfo *>(I), OS);
267371
break;
268372
case InfoType::IT_enum:
269-
genMarkdown(*static_cast<clang::doc::EnumInfo *>(I), OS);
373+
genMarkdown(CDCtx, *static_cast<clang::doc::EnumInfo *>(I), OS);
270374
break;
271375
case InfoType::IT_function:
272-
genMarkdown(*static_cast<clang::doc::FunctionInfo *>(I), OS);
376+
genMarkdown(CDCtx, *static_cast<clang::doc::FunctionInfo *>(I), OS);
273377
break;
274378
case InfoType::IT_default:
275379
return createStringError(llvm::inconvertibleErrorCode(),
@@ -278,11 +382,25 @@ llvm::Error MDGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
278382
return llvm::Error::success();
279383
}
280384

385+
llvm::Error MDGenerator::createResources(ClangDocContext &CDCtx) {
386+
// Write an all_files.md
387+
auto Err = serializeIndex(CDCtx);
388+
if (Err)
389+
return Err;
390+
391+
// Generate the index page.
392+
Err = genIndex(CDCtx);
393+
if (Err)
394+
return Err;
395+
396+
return llvm::Error::success();
397+
}
398+
281399
static GeneratorRegistry::Add<MDGenerator> MD(MDGenerator::Format,
282400
"Generator for MD output.");
283401

284-
// This anchor is used to force the linker to link in the generated object file
285-
// and thus register the generator.
402+
// This anchor is used to force the linker to link in the generated object
403+
// file and thus register the generator.
286404
volatile int MDGeneratorAnchorSource = 0;
287405

288406
} // namespace doc

clang-tools-extra/clang-doc/Representation.cpp

+46
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,52 @@ mergeInfos(std::vector<std::unique_ptr<Info>> &Values) {
114114
}
115115
}
116116

117+
static llvm::SmallString<64>
118+
calculateRelativeFilePath(const InfoType &Type, const StringRef &Path,
119+
const StringRef &Name, const StringRef &CurrentPath) {
120+
llvm::SmallString<64> FilePath;
121+
122+
if (CurrentPath != Path) {
123+
// iterate back to the top
124+
for (llvm::sys::path::const_iterator I =
125+
llvm::sys::path::begin(CurrentPath);
126+
I != llvm::sys::path::end(CurrentPath); ++I)
127+
llvm::sys::path::append(FilePath, "..");
128+
llvm::sys::path::append(FilePath, Path);
129+
}
130+
131+
// Namespace references have a Path to the parent namespace, but
132+
// the file is actually in the subdirectory for the namespace.
133+
if (Type == doc::InfoType::IT_namespace)
134+
llvm::sys::path::append(FilePath, Name);
135+
136+
return llvm::sys::path::relative_path(FilePath);
137+
}
138+
139+
llvm::SmallString<64>
140+
Reference::getRelativeFilePath(const StringRef &CurrentPath) const {
141+
return calculateRelativeFilePath(RefType, Path, Name, CurrentPath);
142+
}
143+
144+
llvm::SmallString<16> Reference::getFileBaseName() const {
145+
if (RefType == InfoType::IT_namespace)
146+
return llvm::SmallString<16>("index");
147+
148+
return Name;
149+
}
150+
151+
llvm::SmallString<64>
152+
Info::getRelativeFilePath(const StringRef &CurrentPath) const {
153+
return calculateRelativeFilePath(IT, Path, extractName(), CurrentPath);
154+
}
155+
156+
llvm::SmallString<16> Info::getFileBaseName() const {
157+
if (IT == InfoType::IT_namespace)
158+
return llvm::SmallString<16>("index");
159+
160+
return extractName();
161+
}
162+
117163
bool Reference::mergeable(const Reference &Other) {
118164
return RefType == Other.RefType && USR == Other.USR;
119165
}

0 commit comments

Comments
 (0)