Integrate needsConfirmation
This commit is contained in:
@@ -13,8 +13,12 @@ package org.springframework.ide.vscode.commons.rewrite;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.eclipse.lsp4j.AnnotatedTextEdit;
|
||||
import org.eclipse.lsp4j.ChangeAnnotation;
|
||||
import org.eclipse.lsp4j.CreateFile;
|
||||
import org.eclipse.lsp4j.DeleteFile;
|
||||
import org.eclipse.lsp4j.Position;
|
||||
@@ -72,7 +76,7 @@ public class ORDocUtils {
|
||||
|
||||
}
|
||||
|
||||
public static Optional<TextDocumentEdit> computeTextDocEdit(TextDocument doc, Result result) {
|
||||
public static Optional<TextDocumentEdit> computeTextDocEdit(TextDocument doc, Result result, String changeAnnotationId) {
|
||||
TextDocument newDoc = new TextDocument(null, LanguageId.PLAINTEXT, 0, result.getAfter().printAll());
|
||||
|
||||
EditList diff = JGitUtils.getDiff(result.getBefore().printAll(), newDoc.get());
|
||||
@@ -85,26 +89,29 @@ public class ORDocUtils {
|
||||
try {
|
||||
switch(e.getType()) {
|
||||
case DELETE:
|
||||
TextEdit textEdit = new TextEdit();
|
||||
AnnotatedTextEdit textEdit = new AnnotatedTextEdit();
|
||||
int start = doc.getLineOffset(e.getBeginA());
|
||||
int end = getStartOfLine(doc, e.getEndA());
|
||||
textEdit.setRange(new Range(doc.toPosition(start), doc.toPosition(end)));
|
||||
textEdit.setNewText("");
|
||||
textEdit.setAnnotationId(changeAnnotationId);
|
||||
textEdits.add(textEdit);
|
||||
break;
|
||||
case INSERT:
|
||||
textEdit = new TextEdit();
|
||||
textEdit = new AnnotatedTextEdit();
|
||||
Position position = doc.toPosition(doc.getLineOffset(e.getBeginA()));
|
||||
textEdit.setRange(new Range(position, position));
|
||||
textEdit.setNewText(newDoc.textBetween(newDoc.getLineOffset(e.getBeginB()), getStartOfLine(newDoc, e.getEndB())));
|
||||
textEdit.setAnnotationId(changeAnnotationId);
|
||||
textEdits.add(textEdit);
|
||||
break;
|
||||
case REPLACE:
|
||||
textEdit = new TextEdit();
|
||||
textEdit = new AnnotatedTextEdit();
|
||||
start = doc.getLineOffset(e.getBeginA());
|
||||
end = getStartOfLine(doc, e.getEndA());
|
||||
textEdit.setRange(new Range(doc.toPosition(start), doc.toPosition(end)));
|
||||
textEdit.setNewText(newDoc.textBetween(newDoc.getLineOffset(e.getBeginB()), getStartOfLine(newDoc, e.getEndB())));
|
||||
textEdit.setAnnotationId(changeAnnotationId);
|
||||
textEdits.add(textEdit);
|
||||
break;
|
||||
case EMPTY:
|
||||
@@ -151,12 +158,14 @@ public class ORDocUtils {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static Optional<WorkspaceEdit> createWorkspaceEdit(Path absoluteProjectDir, SimpleTextDocumentService documents, List<Result> results) {
|
||||
public static Optional<WorkspaceEdit> createWorkspaceEdit(Path absoluteProjectDir, SimpleTextDocumentService documents, List<Result> results, ChangeAnnotation changeAnnotation) {
|
||||
if (results.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
WorkspaceEdit we = new WorkspaceEdit();
|
||||
we.setDocumentChanges(new ArrayList<>());
|
||||
final String changeAnnotationId = UUID.randomUUID().toString();
|
||||
we.setChangeAnnotations(Map.of(changeAnnotationId, changeAnnotation));
|
||||
for (Result result : results) {
|
||||
if (result.getBefore() == null) {
|
||||
String docUri = absoluteProjectDir.resolve(result.getAfter().getSourcePath()).toUri().toASCIIString();
|
||||
@@ -167,7 +176,7 @@ public class ORDocUtils {
|
||||
TextDocumentEdit te = new TextDocumentEdit();
|
||||
te.setTextDocument(new VersionedTextDocumentIdentifier(docUri, 0));
|
||||
Position cursor = new Position(0,0);
|
||||
te.setEdits(List.of(new TextEdit(new Range(cursor, cursor), result.getAfter().printAll())));
|
||||
te.setEdits(List.of(new AnnotatedTextEdit(new Range(cursor, cursor), result.getAfter().printAll(), changeAnnotationId)));
|
||||
we.getDocumentChanges().add(Either.forLeft(te));
|
||||
} else if (result.getAfter() == null) {
|
||||
String docUri = absoluteProjectDir.resolve(result.getBefore().getSourcePath()).toUri().toASCIIString();
|
||||
@@ -177,9 +186,9 @@ public class ORDocUtils {
|
||||
TextDocument doc = documents.getLatestSnapshot(docUri);
|
||||
if (doc == null) {
|
||||
doc = new TextDocument(docUri, null, 0, result.getBefore().printAll());
|
||||
ORDocUtils.computeTextDocEdit(doc, result).ifPresent(te -> we.getDocumentChanges().add(Either.forLeft(te)));
|
||||
ORDocUtils.computeTextDocEdit(doc, result, changeAnnotationId).ifPresent(te -> we.getDocumentChanges().add(Either.forLeft(te)));
|
||||
} else {
|
||||
ORDocUtils.computeTextDocEdit(doc, result).ifPresent(te -> we.getDocumentChanges().add(Either.forLeft(te)));
|
||||
ORDocUtils.computeTextDocEdit(doc, result, changeAnnotationId).ifPresent(te -> we.getDocumentChanges().add(Either.forLeft(te)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.lsp4j.ApplyWorkspaceEditParams;
|
||||
import org.eclipse.lsp4j.ChangeAnnotation;
|
||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||
import org.eclipse.lsp4j.WorkspaceEdit;
|
||||
import org.openrewrite.InMemoryExecutionContext;
|
||||
@@ -282,6 +283,7 @@ public class RewriteRecipeRepository {
|
||||
return recipes().thenCompose(recipes -> {
|
||||
String uri = ((JsonElement) params.getArguments().get(0)).getAsString();
|
||||
JsonElement recipesJson = ((JsonElement) params.getArguments().get(1));
|
||||
boolean needsConfirmation = params.getArguments().size() > 2 ? ((JsonElement) params.getArguments().get(2)).getAsBoolean() : false;
|
||||
|
||||
RecipeDescriptor d = serializationGson.fromJson(recipesJson, RecipeDescriptor.class);
|
||||
|
||||
@@ -295,14 +297,14 @@ public class RewriteRecipeRepository {
|
||||
|| params.getWorkDoneToken().getLeft() == null
|
||||
? (r.getName() == null ? UUID.randomUUID().toString() : r.getName())
|
||||
: params.getWorkDoneToken().getLeft();
|
||||
return apply(r, uri, progressToken);
|
||||
return apply(r, uri, progressToken, needsConfirmation);
|
||||
} else {
|
||||
String progressToken = params.getWorkDoneToken() == null
|
||||
|| params.getWorkDoneToken().getLeft() == null
|
||||
? (aggregateRecipe.getName() == null ? UUID.randomUUID().toString()
|
||||
: aggregateRecipe.getName())
|
||||
: params.getWorkDoneToken().getLeft();
|
||||
return apply(aggregateRecipe, uri, progressToken);
|
||||
return apply(aggregateRecipe, uri, progressToken, needsConfirmation);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -314,23 +316,24 @@ public class RewriteRecipeRepository {
|
||||
|
||||
server.onCommand(CMD_REWRITE_RECIPE_EXECUTE, params -> {
|
||||
String recipeId = ((JsonElement) params.getArguments().get(0)).getAsString();
|
||||
boolean needsConfirmation = params.getArguments().size() > 1 ? ((JsonElement) params.getArguments().get(1)).getAsBoolean() : false;
|
||||
return getRecipe(recipeId).thenCompose(optRecipe -> {
|
||||
Recipe r = optRecipe.orElseThrow(() -> new IllegalArgumentException("No such recipe exists with name " + recipeId));
|
||||
final String progressToken = params.getWorkDoneToken() == null || params.getWorkDoneToken().getLeft() == null ? r.getName() : params.getWorkDoneToken().getLeft();
|
||||
String uri = ((JsonElement) params.getArguments().get(1)).getAsString();
|
||||
return apply(r, uri, progressToken);
|
||||
return apply(r, uri, progressToken, needsConfirmation);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
CompletableFuture<Object> apply(Recipe r, String uri, String progressToken) {
|
||||
CompletableFuture<Object> apply(Recipe r, String uri, String progressToken, boolean needsConfirmation) {
|
||||
final IndefiniteProgressTask progressTask = server.getProgressService().createIndefiniteProgressTask(progressToken, r.getDisplayName(), "Initiated...");
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
return projectFinder.find(new TextDocumentIdentifier(uri));
|
||||
}).thenCompose(p -> {
|
||||
if (p.isPresent()) {
|
||||
try {
|
||||
Optional<WorkspaceEdit> edit = computeWorkspaceEdit(r, p.get(), progressTask);
|
||||
Optional<WorkspaceEdit> edit = computeWorkspaceEdit(r, p.get(), progressTask, needsConfirmation);
|
||||
return CompletableFuture.completedFuture(edit).thenCompose(we -> {
|
||||
if (we.isPresent()) {
|
||||
progressTask.progressEvent("Applying document changes...");
|
||||
@@ -358,7 +361,7 @@ public class RewriteRecipeRepository {
|
||||
});
|
||||
}
|
||||
|
||||
private Optional<WorkspaceEdit> computeWorkspaceEdit(Recipe r, IJavaProject project, IndefiniteProgressTask progressTask) {
|
||||
private Optional<WorkspaceEdit> computeWorkspaceEdit(Recipe r, IJavaProject project, IndefiniteProgressTask progressTask, boolean needsConfirmation) {
|
||||
Path absoluteProjectDir = Paths.get(project.getLocationUri());
|
||||
progressTask.progressEvent("Parsing files...");
|
||||
ProjectParser projectParser = createRewriteProjectParser(project,
|
||||
@@ -374,7 +377,9 @@ public class RewriteRecipeRepository {
|
||||
progressTask.progressEvent("Computing changes...");
|
||||
RecipeRun reciperun = r.run(new InMemoryLargeSourceSet(sources), new InMemoryExecutionContext(e -> log.error("Recipe execution failed", e)));
|
||||
List<Result> results = reciperun.getChangeset().getAllResults();
|
||||
return ORDocUtils.createWorkspaceEdit(absoluteProjectDir, server.getTextDocumentService(), results);
|
||||
ChangeAnnotation changeAnnotation = new ChangeAnnotation("Apply Recipe '" + r.getDisplayName() + "'");
|
||||
changeAnnotation.setNeedsConfirmation(needsConfirmation);
|
||||
return ORDocUtils.createWorkspaceEdit(absoluteProjectDir, server.getTextDocumentService(), results, changeAnnotation);
|
||||
}
|
||||
|
||||
private void reportParseErrors(List<ParseError> parseErrors) {
|
||||
|
||||
@@ -15,12 +15,14 @@ import java.net.URI;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.lsp4j.ChangeAnnotation;
|
||||
import org.eclipse.lsp4j.CodeAction;
|
||||
import org.eclipse.lsp4j.ResourceOperation;
|
||||
import org.eclipse.lsp4j.TextDocumentEdit;
|
||||
@@ -120,22 +122,26 @@ public class RewriteRefactorings implements CodeActionResolver, QuickfixHandler
|
||||
return null;
|
||||
}
|
||||
|
||||
private WorkspaceEdit applyRecipe(Recipe r, IJavaProject project, List<CompilationUnit> cus) {
|
||||
private WorkspaceEdit applyRecipe(Recipe r, IJavaProject project, List<CompilationUnit> cus, boolean needsConfirmation) {
|
||||
List<SourceFile> sources = cus.stream().map(cu -> (SourceFile) cu).collect(Collectors.toList());
|
||||
RecipeRun reciperun = r.run(new InMemoryLargeSourceSet(sources), new InMemoryExecutionContext());
|
||||
List<Result> results = reciperun.getChangeset().getAllResults();
|
||||
final String changeAnnotationId = UUID.randomUUID().toString();
|
||||
List<Either<TextDocumentEdit, ResourceOperation>> edits = results.stream().filter(res -> res.getAfter() != null).map(res -> {
|
||||
URI docUri = res.getAfter().getSourcePath().isAbsolute() ? res.getAfter().getSourcePath().toUri() : project.getLocationUri().resolve(res.getAfter().getSourcePath().toString());
|
||||
TextDocument doc = server.getTextDocumentService().getLatestSnapshot(docUri.toASCIIString());
|
||||
if (doc == null) {
|
||||
doc = new TextDocument(docUri.toASCIIString(), LanguageId.JAVA, 0, res.getBefore() == null ? "" : res.getBefore().printAll());
|
||||
}
|
||||
return ORDocUtils.computeTextDocEdit(doc, res);
|
||||
return ORDocUtils.computeTextDocEdit(doc, res, changeAnnotationId);
|
||||
}).filter(e -> e.isPresent()).map(e -> e.get()).map(e -> Either.<TextDocumentEdit, ResourceOperation>forLeft(e)).collect(Collectors.toList());
|
||||
if (edits.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
WorkspaceEdit workspaceEdit = new WorkspaceEdit();
|
||||
ChangeAnnotation changeAnnotation = new ChangeAnnotation("Applying Recipe '" + r.getDisplayName() + "'");
|
||||
changeAnnotation.setNeedsConfirmation(needsConfirmation);
|
||||
workspaceEdit.setChangeAnnotations(Map.of(changeAnnotationId, changeAnnotation));
|
||||
workspaceEdit.setDocumentChanges(edits);
|
||||
return workspaceEdit;
|
||||
}
|
||||
@@ -155,7 +161,7 @@ public class RewriteRefactorings implements CodeActionResolver, QuickfixHandler
|
||||
PercentageProgressTask progress = server.getProgressService().createPercentageProgressTask(UUID.randomUUID().toString(), inputs.size() + 1, data.getLabel());
|
||||
try {
|
||||
cus = ORAstUtils.parseInputs(jp, inputs, s -> progress.increment());
|
||||
return applyRecipe(r, project.get(), cus);
|
||||
return applyRecipe(r, project.get(), cus, false);
|
||||
} finally {
|
||||
progress.setCurrent(progress.getTotal());
|
||||
progress.done();
|
||||
@@ -164,7 +170,7 @@ public class RewriteRefactorings implements CodeActionResolver, QuickfixHandler
|
||||
JavaParser jp = ORAstUtils.createJavaParserBuilder(project.get()).dependsOn(data.getTypeStubs()).build();
|
||||
List<Input> inputs = data.getDocUris().stream().map(URI::create).map(Paths::get).map(p -> ORAstUtils.getParserInput(server.getTextDocumentService(), p)).collect(Collectors.toList());
|
||||
cus = ORAstUtils.parseInputs(jp, inputs, null);
|
||||
return applyRecipe(r, project.get(), cus);
|
||||
return applyRecipe(r, project.get(), cus, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ public class SpringBootUpgrade {
|
||||
Assert.isLegal(uri != null, "Project URI parameter must not be 'null'");
|
||||
Version targetVersion = Version.parse(((JsonElement) params.getArguments().get(1)).getAsString());
|
||||
Assert.isLegal(targetVersion != null, "Target Spring Boot version must not be 'null'");
|
||||
boolean needsConfirmation = params.getArguments().size() > 2 ? ((JsonElement) params.getArguments().get(2)).getAsBoolean() : false;
|
||||
|
||||
IJavaProject project = projectFinder.find(new TextDocumentIdentifier(uri)).orElse(null);
|
||||
Assert.isLegal(project != null, "No Spring Boot project found for uri: " + uri);
|
||||
@@ -75,7 +76,8 @@ public class SpringBootUpgrade {
|
||||
return recipeRepo.recipes().thenComposeAsync(recipes -> recipeRepo.apply(
|
||||
createUpgradeRecipe(recipes, version, targetVersion),
|
||||
uri,
|
||||
UUID.randomUUID().toString()
|
||||
UUID.randomUUID().toString(),
|
||||
needsConfirmation
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -97,6 +97,7 @@ async function showRefactorings(uri: VSCode.Uri, filter: string) {
|
||||
}
|
||||
const choices = await showCurrentPathQuickPick(VSCode.commands.executeCommand('sts/rewrite/list', uri.toString(true), filter).then((cmds: RecipeDescriptor[]) => cmds.map(convertToQuickPickItem)), []);
|
||||
const recipeDescriptors = choices.filter(i => i.selected).map(convertToRecipeDescriptor);
|
||||
const needsConfirmation = await shwoNeedsConfirmation();
|
||||
if (recipeDescriptors.length) {
|
||||
const aggregateRecipeDescriptor = recipeDescriptors.length === 1 ? recipeDescriptors[0] : {
|
||||
name: `${recipeDescriptors.length} recipes`,
|
||||
@@ -111,7 +112,7 @@ async function showRefactorings(uri: VSCode.Uri, filter: string) {
|
||||
if (aggregateRecipeDescriptor.estimatedEffortPerOccurrence === 0) {
|
||||
delete aggregateRecipeDescriptor.estimatedEffortPerOccurrence;
|
||||
}
|
||||
VSCode.commands.executeCommand('sts/rewrite/execute', uri.toString(true), aggregateRecipeDescriptor);
|
||||
VSCode.commands.executeCommand('sts/rewrite/execute', uri.toString(true), aggregateRecipeDescriptor, needsConfirmation);
|
||||
} else {
|
||||
VSCode.window.showErrorMessage('No Recipes were selected!');
|
||||
}
|
||||
@@ -137,6 +138,33 @@ function convertToQuickPickItem(i: RecipeDescriptor): RecipeQuickPickItem {
|
||||
};
|
||||
}
|
||||
|
||||
function shwoNeedsConfirmation(): Thenable<boolean> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const previewPick = VSCode.window.createQuickPick<VSCode.QuickPickItem>();
|
||||
previewPick.title = 'Preview Before Applying?';
|
||||
previewPick.canSelectMany = false;
|
||||
|
||||
const applyItem = {
|
||||
label: "Apply",
|
||||
description: "Apply changes maded by a recipe"
|
||||
}
|
||||
const previewItem = {
|
||||
label: "Preview",
|
||||
description: "Preview and confirm changes made by a recipe before applying"
|
||||
}
|
||||
previewPick.items = [
|
||||
applyItem,
|
||||
previewItem
|
||||
];
|
||||
previewPick.show();
|
||||
|
||||
previewPick.onDidAccept(() => {
|
||||
previewPick.hide();
|
||||
resolve(previewPick.selectedItems[0] === previewItem);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function showCurrentPathQuickPick(itemsPromise: Thenable<RecipeQuickPickItem[]>, itemsPath: RecipeQuickPickItem[]): Thenable<RecipeQuickPickItem[]> {
|
||||
const quickPick = VSCode.window.createQuickPick<RecipeQuickPickItem>();
|
||||
quickPick.title = 'Loading Recipes...';
|
||||
@@ -176,13 +204,8 @@ function showCurrentPathQuickPick(itemsPromise: Thenable<RecipeQuickPickItem[]>,
|
||||
});
|
||||
quickPick.onDidAccept(() => {
|
||||
currentItems.forEach(i => i.selected = quickPick.selectedItems.includes(i));
|
||||
if (itemsPath.length) {
|
||||
itemsPath.pop();
|
||||
showCurrentPathQuickPick(Promise.resolve(items), itemsPath).then(resolve, reject);
|
||||
} else {
|
||||
quickPick.hide();
|
||||
resolve(items);
|
||||
}
|
||||
quickPick.hide();
|
||||
resolve(items);
|
||||
});
|
||||
quickPick.onDidChangeSelection(selected => {
|
||||
currentItems.forEach(i => {
|
||||
|
||||
Reference in New Issue
Block a user