mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-01-11 18:36:55 +00:00
[clangd] Pull CodeCompletionString handling logic into its own file and add unit test.
Reviewers: sammccall Subscribers: klimek, mgorny, ilya-biryukov, cfe-commits Differential Revision: https://reviews.llvm.org/D41450 llvm-svn: 321193
This commit is contained in:
parent
188adaf46b
commit
63696e14e3
@ -8,6 +8,7 @@ add_clang_library(clangDaemon
|
||||
ClangdUnit.cpp
|
||||
ClangdUnitStore.cpp
|
||||
CodeComplete.cpp
|
||||
CodeCompletionStrings.cpp
|
||||
Context.cpp
|
||||
Compiler.cpp
|
||||
DraftStore.cpp
|
||||
|
@ -15,6 +15,7 @@
|
||||
//===---------------------------------------------------------------------===//
|
||||
|
||||
#include "CodeComplete.h"
|
||||
#include "CodeCompletionStrings.h"
|
||||
#include "Compiler.h"
|
||||
#include "Logger.h"
|
||||
#include "index/Index.h"
|
||||
@ -144,46 +145,6 @@ CompletionItemKind toCompletionItemKind(index::SymbolKind Kind) {
|
||||
llvm_unreachable("Unhandled clang::index::SymbolKind.");
|
||||
}
|
||||
|
||||
std::string escapeSnippet(const llvm::StringRef Text) {
|
||||
std::string Result;
|
||||
Result.reserve(Text.size()); // Assume '$', '}' and '\\' are rare.
|
||||
for (const auto Character : Text) {
|
||||
if (Character == '$' || Character == '}' || Character == '\\')
|
||||
Result.push_back('\\');
|
||||
Result.push_back(Character);
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/// Get the optional chunk as a string. This function is possibly recursive.
|
||||
///
|
||||
/// The parameter info for each parameter is appended to the Parameters.
|
||||
@ -320,7 +281,8 @@ public:
|
||||
/*OutputIsBinary=*/false),
|
||||
ClangdOpts(CodeCompleteOpts), Items(Items),
|
||||
Allocator(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
|
||||
CCTUInfo(Allocator), CompletedName(CompletedName) {}
|
||||
CCTUInfo(Allocator), CompletedName(CompletedName),
|
||||
EnableSnippets(CodeCompleteOpts.EnableSnippets) {}
|
||||
|
||||
void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context,
|
||||
CodeCompletionResult *Results,
|
||||
@ -402,14 +364,16 @@ private:
|
||||
// Adjust this to InsertTextFormat::Snippet iff we encounter a
|
||||
// CK_Placeholder chunk in SnippetCompletionItemsCollector.
|
||||
CompletionItem Item;
|
||||
Item.insertTextFormat = InsertTextFormat::PlainText;
|
||||
|
||||
Item.documentation = getDocumentation(CCS);
|
||||
Item.sortText = Candidate.sortText();
|
||||
|
||||
// Fill in the label, detail, insertText and filterText fields of the
|
||||
// CompletionItem.
|
||||
ProcessChunks(CCS, Item);
|
||||
Item.detail = getDetail(CCS);
|
||||
Item.filterText = getFilterText(CCS);
|
||||
getLabelAndInsertText(CCS, &Item.label, &Item.insertText, EnableSnippets);
|
||||
|
||||
Item.insertTextFormat = EnableSnippets ? InsertTextFormat::Snippet
|
||||
: InsertTextFormat::PlainText;
|
||||
|
||||
// Fill in the kind field of the CompletionItem.
|
||||
Item.kind = toCompletionItemKind(Candidate.Result->Kind,
|
||||
@ -418,170 +382,14 @@ private:
|
||||
return Item;
|
||||
}
|
||||
|
||||
virtual void ProcessChunks(const CodeCompletionString &CCS,
|
||||
CompletionItem &Item) const = 0;
|
||||
|
||||
CodeCompleteOptions ClangdOpts;
|
||||
CompletionList &Items;
|
||||
std::shared_ptr<clang::GlobalCodeCompletionAllocator> Allocator;
|
||||
CodeCompletionTUInfo CCTUInfo;
|
||||
NameToComplete &CompletedName;
|
||||
bool EnableSnippets;
|
||||
}; // CompletionItemsCollector
|
||||
|
||||
bool isInformativeQualifierChunk(CodeCompletionString::Chunk const &Chunk) {
|
||||
return Chunk.Kind == CodeCompletionString::CK_Informative &&
|
||||
StringRef(Chunk.Text).endswith("::");
|
||||
}
|
||||
|
||||
class PlainTextCompletionItemsCollector final
|
||||
: public CompletionItemsCollector {
|
||||
|
||||
public:
|
||||
PlainTextCompletionItemsCollector(const CodeCompleteOptions &CodeCompleteOpts,
|
||||
CompletionList &Items,
|
||||
NameToComplete &CompletedName)
|
||||
: CompletionItemsCollector(CodeCompleteOpts, Items, CompletedName) {}
|
||||
|
||||
private:
|
||||
void ProcessChunks(const CodeCompletionString &CCS,
|
||||
CompletionItem &Item) const override {
|
||||
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:
|
||||
// There's always exactly one CK_TypedText chunk.
|
||||
Item.insertText = Item.filterText = Chunk.Text;
|
||||
Item.label += Chunk.Text;
|
||||
break;
|
||||
case CodeCompletionString::CK_ResultType:
|
||||
assert(Item.detail.empty() && "Unexpected extraneous CK_ResultType");
|
||||
Item.detail = Chunk.Text;
|
||||
break;
|
||||
case CodeCompletionString::CK_Optional:
|
||||
break;
|
||||
default:
|
||||
Item.label += Chunk.Text;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}; // PlainTextCompletionItemsCollector
|
||||
|
||||
class SnippetCompletionItemsCollector final : public CompletionItemsCollector {
|
||||
|
||||
public:
|
||||
SnippetCompletionItemsCollector(const CodeCompleteOptions &CodeCompleteOpts,
|
||||
CompletionList &Items,
|
||||
NameToComplete &CompletedName)
|
||||
: CompletionItemsCollector(CodeCompleteOpts, Items, CompletedName) {}
|
||||
|
||||
private:
|
||||
void ProcessChunks(const CodeCompletionString &CCS,
|
||||
CompletionItem &Item) const override {
|
||||
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:
|
||||
// The piece of text that the user is expected to type to match
|
||||
// the code-completion string, typically a keyword or the name of
|
||||
// a declarator or macro.
|
||||
Item.filterText = Chunk.Text;
|
||||
LLVM_FALLTHROUGH;
|
||||
case CodeCompletionString::CK_Text:
|
||||
// A piece of text that should be placed in the buffer,
|
||||
// e.g., parentheses or a comma in a function call.
|
||||
Item.label += Chunk.Text;
|
||||
Item.insertText += Chunk.Text;
|
||||
break;
|
||||
case CodeCompletionString::CK_Optional:
|
||||
// A code completion string that is entirely optional.
|
||||
// For example, an optional code completion string that
|
||||
// describes the default arguments in a function call.
|
||||
|
||||
// FIXME: Maybe add an option to allow presenting the optional chunks?
|
||||
break;
|
||||
case CodeCompletionString::CK_Placeholder:
|
||||
// A string that acts as a placeholder for, e.g., a function call
|
||||
// argument.
|
||||
++ArgCount;
|
||||
Item.insertText += "${" + std::to_string(ArgCount) + ':' +
|
||||
escapeSnippet(Chunk.Text) + '}';
|
||||
Item.label += Chunk.Text;
|
||||
Item.insertTextFormat = InsertTextFormat::Snippet;
|
||||
break;
|
||||
case CodeCompletionString::CK_Informative:
|
||||
// A piece of text that describes something about the result
|
||||
// but should not be inserted into the buffer.
|
||||
// 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.
|
||||
Item.label += Chunk.Text;
|
||||
// Don't put the informative chunks in the insertText.
|
||||
break;
|
||||
case CodeCompletionString::CK_ResultType:
|
||||
// A piece of text that describes the type of an entity or,
|
||||
// for functions and methods, the return type.
|
||||
assert(Item.detail.empty() && "Unexpected extraneous CK_ResultType");
|
||||
Item.detail = Chunk.Text;
|
||||
break;
|
||||
case CodeCompletionString::CK_CurrentParameter:
|
||||
// A piece of text that describes the parameter that corresponds to
|
||||
// the code-completion location within a function call, message send,
|
||||
// macro invocation, etc.
|
||||
//
|
||||
// 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:
|
||||
// A left parenthesis ('(').
|
||||
case CodeCompletionString::CK_RightParen:
|
||||
// A right parenthesis (')').
|
||||
case CodeCompletionString::CK_LeftBracket:
|
||||
// A left bracket ('[').
|
||||
case CodeCompletionString::CK_RightBracket:
|
||||
// A right bracket (']').
|
||||
case CodeCompletionString::CK_LeftBrace:
|
||||
// A left brace ('{').
|
||||
case CodeCompletionString::CK_RightBrace:
|
||||
// A right brace ('}').
|
||||
case CodeCompletionString::CK_LeftAngle:
|
||||
// A left angle bracket ('<').
|
||||
case CodeCompletionString::CK_RightAngle:
|
||||
// A right angle bracket ('>').
|
||||
case CodeCompletionString::CK_Comma:
|
||||
// A comma separator (',').
|
||||
case CodeCompletionString::CK_Colon:
|
||||
// A colon (':').
|
||||
case CodeCompletionString::CK_SemiColon:
|
||||
// A semicolon (';').
|
||||
case CodeCompletionString::CK_Equal:
|
||||
// An '=' sign.
|
||||
case CodeCompletionString::CK_HorizontalSpace:
|
||||
// Horizontal whitespace (' ').
|
||||
Item.insertText += Chunk.Text;
|
||||
Item.label += Chunk.Text;
|
||||
break;
|
||||
case CodeCompletionString::CK_VerticalSpace:
|
||||
// Vertical whitespace ('\n' or '\r\n', depending on the
|
||||
// platform).
|
||||
Item.insertText += Chunk.Text;
|
||||
// Don't even add a space to the label.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}; // SnippetCompletionItemsCollector
|
||||
|
||||
class SignatureHelpCollector final : public CodeCompleteConsumer {
|
||||
|
||||
public:
|
||||
@ -617,6 +425,8 @@ public:
|
||||
CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
|
||||
|
||||
private:
|
||||
// FIXME(ioeric): consider moving CodeCompletionString logic here to
|
||||
// CompletionString.h.
|
||||
SignatureInformation
|
||||
ProcessOverloadCandidate(const OverloadCandidate &Candidate,
|
||||
const CodeCompletionString &CCS) const {
|
||||
@ -817,15 +627,9 @@ CompletionList codeComplete(const Context &Ctx, PathRef FileName,
|
||||
std::shared_ptr<PCHContainerOperations> PCHs,
|
||||
CodeCompleteOptions Opts) {
|
||||
CompletionList Results;
|
||||
std::unique_ptr<CodeCompleteConsumer> Consumer;
|
||||
NameToComplete CompletedName;
|
||||
if (Opts.EnableSnippets) {
|
||||
Consumer = llvm::make_unique<SnippetCompletionItemsCollector>(
|
||||
Opts, Results, CompletedName);
|
||||
} else {
|
||||
Consumer = llvm::make_unique<PlainTextCompletionItemsCollector>(
|
||||
Opts, Results, CompletedName);
|
||||
}
|
||||
auto Consumer =
|
||||
llvm::make_unique<CompletionItemsCollector>(Opts, Results, CompletedName);
|
||||
invokeCodeComplete(Ctx, std::move(Consumer), Opts.getClangCompleteOpts(),
|
||||
FileName, Command, Preamble, Contents, Pos, std::move(VFS),
|
||||
std::move(PCHs));
|
||||
|
188
clang-tools-extra/clangd/CodeCompletionStrings.cpp
Normal file
188
clang-tools-extra/clangd/CodeCompletionStrings.cpp
Normal file
@ -0,0 +1,188 @@
|
||||
//===--- 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
|
46
clang-tools-extra/clangd/CodeCompletionStrings.h
Normal file
46
clang-tools-extra/clangd/CodeCompletionStrings.h
Normal file
@ -0,0 +1,46 @@
|
||||
//===--- CodeCompletionStrings.h ---------------------------------*- C++-*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===---------------------------------------------------------------------===//
|
||||
//
|
||||
// Functions for retrieving code completion information from
|
||||
// `CodeCompletionString`.
|
||||
//
|
||||
//===---------------------------------------------------------------------===//
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CODECOMPLETIONSTRINGS_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CODECOMPLETIONSTRINGS_H
|
||||
|
||||
#include "clang/Sema/CodeCompleteConsumer.h"
|
||||
|
||||
namespace clang {
|
||||
namespace clangd {
|
||||
|
||||
/// Gets label and insert text for a completion item. For example, for function
|
||||
/// `Foo`, this returns <"Foo(int x, int y)", "Foo"> without snippts enabled.
|
||||
///
|
||||
/// If \p EnableSnippets is true, this will try to use snippet for the insert
|
||||
/// text. Otherwise, the insert text will always be plain text.
|
||||
void getLabelAndInsertText(const CodeCompletionString &CCS, std::string *Label,
|
||||
std::string *InsertText, bool EnableSnippets);
|
||||
|
||||
/// Gets the documentation for a completion item. For example, comment for the
|
||||
/// a class declaration.
|
||||
std::string getDocumentation(const CodeCompletionString &CCS);
|
||||
|
||||
/// Gets detail to be used as the detail field in an LSP completion item. This
|
||||
/// is usually the return type of a function.
|
||||
std::string getDetail(const CodeCompletionString &CCS);
|
||||
|
||||
/// Gets the piece of text that the user is expected to type to match the
|
||||
/// code-completion string, typically a keyword or the name of a declarator or
|
||||
/// macro.
|
||||
std::string getFilterText(const CodeCompletionString &CCS);
|
||||
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
||||
|
||||
#endif
|
@ -12,6 +12,7 @@ add_extra_unittest(ClangdTests
|
||||
Annotations.cpp
|
||||
ClangdTests.cpp
|
||||
CodeCompleteTests.cpp
|
||||
CodeCompletionStringsTests.cpp
|
||||
ContextTests.cpp
|
||||
FileIndexTests.cpp
|
||||
FuzzyMatchTests.cpp
|
||||
|
@ -341,7 +341,7 @@ TEST(CompletionTest, Snippets) {
|
||||
)cpp",
|
||||
Opts);
|
||||
EXPECT_THAT(Results.items,
|
||||
HasSubsequence(PlainText("a"),
|
||||
HasSubsequence(Snippet("a"),
|
||||
Snippet("f(${1:int i}, ${2:const float f})")));
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,142 @@
|
||||
//===-- CodeCompletionStringsTests.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 "clang/Sema/CodeCompleteConsumer.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace clang {
|
||||
namespace clangd {
|
||||
namespace {
|
||||
|
||||
class CompletionStringTest : public ::testing::Test {
|
||||
public:
|
||||
CompletionStringTest()
|
||||
: Allocator(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
|
||||
CCTUInfo(Allocator), Builder(*Allocator, CCTUInfo) {}
|
||||
|
||||
protected:
|
||||
void labelAndInsertText(const CodeCompletionString &CCS,
|
||||
bool EnableSnippets = false) {
|
||||
Label.clear();
|
||||
InsertText.clear();
|
||||
getLabelAndInsertText(CCS, &Label, &InsertText, EnableSnippets);
|
||||
}
|
||||
|
||||
std::shared_ptr<clang::GlobalCodeCompletionAllocator> Allocator;
|
||||
CodeCompletionTUInfo CCTUInfo;
|
||||
CodeCompletionBuilder Builder;
|
||||
std::string Label;
|
||||
std::string InsertText;
|
||||
};
|
||||
|
||||
TEST_F(CompletionStringTest, Detail) {
|
||||
Builder.AddResultTypeChunk("result");
|
||||
Builder.AddResultTypeChunk("redundant result no no");
|
||||
EXPECT_EQ(getDetail(*Builder.TakeString()), "result");
|
||||
}
|
||||
|
||||
TEST_F(CompletionStringTest, FilterText) {
|
||||
Builder.AddTypedTextChunk("typed");
|
||||
Builder.AddTypedTextChunk("redundant typed no no");
|
||||
auto *S = Builder.TakeString();
|
||||
EXPECT_EQ(getFilterText(*S), "typed");
|
||||
}
|
||||
|
||||
TEST_F(CompletionStringTest, Documentation) {
|
||||
Builder.addBriefComment("Is this brief?");
|
||||
EXPECT_EQ(getDocumentation(*Builder.TakeString()), "Is this brief?");
|
||||
}
|
||||
|
||||
TEST_F(CompletionStringTest, DocumentationWithAnnotation) {
|
||||
Builder.addBriefComment("Is this brief?");
|
||||
Builder.AddAnnotation("Ano");
|
||||
EXPECT_EQ(getDocumentation(*Builder.TakeString()),
|
||||
"Annotation: Ano\n\nIs this brief?");
|
||||
}
|
||||
|
||||
TEST_F(CompletionStringTest, MultipleAnnotations) {
|
||||
Builder.AddAnnotation("Ano1");
|
||||
Builder.AddAnnotation("Ano2");
|
||||
Builder.AddAnnotation("Ano3");
|
||||
|
||||
EXPECT_EQ(getDocumentation(*Builder.TakeString()),
|
||||
"Annotations: Ano1 Ano2 Ano3\n");
|
||||
}
|
||||
|
||||
TEST_F(CompletionStringTest, SimpleLabelAndInsert) {
|
||||
Builder.AddTypedTextChunk("X");
|
||||
Builder.AddResultTypeChunk("result no no");
|
||||
labelAndInsertText(*Builder.TakeString());
|
||||
EXPECT_EQ(Label, "X");
|
||||
EXPECT_EQ(InsertText, "X");
|
||||
}
|
||||
|
||||
TEST_F(CompletionStringTest, FunctionPlainText) {
|
||||
Builder.AddResultTypeChunk("result no no");
|
||||
Builder.AddTypedTextChunk("Foo");
|
||||
Builder.AddChunk(CodeCompletionString::CK_LeftParen);
|
||||
Builder.AddPlaceholderChunk("p1");
|
||||
Builder.AddChunk(CodeCompletionString::CK_Comma);
|
||||
Builder.AddPlaceholderChunk("p2");
|
||||
Builder.AddChunk(CodeCompletionString::CK_RightParen);
|
||||
Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
|
||||
Builder.AddInformativeChunk("const");
|
||||
|
||||
labelAndInsertText(*Builder.TakeString());
|
||||
EXPECT_EQ(Label, "Foo(p1, p2) const");
|
||||
EXPECT_EQ(InsertText, "Foo");
|
||||
}
|
||||
|
||||
TEST_F(CompletionStringTest, FunctionSnippet) {
|
||||
Builder.AddResultTypeChunk("result no no");
|
||||
Builder.addBriefComment("Foo's comment");
|
||||
Builder.AddTypedTextChunk("Foo");
|
||||
Builder.AddChunk(CodeCompletionString::CK_LeftParen);
|
||||
Builder.AddPlaceholderChunk("p1");
|
||||
Builder.AddChunk(CodeCompletionString::CK_Comma);
|
||||
Builder.AddPlaceholderChunk("p2");
|
||||
Builder.AddChunk(CodeCompletionString::CK_RightParen);
|
||||
|
||||
auto *CCS = Builder.TakeString();
|
||||
labelAndInsertText(*CCS);
|
||||
EXPECT_EQ(Label, "Foo(p1, p2)");
|
||||
EXPECT_EQ(InsertText, "Foo");
|
||||
|
||||
labelAndInsertText(*CCS, /*EnableSnippets=*/true);
|
||||
EXPECT_EQ(Label, "Foo(p1, p2)");
|
||||
EXPECT_EQ(InsertText, "Foo(${1:p1}, ${2:p2})");
|
||||
EXPECT_EQ(getDocumentation(*CCS), "Foo's comment");
|
||||
EXPECT_EQ(getFilterText(*CCS), "Foo");
|
||||
}
|
||||
|
||||
TEST_F(CompletionStringTest, EscapeSnippet) {
|
||||
Builder.AddTypedTextChunk("Foo");
|
||||
Builder.AddChunk(CodeCompletionString::CK_LeftParen);
|
||||
Builder.AddPlaceholderChunk("$p}1\\");
|
||||
Builder.AddChunk(CodeCompletionString::CK_RightParen);
|
||||
|
||||
labelAndInsertText(*Builder.TakeString(), /*EnableSnippets=*/true);
|
||||
EXPECT_EQ(Label, "Foo($p}1\\)");
|
||||
EXPECT_EQ(InsertText, "Foo(${1:\\$p\\}1\\\\})");
|
||||
}
|
||||
|
||||
TEST_F(CompletionStringTest, IgnoreInformativeQualifier) {
|
||||
Builder.AddTypedTextChunk("X");
|
||||
Builder.AddInformativeChunk("info ok");
|
||||
Builder.AddInformativeChunk("info no no::");
|
||||
labelAndInsertText(*Builder.TakeString());
|
||||
EXPECT_EQ(Label, "Xinfo ok");
|
||||
EXPECT_EQ(InsertText, "X");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
Loading…
x
Reference in New Issue
Block a user