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