diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp index 9c17b4ca9b706..b0a35b40102be 100644 --- a/clang-tools-extra/clangd/CodeComplete.cpp +++ b/clang-tools-extra/clangd/CodeComplete.cpp @@ -97,6 +97,9 @@ toCompletionItemKind(index::SymbolKind Kind, const llvm::StringRef *Signature = nullptr) { using SK = index::SymbolKind; switch (Kind) { + // FIXME: for backwards compatibility, the include directive kind is treated + // the same as Unknown + case SK::IncludeDirective: case SK::Unknown: return CompletionItemKind::Missing; case SK::Module: diff --git a/clang-tools-extra/clangd/CodeCompletionStrings.cpp b/clang-tools-extra/clangd/CodeCompletionStrings.cpp index d6579640cb0fb..9c4241b54057a 100644 --- a/clang-tools-extra/clangd/CodeCompletionStrings.cpp +++ b/clang-tools-extra/clangd/CodeCompletionStrings.cpp @@ -112,7 +112,7 @@ std::string getDeclComment(const ASTContext &Ctx, const NamedDecl &Decl) { std::string Doc; if (Cfg.Documentation.CommentFormat == Config::CommentFormatPolicy::Doxygen && - isa(Decl)) { + isa(Decl)) { // Parameters are documented in their declaration context (function or // template function). const NamedDecl *ND = dyn_cast(Decl.getDeclContext()); @@ -135,7 +135,11 @@ std::string getDeclComment(const ASTContext &Ctx, const NamedDecl &Decl) { std::string RawDoc; llvm::raw_string_ostream OS(RawDoc); - V.parameterDocToString(dyn_cast(&Decl)->getName(), OS); + if (auto *PVD = dyn_cast(&Decl)) + V.parameterDocToString(PVD->getName(), OS); + else + V.templateTypeParmDocToString( + cast(&Decl)->getName(), OS); Doc = StringRef(RawDoc).trim().str(); } else { diff --git a/clang-tools-extra/clangd/Hover.cpp b/clang-tools-extra/clangd/Hover.cpp index 0afa90285db52..a1e81cb479751 100644 --- a/clang-tools-extra/clangd/Hover.cpp +++ b/clang-tools-extra/clangd/Hover.cpp @@ -1272,10 +1272,10 @@ std::optional getHover(ParsedAST &AST, Position Pos, HoverCountMetric.record(1, "include"); HoverInfo HI; HI.Name = std::string(llvm::sys::path::filename(Inc.Resolved)); - // FIXME: We don't have a fitting value for Kind. HI.Definition = URIForFile::canonicalize(Inc.Resolved, AST.tuPath()).file().str(); HI.DefinitionLanguage = ""; + HI.Kind = index::SymbolKind::IncludeDirective; maybeAddUsedSymbols(AST, HI, Inc); return HI; } @@ -1481,10 +1481,6 @@ void HoverInfo::sizeToMarkupParagraph(markup::Paragraph &P) const { } markup::Document HoverInfo::presentDoxygen() const { - // NOTE: this function is currently almost identical to presentDefault(). - // This is to have a minimal change when introducing the doxygen parser. - // This function will be changed when rearranging the output for doxygen - // parsed documentation. markup::Document Output; // Header contains a text of the form: @@ -1500,11 +1496,31 @@ markup::Document HoverInfo::presentDoxygen() const { // level 1 and 2 headers in a huge font, see // https://github.com/microsoft/vscode/issues/88417 for details. markup::Paragraph &Header = Output.addHeading(3); - if (Kind != index::SymbolKind::Unknown) + if (Kind != index::SymbolKind::Unknown && + Kind != index::SymbolKind::IncludeDirective) Header.appendText(index::getSymbolKindString(Kind)).appendSpace(); assert(!Name.empty() && "hover triggered on a nameless symbol"); - Header.appendCode(Name); + if (Kind == index::SymbolKind::IncludeDirective) { + Header.appendCode(Name); + + if (!Definition.empty()) + Output.addParagraph().appendCode(Definition); + + if (!UsedSymbolNames.empty()) { + Output.addRuler(); + usedSymbolNamesToMarkup(Output); + } + + return Output; + } + + if (!Definition.empty()) { + Output.addRuler(); + definitionScopeToMarkup(Output); + } else { + Header.appendCode(Name); + } if (!Provider.empty()) { providerToMarkupParagraph(Output); @@ -1512,33 +1528,76 @@ markup::Document HoverInfo::presentDoxygen() const { // Put a linebreak after header to increase readability. Output.addRuler(); - // Print Types on their own lines to reduce chances of getting line-wrapped by - // editor, as they might be long. - if (ReturnType) { - // For functions we display signature in a list form, e.g.: - // → `x` - // Parameters: - // - `bool param1` - // - `int param2 = 5` - Output.addParagraph().appendText("→ ").appendCode( - llvm::to_string(*ReturnType)); - } SymbolDocCommentVisitor SymbolDoc(Documentation, CommentOpts); + if (SymbolDoc.hasBriefCommand()) { + SymbolDoc.briefToMarkup(Output.addParagraph()); + Output.addRuler(); + } + + // For functions we display signature in a list form, e.g.: + // Template Parameters: + // - `typename T` - description + // Parameters: + // - `bool param1` - description + // - `int param2 = 5` - description + // Returns + // `type` - description + if (TemplateParameters && !TemplateParameters->empty()) { + Output.addParagraph().appendBoldText("Template Parameters:"); + markup::BulletList &L = Output.addBulletList(); + for (const auto &Param : *TemplateParameters) { + markup::Paragraph &P = L.addItem().addParagraph(); + P.appendCode(llvm::to_string(Param)); + if (SymbolDoc.isTemplateTypeParmDocumented(llvm::to_string(Param.Name))) { + P.appendText(" - "); + SymbolDoc.templateTypeParmDocToMarkup(llvm::to_string(Param.Name), P); + } + } + Output.addRuler(); + } + if (Parameters && !Parameters->empty()) { - Output.addParagraph().appendText("Parameters:"); + Output.addParagraph().appendBoldText("Parameters:"); markup::BulletList &L = Output.addBulletList(); for (const auto &Param : *Parameters) { markup::Paragraph &P = L.addItem().addParagraph(); P.appendCode(llvm::to_string(Param)); if (SymbolDoc.isParameterDocumented(llvm::to_string(Param.Name))) { - P.appendText(" -"); + P.appendText(" - "); SymbolDoc.parameterDocToMarkup(llvm::to_string(Param.Name), P); } } + Output.addRuler(); + } + + // Print Types on their own lines to reduce chances of getting line-wrapped by + // editor, as they might be long. + if (ReturnType && + ((ReturnType->Type != "void" && !ReturnType->AKA.has_value()) || + (ReturnType->AKA.has_value() && ReturnType->AKA != "void"))) { + Output.addParagraph().appendBoldText("Returns:"); + markup::Paragraph &P = Output.addParagraph(); + P.appendCode(llvm::to_string(*ReturnType)); + + if (SymbolDoc.hasReturnCommand()) { + P.appendText(" - "); + SymbolDoc.returnToMarkup(P); + } + Output.addRuler(); } + + // add specially handled doxygen commands. + SymbolDoc.warningsToMarkup(Output); + SymbolDoc.notesToMarkup(Output); + + // add any other documentation. + SymbolDoc.docToMarkup(Output); + + Output.addRuler(); + // Don't print Type after Parameters or ReturnType as this will just duplicate // the information if (Type && !ReturnType && !Parameters) @@ -1559,13 +1618,6 @@ markup::Document HoverInfo::presentDoxygen() const { calleeArgInfoToMarkupParagraph(Output.addParagraph()); } - SymbolDoc.docToMarkup(Output); - - if (!Definition.empty()) { - Output.addRuler(); - definitionScopeToMarkup(Output); - } - if (!UsedSymbolNames.empty()) { Output.addRuler(); usedSymbolNamesToMarkup(Output); @@ -1589,7 +1641,8 @@ markup::Document HoverInfo::presentDefault() const { // level 1 and 2 headers in a huge font, see // https://github.com/microsoft/vscode/issues/88417 for details. markup::Paragraph &Header = Output.addHeading(3); - if (Kind != index::SymbolKind::Unknown) + if (Kind != index::SymbolKind::Unknown && + Kind != index::SymbolKind::IncludeDirective) Header.appendText(index::getSymbolKindString(Kind)).appendSpace(); assert(!Name.empty() && "hover triggered on a nameless symbol"); Header.appendCode(Name); @@ -1613,7 +1666,7 @@ markup::Document HoverInfo::presentDefault() const { } if (Parameters && !Parameters->empty()) { - Output.addParagraph().appendText("Parameters: "); + Output.addParagraph().appendText("Parameters:"); markup::BulletList &L = Output.addBulletList(); for (const auto &Param : *Parameters) L.addItem().addParagraph().appendCode(llvm::to_string(Param)); diff --git a/clang-tools-extra/clangd/Quality.cpp b/clang-tools-extra/clangd/Quality.cpp index 3f630b05c654b..dc4afe18cb354 100644 --- a/clang-tools-extra/clangd/Quality.cpp +++ b/clang-tools-extra/clangd/Quality.cpp @@ -143,6 +143,9 @@ categorize(const index::SymbolInfo &D) { case index::SymbolKind::Parameter: case index::SymbolKind::NonTypeTemplateParm: return SymbolQualitySignals::Variable; + // FIXME: for backwards compatibility, the include directive kind is treated + // the same as Unknown + case index::SymbolKind::IncludeDirective: case index::SymbolKind::Using: case index::SymbolKind::Module: case index::SymbolKind::Unknown: diff --git a/clang-tools-extra/clangd/SymbolDocumentation.cpp b/clang-tools-extra/clangd/SymbolDocumentation.cpp index dea637b9100da..9ae1ef3f828e0 100644 --- a/clang-tools-extra/clangd/SymbolDocumentation.cpp +++ b/clang-tools-extra/clangd/SymbolDocumentation.cpp @@ -33,8 +33,8 @@ void commandToMarkup(markup::Paragraph &Out, StringRef Command, comments::CommandMarkerKind CommandMarker, StringRef Args) { Out.appendBoldText(commandMarkerAsString(CommandMarker) + Command.str()); + Out.appendSpace(); if (!Args.empty()) { - Out.appendSpace(); Out.appendEmphasizedText(Args.str()); } } @@ -132,7 +132,8 @@ class ParagraphToMarkupDocument const comments::CommandTraits &Traits; /// If true, the next leading space after a new line is trimmed. - bool LastChunkEndsWithNewline = false; + /// Initially set it to true, to always trim the first text line. + bool LastChunkEndsWithNewline = true; }; class ParagraphToString @@ -263,8 +264,76 @@ class BlockCommentToMarkupDocument StringRef CommentEscapeMarker; }; -void SymbolDocCommentVisitor::parameterDocToMarkup(StringRef ParamName, - markup::Paragraph &Out) { +void SymbolDocCommentVisitor::visitBlockCommandComment( + const comments::BlockCommandComment *B) { + switch (B->getCommandID()) { + case comments::CommandTraits::KCI_brief: { + if (!BriefParagraph) { + BriefParagraph = B->getParagraph(); + return; + } + break; + } + case comments::CommandTraits::KCI_return: + case comments::CommandTraits::KCI_returns: + if (!ReturnParagraph) { + ReturnParagraph = B->getParagraph(); + return; + } + break; + case comments::CommandTraits::KCI_retval: + RetvalParagraphs.push_back(B->getParagraph()); + return; + case comments::CommandTraits::KCI_warning: + WarningParagraphs.push_back(B->getParagraph()); + return; + case comments::CommandTraits::KCI_note: + NoteParagraphs.push_back(B->getParagraph()); + return; + default: + break; + } + + // For all other commands, we store them in the UnhandledCommands map. + // This allows us to keep the order of the comments. + UnhandledCommands[CommentPartIndex] = B; + CommentPartIndex++; +} + +void SymbolDocCommentVisitor::paragraphsToMarkup( + markup::Document &Out, + const llvm::SmallVectorImpl &Paragraphs) + const { + if (Paragraphs.empty()) + return; + + for (const auto *P : Paragraphs) { + ParagraphToMarkupDocument(Out.addParagraph(), Traits).visit(P); + } +} + +void SymbolDocCommentVisitor::briefToMarkup(markup::Paragraph &Out) const { + if (!BriefParagraph) + return; + ParagraphToMarkupDocument(Out, Traits).visit(BriefParagraph); +} + +void SymbolDocCommentVisitor::returnToMarkup(markup::Paragraph &Out) const { + if (!ReturnParagraph) + return; + ParagraphToMarkupDocument(Out, Traits).visit(ReturnParagraph); +} + +void SymbolDocCommentVisitor::notesToMarkup(markup::Document &Out) const { + paragraphsToMarkup(Out, NoteParagraphs); +} + +void SymbolDocCommentVisitor::warningsToMarkup(markup::Document &Out) const { + paragraphsToMarkup(Out, WarningParagraphs); +} + +void SymbolDocCommentVisitor::parameterDocToMarkup( + StringRef ParamName, markup::Paragraph &Out) const { if (ParamName.empty()) return; @@ -274,7 +343,7 @@ void SymbolDocCommentVisitor::parameterDocToMarkup(StringRef ParamName, } void SymbolDocCommentVisitor::parameterDocToString( - StringRef ParamName, llvm::raw_string_ostream &Out) { + StringRef ParamName, llvm::raw_string_ostream &Out) const { if (ParamName.empty()) return; @@ -283,9 +352,9 @@ void SymbolDocCommentVisitor::parameterDocToString( } } -void SymbolDocCommentVisitor::docToMarkup(markup::Document &Out) { +void SymbolDocCommentVisitor::docToMarkup(markup::Document &Out) const { for (unsigned I = 0; I < CommentPartIndex; ++I) { - if (const auto *BC = BlockCommands.lookup(I)) { + if (const auto *BC = UnhandledCommands.lookup(I)) { BlockCommentToMarkupDocument(Out, Traits).visit(BC); } else if (const auto *P = FreeParagraphs.lookup(I)) { ParagraphToMarkupDocument(Out.addParagraph(), Traits).visit(P); @@ -293,5 +362,25 @@ void SymbolDocCommentVisitor::docToMarkup(markup::Document &Out) { } } +void SymbolDocCommentVisitor::templateTypeParmDocToMarkup( + StringRef TemplateParamName, markup::Paragraph &Out) const { + if (TemplateParamName.empty()) + return; + + if (const auto *TP = TemplateParameters.lookup(TemplateParamName)) { + ParagraphToMarkupDocument(Out, Traits).visit(TP->getParagraph()); + } +} + +void SymbolDocCommentVisitor::templateTypeParmDocToString( + StringRef TemplateParamName, llvm::raw_string_ostream &Out) const { + if (TemplateParamName.empty()) + return; + + if (const auto *P = TemplateParameters.lookup(TemplateParamName)) { + ParagraphToString(Out, Traits).visit(P->getParagraph()); + } +} + } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/SymbolDocumentation.h b/clang-tools-extra/clangd/SymbolDocumentation.h index b5120ba04e8f1..b0d3428dfce20 100644 --- a/clang-tools-extra/clangd/SymbolDocumentation.h +++ b/clang-tools-extra/clangd/SymbolDocumentation.h @@ -106,16 +106,43 @@ class SymbolDocCommentVisitor return Parameters.contains(ParamName); } - void parameterDocToMarkup(StringRef ParamName, markup::Paragraph &Out); + bool isTemplateTypeParmDocumented(StringRef ParamName) const { + return TemplateParameters.contains(ParamName); + } - void parameterDocToString(StringRef ParamName, llvm::raw_string_ostream &Out); + bool hasBriefCommand() const { return BriefParagraph; } - void docToMarkup(markup::Document &Out); + bool hasReturnCommand() const { return ReturnParagraph; } - void visitBlockCommandComment(const comments::BlockCommandComment *B) { - BlockCommands[CommentPartIndex] = B; - CommentPartIndex++; - } + bool hasRetvalCommands() const { return !RetvalParagraphs.empty(); } + + bool hasNoteCommands() const { return !NoteParagraphs.empty(); } + + bool hasWarningCommands() const { return !WarningParagraphs.empty(); } + + /// Converts all unhandled comment commands to a markup document. + void docToMarkup(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 "note" command(s) to a markup document. + void notesToMarkup(markup::Document &Out) const; + /// Converts the "warning" command(s) to a markup document. + void warningsToMarkup(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) { FreeParagraphs[CommentPartIndex] = P; @@ -126,6 +153,10 @@ class SymbolDocCommentVisitor Parameters[P->getParamNameAsWritten()] = P; } + void visitTParamCommandComment(const comments::TParamCommandComment *TP) { + TemplateParameters[TP->getParamNameAsWritten()] = std::move(TP); + } + private: comments::CommandTraits Traits; llvm::BumpPtrAllocator Allocator; @@ -136,17 +167,42 @@ class SymbolDocCommentVisitor /// 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; + + /// Paragraph(s) of the "note" command(s) + llvm::SmallVector RetvalParagraphs; + + /// Paragraph(s) of the "note" command(s) + llvm::SmallVector NoteParagraphs; + + /// Paragraph(s) of the "warning" command(s) + llvm::SmallVector WarningParagraphs; + + /// All the paragraphs we don't have any special handling for, + /// e.g. "details". + llvm::SmallDenseMap + UnhandledCommands; + /// Parsed paragaph(s) of the "param" comamnd(s) llvm::SmallDenseMap Parameters; - /// All the block commands. - llvm::SmallDenseMap - BlockCommands; + /// Parsed paragaph(s) of the "tparam" comamnd(s) + llvm::SmallDenseMap + TemplateParameters; /// All "free" text paragraphs. llvm::SmallDenseMap FreeParagraphs; + + void paragraphsToMarkup( + markup::Document &Out, + const llvm::SmallVectorImpl + &Paragraphs) const; }; } // namespace clangd diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp index 3331164ab0024..be56fce19713b 100644 --- a/clang-tools-extra/clangd/unittests/HoverTests.cpp +++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp @@ -3409,7 +3409,8 @@ TEST(Hover, DocsFromMostSpecial) { TEST(Hover, Present) { struct { const std::function Builder; - llvm::StringRef ExpectedRender; + llvm::StringRef ExpectedMarkdownRender; + llvm::StringRef ExpectedDoxygenRender; } Cases[] = { { [](HoverInfo &HI) { @@ -3417,6 +3418,7 @@ TEST(Hover, Present) { HI.Name = "X"; }, R"(X)", + R"(### `X`)", }, { [](HoverInfo &HI) { @@ -3424,6 +3426,7 @@ TEST(Hover, Present) { HI.Name = "foo"; }, R"(namespace-alias foo)", + R"(### namespace-alias `foo`)", }, { [](HoverInfo &HI) { @@ -3446,6 +3449,24 @@ Size: 10 bytes documentation template class Foo {})", + R"(### class + +--- +```cpp +template class Foo {} +``` + +--- +**Template Parameters:** + +- `typename T` +- `typename C = bool` + +--- +documentation + +--- +Size: 10 bytes)", }, { [](HoverInfo &HI) { @@ -3476,6 +3497,26 @@ template class Foo {})", "\n" "// In namespace ns\n" "ret_type foo(params) {}", + R"(### function + +--- +```cpp +// In namespace ns +ret_type foo(params) {} +``` + +--- +**Parameters:** + +- +- `type (aka can_type)` +- `type foo (aka can_type)` +- `type foo = default (aka can_type)` + +--- +**Returns:** + +`ret_type (aka can_ret_type)`)", }, { [](HoverInfo &HI) { @@ -3502,6 +3543,22 @@ Size: 4 bytes (+4 bytes padding), alignment 4 bytes // In test::Bar def)", + R"(### field + +--- +```cpp +// In test::Bar +def +``` + +--- +Type: `type (aka can_type)` + +Value = `value` + +Offset: 12 bytes + +Size: 4 bytes (+4 bytes padding), alignment 4 bytes)", }, { [](HoverInfo &HI) { @@ -3528,6 +3585,22 @@ Size: 25 bits (+4 bits padding), alignment 8 bytes // In test::Bar def)", + R"(### field + +--- +```cpp +// In test::Bar +def +``` + +--- +Type: `type (aka can_type)` + +Value = `value` + +Offset: 4 bytes and 3 bits + +Size: 25 bits (+4 bits padding), alignment 8 bytes)", }, { [](HoverInfo &HI) { @@ -3541,6 +3614,13 @@ def)", // In test::Bar public: def)", + R"(### field + +--- +```cpp +// In test::Bar +public: def +```)", }, { [](HoverInfo &HI) { @@ -3560,6 +3640,18 @@ public: def)", // In cls protected: size_t method())", + R"(### instance-method + +--- +```cpp +// In cls +protected: size_t method() +``` + +--- +**Returns:** + +`size_t (aka unsigned long)`)", }, { [](HoverInfo &HI) { @@ -3587,6 +3679,19 @@ protected: size_t method())", // In cls public: cls(int a, int b = 5))", + R"(### constructor + +--- +```cpp +// In cls +public: cls(int a, int b = 5) +``` + +--- +**Parameters:** + +- `int a` +- `int b = 5`)", }, { [](HoverInfo &HI) { @@ -3600,6 +3705,13 @@ public: cls(int a, int b = 5))", // In namespace ns1 private: union foo {})", + R"(### union + +--- +```cpp +// In namespace ns1 +private: union foo {} +```)", }, { [](HoverInfo &HI) { @@ -3625,6 +3737,20 @@ Passed as arg_a // In test::Bar int foo = 3)", + R"(### variable + +--- +```cpp +// In test::Bar +int foo = 3 +``` + +--- +Type: `int` + +Value = `3` + +Passed as arg_a)", }, { [](HoverInfo &HI) { @@ -3636,6 +3762,10 @@ int foo = 3)", }, R"(variable foo +Passed by value)", + R"(### variable `foo` + +--- Passed by value)", }, { @@ -3662,6 +3792,20 @@ Passed by reference as arg_a // In test::Bar int foo = 3)", + R"(### variable + +--- +```cpp +// In test::Bar +int foo = 3 +``` + +--- +Type: `int` + +Value = `3` + +Passed by reference as arg_a)", }, { [](HoverInfo &HI) { @@ -3687,6 +3831,20 @@ Passed as arg_a (converted to alias_int) // In test::Bar int foo = 3)", + R"(### variable + +--- +```cpp +// In test::Bar +int foo = 3 +``` + +--- +Type: `int` + +Value = `3` + +Passed as arg_a (converted to alias_int))", }, { [](HoverInfo &HI) { @@ -3702,6 +3860,15 @@ int foo = 3)", // Expands to (1 + 1))", + R"(### macro + +--- +```cpp +#define PLUS_ONE(X) (X+1) + +// Expands to +(1 + 1) +```)", }, { [](HoverInfo &HI) { @@ -3727,45 +3894,86 @@ Passed by const reference as arg_a (converted to int) // In test::Bar int foo = 3)", + R"(### variable + +--- +```cpp +// In test::Bar +int foo = 3 +``` + +--- +Type: `int` + +Value = `3` + +Passed by const reference as arg_a (converted to int))", }, { [](HoverInfo &HI) { HI.Name = "stdio.h"; HI.Definition = "/usr/include/stdio.h"; + HI.Kind = index::SymbolKind::IncludeDirective; }, R"(stdio.h /usr/include/stdio.h)", + R"(### `stdio.h` + +`/usr/include/stdio.h`)", }, - {[](HoverInfo &HI) { - HI.Name = "foo.h"; - HI.UsedSymbolNames = {"Foo", "Bar", "Bar"}; - }, - R"(foo.h + { + [](HoverInfo &HI) { + HI.Name = "foo.h"; + HI.UsedSymbolNames = {"Foo", "Bar", "Bar"}; + HI.Kind = index::SymbolKind::IncludeDirective; + }, + R"(foo.h + +provides Foo, Bar, Bar)", + R"(### `foo.h` -provides Foo, Bar, Bar)"}, +--- +provides `Foo`, `Bar`, `Bar`)", + }, {[](HoverInfo &HI) { HI.Name = "foo.h"; HI.UsedSymbolNames = {"Foo", "Bar", "Baz", "Foobar", "Qux", "Quux"}; + HI.Kind = index::SymbolKind::IncludeDirective; }, R"(foo.h -provides Foo, Bar, Baz, Foobar, Qux and 1 more)"}}; +provides Foo, Bar, Baz, Foobar, Qux and 1 more)", + R"(### `foo.h` + +--- +provides `Foo`, `Bar`, `Baz`, `Foobar`, `Qux` and 1 more)"}}; for (const auto &C : Cases) { HoverInfo HI; C.Builder(HI); Config Cfg; Cfg.Hover.ShowAKA = true; + Cfg.Documentation.CommentFormat = Config::CommentFormatPolicy::Markdown; WithContextValue WithCfg(Config::Key, std::move(Cfg)); - EXPECT_EQ(HI.present(MarkupKind::PlainText), C.ExpectedRender); + EXPECT_EQ(HI.present(MarkupKind::PlainText), C.ExpectedMarkdownRender); + } + for (const auto &C : Cases) { + HoverInfo HI; + C.Builder(HI); + Config Cfg; + Cfg.Hover.ShowAKA = true; + Cfg.Documentation.CommentFormat = Config::CommentFormatPolicy::Doxygen; + WithContextValue WithCfg(Config::Key, std::move(Cfg)); + EXPECT_EQ(HI.present(MarkupKind::Markdown), C.ExpectedDoxygenRender); } } TEST(Hover, PresentDocumentation) { struct { const std::function Builder; - llvm::StringRef ExpectedRender; + llvm::StringRef ExpectedMarkdownRender; + llvm::StringRef ExpectedDoxygenRender; } Cases[] = { {[](HoverInfo &HI) { HI.Kind = index::SymbolKind::Function; @@ -3777,14 +3985,26 @@ TEST(Hover, PresentDocumentation) { R"(### function `foo` --- -**@brief** brief doc +@brief brief doc longer doc --- ```cpp void foo() -```)"}, +```)", + R"(### function + +--- +```cpp +void foo() +``` + +--- +brief doc + +--- +longer doc)"}, {[](HoverInfo &HI) { HI.Kind = index::SymbolKind::Function; HI.Documentation = "@brief brief doc\n\n" @@ -3798,14 +4018,31 @@ void foo() --- → `int` -**@brief** brief doc +@brief brief doc longer doc --- ```cpp int foo() -```)"}, +```)", + R"(### function + +--- +```cpp +int foo() +``` + +--- +brief doc + +--- +**Returns:** + +`int` + +--- +longer doc)"}, {[](HoverInfo &HI) { HI.Kind = index::SymbolKind::Function; HI.Documentation = "@brief brief doc\n\n" @@ -3826,18 +4063,40 @@ int foo() Parameters: -- `int a` - this is a param +- `int a` -**@brief** brief doc +@brief brief doc longer doc +@param a this is a param +@return it returns something -**@return** it returns something +--- +```cpp +int foo(int a) +```)", + R"(### function --- ```cpp int foo(int a) -```)"}, +``` + +--- +brief doc + +--- +**Parameters:** + +- `int a` - this is a param + +--- +**Returns:** + +`int` - it returns something + +--- +longer doc)"}, {[](HoverInfo &HI) { HI.Kind = index::SymbolKind::Function; HI.Documentation = "@brief brief doc\n\n" @@ -3858,20 +4117,52 @@ int foo(int a) Parameters: -- `int a` - this is a param +- `int a` -**@brief** brief doc +@brief brief doc longer doc +@param a this is a param +@param b does not exist +@return it returns something -**@return** it returns something +--- +```cpp +int foo(int a) +```)", + R"(### function --- ```cpp int foo(int a) -```)"}, +``` + +--- +brief doc + +--- +**Parameters:** + +- `int a` - this is a param + +--- +**Returns:** + +`int` - it returns something + +--- +longer doc)"}, }; + for (const auto &C : Cases) { + HoverInfo HI; + C.Builder(HI); + Config Cfg; + Cfg.Hover.ShowAKA = true; + Cfg.Documentation.CommentFormat = Config::CommentFormatPolicy::Markdown; + WithContextValue WithCfg(Config::Key, std::move(Cfg)); + EXPECT_EQ(HI.present(MarkupKind::Markdown), C.ExpectedMarkdownRender); + } for (const auto &C : Cases) { HoverInfo HI; C.Builder(HI); @@ -3879,7 +4170,7 @@ int foo(int a) Cfg.Hover.ShowAKA = true; Cfg.Documentation.CommentFormat = Config::CommentFormatPolicy::Doxygen; WithContextValue WithCfg(Config::Key, std::move(Cfg)); - EXPECT_EQ(HI.present(MarkupKind::Markdown), C.ExpectedRender); + EXPECT_EQ(HI.present(MarkupKind::Markdown), C.ExpectedDoxygenRender); } } @@ -4057,6 +4348,21 @@ TEST(Hover, PresentRulers) { "```"; EXPECT_EQ(HI.present(MarkupKind::Markdown), ExpectedMarkdown); + llvm::StringRef ExpectedDoxygenMarkdown = // + "### variable\n" + "\n" + "---\n" + "```cpp\n" + "def\n" + "```\n\n" + "---\n" + "Value = `val`"; + Config Cfg; + Cfg.Hover.ShowAKA = true; + Cfg.Documentation.CommentFormat = Config::CommentFormatPolicy::Doxygen; + WithContextValue WithCfg(Config::Key, std::move(Cfg)); + EXPECT_EQ(HI.present(MarkupKind::Markdown), ExpectedDoxygenMarkdown); + llvm::StringRef ExpectedPlaintext = R"pt(variable foo Value = val @@ -4479,8 +4785,7 @@ TEST(Hover, FunctionParameters) { HI.Definition = "int a"; HI.Documentation = ""; }, - "### param `a`\n\n---\nType: `int`\n\n---\n```cpp\n// In foo\nint " - "a\n```"}, + "### param\n\n---\n```cpp\n// In foo\nint a\n```\n\n---\nType: `int`"}, {R"cpp(/// Function doc /// @param a this is doc for a void foo(int [[^a]]); @@ -4494,8 +4799,8 @@ TEST(Hover, FunctionParameters) { HI.Definition = "int a"; HI.Documentation = "this is doc for a"; }, - "### param `a`\n\n---\nType: `int`\n\nthis is doc for " - "a\n\n---\n```cpp\n// In foo\nint a\n```"}, + "### param\n\n---\n```cpp\n// In foo\nint a\n```\n\n---\nthis is doc " + "for a\n\n---\nType: `int`"}, {R"cpp(/// Function doc /// @param b this is doc for b void foo(int [[^a]], int b); @@ -4509,8 +4814,7 @@ TEST(Hover, FunctionParameters) { HI.Definition = "int a"; HI.Documentation = ""; }, - "### param `a`\n\n---\nType: `int`\n\n---\n```cpp\n// In foo\nint " - "a\n```"}, + "### param\n\n---\n```cpp\n// In foo\nint a\n```\n\n---\nType: `int`"}, {R"cpp(/// Function doc /// @param b this is doc for \p b void foo(int a, int [[^b]]); @@ -4524,8 +4828,8 @@ TEST(Hover, FunctionParameters) { HI.Definition = "int b"; HI.Documentation = "this is doc for \\p b"; }, - "### param `b`\n\n---\nType: `int`\n\nthis is doc for " - "`b`\n\n---\n```cpp\n// In foo\nint b\n```"}, + "### param\n\n---\n```cpp\n// In foo\nint b\n```\n\n---\nthis is doc " + "for `b`\n\n---\nType: `int`"}, {R"cpp(/// Function doc /// @param b this is doc for \p b template @@ -4540,8 +4844,8 @@ TEST(Hover, FunctionParameters) { HI.Definition = "T b"; HI.Documentation = "this is doc for \\p b"; }, - "### param `b`\n\n---\nType: `T`\n\nthis is doc for " - "`b`\n\n---\n```cpp\n// In foo\nT b\n```"}, + "### param\n\n---\n```cpp\n// In foo\nT b\n```\n\n---\nthis is doc for " + "`b`\n\n---\nType: `T`"}, {R"cpp(/// Function doc /// @param b this is doc for \p b void foo(int a, int [[^b]]); @@ -4557,10 +4861,9 @@ TEST(Hover, FunctionParameters) { "this is doc for \\p b"; }, - "### param `b`\n\n---\nType: `int`\n\nthis is \\doc\\ " - "\\ \\for\\ " - "`b`\n\n---\n```cpp\n// In foo\nint b\n```"}, + "### param\n\n---\n```cpp\n// In foo\nint b\n```\n\n---\nthis is " + "\\doc\\ \\ \\for\\ `b`\n\n---\nType: `int`"}, }; // Create a tiny index, so tests above can verify documentation is fetched. diff --git a/clang-tools-extra/clangd/unittests/SymbolDocumentationTests.cpp b/clang-tools-extra/clangd/unittests/SymbolDocumentationTests.cpp index 69eb13b2142d2..31a6a149e33c4 100644 --- a/clang-tools-extra/clangd/unittests/SymbolDocumentationTests.cpp +++ b/clang-tools-extra/clangd/unittests/SymbolDocumentationTests.cpp @@ -15,7 +15,7 @@ namespace clang { namespace clangd { -TEST(SymbolDocumentation, Parse) { +TEST(SymbolDocumentation, UnhandledDocs) { CommentOptions CommentOpts; @@ -75,9 +75,9 @@ TEST(SymbolDocumentation, Parse) { }, { "\\brief this is a \\n\nbrief description", - "\\*\\*\\\\brief\\*\\* this is a \nbrief description", - "**\\brief** this is a \nbrief description", - "**\\brief** this is a\nbrief description", + "", + "", + "", }, { "\\throw exception foo", @@ -86,18 +86,26 @@ TEST(SymbolDocumentation, Parse) { "**\\throw** *exception* foo", }, { - "\\brief this is a brief description\n\n\\li item 1\n\\li item " - "2\n\\arg item 3", - "\\*\\*\\\\brief\\*\\* this is a brief description\n\n- item 1\n\n- " - "item " - "2\n\n- " - "item 3", - "**\\brief** this is a brief description\n\n- item 1\n\n- item " - "2\n\n- " - "item 3", - "**\\brief** this is a brief description\n\n- item 1\n\n- item " - "2\n\n- " - "item 3", + R"(\brief this is a brief description + +\li item 1 +\li item 2 +\arg item 3)", + R"(- item 1 + +- item 2 + +- item 3)", + R"(- item 1 + +- item 2 + +- item 3)", + R"(- item 1 + +- item 2 + +- item 3)", }, { "\\defgroup mygroup this is a group\nthis is not a group description", @@ -110,15 +118,36 @@ TEST(SymbolDocumentation, Parse) { "description", }, { - "\\verbatim\nthis is a\nverbatim block containing\nsome verbatim " - "text\n\\endverbatim", - "\\*\\*@verbatim\\*\\*\n\n```\nthis is a\nverbatim block " - "containing\nsome " - "verbatim text\n```\n\n\\*\\*@endverbatim\\*\\*", - "**@verbatim**\n\n```\nthis is a\nverbatim block containing\nsome " - "verbatim text\n```\n\n**@endverbatim**", - "**@verbatim**\n\nthis is a\nverbatim block containing\nsome " - "verbatim text\n\n**@endverbatim**", + R"(\verbatim +this is a +verbatim block containing +some verbatim text +\endverbatim)", + R"(\*\*@verbatim\*\* + +``` +this is a +verbatim block containing +some verbatim text +``` + +\*\*@endverbatim\*\*)", + R"(**@verbatim** + +``` +this is a +verbatim block containing +some verbatim text +``` + +**@endverbatim**)", + R"(**@verbatim** + +this is a +verbatim block containing +some verbatim text + +**@endverbatim**)", }, { "@param foo this is a parameter\n@param bar this is another " @@ -128,21 +157,46 @@ TEST(SymbolDocumentation, Parse) { "", }, { - "@brief brief docs\n\n@param foo this is a parameter\n\nMore " - "description\ndocumentation", - "\\*\\*@brief\\*\\* brief docs\n\nMore description\ndocumentation", - "**@brief** brief docs\n\nMore description\ndocumentation", - "**@brief** brief docs\n\nMore description documentation", - }, - { - "this is a bold text\nnormal text\nthis is an italic " - "text\nthis is a code block", - "\\this is a bold text\\\nnormal text\n\\this is an italic " - "text\\\n\\this is a code block\\", - "\\this is a bold text\\\nnormal text\n\\this is an italic " - "text\\\n\\this is a code block\\", - "this is a bold text normal text this is an italic " - "text this is a code block", + R"(@brief brief docs + +@param foo this is a parameter + +\brief another brief? + +\details these are details + +More description +documentation)", + R"(\*\*\\brief\*\* another brief? + +\*\*\\details\*\* these are details + +More description +documentation)", + R"(**\brief** another brief? + +**\details** these are details + +More description +documentation)", + R"(**\brief** another brief? + +**\details** these are details + +More description documentation)", + }, + { + R"(this is a bold text +normal textthis is an italic text +this is a code block)", + R"(\this is a bold text\ +normal text\this is an italic text\ +\this is a code block\)", + R"(\this is a bold text\ +normal text\this is an italic text\ +\this is a code block\)", + "this is a bold text normal textthis is an italic text " + "this is a code block", }, }; for (const auto &C : Cases) { diff --git a/clang/include/clang/Index/IndexSymbol.h b/clang/include/clang/Index/IndexSymbol.h index 59e90fced3dda..deb9337d9d1a1 100644 --- a/clang/include/clang/Index/IndexSymbol.h +++ b/clang/include/clang/Index/IndexSymbol.h @@ -27,6 +27,7 @@ enum class SymbolKind : uint8_t { Namespace, NamespaceAlias, Macro, + IncludeDirective, Enum, Struct, diff --git a/clang/lib/Index/IndexSymbol.cpp b/clang/lib/Index/IndexSymbol.cpp index 419ff79a5cbab..c3cbc03cf9b41 100644 --- a/clang/lib/Index/IndexSymbol.cpp +++ b/clang/lib/Index/IndexSymbol.cpp @@ -507,6 +507,9 @@ bool index::printSymbolName(const Decl *D, const LangOptions &LO, StringRef index::getSymbolKindString(SymbolKind K) { switch (K) { + // FIXME: for backwards compatibility, the include directive kind is treated + // the same as Unknown + case SymbolKind::IncludeDirective: case SymbolKind::Unknown: return ""; case SymbolKind::Module: return "module"; case SymbolKind::Namespace: return "namespace";