blob: 8aa4f251992f54b2cca7fc853b37f2a2be7bf6ec [file] [log] [blame]
//===--- CodeCompletionStrings.cpp -------------------------------*- C++-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//
#include "CodeCompletionStrings.h"
#include <utility>
namespace clang {
namespace clangd {
namespace {
bool isInformativeQualifierChunk(CodeCompletionString::Chunk const &Chunk) {
return Chunk.Kind == CodeCompletionString::CK_Informative &&
StringRef(Chunk.Text).endswith("::");
}
void processPlainTextChunks(const CodeCompletionString &CCS,
std::string *LabelOut, std::string *InsertTextOut) {
std::string &Label = *LabelOut;
std::string &InsertText = *InsertTextOut;
for (const auto &Chunk : CCS) {
// Informative qualifier chunks only clutter completion results, skip
// them.
if (isInformativeQualifierChunk(Chunk))
continue;
switch (Chunk.Kind) {
case CodeCompletionString::CK_ResultType:
case CodeCompletionString::CK_Optional:
break;
case CodeCompletionString::CK_TypedText:
InsertText += Chunk.Text;
Label += Chunk.Text;
break;
default:
Label += Chunk.Text;
break;
}
}
}
void appendEscapeSnippet(const llvm::StringRef Text, std::string *Out) {
for (const auto Character : Text) {
if (Character == '$' || Character == '}' || Character == '\\')
Out->push_back('\\');
Out->push_back(Character);
}
}
void processSnippetChunks(const CodeCompletionString &CCS,
std::string *LabelOut, std::string *InsertTextOut) {
std::string &Label = *LabelOut;
std::string &InsertText = *InsertTextOut;
unsigned ArgCount = 0;
for (const auto &Chunk : CCS) {
// Informative qualifier chunks only clutter completion results, skip
// them.
if (isInformativeQualifierChunk(Chunk))
continue;
switch (Chunk.Kind) {
case CodeCompletionString::CK_TypedText:
case CodeCompletionString::CK_Text:
Label += Chunk.Text;
InsertText += Chunk.Text;
break;
case CodeCompletionString::CK_Optional:
// FIXME: Maybe add an option to allow presenting the optional chunks?
break;
case CodeCompletionString::CK_Placeholder:
++ArgCount;
InsertText += "${" + std::to_string(ArgCount) + ':';
appendEscapeSnippet(Chunk.Text, &InsertText);
InsertText += '}';
Label += Chunk.Text;
break;
case CodeCompletionString::CK_Informative:
// For example, the word "const" for a const method, or the name of
// the base class for methods that are part of the base class.
Label += Chunk.Text;
// Don't put the informative chunks in the insertText.
break;
case CodeCompletionString::CK_ResultType:
// This is retrieved as detail.
break;
case CodeCompletionString::CK_CurrentParameter:
// This should never be present while collecting completion items,
// only while collecting overload candidates.
llvm_unreachable("Unexpected CK_CurrentParameter while collecting "
"CompletionItems");
break;
case CodeCompletionString::CK_LeftParen:
case CodeCompletionString::CK_RightParen:
case CodeCompletionString::CK_LeftBracket:
case CodeCompletionString::CK_RightBracket:
case CodeCompletionString::CK_LeftBrace:
case CodeCompletionString::CK_RightBrace:
case CodeCompletionString::CK_LeftAngle:
case CodeCompletionString::CK_RightAngle:
case CodeCompletionString::CK_Comma:
case CodeCompletionString::CK_Colon:
case CodeCompletionString::CK_SemiColon:
case CodeCompletionString::CK_Equal:
case CodeCompletionString::CK_HorizontalSpace:
InsertText += Chunk.Text;
Label += Chunk.Text;
break;
case CodeCompletionString::CK_VerticalSpace:
InsertText += Chunk.Text;
// Don't even add a space to the label.
break;
}
}
}
} // namespace
void getLabelAndInsertText(const CodeCompletionString &CCS, std::string *Label,
std::string *InsertText, bool EnableSnippets) {
return EnableSnippets ? processSnippetChunks(CCS, Label, InsertText)
: processPlainTextChunks(CCS, Label, InsertText);
}
std::string getDocumentation(const CodeCompletionString &CCS) {
// Things like __attribute__((nonnull(1,3))) and [[noreturn]]. Present this
// information in the documentation field.
std::string Result;
const unsigned AnnotationCount = CCS.getAnnotationCount();
if (AnnotationCount > 0) {
Result += "Annotation";
if (AnnotationCount == 1) {
Result += ": ";
} else /* AnnotationCount > 1 */ {
Result += "s: ";
}
for (unsigned I = 0; I < AnnotationCount; ++I) {
Result += CCS.getAnnotation(I);
Result.push_back(I == AnnotationCount - 1 ? '\n' : ' ');
}
}
// Add brief documentation (if there is any).
if (CCS.getBriefComment() != nullptr) {
if (!Result.empty()) {
// This means we previously added annotations. Add an extra newline
// character to make the annotations stand out.
Result.push_back('\n');
}
Result += CCS.getBriefComment();
}
return Result;
}
std::string getDetail(const CodeCompletionString &CCS) {
for (const auto &Chunk : CCS) {
// Informative qualifier chunks only clutter completion results, skip
// them.
switch (Chunk.Kind) {
case CodeCompletionString::CK_ResultType:
return Chunk.Text;
default:
break;
}
}
return "";
}
std::string getFilterText(const CodeCompletionString &CCS) {
for (const auto &Chunk : CCS) {
switch (Chunk.Kind) {
case CodeCompletionString::CK_TypedText:
// There's always exactly one CK_TypedText chunk.
return Chunk.Text;
default:
break;
}
}
return "";
}
} // namespace clangd
} // namespace clang