|  | //===--- CodeCompletionStrings.cpp -------------------------------*- C++-*-===// | 
|  | // | 
|  | //                     The LLVM Compiler Infrastructure | 
|  | // | 
|  | // This file is distributed under the University of Illinois Open Source | 
|  | // License. See LICENSE.TXT for details. | 
|  | // | 
|  | //===---------------------------------------------------------------------===// | 
|  |  | 
|  | #include "CodeCompletionStrings.h" | 
|  | #include "clang/AST/ASTContext.h" | 
|  | #include "clang/AST/DeclObjC.h" | 
|  | #include "clang/AST/RawCommentList.h" | 
|  | #include "clang/Basic/SourceManager.h" | 
|  | #include <utility> | 
|  |  | 
|  | namespace clang { | 
|  | namespace clangd { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | bool isInformativeQualifierChunk(CodeCompletionString::Chunk const &Chunk) { | 
|  | return Chunk.Kind == CodeCompletionString::CK_Informative && | 
|  | StringRef(Chunk.Text).endswith("::"); | 
|  | } | 
|  |  | 
|  | void appendEscapeSnippet(const llvm::StringRef Text, std::string *Out) { | 
|  | for (const auto Character : Text) { | 
|  | if (Character == '$' || Character == '}' || Character == '\\') | 
|  | Out->push_back('\\'); | 
|  | Out->push_back(Character); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool looksLikeDocComment(llvm::StringRef CommentText) { | 
|  | // We don't report comments that only contain "special" chars. | 
|  | // This avoids reporting various delimiters, like: | 
|  | //   ================= | 
|  | //   ----------------- | 
|  | //   ***************** | 
|  | return CommentText.find_first_not_of("/*-= \t\r\n") != llvm::StringRef::npos; | 
|  | } | 
|  |  | 
|  | } // namespace | 
|  |  | 
|  | std::string getDocComment(const ASTContext &Ctx, | 
|  | const CodeCompletionResult &Result, | 
|  | bool CommentsFromHeaders) { | 
|  | // FIXME: clang's completion also returns documentation for RK_Pattern if they | 
|  | // contain a pattern for ObjC properties. Unfortunately, there is no API to | 
|  | // get this declaration, so we don't show documentation in that case. | 
|  | if (Result.Kind != CodeCompletionResult::RK_Declaration) | 
|  | return ""; | 
|  | auto *Decl = Result.getDeclaration(); | 
|  | if (!Decl || llvm::isa<NamespaceDecl>(Decl)) { | 
|  | // Namespaces often have too many redecls for any particular redecl comment | 
|  | // to be useful. Moreover, we often confuse file headers or generated | 
|  | // comments with namespace comments. Therefore we choose to just ignore | 
|  | // the comments for namespaces. | 
|  | return ""; | 
|  | } | 
|  | const RawComment *RC = getCompletionComment(Ctx, Decl); | 
|  | if (!RC) | 
|  | return ""; | 
|  |  | 
|  | // Sanity check that the comment does not come from the PCH. We choose to not | 
|  | // write them into PCH, because they are racy and slow to load. | 
|  | assert(!Ctx.getSourceManager().isLoadedSourceLocation(RC->getLocStart())); | 
|  | std::string Doc = RC->getFormattedText(Ctx.getSourceManager(), Ctx.getDiagnostics()); | 
|  | if (!looksLikeDocComment(Doc)) | 
|  | return ""; | 
|  | return Doc; | 
|  | } | 
|  |  | 
|  | std::string | 
|  | getParameterDocComment(const ASTContext &Ctx, | 
|  | const CodeCompleteConsumer::OverloadCandidate &Result, | 
|  | unsigned ArgIndex, bool CommentsFromHeaders) { | 
|  | auto *Func = Result.getFunction(); | 
|  | if (!Func) | 
|  | return ""; | 
|  | const RawComment *RC = getParameterComment(Ctx, Result, ArgIndex); | 
|  | if (!RC) | 
|  | return ""; | 
|  | // Sanity check that the comment does not come from the PCH. We choose to not | 
|  | // write them into PCH, because they are racy and slow to load. | 
|  | assert(!Ctx.getSourceManager().isLoadedSourceLocation(RC->getLocStart())); | 
|  | std::string Doc = RC->getFormattedText(Ctx.getSourceManager(), Ctx.getDiagnostics()); | 
|  | if (!looksLikeDocComment(Doc)) | 
|  | return ""; | 
|  | return Doc; | 
|  | } | 
|  |  | 
|  | void getSignature(const CodeCompletionString &CCS, std::string *Signature, | 
|  | std::string *Snippet, std::string *RequiredQualifiers) { | 
|  | unsigned ArgCount = 0; | 
|  | for (const auto &Chunk : CCS) { | 
|  | // Informative qualifier chunks only clutter completion results, skip | 
|  | // them. | 
|  | if (isInformativeQualifierChunk(Chunk)) | 
|  | continue; | 
|  |  | 
|  | switch (Chunk.Kind) { | 
|  | case CodeCompletionString::CK_TypedText: | 
|  | // The typed-text chunk is the actual name. We don't record this chunk. | 
|  | // In general our string looks like <qualifiers><name><signature>. | 
|  | // So once we see the name, any text we recorded so far should be | 
|  | // reclassified as qualifiers. | 
|  | if (RequiredQualifiers) | 
|  | *RequiredQualifiers = std::move(*Signature); | 
|  | Signature->clear(); | 
|  | Snippet->clear(); | 
|  | break; | 
|  | case CodeCompletionString::CK_Text: | 
|  | *Signature += Chunk.Text; | 
|  | *Snippet += Chunk.Text; | 
|  | break; | 
|  | case CodeCompletionString::CK_Optional: | 
|  | break; | 
|  | case CodeCompletionString::CK_Placeholder: | 
|  | *Signature += Chunk.Text; | 
|  | ++ArgCount; | 
|  | *Snippet += "${" + std::to_string(ArgCount) + ':'; | 
|  | appendEscapeSnippet(Chunk.Text, Snippet); | 
|  | *Snippet += '}'; | 
|  | break; | 
|  | case CodeCompletionString::CK_Informative: | 
|  | // For example, the word "const" for a const method, or the name of | 
|  | // the base class for methods that are part of the base class. | 
|  | *Signature += Chunk.Text; | 
|  | // Don't put the informative chunks in the snippet. | 
|  | break; | 
|  | case CodeCompletionString::CK_ResultType: | 
|  | // This is not part of the signature. | 
|  | break; | 
|  | case CodeCompletionString::CK_CurrentParameter: | 
|  | // This should never be present while collecting completion items, | 
|  | // only while collecting overload candidates. | 
|  | llvm_unreachable("Unexpected CK_CurrentParameter while collecting " | 
|  | "CompletionItems"); | 
|  | break; | 
|  | case CodeCompletionString::CK_LeftParen: | 
|  | case CodeCompletionString::CK_RightParen: | 
|  | case CodeCompletionString::CK_LeftBracket: | 
|  | case CodeCompletionString::CK_RightBracket: | 
|  | case CodeCompletionString::CK_LeftBrace: | 
|  | case CodeCompletionString::CK_RightBrace: | 
|  | case CodeCompletionString::CK_LeftAngle: | 
|  | case CodeCompletionString::CK_RightAngle: | 
|  | case CodeCompletionString::CK_Comma: | 
|  | case CodeCompletionString::CK_Colon: | 
|  | case CodeCompletionString::CK_SemiColon: | 
|  | case CodeCompletionString::CK_Equal: | 
|  | case CodeCompletionString::CK_HorizontalSpace: | 
|  | *Signature += Chunk.Text; | 
|  | *Snippet += Chunk.Text; | 
|  | break; | 
|  | case CodeCompletionString::CK_VerticalSpace: | 
|  | *Snippet += Chunk.Text; | 
|  | // Don't even add a space to the signature. | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | std::string formatDocumentation(const CodeCompletionString &CCS, | 
|  | llvm::StringRef DocComment) { | 
|  | // Things like __attribute__((nonnull(1,3))) and [[noreturn]]. Present this | 
|  | // information in the documentation field. | 
|  | std::string Result; | 
|  | const unsigned AnnotationCount = CCS.getAnnotationCount(); | 
|  | if (AnnotationCount > 0) { | 
|  | Result += "Annotation"; | 
|  | if (AnnotationCount == 1) { | 
|  | Result += ": "; | 
|  | } else /* AnnotationCount > 1 */ { | 
|  | Result += "s: "; | 
|  | } | 
|  | for (unsigned I = 0; I < AnnotationCount; ++I) { | 
|  | Result += CCS.getAnnotation(I); | 
|  | Result.push_back(I == AnnotationCount - 1 ? '\n' : ' '); | 
|  | } | 
|  | } | 
|  | // Add brief documentation (if there is any). | 
|  | if (!DocComment.empty()) { | 
|  | if (!Result.empty()) { | 
|  | // This means we previously added annotations. Add an extra newline | 
|  | // character to make the annotations stand out. | 
|  | Result.push_back('\n'); | 
|  | } | 
|  | Result += DocComment; | 
|  | } | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | std::string getReturnType(const CodeCompletionString &CCS) { | 
|  | for (const auto &Chunk : CCS) | 
|  | if (Chunk.Kind == CodeCompletionString::CK_ResultType) | 
|  | return Chunk.Text; | 
|  | return ""; | 
|  | } | 
|  |  | 
|  | } // namespace clangd | 
|  | } // namespace clang |