Index: clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp =================================================================== --- clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp +++ clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp @@ -320,11 +320,15 @@ void ClangdLSPServer::onCompletion(TextDocumentPositionParams &Params) { Server.codeComplete(Params.textDocument.uri.file(), Params.position, CCOpts, - [](llvm::Expected List) { + [this](llvm::Expected List) { if (!List) return replyError(ErrorCode::InvalidParams, llvm::toString(List.takeError())); - reply(*List); + CompletionList LSPList; + LSPList.isIncomplete = List->HasMore; + for (const auto &R : List->Completions) + LSPList.items.push_back(R.render(CCOpts)); + reply(std::move(LSPList)); }); } Index: clang-tools-extra/trunk/clangd/ClangdServer.h =================================================================== --- clang-tools-extra/trunk/clangd/ClangdServer.h +++ clang-tools-extra/trunk/clangd/ClangdServer.h @@ -141,7 +141,7 @@ /// when codeComplete results become available. void codeComplete(PathRef File, Position Pos, const clangd::CodeCompleteOptions &Opts, - Callback CB); + Callback CB); /// Provide signature help for \p File at \p Pos. This method should only be /// called for tracked files. Index: clang-tools-extra/trunk/clangd/ClangdServer.cpp =================================================================== --- clang-tools-extra/trunk/clangd/ClangdServer.cpp +++ clang-tools-extra/trunk/clangd/ClangdServer.cpp @@ -146,7 +146,7 @@ void ClangdServer::codeComplete(PathRef File, Position Pos, const clangd::CodeCompleteOptions &Opts, - Callback CB) { + Callback CB) { // Copy completion options for passing them to async task handler. auto CodeCompleteOpts = Opts; if (!CodeCompleteOpts.Index) // Respect overridden index. @@ -156,7 +156,7 @@ std::shared_ptr PCHs = this->PCHs; auto FS = FSProvider.getFileSystem(); auto Task = [PCHs, Pos, FS, - CodeCompleteOpts](Path File, Callback CB, + CodeCompleteOpts](Path File, Callback CB, llvm::Expected IP) { if (!IP) return CB(IP.takeError()); @@ -169,11 +169,7 @@ File, IP->Command, PreambleData ? &PreambleData->Preamble : nullptr, PreambleData ? PreambleData->Inclusions : std::vector(), IP->Contents, Pos, FS, PCHs, CodeCompleteOpts); - CompletionList LSPResult; - LSPResult.isIncomplete = Result.HasMore; - for (const auto &Completion : Result.Completions) - LSPResult.items.push_back(Completion.render(CodeCompleteOpts)); - CB(std::move(LSPResult)); + CB(std::move(Result)); }; WorkScheduler.runWithPreamble("CodeComplete", File, Index: clang-tools-extra/trunk/clangd/CodeComplete.h =================================================================== --- clang-tools-extra/trunk/clangd/CodeComplete.h +++ clang-tools-extra/trunk/clangd/CodeComplete.h @@ -102,7 +102,7 @@ // - ReturnType may be empty // - Documentation may be from one symbol, or a combination of several // Other fields should apply equally to all bundled completions. - unsigned BundleSize; + unsigned BundleSize = 1; // The header through which this symbol could be included. // Quoted string as expected by an #include directive, e.g. "". // Empty for non-symbol completions, or when not known. @@ -136,10 +136,12 @@ // Serialize this to an LSP completion item. This is a lossy operation. CompletionItem render(const CodeCompleteOptions &) const; }; +raw_ostream &operator<<(raw_ostream &, const CodeCompletion &); struct CodeCompleteResult { std::vector Completions; bool HasMore = false; }; +raw_ostream &operator<<(raw_ostream &, const CodeCompleteResult &); /// Get code completions at a specified \p Pos in \p FileName. CodeCompleteResult codeComplete( Index: clang-tools-extra/trunk/clangd/CodeComplete.cpp =================================================================== --- clang-tools-extra/trunk/clangd/CodeComplete.cpp +++ clang-tools-extra/trunk/clangd/CodeComplete.cpp @@ -1248,5 +1248,18 @@ return LSP; } +raw_ostream &operator<<(raw_ostream &OS, const CodeCompletion &C) { + // For now just lean on CompletionItem. + return OS << C.render(CodeCompleteOptions()); +} + +raw_ostream &operator<<(raw_ostream &OS, const CodeCompleteResult &R) { + OS << "CodeCompleteResult: " << R.Completions.size() << (R.HasMore ? "+" : "") + << " items:\n"; + for (const auto &C : R.Completions) + OS << C << "\n"; + return OS; +} + } // namespace clangd } // namespace clang Index: clang-tools-extra/trunk/unittests/clangd/ClangdTests.cpp =================================================================== --- clang-tools-extra/trunk/unittests/clangd/ClangdTests.cpp +++ clang-tools-extra/trunk/unittests/clangd/ClangdTests.cpp @@ -534,7 +534,7 @@ // can't parse the file. EXPECT_THAT(cantFail(runCodeComplete(Server, FooCpp, Position(), clangd::CodeCompleteOptions())) - .items, + .Completions, IsEmpty()); auto SigHelp = runSignatureHelp(Server, FooCpp, Position()); ASSERT_TRUE(bool(SigHelp)) << "signatureHelp returned an error"; Index: clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp =================================================================== --- clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp +++ clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp @@ -13,6 +13,7 @@ #include "Compiler.h" #include "Matchers.h" #include "Protocol.h" +#include "Quality.h" #include "SourceCode.h" #include "SyncAPI.h" #include "TestFS.h" @@ -43,59 +44,31 @@ }; // GMock helpers for matching completion items. -MATCHER_P(Named, Name, "") { return arg.insertText == Name; } -MATCHER_P(Scope, Name, "") { return arg.SymbolScope == Name; } +MATCHER_P(Named, Name, "") { return arg.Name == Name; } +MATCHER_P(Scope, S, "") { return arg.Scope == S; } +MATCHER_P(Qualifier, Q, "") { return arg.RequiredQualifier == Q; } MATCHER_P(Labeled, Label, "") { - std::string Indented; - if (!StringRef(Label).startswith( - CodeCompleteOptions().IncludeIndicator.Insert) && - !StringRef(Label).startswith( - CodeCompleteOptions().IncludeIndicator.NoInsert)) - Indented = - (Twine(CodeCompleteOptions().IncludeIndicator.NoInsert) + Label).str(); - else - Indented = Label; - return arg.label == Indented; + return arg.RequiredQualifier + arg.Name + arg.Signature == Label; } MATCHER_P(SigHelpLabeled, Label, "") { return arg.label == Label; } -MATCHER_P(Kind, K, "") { return arg.kind == K; } -MATCHER_P(Filter, F, "") { return arg.filterText == F; } -MATCHER_P(Doc, D, "") { return arg.documentation == D; } -MATCHER_P(Detail, D, "") { return arg.detail == D; } +MATCHER_P(Kind, K, "") { return arg.Kind == K; } +MATCHER_P(Doc, D, "") { return arg.Documentation == D; } +MATCHER_P(ReturnType, D, "") { return arg.ReturnType == D; } MATCHER_P(InsertInclude, IncludeHeader, "") { - if (arg.additionalTextEdits.size() != 1) - return false; - const auto &Edit = arg.additionalTextEdits[0]; - if (Edit.range.start != Edit.range.end) - return false; - SmallVector Matches; - llvm::Regex RE(R"(#include[ ]*(["<][^">]*[">]))"); - return RE.match(Edit.newText, &Matches) && Matches[1] == IncludeHeader; -} -MATCHER_P(PlainText, Text, "") { - return arg.insertTextFormat == clangd::InsertTextFormat::PlainText && - arg.insertText == Text; -} -MATCHER_P(Snippet, Text, "") { - return arg.insertTextFormat == clangd::InsertTextFormat::Snippet && - arg.insertText == Text; -} -MATCHER(NameContainsFilter, "") { - if (arg.filterText.empty()) - return true; - return llvm::StringRef(arg.insertText).contains(arg.filterText); + return arg.Header == IncludeHeader && bool(arg.HeaderInsertion); } -MATCHER(HasAdditionalEdits, "") { return !arg.additionalTextEdits.empty(); } +MATCHER(InsertInclude, "") { return bool(arg.HeaderInsertion); } +MATCHER_P(SnippetSuffix, Text, "") { return arg.SnippetSuffix == Text; } // Shorthand for Contains(Named(Name)). -Matcher &> Has(std::string Name) { +Matcher &> Has(std::string Name) { return Contains(Named(std::move(Name))); } -Matcher &> Has(std::string Name, +Matcher &> Has(std::string Name, CompletionItemKind K) { return Contains(AllOf(Named(std::move(Name)), Kind(K))); } -MATCHER(IsDocumented, "") { return !arg.documentation.empty(); } +MATCHER(IsDocumented, "") { return !arg.Documentation.empty(); } std::unique_ptr memIndex(std::vector Symbols) { SymbolSlab::Builder Slab; @@ -104,9 +77,9 @@ return MemIndex::build(std::move(Slab).build()); } -CompletionList completions(ClangdServer &Server, StringRef Text, - std::vector IndexSymbols = {}, - clangd::CodeCompleteOptions Opts = {}) { +CodeCompleteResult completions(ClangdServer &Server, StringRef Text, + std::vector IndexSymbols = {}, + clangd::CodeCompleteOptions Opts = {}) { std::unique_ptr OverrideIndex; if (!IndexSymbols.empty()) { assert(!Opts.Index && "both Index and IndexSymbols given!"); @@ -119,16 +92,14 @@ runAddDocument(Server, File, Test.code()); auto CompletionList = cantFail(runCodeComplete(Server, File, Test.point(), Opts)); - // Sanity-check that filterText is valid. - EXPECT_THAT(CompletionList.items, Each(NameContainsFilter())); return CompletionList; } // Builds a server and runs code completion. // If IndexSymbols is non-empty, an index will be built and passed to opts. -CompletionList completions(StringRef Text, - std::vector IndexSymbols = {}, - clangd::CodeCompleteOptions Opts = {}) { +CodeCompleteResult completions(StringRef Text, + std::vector IndexSymbols = {}, + clangd::CodeCompleteOptions Opts = {}) { MockFSProvider FS; MockCompilationDatabase CDB; IgnoreDiagnostics DiagConsumer; @@ -198,8 +169,8 @@ )cpp", /*IndexSymbols=*/{}, Opts); - EXPECT_TRUE(Results.isIncomplete); - EXPECT_THAT(Results.items, ElementsAre(Named("AAA"), Named("BBB"))); + EXPECT_TRUE(Results.HasMore); + EXPECT_THAT(Results.Completions, ElementsAre(Named("AAA"), Named("BBB"))); } TEST(CompletionTest, Filter) { @@ -214,11 +185,11 @@ )cpp"; // Only items matching the fuzzy query are returned. - EXPECT_THAT(completions(Body + "int main() { S().Foba^ }").items, + EXPECT_THAT(completions(Body + "int main() { S().Foba^ }").Completions, AllOf(Has("FooBar"), Has("FooBaz"), Not(Has("Qux")))); // Macros require prefix match. - EXPECT_THAT(completions(Body + "int main() { C^ }").items, + EXPECT_THAT(completions(Body + "int main() { C^ }").Completions, AllOf(Has("Car"), Not(Has("MotorCar")))); } @@ -255,23 +226,24 @@ // Class members. The only items that must be present in after-dot // completion. - EXPECT_THAT(Results.items, - AllOf(Has(Opts.EnableSnippets ? "method()" : "method"), - Has("field"), Not(Has("ClassWithMembers")), + EXPECT_THAT(Results.Completions, + AllOf(Has("method"), Has("field"), Not(Has("ClassWithMembers")), Not(Has("operator=")), Not(Has("~ClassWithMembers")))); - EXPECT_IFF(Opts.IncludeIneligibleResults, Results.items, + EXPECT_IFF(Opts.IncludeIneligibleResults, Results.Completions, Has("private_field")); // Global items. EXPECT_THAT( - Results.items, + Results.Completions, Not(AnyOf(Has("global_var"), Has("index_var"), Has("global_func"), Has("global_func()"), Has("index_func"), Has("GlobalClass"), Has("IndexClass"), Has("MACRO"), Has("LocalClass")))); // There should be no code patterns (aka snippets) in after-dot // completion. At least there aren't any we're aware of. - EXPECT_THAT(Results.items, Not(Contains(Kind(CompletionItemKind::Snippet)))); + EXPECT_THAT(Results.Completions, + Not(Contains(Kind(CompletionItemKind::Snippet)))); // Check documentation. - EXPECT_IFF(Opts.IncludeComments, Results.items, Contains(IsDocumented())); + EXPECT_IFF(Opts.IncludeComments, Results.Completions, + Contains(IsDocumented())); } void TestGlobalScopeCompletion(clangd::CodeCompleteOptions Opts) { @@ -301,22 +273,22 @@ {cls("IndexClass"), var("index_var"), func("index_func")}, Opts); // Class members. Should never be present in global completions. - EXPECT_THAT(Results.items, + EXPECT_THAT(Results.Completions, Not(AnyOf(Has("method"), Has("method()"), Has("field")))); // Global items. - EXPECT_THAT(Results.items, - AllOf(Has("global_var"), Has("index_var"), - Has(Opts.EnableSnippets ? "global_func()" : "global_func"), + EXPECT_THAT(Results.Completions, + AllOf(Has("global_var"), Has("index_var"), Has("global_func"), Has("index_func" /* our fake symbol doesn't include () */), Has("GlobalClass"), Has("IndexClass"))); // A macro. - EXPECT_IFF(Opts.IncludeMacros, Results.items, Has("MACRO")); + EXPECT_IFF(Opts.IncludeMacros, Results.Completions, Has("MACRO")); // Local items. Must be present always. - EXPECT_THAT(Results.items, + EXPECT_THAT(Results.Completions, AllOf(Has("local_var"), Has("LocalClass"), Contains(Kind(CompletionItemKind::Snippet)))); // Check documentation. - EXPECT_IFF(Opts.IncludeComments, Results.items, Contains(IsDocumented())); + EXPECT_IFF(Opts.IncludeComments, Results.Completions, + Contains(IsDocumented())); } TEST(CompletionTest, CompletionOptions) { @@ -328,7 +300,6 @@ auto Flags = { &clangd::CodeCompleteOptions::IncludeMacros, &clangd::CodeCompleteOptions::IncludeComments, - &clangd::CodeCompleteOptions::EnableSnippets, &clangd::CodeCompleteOptions::IncludeCodePatterns, &clangd::CodeCompleteOptions::IncludeIneligibleResults, }; @@ -351,7 +322,7 @@ }; void Foo::pub() { this->^ } )cpp"); - EXPECT_THAT(Internal.items, + EXPECT_THAT(Internal.Completions, HasSubsequence(Named("priv"), Named("prot"), Named("pub"))); auto External = completions(R"cpp( @@ -365,7 +336,7 @@ F.^ } )cpp"); - EXPECT_THAT(External.items, + EXPECT_THAT(External.Completions, AllOf(Has("pub"), Not(Has("prot")), Not(Has("priv")))); } @@ -380,33 +351,37 @@ }; void test() { Bar().^ } )cpp"); - EXPECT_THAT(Results.items, HasSubsequence(Labeled("bar() const"), - Labeled("Foo::foo() const"))); - EXPECT_THAT(Results.items, Not(Contains(Labeled("foo() const")))); // private + EXPECT_THAT(Results.Completions, + HasSubsequence(AllOf(Qualifier(""), Named("bar")), + AllOf(Qualifier("Foo::"), Named("foo")))); + EXPECT_THAT(Results.Completions, + Not(Contains(AllOf(Qualifier(""), Named("foo"))))); // private } TEST(CompletionTest, InjectedTypename) { // These are suppressed when accessed as a member... - EXPECT_THAT(completions("struct X{}; void foo(){ X().^ }").items, + EXPECT_THAT(completions("struct X{}; void foo(){ X().^ }").Completions, Not(Has("X"))); - EXPECT_THAT(completions("struct X{ void foo(){ this->^ } };").items, + EXPECT_THAT(completions("struct X{ void foo(){ this->^ } };").Completions, Not(Has("X"))); // ...but accessible in other, more useful cases. - EXPECT_THAT(completions("struct X{ void foo(){ ^ } };").items, Has("X")); - EXPECT_THAT(completions("struct Y{}; struct X:Y{ void foo(){ ^ } };").items, - Has("Y")); + EXPECT_THAT(completions("struct X{ void foo(){ ^ } };").Completions, + Has("X")); + EXPECT_THAT( + completions("struct Y{}; struct X:Y{ void foo(){ ^ } };").Completions, + Has("Y")); EXPECT_THAT( completions( "template struct Y{}; struct X:Y{ void foo(){ ^ } };") - .items, + .Completions, Has("Y")); // This case is marginal (`using X::X` is useful), we allow it for now. - EXPECT_THAT(completions("struct X{}; void foo(){ X::^ }").items, Has("X")); + EXPECT_THAT(completions("struct X{}; void foo(){ X::^ }").Completions, + Has("X")); } TEST(CompletionTest, Snippets) { clangd::CodeCompleteOptions Opts; - Opts.EnableSnippets = true; auto Results = completions( R"cpp( struct fake { @@ -419,9 +394,10 @@ } )cpp", /*IndexSymbols=*/{}, Opts); - EXPECT_THAT(Results.items, - HasSubsequence(Snippet("a"), - Snippet("f(${1:int i}, ${2:const float f})"))); + EXPECT_THAT( + Results.Completions, + HasSubsequence(Named("a"), + SnippetSuffix("(${1:int i}, ${2:const float f})"))); } TEST(CompletionTest, Kinds) { @@ -434,7 +410,7 @@ int X = ^ )cpp", {func("indexFunction"), var("indexVariable"), cls("indexClass")}); - EXPECT_THAT(Results.items, + EXPECT_THAT(Results.Completions, AllOf(Has("function", CompletionItemKind::Function), Has("variable", CompletionItemKind::Variable), Has("int", CompletionItemKind::Keyword), @@ -445,7 +421,8 @@ Has("indexClass", CompletionItemKind::Class))); Results = completions("nam^"); - EXPECT_THAT(Results.items, Has("namespace", CompletionItemKind::Snippet)); + EXPECT_THAT(Results.Completions, + Has("namespace", CompletionItemKind::Snippet)); } TEST(CompletionTest, NoDuplicates) { @@ -461,7 +438,7 @@ {cls("Adapter")}); // Make sure there are no duplicate entries of 'Adapter'. - EXPECT_THAT(Results.items, ElementsAre(Named("Adapter"))); + EXPECT_THAT(Results.Completions, ElementsAre(Named("Adapter"))); } TEST(CompletionTest, ScopedNoIndex) { @@ -471,7 +448,8 @@ int main() { fake::ba^ } ")cpp"); // Babble is a better match than BigBang. Box doesn't match at all. - EXPECT_THAT(Results.items, ElementsAre(Named("Babble"), Named("BigBang"))); + EXPECT_THAT(Results.Completions, + ElementsAre(Named("Babble"), Named("BigBang"))); } TEST(CompletionTest, Scoped) { @@ -481,7 +459,8 @@ int main() { fake::ba^ } ")cpp", {var("fake::BigBang")}); - EXPECT_THAT(Results.items, ElementsAre(Named("Babble"), Named("BigBang"))); + EXPECT_THAT(Results.Completions, + ElementsAre(Named("Babble"), Named("BigBang"))); } TEST(CompletionTest, ScopedWithFilter) { @@ -490,16 +469,15 @@ void f() { ns::x^ } )cpp", {cls("ns::XYZ"), func("ns::foo")}); - EXPECT_THAT(Results.items, - UnorderedElementsAre(AllOf(Named("XYZ"), Filter("XYZ")))); + EXPECT_THAT(Results.Completions, UnorderedElementsAre(Named("XYZ"))); } TEST(CompletionTest, ReferencesAffectRanking) { auto Results = completions("int main() { abs^ }", {ns("absl"), func("abs")}); - EXPECT_THAT(Results.items, HasSubsequence(Named("abs"), Named("absl"))); + EXPECT_THAT(Results.Completions, HasSubsequence(Named("abs"), Named("absl"))); Results = completions("int main() { abs^ }", {withReferences(10000, ns("absl")), func("abs")}); - EXPECT_THAT(Results.items, HasSubsequence(Named("absl"), Named("abs"))); + EXPECT_THAT(Results.Completions, HasSubsequence(Named("absl"), Named("abs"))); } TEST(CompletionTest, GlobalQualified) { @@ -508,8 +486,9 @@ void f() { ::^ } )cpp", {cls("XYZ")}); - EXPECT_THAT(Results.items, AllOf(Has("XYZ", CompletionItemKind::Class), - Has("f", CompletionItemKind::Function))); + EXPECT_THAT(Results.Completions, + AllOf(Has("XYZ", CompletionItemKind::Class), + Has("f", CompletionItemKind::Function))); } TEST(CompletionTest, FullyQualified) { @@ -519,8 +498,9 @@ void f() { ::ns::^ } )cpp", {cls("ns::XYZ")}); - EXPECT_THAT(Results.items, AllOf(Has("XYZ", CompletionItemKind::Class), - Has("bar", CompletionItemKind::Function))); + EXPECT_THAT(Results.Completions, + AllOf(Has("XYZ", CompletionItemKind::Class), + Has("bar", CompletionItemKind::Function))); } TEST(CompletionTest, SemaIndexMerge) { @@ -532,7 +512,7 @@ {func("ns::both"), cls("ns::Index")}); // We get results from both index and sema, with no duplicates. EXPECT_THAT( - Results.items, + Results.Completions, UnorderedElementsAre(Named("local"), Named("Index"), Named("both"))); } @@ -545,8 +525,8 @@ void f() { ::ns::^ } )cpp", {func("ns::both"), cls("ns::Index")}, Opts); - EXPECT_EQ(Results.items.size(), Opts.Limit); - EXPECT_TRUE(Results.isIncomplete); + EXPECT_EQ(Results.Completions.size(), Opts.Limit); + EXPECT_TRUE(Results.HasMore); } TEST(CompletionTest, IncludeInsertionPreprocessorIntegrationTests) { @@ -572,11 +552,8 @@ int main() { ns::^ } )cpp", {Sym}); - EXPECT_THAT(Results.items, - ElementsAre(AllOf( - Named("X"), - Labeled(CodeCompleteOptions().IncludeIndicator.Insert + "X"), - InsertInclude("\"bar.h\"")))); + EXPECT_THAT(Results.Completions, + ElementsAre(AllOf(Named("X"), InsertInclude("\"bar.h\"")))); // Duplicate based on inclusions in preamble. Results = completions(Server, R"cpp( @@ -584,8 +561,8 @@ int main() { ns::^ } )cpp", {Sym}); - EXPECT_THAT(Results.items, ElementsAre(AllOf(Named("X"), Labeled("X"), - Not(HasAdditionalEdits())))); + EXPECT_THAT(Results.Completions, ElementsAre(AllOf(Named("X"), Labeled("X"), + Not(InsertInclude())))); } TEST(CompletionTest, NoIncludeInsertionWhenDeclFoundInFile) { @@ -614,9 +591,9 @@ int main() { ns::^ } )cpp", {SymX, SymY}); - EXPECT_THAT(Results.items, - ElementsAre(AllOf(Named("X"), Not(HasAdditionalEdits())), - AllOf(Named("Y"), Not(HasAdditionalEdits())))); + EXPECT_THAT(Results.Completions, + ElementsAre(AllOf(Named("X"), Not(InsertInclude())), + AllOf(Named("Y"), Not(InsertInclude())))); } TEST(CompletionTest, IndexSuppressesPreambleCompletions) { @@ -640,16 +617,16 @@ auto I = memIndex({var("ns::index")}); Opts.Index = I.get(); auto WithIndex = cantFail(runCodeComplete(Server, File, Test.point(), Opts)); - EXPECT_THAT(WithIndex.items, + EXPECT_THAT(WithIndex.Completions, UnorderedElementsAre(Named("local"), Named("index"))); auto ClassFromPreamble = cantFail(runCodeComplete(Server, File, Test.point("2"), Opts)); - EXPECT_THAT(ClassFromPreamble.items, Contains(Named("member"))); + EXPECT_THAT(ClassFromPreamble.Completions, Contains(Named("member"))); Opts.Index = nullptr; auto WithoutIndex = cantFail(runCodeComplete(Server, File, Test.point(), Opts)); - EXPECT_THAT(WithoutIndex.items, + EXPECT_THAT(WithoutIndex.Completions, UnorderedElementsAre(Named("local"), Named("preamble"))); } @@ -682,12 +659,12 @@ auto Results = cantFail(runCodeComplete(Server, File, Test.point(), {})); // "XYZ" and "foo" are not included in the file being completed but are still // visible through the index. - EXPECT_THAT(Results.items, Has("XYZ", CompletionItemKind::Class)); - EXPECT_THAT(Results.items, Has("foo", CompletionItemKind::Function)); - EXPECT_THAT(Results.items, Has("XXX", CompletionItemKind::Class)); - EXPECT_THAT(Results.items, Contains(AllOf(Named("fooooo"), Filter("fooooo"), - Kind(CompletionItemKind::Function), - Doc("Doooc"), Detail("void")))); + EXPECT_THAT(Results.Completions, Has("XYZ", CompletionItemKind::Class)); + EXPECT_THAT(Results.Completions, Has("foo", CompletionItemKind::Function)); + EXPECT_THAT(Results.Completions, Has("XXX", CompletionItemKind::Class)); + EXPECT_THAT(Results.Completions, + Contains((Named("fooooo"), Kind(CompletionItemKind::Function), + Doc("Doooc"), ReturnType("void")))); } TEST(CompletionTest, Documentation) { @@ -705,12 +682,12 @@ int x = ^ )cpp"); - EXPECT_THAT(Results.items, + EXPECT_THAT(Results.Completions, Contains(AllOf(Named("foo"), Doc("Non-doxygen comment.")))); EXPECT_THAT( - Results.items, + Results.Completions, Contains(AllOf(Named("bar"), Doc("Doxygen comment.\n\\param int a")))); - EXPECT_THAT(Results.items, + EXPECT_THAT(Results.Completions, Contains(AllOf(Named("baz"), Doc("Multi-line\nblock comment")))); } @@ -725,7 +702,7 @@ XYZ::foooo^ })", {Class, Func}); - EXPECT_THAT(Results.items, IsEmpty()); + EXPECT_THAT(Results.Completions, IsEmpty()); } TEST(CodeCompleteTest, DisableTypoCorrection) { @@ -733,7 +710,7 @@ namespace clang { int v; } void f() { clangd::^ )cpp"); - EXPECT_TRUE(Results.items.empty()); + EXPECT_TRUE(Results.Completions.empty()); } TEST(CodeCompleteTest, NoColonColonAtTheEnd) { @@ -744,8 +721,8 @@ } )cpp"); - EXPECT_THAT(Results.items, Contains(Labeled("clang"))); - EXPECT_THAT(Results.items, Not(Contains(Labeled("clang::")))); + EXPECT_THAT(Results.Completions, Contains(Labeled("clang"))); + EXPECT_THAT(Results.Completions, Not(Contains(Labeled("clang::")))); } TEST(CompletionTest, BacktrackCrashes) { @@ -758,7 +735,7 @@ int foo(ns::FooBar^ )cpp"); - EXPECT_THAT(Results.items, ElementsAre(Labeled("FooBarBaz"))); + EXPECT_THAT(Results.Completions, ElementsAre(Labeled("FooBarBaz"))); // Check we don't crash in that case too. completions(R"cpp( @@ -784,7 +761,7 @@ } )cpp"); - EXPECT_THAT(Results.items, + EXPECT_THAT(Results.Completions, UnorderedElementsAre(Named("X"), Named("Y"))); } @@ -802,7 +779,7 @@ } // namespace ns )cpp"); - EXPECT_THAT(Results.items, Contains(Named("X"))); + EXPECT_THAT(Results.Completions, Contains(Named("X"))); } TEST(CompletionTest, CompleteInExcludedPPBranch) { @@ -817,8 +794,8 @@ } )cpp"); - EXPECT_THAT(Results.items, Contains(Labeled("param_in_foo"))); - EXPECT_THAT(Results.items, Not(Contains(Labeled("param_in_bar")))); + EXPECT_THAT(Results.Completions, Contains(Labeled("param_in_foo"))); + EXPECT_THAT(Results.Completions, Not(Contains(Labeled("param_in_bar")))); } SignatureHelp signatures(StringRef Text) { @@ -1024,7 +1001,7 @@ Foo::^)cpp", {func("::SomeNameInTheIndex"), func("::Foo::SomeNameInTheIndex")}); - EXPECT_THAT(Completions.items, + EXPECT_THAT(Completions.Completions, AllOf(Contains(Labeled("SomeNameOfField")), Contains(Labeled("SomeNameOfTypedefField")), Not(Contains(Labeled("SomeNameInTheIndex"))))); @@ -1041,7 +1018,7 @@ )cpp", {func("::SomeNameInTheIndex")}); - EXPECT_THAT(Completions.items, + EXPECT_THAT(Completions.Completions, Not(Contains(Labeled("SomeNameInTheIndex")))); } @@ -1055,7 +1032,7 @@ )cpp", {func("::SomeNameInTheIndex")}); - EXPECT_THAT(Completions.items, + EXPECT_THAT(Completions.Completions, Not(Contains(Labeled("SomeNameInTheIndex")))); } @@ -1069,7 +1046,7 @@ )cpp", {func("::SomeNameInTheIndex")}); - EXPECT_THAT(Completions.items, + EXPECT_THAT(Completions.Completions, Not(Contains(Labeled("SomeNameInTheIndex")))); } } @@ -1091,13 +1068,13 @@ )cpp"; // Member completions are bundled. - EXPECT_THAT(completions(Context + "int y = X().^", {}, Opts).items, + EXPECT_THAT(completions(Context + "int y = X().^", {}, Opts).Completions, UnorderedElementsAre(Labeled("a(…)"), Labeled("b(float)"))); // Non-member completions are bundled, including index+sema. Symbol NoArgsGFunc = func("GFuncC"); EXPECT_THAT( - completions(Context + "int y = GFunc^", {NoArgsGFunc}, Opts).items, + completions(Context + "int y = GFunc^", {NoArgsGFunc}, Opts).Completions, UnorderedElementsAre(Labeled("GFuncC(…)"), Labeled("GFuncD(int)"))); // Differences in header-to-insert suppress bundling. @@ -1107,26 +1084,22 @@ Detail.IncludeHeader = ""; NoArgsGFunc.Detail = &Detail; EXPECT_THAT( - completions(Context + "int y = GFunc^", {NoArgsGFunc}, Opts).items, - UnorderedElementsAre( - AllOf( - Named("GFuncC"), - Labeled(CodeCompleteOptions().IncludeIndicator.Insert + "GFuncC"), - InsertInclude("")), - Labeled("GFuncC(int)"), Labeled("GFuncD(int)"))); + completions(Context + "int y = GFunc^", {NoArgsGFunc}, Opts).Completions, + UnorderedElementsAre(AllOf(Named("GFuncC"), InsertInclude("")), + Labeled("GFuncC(int)"), Labeled("GFuncD(int)"))); // Examine a bundled completion in detail. - auto A = completions(Context + "int y = X().a^", {}, Opts).items.front(); - EXPECT_EQ(A.label, " a(…)"); - EXPECT_EQ(A.detail, "[2 overloads]"); - EXPECT_EQ(A.insertText, "a"); - EXPECT_EQ(A.kind, CompletionItemKind::Method); + auto A = + completions(Context + "int y = X().a^", {}, Opts).Completions.front(); + EXPECT_EQ(A.Name, "a"); + EXPECT_EQ(A.Signature, "(…)"); + EXPECT_EQ(A.BundleSize, 2u); + EXPECT_EQ(A.Kind, CompletionItemKind::Method); + EXPECT_EQ(A.ReturnType, "int"); // All overloads return int. // For now we just return one of the doc strings arbitrarily. - EXPECT_THAT(A.documentation, AnyOf(HasSubstr("Overload with int"), + EXPECT_THAT(A.Documentation, AnyOf(HasSubstr("Overload with int"), HasSubstr("Overload with bool"))); - Opts.EnableSnippets = true; - A = completions(Context + "int y = X().a^", {}, Opts).items.front(); - EXPECT_EQ(A.insertText, "a(${0})"); + EXPECT_EQ(A.SnippetSuffix, "(${0})"); } TEST(CompletionTest, DocumentationFromChangedFileCrash) { @@ -1161,11 +1134,11 @@ clangd::CodeCompleteOptions Opts; Opts.IncludeComments = true; - CompletionList Completions = + CodeCompleteResult Completions = cantFail(runCodeComplete(Server, FooCpp, Source.point(), Opts)); // We shouldn't crash. Unfortunately, current workaround is to not produce // comments for symbols from headers. - EXPECT_THAT(Completions.items, + EXPECT_THAT(Completions.Completions, Contains(AllOf(Not(IsDocumented()), Named("func")))); } @@ -1218,12 +1191,12 @@ // parsing. CDB.ExtraClangFlags.push_back("-fno-delayed-template-parsing"); Server.addDocument(FooCpp, Source.code(), WantDiagnostics::Yes); - CompletionList Completions = cantFail(runCodeComplete( + CodeCompleteResult Completions = cantFail(runCodeComplete( Server, FooCpp, Source.point(), clangd::CodeCompleteOptions())); // We should not get any of those comments in completion. EXPECT_THAT( - Completions.items, + Completions.Completions, UnorderedElementsAre(AllOf(Not(IsDocumented()), Named("comments_foo")), AllOf(IsDocumented(), Named("comments_baz")), AllOf(IsDocumented(), Named("comments_quux")), @@ -1262,8 +1235,53 @@ )cpp", {func("ns::both"), cls("ns::Index")}); // We get results from both index and sema, with no duplicates. - EXPECT_THAT(Results.items, UnorderedElementsAre(Scope("ns::"), Scope("ns::"), - Scope("ns::"))); + EXPECT_THAT( + Results.Completions, + UnorderedElementsAre(Scope("ns::"), Scope("ns::"), Scope("ns::"))); +} + +TEST(CompletionTest, Render) { + CodeCompletion C; + C.Name = "x"; + C.Signature = "(bool) const"; + C.SnippetSuffix = "(${0:bool})"; + C.ReturnType = "int"; + C.RequiredQualifier = "Foo::"; + C.Scope = "ns::Foo::"; + C.Documentation = "This is x()."; + C.Header = "\"foo.h\""; + C.Kind = CompletionItemKind::Method; + C.Score.Total = 1.0; + + CodeCompleteOptions Opts; + Opts.IncludeIndicator.Insert = "^"; + Opts.IncludeIndicator.NoInsert = ""; + Opts.EnableSnippets = false; + + auto R = C.render(Opts); + EXPECT_EQ(R.label, "Foo::x(bool) const"); + EXPECT_EQ(R.insertText, "Foo::x"); + EXPECT_EQ(R.insertTextFormat, InsertTextFormat::PlainText); + EXPECT_EQ(R.filterText, "x"); + EXPECT_EQ(R.detail, "int\n\"foo.h\""); + EXPECT_EQ(R.documentation, "This is x()."); + EXPECT_THAT(R.additionalTextEdits, IsEmpty()); + EXPECT_EQ(R.SymbolScope, "ns::Foo::"); + EXPECT_EQ(R.sortText, sortText(1.0, "x")); + + Opts.EnableSnippets = true; + R = C.render(Opts); + EXPECT_EQ(R.insertText, "Foo::x(${0:bool})"); + EXPECT_EQ(R.insertTextFormat, InsertTextFormat::Snippet); + + C.HeaderInsertion.emplace(); + R = C.render(Opts); + EXPECT_EQ(R.label, "^Foo::x(bool) const"); + EXPECT_THAT(R.additionalTextEdits, Not(IsEmpty())); + + C.BundleSize = 2; + R = C.render(Opts); + EXPECT_EQ(R.detail, "[2 overloads]\n\"foo.h\""); } } // namespace Index: clang-tools-extra/trunk/unittests/clangd/SyncAPI.h =================================================================== --- clang-tools-extra/trunk/unittests/clangd/SyncAPI.h +++ clang-tools-extra/trunk/unittests/clangd/SyncAPI.h @@ -22,7 +22,7 @@ void runAddDocument(ClangdServer &Server, PathRef File, StringRef Contents, WantDiagnostics WantDiags = WantDiagnostics::Auto); -llvm::Expected +llvm::Expected runCodeComplete(ClangdServer &Server, PathRef File, Position Pos, clangd::CodeCompleteOptions Opts); Index: clang-tools-extra/trunk/unittests/clangd/SyncAPI.cpp =================================================================== --- clang-tools-extra/trunk/unittests/clangd/SyncAPI.cpp +++ clang-tools-extra/trunk/unittests/clangd/SyncAPI.cpp @@ -68,10 +68,10 @@ } } // namespace -llvm::Expected +llvm::Expected runCodeComplete(ClangdServer &Server, PathRef File, Position Pos, clangd::CodeCompleteOptions Opts) { - llvm::Optional> Result; + llvm::Optional> Result; Server.codeComplete(File, Pos, Opts, capture(Result)); return std::move(*Result); }