brintos

brintos / llvm-project-archived public Read only

0
0
Text · 15.1 KiB · fcb75af Raw
509 lines · cpp
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-exception6//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/FormatVariadic.h"14#include "llvm/Support/Path.h"15#include "llvm/Support/raw_ostream.h"16#include <string>17 18using namespace llvm;19 20namespace clang {21namespace doc {22 23// Markdown generation24 25static std::string genItalic(const Twine &Text) {26  return "*" + Text.str() + "*";27}28 29static std::string genEmphasis(const Twine &Text) {30  return "**" + Text.str() + "**";31}32 33static std::string34genReferenceList(const llvm::SmallVectorImpl<Reference> &Refs) {35  std::string Buffer;36  llvm::raw_string_ostream Stream(Buffer);37  for (const auto &R : Refs) {38    if (&R != Refs.begin())39      Stream << ", ";40    Stream << R.Name;41  }42  return Stream.str();43}44 45static void writeLine(const Twine &Text, raw_ostream &OS) {46  OS << Text << "\n\n";47}48 49static void writeNewLine(raw_ostream &OS) { OS << "\n\n"; }50 51static void writeHeader(const Twine &Text, unsigned int Num, raw_ostream &OS) {52  OS << std::string(Num, '#') + " " + Text << "\n\n";53}54 55static void writeSourceFileRef(const ClangDocContext &CDCtx, const Location &L,56                               raw_ostream &OS) {57 58  if (!CDCtx.RepositoryUrl) {59    OS << "*Defined at " << L.Filename << "#"60       << std::to_string(L.StartLineNumber) << "*";61  } else {62 63    OS << formatv("*Defined at [#{0}{1}{2}](#{0}{1}{3})*",64                  CDCtx.RepositoryLinePrefix.value_or(""), L.StartLineNumber,65                  L.Filename, *CDCtx.RepositoryUrl);66  }67  OS << "\n\n";68}69 70static void maybeWriteSourceFileRef(llvm::raw_ostream &OS,71                                    const ClangDocContext &CDCtx,72                                    const std::optional<Location> &DefLoc) {73  if (DefLoc)74    writeSourceFileRef(CDCtx, *DefLoc, OS);75}76 77static void writeDescription(const CommentInfo &I, raw_ostream &OS) {78  switch (I.Kind) {79  case CommentKind::CK_FullComment:80    for (const auto &Child : I.Children)81      writeDescription(*Child, OS);82    break;83 84  case CommentKind::CK_ParagraphComment:85    for (const auto &Child : I.Children)86      writeDescription(*Child, OS);87    writeNewLine(OS);88    break;89 90  case CommentKind::CK_BlockCommandComment:91    OS << genEmphasis(I.Name);92    for (const auto &Child : I.Children)93      writeDescription(*Child, OS);94    break;95 96  case CommentKind::CK_InlineCommandComment:97    OS << genEmphasis(I.Name) << " " << I.Text;98    break;99 100  case CommentKind::CK_ParamCommandComment:101  case CommentKind::CK_TParamCommandComment: {102    std::string Direction = I.Explicit ? (" " + I.Direction).str() : "";103    OS << genEmphasis(I.ParamName) << I.Text << Direction;104    for (const auto &Child : I.Children)105      writeDescription(*Child, OS);106    break;107  }108 109  case CommentKind::CK_VerbatimBlockComment:110    for (const auto &Child : I.Children)111      writeDescription(*Child, OS);112    break;113 114  case CommentKind::CK_VerbatimBlockLineComment:115  case CommentKind::CK_VerbatimLineComment:116    OS << I.Text;117    writeNewLine(OS);118    break;119 120  case CommentKind::CK_HTMLStartTagComment: {121    if (I.AttrKeys.size() != I.AttrValues.size())122      return;123    std::string Buffer;124    llvm::raw_string_ostream Attrs(Buffer);125    for (unsigned Idx = 0; Idx < I.AttrKeys.size(); ++Idx)126      Attrs << " \"" << I.AttrKeys[Idx] << "=" << I.AttrValues[Idx] << "\"";127 128    std::string CloseTag = I.SelfClosing ? "/>" : ">";129    writeLine("<" + I.Name + Attrs.str() + CloseTag, OS);130    break;131  }132 133  case CommentKind::CK_HTMLEndTagComment:134    writeLine("</" + I.Name + ">", OS);135    break;136 137  case CommentKind::CK_TextComment:138    OS << I.Text;139    break;140 141  case CommentKind::CK_Unknown:142    OS << "Unknown comment kind: " << static_cast<int>(I.Kind) << ".\n\n";143    break;144  }145}146 147static void writeNameLink(const StringRef &CurrentPath, const Reference &R,148                          llvm::raw_ostream &OS) {149  llvm::SmallString<64> Path = R.getRelativeFilePath(CurrentPath);150  // Paths in Markdown use POSIX separators.151  llvm::sys::path::native(Path, llvm::sys::path::Style::posix);152  llvm::sys::path::append(Path, llvm::sys::path::Style::posix,153                          R.getFileBaseName() + ".md");154  OS << "[" << R.Name << "](" << Path << ")";155}156 157static void genMarkdown(const ClangDocContext &CDCtx, const EnumInfo &I,158                        llvm::raw_ostream &OS) {159  if (I.Scoped)160    writeLine("| enum class " + I.Name + " |", OS);161  else162    writeLine("| enum " + I.Name + " |", OS);163  writeLine("--", OS);164 165  std::string Buffer;166  llvm::raw_string_ostream Members(Buffer);167  if (!I.Members.empty())168    for (const auto &N : I.Members)169      Members << "| " << N.Name << " |\n";170  writeLine(Members.str(), OS);171 172  maybeWriteSourceFileRef(OS, CDCtx, I.DefLoc);173 174  for (const auto &C : I.Description)175    writeDescription(C, OS);176}177 178static void genMarkdown(const ClangDocContext &CDCtx, const FunctionInfo &I,179                        llvm::raw_ostream &OS) {180  std::string Buffer;181  llvm::raw_string_ostream Stream(Buffer);182  bool First = true;183  for (const auto &N : I.Params) {184    if (!First)185      Stream << ", ";186    Stream << N.Type.QualName + " " + N.Name;187    First = false;188  }189  writeHeader(I.Name, 3, OS);190  StringRef Access = getAccessSpelling(I.Access);191  writeLine(genItalic(Twine(Access) + (!Access.empty() ? " " : "") +192                      (I.IsStatic ? "static " : "") +193                      I.ReturnType.Type.QualName.str() + " " + I.Name.str() +194                      "(" + Twine(Stream.str()) + ")"),195            OS);196 197  maybeWriteSourceFileRef(OS, CDCtx, I.DefLoc);198 199  for (const auto &C : I.Description)200    writeDescription(C, OS);201}202 203static void genMarkdown(const ClangDocContext &CDCtx, const NamespaceInfo &I,204                        llvm::raw_ostream &OS) {205  if (I.Name == "")206    writeHeader("Global Namespace", 1, OS);207  else208    writeHeader("namespace " + I.Name, 1, OS);209  writeNewLine(OS);210 211  if (!I.Description.empty()) {212    for (const auto &C : I.Description)213      writeDescription(C, OS);214    writeNewLine(OS);215  }216 217  llvm::SmallString<64> BasePath = I.getRelativeFilePath("");218 219  if (!I.Children.Namespaces.empty()) {220    writeHeader("Namespaces", 2, OS);221    for (const auto &R : I.Children.Namespaces) {222      OS << "* ";223      writeNameLink(BasePath, R, OS);224      OS << "\n";225    }226    writeNewLine(OS);227  }228 229  if (!I.Children.Records.empty()) {230    writeHeader("Records", 2, OS);231    for (const auto &R : I.Children.Records) {232      OS << "* ";233      writeNameLink(BasePath, R, OS);234      OS << "\n";235    }236    writeNewLine(OS);237  }238 239  if (!I.Children.Functions.empty()) {240    writeHeader("Functions", 2, OS);241    for (const auto &F : I.Children.Functions)242      genMarkdown(CDCtx, F, OS);243    writeNewLine(OS);244  }245  if (!I.Children.Enums.empty()) {246    writeHeader("Enums", 2, OS);247    for (const auto &E : I.Children.Enums)248      genMarkdown(CDCtx, E, OS);249    writeNewLine(OS);250  }251}252 253static void genMarkdown(const ClangDocContext &CDCtx, const RecordInfo &I,254                        llvm::raw_ostream &OS) {255  writeHeader(getTagType(I.TagType) + " " + I.Name, 1, OS);256 257  maybeWriteSourceFileRef(OS, CDCtx, I.DefLoc);258 259  if (!I.Description.empty()) {260    for (const auto &C : I.Description)261      writeDescription(C, OS);262    writeNewLine(OS);263  }264 265  std::string Parents = genReferenceList(I.Parents);266  std::string VParents = genReferenceList(I.VirtualParents);267  if (!Parents.empty() || !VParents.empty()) {268    if (Parents.empty())269      writeLine("Inherits from " + VParents, OS);270    else if (VParents.empty())271      writeLine("Inherits from " + Parents, OS);272    else273      writeLine("Inherits from " + Parents + ", " + VParents, OS);274    writeNewLine(OS);275  }276 277  if (!I.Members.empty()) {278    writeHeader("Members", 2, OS);279    for (const auto &Member : I.Members) {280      StringRef Access = getAccessSpelling(Member.Access);281      writeLine(Twine(Access) + (Access.empty() ? "" : " ") +282                    (Member.IsStatic ? "static " : "") +283                    Member.Type.Name.str() + " " + Member.Name.str(),284                OS);285    }286    writeNewLine(OS);287  }288 289  if (!I.Children.Records.empty()) {290    writeHeader("Records", 2, OS);291    for (const auto &R : I.Children.Records)292      writeLine(R.Name, OS);293    writeNewLine(OS);294  }295  if (!I.Children.Functions.empty()) {296    writeHeader("Functions", 2, OS);297    for (const auto &F : I.Children.Functions)298      genMarkdown(CDCtx, F, OS);299    writeNewLine(OS);300  }301  if (!I.Children.Enums.empty()) {302    writeHeader("Enums", 2, OS);303    for (const auto &E : I.Children.Enums)304      genMarkdown(CDCtx, E, OS);305    writeNewLine(OS);306  }307}308 309static void genMarkdown(const ClangDocContext &CDCtx, const TypedefInfo &I,310                        llvm::raw_ostream &OS) {311  // TODO support typedefs in markdown.312}313 314static void serializeReference(llvm::raw_fd_ostream &OS, Index &I, int Level) {315  // Write out the heading level starting at ##316  OS << "##" << std::string(Level, '#') << " ";317  writeNameLink("", I, OS);318  OS << "\n";319}320 321static llvm::Error serializeIndex(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, "all_files.md");326  llvm::raw_fd_ostream OS(FilePath, FileErr, llvm::sys::fs::OF_Text);327  if (FileErr)328    return llvm::createStringError(llvm::inconvertibleErrorCode(),329                                   "error creating index file: " +330                                       FileErr.message());331 332  CDCtx.Idx.sort();333  OS << "# All Files";334  if (!CDCtx.ProjectName.empty())335    OS << " for " << CDCtx.ProjectName;336  OS << "\n\n";337 338  for (auto C : CDCtx.Idx.Children)339    serializeReference(OS, C, 0);340 341  return llvm::Error::success();342}343 344static llvm::Error genIndex(ClangDocContext &CDCtx) {345  std::error_code FileErr;346  llvm::SmallString<128> FilePath;347  llvm::sys::path::native(CDCtx.OutDirectory, FilePath);348  llvm::sys::path::append(FilePath, "index.md");349  llvm::raw_fd_ostream OS(FilePath, FileErr, llvm::sys::fs::OF_Text);350  if (FileErr)351    return llvm::createStringError(llvm::inconvertibleErrorCode(),352                                   "error creating index file: " +353                                       FileErr.message());354  CDCtx.Idx.sort();355  OS << "# " << CDCtx.ProjectName << " C/C++ Reference\n\n";356  for (auto C : CDCtx.Idx.Children) {357    if (!C.Children.empty()) {358      const char *Type;359      switch (C.RefType) {360      case InfoType::IT_namespace:361        Type = "Namespace";362        break;363      case InfoType::IT_record:364        Type = "Type";365        break;366      case InfoType::IT_enum:367        Type = "Enum";368        break;369      case InfoType::IT_function:370        Type = "Function";371        break;372      case InfoType::IT_typedef:373        Type = "Typedef";374        break;375      case InfoType::IT_concept:376        Type = "Concept";377        break;378      case InfoType::IT_variable:379        Type = "Variable";380        break;381      case InfoType::IT_friend:382        Type = "Friend";383        break;384      case InfoType::IT_default:385        Type = "Other";386      }387      OS << "* " << Type << ": [" << C.Name << "](";388      if (!C.Path.empty())389        OS << C.Path << "/";390      OS << C.Name << ")\n";391    }392  }393  return llvm::Error::success();394}395 396/// Generator for Markdown documentation.397class MDGenerator : public Generator {398public:399  static const char *Format;400 401  llvm::Error generateDocumentation(402      StringRef RootDir, llvm::StringMap<std::unique_ptr<doc::Info>> Infos,403      const ClangDocContext &CDCtx, std::string DirName) override;404  llvm::Error createResources(ClangDocContext &CDCtx) override;405  llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS,406                                 const ClangDocContext &CDCtx) override;407};408 409const char *MDGenerator::Format = "md";410 411llvm::Error MDGenerator::generateDocumentation(412    StringRef RootDir, llvm::StringMap<std::unique_ptr<doc::Info>> Infos,413    const ClangDocContext &CDCtx, std::string DirName) {414  // Track which directories we already tried to create.415  llvm::StringSet<> CreatedDirs;416 417  // Collect all output by file name and create the necessary directories.418  llvm::StringMap<std::vector<doc::Info *>> FileToInfos;419  for (const auto &Group : Infos) {420    doc::Info *Info = Group.getValue().get();421 422    llvm::SmallString<128> Path;423    llvm::sys::path::native(RootDir, Path);424    llvm::sys::path::append(Path, Info->getRelativeFilePath(""));425    if (!CreatedDirs.contains(Path)) {426      if (std::error_code Err = llvm::sys::fs::create_directories(Path);427          Err != std::error_code()) {428        return llvm::createStringError(Err, "Failed to create directory '%s'.",429                                       Path.c_str());430      }431      CreatedDirs.insert(Path);432    }433 434    llvm::sys::path::append(Path, Info->getFileBaseName() + ".md");435    FileToInfos[Path].push_back(Info);436  }437 438  for (const auto &Group : FileToInfos) {439    std::error_code FileErr;440    llvm::raw_fd_ostream InfoOS(Group.getKey(), FileErr,441                                llvm::sys::fs::OF_Text);442    if (FileErr) {443      return llvm::createStringError(FileErr, "Error opening file '%s'",444                                     Group.getKey().str().c_str());445    }446 447    for (const auto &Info : Group.getValue()) {448      if (llvm::Error Err = generateDocForInfo(Info, InfoOS, CDCtx)) {449        return Err;450      }451    }452  }453 454  return llvm::Error::success();455}456 457llvm::Error MDGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,458                                            const ClangDocContext &CDCtx) {459  switch (I->IT) {460  case InfoType::IT_namespace:461    genMarkdown(CDCtx, *static_cast<clang::doc::NamespaceInfo *>(I), OS);462    break;463  case InfoType::IT_record:464    genMarkdown(CDCtx, *static_cast<clang::doc::RecordInfo *>(I), OS);465    break;466  case InfoType::IT_enum:467    genMarkdown(CDCtx, *static_cast<clang::doc::EnumInfo *>(I), OS);468    break;469  case InfoType::IT_function:470    genMarkdown(CDCtx, *static_cast<clang::doc::FunctionInfo *>(I), OS);471    break;472  case InfoType::IT_typedef:473    genMarkdown(CDCtx, *static_cast<clang::doc::TypedefInfo *>(I), OS);474    break;475  case InfoType::IT_concept:476  case InfoType::IT_variable:477  case InfoType::IT_friend:478    break;479  case InfoType::IT_default:480    return createStringError(llvm::inconvertibleErrorCode(),481                             "unexpected InfoType");482  }483  return llvm::Error::success();484}485 486llvm::Error MDGenerator::createResources(ClangDocContext &CDCtx) {487  // Write an all_files.md488  auto Err = serializeIndex(CDCtx);489  if (Err)490    return Err;491 492  // Generate the index page.493  Err = genIndex(CDCtx);494  if (Err)495    return Err;496 497  return llvm::Error::success();498}499 500static GeneratorRegistry::Add<MDGenerator> MD(MDGenerator::Format,501                                              "Generator for MD output.");502 503// This anchor is used to force the linker to link in the generated object504// file and thus register the generator.505volatile int MDGeneratorAnchorSource = 0;506 507} // namespace doc508} // namespace clang509