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