diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -362,6 +362,7 @@ ParseInput.Index = Index; CodeCompleteOpts.MainFileSignals = IP->Signals; + CodeCompleteOpts.AllScopes = Config::current().Completion.AllScopes; // FIXME(ibiryukov): even if Preamble is non-null, we may want to check // both the old and the new version in case only one of them matches. CodeCompleteResult Result = clangd::codeComplete( diff --git a/clang-tools-extra/clangd/Config.h b/clang-tools-extra/clangd/Config.h --- a/clang-tools-extra/clangd/Config.h +++ b/clang-tools-extra/clangd/Config.h @@ -106,6 +106,13 @@ // ::). All nested namespaces are affected as well. std::vector FullyQualifiedNamespaces; } Style; + + /// Configures code completion feature. + struct { + /// Whether code completion includes results that are not visible in current + /// scopes. + bool AllScopes = true; + } Completion; }; } // namespace clangd diff --git a/clang-tools-extra/clangd/ConfigCompile.cpp b/clang-tools-extra/clangd/ConfigCompile.cpp --- a/clang-tools-extra/clangd/ConfigCompile.cpp +++ b/clang-tools-extra/clangd/ConfigCompile.cpp @@ -192,6 +192,7 @@ compile(std::move(F.CompileFlags)); compile(std::move(F.Index)); compile(std::move(F.Diagnostics)); + compile(std::move(F.Completion)); } void compile(Fragment::IfBlock &&F) { @@ -463,6 +464,15 @@ } } + void compile(Fragment::CompletionBlock &&F) { + if (F.AllScopes) { + Out.Apply.push_back( + [AllScopes(**F.AllScopes)](const Params &, Config &C) { + C.Completion.AllScopes = AllScopes; + }); + } + } + constexpr static llvm::SourceMgr::DiagKind Error = llvm::SourceMgr::DK_Error; constexpr static llvm::SourceMgr::DiagKind Warning = llvm::SourceMgr::DK_Warning; diff --git a/clang-tools-extra/clangd/ConfigFragment.h b/clang-tools-extra/clangd/ConfigFragment.h --- a/clang-tools-extra/clangd/ConfigFragment.h +++ b/clang-tools-extra/clangd/ConfigFragment.h @@ -238,6 +238,14 @@ std::vector> FullyQualifiedNamespaces; }; StyleBlock Style; + + /// Describes code completion preferences. + struct CompletionBlock { + /// Whether code completion should include suggestions from scopes that are + /// not visible. The required scope prefix will be inserted. + llvm::Optional> AllScopes; + }; + CompletionBlock Completion; }; } // namespace config diff --git a/clang-tools-extra/clangd/ConfigYAML.cpp b/clang-tools-extra/clangd/ConfigYAML.cpp --- a/clang-tools-extra/clangd/ConfigYAML.cpp +++ b/clang-tools-extra/clangd/ConfigYAML.cpp @@ -63,6 +63,7 @@ Dict.handle("Index", [&](Node &N) { parse(F.Index, N); }); Dict.handle("Style", [&](Node &N) { parse(F.Style, N); }); Dict.handle("Diagnostics", [&](Node &N) { parse(F.Diagnostics, N); }); + Dict.handle("Completion", [&](Node &N) { parse(F.Completion, N); }); Dict.parse(N); return !(N.failed() || HadError); } @@ -165,6 +166,19 @@ Dict.parse(N); } + void parse(Fragment::CompletionBlock &F, Node &N) { + DictParser Dict("Completion", this); + Dict.handle("AllScopes", [&](Node &N) { + if (auto Value = scalarValue(N, "AllScopes")) { + if (auto AllScopes = llvm::yaml::parseBool(**Value)) + F.AllScopes = *AllScopes; + else + warning("AllScopes should be a boolean", N); + } + }); + Dict.parse(N); + } + // Helper for parsing mapping nodes (dictionaries). // We don't use YamlIO as we want to control over unknown keys. class DictParser { diff --git a/clang-tools-extra/clangd/tool/ClangdMain.cpp b/clang-tools-extra/clangd/tool/ClangdMain.cpp --- a/clang-tools-extra/clangd/tool/ClangdMain.cpp +++ b/clang-tools-extra/clangd/tool/ClangdMain.cpp @@ -623,6 +623,7 @@ if (!EnableBackgroundIndex) { BGPolicy = Config::BackgroundPolicy::Skip; } + Frag = [=](const config::Params &, Config &C) { if (CDBSearch) C.CompileFlags.CDBSearch = *CDBSearch; @@ -630,6 +631,8 @@ C.Index.External = *IndexSpec; if (BGPolicy) C.Index.Background = *BGPolicy; + if (AllScopesCompletion.getNumOccurrences()) + C.Completion.AllScopes = AllScopesCompletion; return true; }; } @@ -827,7 +830,6 @@ Opts.CodeComplete.IncludeIndicator.NoInsert.clear(); } Opts.CodeComplete.EnableFunctionArgSnippets = EnableFunctionArgSnippets; - Opts.CodeComplete.AllScopes = AllScopesCompletion; Opts.CodeComplete.RunParser = CodeCompletionParse; Opts.CodeComplete.RankingModel = RankingModel; diff --git a/clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp b/clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp --- a/clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp +++ b/clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp @@ -443,6 +443,22 @@ ASSERT_FALSE(Conf.Index.External); #endif } + +TEST_F(ConfigCompileTests, AllScopes) { + // Defaults to true. + EXPECT_TRUE(compileAndApply()); + EXPECT_TRUE(Conf.Completion.AllScopes); + + Frag = {}; + Frag.Completion.AllScopes = false; + EXPECT_TRUE(compileAndApply()); + EXPECT_FALSE(Conf.Completion.AllScopes); + + Frag = {}; + Frag.Completion.AllScopes = true; + EXPECT_TRUE(compileAndApply()); + EXPECT_TRUE(Conf.Completion.AllScopes); +} } // namespace } // namespace config } // namespace clangd diff --git a/clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp b/clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp --- a/clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp +++ b/clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp @@ -10,10 +10,12 @@ #include "ConfigFragment.h" #include "ConfigTesting.h" #include "Protocol.h" +#include "llvm/ADT/None.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/SMLoc.h" #include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/SourceMgr.h" +#include "llvm/Testing/Support/SupportHelpers.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -164,6 +166,35 @@ EXPECT_THAT(*Results[0].Index.External.getValue()->Server, Val("bar")); } +TEST(ParseYAML, AllScopes) { + CapturedDiags Diags; + Annotations YAML(R"yaml( +Completion: + AllScopes: True + )yaml"); + auto Results = + Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback()); + ASSERT_THAT(Diags.Diagnostics, IsEmpty()); + ASSERT_EQ(Results.size(), 1u); + EXPECT_THAT(Results[0].Completion.AllScopes, llvm::ValueIs(Val(true))); +} + +TEST(ParseYAML, AllScopesWarn) { + CapturedDiags Diags; + Annotations YAML(R"yaml( +Completion: + AllScopes: $diagrange[[Truex]] + )yaml"); + auto Results = + Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback()); + EXPECT_THAT(Diags.Diagnostics, + ElementsAre(AllOf(DiagMessage("AllScopes should be a boolean"), + DiagKind(llvm::SourceMgr::DK_Warning), + DiagPos(YAML.range("diagrange").start), + DiagRange(YAML.range("diagrange"))))); + ASSERT_EQ(Results.size(), 1u); + EXPECT_THAT(Results[0].Completion.AllScopes, testing::Eq(llvm::None)); +} } // namespace } // namespace config } // namespace clangd