Add textDocument/codeAction for clang FixIt

What do you think of the challenge ccls-fringe in Real World CTF?
This commit is contained in:
Fangrui Song 2018-07-28 21:32:41 -07:00
parent bfb759fd09
commit 1c0bf0af65
6 changed files with 103 additions and 17 deletions

View File

@ -214,6 +214,7 @@ target_sources(ccls PRIVATE
src/messages/exit.cc src/messages/exit.cc
src/messages/initialize.cc src/messages/initialize.cc
src/messages/shutdown.cc src/messages/shutdown.cc
src/messages/textDocument_codeAction.cc
src/messages/textDocument_codeLens.cc src/messages/textDocument_codeLens.cc
src/messages/textDocument_completion.cc src/messages/textDocument_completion.cc
src/messages/textDocument_definition.cc src/messages/textDocument_definition.cc

View File

@ -368,7 +368,8 @@ void TryEnsureDocumentParsed(ClangCompleteManager *manager,
WorkingFiles::Snapshot snapshot = session->working_files->AsSnapshot( WorkingFiles::Snapshot snapshot = session->working_files->AsSnapshot(
{StripFileType(session->file.filename)}); {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, *tu = ClangTranslationUnit::Create(session->file.filename, args, snapshot,
diagnostic); diagnostic);
} }
@ -541,7 +542,7 @@ void DiagnosticQueryMain(ClangCompleteManager *manager) {
for (const FixItHint &FixIt : I->getFixIts()) { for (const FixItHint &FixIt : I->getFixIts()) {
lsTextEdit edit; lsTextEdit edit;
edit.newText = FixIt.CodeToInsert; edit.newText = FixIt.CodeToInsert;
r = FromCharRange(SM, LangOpts, FixIt.RemoveRange.getAsRange()); r = FromCharSourceRange(SM, LangOpts, FixIt.RemoveRange);
edit.range = edit.range =
lsRange{{r.start.line, r.start.column}, {r.end.line, r.end.column}}; lsRange{{r.start.line, r.start.column}, {r.end.line, r.end.column}};
ls_diag.fixits_.push_back(edit); ls_diag.fixits_.push_back(edit);

View File

@ -12,13 +12,13 @@ using namespace clang;
#include <assert.h> #include <assert.h>
#include <mutex> #include <mutex>
Range FromSourceRange(const SourceManager &SM, const LangOptions &LangOpts, Range FromCharSourceRange(const SourceManager &SM, const LangOptions &LangOpts,
SourceRange R, llvm::sys::fs::UniqueID *UniqueID, CharSourceRange R,
bool token) { llvm::sys::fs::UniqueID *UniqueID) {
SourceLocation BLoc = R.getBegin(), ELoc = R.getEnd(); SourceLocation BLoc = R.getBegin(), ELoc = R.getEnd();
std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(BLoc); std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(BLoc);
std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(ELoc); std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(ELoc);
if (token) if (R.isTokenRange())
EInfo.second += Lexer::MeasureTokenLength(ELoc, SM, LangOpts); EInfo.second += Lexer::MeasureTokenLength(ELoc, SM, LangOpts);
unsigned l0 = SM.getLineNumber(BInfo.first, BInfo.second) - 1, unsigned l0 = SM.getLineNumber(BInfo.first, BInfo.second) - 1,
c0 = SM.getColumnNumber(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, Range FromCharRange(const SourceManager &SM, const LangOptions &LangOpts,
SourceRange R, SourceRange R, llvm::sys::fs::UniqueID *UniqueID) {
llvm::sys::fs::UniqueID *UniqueID) { return FromCharSourceRange(SM, LangOpts, CharSourceRange::getCharRange(R),
return FromSourceRange(SM, LangOpts, R, UniqueID, false); UniqueID);
} }
Range FromTokenRange(const SourceManager &SM, const LangOptions &LangOpts, Range FromTokenRange(const SourceManager &SM, const LangOptions &LangOpts,
SourceRange R, SourceRange R, llvm::sys::fs::UniqueID *UniqueID) {
llvm::sys::fs::UniqueID *UniqueID) { return FromCharSourceRange(SM, LangOpts, CharSourceRange::getTokenRange(R),
return FromSourceRange(SM, LangOpts, R, UniqueID, true); UniqueID);
} }
std::vector<ASTUnit::RemappedFile> std::vector<ASTUnit::RemappedFile>
@ -70,8 +70,9 @@ std::unique_ptr<ClangTranslationUnit> ClangTranslationUnit::Create(
std::vector<const char *> Args; std::vector<const char *> Args;
for (auto& arg : args) for (auto& arg : args)
Args.push_back(arg.c_str()); Args.push_back(arg.c_str());
Args.push_back("-fno-spell-checking");
Args.push_back("-fallow-editor-placeholders"); Args.push_back("-fallow-editor-placeholders");
if (!diagnostic)
Args.push_back("-fno-spell-checking");
auto ret = std::make_unique<ClangTranslationUnit>(); auto ret = std::make_unique<ClangTranslationUnit>();
IntrusiveRefCntPtr<DiagnosticsEngine> Diags( IntrusiveRefCntPtr<DiagnosticsEngine> Diags(

View File

@ -14,6 +14,11 @@
std::vector<clang::ASTUnit::RemappedFile> std::vector<clang::ASTUnit::RemappedFile>
GetRemapped(const WorkingFiles::Snapshot &snapshot); 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, Range FromCharRange(const clang::SourceManager &SM, const clang::LangOptions &LangOpts,
clang::SourceRange R, clang::SourceRange R,
llvm::sys::fs::UniqueID *UniqueID = nullptr); llvm::sys::fs::UniqueID *UniqueID = nullptr);

View File

@ -593,7 +593,7 @@ public:
auto R = SM.isMacroArgExpansion(Loc) ? CharSourceRange::getTokenRange(Spell) auto R = SM.isMacroArgExpansion(Loc) ? CharSourceRange::getTokenRange(Spell)
: SM.getExpansionRange(Loc); : SM.getExpansionRange(Loc);
#endif #endif
loc = FromTokenRange(SM, Lang, R.getAsRange()); loc = FromCharSourceRange(SM, Lang, R);
LocFID = SM.getFileID(R.getBegin()); LocFID = SM.getFileID(R.getBegin());
FE = SM.getFileEntryForID(LocFID); FE = SM.getFileEntryForID(LocFID);
if (!FE) if (!FE)
@ -992,9 +992,10 @@ public:
if (!File) if (!File)
return; return;
llvm::sys::fs::UniqueID UniqueID; llvm::sys::fs::UniqueID UniqueID;
SourceRange R = FilenameRange.getAsRange(); auto spell = FromCharSourceRange(SM, param.Ctx->getLangOpts(),
auto spell = FromCharRange(SM, param.Ctx->getLangOpts(), R, &UniqueID); FilenameRange, &UniqueID);
const FileEntry *FE = SM.getFileEntryForID(SM.getFileID(R.getBegin())); const FileEntry *FE =
SM.getFileEntryForID(SM.getFileID(FilenameRange.getBegin()));
if (!FE) if (!FE)
return; return;
if (IndexFile *db = param.ConsumeFile(*FE)) { if (IndexFile *db = param.ConsumeFile(*FE)) {

View File

@ -0,0 +1,77 @@
#include "message_handler.h"
#include "pipeline.hh"
#include "working_files.h"
using namespace ccls;
struct CommandArgs {
lsDocumentUri textDocumentUri;
std::vector<lsTextEdit> 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<lsDiagnostic> 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<Out_TextDocumentCodeAction> {
lsRequestId id;
std::vector<lsCommand<CommandArgs>> result;
};
MAKE_REFLECT_STRUCT(Out_TextDocumentCodeAction, jsonrpc, id, result);
struct Handler_TextDocumentCodeAction
: BaseMessageHandler<In_TextDocumentCodeAction> {
MethodType GetMethodType() const override { return kMethodType; }
void Run(In_TextDocumentCodeAction* request) override {
const auto &params = request->params;
WorkingFile *wfile =
working_files->GetFileByFilename(params.textDocument.uri.GetPath());
if (!wfile)
return;
Out_TextDocumentCodeAction out;
out.id = request->id;
std::vector<lsDiagnostic> diagnostics;
working_files->DoAction([&]() { diagnostics = wfile->diagnostics_; });
for (lsDiagnostic &diag : diagnostics)
if (diag.fixits_.size()) {
lsCommand<CommandArgs> 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);
}