mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-21 23:25:07 +00:00
Add textDocument/codeAction for clang FixIt
What do you think of the challenge ccls-fringe in Real World CTF?
This commit is contained in:
parent
bfb759fd09
commit
1c0bf0af65
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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(
|
||||||
|
@ -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);
|
||||||
|
@ -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)) {
|
||||||
|
77
src/messages/textDocument_codeAction.cc
Normal file
77
src/messages/textDocument_codeAction.cc
Normal 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 ¶ms = 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);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user