[clang-tools-extra] r296636 - [clangd] Add support for FixIts.
Benjamin Kramer via cfe-commits
cfe-commits at lists.llvm.org
Wed Mar 1 08:16:29 PST 2017
Author: d0k
Date: Wed Mar 1 10:16:29 2017
New Revision: 296636
URL: http://llvm.org/viewvc/llvm-project?rev=296636&view=rev
Log:
[clangd] Add support for FixIts.
Summary:
This uses CodeActions to show 'apply fix' actions when code actions are
requested for a location. The actions themselves make use of a
clangd.applyFix command which has to be implemented on the editor side. I
included an implementation for vscode.
This also adds a -run-synchronously flag which runs everything on the main
thread. This is useful for testing.
Reviewers: krasimir
Subscribers: cfe-commits
Differential Revision: https://reviews.llvm.org/D30498
Added:
clang-tools-extra/trunk/test/clangd/fixits.test
Modified:
clang-tools-extra/trunk/clangd/ASTManager.cpp
clang-tools-extra/trunk/clangd/ASTManager.h
clang-tools-extra/trunk/clangd/ClangDMain.cpp
clang-tools-extra/trunk/clangd/Protocol.cpp
clang-tools-extra/trunk/clangd/Protocol.h
clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp
clang-tools-extra/trunk/clangd/ProtocolHandlers.h
clang-tools-extra/trunk/clangd/clients/clangd-vscode/src/extension.ts
clang-tools-extra/trunk/test/clangd/formatting.test
Modified: clang-tools-extra/trunk/clangd/ASTManager.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ASTManager.cpp?rev=296636&r1=296635&r2=296636&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ASTManager.cpp (original)
+++ clang-tools-extra/trunk/clangd/ASTManager.cpp Wed Mar 1 10:16:29 2017
@@ -54,8 +54,9 @@ static int getSeverity(DiagnosticsEngine
llvm_unreachable("Unknown diagnostic level!");
}
-ASTManager::ASTManager(JSONOutput &Output, DocumentStore &Store)
- : Output(Output), Store(Store),
+ASTManager::ASTManager(JSONOutput &Output, DocumentStore &Store,
+ bool RunSynchronously)
+ : Output(Output), Store(Store), RunSynchronously(RunSynchronously),
PCHs(std::make_shared<PCHContainerOperations>()),
ClangWorker([this]() { runWorker(); }) {}
@@ -67,9 +68,8 @@ void ASTManager::runWorker() {
std::unique_lock<std::mutex> Lock(RequestLock);
// Check if there's another request pending. We keep parsing until
// our one-element queue is empty.
- ClangRequestCV.wait(Lock, [this] {
- return !RequestQueue.empty() || Done;
- });
+ ClangRequestCV.wait(Lock,
+ [this] { return !RequestQueue.empty() || Done; });
if (RequestQueue.empty() && Done)
return;
@@ -78,49 +78,67 @@ void ASTManager::runWorker() {
RequestQueue.pop_back();
} // unlock.
- auto &Unit = ASTs[File]; // Only one thread can access this at a time.
+ parseFileAndPublishDiagnostics(File);
+ }
+}
- if (!Unit) {
- Unit = createASTUnitForFile(File, this->Store);
- } else {
- // Do a reparse if this wasn't the first parse.
- // FIXME: This might have the wrong working directory if it changed in the
- // meantime.
- Unit->Reparse(PCHs, getRemappedFiles(this->Store));
- }
+void ASTManager::parseFileAndPublishDiagnostics(StringRef File) {
+ auto &Unit = ASTs[File]; // Only one thread can access this at a time.
- if (!Unit)
- continue;
+ if (!Unit) {
+ Unit = createASTUnitForFile(File, this->Store);
+ } else {
+ // Do a reparse if this wasn't the first parse.
+ // FIXME: This might have the wrong working directory if it changed in the
+ // meantime.
+ Unit->Reparse(PCHs, getRemappedFiles(this->Store));
+ }
+
+ if (!Unit)
+ return;
- // Send the diagnotics to the editor.
- // FIXME: If the diagnostic comes from a different file, do we want to
- // show them all? Right now we drop everything not coming from the
- // main file.
- // FIXME: Send FixIts to the editor.
- std::string Diagnostics;
- for (ASTUnit::stored_diag_iterator D = Unit->stored_diag_begin(),
- DEnd = Unit->stored_diag_end();
- D != DEnd; ++D) {
- if (!D->getLocation().isValid() ||
- !D->getLocation().getManager().isInMainFile(D->getLocation()))
- continue;
- Position P;
- P.line = D->getLocation().getSpellingLineNumber() - 1;
- P.character = D->getLocation().getSpellingColumnNumber();
- Range R = {P, P};
- Diagnostics +=
- R"({"range":)" + Range::unparse(R) +
- R"(,"severity":)" + std::to_string(getSeverity(D->getLevel())) +
- R"(,"message":")" + llvm::yaml::escape(D->getMessage()) +
- R"("},)";
+ // Send the diagnotics to the editor.
+ // FIXME: If the diagnostic comes from a different file, do we want to
+ // show them all? Right now we drop everything not coming from the
+ // main file.
+ std::string Diagnostics;
+ DiagnosticToReplacementMap LocalFixIts; // Temporary storage
+ for (ASTUnit::stored_diag_iterator D = Unit->stored_diag_begin(),
+ DEnd = Unit->stored_diag_end();
+ D != DEnd; ++D) {
+ if (!D->getLocation().isValid() ||
+ !D->getLocation().getManager().isInMainFile(D->getLocation()))
+ continue;
+ Position P;
+ P.line = D->getLocation().getSpellingLineNumber() - 1;
+ P.character = D->getLocation().getSpellingColumnNumber();
+ Range R = {P, P};
+ Diagnostics +=
+ R"({"range":)" + Range::unparse(R) +
+ R"(,"severity":)" + std::to_string(getSeverity(D->getLevel())) +
+ R"(,"message":")" + llvm::yaml::escape(D->getMessage()) +
+ R"("},)";
+
+ // We convert to Replacements to become independent of the SourceManager.
+ clangd::Diagnostic Diag = {R, getSeverity(D->getLevel()), D->getMessage()};
+ auto &FixItsForDiagnostic = LocalFixIts[Diag];
+ for (const FixItHint &Fix : D->getFixIts()) {
+ FixItsForDiagnostic.push_back(clang::tooling::Replacement(
+ Unit->getSourceManager(), Fix.RemoveRange, Fix.CodeToInsert));
}
+ }
- if (!Diagnostics.empty())
- Diagnostics.pop_back(); // Drop trailing comma.
- Output.writeMessage(
- R"({"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":")" +
- File + R"(","diagnostics":[)" + Diagnostics + R"(]}})");
+ // Put FixIts into place.
+ {
+ std::lock_guard<std::mutex> Guard(FixItLock);
+ FixIts = std::move(LocalFixIts);
}
+
+ if (!Diagnostics.empty())
+ Diagnostics.pop_back(); // Drop trailing comma.
+ Output.writeMessage(
+ R"({"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":")" +
+ File + R"(","diagnostics":[)" + Diagnostics + R"(]}})");
}
ASTManager::~ASTManager() {
@@ -134,6 +152,10 @@ ASTManager::~ASTManager() {
}
void ASTManager::onDocumentAdd(StringRef Uri) {
+ if (RunSynchronously) {
+ parseFileAndPublishDiagnostics(Uri);
+ return;
+ }
std::lock_guard<std::mutex> Guard(RequestLock);
// Currently we discard all pending requests and just enqueue the latest one.
RequestQueue.clear();
@@ -201,3 +223,12 @@ ASTManager::createASTUnitForFile(StringR
/*IncludeBriefCommentsInCodeCompletion=*/true,
/*AllowPCHWithCompilerErrors=*/true));
}
+
+llvm::ArrayRef<clang::tooling::Replacement>
+ASTManager::getFixIts(const clangd::Diagnostic &D) {
+ std::lock_guard<std::mutex> Guard(FixItLock);
+ auto I = FixIts.find(D);
+ if (I != FixIts.end())
+ return I->second;
+ return llvm::None;
+}
Modified: clang-tools-extra/trunk/clangd/ASTManager.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ASTManager.h?rev=296636&r1=296635&r2=296636&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ASTManager.h (original)
+++ clang-tools-extra/trunk/clangd/ASTManager.h Wed Mar 1 10:16:29 2017
@@ -12,6 +12,8 @@
#include "DocumentStore.h"
#include "JSONRPCDispatcher.h"
+#include "Protocol.h"
+#include "clang/Tooling/Core/Replacement.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include <condition_variable>
#include <deque>
@@ -29,17 +31,27 @@ namespace clangd {
class ASTManager : public DocumentStoreListener {
public:
- ASTManager(JSONOutput &Output, DocumentStore &Store);
+ ASTManager(JSONOutput &Output, DocumentStore &Store, bool RunSynchronously);
~ASTManager() override;
void onDocumentAdd(StringRef Uri) override;
// FIXME: Implement onDocumentRemove
// FIXME: Implement codeComplete
+ /// Get the fixes associated with a certain diagnostic as replacements.
+ llvm::ArrayRef<clang::tooling::Replacement>
+ getFixIts(const clangd::Diagnostic &D);
+
+ DocumentStore &getStore() const { return Store; }
+
private:
JSONOutput &Output;
DocumentStore &Store;
+ // Set to true if requests should block instead of being processed
+ // asynchronously.
+ bool RunSynchronously;
+
/// Loads a compilation database for URI. May return nullptr if it fails. The
/// database is cached for subsequent accesses.
clang::tooling::CompilationDatabase *
@@ -50,6 +62,7 @@ private:
createASTUnitForFile(StringRef Uri, const DocumentStore &Docs);
void runWorker();
+ void parseFileAndPublishDiagnostics(StringRef File);
/// Clang objects.
llvm::StringMap<std::unique_ptr<clang::ASTUnit>> ASTs;
@@ -57,6 +70,11 @@ private:
CompilationDatabases;
std::shared_ptr<clang::PCHContainerOperations> PCHs;
+ typedef std::map<clangd::Diagnostic, std::vector<clang::tooling::Replacement>>
+ DiagnosticToReplacementMap;
+ DiagnosticToReplacementMap FixIts;
+ std::mutex FixItLock;
+
/// Queue of requests.
std::deque<std::string> RequestQueue;
/// Setting Done to true will make the worker thread terminate.
Modified: clang-tools-extra/trunk/clangd/ClangDMain.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangDMain.cpp?rev=296636&r1=296635&r2=296636&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangDMain.cpp (original)
+++ clang-tools-extra/trunk/clangd/ClangDMain.cpp Wed Mar 1 10:16:29 2017
@@ -11,13 +11,20 @@
#include "DocumentStore.h"
#include "JSONRPCDispatcher.h"
#include "ProtocolHandlers.h"
+#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Program.h"
#include <iostream>
#include <string>
using namespace clang::clangd;
+static llvm::cl::opt<bool>
+ RunSynchronously("run-synchronously",
+ llvm::cl::desc("parse on main thread"),
+ llvm::cl::init(false), llvm::cl::Hidden);
+
int main(int argc, char *argv[]) {
+ llvm::cl::ParseCommandLineOptions(argc, argv, "clangd");
llvm::raw_ostream &Outs = llvm::outs();
llvm::raw_ostream &Logs = llvm::errs();
JSONOutput Out(Outs, Logs);
@@ -28,14 +35,14 @@ int main(int argc, char *argv[]) {
// Set up a document store and intialize all the method handlers for JSONRPC
// dispatching.
DocumentStore Store;
- ASTManager AST(Out, Store);
+ ASTManager AST(Out, Store, RunSynchronously);
Store.addListener(&AST);
JSONRPCDispatcher Dispatcher(llvm::make_unique<Handler>(Out));
Dispatcher.registerHandler("initialize",
llvm::make_unique<InitializeHandler>(Out));
auto ShutdownPtr = llvm::make_unique<ShutdownHandler>(Out);
auto *ShutdownHandler = ShutdownPtr.get();
- Dispatcher.registerHandler("shutdown",std::move(ShutdownPtr));
+ Dispatcher.registerHandler("shutdown", std::move(ShutdownPtr));
Dispatcher.registerHandler(
"textDocument/didOpen",
llvm::make_unique<TextDocumentDidOpenHandler>(Out, Store));
@@ -52,6 +59,8 @@ int main(int argc, char *argv[]) {
Dispatcher.registerHandler(
"textDocument/formatting",
llvm::make_unique<TextDocumentFormattingHandler>(Out, Store));
+ Dispatcher.registerHandler("textDocument/codeAction",
+ llvm::make_unique<CodeActionHandler>(Out, AST));
while (std::cin.good()) {
// A Language Server Protocol message starts with a HTTP header, delimited
Modified: clang-tools-extra/trunk/clangd/Protocol.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Protocol.cpp?rev=296636&r1=296635&r2=296636&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/Protocol.cpp (original)
+++ clang-tools-extra/trunk/clangd/Protocol.cpp Wed Mar 1 10:16:29 2017
@@ -457,3 +457,116 @@ DocumentFormattingParams::parse(llvm::ya
}
return Result;
}
+
+llvm::Optional<Diagnostic> Diagnostic::parse(llvm::yaml::MappingNode *Params) {
+ Diagnostic 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);
+
+ llvm::SmallString<10> Storage;
+ if (KeyValue == "range") {
+ auto *Value =
+ dyn_cast_or_null<llvm::yaml::MappingNode>(NextKeyValue.getValue());
+ if (!Value)
+ return llvm::None;
+ auto Parsed = Range::parse(Value);
+ if (!Parsed)
+ return llvm::None;
+ Result.range = std::move(*Parsed);
+ } else if (KeyValue == "severity") {
+ auto *Value =
+ dyn_cast_or_null<llvm::yaml::ScalarNode>(NextKeyValue.getValue());
+ if (!Value)
+ return llvm::None;
+ long long Val;
+ if (llvm::getAsSignedInteger(Value->getValue(Storage), 0, Val))
+ return llvm::None;
+ Result.severity = Val;
+ } else if (KeyValue == "message") {
+ auto *Value =
+ dyn_cast_or_null<llvm::yaml::ScalarNode>(NextKeyValue.getValue());
+ if (!Value)
+ return llvm::None;
+ Result.message = Value->getValue(Storage);
+ } else {
+ return llvm::None;
+ }
+ }
+ return Result;
+}
+
+llvm::Optional<CodeActionContext>
+CodeActionContext::parse(llvm::yaml::MappingNode *Params) {
+ CodeActionContext 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 == "diagnostics") {
+ auto *Seq = dyn_cast<llvm::yaml::SequenceNode>(Value);
+ if (!Seq)
+ return llvm::None;
+ for (auto &Item : *Seq) {
+ auto *I = dyn_cast<llvm::yaml::MappingNode>(&Item);
+ if (!I)
+ return llvm::None;
+ auto Parsed = Diagnostic::parse(I);
+ if (!Parsed)
+ return llvm::None;
+ Result.diagnostics.push_back(std::move(*Parsed));
+ }
+ } else {
+ return llvm::None;
+ }
+ }
+ return Result;
+}
+
+llvm::Optional<CodeActionParams>
+CodeActionParams::parse(llvm::yaml::MappingNode *Params) {
+ CodeActionParams 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 =
+ dyn_cast_or_null<llvm::yaml::MappingNode>(NextKeyValue.getValue());
+ if (!Value)
+ return llvm::None;
+
+ llvm::SmallString<10> Storage;
+ if (KeyValue == "textDocument") {
+ auto Parsed = TextDocumentIdentifier::parse(Value);
+ if (!Parsed)
+ return llvm::None;
+ Result.textDocument = std::move(*Parsed);
+ } else if (KeyValue == "range") {
+ auto Parsed = Range::parse(Value);
+ if (!Parsed)
+ return llvm::None;
+ Result.range = std::move(*Parsed);
+ } else if (KeyValue == "context") {
+ auto Parsed = CodeActionContext::parse(Value);
+ if (!Parsed)
+ return llvm::None;
+ Result.context = std::move(*Parsed);
+ } else {
+ return llvm::None;
+ }
+ }
+ return Result;
+}
Modified: clang-tools-extra/trunk/clangd/Protocol.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Protocol.h?rev=296636&r1=296635&r2=296636&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/Protocol.h (original)
+++ clang-tools-extra/trunk/clangd/Protocol.h Wed Mar 1 10:16:29 2017
@@ -44,6 +44,15 @@ struct Position {
/// Character offset on a line in a document (zero-based).
int character;
+ friend bool operator==(const Position &LHS, const Position &RHS) {
+ return std::tie(LHS.line, LHS.character) ==
+ std::tie(RHS.line, RHS.character);
+ }
+ friend bool operator<(const Position &LHS, const Position &RHS) {
+ return std::tie(LHS.line, LHS.character) <
+ std::tie(RHS.line, RHS.character);
+ }
+
static llvm::Optional<Position> parse(llvm::yaml::MappingNode *Params);
static std::string unparse(const Position &P);
};
@@ -55,6 +64,13 @@ struct Range {
/// The range's end position.
Position end;
+ friend bool operator==(const Range &LHS, const Range &RHS) {
+ return std::tie(LHS.start, LHS.end) == std::tie(RHS.start, RHS.end);
+ }
+ friend bool operator<(const Range &LHS, const Range &RHS) {
+ return std::tie(LHS.start, LHS.end) < std::tie(RHS.start, RHS.end);
+ }
+
static llvm::Optional<Range> parse(llvm::yaml::MappingNode *Params);
static std::string unparse(const Range &P);
};
@@ -172,6 +188,51 @@ struct DocumentFormattingParams {
parse(llvm::yaml::MappingNode *Params);
};
+struct Diagnostic {
+ /// The range at which the message applies.
+ Range range;
+
+ /// The diagnostic's severity. Can be omitted. If omitted it is up to the
+ /// client to interpret diagnostics as error, warning, info or hint.
+ int severity;
+
+ /// The diagnostic's message.
+ std::string message;
+
+ friend bool operator==(const Diagnostic &LHS, const Diagnostic &RHS) {
+ return std::tie(LHS.range, LHS.severity, LHS.message) ==
+ std::tie(RHS.range, RHS.severity, RHS.message);
+ }
+ friend bool operator<(const Diagnostic &LHS, const Diagnostic &RHS) {
+ return std::tie(LHS.range, LHS.severity, LHS.message) <
+ std::tie(RHS.range, RHS.severity, RHS.message);
+ }
+
+ static llvm::Optional<Diagnostic> parse(llvm::yaml::MappingNode *Params);
+};
+
+struct CodeActionContext {
+ /// An array of diagnostics.
+ std::vector<Diagnostic> diagnostics;
+
+ static llvm::Optional<CodeActionContext>
+ parse(llvm::yaml::MappingNode *Params);
+};
+
+struct CodeActionParams {
+ /// The document in which the command was invoked.
+ TextDocumentIdentifier textDocument;
+
+ /// The range for which the command was invoked.
+ Range range;
+
+ /// Context carrying additional information.
+ CodeActionContext context;
+
+ static llvm::Optional<CodeActionParams>
+ parse(llvm::yaml::MappingNode *Params);
+};
+
} // namespace clangd
} // namespace clang
Modified: clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp?rev=296636&r1=296635&r2=296636&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp (original)
+++ clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp Wed Mar 1 10:16:29 2017
@@ -8,6 +8,7 @@
//===----------------------------------------------------------------------===//
#include "ProtocolHandlers.h"
+#include "ASTManager.h"
#include "DocumentStore.h"
#include "clang/Format/Format.h"
using namespace clang;
@@ -58,18 +59,9 @@ static Position offsetToPosition(StringR
return {Lines, Cols};
}
-static std::string formatCode(StringRef Code, StringRef Filename,
- ArrayRef<tooling::Range> Ranges, StringRef ID) {
- // Call clang-format.
- // FIXME: Don't ignore style.
- format::FormatStyle Style = format::getLLVMStyle();
- // On windows FileManager doesn't like file://. Just strip it, clang-format
- // doesn't need it.
- Filename.consume_front("file://");
- tooling::Replacements Replacements =
- format::reformat(Style, Code, Ranges, Filename);
-
- // Now turn the replacements into the format specified by the Language Server
+template <typename T>
+static std::string replacementsToEdits(StringRef Code, const T &Replacements) {
+ // Turn the replacements into the format specified by the Language Server
// Protocol. Fuse them into one big JSON array.
std::string Edits;
for (auto &R : Replacements) {
@@ -83,6 +75,21 @@ static std::string formatCode(StringRef
if (!Edits.empty())
Edits.pop_back();
+ return Edits;
+}
+
+static std::string formatCode(StringRef Code, StringRef Filename,
+ ArrayRef<tooling::Range> Ranges, StringRef ID) {
+ // Call clang-format.
+ // FIXME: Don't ignore style.
+ format::FormatStyle Style = format::getLLVMStyle();
+ // On windows FileManager doesn't like file://. Just strip it, clang-format
+ // doesn't need it.
+ Filename.consume_front("file://");
+ tooling::Replacements Replacements =
+ format::reformat(Style, Code, Ranges, Filename);
+
+ std::string Edits = replacementsToEdits(Code, Replacements);
return R"({"jsonrpc":"2.0","id":)" + ID.str() +
R"(,"result":[)" + Edits + R"(]})";
}
@@ -138,3 +145,36 @@ void TextDocumentFormattingHandler::hand
writeMessage(formatCode(Code, DFP->textDocument.uri,
{clang::tooling::Range(0, Code.size())}, ID));
}
+
+void CodeActionHandler::handleMethod(llvm::yaml::MappingNode *Params,
+ StringRef ID) {
+ auto CAP = CodeActionParams::parse(Params);
+ if (!CAP) {
+ Output.log("Failed to decode CodeActionParams!\n");
+ return;
+ }
+
+ // We provide a code action for each diagnostic at the requested location
+ // which has FixIts available.
+ std::string Code = AST.getStore().getDocument(CAP->textDocument.uri);
+ std::string Commands;
+ for (Diagnostic &D : CAP->context.diagnostics) {
+ std::vector<clang::tooling::Replacement> Fixes = AST.getFixIts(D);
+ std::string Edits = replacementsToEdits(Code, Fixes);
+
+ if (!Edits.empty())
+ Commands +=
+ R"({"title":"Apply FixIt ')" + llvm::yaml::escape(D.message) +
+ R"('", "command": "clangd.applyFix", "arguments": [")" +
+ llvm::yaml::escape(CAP->textDocument.uri) +
+ R"(", [)" + Edits +
+ R"(]]},)";
+ }
+ if (!Commands.empty())
+ Commands.pop_back();
+
+ writeMessage(
+ R"({"jsonrpc":"2.0","id":)" + ID.str() +
+ R"(, "result": [)" + Commands +
+ R"(]})");
+}
Modified: clang-tools-extra/trunk/clangd/ProtocolHandlers.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ProtocolHandlers.h?rev=296636&r1=296635&r2=296636&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ProtocolHandlers.h (original)
+++ clang-tools-extra/trunk/clangd/ProtocolHandlers.h Wed Mar 1 10:16:29 2017
@@ -22,6 +22,7 @@
namespace clang {
namespace clangd {
+class ASTManager;
class DocumentStore;
struct InitializeHandler : Handler {
@@ -34,7 +35,8 @@ struct InitializeHandler : Handler {
"textDocumentSync": 1,
"documentFormattingProvider": true,
"documentRangeFormattingProvider": true,
- "documentOnTypeFormattingProvider": {"firstTriggerCharacter":"}","moreTriggerCharacter":[]}
+ "documentOnTypeFormattingProvider": {"firstTriggerCharacter":"}","moreTriggerCharacter":[]},
+ "codeActionProvider": true
}}})");
}
};
@@ -102,6 +104,16 @@ private:
DocumentStore &Store;
};
+struct CodeActionHandler : Handler {
+ CodeActionHandler(JSONOutput &Output, ASTManager &AST)
+ : Handler(Output), AST(AST) {}
+
+ void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override;
+
+private:
+ ASTManager &AST;
+};
+
} // namespace clangd
} // namespace clang
Modified: clang-tools-extra/trunk/clangd/clients/clangd-vscode/src/extension.ts
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/clients/clangd-vscode/src/extension.ts?rev=296636&r1=296635&r2=296636&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/clients/clangd-vscode/src/extension.ts (original)
+++ clang-tools-extra/trunk/clangd/clients/clangd-vscode/src/extension.ts Wed Mar 1 10:16:29 2017
@@ -18,9 +18,25 @@ export function activate(context: vscode
const clangdClient = new vscodelc.LanguageClient('Clang Language Server', serverOptions, clientOptions);
+ function applyTextEdits(uri: string, edits: vscodelc.TextEdit[]) {
+ let textEditor = vscode.window.activeTextEditor;
+
+ if (textEditor && textEditor.document.uri.toString() === uri) {
+ textEditor.edit(mutator => {
+ for (const edit of edits) {
+ mutator.replace(vscodelc.Protocol2Code.asRange(edit.range), edit.newText);
+ }
+ }).then((success) => {
+ if (!success) {
+ vscode.window.showErrorMessage('Failed to apply fixes to the document.');
+ }
+ });
+ }
+ }
+
console.log('Clang Language Server is now active!');
const disposable = clangdClient.start();
- context.subscriptions.push(disposable);
+ context.subscriptions.push(disposable, vscode.commands.registerCommand('clangd.applyFix', applyTextEdits));
}
\ No newline at end of file
Added: clang-tools-extra/trunk/test/clangd/fixits.test
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/fixits.test?rev=296636&view=auto
==============================================================================
--- clang-tools-extra/trunk/test/clangd/fixits.test (added)
+++ clang-tools-extra/trunk/test/clangd/fixits.test Wed Mar 1 10:16:29 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: 180
+
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///foo.c","languageId":"c","version":1,"text":"int main(int i, char **a) { if (i = 2) {}}"}}}
+#
+# CHECK: {"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"file:///foo.c","diagnostics":[{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":2,"message":"using the result of an assignment as a condition without parentheses"},{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":3,"message":"place parentheses around the assignment to silence this warning"},{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":3,"message":"use '==' to turn this assignment into an equality comparison"}]}}
+#
+Content-Length: 746
+
+ {"jsonrpc":"2.0","id":2,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"file:///foo.c"},"range":{"start":{"line":104,"character":13},"end":{"line":0,"character":35}},"context":{"diagnostics":[{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":2,"message":"using the result of an assignment as a condition without parentheses"},{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":3,"message":"place parentheses around the assignment to silence this warning"},{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":3,"message":"use '==' to turn this assignment into an equality comparison"}]}}}
+#
+# CHECK: {"jsonrpc":"2.0","id":2, "result": [{"title":"Apply FixIt 'place parentheses around the assignment to silence this warning'", "command": "clangd.applyFix", "arguments": ["file:///foo.c", [{"range": {"start": {"line": 0, "character": 32}, "end": {"line": 0, "character": 32}}, "newText": "("},{"range": {"start": {"line": 0, "character": 37}, "end": {"line": 0, "character": 37}}, "newText": ")"}]]},{"title":"Apply FixIt 'use '==' to turn this assignment into an equality comparison'", "command": "clangd.applyFix", "arguments": ["file:///foo.c", [{"range": {"start": {"line": 0, "character": 34}, "end": {"line": 0, "character": 35}}, "newText": "=="}]]}]
+#
+Content-Length: 44
+
+{"jsonrpc":"2.0","id":3,"method":"shutdown"}
Modified: clang-tools-extra/trunk/test/clangd/formatting.test
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/formatting.test?rev=296636&r1=296635&r2=296636&view=diff
==============================================================================
--- clang-tools-extra/trunk/test/clangd/formatting.test (original)
+++ clang-tools-extra/trunk/test/clangd/formatting.test Wed Mar 1 10:16:29 2017
@@ -4,12 +4,13 @@
Content-Length: 125
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
-# CHECK: Content-Length: 294
+# CHECK: Content-Length: 332
# CHECK: {"jsonrpc":"2.0","id":0,"result":{"capabilities":{
# CHECK: "textDocumentSync": 1,
# CHECK: "documentFormattingProvider": true,
# CHECK: "documentRangeFormattingProvider": true,
-# CHECK: "documentOnTypeFormattingProvider": {"firstTriggerCharacter":"}","moreTriggerCharacter":[]}
+# CHECK: "documentOnTypeFormattingProvider": {"firstTriggerCharacter":"}","moreTriggerCharacter":[]},
+# CHECK: "codeActionProvider": true
# CHECK: }}}
#
Content-Length: 193
More information about the cfe-commits
mailing list