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