summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Liu <ioeric@google.com>2017-12-20 17:24:31 +0000
committerEric Liu <ioeric@google.com>2017-12-20 17:24:31 +0000
commit3565d1a1a692fc9f5c21e634b470535da2bb4d25 (patch)
tree104a96e935de410b69ab65666f66c7658f5f906a
parentbf3c9d97f5f0ea6678a0cfea4624291a97ab619c (diff)
[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 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@321193 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--clangd/CMakeLists.txt1
-rw-r--r--clangd/CodeComplete.cpp224
-rw-r--r--clangd/CodeCompletionStrings.cpp188
-rw-r--r--clangd/CodeCompletionStrings.h46
-rw-r--r--unittests/clangd/CMakeLists.txt1
-rw-r--r--unittests/clangd/CodeCompleteTests.cpp2
-rw-r--r--unittests/clangd/CodeCompletionStringsTests.cpp142
7 files changed, 393 insertions, 211 deletions
diff --git a/clangd/CMakeLists.txt b/clangd/CMakeLists.txt
index df242da5..e384f4e4 100644
--- a/clangd/CMakeLists.txt
+++ b/clangd/CMakeLists.txt
@@ -8,6 +8,7 @@ add_clang_library(clangDaemon
ClangdUnit.cpp
ClangdUnitStore.cpp
CodeComplete.cpp
+ CodeCompletionStrings.cpp
Context.cpp
Compiler.cpp
DraftStore.cpp
diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp
index cb56afe9..87f73b20 100644
--- a/clangd/CodeComplete.cpp
+++ b/clangd/CodeComplete.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));
diff --git a/clangd/CodeCompletionStrings.cpp b/clangd/CodeCompletionStrings.cpp
new file mode 100644
index 00000000..8aa4f251
--- /dev/null
+++ b/clangd/CodeCompletionStrings.cpp
@@ -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
diff --git a/clangd/CodeCompletionStrings.h b/clangd/CodeCompletionStrings.h
new file mode 100644
index 00000000..2c500a3a
--- /dev/null
+++ b/clangd/CodeCompletionStrings.h
@@ -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
diff --git a/unittests/clangd/CMakeLists.txt b/unittests/clangd/CMakeLists.txt
index ffe11b77..cbb7385c 100644
--- a/unittests/clangd/CMakeLists.txt
+++ b/unittests/clangd/CMakeLists.txt
@@ -12,6 +12,7 @@ add_extra_unittest(ClangdTests
Annotations.cpp
ClangdTests.cpp
CodeCompleteTests.cpp
+ CodeCompletionStringsTests.cpp
ContextTests.cpp
FileIndexTests.cpp
FuzzyMatchTests.cpp
diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp
index 7411e1c3..db998b08 100644
--- a/unittests/clangd/CodeCompleteTests.cpp
+++ b/unittests/clangd/CodeCompleteTests.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})")));
}
diff --git a/unittests/clangd/CodeCompletionStringsTests.cpp b/unittests/clangd/CodeCompletionStringsTests.cpp
new file mode 100644
index 00000000..9f2ed804
--- /dev/null
+++ b/unittests/clangd/CodeCompletionStringsTests.cpp
@@ -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