llvm-project

Форк
0
473 строки · 14.7 Кб
1
//===-- MDGenerator.cpp - Markdown Generator --------------------*- C++ -*-===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8

9
#include "Generators.h"
10
#include "Representation.h"
11
#include "llvm/ADT/StringRef.h"
12
#include "llvm/Support/FileSystem.h"
13
#include "llvm/Support/Path.h"
14
#include <string>
15

16
using namespace llvm;
17

18
namespace clang {
19
namespace doc {
20

21
// Markdown generation
22

23
static std::string genItalic(const Twine &Text) {
24
  return "*" + Text.str() + "*";
25
}
26

27
static std::string genEmphasis(const Twine &Text) {
28
  return "**" + Text.str() + "**";
29
}
30

31
static std::string
32
genReferenceList(const llvm::SmallVectorImpl<Reference> &Refs) {
33
  std::string Buffer;
34
  llvm::raw_string_ostream Stream(Buffer);
35
  for (const auto &R : Refs) {
36
    if (&R != Refs.begin())
37
      Stream << ", ";
38
    Stream << R.Name;
39
  }
40
  return Stream.str();
41
}
42

43
static void writeLine(const Twine &Text, raw_ostream &OS) {
44
  OS << Text << "\n\n";
45
}
46

47
static void writeNewLine(raw_ostream &OS) { OS << "\n\n"; }
48

49
static void writeHeader(const Twine &Text, unsigned int Num, raw_ostream &OS) {
50
  OS << std::string(Num, '#') + " " + Text << "\n\n";
51
}
52

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}
62
       << llvm::sys::path::relative_path(L.Filename) << "#"
63
       << std::to_string(L.LineNumber) << ")"
64
       << "*";
65
  }
66
  OS << "\n\n";
67
}
68

69
static void writeDescription(const CommentInfo &I, raw_ostream &OS) {
70
  if (I.Kind == "FullComment") {
71
    for (const auto &Child : I.Children)
72
      writeDescription(*Child, OS);
73
  } else if (I.Kind == "ParagraphComment") {
74
    for (const auto &Child : I.Children)
75
      writeDescription(*Child, OS);
76
    writeNewLine(OS);
77
  } else if (I.Kind == "BlockCommandComment") {
78
    OS << genEmphasis(I.Name);
79
    for (const auto &Child : I.Children)
80
      writeDescription(*Child, OS);
81
  } else if (I.Kind == "InlineCommandComment") {
82
    OS << genEmphasis(I.Name) << " " << I.Text;
83
  } else if (I.Kind == "ParamCommandComment") {
84
    std::string Direction = I.Explicit ? (" " + I.Direction).str() : "";
85
    OS << genEmphasis(I.ParamName) << I.Text << Direction;
86
    for (const auto &Child : I.Children)
87
      writeDescription(*Child, OS);
88
  } else if (I.Kind == "TParamCommandComment") {
89
    std::string Direction = I.Explicit ? (" " + I.Direction).str() : "";
90
    OS << genEmphasis(I.ParamName) << I.Text << Direction;
91
    for (const auto &Child : I.Children)
92
      writeDescription(*Child, OS);
93
  } else if (I.Kind == "VerbatimBlockComment") {
94
    for (const auto &Child : I.Children)
95
      writeDescription(*Child, OS);
96
  } else if (I.Kind == "VerbatimBlockLineComment") {
97
    OS << I.Text;
98
    writeNewLine(OS);
99
  } else if (I.Kind == "VerbatimLineComment") {
100
    OS << I.Text;
101
    writeNewLine(OS);
102
  } else if (I.Kind == "HTMLStartTagComment") {
103
    if (I.AttrKeys.size() != I.AttrValues.size())
104
      return;
105
    std::string Buffer;
106
    llvm::raw_string_ostream Attrs(Buffer);
107
    for (unsigned Idx = 0; Idx < I.AttrKeys.size(); ++Idx)
108
      Attrs << " \"" << I.AttrKeys[Idx] << "=" << I.AttrValues[Idx] << "\"";
109

110
    std::string CloseTag = I.SelfClosing ? "/>" : ">";
111
    writeLine("<" + I.Name + Attrs.str() + CloseTag, OS);
112
  } else if (I.Kind == "HTMLEndTagComment") {
113
    writeLine("</" + I.Name + ">", OS);
114
  } else if (I.Kind == "TextComment") {
115
    OS << I.Text;
116
  } else {
117
    OS << "Unknown comment kind: " << I.Kind << ".\n\n";
118
  }
119
}
120

121
static void writeNameLink(const StringRef &CurrentPath, const Reference &R,
122
                          llvm::raw_ostream &OS) {
123
  llvm::SmallString<64> Path = R.getRelativeFilePath(CurrentPath);
124
  // Paths in Markdown use POSIX separators.
125
  llvm::sys::path::native(Path, llvm::sys::path::Style::posix);
126
  llvm::sys::path::append(Path, llvm::sys::path::Style::posix,
127
                          R.getFileBaseName() + ".md");
128
  OS << "[" << R.Name << "](" << Path << ")";
129
}
130

131
static void genMarkdown(const ClangDocContext &CDCtx, const EnumInfo &I,
132
                        llvm::raw_ostream &OS) {
133
  if (I.Scoped)
134
    writeLine("| enum class " + I.Name + " |", OS);
135
  else
136
    writeLine("| enum " + I.Name + " |", OS);
137
  writeLine("--", OS);
138

139
  std::string Buffer;
140
  llvm::raw_string_ostream Members(Buffer);
141
  if (!I.Members.empty())
142
    for (const auto &N : I.Members)
143
      Members << "| " << N.Name << " |\n";
144
  writeLine(Members.str(), OS);
145
  if (I.DefLoc)
146
    writeFileDefinition(CDCtx, *I.DefLoc, OS);
147

148
  for (const auto &C : I.Description)
149
    writeDescription(C, OS);
150
}
151

152
static void genMarkdown(const ClangDocContext &CDCtx, const FunctionInfo &I,
153
                        llvm::raw_ostream &OS) {
154
  std::string Buffer;
155
  llvm::raw_string_ostream Stream(Buffer);
156
  bool First = true;
157
  for (const auto &N : I.Params) {
158
    if (!First)
159
      Stream << ", ";
160
    Stream << N.Type.Name + " " + N.Name;
161
    First = false;
162
  }
163
  writeHeader(I.Name, 3, OS);
164
  std::string Access = getAccessSpelling(I.Access).str();
165
  if (Access != "")
166
    writeLine(genItalic(Access + " " + I.ReturnType.Type.Name + " " + I.Name +
167
                        "(" + Stream.str() + ")"),
168
              OS);
169
  else
170
    writeLine(genItalic(I.ReturnType.Type.Name + " " + I.Name + "(" +
171
                        Stream.str() + ")"),
172
              OS);
173
  if (I.DefLoc)
174
    writeFileDefinition(CDCtx, *I.DefLoc, OS);
175

176
  for (const auto &C : I.Description)
177
    writeDescription(C, OS);
178
}
179

180
static void genMarkdown(const ClangDocContext &CDCtx, const NamespaceInfo &I,
181
                        llvm::raw_ostream &OS) {
182
  if (I.Name == "")
183
    writeHeader("Global Namespace", 1, OS);
184
  else
185
    writeHeader("namespace " + I.Name, 1, OS);
186
  writeNewLine(OS);
187

188
  if (!I.Description.empty()) {
189
    for (const auto &C : I.Description)
190
      writeDescription(C, OS);
191
    writeNewLine(OS);
192
  }
193

194
  llvm::SmallString<64> BasePath = I.getRelativeFilePath("");
195

196
  if (!I.Children.Namespaces.empty()) {
197
    writeHeader("Namespaces", 2, OS);
198
    for (const auto &R : I.Children.Namespaces) {
199
      OS << "* ";
200
      writeNameLink(BasePath, R, OS);
201
      OS << "\n";
202
    }
203
    writeNewLine(OS);
204
  }
205

206
  if (!I.Children.Records.empty()) {
207
    writeHeader("Records", 2, OS);
208
    for (const auto &R : I.Children.Records) {
209
      OS << "* ";
210
      writeNameLink(BasePath, R, OS);
211
      OS << "\n";
212
    }
213
    writeNewLine(OS);
214
  }
215

216
  if (!I.Children.Functions.empty()) {
217
    writeHeader("Functions", 2, OS);
218
    for (const auto &F : I.Children.Functions)
219
      genMarkdown(CDCtx, F, OS);
220
    writeNewLine(OS);
221
  }
222
  if (!I.Children.Enums.empty()) {
223
    writeHeader("Enums", 2, OS);
224
    for (const auto &E : I.Children.Enums)
225
      genMarkdown(CDCtx, E, OS);
226
    writeNewLine(OS);
227
  }
228
}
229

230
static void genMarkdown(const ClangDocContext &CDCtx, const RecordInfo &I,
231
                        llvm::raw_ostream &OS) {
232
  writeHeader(getTagType(I.TagType) + " " + I.Name, 1, OS);
233
  if (I.DefLoc)
234
    writeFileDefinition(CDCtx, *I.DefLoc, OS);
235

236
  if (!I.Description.empty()) {
237
    for (const auto &C : I.Description)
238
      writeDescription(C, OS);
239
    writeNewLine(OS);
240
  }
241

242
  std::string Parents = genReferenceList(I.Parents);
243
  std::string VParents = genReferenceList(I.VirtualParents);
244
  if (!Parents.empty() || !VParents.empty()) {
245
    if (Parents.empty())
246
      writeLine("Inherits from " + VParents, OS);
247
    else if (VParents.empty())
248
      writeLine("Inherits from " + Parents, OS);
249
    else
250
      writeLine("Inherits from " + Parents + ", " + VParents, OS);
251
    writeNewLine(OS);
252
  }
253

254
  if (!I.Members.empty()) {
255
    writeHeader("Members", 2, OS);
256
    for (const auto &Member : I.Members) {
257
      std::string Access = getAccessSpelling(Member.Access).str();
258
      if (Access != "")
259
        writeLine(Access + " " + Member.Type.Name + " " + Member.Name, OS);
260
      else
261
        writeLine(Member.Type.Name + " " + Member.Name, OS);
262
    }
263
    writeNewLine(OS);
264
  }
265

266
  if (!I.Children.Records.empty()) {
267
    writeHeader("Records", 2, OS);
268
    for (const auto &R : I.Children.Records)
269
      writeLine(R.Name, OS);
270
    writeNewLine(OS);
271
  }
272
  if (!I.Children.Functions.empty()) {
273
    writeHeader("Functions", 2, OS);
274
    for (const auto &F : I.Children.Functions)
275
      genMarkdown(CDCtx, F, OS);
276
    writeNewLine(OS);
277
  }
278
  if (!I.Children.Enums.empty()) {
279
    writeHeader("Enums", 2, OS);
280
    for (const auto &E : I.Children.Enums)
281
      genMarkdown(CDCtx, E, OS);
282
    writeNewLine(OS);
283
  }
284
}
285

286
static void genMarkdown(const ClangDocContext &CDCtx, const TypedefInfo &I,
287
                        llvm::raw_ostream &OS) {
288
  // TODO support typedefs in markdown.
289
}
290

291
static void serializeReference(llvm::raw_fd_ostream &OS, Index &I, int Level) {
292
  // Write out the heading level starting at ##
293
  OS << "##" << std::string(Level, '#') << " ";
294
  writeNameLink("", I, OS);
295
  OS << "\n";
296
}
297

298
static llvm::Error serializeIndex(ClangDocContext &CDCtx) {
299
  std::error_code FileErr;
300
  llvm::SmallString<128> FilePath;
301
  llvm::sys::path::native(CDCtx.OutDirectory, FilePath);
302
  llvm::sys::path::append(FilePath, "all_files.md");
303
  llvm::raw_fd_ostream OS(FilePath, FileErr, llvm::sys::fs::OF_None);
304
  if (FileErr)
305
    return llvm::createStringError(llvm::inconvertibleErrorCode(),
306
                                   "error creating index file: " +
307
                                       FileErr.message());
308

309
  CDCtx.Idx.sort();
310
  OS << "# All Files";
311
  if (!CDCtx.ProjectName.empty())
312
    OS << " for " << CDCtx.ProjectName;
313
  OS << "\n\n";
314

315
  for (auto C : CDCtx.Idx.Children)
316
    serializeReference(OS, C, 0);
317

318
  return llvm::Error::success();
319
}
320

321
static llvm::Error genIndex(ClangDocContext &CDCtx) {
322
  std::error_code FileErr;
323
  llvm::SmallString<128> FilePath;
324
  llvm::sys::path::native(CDCtx.OutDirectory, FilePath);
325
  llvm::sys::path::append(FilePath, "index.md");
326
  llvm::raw_fd_ostream OS(FilePath, FileErr, llvm::sys::fs::OF_None);
327
  if (FileErr)
328
    return llvm::createStringError(llvm::inconvertibleErrorCode(),
329
                                   "error creating index file: " +
330
                                       FileErr.message());
331
  CDCtx.Idx.sort();
332
  OS << "# " << CDCtx.ProjectName << " C/C++ Reference\n\n";
333
  for (auto C : CDCtx.Idx.Children) {
334
    if (!C.Children.empty()) {
335
      const char *Type;
336
      switch (C.RefType) {
337
      case InfoType::IT_namespace:
338
        Type = "Namespace";
339
        break;
340
      case InfoType::IT_record:
341
        Type = "Type";
342
        break;
343
      case InfoType::IT_enum:
344
        Type = "Enum";
345
        break;
346
      case InfoType::IT_function:
347
        Type = "Function";
348
        break;
349
      case InfoType::IT_typedef:
350
        Type = "Typedef";
351
        break;
352
      case InfoType::IT_default:
353
        Type = "Other";
354
      }
355
      OS << "* " << Type << ": [" << C.Name << "](";
356
      if (!C.Path.empty())
357
        OS << C.Path << "/";
358
      OS << C.Name << ")\n";
359
    }
360
  }
361
  return llvm::Error::success();
362
}
363

364
/// Generator for Markdown documentation.
365
class MDGenerator : public Generator {
366
public:
367
  static const char *Format;
368

369
  llvm::Error generateDocs(StringRef RootDir,
370
                           llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
371
                           const ClangDocContext &CDCtx) override;
372
  llvm::Error createResources(ClangDocContext &CDCtx) override;
373
  llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS,
374
                                 const ClangDocContext &CDCtx) override;
375
};
376

377
const char *MDGenerator::Format = "md";
378

379
llvm::Error
380
MDGenerator::generateDocs(StringRef RootDir,
381
                          llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
382
                          const ClangDocContext &CDCtx) {
383
  // Track which directories we already tried to create.
384
  llvm::StringSet<> CreatedDirs;
385

386
  // Collect all output by file name and create the necessary directories.
387
  llvm::StringMap<std::vector<doc::Info *>> FileToInfos;
388
  for (const auto &Group : Infos) {
389
    doc::Info *Info = Group.getValue().get();
390

391
    llvm::SmallString<128> Path;
392
    llvm::sys::path::native(RootDir, Path);
393
    llvm::sys::path::append(Path, Info->getRelativeFilePath(""));
394
    if (!CreatedDirs.contains(Path)) {
395
      if (std::error_code Err = llvm::sys::fs::create_directories(Path);
396
          Err != std::error_code()) {
397
        return llvm::createStringError(Err, "Failed to create directory '%s'.",
398
                                       Path.c_str());
399
      }
400
      CreatedDirs.insert(Path);
401
    }
402

403
    llvm::sys::path::append(Path, Info->getFileBaseName() + ".md");
404
    FileToInfos[Path].push_back(Info);
405
  }
406

407
  for (const auto &Group : FileToInfos) {
408
    std::error_code FileErr;
409
    llvm::raw_fd_ostream InfoOS(Group.getKey(), FileErr,
410
                                llvm::sys::fs::OF_None);
411
    if (FileErr) {
412
      return llvm::createStringError(FileErr, "Error opening file '%s'",
413
                                     Group.getKey().str().c_str());
414
    }
415

416
    for (const auto &Info : Group.getValue()) {
417
      if (llvm::Error Err = generateDocForInfo(Info, InfoOS, CDCtx)) {
418
        return Err;
419
      }
420
    }
421
  }
422

423
  return llvm::Error::success();
424
}
425

426
llvm::Error MDGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
427
                                            const ClangDocContext &CDCtx) {
428
  switch (I->IT) {
429
  case InfoType::IT_namespace:
430
    genMarkdown(CDCtx, *static_cast<clang::doc::NamespaceInfo *>(I), OS);
431
    break;
432
  case InfoType::IT_record:
433
    genMarkdown(CDCtx, *static_cast<clang::doc::RecordInfo *>(I), OS);
434
    break;
435
  case InfoType::IT_enum:
436
    genMarkdown(CDCtx, *static_cast<clang::doc::EnumInfo *>(I), OS);
437
    break;
438
  case InfoType::IT_function:
439
    genMarkdown(CDCtx, *static_cast<clang::doc::FunctionInfo *>(I), OS);
440
    break;
441
  case InfoType::IT_typedef:
442
    genMarkdown(CDCtx, *static_cast<clang::doc::TypedefInfo *>(I), OS);
443
    break;
444
  case InfoType::IT_default:
445
    return createStringError(llvm::inconvertibleErrorCode(),
446
                             "unexpected InfoType");
447
  }
448
  return llvm::Error::success();
449
}
450

451
llvm::Error MDGenerator::createResources(ClangDocContext &CDCtx) {
452
  // Write an all_files.md
453
  auto Err = serializeIndex(CDCtx);
454
  if (Err)
455
    return Err;
456

457
  // Generate the index page.
458
  Err = genIndex(CDCtx);
459
  if (Err)
460
    return Err;
461

462
  return llvm::Error::success();
463
}
464

465
static GeneratorRegistry::Add<MDGenerator> MD(MDGenerator::Format,
466
                                              "Generator for MD output.");
467

468
// This anchor is used to force the linker to link in the generated object
469
// file and thus register the generator.
470
volatile int MDGeneratorAnchorSource = 0;
471

472
} // namespace doc
473
} // namespace clang
474

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.