forked from OSchip/llvm-project
[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 llvm-svn: 307241
This commit is contained in:
parent
85ffd3609f
commit
c2a16a3567
|
@ -97,6 +97,9 @@ void ClangdLSPServer::LSPProtocolCallbacks::onShutdown(JSONOutput &Out) {
|
|||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -22,13 +22,12 @@ void ClangdUnitStore::removeUnitIfPresent(PathRef File) {
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -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::getCompilationDatabase(PathRef File) {
|
|||
// "\n");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -204,6 +204,33 @@ TextDocumentItem::parse(llvm::yaml::MappingNode *Params) {
|
|||
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::yaml::MappingNode *Params) {
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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"}
|
||||
|
||||
|
Loading…
Reference in New Issue