You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by db...@apache.org on 2022/03/18 08:41:59 UTC
[netbeans] branch master updated: LSP: WebView based UI for Move refactoring. (#3798)
This is an automated email from the ASF dual-hosted git repository.
dbalek pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git
The following commit(s) were added to refs/heads/master by this push:
new b40e6fe LSP: WebView based UI for Move refactoring. (#3798)
b40e6fe is described below
commit b40e6fe155b762e90acd3639c7f8f0f6e434476c
Author: Dusan Balek <du...@oracle.com>
AuthorDate: Fri Mar 18 09:41:38 2022 +0100
LSP: WebView based UI for Move refactoring. (#3798)
---
.../lsp/server/protocol/CodeActionsProvider.java | 12 +-
.../lsp/server/refactoring/MoveRefactoring.java | 474 +++++++++++++++------
.../java/lsp/server/refactoring/ui/MoveClass.html | 67 +++
.../lsp/server/refactoring/ui/MoveMembers.html | 96 +++++
.../java/lsp/server/refactoring/ui/refactoring.css | 47 +-
.../java/lsp/server/protocol/ServerTest.java | 30 +-
6 files changed, 584 insertions(+), 142 deletions(-)
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeActionsProvider.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeActionsProvider.java
index 30910a4..c73402c 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeActionsProvider.java
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeActionsProvider.java
@@ -125,8 +125,10 @@ public abstract class CodeActionsProvider {
protected static String createLabel(CompilationInfo info, VariableElement e, boolean fqn) {
StringBuilder sb = new StringBuilder();
sb.append(Utils.label(info, e, fqn));
- sb.append(" : "); // NOI18N
- sb.append(Utils.detail(info, e, fqn));
+ String detail = Utils.detail(info, e, fqn);
+ if (detail != null) {
+ sb.append(detail);
+ }
return sb.toString();
}
@@ -137,8 +139,10 @@ public abstract class CodeActionsProvider {
protected static String createLabel(CompilationInfo info, ExecutableElement e, boolean fqn) {
StringBuilder sb = new StringBuilder();
sb.append(Utils.label(info, e, fqn));
- sb.append(" : "); // NOI18N
- sb.append(Utils.detail(info, e, fqn));
+ String detail = Utils.detail(info, e, fqn);
+ if (detail != null) {
+ sb.append(detail);
+ }
return sb.toString();
}
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/refactoring/MoveRefactoring.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/refactoring/MoveRefactoring.java
index 5554da0..466f92b 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/refactoring/MoveRefactoring.java
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/refactoring/MoveRefactoring.java
@@ -19,28 +19,41 @@
package org.netbeans.modules.java.lsp.server.refactoring;
import com.google.gson.Gson;
+import com.google.gson.JsonSyntaxException;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.Tree;
import com.sun.source.util.SourcePositions;
import com.sun.source.util.Trees;
+import java.io.IOException;
+import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
+import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
-import java.util.function.Consumer;
+import java.util.logging.Level;
+import java.util.stream.Collectors;
import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
+import net.java.html.json.ComputedProperty;
+import net.java.html.json.Function;
+import net.java.html.json.Model;
+import net.java.html.json.ModelOperation;
+import net.java.html.json.Property;
import org.eclipse.lsp4j.ApplyWorkspaceEditParams;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.CodeActionKind;
import org.eclipse.lsp4j.CodeActionParams;
import org.eclipse.lsp4j.MessageParams;
import org.eclipse.lsp4j.MessageType;
+import org.netbeans.api.htmlui.HTMLDialog;
import org.netbeans.api.java.classpath.ClassPath;
import org.netbeans.api.java.project.JavaProjectConstants;
import org.netbeans.api.java.source.ClassIndex;
@@ -48,23 +61,31 @@ import org.netbeans.api.java.source.ClasspathInfo;
import org.netbeans.api.java.source.CompilationController;
import org.netbeans.api.java.source.CompilationInfo;
import org.netbeans.api.java.source.ElementHandle;
+import org.netbeans.api.java.source.ElementUtilities;
import org.netbeans.api.java.source.JavaSource;
import org.netbeans.api.java.source.TreePathHandle;
import org.netbeans.api.project.FileOwnerQuery;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.ProjectUtils;
import org.netbeans.api.project.SourceGroup;
+import org.netbeans.api.project.Sources;
+import org.netbeans.api.project.ui.OpenProjects;
import org.netbeans.modules.java.lsp.server.Utils;
import org.netbeans.modules.java.lsp.server.protocol.CodeActionsProvider;
import org.netbeans.modules.java.lsp.server.protocol.NbCodeLanguageClient;
-import org.netbeans.modules.java.lsp.server.protocol.QuickPickItem;
-import org.netbeans.modules.java.lsp.server.protocol.ShowQuickPickParams;
+import org.netbeans.modules.java.source.ElementHandleAccessor;
import org.netbeans.modules.parsing.api.ResultIterator;
import org.netbeans.modules.refactoring.java.api.JavaMoveMembersProperties;
import org.netbeans.modules.refactoring.java.api.JavaRefactoringUtils;
import org.netbeans.spi.java.classpath.support.ClassPathSupport;
import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.util.Exceptions;
+import org.openide.util.Lookup;
import org.openide.util.NbBundle;
+import org.openide.util.RequestProcessor;
+import org.openide.util.lookup.AbstractLookup;
+import org.openide.util.lookup.InstanceContent;
import org.openide.util.lookup.Lookups;
import org.openide.util.lookup.ServiceProvider;
@@ -100,9 +121,7 @@ public final class MoveRefactoring extends CodeRefactoring {
String uri = Utils.toUri(info.getFileObject());
Element element = elementForOffset(info, offset);
if (element != null) {
- QuickPickItem elementItem = new QuickPickItem(createLabel(info, element));
- elementItem.setUserData(new ElementData(element));
- return Collections.singletonList(createCodeAction(Bundle.DN_Move(), MOVE_REFACTORING_KIND, null, MOVE_REFACTORING_COMMAND, uri, elementItem));
+ return Collections.singletonList(createCodeAction(Bundle.DN_Move(), MOVE_REFACTORING_KIND, null, MOVE_REFACTORING_COMMAND, uri, new ElementData(element)));
} else {
return Collections.singletonList(createCodeAction(Bundle.DN_Move(), MOVE_REFACTORING_KIND, null, MOVE_REFACTORING_COMMAND, uri));
}
@@ -116,101 +135,354 @@ public final class MoveRefactoring extends CodeRefactoring {
@Override
@NbBundle.Messages({
"DN_DefaultPackage=<default package>",
- "DN_SelectTargetPackage=Select target package",
"DN_CreateNewClass=<create new class>",
- "DN_SelectTargetClass=Select target class",
})
public CompletableFuture<Object> processCommand(NbCodeLanguageClient client, String command, List<Object> arguments) {
try {
if (arguments.size() > 0) {
String uri = gson.fromJson(gson.toJson(arguments.get(0)), String.class);
- QuickPickItem elementItem = arguments.size() > 1 ? gson.fromJson(gson.toJson(arguments.get(1)), QuickPickItem.class) : null;
FileObject file = Utils.fromUri(uri);
- Project project = FileOwnerQuery.getOwner(file);
- HashSet<QuickPickItem> items = new HashSet<>();
- if (project != null) {
- for(SourceGroup sourceGroup : ProjectUtils.getSources(project).getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA)) {
- String name = sourceGroup.getDisplayName();
- FileObject rootFolder = sourceGroup.getRootFolder();
- if (elementItem == null) {
- items.add(new QuickPickItem(Bundle.DN_DefaultPackage(), name, null, false, Utils.toUri(rootFolder)));
- }
- for (String packageName : ClasspathInfo.create(rootFolder).getClassIndex().getPackageNames("", false, EnumSet.of(ClassIndex.SearchScope.SOURCE))) {
- if (elementItem == null) {
- String pkg = "";
- for (String part : packageName.split("\\.")) {
- if (!part.isEmpty()) {
- pkg += pkg.length() == 0 ? part : "." + part;
- items.add(new QuickPickItem(pkg, name, null, false, Utils.toUri(rootFolder.getFileObject(pkg.replace('.', '/')))));
+ JavaSource js = JavaSource.forFileObject(file);
+ if (js != null) {
+ return CompletableFuture.supplyAsync(() -> {
+ try {
+ js.runUserActionTask(ci -> {
+ ci.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
+ if (arguments.size() > 1) {
+ Element element = gson.fromJson(gson.toJson(arguments.get(1)), ElementData.class).resolve(ci);
+ if (element != null) {
+ if (element.getKind().isClass() || element.getKind().isInterface()) {
+ Pages.showMoveClassUI(ci, client, file, element);
+ } else {
+ Pages.showMoveMembersUI(ci, client, file, element);
+ }
}
+ } else {
+ Pages.showMoveClassUI(ci, client, file, null);
}
- } else {
- items.add(new QuickPickItem(packageName, name, null, false, Utils.toUri(rootFolder.getFileObject(packageName.replace('.', '/')))));
- }
+ }, true);
+ return null;
+ } catch (IOException ex) {
+ throw new IllegalStateException(ex);
+ }
+ }, RequestProcessor.getDefault());
+ }
+ } else {
+ throw new IllegalArgumentException(String.format("Illegal number of arguments received for command: %s", command));
+ }
+ } catch (JsonSyntaxException | IllegalArgumentException | MalformedURLException ex) {
+ client.showMessage(new MessageParams(MessageType.Error, ex.getLocalizedMessage()));
+ }
+ return CompletableFuture.completedFuture(true);
+ }
+
+ @HTMLDialog(url = "ui/MoveClass.html")
+ static HTMLDialog.OnSubmit showMoveClassUI(
+ CompilationController ci,
+ NbCodeLanguageClient client,
+ FileObject file,
+ Element element
+ ) {
+ MoveElementUI model = new MoveElementUI();
+ model.withMoveClass(true)
+ .withFrom(file.getName())
+ .assignData(client, file, element != null ? TreePathHandle.create(element, ci) : null);
+ model.applyBindings();
+ return (id) -> {
+ if ("accept".equals(id)) {
+ model.doRefactoring();
+ }
+ return true; // return false, if validation fails
+ };
+ }
+
+ @HTMLDialog(url = "ui/MoveMembers.html")
+ static HTMLDialog.OnSubmit showMoveMembersUI(
+ CompilationController ci,
+ NbCodeLanguageClient client,
+ FileObject file,
+ Element element
+ ) {
+ ElementUtilities eu = ci.getElementUtilities();
+ Element enclosingElement = element.getEnclosingElement();
+ String parentName = createLabel(ci, enclosingElement);
+ ElementUI[] members = enclosingElement.getEnclosedElements().stream()
+ .filter(memberElement -> (memberElement.getKind().isField() || memberElement.getKind() == ElementKind.METHOD) && !eu.isSynthetic(memberElement))
+ .map(memberElement -> {
+ String label = createLabel(ci, memberElement);
+ ElementData data = new ElementData(memberElement);
+ ElementUI memberElementUI = new ElementUI(memberElement == element, label, memberElement.getKind().name(), data.getSignature());
+ return memberElementUI;
+ }).toArray(ElementUI[]::new);
+ MoveElementUI model = new MoveElementUI();
+ model.withMoveClass(false)
+ .withFrom(parentName)
+ .withMembers(members)
+ .withKeepMethodSelected(false)
+ .withDeprecateMethodSelected(true)
+ .assignData(client, file, TreePathHandle.create(element, ci));
+ model.applyBindings();
+ return (id) -> {
+ if ("accept".equals(id)) {
+ model.doRefactoring();
+ }
+ return true; // return false, if validation fails
+ };
+ }
+
+ @Model(className = "MoveElementUI", targetId = "", instance = true, builder = "with", properties = {
+ @Property(name = "moveClass", type = boolean.class),
+ @Property(name = "from", type = String.class),
+ @Property(name = "selectedProject", type = NamedPath.class),
+ @Property(name = "selectedRoot", type = NamedPath.class),
+ @Property(name = "selectedPackage", type = String.class),
+ @Property(name = "selectedClass", type = ElementUI.class),
+ @Property(name = "selectedVisibility", type = Visibility.class),
+ @Property(name = "selectedJavaDoc", type = JavaDoc.class),
+ @Property(name = "members", type = ElementUI.class, array = true),
+ @Property(name = "keepMethodSelected", type = boolean.class),
+ @Property(name = "deprecateMethodSelected", type = boolean.class)
+ })
+ static final class MoveElementControl {
+
+ private NbCodeLanguageClient client;
+ private FileObject file;
+ private TreePathHandle handle;
+
+ @ModelOperation
+ void assignData(MoveElementUI ui, NbCodeLanguageClient client, FileObject file, TreePathHandle handle) {
+ this.client = client;
+ this.file = file;
+ this.handle = handle;
+ }
+
+ @ComputedProperty
+ static List<NamedPath> availableProjects() {
+ Project[] openProjects = OpenProjects.getDefault().getOpenProjects();
+ List<NamedPath> projectNames = new ArrayList<>(openProjects.length);
+ for (int i = 0; i < openProjects.length; i++) {
+ projectNames.add(new NamedPath(ProjectUtils.getInformation(openProjects[i]).getDisplayName(), Utils.toUri(openProjects[i].getProjectDirectory())));
+ }
+ return projectNames;
+ }
+
+ @ComputedProperty
+ static List<NamedPath> availableRoots(NamedPath selectedProject) {
+ Project project = getSelectedProject(selectedProject);
+ if (project != null) {
+ Sources sources = ProjectUtils.getSources(project);
+ SourceGroup[] groups = sources.getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA);
+ List<NamedPath> projectRoots = new ArrayList<>(groups.length);
+ for (int i = 0; i < groups.length; i++) {
+ projectRoots.add(new NamedPath(groups[i].getDisplayName(), Utils.toUri(groups[i].getRootFolder())));
+ }
+ return projectRoots;
+ }
+ return Collections.emptyList();
+ }
+
+ @ComputedProperty
+ static List<String> availablePackages(boolean moveClass, NamedPath selectedRoot) {
+ FileObject rootFolder = getSelectedRoot(selectedRoot);
+ if (rootFolder != null) {
+ List<String> packages;
+ if (moveClass) {
+ packages = new ArrayList<>();
+ packages.add(Bundle.DN_DefaultPackage());
+ Enumeration<? extends FileObject> children = rootFolder.getChildren(true);
+ while (children.hasMoreElements()) {
+ FileObject child = children.nextElement();
+ if (child.isFolder()) {
+ packages.add(FileUtil.getRelativePath(rootFolder, child).replace('/', '.'));
}
}
+ } else {
+ packages = ClasspathInfo.create(rootFolder).getClassIndex().getPackageNames("", false, EnumSet.of(ClassIndex.SearchScope.SOURCE)).stream().collect(Collectors.toList());
}
- ArrayList<QuickPickItem> packages = new ArrayList<>(items);
- Collections.sort(packages, (item1, item2) -> {
- int i = item1.getDescription().compareTo(item2.getDescription());
- return i == 0 ? item1.getLabel().compareTo(item2.getLabel()) : i;
+ packages.sort((s1, s2) -> s1.compareTo(s2));
+ return packages;
+ }
+ return Collections.emptyList();
+ }
+
+ @ComputedProperty
+ static List<ElementUI> availableClasses(boolean moveClass, NamedPath selectedRoot, String selectedPackage) {
+ FileObject rootFolder = getSelectedRoot(selectedRoot);
+ if (rootFolder != null && selectedPackage != null) {
+ FileObject fo = rootFolder.getFileObject(selectedPackage.replace('.', '/'));
+ ClassPath sourcePath = ClassPath.getClassPath(fo, ClassPath.SOURCE);
+ final ClasspathInfo info = ClasspathInfo.create(EMPTY_PATH, EMPTY_PATH, sourcePath);
+ Set<ClassIndex.SearchScopeType> searchScopeType = new HashSet<>(1);
+ final Set<String> packageSet = Collections.singleton(selectedPackage);
+ searchScopeType.add(new ClassIndex.SearchScopeType() {
+ @Override
+ public Set<? extends String> getPackages() {
+ return packageSet;
+ }
+
+ @Override
+ public boolean isSources() {
+ return true;
+ }
+
+ @Override
+ public boolean isDependencies() {
+ return false;
+ }
});
- Consumer<List<QuickPickItem>> f = selectedPackage -> {
- if (selectedPackage != null && !selectedPackage.isEmpty()) {
- ClasspathInfo info = ClasspathInfo.create(file);
- TreePathHandle tph = elementItem != null ? TreePathHandle.from(gson.fromJson(gson.toJson(elementItem.getUserData()), ElementData.class).toHandle(), info) : null;
- List<QuickPickItem> classes = packageClasses(selectedPackage.get(0), tph == null || tph.getKind() == Tree.Kind.CLASS);
- if (classes.isEmpty()) {
- if (tph == null) {
- move(client, uri, selectedPackage.get(0), ClasspathInfo.create(file));
- } else {
- throw new IllegalArgumentException(String.format("No target class found in selected package"));
- }
+ List<ElementUI> ret = new ArrayList<>();
+ if (moveClass) {
+ ret.add(new ElementUI(false, Bundle.DN_CreateNewClass(), null));
+ }
+ for (ElementHandle<TypeElement> eh : info.getClassIndex().getDeclaredTypes("", ClassIndex.NameKind.PREFIX, searchScopeType)) {
+ ElementData data = new ElementData(eh);
+ String shortName = eh.getQualifiedName().substring(selectedPackage.length() + 1);
+ int idx = shortName.indexOf('.');
+ if (fo.getFileObject(idx < 0 ? shortName : shortName.substring(0, idx), "java") != null) {
+ ret.add(new ElementUI(false, shortName, data.getKind(), data.getSignature()));
+ }
+ }
+ ret.sort((e1, e2) -> e1.getLabel().compareTo(e2.getLabel()));
+ return ret;
+ }
+ return Collections.emptyList();
+ }
+
+ @ComputedProperty
+ static List<Visibility> availableVisibilities() {
+ return Arrays.asList(Visibility.values());
+ }
+
+ @ComputedProperty
+ static List<JavaDoc> availableJavaDoc() {
+ return Arrays.asList(JavaDoc.values());
+ }
+
+ @ModelOperation
+ @Function
+ void doRefactoring(MoveElementUI ui) {
+ try {
+ org.netbeans.modules.refactoring.api.MoveRefactoring refactoring;
+ if (handle == null) {
+ refactoring = new org.netbeans.modules.refactoring.api.MoveRefactoring(Lookups.fixed(file));
+ refactoring.getContext().add(JavaRefactoringUtils.getClasspathInfoFor(file));
+ } else {
+ InstanceContent ic = new InstanceContent();
+ refactoring = new org.netbeans.modules.refactoring.api.MoveRefactoring(new AbstractLookup(ic));
+ List<TreePathHandle> selectedElements = ui.getMembers().stream().filter(member -> member.isSelected()).map(selectedMember -> {
+ ElementHandle memberHandle = ElementHandleAccessor.getInstance().create(ElementKind.valueOf(selectedMember.getKind()), selectedMember.getSignature().toArray(new String[selectedMember.getSignature().size()]));
+ return TreePathHandle.from(memberHandle, ClasspathInfo.create(file));
+ }).collect(Collectors.toList());
+ ic.set(selectedElements, null);
+ if (handle.getKind() == Tree.Kind.CLASS) {
+ refactoring.getContext().add(JavaRefactoringUtils.getClasspathInfoFor(handle.getFileObject()));
+ } else {
+ JavaMoveMembersProperties properties = new JavaMoveMembersProperties(selectedElements.toArray(new TreePathHandle[selectedElements.size()]));
+ properties.setVisibility(JavaMoveMembersProperties.Visibility.valueOf(ui.getSelectedVisibility().name()));
+ properties.setDelegate(ui.isKeepMethodSelected());
+ properties.setUpdateJavaDoc(ui.getSelectedJavaDoc() == JavaDoc.UPDATE);
+ properties.setAddDeprecated(ui.isDeprecateMethodSelected());
+ refactoring.getContext().add(properties);
+ }
+ }
+ ElementUI selectedClass = ui.getSelectedClass();
+ if (selectedClass != null) {
+ if (selectedClass.getKind() != null && selectedClass.getSignature() != null) {
+ ElementHandle eh = ElementHandleAccessor.getInstance().create(ElementKind.valueOf(selectedClass.getKind()), selectedClass.getSignature().toArray(new String[selectedClass.getSignature().size()]));
+ refactoring.setTarget(Lookups.singleton(TreePathHandle.from(eh, ClasspathInfo.create(file))));
+ } else {
+ FileObject rootFolder = getSelectedRoot(ui.getSelectedRoot());
+ if (rootFolder != null && ui.getSelectedPackage()!= null) {
+ refactoring.setTarget(Lookups.singleton(rootFolder.getFileObject(ui.getSelectedPackage().replace('.', '/')).toURL()));
} else {
- client.showQuickPick(new ShowQuickPickParams(Bundle.DN_SelectTargetClass(), false, classes)).thenAccept(selectedClass -> {
- if (selectedClass != null && !selectedClass.isEmpty()) {
- QuickPickItem selected = Bundle.DN_CreateNewClass().equals(selectedClass.get(0).getLabel()) ? selectedPackage.get(0) : selectedClass.get(0);
- move(client, tph != null ? tph : uri, selected, info);
- }
- });
+ refactoring.setTarget(Lookup.EMPTY);
}
}
- };
- if (packages.size() == 1) {
- f.accept(packages);
} else {
- client.showQuickPick(new ShowQuickPickParams(Bundle.DN_SelectTargetPackage(), false, packages)).thenAccept(f);
+ refactoring.setTarget(Lookup.EMPTY);
+ }
+ client.applyEdit(new ApplyWorkspaceEditParams(perform(refactoring, "Move")));
+ } catch (Exception ex) {
+ if (client == null) {
+ Exceptions.printStackTrace(
+ Exceptions.attachSeverity(ex, Level.SEVERE)
+ );
+ } else {
+ client.showMessage(new MessageParams(MessageType.Error, ex.getLocalizedMessage()));
}
- } else {
- throw new IllegalArgumentException(String.format("Illegal number of arguments received for command: %s", command));
}
- } catch (Exception ex) {
- client.showMessage(new MessageParams(MessageType.Error, ex.getLocalizedMessage()));
}
- return CompletableFuture.completedFuture(true);
- }
- private void move(NbCodeLanguageClient client, Object source, QuickPickItem target, ClasspathInfo info) {
- try {
- org.netbeans.modules.refactoring.api.MoveRefactoring refactoring;
- if (source instanceof String) {
- FileObject file = Utils.fromUri((String) source);
- refactoring = new org.netbeans.modules.refactoring.api.MoveRefactoring(Lookups.fixed(file));
- refactoring.getContext().add(JavaRefactoringUtils.getClasspathInfoFor(file));
- } else {
- TreePathHandle tph = (TreePathHandle) source;
- refactoring = new org.netbeans.modules.refactoring.api.MoveRefactoring(Lookups.fixed(tph));
- refactoring.getContext().add(tph.getKind() == Tree.Kind.CLASS ? JavaRefactoringUtils.getClasspathInfoFor(tph.getFileObject()) : new JavaMoveMembersProperties(tph));
+ private static Project getSelectedProject(NamedPath selectedProject) {
+ try {
+ String path = selectedProject.getPath();
+ return path != null ? FileOwnerQuery.getOwner(Utils.fromUri(path)) : null;
+ } catch (MalformedURLException ex) {
+ return null;
}
- if (target.getDescription() != null) {
- refactoring.setTarget(Lookups.singleton(new URL((String) target.getUserData())));
- } else {
- ElementHandle handle = gson.fromJson(gson.toJson(target.getUserData()), ElementData.class).toHandle();
- refactoring.setTarget(Lookups.singleton(TreePathHandle.from(handle, info)));
+ }
+
+ private static FileObject getSelectedRoot(NamedPath selectedRoot) {
+ try {
+ String path = selectedRoot.getPath();
+ return path != null ? Utils.fromUri(path) : null;
+ } catch (MalformedURLException ex) {
+ return null;
}
- client.applyEdit(new ApplyWorkspaceEditParams(perform(refactoring, "Move")));
- } catch (Exception ex) {
- client.showMessage(new MessageParams(MessageType.Error, ex.getLocalizedMessage()));
+ }
+ }
+
+ @Model(className = "ElementUI", instance = true, properties = {
+ @Property(name = "selected", type = boolean.class),
+ @Property(name = "label", type = String.class),
+ @Property(name = "kind", type = String.class),
+ @Property(name = "signature", type = String.class, array = true)
+ })
+ static final class ElementControl {
+ }
+
+ @Model(className = "NamedPath", instance = true, properties = {
+ @Property(name = "name", type = String.class),
+ @Property(name = "path", type = String.class)
+ })
+ static final class NamedPathControl {
+ }
+
+ public static enum Visibility {
+
+ ESCALATE("Escalate"),
+ ASIS("As is"),
+ PUBLIC("Public"),
+ PROTECTED("Protected"),
+ PACKAGE_PRIVATE("Package private"),
+ PRIVATE("Private");
+
+ final String humanName;
+
+ Visibility(String humanName) {
+ this.humanName = humanName;
+ }
+
+ @Override
+ public String toString() {
+ return humanName;
+ }
+ }
+
+ public static enum JavaDoc {
+
+ ASIS("As is"),
+ UPDATE("Update");
+
+ final String humanName;
+
+ JavaDoc(String humanName) {
+ this.humanName = humanName;
+ }
+
+ @Override
+ public String toString() {
+ return humanName;
}
}
@@ -237,50 +509,4 @@ public final class MoveRefactoring extends CodeRefactoring {
}
return null;
}
-
- private static List<QuickPickItem> packageClasses(QuickPickItem targetPackage, boolean proposeNew) {
- try {
- FileObject fo = Utils.fromUri((String) targetPackage.getUserData());
- ClassPath sourcePath = ClassPath.getClassPath(fo, ClassPath.SOURCE);
- final ClasspathInfo info = ClasspathInfo.create(EMPTY_PATH, EMPTY_PATH, sourcePath);
- Set<ClassIndex.SearchScopeType> searchScopeType = new HashSet<>(1);
- String packageName = Bundle.DN_DefaultPackage().equals(targetPackage.getLabel()) ? "" : targetPackage.getLabel();
- final Set<String> packageSet = Collections.singleton(packageName);
- searchScopeType.add(new ClassIndex.SearchScopeType() {
- @Override
- public Set<? extends String> getPackages() {
- return packageSet;
- }
-
- @Override
- public boolean isSources() {
- return true;
- }
-
- @Override
- public boolean isDependencies() {
- return false;
- }
- });
- final Set<ElementHandle<TypeElement>> result = info.getClassIndex().getDeclaredTypes("", ClassIndex.NameKind.PREFIX, searchScopeType);
- if (result != null && !result.isEmpty()) {
- List<QuickPickItem> ret = new ArrayList<>(result.size() + 1);
- if (proposeNew) {
- ret.add(new QuickPickItem(Bundle.DN_CreateNewClass()));
- }
- for (ElementHandle<TypeElement> elementHandle : result) {
- String qualifiedName = elementHandle.getQualifiedName();
- if (qualifiedName.startsWith(packageName)) {
- String shortName = qualifiedName.substring(packageName.length() + 1);
- int idx = shortName.indexOf('.');
- if (fo.getFileObject(idx < 0 ? shortName : shortName.substring(0, idx), "java") != null) {
- ret.add(new QuickPickItem(shortName, null, null, false, new ElementData(elementHandle)));
- }
- }
- }
- return ret;
- }
- } catch (Exception ex) {}
- return Collections.emptyList();
- }
}
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/refactoring/ui/MoveClass.html b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/refactoring/ui/MoveClass.html
new file mode 100644
index 0000000..c6f45c7
--- /dev/null
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/refactoring/ui/MoveClass.html
@@ -0,0 +1,67 @@
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+-->
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+ <meta charset="UTF-8">
+ <meta http-equiv="Content-Security-Policy" content="default-src http: 'unsafe-inline' 'unsafe-eval'">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <link rel="stylesheet" href="refactoring.css">
+ <link rel="stylesheet" href="codicon.css">
+ <title>Refactor: Move Class...</title>
+</head>
+
+<body>
+ <div class="section">
+ <label>Move class <span class="vscode-editor-font" data-bind="text: from"></span> to:</label>
+ </div>
+ <div class="section3">
+ <div class="flex vdivider2">
+ <div class="flex-equal">
+ <label>Project:</label>
+ <select data-bind="options: availableProjects, optionsText: 'name', value: selectedProject"></select>
+ </div>
+ <div class="flex-equal hdivider">
+ <label>Location:</label>
+ <select data-bind="options: availableRoots, optionsText: 'name', value: selectedRoot">
+ </select>
+ </div>
+ </div>
+ <div class="flex vdivider">
+ <div class="flex-equal">
+ <label>Package:</label>
+ <select data-bind="options: availablePackages, value: selectedPackage">
+ </select>
+ </div>
+ <div class="flex-equal hdivider">
+ <label>Class:</label>
+ <select data-bind="options: availableClasses, optionsText: 'label', value: selectedClass">
+ </select>
+ </div>
+ </div>
+ </div>
+ <div class="flex section">
+ <button id="accept" hidden class="regular-button vscode-font align-right">Refactor</button>
+ <button id="reject" hidden class="regular-button vscode-font">Cancel</button>
+ </div>
+</body>
+</html>
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/refactoring/ui/MoveMembers.html b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/refactoring/ui/MoveMembers.html
new file mode 100644
index 0000000..fdee365
--- /dev/null
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/refactoring/ui/MoveMembers.html
@@ -0,0 +1,96 @@
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+-->
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+ <meta charset="UTF-8">
+ <meta http-equiv="Content-Security-Policy" content="default-src http: 'unsafe-inline' 'unsafe-eval'">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <link rel="stylesheet" href="refactoring.css">
+ <link rel="stylesheet" href="codicon.css">
+ <title>Refactor: Move Members...</title>
+</head>
+
+<body>
+ <div class="section">
+ <label>Move members from <span class="vscode-editor-font" data-bind="text: from"></span> to:</label>
+ </div>
+ <div class="section3">
+ <div class="flex vdivider2">
+ <div class="flex-equal">
+ <label>Project:</label>
+ <select data-bind="options: availableProjects, optionsText: 'name', value: selectedProject"></select>
+ </div>
+ <div class="flex-equal hdivider">
+ <label>Location:</label>
+ <select data-bind="options: availableRoots, optionsText: 'name', value: selectedRoot">
+ </select>
+ </div>
+ </div>
+ <div class="flex vdivider">
+ <div class="flex-equal">
+ <label>Package:</label>
+ <select data-bind="options: availablePackages, value: selectedPackage">
+ </select>
+ </div>
+ <div class="flex-equal hdivider">
+ <label>Class:</label>
+ <select data-bind="options: availableClasses, optionsText: 'label', value: selectedClass">
+ </select>
+ </div>
+ </div>
+ <div class="flex vdivider">
+ <div class="flex-equal">
+ <label>Visibility:</label>
+ <select data-bind="options: availableVisibilities, value: selectedVisibility">
+ </select>
+ </div>
+ <div class="flex-equal hdivider">
+ <label>JavaDoc:</label>
+ <select data-bind="options: availableJavaDoc, value: selectedJavaDoc">
+ </select>
+ </div>
+ </div>
+ <label>Members to be moved:</label>
+ </div>
+ <div class="flex-vscrollable section1">
+ <nobr data-bind="foreach: members">
+ <div class="flex row">
+ <input type="checkbox" data-bind="checked: selected, attr: {id: 'member_' + $index()}">
+ <label class="checkbox-label vscode-editor-font" data-bind="text: label, attr: {for: 'member_' + $index()}"></label>
+ </div>
+ </nobr>
+ </div>
+ <div class="flex section">
+ <input type="checkbox" id="keep_original" data-bind="checked: keepMethodSelected">
+ <label class="checkbox-label" for="keep_original">Keep original method(s) and delegate to the moved method</label>
+ </div>
+ <div class="flex section-nested">
+ <input type="checkbox" id="deprecate_old" data-bind="checked: deprecateMethodSelected, enable: keepMethodSelected">
+ <label class="checkbox-label" for="deprecate_old">Deprecate the old method(s)</label>
+ </div>
+ <div class="flex section">
+ <button id="accept" hidden class="regular-button vscode-font align-right">Refactor</button>
+ <button id="reject" hidden class="regular-button vscode-font">Cancel</button>
+ </div>
+</body>
+</html>
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/refactoring/ui/refactoring.css b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/refactoring/ui/refactoring.css
index 5c64bf4..50c9fec 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/refactoring/ui/refactoring.css
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/refactoring/ui/refactoring.css
@@ -29,7 +29,7 @@ body {
flex-direction: column;
overflow-x: scroll;
}
-input, select {
+input[type="text"], select {
width: 100%;
font-family: var(--vscode-editor-font-family) !important;
border: var(--vscode-input-border);
@@ -41,9 +41,23 @@ input, select {
input:focus, select:focus {
outline-color: var(--vscode-focusBorder);
}
-input {
+input[type="text"] {
padding: 5px 4px !important;
}
+input[type="checkbox"] {
+ width: 1.2em;
+ min-width: 1.2em;
+ height: 1.2em;
+ min-height: 1.2em;
+ cursor: pointer;
+ filter: opacity(80%);
+}
+input[type="checkbox"]:disabled {
+ filter: brightness(90%);
+}
+input[type="checkbox"]:checked {
+ filter: opacity(100%);
+}
select {
padding: 4px !important;
}
@@ -60,6 +74,10 @@ label {
font-size: var(--vscode-font-size);
text-decoration: none;
}
+.vscode-editor-font {
+ font-family: var(--vscode-editor-font-family) !important;
+ /*font-size: var(--vscode-editor-font-size);*/
+}
.button-text {
margin-left: 5px;
vertical-align: text-top;
@@ -111,6 +129,13 @@ label {
background-color: var(--vscode-button-hoverBackground);
outline: none;
}
+.checkbox-label {
+ align-self: center;
+ display: initial;
+ cursor: pointer;
+ margin: 0px;
+ margin-left: 3px;
+}
.params-caption {
margin-bottom: 8px;
}
@@ -134,9 +159,21 @@ label {
padding: 0px 10px;
margin-top: -6px;
}
+.section3 {
+ padding: 0px 20px 0px;
+}
+.section-newline {
+ padding: 0px 20px 0px;
+}
+.section-nested {
+ padding: 5px 40px;
+}
.vdivider {
margin-bottom: 20px;
}
+.vdivider2 {
+ margin-bottom: 6px;
+}
.hdivider {
margin-left: 6px;
}
@@ -156,6 +193,10 @@ label {
flex-grow: 1;
display: block;
}
+.flex-equal {
+ flex: 1 1 0;
+ display: block;
+}
.flex-vscrollable {
overflow-x: hidden;
overflow-y: auto;
@@ -166,4 +207,4 @@ label {
.add-param {
padding: 0px 15px;
padding-top: 2px;
-}
\ No newline at end of file
+}
diff --git a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java
index 160d931..5a5acb1 100644
--- a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java
+++ b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java
@@ -156,6 +156,7 @@ import org.netbeans.junit.NbTestCase;
import org.netbeans.modules.java.hints.infrastructure.JavaErrorProvider;
import org.netbeans.modules.java.lsp.server.TestCodeLanguageClient;
import org.netbeans.modules.java.lsp.server.refactoring.ChangeMethodParameterUI;
+import org.netbeans.modules.java.lsp.server.refactoring.MoveElementUI;
import org.netbeans.modules.java.lsp.server.refactoring.ParameterUI;
import org.netbeans.modules.java.lsp.server.ui.MockHtmlViewer;
import org.netbeans.modules.java.source.BootClassPathUtil;
@@ -3648,9 +3649,14 @@ public class ServerTest extends NbTestCase {
}
@Override
- public CompletableFuture<List<QuickPickItem>> showQuickPick(ShowQuickPickParams params) {
- List<QuickPickItem> items = params.getItems();
- return CompletableFuture.completedFuture("Select target package".equals(params.getPlaceHolder()) ? items.subList(2, 3) : items.subList(0, 1));
+ public CompletableFuture<String> showHtmlPage(HtmlPageParams params) {
+ MoveElementUI ui = MockHtmlViewer.assertDialogShown(params.getUri(), MoveElementUI.class);
+ ui.setSelectedProject(ui.getAvailableProjects().get(0));
+ ui.setSelectedRoot(ui.getAvailableRoots().get(0));
+ ui.setSelectedPackage(ui.getAvailablePackages().get(2));
+ ui.setSelectedClass(ui.getAvailableClasses().get(0));
+ ui.doRefactoring();
+ return CompletableFuture.completedFuture(null);
}
}, client.getInputStream(), client.getOutputStream());
serverLauncher.startListening();
@@ -3758,9 +3764,16 @@ public class ServerTest extends NbTestCase {
}
@Override
- public CompletableFuture<List<QuickPickItem>> showQuickPick(ShowQuickPickParams params) {
- List<QuickPickItem> items = params.getItems();
- return CompletableFuture.completedFuture("Select target package".equals(params.getPlaceHolder()) ? items.subList(1, 2) : items.subList(0, 1));
+ public CompletableFuture<String> showHtmlPage(HtmlPageParams params) {
+ MoveElementUI ui = MockHtmlViewer.assertDialogShown(params.getUri(), MoveElementUI.class);
+ ui.setSelectedProject(ui.getAvailableProjects().get(0));
+ ui.setSelectedRoot(ui.getAvailableRoots().get(0));
+ ui.setSelectedPackage(ui.getAvailablePackages().get(1));
+ ui.setSelectedClass(ui.getAvailableClasses().get(0));
+ ui.setSelectedVisibility(ui.getAvailableVisibilities().get(0));
+ ui.setSelectedJavaDoc(ui.getAvailableJavaDoc().get(0));
+ ui.doRefactoring();
+ return CompletableFuture.completedFuture(null);
}
}, client.getInputStream(), client.getOutputStream());
serverLauncher.startListening();
@@ -4325,11 +4338,6 @@ public class ServerTest extends NbTestCase {
}
@Override
- public CompletableFuture<String> showInputBox(ShowInputBoxParams params) {
- return CompletableFuture.completedFuture("(java.lang.String s, int cnt, boolean b):java.lang.String");
- }
-
- @Override
public CompletableFuture<String> showHtmlPage(HtmlPageParams params) {
ChangeMethodParameterUI ui = MockHtmlViewer.assertDialogShown(params.getUri(), ChangeMethodParameterUI.class);
ui.getParameters().add(1, new ParameterUI("int", "cnt"));
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org
For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists