Fixes some followup issues from clangd/clangd#529. `\note` and `\warning`: In the hover card, they are now displayed with heading and enclosing rulers. `\retval` commands: Each `\retval` command is now a bullet point under the return section of the hover card. Added a Markdown preprocessing step before parsing the documentation with the doxygen parser. This mainly replaces markdown code blocks with `@code...@endcode` commands.
206 lines
7.2 KiB
C++
206 lines
7.2 KiB
C++
//===--- SymbolDocumentation.h ==---------------------------------*- C++-*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Class to parse doxygen comments into a flat structure for consumption
|
|
// in e.g. Hover and Code Completion
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SYMBOLDOCUMENTATION_H
|
|
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SYMBOLDOCUMENTATION_H
|
|
|
|
#include "support/Markup.h"
|
|
#include "clang/AST/Comment.h"
|
|
#include "clang/AST/CommentLexer.h"
|
|
#include "clang/AST/CommentParser.h"
|
|
#include "clang/AST/CommentSema.h"
|
|
#include "clang/AST/CommentVisitor.h"
|
|
#include "clang/Basic/SourceManager.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include <string>
|
|
|
|
namespace clang {
|
|
namespace clangd {
|
|
|
|
class SymbolDocCommentVisitor
|
|
: public comments::ConstCommentVisitor<SymbolDocCommentVisitor> {
|
|
public:
|
|
SymbolDocCommentVisitor(comments::FullComment *FC,
|
|
const CommentOptions &CommentOpts)
|
|
: Traits(Allocator, CommentOpts), Allocator() {
|
|
if (!FC)
|
|
return;
|
|
|
|
for (auto *Block : FC->getBlocks()) {
|
|
visit(Block);
|
|
}
|
|
}
|
|
|
|
SymbolDocCommentVisitor(llvm::StringRef Documentation,
|
|
const CommentOptions &CommentOpts)
|
|
: Traits(Allocator, CommentOpts), Allocator() {
|
|
|
|
if (Documentation.empty())
|
|
return;
|
|
|
|
CommentWithMarkers.reserve(Documentation.size() +
|
|
Documentation.count('\n') * 3);
|
|
|
|
preprocessDocumentation(Documentation);
|
|
|
|
SourceManagerForFile SourceMgrForFile("mock_file.cpp", CommentWithMarkers);
|
|
|
|
SourceManager &SourceMgr = SourceMgrForFile.get();
|
|
// The doxygen Sema requires a Diagostics consumer, since it reports
|
|
// warnings e.g. when parameters are not documented correctly. These
|
|
// warnings are not relevant for us, so we can ignore them.
|
|
SourceMgr.getDiagnostics().setClient(new IgnoringDiagConsumer);
|
|
|
|
comments::Sema S(Allocator, SourceMgr, SourceMgr.getDiagnostics(), Traits,
|
|
/*PP=*/nullptr);
|
|
comments::Lexer L(Allocator, SourceMgr.getDiagnostics(), Traits,
|
|
SourceMgr.getLocForStartOfFile(SourceMgr.getMainFileID()),
|
|
CommentWithMarkers.data(),
|
|
CommentWithMarkers.data() + CommentWithMarkers.size());
|
|
comments::Parser P(L, S, Allocator, SourceMgr, SourceMgr.getDiagnostics(),
|
|
Traits);
|
|
comments::FullComment *FC = P.parseFullComment();
|
|
|
|
if (!FC)
|
|
return;
|
|
|
|
for (auto *Block : FC->getBlocks()) {
|
|
visit(Block);
|
|
}
|
|
|
|
// If we have not seen a brief command, use the very first free paragraph as
|
|
// the brief.
|
|
if (!BriefParagraph && !FreeParagraphs.empty() &&
|
|
FreeParagraphs.contains(0)) {
|
|
BriefParagraph = FreeParagraphs.lookup(0);
|
|
FreeParagraphs.erase(0);
|
|
}
|
|
}
|
|
|
|
bool isParameterDocumented(StringRef ParamName) const {
|
|
return Parameters.contains(ParamName);
|
|
}
|
|
|
|
bool isTemplateTypeParmDocumented(StringRef ParamName) const {
|
|
return TemplateParameters.contains(ParamName);
|
|
}
|
|
|
|
bool hasBriefCommand() const { return BriefParagraph; }
|
|
|
|
bool hasReturnCommand() const { return ReturnParagraph; }
|
|
|
|
bool hasDetailedDoc() const {
|
|
return !FreeParagraphs.empty() || !BlockCommands.empty();
|
|
}
|
|
|
|
/// Converts all unhandled comment commands to a markup document.
|
|
void detailedDocToMarkup(markup::Document &Out) const;
|
|
/// Converts the "brief" command(s) to a markup document.
|
|
void briefToMarkup(markup::Paragraph &Out) const;
|
|
/// Converts the "return" command(s) to a markup document.
|
|
void returnToMarkup(markup::Paragraph &Out) const;
|
|
/// Converts the "retval" command(s) to a markup document.
|
|
void retvalsToMarkup(markup::Document &Out) const;
|
|
|
|
void visitBlockCommandComment(const comments::BlockCommandComment *B);
|
|
|
|
void templateTypeParmDocToMarkup(StringRef TemplateParamName,
|
|
markup::Paragraph &Out) const;
|
|
|
|
void templateTypeParmDocToString(StringRef TemplateParamName,
|
|
llvm::raw_string_ostream &Out) const;
|
|
|
|
void parameterDocToMarkup(StringRef ParamName, markup::Paragraph &Out) const;
|
|
|
|
void parameterDocToString(StringRef ParamName,
|
|
llvm::raw_string_ostream &Out) const;
|
|
|
|
void visitParagraphComment(const comments::ParagraphComment *P) {
|
|
if (!P->isWhitespace()) {
|
|
FreeParagraphs[CommentPartIndex] = P;
|
|
CommentPartIndex++;
|
|
}
|
|
}
|
|
|
|
void visitParamCommandComment(const comments::ParamCommandComment *P) {
|
|
Parameters[P->getParamNameAsWritten()] = P;
|
|
}
|
|
|
|
void visitTParamCommandComment(const comments::TParamCommandComment *TP) {
|
|
TemplateParameters[TP->getParamNameAsWritten()] = std::move(TP);
|
|
}
|
|
|
|
/// \brief Preprocesses the raw documentation string to prepare it for doxygen
|
|
/// parsing.
|
|
///
|
|
/// This is a workaround to provide better support for markdown in
|
|
/// doxygen. Clang's doxygen parser e.g. does not handle markdown code blocks.
|
|
///
|
|
/// The documentation string is preprocessed to replace some markdown
|
|
/// constructs with parsable doxygen commands. E.g. markdown code blocks are
|
|
/// replaced with doxygen \\code{.lang} ...
|
|
/// \\endcode blocks.
|
|
///
|
|
/// Additionally, potential doxygen commands inside markdown
|
|
/// inline code spans are escaped to avoid that doxygen tries to interpret
|
|
/// them as commands.
|
|
///
|
|
/// \note Although this is a workaround, it is very similar to what
|
|
/// doxygen itself does for markdown. In doxygen, the first parsing step is
|
|
/// also a markdown preprocessing step.
|
|
/// See https://www.doxygen.nl/manual/markdown.html
|
|
void preprocessDocumentation(StringRef Doc);
|
|
|
|
private:
|
|
comments::CommandTraits Traits;
|
|
llvm::BumpPtrAllocator Allocator;
|
|
std::string CommentWithMarkers;
|
|
|
|
/// Index to keep track of the order of the comments.
|
|
/// We want to rearange some commands like \\param.
|
|
/// This index allows us to keep the order of the other comment parts.
|
|
unsigned CommentPartIndex = 0;
|
|
|
|
/// Paragraph of the "brief" command.
|
|
const comments::ParagraphComment *BriefParagraph = nullptr;
|
|
|
|
/// Paragraph of the "return" command.
|
|
const comments::ParagraphComment *ReturnParagraph = nullptr;
|
|
|
|
/// All the "retval" command(s)
|
|
llvm::SmallVector<const comments::BlockCommandComment *> RetvalCommands;
|
|
|
|
/// All the parsed doxygen block commands.
|
|
/// They might have special handling internally like \\note or \\warning
|
|
llvm::SmallDenseMap<unsigned, const comments::BlockCommandComment *>
|
|
BlockCommands;
|
|
|
|
/// Parsed paragaph(s) of the "param" comamnd(s)
|
|
llvm::SmallDenseMap<StringRef, const comments::ParamCommandComment *>
|
|
Parameters;
|
|
|
|
/// Parsed paragaph(s) of the "tparam" comamnd(s)
|
|
llvm::SmallDenseMap<StringRef, const comments::TParamCommandComment *>
|
|
TemplateParameters;
|
|
|
|
/// All "free" text paragraphs.
|
|
llvm::SmallDenseMap<unsigned, const comments::ParagraphComment *>
|
|
FreeParagraphs;
|
|
};
|
|
|
|
} // namespace clangd
|
|
} // namespace clang
|
|
|
|
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_SYMBOLDOCUMENTATION_H
|