Index: clang-tools-extra/trunk/clangd/CodeComplete.h =================================================================== --- clang-tools-extra/trunk/clangd/CodeComplete.h +++ clang-tools-extra/trunk/clangd/CodeComplete.h @@ -68,6 +68,11 @@ /// If more results are available, we set CompletionList.isIncomplete. size_t Limit = 0; + enum IncludeInsertion { + IWYU, + NeverInsert, + } InsertIncludes = IncludeInsertion::IWYU; + /// A visual indicator to prepend to the completion label to indicate whether /// completion result would trigger an #include insertion or not. struct IncludeInsertionIndicator { Index: clang-tools-extra/trunk/clangd/CodeComplete.cpp =================================================================== --- clang-tools-extra/trunk/clangd/CodeComplete.cpp +++ clang-tools-extra/trunk/clangd/CodeComplete.cpp @@ -191,7 +191,9 @@ // Returns a token identifying the overload set this is part of. // 0 indicates it's not part of any overload set. - size_t overloadSet() const { + size_t overloadSet(const CodeCompleteOptions &Opts) const { + if (!Opts.BundleOverloads) + return 0; llvm::SmallString<256> Scratch; if (IndexResult) { switch (IndexResult->SymInfo.Kind) { @@ -208,7 +210,7 @@ // This could break #include insertion. return llvm::hash_combine( (IndexResult->Scope + IndexResult->Name).toStringRef(Scratch), - headerToInsertIfAllowed().getValueOr("")); + headerToInsertIfAllowed(Opts).getValueOr("")); default: return 0; } @@ -223,12 +225,14 @@ D->printQualifiedName(OS); } return llvm::hash_combine(Scratch, - headerToInsertIfAllowed().getValueOr("")); + headerToInsertIfAllowed(Opts).getValueOr("")); } // The best header to include if include insertion is allowed. - llvm::Optional headerToInsertIfAllowed() const { - if (RankedIncludeHeaders.empty()) + llvm::Optional + headerToInsertIfAllowed(const CodeCompleteOptions &Opts) const { + if (Opts.InsertIncludes == CodeCompleteOptions::NeverInsert || + RankedIncludeHeaders.empty()) return None; if (SemaResult && SemaResult->Declaration) { // Avoid inserting new #include if the declaration is found in the current @@ -338,7 +342,7 @@ Includes.calculateIncludePath(*ResolvedDeclaring, *ResolvedInserted), Includes.shouldInsertInclude(*ResolvedDeclaring, *ResolvedInserted)); }; - bool ShouldInsert = C.headerToInsertIfAllowed().hasValue(); + bool ShouldInsert = C.headerToInsertIfAllowed(Opts).hasValue(); // Calculate include paths and edits for all possible headers. for (const auto &Inc : C.RankedIncludeHeaders) { if (auto ToInclude = Inserted(Inc)) { @@ -1373,7 +1377,7 @@ if (C.IndexResult) C.RankedIncludeHeaders = getRankedIncludes(*C.IndexResult); C.Name = IndexResult ? IndexResult->Name : Recorder->getName(*SemaResult); - if (auto OverloadSet = Opts.BundleOverloads ? C.overloadSet() : 0) { + if (auto OverloadSet = C.overloadSet(Opts)) { auto Ret = BundleLookup.try_emplace(OverloadSet, Bundles.size()); if (Ret.second) Bundles.emplace_back(); Index: clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp =================================================================== --- clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp +++ clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp @@ -6,8 +6,9 @@ // //===----------------------------------------------------------------------===// -#include "Features.inc" #include "ClangdLSPServer.h" +#include "CodeComplete.h" +#include "Features.inc" #include "Path.h" #include "Protocol.h" #include "Trace.h" @@ -154,6 +155,20 @@ "debug-origin", llvm::cl::desc("Show origins of completion items"), llvm::cl::init(CodeCompleteOptions().ShowOrigins), llvm::cl::Hidden); +static llvm::cl::opt HeaderInsertion( + "header-insertion", + llvm::cl::desc("Add #include directives when accepting code completions"), + llvm::cl::init(CodeCompleteOptions().InsertIncludes), + llvm::cl::values( + clEnumValN(CodeCompleteOptions::IWYU, "iwyu", + "Include what you use. " + "Insert the owning header for top-level symbols, unless the " + "header is already directly included or the symbol is " + "forward-declared."), + clEnumValN( + CodeCompleteOptions::NeverInsert, "never", + "Never insert #include directives as part of code completion"))); + static llvm::cl::opt HeaderInsertionDecorators( "header-insertion-decorators", llvm::cl::desc("Prepend a circular dot or space before the completion " @@ -438,6 +453,7 @@ CCOpts.Limit = LimitResults; CCOpts.BundleOverloads = CompletionStyle != Detailed; CCOpts.ShowOrigins = ShowOrigins; + CCOpts.InsertIncludes = HeaderInsertion; if (!HeaderInsertionDecorators) { CCOpts.IncludeIndicator.Insert.clear(); CCOpts.IncludeIndicator.NoInsert.clear(); 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 @@ -555,6 +555,16 @@ {Sym}); EXPECT_THAT(Results.Completions, ElementsAre(AllOf(Named("X"), InsertInclude("\"bar.h\"")))); + // Can be disabled via option. + CodeCompleteOptions NoInsertion; + NoInsertion.InsertIncludes = CodeCompleteOptions::NeverInsert; + Results = completions(Server, + R"cpp( + int main() { ns::^ } + )cpp", + {Sym}, NoInsertion); + EXPECT_THAT(Results.Completions, + ElementsAre(AllOf(Named("X"), Not(InsertInclude())))); // Duplicate based on inclusions in preamble. Results = completions(Server, R"cpp(