Eclipse UI for Rewrite Recipes
This commit is contained in:
@@ -175,8 +175,8 @@
|
||||
</activeWhen>
|
||||
</handler>
|
||||
<handler
|
||||
class="org.springframework.tooling.boot.ls.commands.UpgradeToBoot3Handler"
|
||||
commandId="org.springframework.tooling.boot.ls.UpgradeToBoot3">
|
||||
class="org.springframework.tooling.boot.ls.commands.RewriteRefactoringsHandler"
|
||||
commandId="org.springframework.tooling.boot.ls.Rewrite">
|
||||
</handler>
|
||||
</extension>
|
||||
<extension
|
||||
@@ -194,9 +194,9 @@
|
||||
</command>
|
||||
<command
|
||||
categoryId="org.springframework.ide.eclipse.commands"
|
||||
description="Upgrade Spring Boot 2.x prohect to Spring Boot 3.x"
|
||||
id="org.springframework.tooling.boot.ls.UpgradeToBoot3"
|
||||
name="Upgrade to Boot 3">
|
||||
description="Rewrite Refactorings for Spring Boot projects"
|
||||
id="org.springframework.tooling.boot.ls.Rewrite"
|
||||
name="Rewrite Refactorings...">
|
||||
</command>
|
||||
</extension>
|
||||
|
||||
@@ -372,9 +372,9 @@
|
||||
allPopups="false"
|
||||
locationURI="popup:org.springframework.ide.eclipse.ui.tools?after=boot">
|
||||
<command
|
||||
commandId="org.springframework.tooling.boot.ls.UpgradeToBoot3"
|
||||
id="org.springframework.tooling.boot.ls.UpgradeToBoot3"
|
||||
label="Upgrade to Boot 3"
|
||||
commandId="org.springframework.tooling.boot.ls.Rewrite"
|
||||
id="org.springframework.tooling.boot.ls.Rewrite"
|
||||
label="Rewrite Refactorings..."
|
||||
style="push">
|
||||
<visibleWhen
|
||||
checkEnabled="false">
|
||||
@@ -389,7 +389,7 @@
|
||||
<and>
|
||||
<test
|
||||
forcePluginActivation="true"
|
||||
property="org.springsource.ide.eclipse.boot.isBoot2Resource">
|
||||
property="org.springsource.ide.eclipse.boot.isBootResource">
|
||||
</test>
|
||||
<or>
|
||||
<test
|
||||
@@ -416,7 +416,7 @@
|
||||
</test>
|
||||
<test
|
||||
forcePluginActivation="true"
|
||||
property="org.springsource.ide.eclipse.boot.javaelement.isInBoot2Project">
|
||||
property="org.springsource.ide.eclipse.boot.javaelement.isBootProject">
|
||||
</test>
|
||||
</and>
|
||||
</or>
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2022 VMware, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* VMware, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.tooling.boot.ls.commands;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
class RecipeDescriptor {
|
||||
|
||||
String name;
|
||||
|
||||
String displayName;
|
||||
|
||||
String description;
|
||||
|
||||
Set<String> tags;
|
||||
|
||||
Duration estimatedEffortPerOccurrence;
|
||||
|
||||
List<OptionDescriptor> options;
|
||||
|
||||
List<String> languages;
|
||||
|
||||
List<RecipeDescriptor> recipeList;
|
||||
|
||||
URI source;
|
||||
|
||||
RecipeDescriptor getCopyWithoutSubRecipes() {
|
||||
RecipeDescriptor copy = new RecipeDescriptor();
|
||||
copy.name = name;
|
||||
copy.displayName = displayName;
|
||||
copy.description = description;
|
||||
copy.tags = tags;
|
||||
copy.estimatedEffortPerOccurrence = estimatedEffortPerOccurrence;
|
||||
copy.options = options;
|
||||
copy.languages = languages;
|
||||
copy.source = source;
|
||||
return copy;
|
||||
}
|
||||
|
||||
static class OptionDescriptor {
|
||||
|
||||
String name;
|
||||
|
||||
String type;
|
||||
|
||||
String displayName;
|
||||
|
||||
String description;
|
||||
|
||||
String example;
|
||||
|
||||
List<String> valid;
|
||||
|
||||
boolean required;
|
||||
|
||||
Object value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2022 VMware, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* VMware, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.tooling.boot.ls.commands;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
|
||||
|
||||
public class RecipeTreeModel {
|
||||
|
||||
public enum CheckedState {
|
||||
UNCHECKED,
|
||||
CHECKED,
|
||||
GRAYED
|
||||
}
|
||||
|
||||
private RecipeDescriptor[] recipeDescriptors;
|
||||
private Map<RecipeDescriptor, RecipeDescriptor> parentMap = new IdentityHashMap<>();
|
||||
private Map<RecipeDescriptor, CheckedState> checkedMap = new IdentityHashMap<>();
|
||||
|
||||
RecipeTreeModel(RecipeDescriptor[] recipeDescriptors) {
|
||||
this.recipeDescriptors = recipeDescriptors;
|
||||
for (RecipeDescriptor d : recipeDescriptors) {
|
||||
initParentMap(d);
|
||||
}
|
||||
}
|
||||
|
||||
private void initParentMap(RecipeDescriptor d) {
|
||||
if (d.recipeList != null) {
|
||||
for (RecipeDescriptor dc : d.recipeList) {
|
||||
parentMap.put(dc, d);
|
||||
initParentMap(dc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void check(RecipeDescriptor d) {
|
||||
if (simpleCheck(d)) {
|
||||
inferCheckedStateFromChildren(parentMap.get(d));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean simpleCheck(RecipeDescriptor d) {
|
||||
if (checkedMap.get(d) != CheckedState.CHECKED) {
|
||||
checkedMap.put(d, CheckedState.CHECKED);
|
||||
for (RecipeDescriptor dc : d.recipeList) {
|
||||
simpleCheck(dc);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void uncheck(RecipeDescriptor d) {
|
||||
if (simpleUncheck(d)) {
|
||||
inferCheckedStateFromChildren(parentMap.get(d));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean simpleUncheck(RecipeDescriptor d) {
|
||||
if (checkedMap.get(d) != CheckedState.UNCHECKED) {
|
||||
checkedMap.put(d, CheckedState.UNCHECKED);
|
||||
for (RecipeDescriptor dc : d.recipeList) {
|
||||
simpleUncheck(dc);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public CheckedState getCheckedState(RecipeDescriptor d) {
|
||||
CheckedState state = checkedMap.get(d);
|
||||
return state == null ? CheckedState.UNCHECKED : state;
|
||||
}
|
||||
|
||||
private void inferCheckedStateFromChildren(RecipeDescriptor d) {
|
||||
if (d != null && d.recipeList != null) {
|
||||
boolean all = true;
|
||||
boolean none = true;
|
||||
for (RecipeDescriptor child : d.recipeList) {
|
||||
CheckedState childState = getCheckedState(child);
|
||||
if (childState == CheckedState.UNCHECKED) {
|
||||
all = false;
|
||||
} else {
|
||||
none = false;
|
||||
}
|
||||
}
|
||||
CheckedState inferredState = CheckedState.GRAYED;
|
||||
if (all) {
|
||||
inferredState = CheckedState.CHECKED;
|
||||
} else if (none) {
|
||||
inferredState = CheckedState.UNCHECKED;
|
||||
}
|
||||
if (getCheckedState(d) != inferredState) {
|
||||
checkedMap.put(d, inferredState);
|
||||
inferCheckedStateFromChildren(parentMap.get(d));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public RecipeDescriptor[] getRecipeDescriptors() {
|
||||
return recipeDescriptors;
|
||||
}
|
||||
|
||||
public RecipeDescriptor getSelectedRecipeDescriptors() throws CoreException {
|
||||
RecipeDescriptor[] recipes = Arrays.stream(recipeDescriptors).map(this::copySelectedDescriptor).filter(Objects::nonNull).toArray(RecipeDescriptor[]::new);
|
||||
if (recipes.length == 0) {
|
||||
throw new CoreException(Status.error("No recipes selected"));
|
||||
} else if (recipes.length == 1) {
|
||||
return recipes[0];
|
||||
} else {
|
||||
RecipeDescriptor aggregate = new RecipeDescriptor();
|
||||
aggregate.displayName = recipes.length + " recipes";
|
||||
aggregate.description = "Multiple recipes to be applied. Number of recipes " + recipes.length;
|
||||
aggregate.tags = Arrays.stream(recipes).flatMap(r -> r.tags.stream()).collect(Collectors.toSet());
|
||||
aggregate.recipeList = Arrays.asList(recipes);
|
||||
return aggregate;
|
||||
}
|
||||
}
|
||||
|
||||
private RecipeDescriptor copySelectedDescriptor(RecipeDescriptor d) {
|
||||
if (getCheckedState(d) != CheckedState.UNCHECKED) {
|
||||
RecipeDescriptor copy = d.getCopyWithoutSubRecipes();
|
||||
if (d.recipeList != null) {
|
||||
copy.recipeList = d.recipeList.stream().map(this::copySelectedDescriptor).filter(Objects::nonNull).collect(Collectors.toList());
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2022 VMware, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* VMware, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.tooling.boot.ls.commands;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.eclipse.core.commands.AbstractHandler;
|
||||
import org.eclipse.core.commands.ExecutionEvent;
|
||||
import org.eclipse.core.commands.ExecutionException;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IAdaptable;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jface.viewers.IStructuredSelection;
|
||||
import org.eclipse.jface.window.Window;
|
||||
import org.eclipse.lsp4e.LanguageServiceAccessor;
|
||||
import org.eclipse.lsp4j.ExecuteCommandParams;
|
||||
import org.eclipse.lsp4j.services.LanguageServer;
|
||||
import org.eclipse.swt.widgets.Display;
|
||||
import org.eclipse.ui.PlatformUI;
|
||||
import org.eclipse.ui.handlers.HandlerUtil;
|
||||
import org.springframework.tooling.boot.ls.BootLanguageServerPlugin;
|
||||
import org.springsource.ide.eclipse.commons.livexp.util.Log;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
|
||||
public class RewriteRefactoringsHandler extends AbstractHandler {
|
||||
|
||||
private static class DurationTypeConverter implements JsonSerializer<Duration>, JsonDeserializer<Duration> {
|
||||
@Override
|
||||
public JsonElement serialize(Duration src, Type srcType, JsonSerializationContext context) {
|
||||
return new JsonPrimitive(src.toNanos());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration deserialize(JsonElement json, Type type, JsonDeserializationContext context)
|
||||
throws JsonParseException {
|
||||
return Duration.ofNanos(json.getAsLong());
|
||||
}
|
||||
}
|
||||
|
||||
private static Gson serializationGson = new GsonBuilder()
|
||||
.registerTypeAdapter(Duration.class, new DurationTypeConverter())
|
||||
.create();
|
||||
|
||||
|
||||
private static final String REWRITE_REFACTORINGS_LIST = "sts/rewrite/list";
|
||||
private static final String REWRITE_REFACTORINGS_EXEC = "sts/rewrite/execute";
|
||||
|
||||
@Override
|
||||
public Object execute(ExecutionEvent event) throws ExecutionException {
|
||||
|
||||
IStructuredSelection selection = HandlerUtil.getCurrentStructuredSelection(event);
|
||||
Object o = selection.getFirstElement();
|
||||
IProject project = null;
|
||||
if (o instanceof IResource) {
|
||||
project = ((IResource) o).getProject();
|
||||
} else if (o instanceof IProject) {
|
||||
project = (IProject) o;
|
||||
} else if (o instanceof IAdaptable) {
|
||||
project = ((IAdaptable) o).getAdapter(IProject.class);
|
||||
}
|
||||
if (project != null) {
|
||||
List<@NonNull LanguageServer> usedLanguageServers = LanguageServiceAccessor
|
||||
.getActiveLanguageServers(serverCapabilities -> true);
|
||||
|
||||
if (!usedLanguageServers.isEmpty()) {
|
||||
final String uri = project.getLocationURI().toString();
|
||||
ExecuteCommandParams commandParams = new ExecuteCommandParams();
|
||||
commandParams.setCommand(REWRITE_REFACTORINGS_LIST);
|
||||
commandParams.setArguments(List.of(uri));
|
||||
|
||||
try {
|
||||
List<Object> allRewriteRecipesJson = new ArrayList<>();
|
||||
List<Object> syncRecipesJson = Collections.synchronizedList(allRewriteRecipesJson);
|
||||
CompletableFuture.allOf(usedLanguageServers.stream()
|
||||
.map(ls -> ls.getWorkspaceService().executeCommand(commandParams).thenAccept(or -> {
|
||||
if (or != null) {
|
||||
syncRecipesJson.add(or);
|
||||
}
|
||||
}).exceptionally(t -> null))
|
||||
.toArray(CompletableFuture[]::new)).thenRun(() -> {
|
||||
allRewriteRecipesJson.stream().filter(List.class::isInstance).map(List.class::cast).findFirst().ifPresent(obj -> {
|
||||
RecipeDescriptor[] descriptors = serializationGson.fromJson(serializationGson.toJson(obj), RecipeDescriptor[].class);
|
||||
|
||||
PlatformUI.getWorkbench().getDisplay().asyncExec(() -> {
|
||||
RecipeTreeModel recipesModel = new RecipeTreeModel(descriptors);
|
||||
int returnCode = new SelectRecipesDialog(Display.getCurrent().getActiveShell(), recipesModel).open();
|
||||
if (returnCode == Window.OK) {
|
||||
try {
|
||||
RecipeDescriptor recipeToApply = recipesModel.getSelectedRecipeDescriptors();
|
||||
PlatformUI.getWorkbench().getProgressService().run(true, false, monitor -> {
|
||||
try {
|
||||
if (!usedLanguageServers.isEmpty()) {
|
||||
monitor.beginTask("Applying recipe '" + recipeToApply.displayName + "'", IProgressMonitor.UNKNOWN);
|
||||
ExecuteCommandParams cmdParams = new ExecuteCommandParams();
|
||||
cmdParams.setCommand(REWRITE_REFACTORINGS_EXEC);
|
||||
cmdParams.setArguments(List.of(
|
||||
uri,
|
||||
serializationGson.toJsonTree(recipeToApply)
|
||||
));
|
||||
|
||||
CompletableFuture.allOf(usedLanguageServers.stream()
|
||||
.map(ls -> ls.getWorkspaceService().executeCommand(cmdParams))
|
||||
.toArray(CompletableFuture[]::new)).get();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
} finally {
|
||||
monitor.done();
|
||||
}
|
||||
});
|
||||
|
||||
} catch (CoreException | InvocationTargetException | InterruptedException e) {
|
||||
BootLanguageServerPlugin.getDefault().getLog().error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
} catch (Exception e) {
|
||||
throw new ExecutionException("Failed to apply Rewrite recipe(s)", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2022 VMware, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* VMware, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.tooling.boot.ls.commands;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.eclipse.core.runtime.Status;
|
||||
import org.eclipse.jdt.internal.ui.text.java.hover.JavadocHover;
|
||||
import org.eclipse.jdt.ui.PreferenceConstants;
|
||||
import org.eclipse.jface.dialogs.IDialogSettings;
|
||||
import org.eclipse.jface.dialogs.StatusDialog;
|
||||
import org.eclipse.jface.internal.text.html.HTMLPrinter;
|
||||
import org.eclipse.jface.layout.FillLayoutFactory;
|
||||
import org.eclipse.jface.layout.GridDataFactory;
|
||||
import org.eclipse.jface.resource.ColorRegistry;
|
||||
import org.eclipse.jface.resource.JFaceResources;
|
||||
import org.eclipse.jface.viewers.CheckStateChangedEvent;
|
||||
import org.eclipse.jface.viewers.CheckboxTreeViewer;
|
||||
import org.eclipse.jface.viewers.ICheckStateListener;
|
||||
import org.eclipse.jface.viewers.ICheckStateProvider;
|
||||
import org.eclipse.jface.viewers.ISelectionChangedListener;
|
||||
import org.eclipse.jface.viewers.ITreeContentProvider;
|
||||
import org.eclipse.jface.viewers.LabelProvider;
|
||||
import org.eclipse.jface.viewers.SelectionChangedEvent;
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.browser.Browser;
|
||||
import org.eclipse.swt.custom.SashForm;
|
||||
import org.eclipse.swt.graphics.FontData;
|
||||
import org.eclipse.swt.graphics.RGB;
|
||||
import org.eclipse.swt.layout.FillLayout;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.Control;
|
||||
import org.eclipse.swt.widgets.Display;
|
||||
import org.eclipse.swt.widgets.Menu;
|
||||
import org.eclipse.swt.widgets.Shell;
|
||||
import org.eclipse.ui.PlatformUI;
|
||||
import org.osgi.framework.FrameworkUtil;
|
||||
import org.springframework.tooling.boot.ls.commands.RecipeDescriptor.OptionDescriptor;
|
||||
import org.springframework.tooling.boot.ls.commands.RecipeTreeModel.CheckedState;
|
||||
|
||||
public class SelectRecipesDialog extends StatusDialog {
|
||||
|
||||
private static final int MARGIN = 5;
|
||||
private static final String SELECT_REWRITE_RECIPE_S_FROM_THE_LIST = "Select Rewrite Recipe(s) from the list";
|
||||
private static String fgStyleSheet;
|
||||
|
||||
private RecipeTreeModel model;
|
||||
|
||||
public SelectRecipesDialog(Shell parentShell, RecipeTreeModel model) {
|
||||
super(parentShell);
|
||||
setShellStyle(getShellStyle() | SWT.RESIZE);
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Control createDialogArea(Composite parent) {
|
||||
SashForm form = new SashForm(parent, SWT.HORIZONTAL);
|
||||
form.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create());
|
||||
form.setLayout(new FillLayout());
|
||||
|
||||
Composite left = new Composite(form, SWT.NONE);
|
||||
left.setLayout(FillLayoutFactory.fillDefaults().margins(MARGIN, MARGIN).create());
|
||||
CheckboxTreeViewer treeViewer = new CheckboxTreeViewer(left);
|
||||
treeViewer.setContentProvider(new ITreeContentProvider() {
|
||||
|
||||
@Override
|
||||
public Object[] getElements(Object inputElement) {
|
||||
if (inputElement instanceof RecipeTreeModel) {
|
||||
return ((RecipeTreeModel) inputElement).getRecipeDescriptors();
|
||||
}
|
||||
return new Object[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getChildren(Object parentElement) {
|
||||
if (parentElement instanceof RecipeDescriptor) {
|
||||
RecipeDescriptor r = (RecipeDescriptor) parentElement;
|
||||
return r.recipeList.toArray(new RecipeDescriptor[r.recipeList.size()]);
|
||||
}
|
||||
return new RecipeDescriptor[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getParent(Object element) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasChildren(Object element) {
|
||||
if (element instanceof RecipeDescriptor) {
|
||||
RecipeDescriptor r = (RecipeDescriptor) element;
|
||||
return r.recipeList != null && !r.recipeList.isEmpty();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
treeViewer.setLabelProvider(LabelProvider.createTextProvider(input -> {
|
||||
if (input instanceof RecipeDescriptor) {
|
||||
return ((RecipeDescriptor)input).displayName;
|
||||
}
|
||||
return "unknown";
|
||||
}));
|
||||
treeViewer.setCheckStateProvider(new ICheckStateProvider() {
|
||||
|
||||
@Override
|
||||
public boolean isGrayed(Object element) {
|
||||
if (element instanceof RecipeDescriptor) {
|
||||
RecipeDescriptor r = (RecipeDescriptor) element;
|
||||
return model.getCheckedState(r) == CheckedState.GRAYED;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChecked(Object element) {
|
||||
if (element instanceof RecipeDescriptor) {
|
||||
RecipeDescriptor r = (RecipeDescriptor) element;
|
||||
return model.getCheckedState(r) != CheckedState.UNCHECKED;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
treeViewer.addCheckStateListener(new ICheckStateListener() {
|
||||
|
||||
@Override
|
||||
public void checkStateChanged(CheckStateChangedEvent event) {
|
||||
if (event.getElement() instanceof RecipeDescriptor) {
|
||||
RecipeDescriptor d = (RecipeDescriptor) event.getElement();
|
||||
if (event.getChecked()) {
|
||||
model.check(d);
|
||||
} else {
|
||||
model.uncheck(d);
|
||||
}
|
||||
treeViewer.refresh();
|
||||
updateStatus();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
treeViewer.setInput(model);
|
||||
|
||||
Composite right = new Composite(form, SWT.NONE);
|
||||
right.setLayout(FillLayoutFactory.fillDefaults().extendedMargins(MARGIN, MARGIN).create());
|
||||
Browser docViewer = new Browser(right, SWT.NONE);
|
||||
docViewer.setJavascriptEnabled(false);
|
||||
|
||||
Display display= parent.getDisplay();
|
||||
docViewer.setForeground(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND));
|
||||
docViewer.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
|
||||
|
||||
// Cancel opening of new windows
|
||||
docViewer.addOpenWindowListener(event -> event.required= true);
|
||||
|
||||
// Replace browser's built-in context menu with none
|
||||
docViewer.setMenu(new Menu(getShell(), SWT.NONE));
|
||||
|
||||
docViewer.setText(wrapHtml("Select a Recipe on the left to read description"));
|
||||
|
||||
|
||||
treeViewer.addSelectionChangedListener(new ISelectionChangedListener() {
|
||||
|
||||
@Override
|
||||
public void selectionChanged(SelectionChangedEvent event) {
|
||||
Object o = event.getStructuredSelection().getFirstElement();
|
||||
if (o instanceof RecipeDescriptor) {
|
||||
docViewer.setText(wrapHtml(buildHtmlDescriptionSnippet((RecipeDescriptor) o)));
|
||||
} else {
|
||||
docViewer.setText(wrapHtml("Select a Recipe on the left to read description"));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
form.setWeights(new int[] { 50, 50 });
|
||||
|
||||
return form;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected Control createContents(Composite parent) {
|
||||
Control c = super.createContents(parent);
|
||||
setTitle("Select Rewrite Recipe");
|
||||
parent.getDisplay().asyncExec(() -> updateStatus());
|
||||
return c;
|
||||
}
|
||||
|
||||
private void updateStatus() {
|
||||
boolean anythingSelected = Arrays.stream(model.getRecipeDescriptors()).anyMatch(d -> model.getCheckedState(d) != CheckedState.UNCHECKED);
|
||||
updateStatus(anythingSelected ? Status.info(SELECT_REWRITE_RECIPE_S_FROM_THE_LIST) : Status.error(SELECT_REWRITE_RECIPE_S_FROM_THE_LIST));
|
||||
}
|
||||
|
||||
private String buildHtmlDescriptionSnippet(RecipeDescriptor r) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("<p>");
|
||||
sb.append(r.description);
|
||||
sb.append("</p>");
|
||||
sb.append("<ul>");
|
||||
for (OptionDescriptor option : r.options) {
|
||||
if (option.value != null) {
|
||||
sb.append("<li>");
|
||||
sb.append("<pre>");
|
||||
sb.append(option.value);
|
||||
sb.append("</pre>");
|
||||
sb.append(option.description);
|
||||
sb.append("</li>");
|
||||
}
|
||||
}
|
||||
sb.append("</ul>");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static String wrapHtml(String html) {
|
||||
/*
|
||||
* No JDT content. Means no JDT CSS part either. Therefore add JDT CSS chunk to it.
|
||||
*/
|
||||
ColorRegistry registry = JFaceResources.getColorRegistry();
|
||||
RGB fgRGB = registry.getRGB("org.eclipse.jdt.ui.Javadoc.foregroundColor"); //$NON-NLS-1$
|
||||
RGB bgRGB= registry.getRGB("org.eclipse.jdt.ui.Javadoc.backgroundColor"); //$NON-NLS-1$
|
||||
|
||||
StringBuilder buffer = new StringBuilder(html);
|
||||
HTMLPrinter.insertPageProlog(buffer, 0, fgRGB, bgRGB, getStyleSheet());
|
||||
HTMLPrinter.addPageEpilog(buffer);
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
protected IDialogSettings getDialogBoundsSettings() {
|
||||
String sectionName= getClass().getName() + "_dialogBounds"; //$NON-NLS-1$
|
||||
IDialogSettings settings = PlatformUI
|
||||
.getDialogSettingsProvider(FrameworkUtil.getBundle(getClass())).getDialogSettings();
|
||||
IDialogSettings section= settings.getSection(sectionName);
|
||||
if (section == null)
|
||||
section= settings.addNewSection(sectionName);
|
||||
return section;
|
||||
}
|
||||
|
||||
/**
|
||||
* Taken from {@link JavadocHover}. It's <code>private</code>. See {@link JavadocHover#getStyleSheet()}.
|
||||
* @return CSS as string
|
||||
*/
|
||||
private static String getStyleSheet() {
|
||||
if (fgStyleSheet == null) {
|
||||
fgStyleSheet= JavadocHover.loadStyleSheet("/JavadocHoverStyleSheet.css"); //$NON-NLS-1$
|
||||
}
|
||||
String css= fgStyleSheet;
|
||||
if (css != null) {
|
||||
FontData fontData= JFaceResources.getFontRegistry().getFontData(PreferenceConstants.APPEARANCE_JAVADOC_FONT)[0];
|
||||
css= HTMLPrinter.convertTopLevelFont(css, fontData);
|
||||
}
|
||||
return css;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2022 VMware, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* VMware, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.tooling.boot.ls.commands;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.eclipse.core.commands.AbstractHandler;
|
||||
import org.eclipse.core.commands.ExecutionEvent;
|
||||
import org.eclipse.core.commands.ExecutionException;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
import org.eclipse.core.runtime.IAdaptable;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jface.viewers.IStructuredSelection;
|
||||
import org.eclipse.lsp4e.LanguageServiceAccessor;
|
||||
import org.eclipse.lsp4j.ExecuteCommandParams;
|
||||
import org.eclipse.lsp4j.services.LanguageServer;
|
||||
import org.eclipse.ui.handlers.HandlerUtil;
|
||||
import org.springsource.ide.eclipse.commons.livexp.util.Log;
|
||||
|
||||
public class UpgradeToBoot3Handler extends AbstractHandler {
|
||||
|
||||
private static final String UPGRADE_TO_BOOT_3_COMMAND_ID = "sts/rewrite/recipe/org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_0";
|
||||
|
||||
@Override
|
||||
public Object execute(ExecutionEvent event) throws ExecutionException {
|
||||
|
||||
IStructuredSelection selection = HandlerUtil.getCurrentStructuredSelection(event);
|
||||
Object o = selection.getFirstElement();
|
||||
IProject project = null;
|
||||
if (o instanceof IResource) {
|
||||
project = ((IResource) o).getProject();
|
||||
} else if (o instanceof IProject) {
|
||||
project = (IProject) o;
|
||||
} else if (o instanceof IAdaptable) {
|
||||
project = ((IAdaptable) o).getAdapter(IProject.class);
|
||||
}
|
||||
if (project != null) {
|
||||
List<@NonNull LanguageServer> usedLanguageServers = LanguageServiceAccessor
|
||||
.getActiveLanguageServers(serverCapabilities -> true);
|
||||
|
||||
if (!usedLanguageServers.isEmpty()) {
|
||||
ExecuteCommandParams commandParams = new ExecuteCommandParams();
|
||||
commandParams.setCommand(UPGRADE_TO_BOOT_3_COMMAND_ID);
|
||||
commandParams.setArguments(List.of(project.getLocationURI().toString()));
|
||||
|
||||
try {
|
||||
CompletableFuture.allOf(usedLanguageServers.stream()
|
||||
.map(ls -> ls.getWorkspaceService().executeCommand(commandParams))
|
||||
.toArray(CompletableFuture[]::new));
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
throw new ExecutionException("Failed to perform Upgarde to Spring Boot 3", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user