Author: krasimir Date: Thu Jul 6 01:44:54 2017 New Revision: 307241 URL: http://llvm.org/viewvc/llvm-project?rev=307241&view=rev Log: [clangd] Add support for per-file extra flags
Summary: This patch adds the ability to specify user-defined extra flags per opened file through the LSP layer. This is a non-standard extension to the protocol. I've already created a feature request about it for upstream lsp: https://github.com/Microsoft/language-server-protocol/issues/255 The particular use-case is ycmd, which has a python script for figuring out extra flags per file: https://github.com/Valloric/ycmd#flagsforfile-filename-kwargs- Reviewers: ilya-biryukov, klimek, bkramer Reviewed By: ilya-biryukov Subscribers: cfe-commits Tags: #clang-tools-extra Differential Revision: https://reviews.llvm.org/D34947 Added: clang-tools-extra/trunk/test/clangd/extra-flags.test Modified: clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp clang-tools-extra/trunk/clangd/ClangdUnitStore.cpp 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 Modified: clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp?rev=307241&r1=307240&r2=307241&view=diff ============================================================================== --- clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp (original) +++ clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp Thu Jul 6 01:44:54 2017 @@ -97,6 +97,9 @@ void ClangdLSPServer::LSPProtocolCallbac void ClangdLSPServer::LSPProtocolCallbacks::onDocumentDidOpen( DidOpenTextDocumentParams Params, JSONOutput &Out) { + if (Params.metadata && !Params.metadata->extraFlags.empty()) + LangServer.CDB.setExtraFlagsForFile(Params.textDocument.uri.file, + std::move(Params.metadata->extraFlags)); LangServer.Server.addDocument(Params.textDocument.uri.file, Params.textDocument.text); } Modified: clang-tools-extra/trunk/clangd/ClangdUnitStore.cpp URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdUnitStore.cpp?rev=307241&r1=307240&r2=307241&view=diff ============================================================================== --- clang-tools-extra/trunk/clangd/ClangdUnitStore.cpp (original) +++ clang-tools-extra/trunk/clangd/ClangdUnitStore.cpp Thu Jul 6 01:44:54 2017 @@ -22,13 +22,12 @@ void ClangdUnitStore::removeUnitIfPresen OpenedFiles.erase(It); } -std::vector<tooling::CompileCommand> ClangdUnitStore::getCompileCommands(GlobalCompilationDatabase &CDB, PathRef File) { +std::vector<tooling::CompileCommand> +ClangdUnitStore::getCompileCommands(GlobalCompilationDatabase &CDB, + PathRef File) { std::vector<tooling::CompileCommand> Commands = CDB.getCompileCommands(File); - if (Commands.empty()) { + if (Commands.empty()) // Add a fake command line if we know nothing. - Commands.push_back(tooling::CompileCommand( - llvm::sys::path::parent_path(File), llvm::sys::path::filename(File), - {"clang", "-fsyntax-only", File.str()}, "")); - } + Commands.push_back(getDefaultCompileCommand(File)); return Commands; } Modified: clang-tools-extra/trunk/clangd/GlobalCompilationDatabase.cpp URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/GlobalCompilationDatabase.cpp?rev=307241&r1=307240&r2=307241&view=diff ============================================================================== --- clang-tools-extra/trunk/clangd/GlobalCompilationDatabase.cpp (original) +++ clang-tools-extra/trunk/clangd/GlobalCompilationDatabase.cpp Thu Jul 6 01:44:54 2017 @@ -12,17 +12,53 @@ #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" -using namespace clang::clangd; -using namespace clang; +namespace clang { +namespace clangd { + +static void addExtraFlags(tooling::CompileCommand &Command, + const std::vector<std::string> &ExtraFlags) { + if (ExtraFlags.empty()) + return; + assert(Command.CommandLine.size() >= 2 && + "Expected a command line containing at least 2 arguments, the " + "compiler binary and the output file"); + // The last argument of CommandLine is the name of the input file. + // Add ExtraFlags before it. + auto It = Command.CommandLine.end(); + --It; + Command.CommandLine.insert(It, ExtraFlags.begin(), ExtraFlags.end()); +} + +tooling::CompileCommand getDefaultCompileCommand(PathRef File) { + std::vector<std::string> CommandLine{"clang", "-fsyntax-only", File.str()}; + return tooling::CompileCommand(llvm::sys::path::parent_path(File), + llvm::sys::path::filename(File), CommandLine, + /*Output=*/""); +} std::vector<tooling::CompileCommand> DirectoryBasedGlobalCompilationDatabase::getCompileCommands(PathRef File) { std::vector<tooling::CompileCommand> Commands; auto CDB = getCompilationDatabase(File); - if (!CDB) - return {}; - return CDB->getCompileCommands(File); + if (CDB) + Commands = CDB->getCompileCommands(File); + if (Commands.empty()) + Commands.push_back(getDefaultCompileCommand(File)); + + auto It = ExtraFlagsForFile.find(File); + if (It != ExtraFlagsForFile.end()) { + // Append the user-specified flags to the compile commands. + for (tooling::CompileCommand &Command : Commands) + addExtraFlags(Command, It->second); + } + + return Commands; +} + +void DirectoryBasedGlobalCompilationDatabase::setExtraFlagsForFile( + PathRef File, std::vector<std::string> ExtraFlags) { + ExtraFlagsForFile[File] = std::move(ExtraFlags); } tooling::CompilationDatabase * @@ -63,3 +99,6 @@ DirectoryBasedGlobalCompilationDatabase: // "\n"); return nullptr; } + +} // 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=307241&r1=307240&r2=307241&view=diff ============================================================================== --- clang-tools-extra/trunk/clangd/GlobalCompilationDatabase.h (original) +++ clang-tools-extra/trunk/clangd/GlobalCompilationDatabase.h Thu Jul 6 01:44:54 2017 @@ -25,6 +25,9 @@ struct CompileCommand; namespace clangd { +/// Returns a default compile command to use for \p File. +tooling::CompileCommand getDefaultCompileCommand(PathRef File); + /// Provides compilation arguments used for building ClangdUnit. class GlobalCompilationDatabase { public: @@ -45,6 +48,8 @@ public: std::vector<tooling::CompileCommand> getCompileCommands(PathRef File) override; + void setExtraFlagsForFile(PathRef File, std::vector<std::string> ExtraFlags); + private: tooling::CompilationDatabase *getCompilationDatabase(PathRef File); @@ -53,6 +58,9 @@ private: /// directories). llvm::StringMap<std::unique_ptr<clang::tooling::CompilationDatabase>> CompilationDatabases; + + /// Stores extra flags per file. + llvm::StringMap<std::vector<std::string>> ExtraFlagsForFile; }; } // 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=307241&r1=307240&r2=307241&view=diff ============================================================================== --- clang-tools-extra/trunk/clangd/Protocol.cpp (original) +++ clang-tools-extra/trunk/clangd/Protocol.cpp Thu Jul 6 01:44:54 2017 @@ -204,6 +204,33 @@ TextDocumentItem::parse(llvm::yaml::Mapp return Result; } +llvm::Optional<Metadata> Metadata::parse(llvm::yaml::MappingNode *Params) { + Metadata Result; + for (auto &NextKeyValue : *Params) { + auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey()); + if (!KeyString) + return llvm::None; + + llvm::SmallString<10> KeyStorage; + StringRef KeyValue = KeyString->getValue(KeyStorage); + auto *Value = NextKeyValue.getValue(); + + llvm::SmallString<10> Storage; + if (KeyValue == "extraFlags") { + auto *Seq = dyn_cast<llvm::yaml::SequenceNode>(Value); + if (!Seq) + return llvm::None; + for (auto &Item : *Seq) { + auto *Node = dyn_cast<llvm::yaml::ScalarNode>(&Item); + if (!Node) + return llvm::None; + Result.extraFlags.push_back(Node->getValue(Storage)); + } + } + } + return Result; +} + llvm::Optional<TextEdit> TextEdit::parse(llvm::yaml::MappingNode *Params) { TextEdit Result; for (auto &NextKeyValue : *Params) { @@ -265,6 +292,11 @@ DidOpenTextDocumentParams::parse(llvm::y if (!Parsed) return llvm::None; Result.textDocument = std::move(*Parsed); + } else if (KeyValue == "metadata") { + auto Parsed = Metadata::parse(Value); + if (!Parsed) + return llvm::None; + Result.metadata = std::move(*Parsed); } else { return llvm::None; } Modified: clang-tools-extra/trunk/clangd/Protocol.h URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Protocol.h?rev=307241&r1=307240&r2=307241&view=diff ============================================================================== --- clang-tools-extra/trunk/clangd/Protocol.h (original) +++ clang-tools-extra/trunk/clangd/Protocol.h Thu Jul 6 01:44:54 2017 @@ -118,6 +118,12 @@ struct Location { static std::string unparse(const Location &P); }; +struct Metadata { + std::vector<std::string> extraFlags; + + static llvm::Optional<Metadata> parse(llvm::yaml::MappingNode *Params); +}; + struct TextEdit { /// The range of the text document to be manipulated. To insert /// text into a document create a range where start === end. @@ -152,6 +158,9 @@ struct DidOpenTextDocumentParams { /// The document that was opened. TextDocumentItem textDocument; + /// Extension storing per-file metadata, such as compilation flags. + llvm::Optional<Metadata> metadata; + static llvm::Optional<DidOpenTextDocumentParams> parse(llvm::yaml::MappingNode *Params); }; Added: clang-tools-extra/trunk/test/clangd/extra-flags.test URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/extra-flags.test?rev=307241&view=auto ============================================================================== --- clang-tools-extra/trunk/test/clangd/extra-flags.test (added) +++ clang-tools-extra/trunk/test/clangd/extra-flags.test Thu Jul 6 01:44:54 2017 @@ -0,0 +1,22 @@ +# 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: 205 + +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///foo.c","languageId":"c","version":1,"text":"int main() { int i; return i; }"},"metadata":{"extraFlags":["-Wall"]}}} +# CHECK: {"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"file:///foo.c","diagnostics":[{"range":{"start": {"line": 0, "character": 28}, "end": {"line": 0, "character": 28}},"severity":2,"message":"variable 'i' is uninitialized when used here"},{"range":{"start": {"line": 0, "character": 19}, "end": {"line": 0, "character": 19}},"severity":3,"message":"initialize the variable 'i' to silence this warning"}]}} +# +Content-Length: 175 + +{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///foo.c","version":2},"contentChanges":[{"text":"int main() { int i; return i; }"}]}} +# CHECK: {"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"file:///foo.c","diagnostics":[{"range":{"start": {"line": 0, "character": 28}, "end": {"line": 0, "character": 28}},"severity":2,"message":"variable 'i' is uninitialized when used here"},{"range":{"start": {"line": 0, "character": 19}, "end": {"line": 0, "character": 19}},"severity":3,"message":"initialize the variable 'i' to silence this warning"}]}} +# +Content-Length: 44 + +{"jsonrpc":"2.0","id":5,"method":"shutdown"} + + _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits