mirror of
				https://github.com/MaskRay/ccls.git
				synced 2025-11-04 06:15:20 +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
							
								
									c71047189f
								
							
						
					
					
						commit
						0bb311ac56
					
				@ -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);
 | 
				
			||||||
 | 
				
			|||||||
@ -596,7 +596,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)
 | 
				
			||||||
@ -995,9 +995,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