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/RawCommentList.h" 13 #include <utility> 14 15 namespace clang { 16 namespace clangd { 17 18 namespace { 19 20 bool isInformativeQualifierChunk(CodeCompletionString::Chunk const &Chunk) { 21 return Chunk.Kind == CodeCompletionString::CK_Informative && 22 StringRef(Chunk.Text).endswith("::"); 23 } 24 25 void processPlainTextChunks(const CodeCompletionString &CCS, 26 std::string *LabelOut, std::string *InsertTextOut) { 27 std::string &Label = *LabelOut; 28 std::string &InsertText = *InsertTextOut; 29 for (const auto &Chunk : CCS) { 30 // Informative qualifier chunks only clutter completion results, skip 31 // them. 32 if (isInformativeQualifierChunk(Chunk)) 33 continue; 34 35 switch (Chunk.Kind) { 36 case CodeCompletionString::CK_ResultType: 37 case CodeCompletionString::CK_Optional: 38 break; 39 case CodeCompletionString::CK_TypedText: 40 InsertText += Chunk.Text; 41 Label += Chunk.Text; 42 break; 43 default: 44 Label += Chunk.Text; 45 break; 46 } 47 } 48 } 49 50 void appendEscapeSnippet(const llvm::StringRef Text, std::string *Out) { 51 for (const auto Character : Text) { 52 if (Character == '$' || Character == '}' || Character == '\\') 53 Out->push_back('\\'); 54 Out->push_back(Character); 55 } 56 } 57 58 void processSnippetChunks(const CodeCompletionString &CCS, 59 std::string *LabelOut, std::string *InsertTextOut) { 60 std::string &Label = *LabelOut; 61 std::string &InsertText = *InsertTextOut; 62 63 unsigned ArgCount = 0; 64 for (const auto &Chunk : CCS) { 65 // Informative qualifier chunks only clutter completion results, skip 66 // them. 67 if (isInformativeQualifierChunk(Chunk)) 68 continue; 69 70 switch (Chunk.Kind) { 71 case CodeCompletionString::CK_TypedText: 72 case CodeCompletionString::CK_Text: 73 Label += Chunk.Text; 74 InsertText += Chunk.Text; 75 break; 76 case CodeCompletionString::CK_Optional: 77 // FIXME: Maybe add an option to allow presenting the optional chunks? 78 break; 79 case CodeCompletionString::CK_Placeholder: 80 ++ArgCount; 81 InsertText += "${" + std::to_string(ArgCount) + ':'; 82 appendEscapeSnippet(Chunk.Text, &InsertText); 83 InsertText += '}'; 84 Label += Chunk.Text; 85 break; 86 case CodeCompletionString::CK_Informative: 87 // For example, the word "const" for a const method, or the name of 88 // the base class for methods that are part of the base class. 89 Label += Chunk.Text; 90 // Don't put the informative chunks in the insertText. 91 break; 92 case CodeCompletionString::CK_ResultType: 93 // This is retrieved as detail. 94 break; 95 case CodeCompletionString::CK_CurrentParameter: 96 // This should never be present while collecting completion items, 97 // only while collecting overload candidates. 98 llvm_unreachable("Unexpected CK_CurrentParameter while collecting " 99 "CompletionItems"); 100 break; 101 case CodeCompletionString::CK_LeftParen: 102 case CodeCompletionString::CK_RightParen: 103 case CodeCompletionString::CK_LeftBracket: 104 case CodeCompletionString::CK_RightBracket: 105 case CodeCompletionString::CK_LeftBrace: 106 case CodeCompletionString::CK_RightBrace: 107 case CodeCompletionString::CK_LeftAngle: 108 case CodeCompletionString::CK_RightAngle: 109 case CodeCompletionString::CK_Comma: 110 case CodeCompletionString::CK_Colon: 111 case CodeCompletionString::CK_SemiColon: 112 case CodeCompletionString::CK_Equal: 113 case CodeCompletionString::CK_HorizontalSpace: 114 InsertText += Chunk.Text; 115 Label += Chunk.Text; 116 break; 117 case CodeCompletionString::CK_VerticalSpace: 118 InsertText += Chunk.Text; 119 // Don't even add a space to the label. 120 break; 121 } 122 } 123 } 124 125 } // namespace 126 127 std::string getDocComment(const ASTContext &Ctx, 128 const CodeCompletionResult &Result) { 129 // FIXME: clang's completion also returns documentation for RK_Pattern if they 130 // contain a pattern for ObjC properties. Unfortunately, there is no API to 131 // get this declaration, so we don't show documentation in that case. 132 if (Result.Kind != CodeCompletionResult::RK_Declaration) 133 return ""; 134 auto Decl = Result.getDeclaration(); 135 if (!Decl) 136 return ""; 137 const RawComment *RC = getCompletionComment(Ctx, Decl); 138 if (!RC) 139 return ""; 140 return RC->getFormattedText(Ctx.getSourceManager(), Ctx.getDiagnostics()); 141 } 142 143 std::string 144 getParameterDocComment(const ASTContext &Ctx, 145 const CodeCompleteConsumer::OverloadCandidate &Result, 146 unsigned ArgIndex) { 147 auto Func = Result.getFunction(); 148 if (!Func) 149 return ""; 150 const RawComment *RC = getParameterComment(Ctx, Result, ArgIndex); 151 if (!RC) 152 return ""; 153 return RC->getFormattedText(Ctx.getSourceManager(), Ctx.getDiagnostics()); 154 } 155 156 void getLabelAndInsertText(const CodeCompletionString &CCS, std::string *Label, 157 std::string *InsertText, bool EnableSnippets) { 158 return EnableSnippets ? processSnippetChunks(CCS, Label, InsertText) 159 : processPlainTextChunks(CCS, Label, InsertText); 160 } 161 162 std::string formatDocumentation(const CodeCompletionString &CCS, 163 llvm::StringRef DocComment) { 164 // Things like __attribute__((nonnull(1,3))) and [[noreturn]]. Present this 165 // information in the documentation field. 166 std::string Result; 167 const unsigned AnnotationCount = CCS.getAnnotationCount(); 168 if (AnnotationCount > 0) { 169 Result += "Annotation"; 170 if (AnnotationCount == 1) { 171 Result += ": "; 172 } else /* AnnotationCount > 1 */ { 173 Result += "s: "; 174 } 175 for (unsigned I = 0; I < AnnotationCount; ++I) { 176 Result += CCS.getAnnotation(I); 177 Result.push_back(I == AnnotationCount - 1 ? '\n' : ' '); 178 } 179 } 180 // Add brief documentation (if there is any). 181 if (!DocComment.empty()) { 182 if (!Result.empty()) { 183 // This means we previously added annotations. Add an extra newline 184 // character to make the annotations stand out. 185 Result.push_back('\n'); 186 } 187 Result += DocComment; 188 } 189 return Result; 190 } 191 192 std::string getDetail(const CodeCompletionString &CCS) { 193 for (const auto &Chunk : CCS) { 194 // Informative qualifier chunks only clutter completion results, skip 195 // them. 196 switch (Chunk.Kind) { 197 case CodeCompletionString::CK_ResultType: 198 return Chunk.Text; 199 default: 200 break; 201 } 202 } 203 return ""; 204 } 205 206 std::string getFilterText(const CodeCompletionString &CCS) { 207 for (const auto &Chunk : CCS) { 208 switch (Chunk.Kind) { 209 case CodeCompletionString::CK_TypedText: 210 // There's always exactly one CK_TypedText chunk. 211 return Chunk.Text; 212 default: 213 break; 214 } 215 } 216 return ""; 217 } 218 219 } // namespace clangd 220 } // namespace clang 221