Index: clangd/ClangdLSPServer.h =================================================================== --- clangd/ClangdLSPServer.h +++ clangd/ClangdLSPServer.h @@ -25,7 +25,8 @@ /// dispatch and ClangdServer together. class ClangdLSPServer { public: - ClangdLSPServer(JSONOutput &Out, bool RunSynchronously); + ClangdLSPServer(JSONOutput &Out, bool RunSynchronously, + llvm::Optional CompileCommandsDir); /// Run LSP server loop, receiving input for it from \p In. \p In must be /// opened in binary mode. Output will be written using Out variable passed to Index: clangd/ClangdLSPServer.cpp =================================================================== --- clangd/ClangdLSPServer.cpp +++ clangd/ClangdLSPServer.cpp @@ -70,7 +70,7 @@ void onCompletion(TextDocumentPositionParams Params, StringRef ID, JSONOutput &Out) override; void onGoToDefinition(TextDocumentPositionParams Params, StringRef ID, - JSONOutput &Out) override; + JSONOutput &Out) override; private: ClangdLSPServer &LangServer; @@ -181,9 +181,11 @@ void ClangdLSPServer::LSPProtocolCallbacks::onCompletion( TextDocumentPositionParams Params, StringRef ID, JSONOutput &Out) { - auto Items = LangServer.Server.codeComplete( - Params.textDocument.uri.file, - Position{Params.position.line, Params.position.character}).Value; + auto Items = LangServer.Server + .codeComplete(Params.textDocument.uri.file, + Position{Params.position.line, + Params.position.character}) + .Value; std::string Completions; for (const auto &Item : Items) { @@ -200,9 +202,11 @@ void ClangdLSPServer::LSPProtocolCallbacks::onGoToDefinition( TextDocumentPositionParams Params, StringRef ID, JSONOutput &Out) { - auto Items = LangServer.Server.findDefinitions( - Params.textDocument.uri.file, - Position{Params.position.line, Params.position.character}).Value; + auto Items = LangServer.Server + .findDefinitions(Params.textDocument.uri.file, + Position{Params.position.line, + Params.position.character}) + .Value; std::string Locations; for (const auto &Item : Items) { @@ -216,8 +220,9 @@ R"(,"result":[)" + Locations + R"(]})"); } -ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, bool RunSynchronously) - : Out(Out), DiagConsumer(*this), +ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, bool RunSynchronously, + llvm::Optional CompileCommandsDir) + : Out(Out), CDB(CompileCommandsDir), DiagConsumer(*this), Server(CDB, DiagConsumer, FSProvider, RunSynchronously) {} void ClangdLSPServer::run(std::istream &In) { Index: clangd/ClangdServer.cpp =================================================================== --- clangd/ClangdServer.cpp +++ clangd/ClangdServer.cpp @@ -273,16 +273,16 @@ return DumpFuture.get(); } -Tagged> -ClangdServer::findDefinitions(PathRef File, Position Pos) { +Tagged> ClangdServer::findDefinitions(PathRef File, + Position Pos) { auto FileContents = DraftMgr.getDraft(File); - assert(FileContents.Draft && "findDefinitions is called for non-added document"); + assert(FileContents.Draft && + "findDefinitions is called for non-added document"); std::vector Result; auto TaggedFS = FSProvider.getTaggedFileSystem(File); - Units.runOnUnit(File, *FileContents.Draft, ResourceDir, CDB, PCHs, - TaggedFS.Value, [&](ClangdUnit &Unit) { - Result = Unit.findDefinitions(Pos); - }); + Units.runOnUnit( + File, *FileContents.Draft, ResourceDir, CDB, PCHs, TaggedFS.Value, + [&](ClangdUnit &Unit) { Result = Unit.findDefinitions(Pos); }); return make_tagged(std::move(Result), TaggedFS.Tag); } Index: clangd/GlobalCompilationDatabase.h =================================================================== --- clangd/GlobalCompilationDatabase.h +++ clangd/GlobalCompilationDatabase.h @@ -45,6 +45,9 @@ class DirectoryBasedGlobalCompilationDatabase : public GlobalCompilationDatabase { public: + DirectoryBasedGlobalCompilationDatabase( + llvm::Optional NewCompileCommandsDir) + : CompileCommandsDir(NewCompileCommandsDir.getValue()) {} std::vector getCompileCommands(PathRef File) override; @@ -52,6 +55,8 @@ private: tooling::CompilationDatabase *getCompilationDatabase(PathRef File); + Path CompileCommandsDir; + tooling::CompilationDatabase *tryLoadDatabaseFromPath(PathRef File); std::mutex Mutex; /// Caches compilation databases loaded from directories(keys are Index: clangd/GlobalCompilationDatabase.cpp =================================================================== --- clangd/GlobalCompilationDatabase.cpp +++ clangd/GlobalCompilationDatabase.cpp @@ -62,43 +62,52 @@ } tooling::CompilationDatabase * -DirectoryBasedGlobalCompilationDatabase::getCompilationDatabase(PathRef File) { - std::lock_guard Lock(Mutex); - +DirectoryBasedGlobalCompilationDatabase::tryLoadDatabaseFromPath(PathRef File) { namespace path = llvm::sys::path; + auto CachedIt = CompilationDatabases.find(File); + std::string Error = ""; assert((path::is_absolute(File, path::Style::posix) || path::is_absolute(File, path::Style::windows)) && "path must be absolute"); - for (auto Path = path::parent_path(File); !Path.empty(); - Path = path::parent_path(Path)) { - - auto CachedIt = CompilationDatabases.find(Path); - if (CachedIt != CompilationDatabases.end()) - return CachedIt->second.get(); - std::string Error; - auto CDB = tooling::CompilationDatabase::loadFromDirectory(Path, Error); - if (!CDB) { - if (!Error.empty()) { - // FIXME(ibiryukov): logging - // Output.log("Error when trying to load compilation database from " + - // Twine(Path) + ": " + Twine(Error) + "\n"); - } - continue; - } + if (CachedIt != CompilationDatabases.end()) + return (CachedIt->second.get()); + auto CDB = tooling::CompilationDatabase::loadFromDirectory(File, Error); + if (CDB && Error.empty()) { + auto result = CDB.get(); + CompilationDatabases.insert(std::make_pair(File, std::move(CDB))); // FIXME(ibiryukov): Invalidate cached compilation databases on changes - auto result = CDB.get(); - CompilationDatabases.insert(std::make_pair(Path, std::move(CDB))); return result; } - // FIXME(ibiryukov): logging // Output.log("Failed to find compilation database for " + Twine(File) + // "\n"); return nullptr; } +tooling::CompilationDatabase * +DirectoryBasedGlobalCompilationDatabase::getCompilationDatabase(PathRef File) { + std::lock_guard Lock(Mutex); + + namespace path = llvm::sys::path; + if (!CompileCommandsDir.empty()) { + auto CDB = tryLoadDatabaseFromPath(CompileCommandsDir); + return CDB; + } + + for (auto Path = path::parent_path(File); !Path.empty(); + Path = path::parent_path(Path)) { + auto CDB = tryLoadDatabaseFromPath(Path); + if (!CDB) + continue; + + return CDB; + } + + return nullptr; +} + } // namespace clangd } // namespace clang Index: clangd/tool/ClangdMain.cpp =================================================================== --- clangd/tool/ClangdMain.cpp +++ clangd/tool/ClangdMain.cpp @@ -11,8 +11,8 @@ #include "JSONRPCDispatcher.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" #include "llvm/Support/Program.h" - #include #include #include @@ -25,6 +25,12 @@ llvm::cl::desc("parse on main thread"), llvm::cl::init(false), llvm::cl::Hidden); +static llvm::cl::opt CompileCommandsDir( + "compile-commands-dir", + llvm::cl::desc("Specify a path to look for compile_commands.json. If path " + "is invalid, clangd will look in the current directory and " + "parent paths of each source file.")); + int main(int argc, char *argv[]) { llvm::cl::ParseCommandLineOptions(argc, argv, "clangd"); @@ -32,9 +38,30 @@ llvm::raw_ostream &Logs = llvm::errs(); JSONOutput Out(Outs, Logs); + // If --compile-commands-dir arg was invoked, check value and override default + // path. + namespace path = llvm::sys::path; + + if (!llvm::sys::path::is_absolute(CompileCommandsDir)) { + Out.log("Path specified by --compile-commands-dir must be an absolute " + "path. The argument will be ignored.\n"); + CompileCommandsDir = ""; + } + + if (!llvm::sys::fs::exists(CompileCommandsDir)) { + Out.log("Path specified by --compile-commands-dir. The argument will be " + "ignored.\n"); + CompileCommandsDir = ""; + } + llvm::Optional CompileCommandsDirPath; + // Change stdin to binary to not lose \r\n on windows. llvm::sys::ChangeStdinToBinary(); + if (CompileCommandsDir.empty()) + CompileCommandsDirPath = llvm::None; + else + CompileCommandsDirPath = CompileCommandsDir; - ClangdLSPServer LSPServer(Out, RunSynchronously); + ClangdLSPServer LSPServer(Out, RunSynchronously, CompileCommandsDirPath); LSPServer.run(std::cin); }