From 1c0bf0af655491251e56e78bb0a00aaaed3e8d0e Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Sat, 28 Jul 2018 21:32:41 -0700 Subject: [PATCH] Add textDocument/codeAction for clang FixIt What do you think of the challenge ccls-fringe in Real World CTF? --- CMakeLists.txt | 1 + src/clang_complete.cc | 5 +- src/clang_tu.cc | 23 ++++---- src/clang_tu.h | 5 ++ src/indexer.cc | 9 +-- src/messages/textDocument_codeAction.cc | 77 +++++++++++++++++++++++++ 6 files changed, 103 insertions(+), 17 deletions(-) create mode 100644 src/messages/textDocument_codeAction.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 19af412f..776fa8b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -214,6 +214,7 @@ target_sources(ccls PRIVATE src/messages/exit.cc src/messages/initialize.cc src/messages/shutdown.cc + src/messages/textDocument_codeAction.cc src/messages/textDocument_codeLens.cc src/messages/textDocument_completion.cc src/messages/textDocument_definition.cc diff --git a/src/clang_complete.cc b/src/clang_complete.cc index 15656eb2..2f1a019b 100644 --- a/src/clang_complete.cc +++ b/src/clang_complete.cc @@ -368,7 +368,8 @@ void TryEnsureDocumentParsed(ClangCompleteManager *manager, WorkingFiles::Snapshot snapshot = session->working_files->AsSnapshot( {StripFileType(session->file.filename)}); - LOG_S(INFO) << "create completion session for " << session->file.filename; + LOG_S(INFO) << "create " << (diagnostic ? "diagnostic" : "completion") + << " TU for " << session->file.filename; *tu = ClangTranslationUnit::Create(session->file.filename, args, snapshot, diagnostic); } @@ -541,7 +542,7 @@ void DiagnosticQueryMain(ClangCompleteManager *manager) { for (const FixItHint &FixIt : I->getFixIts()) { lsTextEdit edit; edit.newText = FixIt.CodeToInsert; - r = FromCharRange(SM, LangOpts, FixIt.RemoveRange.getAsRange()); + r = FromCharSourceRange(SM, LangOpts, FixIt.RemoveRange); edit.range = lsRange{{r.start.line, r.start.column}, {r.end.line, r.end.column}}; ls_diag.fixits_.push_back(edit); diff --git a/src/clang_tu.cc b/src/clang_tu.cc index bc40e404..ab43e81e 100644 --- a/src/clang_tu.cc +++ b/src/clang_tu.cc @@ -12,13 +12,13 @@ using namespace clang; #include #include -Range FromSourceRange(const SourceManager &SM, const LangOptions &LangOpts, - SourceRange R, llvm::sys::fs::UniqueID *UniqueID, - bool token) { +Range FromCharSourceRange(const SourceManager &SM, const LangOptions &LangOpts, + CharSourceRange R, + llvm::sys::fs::UniqueID *UniqueID) { SourceLocation BLoc = R.getBegin(), ELoc = R.getEnd(); std::pair BInfo = SM.getDecomposedLoc(BLoc); std::pair EInfo = SM.getDecomposedLoc(ELoc); - if (token) + if (R.isTokenRange()) EInfo.second += Lexer::MeasureTokenLength(ELoc, SM, LangOpts); unsigned l0 = SM.getLineNumber(BInfo.first, BInfo.second) - 1, c0 = SM.getColumnNumber(BInfo.first, BInfo.second) - 1, @@ -42,15 +42,15 @@ Range FromSourceRange(const SourceManager &SM, const LangOptions &LangOpts, } Range FromCharRange(const SourceManager &SM, const LangOptions &LangOpts, - SourceRange R, - llvm::sys::fs::UniqueID *UniqueID) { - return FromSourceRange(SM, LangOpts, R, UniqueID, false); + SourceRange R, llvm::sys::fs::UniqueID *UniqueID) { + return FromCharSourceRange(SM, LangOpts, CharSourceRange::getCharRange(R), + UniqueID); } Range FromTokenRange(const SourceManager &SM, const LangOptions &LangOpts, - SourceRange R, - llvm::sys::fs::UniqueID *UniqueID) { - return FromSourceRange(SM, LangOpts, R, UniqueID, true); + SourceRange R, llvm::sys::fs::UniqueID *UniqueID) { + return FromCharSourceRange(SM, LangOpts, CharSourceRange::getTokenRange(R), + UniqueID); } std::vector @@ -70,8 +70,9 @@ std::unique_ptr ClangTranslationUnit::Create( std::vector Args; for (auto& arg : args) Args.push_back(arg.c_str()); - Args.push_back("-fno-spell-checking"); Args.push_back("-fallow-editor-placeholders"); + if (!diagnostic) + Args.push_back("-fno-spell-checking"); auto ret = std::make_unique(); IntrusiveRefCntPtr Diags( diff --git a/src/clang_tu.h b/src/clang_tu.h index a20ad74e..c9253878 100644 --- a/src/clang_tu.h +++ b/src/clang_tu.h @@ -14,6 +14,11 @@ std::vector GetRemapped(const WorkingFiles::Snapshot &snapshot); +Range FromCharSourceRange(const clang::SourceManager &SM, + const clang::LangOptions &LangOpts, + clang::CharSourceRange R, + llvm::sys::fs::UniqueID *UniqueID = nullptr); + Range FromCharRange(const clang::SourceManager &SM, const clang::LangOptions &LangOpts, clang::SourceRange R, llvm::sys::fs::UniqueID *UniqueID = nullptr); diff --git a/src/indexer.cc b/src/indexer.cc index efc14d2c..4853adb5 100644 --- a/src/indexer.cc +++ b/src/indexer.cc @@ -593,7 +593,7 @@ public: auto R = SM.isMacroArgExpansion(Loc) ? CharSourceRange::getTokenRange(Spell) : SM.getExpansionRange(Loc); #endif - loc = FromTokenRange(SM, Lang, R.getAsRange()); + loc = FromCharSourceRange(SM, Lang, R); LocFID = SM.getFileID(R.getBegin()); FE = SM.getFileEntryForID(LocFID); if (!FE) @@ -992,9 +992,10 @@ public: if (!File) return; llvm::sys::fs::UniqueID UniqueID; - SourceRange R = FilenameRange.getAsRange(); - auto spell = FromCharRange(SM, param.Ctx->getLangOpts(), R, &UniqueID); - const FileEntry *FE = SM.getFileEntryForID(SM.getFileID(R.getBegin())); + auto spell = FromCharSourceRange(SM, param.Ctx->getLangOpts(), + FilenameRange, &UniqueID); + const FileEntry *FE = + SM.getFileEntryForID(SM.getFileID(FilenameRange.getBegin())); if (!FE) return; if (IndexFile *db = param.ConsumeFile(*FE)) { diff --git a/src/messages/textDocument_codeAction.cc b/src/messages/textDocument_codeAction.cc new file mode 100644 index 00000000..2832cfca --- /dev/null +++ b/src/messages/textDocument_codeAction.cc @@ -0,0 +1,77 @@ +#include "message_handler.h" +#include "pipeline.hh" +#include "working_files.h" +using namespace ccls; + +struct CommandArgs { + lsDocumentUri textDocumentUri; + std::vector edits; +}; +MAKE_REFLECT_STRUCT_WRITER_AS_ARRAY(CommandArgs, textDocumentUri, edits); + +namespace { +MethodType kMethodType = "textDocument/codeAction"; + +struct In_TextDocumentCodeAction : public RequestInMessage { + MethodType GetMethodType() const override { return kMethodType; } + + // Contains additional diagnostic information about the context in which + // a code action is run. + struct lsCodeActionContext { + // An array of diagnostics. + std::vector diagnostics; + }; + // Params for the CodeActionRequest + struct lsCodeActionParams { + // The document in which the command was invoked. + lsTextDocumentIdentifier textDocument; + // The range for which the command was invoked. + lsRange range; + // Context carrying additional information. + lsCodeActionContext context; + } params; +}; +MAKE_REFLECT_STRUCT(In_TextDocumentCodeAction::lsCodeActionContext, + diagnostics); +MAKE_REFLECT_STRUCT(In_TextDocumentCodeAction::lsCodeActionParams, + textDocument, + range, + context); +MAKE_REFLECT_STRUCT(In_TextDocumentCodeAction, id, params); +REGISTER_IN_MESSAGE(In_TextDocumentCodeAction); + +struct Out_TextDocumentCodeAction + : public lsOutMessage { + lsRequestId id; + std::vector> result; +}; +MAKE_REFLECT_STRUCT(Out_TextDocumentCodeAction, jsonrpc, id, result); + +struct Handler_TextDocumentCodeAction + : BaseMessageHandler { + MethodType GetMethodType() const override { return kMethodType; } + + void Run(In_TextDocumentCodeAction* request) override { + const auto ¶ms = request->params; + WorkingFile *wfile = + working_files->GetFileByFilename(params.textDocument.uri.GetPath()); + if (!wfile) + return; + Out_TextDocumentCodeAction out; + out.id = request->id; + std::vector diagnostics; + working_files->DoAction([&]() { diagnostics = wfile->diagnostics_; }); + for (lsDiagnostic &diag : diagnostics) + if (diag.fixits_.size()) { + lsCommand command; + command.title = "FixIt: " + diag.message; + command.command = "ccls._applyFixIt"; + command.arguments.textDocumentUri = params.textDocument.uri; + command.arguments.edits = diag.fixits_; + out.result.push_back(command); + } + pipeline::WriteStdout(kMethodType, out); + } +}; +REGISTER_MESSAGE_HANDLER(Handler_TextDocumentCodeAction); +}