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 auto *Decl = Result.getDeclaration(); 55 if (!Decl || llvm::isa<NamespaceDecl>(Decl)) { 56 // Namespaces often have too many redecls for any particular redecl comment 57 // to be useful. Moreover, we often confuse file headers or generated 58 // comments with namespace comments. Therefore we choose to just ignore 59 // the comments for namespaces. 60 return ""; 61 } 62 const RawComment *RC = getCompletionComment(Ctx, Decl); 63 if (!RC) 64 return ""; 65 66 // Sanity check that the comment does not come from the PCH. We choose to not 67 // write them into PCH, because they are racy and slow to load. 68 assert(!Ctx.getSourceManager().isLoadedSourceLocation(RC->getLocStart())); 69 std::string Doc = RC->getFormattedText(Ctx.getSourceManager(), Ctx.getDiagnostics()); 70 if (!looksLikeDocComment(Doc)) 71 return ""; 72 return Doc; 73 } 74 75 std::string 76 getParameterDocComment(const ASTContext &Ctx, 77 const CodeCompleteConsumer::OverloadCandidate &Result, 78 unsigned ArgIndex, bool CommentsFromHeaders) { 79 auto *Func = Result.getFunction(); 80 if (!Func) 81 return ""; 82 const RawComment *RC = getParameterComment(Ctx, Result, ArgIndex); 83 if (!RC) 84 return ""; 85 // Sanity check that the comment does not come from the PCH. We choose to not 86 // write them into PCH, because they are racy and slow to load. 87 assert(!Ctx.getSourceManager().isLoadedSourceLocation(RC->getLocStart())); 88 std::string Doc = RC->getFormattedText(Ctx.getSourceManager(), Ctx.getDiagnostics()); 89 if (!looksLikeDocComment(Doc)) 90 return ""; 91 return Doc; 92 } 93 94 void getSignature(const CodeCompletionString &CCS, std::string *Signature, 95 std::string *Snippet, std::string *RequiredQualifiers) { 96 unsigned ArgCount = 0; 97 for (const auto &Chunk : CCS) { 98 // Informative qualifier chunks only clutter completion results, skip 99 // them. 100 if (isInformativeQualifierChunk(Chunk)) 101 continue; 102 103 switch (Chunk.Kind) { 104 case CodeCompletionString::CK_TypedText: 105 // The typed-text chunk is the actual name. We don't record this chunk. 106 // In general our string looks like <qualifiers><name><signature>. 107 // So once we see the name, any text we recorded so far should be 108 // reclassified as qualifiers. 109 if (RequiredQualifiers) 110 *RequiredQualifiers = std::move(*Signature); 111 Signature->clear(); 112 Snippet->clear(); 113 break; 114 case CodeCompletionString::CK_Text: 115 *Signature += Chunk.Text; 116 *Snippet += Chunk.Text; 117 break; 118 case CodeCompletionString::CK_Optional: 119 break; 120 case CodeCompletionString::CK_Placeholder: 121 *Signature += Chunk.Text; 122 ++ArgCount; 123 *Snippet += "${" + std::to_string(ArgCount) + ':'; 124 appendEscapeSnippet(Chunk.Text, Snippet); 125 *Snippet += '}'; 126 break; 127 case CodeCompletionString::CK_Informative: 128 // For example, the word "const" for a const method, or the name of 129 // the base class for methods that are part of the base class. 130 *Signature += Chunk.Text; 131 // Don't put the informative chunks in the snippet. 132 break; 133 case CodeCompletionString::CK_ResultType: 134 // This is not part of the signature. 135 break; 136 case CodeCompletionString::CK_CurrentParameter: 137 // This should never be present while collecting completion items, 138 // only while collecting overload candidates. 139 llvm_unreachable("Unexpected CK_CurrentParameter while collecting " 140 "CompletionItems"); 141 break; 142 case CodeCompletionString::CK_LeftParen: 143 case CodeCompletionString::CK_RightParen: 144 case CodeCompletionString::CK_LeftBracket: 145 case CodeCompletionString::CK_RightBracket: 146 case CodeCompletionString::CK_LeftBrace: 147 case CodeCompletionString::CK_RightBrace: 148 case CodeCompletionString::CK_LeftAngle: 149 case CodeCompletionString::CK_RightAngle: 150 case CodeCompletionString::CK_Comma: 151 case CodeCompletionString::CK_Colon: 152 case CodeCompletionString::CK_SemiColon: 153 case CodeCompletionString::CK_Equal: 154 case CodeCompletionString::CK_HorizontalSpace: 155 *Signature += Chunk.Text; 156 *Snippet += Chunk.Text; 157 break; 158 case CodeCompletionString::CK_VerticalSpace: 159 *Snippet += Chunk.Text; 160 // Don't even add a space to the signature. 161 break; 162 } 163 } 164 } 165 166 std::string formatDocumentation(const CodeCompletionString &CCS, 167 llvm::StringRef DocComment) { 168 // Things like __attribute__((nonnull(1,3))) and [[noreturn]]. Present this 169 // information in the documentation field. 170 std::string Result; 171 const unsigned AnnotationCount = CCS.getAnnotationCount(); 172 if (AnnotationCount > 0) { 173 Result += "Annotation"; 174 if (AnnotationCount == 1) { 175 Result += ": "; 176 } else /* AnnotationCount > 1 */ { 177 Result += "s: "; 178 } 179 for (unsigned I = 0; I < AnnotationCount; ++I) { 180 Result += CCS.getAnnotation(I); 181 Result.push_back(I == AnnotationCount - 1 ? '\n' : ' '); 182 } 183 } 184 // Add brief documentation (if there is any). 185 if (!DocComment.empty()) { 186 if (!Result.empty()) { 187 // This means we previously added annotations. Add an extra newline 188 // character to make the annotations stand out. 189 Result.push_back('\n'); 190 } 191 Result += DocComment; 192 } 193 return Result; 194 } 195 196 std::string getReturnType(const CodeCompletionString &CCS) { 197 for (const auto &Chunk : CCS) 198 if (Chunk.Kind == CodeCompletionString::CK_ResultType) 199 return Chunk.Text; 200 return ""; 201 } 202 203 } // namespace clangd 204 } // namespace clang 205