diff --git a/clang-tools-extra/clangd/ClangdLSPServer.h b/clang-tools-extra/clangd/ClangdLSPServer.h --- a/clang-tools-extra/clangd/ClangdLSPServer.h +++ b/clang-tools-extra/clangd/ClangdLSPServer.h @@ -36,17 +36,24 @@ /// The server also supports $/cancelRequest (MessageHandler provides this). class ClangdLSPServer : private ClangdServer::Callbacks { public: - /// If \p CompileCommandsDir has a value, compile_commands.json will be - /// loaded only from \p CompileCommandsDir. Otherwise, clangd will look - /// for compile_commands.json in all parent directories of each file. - /// If UseDirBasedCDB is false, compile commands are not read from disk. - // FIXME: Clean up signature around CDBs. + struct Options : ClangdServer::Options { + /// Look for compilation databases, rather than using compile commands + /// set via LSP (extensions) only. + bool UseDirBasedCDB = true; + /// A fixed directory to search for a compilation database in. + /// If not set, we search upward from the source file. + llvm::Optional CompileCommandsDir; + /// The offset-encoding to use, or None to negotiate it over LSP. + llvm::Optional OffsetEncoding; + + /// Per-feature options. Generally ClangdServer lets these vary + /// per-request, but LSP allows limited/no customizations. + clangd::CodeCompleteOptions CodeComplete; + clangd::RenameOptions Rename; + }; + ClangdLSPServer(Transport &Transp, const ThreadsafeFS &TFS, - const clangd::CodeCompleteOptions &CCOpts, - const clangd::RenameOptions &RenameOpts, - llvm::Optional CompileCommandsDir, bool UseDirBasedCDB, - llvm::Optional ForcedOffsetEncoding, - const ClangdServer::Options &Opts); + const ClangdLSPServer::Options &Opts); /// The destructor blocks on any outstanding background tasks. ~ClangdLSPServer(); @@ -227,10 +234,6 @@ } const ThreadsafeFS &TFS; - /// Options used for code completion - clangd::CodeCompleteOptions CCOpts; - /// Options used for rename. - clangd::RenameOptions RenameOpts; /// Options used for diagnostics. ClangdDiagnosticOptions DiagOpts; /// The supported kinds of the client. @@ -268,14 +271,11 @@ // Store of the current versions of the open documents. DraftStore DraftMgr; + Options Opts; // The CDB is created by the "initialize" LSP method. - bool UseDirBasedCDB; // FIXME: make this a capability. - llvm::Optional CompileCommandsDir; // FIXME: merge with capability? std::unique_ptr BaseCDB; // CDB is BaseCDB plus any commands overridden via LSP extensions. llvm::Optional CDB; - ClangdServer::Options ClangdServerOpts; - llvm::Optional NegotiatedOffsetEncoding; // The ClangdServer is created by the "initialize" LSP method. llvm::Optional Server; }; diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp --- a/clang-tools-extra/clangd/ClangdLSPServer.cpp +++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -395,7 +395,7 @@ Context handlerContext() const { return Context::current().derive( kCurrentOffsetEncoding, - Server.NegotiatedOffsetEncoding.getValueOr(OffsetEncoding::UTF16)); + Server.Opts.OffsetEncoding.getValueOr(OffsetEncoding::UTF16)); } // We run cancelable requests in a context that does two things: @@ -465,43 +465,42 @@ void ClangdLSPServer::onInitialize(const InitializeParams &Params, Callback Reply) { // Determine character encoding first as it affects constructed ClangdServer. - if (Params.capabilities.offsetEncoding && !NegotiatedOffsetEncoding) { - NegotiatedOffsetEncoding = OffsetEncoding::UTF16; // fallback + if (Params.capabilities.offsetEncoding && !Opts.OffsetEncoding) { + Opts.OffsetEncoding = OffsetEncoding::UTF16; // fallback for (OffsetEncoding Supported : *Params.capabilities.offsetEncoding) if (Supported != OffsetEncoding::UnsupportedEncoding) { - NegotiatedOffsetEncoding = Supported; + Opts.OffsetEncoding = Supported; break; } } - ClangdServerOpts.TheiaSemanticHighlighting = + Opts.TheiaSemanticHighlighting = Params.capabilities.TheiaSemanticHighlighting; if (Params.capabilities.TheiaSemanticHighlighting && Params.capabilities.SemanticTokens) { log("Client supports legacy semanticHighlights notification and standard " "semanticTokens request, choosing the latter (no notifications)."); - ClangdServerOpts.TheiaSemanticHighlighting = false; + Opts.TheiaSemanticHighlighting = false; } if (Params.rootUri && *Params.rootUri) - ClangdServerOpts.WorkspaceRoot = std::string(Params.rootUri->file()); + Opts.WorkspaceRoot = std::string(Params.rootUri->file()); else if (Params.rootPath && !Params.rootPath->empty()) - ClangdServerOpts.WorkspaceRoot = *Params.rootPath; + Opts.WorkspaceRoot = *Params.rootPath; if (Server) return Reply(llvm::make_error("server already initialized", ErrorCode::InvalidRequest)); if (const auto &Dir = Params.initializationOptions.compilationDatabasePath) - CompileCommandsDir = Dir; - if (UseDirBasedCDB) { + Opts.CompileCommandsDir = Dir; + if (Opts.UseDirBasedCDB) { BaseCDB = std::make_unique( - CompileCommandsDir); - BaseCDB = getQueryDriverDatabase( - llvm::makeArrayRef(ClangdServerOpts.QueryDriverGlobs), - std::move(BaseCDB)); + Opts.CompileCommandsDir); + BaseCDB = getQueryDriverDatabase(llvm::makeArrayRef(Opts.QueryDriverGlobs), + std::move(BaseCDB)); } auto Mangler = CommandMangler::detect(); - if (ClangdServerOpts.ResourceDir) - Mangler.ResourceDir = *ClangdServerOpts.ResourceDir; + if (Opts.ResourceDir) + Mangler.ResourceDir = *Opts.ResourceDir; CDB.emplace(BaseCDB.get(), Params.initializationOptions.fallbackFlags, tooling::ArgumentsAdjuster(std::move(Mangler))); { @@ -510,19 +509,18 @@ // Server, CDB, etc. WithContext MainContext(BackgroundContext.clone()); llvm::Optional WithOffsetEncoding; - if (NegotiatedOffsetEncoding) - WithOffsetEncoding.emplace(kCurrentOffsetEncoding, - *NegotiatedOffsetEncoding); - Server.emplace(*CDB, TFS, ClangdServerOpts, + if (Opts.OffsetEncoding) + WithOffsetEncoding.emplace(kCurrentOffsetEncoding, *Opts.OffsetEncoding); + Server.emplace(*CDB, TFS, Opts, static_cast(this)); } applyConfiguration(Params.initializationOptions.ConfigSettings); - CCOpts.EnableSnippets = Params.capabilities.CompletionSnippets; - CCOpts.IncludeFixIts = Params.capabilities.CompletionFixes; - if (!CCOpts.BundleOverloads.hasValue()) - CCOpts.BundleOverloads = Params.capabilities.HasSignatureHelp; - CCOpts.DocumentationFormat = + Opts.CodeComplete.EnableSnippets = Params.capabilities.CompletionSnippets; + Opts.CodeComplete.IncludeFixIts = Params.capabilities.CompletionFixes; + if (!Opts.CodeComplete.BundleOverloads.hasValue()) + Opts.CodeComplete.BundleOverloads = Params.capabilities.HasSignatureHelp; + Opts.CodeComplete.DocumentationFormat = Params.capabilities.CompletionDocumentationFormat; DiagOpts.EmbedFixesInDiagnostics = Params.capabilities.DiagnosticFixes; DiagOpts.SendDiagnosticCategory = Params.capabilities.DiagnosticCategory; @@ -622,14 +620,14 @@ }}, {"typeHierarchyProvider", true}, }}}}; - if (NegotiatedOffsetEncoding) - Result["offsetEncoding"] = *NegotiatedOffsetEncoding; - if (ClangdServerOpts.TheiaSemanticHighlighting) + if (Opts.OffsetEncoding) + Result["offsetEncoding"] = *Opts.OffsetEncoding; + if (Opts.TheiaSemanticHighlighting) Result.getObject("capabilities") ->insert( {"semanticHighlighting", llvm::json::Object{{"scopes", buildHighlightScopeLookupTable()}}}); - if (ClangdServerOpts.FoldingRanges) + if (Opts.FoldingRanges) Result.getObject("capabilities")->insert({"foldingRangeProvider", true}); Reply(std::move(Result)); } @@ -788,7 +786,7 @@ const WorkspaceSymbolParams &Params, Callback> Reply) { Server->workspaceSymbols( - Params.query, CCOpts.Limit, + Params.query, Opts.CodeComplete.Limit, [Reply = std::move(Reply), this](llvm::Expected> Items) mutable { if (!Items) @@ -803,7 +801,7 @@ void ClangdLSPServer::onPrepareRename(const TextDocumentPositionParams &Params, Callback> Reply) { Server->prepareRename(Params.textDocument.uri.file(), Params.position, - RenameOpts, std::move(Reply)); + Opts.Rename, std::move(Reply)); } void ClangdLSPServer::onRename(const RenameParams &Params, @@ -813,7 +811,7 @@ return Reply(llvm::make_error( "onRename called for non-added file", ErrorCode::InvalidParams)); Server->rename( - File, Params.position, Params.newName, RenameOpts, + File, Params.position, Params.newName, Opts.Rename, [File, Params, Reply = std::move(Reply), this](llvm::Expected Edits) mutable { if (!Edits) @@ -1030,7 +1028,8 @@ vlog("ignored auto-triggered completion, preceding char did not match"); return Reply(CompletionList()); } - Server->codeComplete(Params.textDocument.uri.file(), Params.position, CCOpts, + Server->codeComplete(Params.textDocument.uri.file(), Params.position, + Opts.CodeComplete, [Reply = std::move(Reply), this](llvm::Expected List) mutable { if (!List) @@ -1038,7 +1037,7 @@ CompletionList LSPList; LSPList.isIncomplete = List->HasMore; for (const auto &R : List->Completions) { - CompletionItem C = R.render(CCOpts); + CompletionItem C = R.render(Opts.CodeComplete); C.kind = adjustKindToCapability( C.kind, SupportedCompletionItemKinds); LSPList.items.push_back(std::move(C)); @@ -1224,7 +1223,7 @@ void ClangdLSPServer::onReference(const ReferenceParams &Params, Callback> Reply) { Server->findReferences(Params.textDocument.uri.file(), Params.position, - CCOpts.Limit, + Opts.CodeComplete.Limit, [Reply = std::move(Reply)]( llvm::Expected Refs) mutable { if (!Refs) @@ -1340,20 +1339,13 @@ }); } -ClangdLSPServer::ClangdLSPServer( - class Transport &Transp, const ThreadsafeFS &TFS, - const clangd::CodeCompleteOptions &CCOpts, - const clangd::RenameOptions &RenameOpts, - llvm::Optional CompileCommandsDir, bool UseDirBasedCDB, - llvm::Optional ForcedOffsetEncoding, - const ClangdServer::Options &Opts) +ClangdLSPServer::ClangdLSPServer(class Transport &Transp, + const ThreadsafeFS &TFS, + const ClangdLSPServer::Options &Opts) : BackgroundContext(Context::current().clone()), Transp(Transp), - MsgHandler(new MessageHandler(*this)), TFS(TFS), CCOpts(CCOpts), - RenameOpts(RenameOpts), SupportedSymbolKinds(defaultSymbolKinds()), - SupportedCompletionItemKinds(defaultCompletionItemKinds()), - UseDirBasedCDB(UseDirBasedCDB), - CompileCommandsDir(std::move(CompileCommandsDir)), ClangdServerOpts(Opts), - NegotiatedOffsetEncoding(ForcedOffsetEncoding) { + MsgHandler(new MessageHandler(*this)), TFS(TFS), + SupportedSymbolKinds(defaultSymbolKinds()), + SupportedCompletionItemKinds(defaultCompletionItemKinds()), Opts(Opts) { // clang-format off MsgHandler->bind("initialize", &ClangdLSPServer::onInitialize); MsgHandler->bind("initialized", &ClangdLSPServer::onInitialized); 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 @@ -670,9 +670,11 @@ if (auto EnvFlags = llvm::sys::Process::GetEnv(FlagsEnvVar)) log("{0}: {1}", FlagsEnvVar, *EnvFlags); + ClangdLSPServer::Options Opts; + Opts.UseDirBasedCDB = (CompileArgsFrom == FilesystemCompileArgs); + // If --compile-commands-dir arg was invoked, check value and override default // path. - llvm::Optional CompileCommandsDirPath; if (!CompileCommandsDir.empty()) { if (llvm::sys::fs::exists(CompileCommandsDir)) { // We support passing both relative and absolute paths to the @@ -686,7 +688,7 @@ "will be ignored.", EC.message()); } else { - CompileCommandsDirPath = std::string(Path.str()); + Opts.CompileCommandsDir = std::string(Path.str()); } } else { elog("Path specified by --compile-commands-dir does not exist. The " @@ -694,7 +696,6 @@ } } - ClangdServer::Options Opts; switch (PCHStorage) { case PCHStorageFlag::Memory: Opts.StorePreamblesInMemory = true; @@ -744,23 +745,22 @@ Opts.PreserveRecoveryASTType = RecoveryASTType; Opts.FoldingRanges = FoldingRanges; - clangd::CodeCompleteOptions CCOpts; - CCOpts.IncludeIneligibleResults = IncludeIneligibleResults; - CCOpts.Limit = LimitResults; + Opts.CodeComplete.IncludeIneligibleResults = IncludeIneligibleResults; + Opts.CodeComplete.Limit = LimitResults; if (CompletionStyle.getNumOccurrences()) - CCOpts.BundleOverloads = CompletionStyle != Detailed; - CCOpts.ShowOrigins = ShowOrigins; - CCOpts.InsertIncludes = HeaderInsertion; + Opts.CodeComplete.BundleOverloads = CompletionStyle != Detailed; + Opts.CodeComplete.ShowOrigins = ShowOrigins; + Opts.CodeComplete.InsertIncludes = HeaderInsertion; if (!HeaderInsertionDecorators) { - CCOpts.IncludeIndicator.Insert.clear(); - CCOpts.IncludeIndicator.NoInsert.clear(); + Opts.CodeComplete.IncludeIndicator.Insert.clear(); + Opts.CodeComplete.IncludeIndicator.NoInsert.clear(); } - CCOpts.SpeculativeIndexRequest = Opts.StaticIndex; - CCOpts.EnableFunctionArgSnippets = EnableFunctionArgSnippets; - CCOpts.AllScopes = AllScopesCompletion; - CCOpts.RunParser = CodeCompletionParse; - CCOpts.RankingModel = RankingModel; - CCOpts.DecisionForestBase = DecisionForestBase; + Opts.CodeComplete.SpeculativeIndexRequest = Opts.StaticIndex; + Opts.CodeComplete.EnableFunctionArgSnippets = EnableFunctionArgSnippets; + Opts.CodeComplete.AllScopes = AllScopesCompletion; + Opts.CodeComplete.RunParser = CodeCompletionParse; + Opts.CodeComplete.RankingModel = RankingModel; + Opts.CodeComplete.DecisionForestBase = DecisionForestBase; RealThreadsafeFS TFS; std::vector> ProviderStack; @@ -819,13 +819,11 @@ return llvm::is_contained(TweakList, T.id()); return true; }; - llvm::Optional OffsetEncodingFromFlag; if (ForceOffsetEncoding != OffsetEncoding::UnsupportedEncoding) - OffsetEncodingFromFlag = ForceOffsetEncoding; + Opts.OffsetEncoding = ForceOffsetEncoding; - clangd::RenameOptions RenameOpts; // Shall we allow to customize the file limit? - RenameOpts.AllowCrossFile = CrossFileRename; + Opts.Rename.AllowCrossFile = CrossFileRename; // Initialize and run ClangdLSPServer. // Change stdin to binary to not lose \r\n on windows. @@ -856,10 +854,7 @@ std::move(*Mappings)); } - ClangdLSPServer LSPServer( - *TransportLayer, TFS, CCOpts, RenameOpts, CompileCommandsDirPath, - /*UseDirBasedCDB=*/CompileArgsFrom == FilesystemCompileArgs, - OffsetEncodingFromFlag, Opts); + ClangdLSPServer LSPServer(*TransportLayer, TFS, Opts); llvm::set_thread_name("clangd.main"); int ExitCode = LSPServer.run() ? 0 diff --git a/clang-tools-extra/clangd/unittests/ClangdLSPServerTests.cpp b/clang-tools-extra/clangd/unittests/ClangdLSPServerTests.cpp --- a/clang-tools-extra/clangd/unittests/ClangdLSPServerTests.cpp +++ b/clang-tools-extra/clangd/unittests/ClangdLSPServerTests.cpp @@ -8,11 +8,9 @@ #include "Annotations.h" #include "ClangdLSPServer.h" -#include "CodeComplete.h" #include "LSPClient.h" #include "Protocol.h" #include "TestFS.h" -#include "refactor/Rename.h" #include "support/Logger.h" #include "support/TestTracer.h" #include "llvm/ADT/StringRef.h" @@ -36,13 +34,14 @@ class LSPTest : public ::testing::Test, private clangd::Logger { protected: - LSPTest() : LogSession(*this) {} + LSPTest() : LogSession(*this) { + ClangdServer::Options &Base = Opts; + Base = ClangdServer::optsForTest(); + } LSPClient &start() { EXPECT_FALSE(Server.hasValue()) << "Already initialized"; - Server.emplace(Client.transport(), FS, CCOpts, RenameOpts, - /*CompileCommandsDir=*/llvm::None, /*UseDirBasedCDB=*/false, - /*ForcedOffsetEncoding=*/llvm::None, Opts); + Server.emplace(Client.transport(), FS, Opts); ServerThread.emplace([&] { EXPECT_TRUE(Server->run()); }); Client.call("initialize", llvm::json::Object{}); return Client; @@ -64,9 +63,7 @@ } MockFS FS; - CodeCompleteOptions CCOpts; - RenameOptions RenameOpts; - ClangdServer::Options Opts = ClangdServer::optsForTest(); + ClangdLSPServer::Options Opts; private: // Color logs so we can distinguish them from test output.