[clang-tools-extra] r338597 - [clangd] allow clients to control the compilation database by passing in
Alex Lorenz via cfe-commits
cfe-commits at lists.llvm.org
Wed Aug 1 10:39:30 PDT 2018
Author: arphaman
Date: Wed Aug 1 10:39:29 2018
New Revision: 338597
URL: http://llvm.org/viewvc/llvm-project?rev=338597&view=rev
Log:
[clangd] allow clients to control the compilation database by passing in
compilationDatabaseChanges in the 'workspace/didChangeConfiguration' request
This commit allows clangd to use an in-memory compilation database that's
controlled from the LSP client (-compile_args_from=lsp). It extends the
'workspace/didChangeConfiguration' request to allow the client to pass in a
compilation database subset that needs to be updated in the workspace.
Differential Revision: https://reviews.llvm.org/D49758
Added:
clang-tools-extra/trunk/test/clangd/did-change-configuration-params.test
Modified:
clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp
clang-tools-extra/trunk/clangd/ClangdLSPServer.h
clang-tools-extra/trunk/clangd/GlobalCompilationDatabase.cpp
clang-tools-extra/trunk/clangd/GlobalCompilationDatabase.h
clang-tools-extra/trunk/clangd/Protocol.cpp
clang-tools-extra/trunk/clangd/Protocol.h
clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp
Modified: clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp?rev=338597&r1=338596&r2=338597&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp (original)
+++ clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp Wed Aug 1 10:39:29 2018
@@ -135,11 +135,8 @@ void ClangdLSPServer::onExit(ExitParams
void ClangdLSPServer::onDocumentDidOpen(DidOpenTextDocumentParams &Params) {
PathRef File = Params.textDocument.uri.file();
- if (Params.metadata && !Params.metadata->extraFlags.empty()) {
- NonCachedCDB.setExtraFlagsForFile(File,
- std::move(Params.metadata->extraFlags));
- CDB.invalidate(File);
- }
+ if (Params.metadata && !Params.metadata->extraFlags.empty())
+ CDB.setExtraFlagsForFile(File, std::move(Params.metadata->extraFlags));
std::string &Contents = Params.textDocument.text;
@@ -250,6 +247,7 @@ void ClangdLSPServer::onDocumentDidClose
PathRef File = Params.textDocument.uri.file();
DraftMgr.removeDraft(File);
Server.removeDocument(File);
+ CDB.invalidate(File);
}
void ClangdLSPServer::onDocumentOnTypeFormatting(
@@ -405,12 +403,29 @@ void ClangdLSPServer::applyConfiguration
const ClangdConfigurationParamsChange &Settings) {
// Compilation database change.
if (Settings.compilationDatabasePath.hasValue()) {
- NonCachedCDB.setCompileCommandsDir(
- Settings.compilationDatabasePath.getValue());
- CDB.clear();
+ CDB.setCompileCommandsDir(Settings.compilationDatabasePath.getValue());
reparseOpenedFiles();
}
+
+ // Update to the compilation database.
+ if (Settings.compilationDatabaseChanges) {
+ const auto &CompileCommandUpdates = *Settings.compilationDatabaseChanges;
+ bool ShouldReparseOpenFiles = false;
+ for (auto &Entry : CompileCommandUpdates) {
+ /// The opened files need to be reparsed only when some existing
+ /// entries are changed.
+ PathRef File = Entry.first;
+ if (!CDB.setCompilationCommandForFile(
+ File, tooling::CompileCommand(
+ std::move(Entry.second.workingDirectory), File,
+ std::move(Entry.second.compilationCommand),
+ /*Output=*/"")))
+ ShouldReparseOpenFiles = true;
+ }
+ if (ShouldReparseOpenFiles)
+ reparseOpenedFiles();
+ }
}
// FIXME: This function needs to be properly tested.
@@ -422,10 +437,13 @@ void ClangdLSPServer::onChangeConfigurat
ClangdLSPServer::ClangdLSPServer(JSONOutput &Out,
const clangd::CodeCompleteOptions &CCOpts,
llvm::Optional<Path> CompileCommandsDir,
+ bool ShouldUseInMemoryCDB,
const ClangdServer::Options &Opts)
- : Out(Out), NonCachedCDB(std::move(CompileCommandsDir)), CDB(NonCachedCDB),
+ : Out(Out), CDB(ShouldUseInMemoryCDB ? CompilationDB::makeInMemory()
+ : CompilationDB::makeDirectoryBased(
+ std::move(CompileCommandsDir))),
CCOpts(CCOpts), SupportedSymbolKinds(defaultSymbolKinds()),
- Server(CDB, FSProvider, /*DiagConsumer=*/*this, Opts) {}
+ Server(CDB.getCDB(), FSProvider, /*DiagConsumer=*/*this, Opts) {}
bool ClangdLSPServer::run(std::FILE *In, JSONStreamStyle InputStyle) {
assert(!IsDone && "Run was called before");
@@ -504,3 +522,67 @@ void ClangdLSPServer::reparseOpenedFiles
Server.addDocument(FilePath, *DraftMgr.getDraft(FilePath),
WantDiagnostics::Auto);
}
+
+ClangdLSPServer::CompilationDB ClangdLSPServer::CompilationDB::makeInMemory() {
+ return CompilationDB(llvm::make_unique<InMemoryCompilationDb>(), nullptr,
+ /*IsDirectoryBased=*/false);
+}
+
+ClangdLSPServer::CompilationDB
+ClangdLSPServer::CompilationDB::makeDirectoryBased(
+ llvm::Optional<Path> CompileCommandsDir) {
+ auto CDB = llvm::make_unique<DirectoryBasedGlobalCompilationDatabase>(
+ std::move(CompileCommandsDir));
+ auto CachingCDB = llvm::make_unique<CachingCompilationDb>(*CDB);
+ return CompilationDB(std::move(CDB), std::move(CachingCDB),
+ /*IsDirectoryBased=*/true);
+}
+
+void ClangdLSPServer::CompilationDB::invalidate(PathRef File) {
+ if (!IsDirectoryBased)
+ static_cast<InMemoryCompilationDb *>(CDB.get())->invalidate(File);
+ else
+ CachingCDB->invalidate(File);
+}
+
+bool ClangdLSPServer::CompilationDB::setCompilationCommandForFile(
+ PathRef File, tooling::CompileCommand CompilationCommand) {
+ if (IsDirectoryBased) {
+ elog("Trying to set compile command for {0} while using directory-based "
+ "compilation database",
+ File);
+ return false;
+ }
+ return static_cast<InMemoryCompilationDb *>(CDB.get())
+ ->setCompilationCommandForFile(File, std::move(CompilationCommand));
+}
+
+void ClangdLSPServer::CompilationDB::setExtraFlagsForFile(
+ PathRef File, std::vector<std::string> ExtraFlags) {
+ if (!IsDirectoryBased) {
+ elog("Trying to set extra flags for {0} while using in-memory compilation "
+ "database",
+ File);
+ return;
+ }
+ static_cast<DirectoryBasedGlobalCompilationDatabase *>(CDB.get())
+ ->setExtraFlagsForFile(File, std::move(ExtraFlags));
+ CachingCDB->invalidate(File);
+}
+
+void ClangdLSPServer::CompilationDB::setCompileCommandsDir(Path P) {
+ if (!IsDirectoryBased) {
+ elog("Trying to set compile commands dir while using in-memory compilation "
+ "database");
+ return;
+ }
+ static_cast<DirectoryBasedGlobalCompilationDatabase *>(CDB.get())
+ ->setCompileCommandsDir(P);
+ CachingCDB->clear();
+}
+
+GlobalCompilationDatabase &ClangdLSPServer::CompilationDB::getCDB() {
+ if (CachingCDB)
+ return *CachingCDB;
+ return *CDB;
+}
Modified: clang-tools-extra/trunk/clangd/ClangdLSPServer.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdLSPServer.h?rev=338597&r1=338596&r2=338597&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdLSPServer.h (original)
+++ clang-tools-extra/trunk/clangd/ClangdLSPServer.h Wed Aug 1 10:39:29 2018
@@ -35,7 +35,7 @@ public:
/// for compile_commands.json in all parent directories of each file.
ClangdLSPServer(JSONOutput &Out, const clangd::CodeCompleteOptions &CCOpts,
llvm::Optional<Path> CompileCommandsDir,
- const ClangdServer::Options &Opts);
+ bool ShouldUseInMemoryCDB, const ClangdServer::Options &Opts);
/// 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
@@ -100,10 +100,57 @@ private:
/// Caches FixIts per file and diagnostics
llvm::StringMap<DiagnosticToReplacementMap> FixItsMap;
+ /// Encapsulates the directory-based or the in-memory compilation database
+ /// that's used by the LSP server.
+ class CompilationDB {
+ public:
+ static CompilationDB makeInMemory();
+ static CompilationDB
+ makeDirectoryBased(llvm::Optional<Path> CompileCommandsDir);
+
+ void invalidate(PathRef File);
+
+ /// Sets the compilation command for a particular file.
+ /// Only valid for in-memory CDB, no-op and error log on DirectoryBasedCDB.
+ ///
+ /// \returns True if the File had no compilation command before.
+ bool
+ setCompilationCommandForFile(PathRef File,
+ tooling::CompileCommand CompilationCommand);
+
+ /// Adds extra compilation flags to the compilation command for a particular
+ /// file. Only valid for directory-based CDB, no-op and error log on
+ /// InMemoryCDB;
+ void setExtraFlagsForFile(PathRef File,
+ std::vector<std::string> ExtraFlags);
+
+ /// Set the compile commands directory to \p P.
+ /// Only valid for directory-based CDB, no-op and error log on InMemoryCDB;
+ void setCompileCommandsDir(Path P);
+
+ /// Returns a CDB that should be used to get compile commands for the
+ /// current instance of ClangdLSPServer.
+ GlobalCompilationDatabase &getCDB();
+
+ private:
+ CompilationDB(std::unique_ptr<GlobalCompilationDatabase> CDB,
+ std::unique_ptr<CachingCompilationDb> CachingCDB,
+ bool IsDirectoryBased)
+ : CDB(std::move(CDB)), CachingCDB(std::move(CachingCDB)),
+ IsDirectoryBased(IsDirectoryBased) {}
+
+ // if IsDirectoryBased is true, an instance of InMemoryCDB.
+ // If IsDirectoryBased is false, an instance of DirectoryBasedCDB.
+ // unique_ptr<GlobalCompilationDatabase> CDB;
+ std::unique_ptr<GlobalCompilationDatabase> CDB;
+ // Non-null only for directory-based CDB
+ std::unique_ptr<CachingCompilationDb> CachingCDB;
+ bool IsDirectoryBased;
+ };
+
// Various ClangdServer parameters go here. It's important they're created
// before ClangdServer.
- DirectoryBasedGlobalCompilationDatabase NonCachedCDB;
- CachingCompilationDb CDB;
+ CompilationDB CDB;
RealFileSystemProvider FSProvider;
/// Options used for code completion
Modified: clang-tools-extra/trunk/clangd/GlobalCompilationDatabase.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/GlobalCompilationDatabase.cpp?rev=338597&r1=338596&r2=338597&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/GlobalCompilationDatabase.cpp (original)
+++ clang-tools-extra/trunk/clangd/GlobalCompilationDatabase.cpp Wed Aug 1 10:39:29 2018
@@ -152,5 +152,29 @@ void CachingCompilationDb::clear() {
Cached.clear();
}
+llvm::Optional<tooling::CompileCommand>
+InMemoryCompilationDb::getCompileCommand(PathRef File) const {
+ std::lock_guard<std::mutex> Lock(Mutex);
+ auto It = Commands.find(File);
+ if (It == Commands.end())
+ return None;
+ return It->second;
+}
+
+bool InMemoryCompilationDb::setCompilationCommandForFile(
+ PathRef File, tooling::CompileCommand CompilationCommand) {
+ std::unique_lock<std::mutex> Lock(Mutex);
+ auto ItInserted = Commands.insert(std::make_pair(File, CompilationCommand));
+ if (ItInserted.second)
+ return true;
+ ItInserted.first->setValue(std::move(CompilationCommand));
+ return false;
+}
+
+void InMemoryCompilationDb::invalidate(PathRef File) {
+ std::unique_lock<std::mutex> Lock(Mutex);
+ Commands.erase(File);
+}
+
} // namespace clangd
} // namespace clang
Modified: clang-tools-extra/trunk/clangd/GlobalCompilationDatabase.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/GlobalCompilationDatabase.h?rev=338597&r1=338596&r2=338597&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/GlobalCompilationDatabase.h (original)
+++ clang-tools-extra/trunk/clangd/GlobalCompilationDatabase.h Wed Aug 1 10:39:29 2018
@@ -113,6 +113,29 @@ private:
Cached; /* GUARDED_BY(Mut) */
};
+/// Gets compile args from an in-memory mapping based on a filepath. Typically
+/// used by clients who provide the compile commands themselves.
+class InMemoryCompilationDb : public GlobalCompilationDatabase {
+public:
+ /// Gets compile command for \p File from the stored mapping.
+ llvm::Optional<tooling::CompileCommand>
+ getCompileCommand(PathRef File) const override;
+
+ /// Sets the compilation command for a particular file.
+ ///
+ /// \returns True if the File had no compilation command before.
+ bool setCompilationCommandForFile(PathRef File,
+ tooling::CompileCommand CompilationCommand);
+
+ /// Removes the compilation command for \p File if it's present in the
+ /// mapping.
+ void invalidate(PathRef File);
+
+private:
+ mutable std::mutex Mutex;
+ llvm::StringMap<tooling::CompileCommand> Commands; /* GUARDED_BY(Mut) */
+};
+
} // namespace clangd
} // namespace clang
Modified: clang-tools-extra/trunk/clangd/Protocol.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Protocol.cpp?rev=338597&r1=338596&r2=338597&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/Protocol.cpp (original)
+++ clang-tools-extra/trunk/clangd/Protocol.cpp Wed Aug 1 10:39:29 2018
@@ -591,10 +591,18 @@ bool fromJSON(const json::Value &Params,
return O && O.map("settings", CCP.settings);
}
+bool fromJSON(const llvm::json::Value &Params,
+ ClangdCompileCommand &CDbUpdate) {
+ json::ObjectMapper O(Params);
+ return O && O.map("workingDirectory", CDbUpdate.workingDirectory) &&
+ O.map("compilationCommand", CDbUpdate.compilationCommand);
+}
+
bool fromJSON(const json::Value &Params,
ClangdConfigurationParamsChange &CCPC) {
json::ObjectMapper O(Params);
- return O && O.map("compilationDatabasePath", CCPC.compilationDatabasePath);
+ return O && O.map("compilationDatabasePath", CCPC.compilationDatabasePath) &&
+ O.map("compilationDatabaseChanges", CCPC.compilationDatabaseChanges);
}
} // namespace clangd
Modified: clang-tools-extra/trunk/clangd/Protocol.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Protocol.h?rev=338597&r1=338596&r2=338597&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/Protocol.h (original)
+++ clang-tools-extra/trunk/clangd/Protocol.h Wed Aug 1 10:39:29 2018
@@ -322,11 +322,25 @@ struct ClientCapabilities {
bool fromJSON(const llvm::json::Value &, ClientCapabilities &);
+/// Clangd extension that's used in the 'compilationDatabaseChanges' in
+/// workspace/didChangeConfiguration to record updates to the in-memory
+/// compilation database.
+struct ClangdCompileCommand {
+ std::string workingDirectory;
+ std::vector<std::string> compilationCommand;
+};
+bool fromJSON(const llvm::json::Value &, ClangdCompileCommand &);
+
/// Clangd extension to set clangd-specific "initializationOptions" in the
/// "initialize" request and for the "workspace/didChangeConfiguration"
/// notification since the data received is described as 'any' type in LSP.
struct ClangdConfigurationParamsChange {
llvm::Optional<std::string> compilationDatabasePath;
+
+ // The changes that happened to the compilation database.
+ // The key of the map is a file name.
+ llvm::Optional<std::map<std::string, ClangdCompileCommand>>
+ compilationDatabaseChanges;
};
bool fromJSON(const llvm::json::Value &, ClangdConfigurationParamsChange &);
Modified: clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp?rev=338597&r1=338596&r2=338597&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp (original)
+++ clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp Wed Aug 1 10:39:29 2018
@@ -173,6 +173,18 @@ static llvm::cl::opt<Path> YamlSymbolFil
"eventually. Don't rely on it."),
llvm::cl::init(""), llvm::cl::Hidden);
+enum CompileArgsFrom { LSPCompileArgs, FilesystemCompileArgs };
+
+static llvm::cl::opt<CompileArgsFrom> CompileArgsFrom(
+ "compile_args_from", llvm::cl::desc("The source of compile commands"),
+ llvm::cl::values(clEnumValN(LSPCompileArgs, "lsp",
+ "All compile commands come from LSP and "
+ "'compile_commands.json' files are ignored"),
+ clEnumValN(FilesystemCompileArgs, "filesystem",
+ "All compile commands come from the "
+ "'compile_commands.json' files")),
+ llvm::cl::init(FilesystemCompileArgs), llvm::cl::Hidden);
+
int main(int argc, char *argv[]) {
llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
llvm::cl::SetVersionPrinter([](llvm::raw_ostream &OS) {
@@ -289,7 +301,9 @@ int main(int argc, char *argv[]) {
}
// Initialize and run ClangdLSPServer.
- ClangdLSPServer LSPServer(Out, CCOpts, CompileCommandsDirPath, Opts);
+ ClangdLSPServer LSPServer(
+ Out, CCOpts, CompileCommandsDirPath,
+ /*ShouldUseInMemoryCDB=*/CompileArgsFrom == LSPCompileArgs, Opts);
constexpr int NoShutdownRequestErrorCode = 1;
llvm::set_thread_name("clangd.main");
// Change stdin to binary to not lose \r\n on windows.
Added: clang-tools-extra/trunk/test/clangd/did-change-configuration-params.test
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/did-change-configuration-params.test?rev=338597&view=auto
==============================================================================
--- clang-tools-extra/trunk/test/clangd/did-change-configuration-params.test (added)
+++ clang-tools-extra/trunk/test/clangd/did-change-configuration-params.test Wed Aug 1 10:39:29 2018
@@ -0,0 +1,52 @@
+# RUN: clangd -compile_args_from=lsp -lit-test < %s 2> %t | FileCheck -strict-whitespace %s
+# RUN: cat %t | FileCheck --check-prefix=ERR %s
+# UNSUPPORTED: mingw32,win32
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
+---
+{"jsonrpc":"2.0","method":"workspace/didChangeConfiguration","params":{"settings":{"compilationDatabaseChanges":{"/clangd-test/foo.c": {"workingDirectory":"/clangd-test", "compilationCommand": ["clang", "-c", "foo.c"]}}}}}
+---
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","version":1,"text":"int main() { int i; return i; }"}}}
+# CHECK: "method": "textDocument/publishDiagnostics",
+# CHECK-NEXT: "params": {
+# CHECK-NEXT: "diagnostics": [],
+# CHECK-NEXT: "uri": "file://{{.*}}/foo.c"
+# CHECK-NEXT: }
+---
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///bar.c","languageId":"c","version":1,"text":"int main() { int i; return i; }"}}}
+# CHECK: "method": "textDocument/publishDiagnostics",
+# CHECK-NEXT: "params": {
+# CHECK-NEXT: "diagnostics": [],
+# CHECK-NEXT: "uri": "file://{{.*}}/bar.c"
+# CHECK-NEXT: }
+---
+{"jsonrpc":"2.0","method":"workspace/didChangeConfiguration","params":{"settings":{"compilationDatabaseChanges":{"/clangd-test/foo.c": {"workingDirectory":"/clangd-test2", "compilationCommand": ["clang", "-c", "foo.c", "-Wall", "-Werror"]}}}}}
+# CHECK: "method": "textDocument/publishDiagnostics",
+# CHECK-NEXT: "params": {
+# CHECK-NEXT: "diagnostics": [
+# CHECK-NEXT: {
+# CHECK-NEXT: "message": "variable 'i' is uninitialized when used here",
+# CHECK-NEXT: "range": {
+# CHECK-NEXT: "end": {
+# CHECK-NEXT: "character": 28,
+# CHECK-NEXT: "line": 0
+# CHECK-NEXT: },
+# CHECK-NEXT: "start": {
+# CHECK-NEXT: "character": 27,
+# CHECK-NEXT: "line": 0
+# CHECK-NEXT: }
+# CHECK-NEXT: },
+# CHECK-NEXT: "severity": 1
+# CHECK-NEXT: }
+# CHECK-NEXT: ],
+# CHECK-NEXT: "uri": "file://{{.*}}/foo.c"
+# CHECK-NEXT: }
+#
+# ERR: Updating file {{.*}}foo.c with command [{{.*}}clangd-test2] clang -c foo.c -Wall -Werror
+# Don't reparse the second file:
+# ERR: Skipping rebuild of the AST for {{.*}}bar.c
+---
+{"jsonrpc":"2.0","id":5,"method":"shutdown"}
+---
+{"jsonrpc":"2.0","method":"exit"}
+
+
More information about the cfe-commits
mailing list