1 //===--- CodeCompletionStrings.cpp -------------------------------*- C++-*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "CodeCompletionStrings.h" 11 #include "clang/AST/ASTContext.h" 12 #include "clang/AST/DeclObjC.h" 13 #include "clang/AST/RawCommentList.h" 14 #include "clang/Basic/SourceManager.h" 15 #include <utility> 16 17 namespace clang { 18 namespace clangd { 19 20 namespace { 21 22 bool isInformativeQualifierChunk(CodeCompletionString::Chunk const &Chunk) { 23 return Chunk.Kind == CodeCompletionString::CK_Informative && 24 StringRef(Chunk.Text).endswith("::"); 25 } 26 27 void appendEscapeSnippet(const llvm::StringRef Text, std::string *Out) { 28 for (const auto Character : Text) { 29 if (Character == '$' || Character == '}' || Character == '\\') 30 Out->push_back('\\'); 31 Out->push_back(Character); 32 } 33 } 34 35 bool looksLikeDocComment(llvm::StringRef CommentText) { 36 // We don't report comments that only contain "special" chars. 37 // This avoids reporting various delimiters, like: 38 // ================= 39 // ----------------- 40 // ***************** 41 return CommentText.find_first_not_of("/*-= \t\r\n") != llvm::StringRef::npos; 42 } 43 44 } // namespace 45 46 std::string getDocComment(const ASTContext &Ctx, 47 const CodeCompletionResult &Result, 48 bool CommentsFromHeaders) { 49 // FIXME: clang's completion also returns documentation for RK_Pattern if they 50 // contain a pattern for ObjC properties. Unfortunately, there is no API to 51 // get this declaration, so we don't show documentation in that case. 52 if (Result.Kind != CodeCompletionResult::RK_Declaration) 53 return ""; 54 return Result.getDeclaration() ? getDeclComment(Ctx, *Result.getDeclaration()) 55 : ""; 56 } 57 58 std::string getDeclComment(const ASTContext &Ctx, const NamedDecl &Decl) { 59 if (llvm::isa<NamespaceDecl>(Decl)) { 60 // Namespaces often have too many redecls for any particular redecl comment 61 // to be useful. Moreover, we often confuse file headers or generated 62 // comments with namespace comments. Therefore we choose to just ignore 63 // the comments for namespaces. 64 return ""; 65 } 66 const RawComment *RC = getCompletionComment(Ctx, &Decl); 67 if (!RC) 68 return ""; 69 // Sanity check that the comment does not come from the PCH. We choose to not 70 // write them into PCH, because they are racy and slow to load. 71 assert(!Ctx.getSourceManager().isLoadedSourceLocation(RC->getBeginLoc())); 72 std::string Doc = RC->getFormattedText(Ctx.getSourceManager(), Ctx.getDiagnostics()); 73 return looksLikeDocComment(Doc) ? Doc : ""; 74 } 75 76 void getSignature(const CodeCompletionString &CCS, std::string *Signature, 77 std::string *Snippet, std::string *RequiredQualifiers) { 78 unsigned ArgCount = 0; 79 for (const auto &Chunk : CCS) { 80 // Informative qualifier chunks only clutter completion results, skip 81 // them. 82 if (isInformativeQualifierChunk(Chunk)) 83 continue; 84 85 switch (Chunk.Kind) { 86 case CodeCompletionString::CK_TypedText: 87 // The typed-text chunk is the actual name. We don't record this chunk. 88 // In general our string looks like <qualifiers><name><signature>. 89 // So once we see the name, any text we recorded so far should be 90 // reclassified as qualifiers. 91 if (RequiredQualifiers) 92 *RequiredQualifiers = std::move(*Signature); 93 Signature->clear(); 94 Snippet->clear(); 95 break; 96 case CodeCompletionString::CK_Text: 97 *Signature += Chunk.Text; 98 *Snippet += Chunk.Text; 99 break; 100 case CodeCompletionString::CK_Optional: 101 break; 102 case CodeCompletionString::CK_Placeholder: 103 *Signature += Chunk.Text; 104 ++ArgCount; 105 *Snippet += "${" + std::to_string(ArgCount) + ':'; 106 appendEscapeSnippet(Chunk.Text, Snippet); 107 *Snippet += '}'; 108 break; 109 case CodeCompletionString::CK_Informative: 110 // For example, the word "const" for a const method, or the name of 111 // the base class for methods that are part of the base class. 112 *Signature += Chunk.Text; 113 // Don't put the informative chunks in the snippet. 114 break; 115 case CodeCompletionString::CK_ResultType: 116 // This is not part of the signature. 117 break; 118 case CodeCompletionString::CK_CurrentParameter: 119 // This should never be present while collecting completion items, 120 // only while collecting overload candidates. 121 llvm_unreachable("Unexpected CK_CurrentParameter while collecting " 122 "CompletionItems"); 123 break; 124 case CodeCompletionString::CK_LeftParen: 125 case CodeCompletionString::CK_RightParen: 126 case CodeCompletionString::CK_LeftBracket: 127 case CodeCompletionString::CK_RightBracket: 128 case CodeCompletionString::CK_LeftBrace: 129 case CodeCompletionString::CK_RightBrace: 130 case CodeCompletionString::CK_LeftAngle: 131 case CodeCompletionString::CK_RightAngle: 132 case CodeCompletionString::CK_Comma: 133 case CodeCompletionString::CK_Colon: 134 case CodeCompletionString::CK_SemiColon: 135 case CodeCompletionString::CK_Equal: 136 case CodeCompletionString::CK_HorizontalSpace: 137 *Signature += Chunk.Text; 138 *Snippet += Chunk.Text; 139 break; 140 case CodeCompletionString::CK_VerticalSpace: 141 *Snippet += Chunk.Text; 142 // Don't even add a space to the signature. 143 break; 144 } 145 } 146 } 147 148 std::string formatDocumentation(const CodeCompletionString &CCS, 149 llvm::StringRef DocComment) { 150 // Things like __attribute__((nonnull(1,3))) and [[noreturn]]. Present this 151 // information in the documentation field. 152 std::string Result; 153 const unsigned AnnotationCount = CCS.getAnnotationCount(); 154 if (AnnotationCount > 0) { 155 Result += "Annotation"; 156 if (AnnotationCount == 1) { 157 Result += ": "; 158 } else /* AnnotationCount > 1 */ { 159 Result += "s: "; 160 } 161 for (unsigned I = 0; I < AnnotationCount; ++I) { 162 Result += CCS.getAnnotation(I); 163 Result.push_back(I == AnnotationCount - 1 ? '\n' : ' '); 164 } 165 } 166 // Add brief documentation (if there is any). 167 if (!DocComment.empty()) { 168 if (!Result.empty()) { 169 // This means we previously added annotations. Add an extra newline 170 // character to make the annotations stand out. 171 Result.push_back('\n'); 172 } 173 Result += DocComment; 174 } 175 return Result; 176 } 177 178 std::string getReturnType(const CodeCompletionString &CCS) { 179 for (const auto &Chunk : CCS) 180 if (Chunk.Kind == CodeCompletionString::CK_ResultType) 181 return Chunk.Text; 182 return ""; 183 } 184 185 } // namespace clangd 186 } // namespace clang 187