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 @@ -404,6 +404,8 @@ CodeCompleteOpts.MainFileSignals = IP->Signals; CodeCompleteOpts.AllScopes = Config::current().Completion.AllScopes; + CodeCompleteOpts.InsertIncludes = + Config::current().Completion.InsertIncludes; // 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 @@ -24,6 +24,7 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CONFIG_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CONFIG_H +#include "CodeComplete.h" #include "support/Context.h" #include "llvm/ADT/FunctionExtras.h" #include "llvm/ADT/Optional.h" @@ -123,6 +124,9 @@ /// Whether code completion includes results that are not visible in current /// scopes. bool AllScopes = true; + /// Add #include directives when accepting code completions. + CodeCompleteOptions::IncludeInsertion InsertIncludes = + CodeCompleteOptions().InsertIncludes; } Completion; /// Configures hover feature. 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 @@ -558,6 +558,18 @@ C.Completion.AllScopes = AllScopes; }); } + if (F.HeaderInsertion) { + if (auto Val = + compileEnum( + "HeaderInsertion", **F.HeaderInsertion) + .map("Never", + CodeCompleteOptions::IncludeInsertion::NeverInsert) + .map("IWYU", CodeCompleteOptions::IncludeInsertion::IWYU) + .value()) + Out.Apply.push_back([Val](const Params &, Config &C) { + C.Completion.InsertIncludes = *Val; + }); + } } void compile(Fragment::HoverBlock &&F) { 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 @@ -284,6 +284,12 @@ /// Whether code completion should include suggestions from scopes that are /// not visible. The required scope prefix will be inserted. llvm::Optional> AllScopes; + /// Add #include directives when accepting code completions. + /// + /// Valid values are: + /// - Never + /// - IWYU + llvm::Optional> HeaderInsertion; }; CompletionBlock Completion; 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 @@ -218,6 +218,10 @@ if (auto AllScopes = boolValue(N, "AllScopes")) F.AllScopes = *AllScopes; }); + Dict.handle("HeaderInsertion", [&](Node &N) { + if (auto HeaderInsertion = scalarValue(N, "HeaderInsertion")) + F.HeaderInsertion = *HeaderInsertion; + }); Dict.parse(N); } 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 @@ -697,6 +697,8 @@ C.Index.Background = *BGPolicy; if (AllScopesCompletion.getNumOccurrences()) C.Completion.AllScopes = AllScopesCompletion; + if (HeaderInsertion.getNumOccurrences()) + C.Completion.InsertIncludes = HeaderInsertion; return true; }; } @@ -904,7 +906,6 @@ if (CompletionStyle.getNumOccurrences()) Opts.CodeComplete.BundleOverloads = CompletionStyle != Detailed; Opts.CodeComplete.ShowOrigins = ShowOrigins; - Opts.CodeComplete.InsertIncludes = HeaderInsertion; if (!HeaderInsertionDecorators) { Opts.CodeComplete.IncludeIndicator.Insert.clear(); Opts.CodeComplete.IncludeIndicator.NoInsert.clear(); 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 @@ -6,6 +6,7 @@ // //===----------------------------------------------------------------------===// +#include "CodeComplete.h" #include "Config.h" #include "ConfigFragment.h" #include "ConfigTesting.h" @@ -537,6 +538,25 @@ EXPECT_TRUE(Conf.Completion.AllScopes); } +TEST_F(ConfigCompileTests, HeaderInsertion) { + // Defaults to IWYU. + EXPECT_TRUE(compileAndApply()); + EXPECT_EQ(Conf.Completion.InsertIncludes, + CodeCompleteOptions::IncludeInsertion::IWYU); + + Frag = {}; + Frag.Completion.HeaderInsertion = std::string("IWYU"); + EXPECT_TRUE(compileAndApply()); + EXPECT_EQ(Conf.Completion.InsertIncludes, + CodeCompleteOptions::IncludeInsertion::IWYU); + + Frag = {}; + Frag.Completion.HeaderInsertion = std::string("Never"); + EXPECT_TRUE(compileAndApply()); + EXPECT_EQ(Conf.Completion.InsertIncludes, + CodeCompleteOptions::IncludeInsertion::NeverInsert); +} + TEST_F(ConfigCompileTests, Style) { Frag = {}; Frag.Style.FullyQualifiedNamespaces.push_back(std::string("foo")); 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 @@ -218,6 +218,20 @@ EXPECT_THAT(Results[0].Completion.AllScopes, testing::Eq(llvm::None)); } +TEST(ParseYAML, HeaderInsertion) { + CapturedDiags Diags; + Annotations YAML(R"yaml( +Completion: + HeaderInsertion: Never + )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.HeaderInsertion, + llvm::ValueIs(val("Never"))); +} + TEST(ParseYAML, ShowAKA) { CapturedDiags Diags; Annotations YAML(R"yaml(