Index: clangd/ClangdServer.cpp =================================================================== --- clangd/ClangdServer.cpp +++ clangd/ClangdServer.cpp @@ -197,10 +197,12 @@ }; // We use a potentially-stale preamble because latency is critical here. - WorkScheduler.runWithPreamble("CodeComplete", File, - Opts.AllowFallback ? TUScheduler::StaleOrAbsent - : TUScheduler::Stale, - Bind(Task, File.str(), std::move(CB))); + WorkScheduler.runWithPreamble( + "CodeComplete", File, + (Opts.RunParser == CodeCompleteOptions::AlwaysParse) + ? TUScheduler::Stale + : TUScheduler::StaleOrAbsent, + Bind(Task, File.str(), std::move(CB))); } void ClangdServer::signatureHelp(PathRef File, Position Pos, Index: clangd/CodeComplete.h =================================================================== --- clangd/CodeComplete.h +++ clangd/CodeComplete.h @@ -115,11 +115,18 @@ /// Such completions can insert scope qualifiers. bool AllScopes = false; - /// Whether to allow falling back to code completion without compiling files - /// (using identifiers in the current file and symbol indexes), when file - /// cannot be built (e.g. missing compile command), or the build is not ready - /// (e.g. preamble is still being built). - bool AllowFallback = false; + /// Whether to use the clang parser, or fallback to text-based completion + /// (using identifiers in the current file and symbol indexes). + enum CodeCompletionParse { + /// Block until we can run the parser (e.g. preamble is built). + /// Return an error if this fails. + AlwaysParse, + /// Run the parser if inputs (preamble) are ready. + /// Otherwise, use text-based completion. + ParseIfReady, + /// Always use text-based completion. + NeverParse, + } RunParser = ParseIfReady; }; // Semi-structured representation of a code-complete suggestion for our C++ API. Index: clangd/CodeComplete.cpp =================================================================== --- clangd/CodeComplete.cpp +++ clangd/CodeComplete.cpp @@ -1738,9 +1738,10 @@ auto Flow = CodeCompleteFlow( FileName, Preamble ? Preamble->Includes : IncludeStructure(), SpecFuzzyFind, Opts); - return Preamble ? std::move(Flow).run( - {FileName, Command, Preamble, Contents, *Offset, VFS}) - : std::move(Flow).runWithoutSema(Contents, *Offset, VFS); + return (!Preamble || Opts.RunParser == CodeCompleteOptions::NeverParse) + ? std::move(Flow).runWithoutSema(Contents, *Offset, VFS) + : std::move(Flow).run( + {FileName, Command, Preamble, Contents, *Offset, VFS}); } SignatureHelp signatureHelp(PathRef FileName, Index: clangd/tool/ClangdMain.cpp =================================================================== --- clangd/tool/ClangdMain.cpp +++ clangd/tool/ClangdMain.cpp @@ -254,13 +254,18 @@ "Offsets are in UTF-16 code units")), llvm::cl::init(OffsetEncoding::UnsupportedEncoding)); -static llvm::cl::opt AllowFallbackCompletion( - "allow-fallback-completion", - llvm::cl::desc( - "Allow falling back to code completion without compiling files (using " - "identifiers and symbol indexes), when file cannot be built or the " - "build is not ready."), - llvm::cl::init(false)); +static llvm::cl::opt + CodeCompletionParse( + "code-completion-parse", + llvm::cl::desc("Whether the clang-parser is used for code-completion"), + llvm::cl::values(clEnumValN(CodeCompleteOptions::AlwaysParse, "always", + "Block until the parser can be used"), + clEnumValN(CodeCompleteOptions::ParseIfReady, "auto", + "Use text-based completion if the parser " + "is not ready"), + clEnumValN(CodeCompleteOptions::NeverParse, "never", + "Always used text-based completion")), + llvm::cl::init(CodeCompleteOptions::ParseIfReady), llvm::cl::Hidden); namespace { @@ -473,7 +478,7 @@ CCOpts.SpeculativeIndexRequest = Opts.StaticIndex; CCOpts.EnableFunctionArgSnippets = EnableFunctionArgSnippets; CCOpts.AllScopes = AllScopesCompletion; - CCOpts.AllowFallback = AllowFallbackCompletion; + CCOpts.RunParser = CodeCompletionParse; RealFileSystemProvider FSProvider; // Initialize and run ClangdLSPServer. Index: clangd/unittests/ClangdTests.cpp =================================================================== --- clangd/unittests/ClangdTests.cpp +++ clangd/unittests/ClangdTests.cpp @@ -9,6 +9,7 @@ #include "Annotations.h" #include "ClangdLSPServer.h" #include "ClangdServer.h" +#include "CodeComplete.h" #include "GlobalCompilationDatabase.h" #include "Matchers.h" #include "SyncAPI.h" @@ -1075,7 +1076,7 @@ FS.Files[FooCpp] = FooCpp; auto Opts = clangd::CodeCompleteOptions(); - Opts.AllowFallback = true; + Opts.RunParser = CodeCompleteOptions::ParseIfReady; // This will make compile command broken and preamble absent. CDB.ExtraClangFlags = {"yolo.cc"}; @@ -1092,11 +1093,17 @@ CDB.ExtraClangFlags = {"-std=c++11"}; Server.addDocument(FooCpp, Code.code()); ASSERT_TRUE(Server.blockUntilIdleForTest()); - EXPECT_THAT(cantFail(runCodeComplete(Server, FooCpp, Code.point(), - clangd::CodeCompleteOptions())) - .Completions, - ElementsAre(AllOf(Field(&CodeCompletion::Name, "xyz"), - Field(&CodeCompletion::Scope, "ns::")))); + EXPECT_THAT( + cantFail(runCodeComplete(Server, FooCpp, Code.point(), Opts)).Completions, + ElementsAre(AllOf(Field(&CodeCompletion::Name, "xyz"), + Field(&CodeCompletion::Scope, "ns::")))); + + // Now force identifier-based completion. + Opts.RunParser = CodeCompleteOptions::NeverParse; + EXPECT_THAT( + cantFail(runCodeComplete(Server, FooCpp, Code.point(), Opts)).Completions, + ElementsAre(AllOf(Field(&CodeCompletion::Name, "xyz"), + Field(&CodeCompletion::Scope, "")))); } TEST_F(ClangdVFSTest, FallbackWhenWaitingForCompileCommand) { @@ -1143,7 +1150,7 @@ // hasn't been scheduled. std::this_thread::sleep_for(std::chrono::milliseconds(10)); auto Opts = clangd::CodeCompleteOptions(); - Opts.AllowFallback = true; + Opts.RunParser = CodeCompleteOptions::ParseIfReady; auto Res = cantFail(runCodeComplete(Server, FooCpp, Code.point(), Opts)); EXPECT_EQ(Res.Context, CodeCompletionContext::CCC_Recovery);