10#include "clang-c/Index.h"
11#include "clang/AST/ASTContext.h"
12#include "clang/AST/RawCommentList.h"
13#include "clang/Basic/SourceManager.h"
14#include "clang/Sema/CodeCompleteConsumer.h"
15#include "llvm/Support/Compiler.h"
16#include "llvm/Support/JSON.h"
24bool isInformativeQualifierChunk(CodeCompletionString::Chunk
const &Chunk) {
25 return Chunk.Kind == CodeCompletionString::CK_Informative &&
26 llvm::StringRef(Chunk.Text).ends_with(
"::");
29void appendEscapeSnippet(
const llvm::StringRef
Text, std::string *
Out) {
30 for (
const auto Character :
Text) {
31 if (Character ==
'$' || Character ==
'}' || Character ==
'\\')
33 Out->push_back(Character);
37void appendOptionalChunk(
const CodeCompletionString &CCS, std::string *
Out) {
38 for (
const CodeCompletionString::Chunk &
C : CCS) {
40 case CodeCompletionString::CK_Optional:
42 "Expected the optional code completion string to be non-null.");
43 appendOptionalChunk(*
C.Optional,
Out);
52bool looksLikeDocComment(llvm::StringRef CommentText) {
58 return CommentText.find_first_not_of(
"/*-= \t\r\n") != llvm::StringRef::npos;
63bool shouldPatchPlaceholder0(CodeCompletionResult::ResultKind ResultKind,
64 CXCursorKind CursorKind) {
65 bool CompletingPattern = ResultKind == CodeCompletionResult::RK_Pattern;
67 if (!CompletingPattern)
74 if (CursorKind == CXCursorKind::CXCursor_Constructor ||
75 CursorKind == CXCursorKind::CXCursor_Destructor)
84 const CodeCompletionResult &Result,
85 bool CommentsFromHeaders) {
89 if (Result.Kind != CodeCompletionResult::RK_Declaration)
91 return Result.getDeclaration() ?
getDeclComment(Ctx, *Result.getDeclaration())
96 if (isa<NamespaceDecl>(
Decl)) {
103 const RawComment *RC = getCompletionComment(Ctx, &
Decl);
108 assert(!Ctx.getSourceManager().isLoadedSourceLocation(RC->getBeginLoc()));
110 RC->getFormattedText(Ctx.getSourceManager(), Ctx.getDiagnostics());
111 if (!looksLikeDocComment(Doc))
114 if (!llvm::json::isUTF8(Doc))
115 Doc = llvm::json::fixUTF8(Doc);
121 CodeCompletionResult::ResultKind ResultKind,
122 CXCursorKind CursorKind,
bool IncludeFunctionArguments,
123 std::string *RequiredQualifiers) {
126 unsigned CursorSnippetArg = std::numeric_limits<unsigned>::max();
134 if (shouldPatchPlaceholder0(ResultKind, CursorKind)) {
136 llvm::count_if(CCS, [](
const CodeCompletionString::Chunk &
C) {
137 return C.Kind == CodeCompletionString::CK_Placeholder;
140 unsigned SnippetArg = 0;
141 bool HadObjCArguments =
false;
142 bool HadInformativeChunks =
false;
144 std::optional<unsigned> TruncateSnippetAt;
145 for (
const auto &Chunk : CCS) {
148 if (isInformativeQualifierChunk(Chunk))
151 switch (Chunk.Kind) {
152 case CodeCompletionString::CK_TypedText:
168 if (!llvm::StringRef(Chunk.Text).ends_with(
":")) {
169 if (RequiredQualifiers)
170 *RequiredQualifiers = std::move(*
Signature);
180 if (!HadObjCArguments) {
181 HadObjCArguments =
true;
199 if (!HadInformativeChunks) {
200 if (RequiredQualifiers)
201 *RequiredQualifiers = std::move(*
Signature);
211 case CodeCompletionString::CK_Text:
215 case CodeCompletionString::CK_Optional:
216 assert(Chunk.Optional);
218 appendOptionalChunk(*Chunk.Optional,
Signature);
220 case CodeCompletionString::CK_Placeholder:
223 if (SnippetArg == CursorSnippetArg) {
228 *
Snippet +=
"${" + std::to_string(SnippetArg) +
':';
229 appendEscapeSnippet(Chunk.Text,
Snippet);
233 case CodeCompletionString::CK_Informative:
234 HadInformativeChunks =
true;
240 case CodeCompletionString::CK_ResultType:
243 case CodeCompletionString::CK_CurrentParameter:
246 llvm_unreachable(
"Unexpected CK_CurrentParameter while collecting "
249 case CodeCompletionString::CK_LeftParen:
253 if (!IncludeFunctionArguments &&
254 ResultKind == CodeCompletionResult::RK_Declaration)
255 TruncateSnippetAt.emplace(
Snippet->size());
257 case CodeCompletionString::CK_RightParen:
258 case CodeCompletionString::CK_LeftBracket:
259 case CodeCompletionString::CK_RightBracket:
260 case CodeCompletionString::CK_LeftBrace:
261 case CodeCompletionString::CK_RightBrace:
262 case CodeCompletionString::CK_LeftAngle:
263 case CodeCompletionString::CK_RightAngle:
264 case CodeCompletionString::CK_Comma:
265 case CodeCompletionString::CK_Colon:
266 case CodeCompletionString::CK_SemiColon:
267 case CodeCompletionString::CK_Equal:
268 case CodeCompletionString::CK_HorizontalSpace:
272 case CodeCompletionString::CK_VerticalSpace:
278 if (TruncateSnippetAt)
283 llvm::StringRef DocComment) {
287 const unsigned AnnotationCount = CCS.getAnnotationCount();
288 if (AnnotationCount > 0) {
289 Result +=
"Annotation";
290 if (AnnotationCount == 1) {
295 for (
unsigned I = 0; I < AnnotationCount; ++I) {
296 Result += CCS.getAnnotation(I);
297 Result.push_back(I == AnnotationCount - 1 ?
'\n' :
' ');
301 if (!DocComment.empty()) {
302 if (!Result.empty()) {
305 Result.push_back(
'\n');
307 Result += DocComment;
313 for (
const auto &Chunk : CCS)
314 if (Chunk.Kind == CodeCompletionString::CK_ResultType)
const FunctionDecl * Decl
CompiledFragmentImpl & Out
std::string formatDocumentation(const CodeCompletionString &CCS, llvm::StringRef DocComment)
Assembles formatted documentation for a completion result.
std::string getDeclComment(const ASTContext &Ctx, const NamedDecl &Decl)
Similar to getDocComment, but returns the comment for a NamedDecl.
std::string getReturnType(const CodeCompletionString &CCS)
Gets detail to be used as the detail field in an LSP completion item.
void getSignature(const CodeCompletionString &CCS, std::string *Signature, std::string *Snippet, CodeCompletionResult::ResultKind ResultKind, CXCursorKind CursorKind, bool IncludeFunctionArguments, std::string *RequiredQualifiers)
Formats the signature for an item, as a display string and snippet.
std::string getDocComment(const ASTContext &Ctx, const CodeCompletionResult &Result, bool CommentsFromHeaders)
Gets a minimally formatted documentation comment of Result, with comment markers stripped.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//