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 namespace { 20 21 bool isInformativeQualifierChunk(CodeCompletionString::Chunk const &Chunk) { 22 return Chunk.Kind == CodeCompletionString::CK_Informative && 23 llvm::StringRef(Chunk.Text).endswith("::"); 24 } 25 26 void appendEscapeSnippet(const llvm::StringRef Text, std::string *Out) { 27 for (const auto Character : Text) { 28 if (Character == '$' || Character == '}' || Character == '\\') 29 Out->push_back('\\'); 30 Out->push_back(Character); 31 } 32 } 33 34 bool looksLikeDocComment(llvm::StringRef CommentText) { 35 // We don't report comments that only contain "special" chars. 36 // This avoids reporting various delimiters, like: 37 // ================= 38 // ----------------- 39 // ***************** 40 return CommentText.find_first_not_of("/*-= \t\r\n") != llvm::StringRef::npos; 41 } 42 43 } // namespace 44 45 std::string getDocComment(const ASTContext &Ctx, 46 const CodeCompletionResult &Result, 47 bool CommentsFromHeaders) { 48 // FIXME: clang's completion also returns documentation for RK_Pattern if they 49 // contain a pattern for ObjC properties. Unfortunately, there is no API to 50 // get this declaration, so we don't show documentation in that case. 51 if (Result.Kind != CodeCompletionResult::RK_Declaration) 52 return ""; 53 return Result.getDeclaration() ? getDeclComment(Ctx, *Result.getDeclaration()) 54 : ""; 55 } 56 57 std::string getDeclComment(const ASTContext &Ctx, const NamedDecl &Decl) { 58 if (isa<NamespaceDecl>(Decl)) { 59 // Namespaces often have too many redecls for any particular redecl comment 60 // to be useful. Moreover, we often confuse file headers or generated 61 // comments with namespace comments. Therefore we choose to just ignore 62 // the comments for namespaces. 63 return ""; 64 } 65 const RawComment *RC = getCompletionComment(Ctx, &Decl); 66 if (!RC) 67 return ""; 68 // Sanity check that the comment does not come from the PCH. We choose to not 69 // write them into PCH, because they are racy and slow to load. 70 assert(!Ctx.getSourceManager().isLoadedSourceLocation(RC->getBeginLoc())); 71 std::string Doc = 72 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 bool HadObjCArguments = false; 80 for (const auto &Chunk : CCS) { 81 // Informative qualifier chunks only clutter completion results, skip 82 // them. 83 if (isInformativeQualifierChunk(Chunk)) 84 continue; 85 86 switch (Chunk.Kind) { 87 case CodeCompletionString::CK_TypedText: 88 // The typed-text chunk is the actual name. We don't record this chunk. 89 // C++: 90 // In general our string looks like <qualifiers><name><signature>. 91 // So once we see the name, any text we recorded so far should be 92 // reclassified as qualifiers. 93 // 94 // Objective-C: 95 // Objective-C methods may have multiple typed-text chunks, so we must 96 // treat them carefully. For Objective-C methods, all typed-text chunks 97 // will end in ':' (unless there are no arguments, in which case we 98 // can safely treat them as C++). 99 if (!llvm::StringRef(Chunk.Text).endswith(":")) { // Treat as C++. 100 if (RequiredQualifiers) 101 *RequiredQualifiers = std::move(*Signature); 102 Signature->clear(); 103 Snippet->clear(); 104 } else { // Objective-C method with args. 105 // If this is the first TypedText to the Objective-C method, discard any 106 // text that we've previously seen (such as previous parameter selector, 107 // which will be marked as Informative text). 108 // 109 // TODO: Make previous parameters part of the signature for Objective-C 110 // methods. 111 if (!HadObjCArguments) { 112 HadObjCArguments = true; 113 Signature->clear(); 114 } else { // Subsequent argument, considered part of snippet/signature. 115 *Signature += Chunk.Text; 116 *Snippet += Chunk.Text; 117 } 118 } 119 break; 120 case CodeCompletionString::CK_Text: 121 *Signature += Chunk.Text; 122 *Snippet += Chunk.Text; 123 break; 124 case CodeCompletionString::CK_Optional: 125 break; 126 case CodeCompletionString::CK_Placeholder: 127 *Signature += Chunk.Text; 128 ++ArgCount; 129 *Snippet += "${" + std::to_string(ArgCount) + ':'; 130 appendEscapeSnippet(Chunk.Text, Snippet); 131 *Snippet += '}'; 132 break; 133 case CodeCompletionString::CK_Informative: 134 // For example, the word "const" for a const method, or the name of 135 // the base class for methods that are part of the base class. 136 *Signature += Chunk.Text; 137 // Don't put the informative chunks in the snippet. 138 break; 139 case CodeCompletionString::CK_ResultType: 140 // This is not part of the signature. 141 break; 142 case CodeCompletionString::CK_CurrentParameter: 143 // This should never be present while collecting completion items, 144 // only while collecting overload candidates. 145 llvm_unreachable("Unexpected CK_CurrentParameter while collecting " 146 "CompletionItems"); 147 break; 148 case CodeCompletionString::CK_LeftParen: 149 case CodeCompletionString::CK_RightParen: 150 case CodeCompletionString::CK_LeftBracket: 151 case CodeCompletionString::CK_RightBracket: 152 case CodeCompletionString::CK_LeftBrace: 153 case CodeCompletionString::CK_RightBrace: 154 case CodeCompletionString::CK_LeftAngle: 155 case CodeCompletionString::CK_RightAngle: 156 case CodeCompletionString::CK_Comma: 157 case CodeCompletionString::CK_Colon: 158 case CodeCompletionString::CK_SemiColon: 159 case CodeCompletionString::CK_Equal: 160 case CodeCompletionString::CK_HorizontalSpace: 161 *Signature += Chunk.Text; 162 *Snippet += Chunk.Text; 163 break; 164 case CodeCompletionString::CK_VerticalSpace: 165 *Snippet += Chunk.Text; 166 // Don't even add a space to the signature. 167 break; 168 } 169 } 170 } 171 172 std::string formatDocumentation(const CodeCompletionString &CCS, 173 llvm::StringRef DocComment) { 174 // Things like __attribute__((nonnull(1,3))) and [[noreturn]]. Present this 175 // information in the documentation field. 176 std::string Result; 177 const unsigned AnnotationCount = CCS.getAnnotationCount(); 178 if (AnnotationCount > 0) { 179 Result += "Annotation"; 180 if (AnnotationCount == 1) { 181 Result += ": "; 182 } else /* AnnotationCount > 1 */ { 183 Result += "s: "; 184 } 185 for (unsigned I = 0; I < AnnotationCount; ++I) { 186 Result += CCS.getAnnotation(I); 187 Result.push_back(I == AnnotationCount - 1 ? '\n' : ' '); 188 } 189 } 190 // Add brief documentation (if there is any). 191 if (!DocComment.empty()) { 192 if (!Result.empty()) { 193 // This means we previously added annotations. Add an extra newline 194 // character to make the annotations stand out. 195 Result.push_back('\n'); 196 } 197 Result += DocComment; 198 } 199 return Result; 200 } 201 202 std::string getReturnType(const CodeCompletionString &CCS) { 203 for (const auto &Chunk : CCS) 204 if (Chunk.Kind == CodeCompletionString::CK_ResultType) 205 return Chunk.Text; 206 return ""; 207 } 208 209 } // namespace clangd 210 } // namespace clang 211