hokein updated this revision to Diff 212347.
hokein added a comment.
update the diff
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D65263/new/
https://reviews.llvm.org/D65263
Files:
clang-tools-extra/clangd/ClangdLSPServer.cpp
clang-tools-extra/clangd/clients/clangd-vscode/src/extension.ts
clang-tools-extra/clangd/refactor/Tweak.h
clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp
Index: clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp
===================================================================
--- clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp
+++ clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp
@@ -365,7 +365,43 @@
// replace expression with variable name
if (auto Err = Result.add(Target->replaceWithVar(Range, VarName)))
return std::move(Err);
- return Effect::applyEdit(Result);
+ // TODO: refine this.
+ // We do the format internally in order to keep the position of the extracted
+ // variable not being changed from the caller.
+ auto &SM = Inputs.AST.getASTContext().getSourceManager();
+ auto *FS = &SM.getFileManager().getVirtualFileSystem();
+ auto Style = getFormatStyleForFile(
+ SM.getFileEntryForID(SM.getMainFileID())->tryGetRealPathName(),
+ Inputs.Code, FS);
+ auto Formatted = cleanupAndFormat(Inputs.Code, Result, Style);
+ if (!Formatted)
+ return Formatted.takeError();
+ Result = *Formatted;
+ auto NewCode = tooling::applyAllReplacements(Inputs.Code, Result);
+ if (!NewCode)
+ return NewCode.takeError();
+
+ assert(!Result.empty());
+ // Calculate the offset of the dummy variable after applying replacements.
+ size_t ExtractVarOffset = Result.begin()->getOffset();
+ for (auto &R : Result) {
+ auto OffsetInReplacement = R.getReplacementText().find(VarName);
+ if (OffsetInReplacement != llvm::StringRef::npos) {
+ ExtractVarOffset += OffsetInReplacement;
+ break;
+ }
+ ExtractVarOffset += R.getReplacementText().size() - R.getLength();
+ }
+
+ assert(ExtractVarOffset != llvm::StringRef::npos);
+ Step Apply;
+ Apply.ApplyKind = Step::ApplyEdit;
+ Apply.ApplyEditParams = std::move(Result);
+ Step Rename;
+ Rename.ApplyKind = Step::Rename;
+ Rename.RenameParams = offsetToPosition(*NewCode, ExtractVarOffset);
+ // TODO: We should not emit Rename if the client doesn't support it.
+ return Effect::applySteps({std::move(Apply), std::move(Rename)});
}
// Find the CallExpr whose callee is an ancestor of the DeclRef
Index: clang-tools-extra/clangd/refactor/Tweak.h
===================================================================
--- clang-tools-extra/clangd/refactor/Tweak.h
+++ clang-tools-extra/clangd/refactor/Tweak.h
@@ -64,12 +64,44 @@
/// Provide information to the user.
Info,
};
+
+ // The class represents a small and focus refactoring. The purpose is to form
+ // a chain of different refactorings, so that we could perform a large and
+ // complicate refactoring.
+ //
+ // Steps are applied in sequence. In LSP layer, each step is transformed to a
+ // call request to the LSP client, LSPServer waits for the client response and
+ // continues to apply next step when the previous step is finished
+ // successfully.
+ //
+ // clangd doesn't track changes of the source files when apply a step, so
+ // the following steps are not aware of the changes, Tweak is corresponding to
+ // pre-calculate the changes for all steps.
+ // FIXME: we should have a mechanism to track the changes for each step.
+ struct Step {
+ enum Kind {
+ ApplyEdit,
+ Rename,
+ };
+ Kind ApplyKind;
+
+ llvm::Optional<tooling::Replacements> ApplyEditParams; // For ApplyEdit
+ llvm::Optional<Position> RenameParams; // For Rename
+ };
+
struct Effect {
/// A message to be displayed to the user.
llvm::Optional<std::string> ShowMessage;
/// An edit to apply to the input file.
llvm::Optional<tooling::Replacements> ApplyEdit;
+ /// A chain of steps being executed in order when applying the tweak.
+ llvm::Optional<std::vector<Step>> ApplySteps;
+ static Effect applySteps(std::vector<Step> Steps) {
+ Effect E;
+ E.ApplySteps = std::move(Steps);
+ return E;
+ }
static Effect applyEdit(tooling::Replacements R) {
Effect E;
E.ApplyEdit = std::move(R);
Index: clang-tools-extra/clangd/clients/clangd-vscode/src/extension.ts
===================================================================
--- clang-tools-extra/clangd/clients/clangd-vscode/src/extension.ts
+++ clang-tools-extra/clangd/clients/clangd-vscode/src/extension.ts
@@ -128,6 +128,36 @@
status.clear();
}
})
+
+ clangdClient.onReady().then(
+ () => {clangdClient.onRequest(
+ "clangd.triggerRename", async (result: any) => {
+ if (result.arguments) {
+ return await vscode.commands
+ .executeCommand(
+ 'editor.action.rename',
+ [
+ vscode.Uri.file(
+ vscode.window.activeTextEditor.document.fileName),
+ result.arguments[0]
+ ])
+ .then((value) => { return Promise.resolve(true); },
+ (err) => {
+ if (err.name == 'Canceled') {
+ // VSCode will cancel the request automatically
+ // when it detects there is a change of the file
+ // (rename) during the process of handling the
+ // triggerRename request. but we don't want client
+ // send back a cancel error to clangd, so consume
+ // the cancel here.
+ return Promise.resolve(true);
+ }
+
+ return Promise.resolve(err);
+ });
+ }
+ return Promise.reject(new Error("Invalid commend arguments"));
+ })});
// An empty place holder for the activate command, otherwise we'll get an
// "command is not registered" error.
context.subscriptions.push(vscode.commands.registerCommand(
Index: clang-tools-extra/clangd/ClangdLSPServer.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdLSPServer.cpp
+++ clang-tools-extra/clangd/ClangdLSPServer.cpp
@@ -551,9 +551,87 @@
auto Action = [this, ApplyEdit](decltype(Reply) Reply, URIForFile File,
std::string Code,
llvm::Expected<Tweak::Effect> R) {
+ // Function object to apply steps for a tweak.
+ // Steps are executed in order: for each step, clangd sends a new request
+ // to the LSP client, waits for the response and continues next step; if
+ // there is any error during the apply process, clangd stops applying the
+ // following steps.
+ //
+ // Example steps [applyEdit, Rename]
+ // - apply the first step: send call(applyEdit, Workspace) request
+ // - ..wait for client response
+ // - apply the second step: send call(rename, trigger)
+ // - ..wait for the reply, and finish.
+ struct CallInSequence {
+ public:
+ CallInSequence(ClangdLSPServer *Server, std::vector<Tweak::Step> Steps,
+ std::string Code, URIForFile MainFile)
+ : Server(Server), Steps(std::move(Steps)), nextStepIndex(0),
+ Code(std::move(Code)), MainFile(std::move(MainFile)) {}
+
+ void operator()(llvm::Expected<llvm::json::Value> Result) {
+ if (!Result) {
+ // FIXME: should send a failed reply to client.
+ log("receive error: {0}", llvm::toString(Result.takeError()));
+ return;
+ }
+ if (nextStepIndex >= Steps.size()) {
+ // FIXME: should send a successful reply to client.
+ log("applied tweak successfully");
+ return;
+ }
+ auto RequestToClient = takeNextStep();
+ nextCall(Server, RequestToClient.first, RequestToClient.second);
+ }
+
+ std::pair<std::string, llvm::json::Value> takeNextStep() {
+ assert(nextStepIndex < Steps.size() && "index out of boundary!");
+ return transform(Steps[nextStepIndex++]);
+ }
+
+ private:
+ // Transform the step into a LSP request.
+ std::pair</* Method */ std::string,
+ /* MethodParams */ llvm::json::Value>
+ transform(const Tweak::Step &Step) {
+ if (Step.ApplyKind == Tweak::Step::ApplyEdit) {
+ ApplyWorkspaceEditParams Edit;
+
+ Edit.edit.changes.emplace();
+ (*Edit.edit.changes)[MainFile.uri()] =
+ replacementsToEdits(Code, *Step.ApplyEditParams);
+ return {"workspace/applyEdit", std::move(Edit)};
+ } else if (Step.ApplyKind == Tweak::Step::Rename) {
+ // Clangd extension.
+ llvm::json::Object Result;
+ Result["arguments"] = {*Step.RenameParams};
+ return {"clangd.triggerRename", std::move(Result)};
+ }
+ llvm_unreachable("unhandled apply kind");
+ }
+ void nextCall(ClangdLSPServer *LSPServer, llvm::StringRef Method,
+ llvm::json::Value Params) {
+ LSPServer->call(Method, std::move(Params), std::move(*this));
+ }
+
+ ClangdLSPServer *Server;
+ std::vector<Tweak::Step> Steps;
+ size_t nextStepIndex;
+
+ // datas for transforming step.
+ std::string Code;
+ URIForFile MainFile;
+ };
+
if (!R)
return Reply(R.takeError());
+ if (R->ApplySteps) {
+ CallInSequence Calls(this, *R->ApplySteps, Code, File);
+ auto Request = Calls.takeNextStep();
+ // Starts the call chain.
+ call(Request.first, std::move(Request.second), std::move(Calls));
+ }
if (R->ApplyEdit) {
WorkspaceEdit WE;
WE.changes.emplace();
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits