Index: clangd/ClangdLSPServer.h =================================================================== --- clangd/ClangdLSPServer.h +++ clangd/ClangdLSPServer.h @@ -25,7 +25,7 @@ /// dispatch and ClangdServer together. class ClangdLSPServer { public: - ClangdLSPServer(JSONOutput &Out, bool RunSynchronously); + ClangdLSPServer(JSONOutput &Out, bool RunSynchronously, std::string CompileCommands); /// 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 @@ -216,9 +216,9 @@ R"(,"result":[)" + Locations + R"(]})"); } -ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, bool RunSynchronously) +ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, bool RunSynchronously, std::string CompileCommands) : Out(Out), DiagConsumer(*this), - Server(CDB, DiagConsumer, FSProvider, RunSynchronously) {} + Server(CDB, DiagConsumer, FSProvider, RunSynchronously, CompileCommands) {} void ClangdLSPServer::run(std::istream &In) { assert(!IsDone && "Run was called before"); Index: clangd/ClangdServer.h =================================================================== --- clangd/ClangdServer.h +++ clangd/ClangdServer.h @@ -156,7 +156,7 @@ /// location, obtained via CompilerInvocation::GetResourcePath. ClangdServer(GlobalCompilationDatabase &CDB, DiagnosticsConsumer &DiagConsumer, - FileSystemProvider &FSProvider, bool RunSynchronously, + FileSystemProvider &FSProvider, bool RunSynchronously, std::string CompileCommands, llvm::Optional ResourceDir = llvm::None); /// Add a \p File to the list of tracked C++ files or update the contents if Index: clangd/ClangdServer.cpp =================================================================== --- clangd/ClangdServer.cpp +++ clangd/ClangdServer.cpp @@ -147,11 +147,16 @@ DiagnosticsConsumer &DiagConsumer, FileSystemProvider &FSProvider, bool RunSynchronously, + std::string CompileCommands, llvm::Optional ResourceDir) : CDB(CDB), DiagConsumer(DiagConsumer), FSProvider(FSProvider), ResourceDir(ResourceDir ? ResourceDir->str() : getStandardResourceDir()), PCHs(std::make_shared()), - WorkScheduler(RunSynchronously) {} + WorkScheduler(RunSynchronously) { + + if (CompileCommands != "") + CDB.CompileCommands = CompileCommands; + } void ClangdServer::addDocument(PathRef File, StringRef Contents) { DocVersion Version = DraftMgr.updateDraft(File, Contents); Index: clangd/GlobalCompilationDatabase.h =================================================================== --- clangd/GlobalCompilationDatabase.h +++ clangd/GlobalCompilationDatabase.h @@ -33,8 +33,9 @@ public: virtual ~GlobalCompilationDatabase() = default; - virtual std::vector + virtual std::vector getCompileCommands(PathRef File) = 0; + std::string CompileCommands; /// FIXME(ibiryukov): add facilities to track changes to compilation flags of /// existing targets. @@ -49,6 +50,7 @@ getCompileCommands(PathRef File) override; void setExtraFlagsForFile(PathRef File, std::vector ExtraFlags); + tooling::CompilationDatabase *tryLoadDatabaseFromPath(PathRef File, std::string &Error); private: tooling::CompilationDatabase *getCompilationDatabase(PathRef File); Index: clangd/GlobalCompilationDatabase.cpp =================================================================== --- clangd/GlobalCompilationDatabase.cpp +++ clangd/GlobalCompilationDatabase.cpp @@ -11,6 +11,16 @@ #include "clang/Tooling/CompilationDatabase.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/Mutex.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/YAMLTraits.h" namespace clang { namespace clangd { @@ -61,37 +71,44 @@ ExtraFlagsForFile[File] = std::move(ExtraFlags); } +tooling::CompilationDatabase * DirectoryBasedGlobalCompilationDatabase::tryLoadDatabaseFromPath(PathRef File, std::string &Error) +{ + auto CachedIt = CompilationDatabases.find(File); + 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))); + return result; + } + return nullptr; +} + tooling::CompilationDatabase * DirectoryBasedGlobalCompilationDatabase::getCompilationDatabase(PathRef File) { std::lock_guard Lock(Mutex); - - namespace path = llvm::sys::path; - - assert((path::is_absolute(File, path::Style::posix) || - path::is_absolute(File, path::Style::windows)) && - "path must be absolute"); + namespace path = llvm::sys::path; + if (llvm::sys::fs::exists(CompileCommands)) + File = CompileCommands; + + std::string Error; + File = path::parent_path(File); + auto CDB = tryLoadDatabaseFromPath(File, Error); + + if (CDB && Error.empty()) + return CDB; 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; - } - - // FIXME(ibiryukov): Invalidate cached compilation databases on changes - auto result = CDB.get(); - CompilationDatabases.insert(std::make_pair(Path, std::move(CDB))); - return result; + Path = path::parent_path(Path)) { + Error = ""; + CDB = tryLoadDatabaseFromPath(Path, Error); + if (!CDB || !Error.empty()) + continue; + + // FIXME(ibiryukov): Invalidate cached compilation databases on changes + return CDB; } // FIXME(ibiryukov): logging Index: clangd/tool/ClangdMain.cpp =================================================================== --- clangd/tool/ClangdMain.cpp +++ clangd/tool/ClangdMain.cpp @@ -12,6 +12,7 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Program.h" +#include "llvm/Support/Path.h" #include #include @@ -25,6 +26,11 @@ llvm::cl::desc("parse on main thread"), llvm::cl::init(false), llvm::cl::Hidden); +static llvm::cl::opt + CompileCommands("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,17 @@ llvm::raw_ostream &Logs = llvm::errs(); JSONOutput Out(Outs, Logs); + // If --compileCommands arg was invoked, check value and override default path. + namespace path = llvm::sys::path; + if (!llvm::sys::path::is_absolute(CompileCommands) || CompileCommands.empty()) + Logs << "Path specified for compilation database must be absolute. Verifying current folder instead."; + + if (!llvm::sys::fs::exists(CompileCommands)) + Logs << "File does not exist. Verifying current folder instead."; + // Change stdin to binary to not lose \r\n on windows. llvm::sys::ChangeStdinToBinary(); - ClangdLSPServer LSPServer(Out, RunSynchronously); + ClangdLSPServer LSPServer(Out, RunSynchronously, CompileCommands); LSPServer.run(std::cin); } Index: test/clangd/hover.test =================================================================== --- /dev/null +++ test/clangd/hover.test @@ -0,0 +1,26 @@ +# RUN: clangd -run-synchronously < %s | FileCheck %s +# It is absolutely vital that this file has CRLF line endings. +# +Content-Length: 125 + +{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}} + +Content-Length: 172 + +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///main.cpp","languageId":"cpp","version":1,"text":"int main() {\nint a;\na;\n}\n"}}} + +Content-Length: 143 + +{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":0,"character":5}}} +# Go to local variable +# CHECK: {"jsonrpc":"2.0","id":1,"result":{"contents": {"language": "C++", "value": "int main() {\nint a;\na;\n}"}, "range": {"start": {"line": 0, "character": 0}, "end": {"line": 3, "character": 1}}}} + +Content-Length: 143 + +{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":1,"character":5}}} +# Go to local variable +# CHECK: {"jsonrpc":"2.0","id":1,"result":{"contents": {"language": "C++", "value": "int a"}, "range": {"start": {"line": 1, "character": 0}, "end": {"line": 1, "character": 5}}}} + +Content-Length: 44 + +{"jsonrpc":"2.0","id":3,"method":"shutdown"}