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 2021/09/17 17:44:45 UTC

[netbeans] branch master updated: LSP: ChangeMethodParameters refactoring added. (#3174)

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 c11231f  LSP: ChangeMethodParameters refactoring added. (#3174)
c11231f is described below

commit c11231f9eadf790b1dbd267e3e958cc9b39e77d8
Author: Dusan Balek <du...@oracle.com>
AuthorDate: Fri Sep 17 19:44:28 2021 +0200

    LSP: ChangeMethodParameters refactoring added. (#3174)
---
 .../ChangeMethodParametersRefactoring.java         | 276 +++++++++++++++++++++
 .../lsp/server/protocol/CodeActionsProvider.java   |  34 ++-
 .../java/lsp/server/protocol/ServerTest.java       | 160 ++++++++++++
 3 files changed, 461 insertions(+), 9 deletions(-)

diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ChangeMethodParametersRefactoring.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ChangeMethodParametersRefactoring.java
new file mode 100644
index 0000000..11c5bce
--- /dev/null
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ChangeMethodParametersRefactoring.java
@@ -0,0 +1,276 @@
+/*
+ * 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.
+ */
+package org.netbeans.modules.java.lsp.server.protocol;
+
+import com.google.gson.Gson;
+import com.sun.source.tree.BlockTree;
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.StatementTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.util.SourcePositions;
+import com.sun.source.util.TreePath;
+import com.sun.source.util.Trees;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.atomic.AtomicReference;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeKind;
+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.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.JavaSource;
+import org.netbeans.api.java.source.TreePathHandle;
+import org.netbeans.api.java.source.TreeUtilities;
+import org.netbeans.modules.editor.java.Utilities;
+import org.netbeans.modules.java.lsp.server.Utils;
+import org.netbeans.modules.parsing.api.ResultIterator;
+import org.netbeans.modules.refactoring.java.api.ChangeParametersRefactoring;
+import org.netbeans.modules.refactoring.java.api.JavaRefactoringUtils;
+import org.openide.filesystems.FileObject;
+import org.openide.util.NbBundle;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+@ServiceProvider(service = CodeActionsProvider.class, position = 200)
+public final class ChangeMethodParametersRefactoring extends CodeRefactoring {
+
+    private static final String CHANGE_METHOD_PARAMS_REFACTORING_KIND = "refactor.change.method.params";
+    private static final String CHANGE_METHOD_PARAMS_REFACTORING_COMMAND =  "java.refactor.change.method.params";
+
+    private final Set<String> commands = Collections.singleton(CHANGE_METHOD_PARAMS_REFACTORING_COMMAND);
+    private final Gson gson = new Gson();
+
+    @Override
+    @NbBundle.Messages({
+        "DN_ChangeMethodParams=Change Method Parameters...",
+    })
+    public List<CodeAction> getCodeActions(ResultIterator resultIterator, CodeActionParams params) throws Exception {
+        List<String> only = params.getContext().getOnly();
+        if (only == null || !only.contains(CodeActionKind.Refactor)) {
+            return Collections.emptyList();
+        }
+        CompilationController info = CompilationController.get(resultIterator.getParserResult());
+        if (info == null || !JavaRefactoringUtils.isRefactorable(info.getFileObject())) {
+            return Collections.emptyList();
+        }
+        info.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
+        int offset = getOffset(info, params.getRange().getStart());
+        String uri = Utils.toUri(info.getFileObject());
+        Trees trees = info.getTrees();
+        TreePath path = info.getTreeUtilities().pathFor(offset);
+        Tree.Kind kind = null;
+        while (path != null && (kind = path.getLeaf().getKind()) != Tree.Kind.METHOD && kind != Tree.Kind.METHOD_INVOCATION && kind != Tree.Kind.NEW_CLASS && kind != Tree.Kind.MEMBER_REFERENCE) {
+            path = path.getParentPath();
+        }
+        if (kind == Tree.Kind.METHOD_INVOCATION || kind == Tree.Kind.NEW_CLASS || kind == Tree.Kind.MEMBER_REFERENCE) {
+            Element element = trees.getElement(path);
+            if (element == null || element.asType().getKind() == TypeKind.ERROR) {
+                return Collections.emptyList();
+            }
+            ExecutableElement method = (ExecutableElement) element;
+            path = info.getTrees().getPath(method);
+        }
+        if (path == null) {
+            return Collections.emptyList();
+        }
+        Element element = trees.getElement(path);
+        if (!(element instanceof ExecutableElement)) {
+            return Collections.emptyList();
+        }
+        QuickPickItem elementItem = new QuickPickItem(createLabel(info, element, true));
+        elementItem.setUserData(new ElementData(element));
+        return Collections.singletonList(createCodeAction(Bundle.DN_ChangeMethodParams(), CHANGE_METHOD_PARAMS_REFACTORING_KIND, CHANGE_METHOD_PARAMS_REFACTORING_COMMAND, uri, elementItem));
+    }
+
+    @Override
+    public Set<String> getCommands() {
+        return commands;
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_ChangeMethodSignature=Change method signature",
+    })
+    public CompletableFuture<Object> processCommand(NbCodeLanguageClient client, String command, List<Object> arguments) {
+        try {
+            if (arguments.size() > 1) {
+                String uri = gson.fromJson(gson.toJson(arguments.get(0)), String.class);
+                QuickPickItem sourceItem = gson.fromJson(gson.toJson(arguments.get(1)), QuickPickItem.class);
+                String label = sourceItem.getLabel();
+                int idx = label.indexOf('(');
+                client.showInputBox(new ShowInputBoxParams(Bundle.DN_ChangeMethodSignature(), label.substring(idx))).thenAccept(signature -> {
+                    if (signature != null && !signature.isEmpty()) {
+                        changeMethodParams(client, uri, sourceItem, signature);
+                    }
+                });
+            } 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 changeMethodParams(NbCodeLanguageClient client, String uri, QuickPickItem source, String signature) {
+        try {
+            FileObject file = Utils.fromUri(uri);
+            ClasspathInfo info = ClasspathInfo.create(file);
+            JavaSource js = JavaSource.forFileObject(file);
+            if (js == null) {
+                throw new IOException("Cannot get JavaSource for: " + uri);
+            }
+            ElementHandle handle = gson.fromJson(gson.toJson(source.getUserData()), ElementData.class).toHandle();
+            AtomicReference<ChangeParametersRefactoring.ParameterInfo[]> params = new AtomicReference<>();
+            StringBuilder ret = new StringBuilder();
+            js.runUserActionTask(ci -> {
+                ci.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
+                ExecutableElement method = (ExecutableElement) handle.resolve(ci);
+                if (method != null) {
+                    TreePath path = ci.getTrees().getPath(method);
+                    if (path != null) {
+                        ExecutableElement el = fromSignature(ci, signature, path);
+                        if (el != null) {
+                            if (method.getReturnType() != el.getReturnType()) {
+                                ret.append(Utilities.getTypeName(ci, el.getReturnType(), true));
+                            }
+                            params.set(matchParameters(ci, el.getParameters(), method.getParameters()));
+                        }
+                    }
+                }
+            }, true);
+            if (params.get() == null) {
+                throw new IllegalArgumentException("Error while parsing new method signature.");
+            }
+            ChangeParametersRefactoring refactoring = new ChangeParametersRefactoring(TreePathHandle.from(handle, info));
+            refactoring.setReturnType(ret.length() > 0 ? ret.toString() : null);
+            refactoring.setParameterInfo(params.get());
+            refactoring.getContext().add(JavaRefactoringUtils.getClasspathInfoFor(file));
+            client.applyEdit(new ApplyWorkspaceEditParams(perform(refactoring, "ChangeMethodParameters")));
+        } catch (Exception ex) {
+            client.showMessage(new MessageParams(MessageType.Error, ex.getLocalizedMessage()));
+        }
+    }
+
+    private static ExecutableElement fromSignature(CompilationInfo info, String signature, TreePath path) {
+        int idx = signature.lastIndexOf(':');
+        StringBuilder toParse = new StringBuilder("{class _X { public ");
+        if (idx < 0) {
+            toParse.append("_X").append(signature);
+        } else {
+            toParse.append(signature.substring(idx + 1)).append(" m").append(signature.substring(0, idx));
+        }
+        toParse.append("{}}}");
+        SourcePositions[] sp = new SourcePositions[1];
+        TreeUtilities treeUtilities = info.getTreeUtilities();
+        StatementTree stmt = treeUtilities.parseStatement(toParse.toString(), sp);
+        if (stmt != null && stmt.getKind() == Tree.Kind.BLOCK) {
+            treeUtilities.attributeTree(stmt, info.getTrees().getScope(path));
+            List<? extends StatementTree> stmts = ((BlockTree) stmt).getStatements();
+            if (!stmts.isEmpty() && stmts.get(0).getKind() == Tree.Kind.CLASS) {
+                ClassTree ct = (ClassTree) stmts.get(0);
+                for (Tree member : ct.getMembers()) {
+                    TreePath memberPath = new TreePath(path, member);
+                    if (!treeUtilities.isSynthetic(memberPath) && member.getKind() == Tree.Kind.METHOD) {
+                        Element element = info.getTrees().getElement(memberPath);
+                        if (element != null && (element.getKind() == ElementKind.METHOD || element.getKind() == ElementKind.CONSTRUCTOR)) {
+                            return (ExecutableElement) element;
+                        }
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    private static ChangeParametersRefactoring.ParameterInfo[] matchParameters(CompilationInfo info, List<? extends VariableElement> params, List<? extends VariableElement> orig) {
+        ChangeParametersRefactoring.ParameterInfo[] result = new ChangeParametersRefactoring.ParameterInfo[params.size()];
+        boolean[] used = new boolean[orig.size()];
+        for (int idx = 0; idx < params.size(); idx++) {
+            VariableElement param = params.get(idx);
+            for (int i = 0; i < orig.size(); i++) {
+                if (!used[i]) {
+                    VariableElement origParam = orig.get(i);
+                    if (origParam.getSimpleName().contentEquals(param.getSimpleName())) {
+                        result[idx] = new ChangeParametersRefactoring.ParameterInfo(i, param.getSimpleName().toString(), Utilities.getTypeName(info, param.asType(), true).toString(), null);
+                        used[i] = true;
+                    }
+                }
+            }
+        }
+        for (int idx = 0; idx < params.size(); idx++) {
+            if (result[idx] == null) {
+                VariableElement param = params.get(idx);
+                for (int i = 0; i < orig.size(); i++) {
+                    if (!used[i]) {
+                        VariableElement origParam = orig.get(i);
+                        if (origParam.asType() == param.asType()) {
+                            result[idx] = new ChangeParametersRefactoring.ParameterInfo(i, param.getSimpleName().toString(), Utilities.getTypeName(info, param.asType(), true).toString(), null);
+                            used[i] = true;
+                        }
+                    }
+                }
+            }
+        }
+        for (int idx = 0; idx < params.size(); idx++) {
+            if (result[idx] == null) {
+                VariableElement param = params.get(idx);
+                result[idx] = idx >= orig.size() || used[idx]
+                        ? new ChangeParametersRefactoring.ParameterInfo(-1, param.getSimpleName().toString(), Utilities.getTypeName(info, param.asType(), true).toString(), defaultValue(param))
+                        : new ChangeParametersRefactoring.ParameterInfo(idx, param.getSimpleName().toString(), Utilities.getTypeName(info, param.asType(), true).toString(), null);
+            }
+        }
+        return result;
+    }
+
+    private static String defaultValue(VariableElement param) {
+        switch(param.asType().getKind()) {
+            case ARRAY:
+            case DECLARED:
+                return "null";
+            case BOOLEAN:
+                return "false";
+            case BYTE:
+            case CHAR:
+            case DOUBLE:
+            case FLOAT:
+            case INT:
+            case LONG:
+            case SHORT:
+                return "0";
+        }
+        return null;
+    }
+}
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 61380d7..50c8b76 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
@@ -73,26 +73,34 @@ public abstract class CodeActionsProvider {
     }
 
     protected static String createLabel(CompilationInfo info, Element e) {
+        return createLabel(info, e, false);
+    }
+
+    protected static String createLabel(CompilationInfo info, Element e, boolean fqn) {
         switch (e.getKind()) {
             case ANNOTATION_TYPE:
             case CLASS:
             case ENUM:
             case INTERFACE:
-                return createLabel(info, (TypeElement) e);
+                return createLabel(info, (TypeElement) e, fqn);
             case CONSTRUCTOR:
             case METHOD:
-                return createLabel(info, (ExecutableElement) e);
+                return createLabel(info, (ExecutableElement) e, fqn);
             case ENUM_CONSTANT:
             case FIELD:
-                return createLabel(info, (VariableElement) e);
+                return createLabel(info, (VariableElement) e, fqn);
             default:
                 return null;
         }
     }
 
     protected static String createLabel(CompilationInfo info, TypeElement e) {
+        return createLabel(info, e, false);
+    }
+
+    protected static String createLabel(CompilationInfo info, TypeElement e, boolean fqn) {
         StringBuilder sb = new StringBuilder();
-        sb.append(e.getSimpleName());
+        sb.append(fqn ? e.getQualifiedName() : e.getSimpleName());
         List<? extends TypeParameterElement> typeParams = e.getTypeParameters();
         if (typeParams != null && !typeParams.isEmpty()) {
             sb.append("<"); // NOI18N
@@ -104,7 +112,7 @@ public abstract class CodeActionsProvider {
                     if (bounds.size() > 1 || !"java.lang.Object".equals(bounds.get(0).toString())) { // NOI18N
                         sb.append(" extends "); // NOI18N
                         for (Iterator<? extends TypeMirror> bIt = bounds.iterator(); bIt.hasNext();) {
-                            sb.append(Utilities.getTypeName(info, bIt.next(), false));
+                            sb.append(Utilities.getTypeName(info, bIt.next(), fqn));
                             if (bIt.hasNext()) {
                                 sb.append(" & "); // NOI18N
                             }
@@ -121,16 +129,24 @@ public abstract class CodeActionsProvider {
     }
 
     protected static String createLabel(CompilationInfo info, VariableElement e) {
+        return createLabel(info, e, false);
+    }
+
+    protected static String createLabel(CompilationInfo info, VariableElement e, boolean fqn) {
         StringBuilder sb = new StringBuilder();
         sb.append(e.getSimpleName());
         if (e.getKind() != ElementKind.ENUM_CONSTANT) {
             sb.append(" : "); // NOI18N
-            sb.append(Utilities.getTypeName(info, e.asType(), false));
+            sb.append(Utilities.getTypeName(info, e.asType(), fqn));
         }
         return sb.toString();
     }
 
     protected static String createLabel(CompilationInfo info, ExecutableElement e) {
+        return createLabel(info, e, false);
+    }
+
+    protected static String createLabel(CompilationInfo info, ExecutableElement e, boolean fqn) {
         StringBuilder sb = new StringBuilder();
         if (e.getKind() == ElementKind.CONSTRUCTOR) {
             sb.append(e.getEnclosingElement().getSimpleName());
@@ -141,10 +157,10 @@ public abstract class CodeActionsProvider {
         for (Iterator<? extends VariableElement> it = e.getParameters().iterator(); it.hasNext();) {
             VariableElement param = it.next();
             if (!it.hasNext() && e.isVarArgs() && param.asType().getKind() == TypeKind.ARRAY) {
-                sb.append(Utilities.getTypeName(info, ((ArrayType) param.asType()).getComponentType(), false));
+                sb.append(Utilities.getTypeName(info, ((ArrayType) param.asType()).getComponentType(), fqn));
                 sb.append("...");
             } else {
-                sb.append(Utilities.getTypeName(info, param.asType(), false));
+                sb.append(Utilities.getTypeName(info, param.asType(), fqn));
             }
             sb.append(" "); // NOI18N
             sb.append(param.getSimpleName());
@@ -157,7 +173,7 @@ public abstract class CodeActionsProvider {
             TypeMirror rt = e.getReturnType();
             if (rt.getKind() != TypeKind.VOID) {
                 sb.append(" : "); // NOI18N
-                sb.append(Utilities.getTypeName(info, e.getReturnType(), false));
+                sb.append(Utilities.getTypeName(info, e.getReturnType(), fqn));
             }
         }
         return sb.toString();
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 5b9e704..e5edcc8 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
@@ -4946,6 +4946,166 @@ public class ServerTest extends NbTestCase {
         }
     }
 
+    public void testChangeMethodParameters() throws Exception {
+        File src = new File(getWorkDir(), "a/Foo.java");
+        src.getParentFile().mkdirs();
+        try (Writer w = new FileWriter(new File(src.getParentFile().getParentFile(), ".test-project"))) {}
+        String code = "package a;\n" +
+                      "\n" +
+                      "public class Foo {\n" +
+                      "    public String bar(String s, boolean b) {\n" +
+                      "        return b ? s.toUpperCase() : s.toLowerCase();\n" +
+                      "    }\n" +
+                      "}\n";
+        try (Writer w = new FileWriter(src)) {
+            w.write(code);
+        }
+        File src2 = new File(getWorkDir(), "a/Test.java");
+        String code2 = "package a;\n" +
+                       "\n" +
+                       "public class Test {\n" +
+                       "    public void test(Foo f) {\n" +
+                       "        String s = f.bar(\"Test\", false);\n" +
+                       "    }\n" +
+                       "}\n";
+        try (Writer w = new FileWriter(src2)) {
+            w.write(code2);
+        }
+        List<Diagnostic>[] diags = new List[1];
+        CountDownLatch indexingComplete = new CountDownLatch(1);
+        WorkspaceEdit[] edit = new WorkspaceEdit[1];
+        Launcher<LanguageServer> serverLauncher = LSPLauncher.createClientLauncher(new NbCodeLanguageClient() {
+            @Override
+            public void telemetryEvent(Object arg0) {
+                throw new UnsupportedOperationException("Not supported yet.");
+            }
+
+            @Override
+            public void publishDiagnostics(PublishDiagnosticsParams params) {
+                synchronized (diags) {
+                    diags[0] = params.getDiagnostics();
+                    diags.notifyAll();
+                }
+            }
+
+            @Override
+            public void showMessage(MessageParams params) {
+                if (Server.INDEXING_COMPLETED.equals(params.getMessage())) {
+                    indexingComplete.countDown();
+                } else {
+                    throw new UnsupportedOperationException("Unexpected message.");
+                }
+            }
+
+            @Override
+            public CompletableFuture<MessageActionItem> showMessageRequest(ShowMessageRequestParams arg0) {
+                throw new UnsupportedOperationException("Not supported yet.");
+            }
+
+            @Override
+            public void logMessage(MessageParams arg0) {
+                throw new UnsupportedOperationException("Not supported yet.");
+            }
+
+            @Override
+            public CompletableFuture<ApplyWorkspaceEditResponse> applyEdit(ApplyWorkspaceEditParams params) {
+                edit[0] = params.getEdit();
+                return CompletableFuture.completedFuture(new ApplyWorkspaceEditResponse(false));
+            }
+
+            @Override
+            public CompletableFuture<String> createTextEditorDecoration(DecorationRenderOptions params) {
+                throw new UnsupportedOperationException("Not supported yet.");
+            }
+
+            @Override
+            public void disposeTextEditorDecoration(String params) {
+                throw new UnsupportedOperationException("Not supported yet.");
+            }
+
+            @Override
+            public NbCodeClientCapabilities getNbCodeCapabilities() {
+                throw new UnsupportedOperationException("Not supported yet.");
+            }
+
+            @Override
+            public void notifyTestProgress(TestProgressParams params) {
+                throw new UnsupportedOperationException("Not supported yet.");
+            }
+
+            @Override
+            public void setTextEditorDecoration(SetTextEditorDecorationParams params) {
+                throw new UnsupportedOperationException("Not supported yet.");
+            }
+
+            @Override
+            public CompletableFuture<String> showInputBox(ShowInputBoxParams params) {
+                return CompletableFuture.completedFuture("(java.lang.String s, int cnt, boolean b):java.lang.String");
+            }
+
+            @Override
+            public CompletableFuture<List<QuickPickItem>> showQuickPick(ShowQuickPickParams params) {
+                throw new UnsupportedOperationException("Not supported yet.");
+            }
+
+            @Override
+            public void showStatusBarMessage(ShowStatusMessageParams params) {
+                throw new UnsupportedOperationException("Not supported yet.");
+            }
+        }, client.getInputStream(), client.getOutputStream());
+        serverLauncher.startListening();
+        LanguageServer server = serverLauncher.getRemoteProxy();
+        InitializeParams initParams = new InitializeParams();
+        initParams.setRootUri(getWorkDir().toURI().toString());
+        InitializeResult result = server.initialize(initParams).get();
+        indexingComplete.await();
+        server.getTextDocumentService().didOpen(new DidOpenTextDocumentParams(new TextDocumentItem(toURI(src2), "java", 0, code2)));
+        VersionedTextDocumentIdentifier id = new VersionedTextDocumentIdentifier(src2.toURI().toString(), 1);
+        List<Either<Command, CodeAction>> codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(id, new Range(new Position(4, 22), new Position(5, 22)), new CodeActionContext(Arrays.asList(), Arrays.asList(CodeActionKind.Refactor)))).get();
+        Optional<CodeAction> changeMethodParams =
+                codeActions.stream()
+                           .filter(Either::isRight)
+                           .map(Either::getRight)
+                           .filter(a -> Bundle.DN_ChangeMethodParams().equals(a.getTitle()))
+                           .findAny();
+        assertTrue(changeMethodParams.isPresent());
+        server.getWorkspaceService().executeCommand(new ExecuteCommandParams(changeMethodParams.get().getCommand().getCommand(), changeMethodParams.get().getCommand().getArguments())).get();
+        int cnt = 0;
+        while(edit[0] == null && cnt++ < 10) {
+            Thread.sleep(1000);
+        }
+        List<Either<TextDocumentEdit, ResourceOperation>> documentChanges = edit[0].getDocumentChanges();
+        assertEquals(2, documentChanges.size());
+        for (int i = 0; i <= 1; i++) {
+            Either<TextDocumentEdit, ResourceOperation> change = documentChanges.get(i);
+            assertTrue(change.isLeft());
+            TextDocumentEdit tde = change.getLeft();
+            if (tde.getTextDocument().getUri().endsWith("a/Test.java")) {
+                List<TextEdit> fileChanges = tde.getEdits();
+                assertNotNull(fileChanges);
+                assertEquals(1, fileChanges.size());
+                assertEquals(new Range(new Position(4, 33),
+                                       new Position(4, 33)),
+                             fileChanges.get(0).getRange());
+                assertEquals("0, ", fileChanges.get(0).getNewText());
+            } else if (tde.getTextDocument().getUri().endsWith("a/Foo.java")) {
+                List<TextEdit> fileChanges = tde.getEdits();
+                assertNotNull(fileChanges);
+                assertEquals(2, fileChanges.size());
+                assertEquals(new Range(new Position(3, 22),
+                                       new Position(3, 22)),
+                             fileChanges.get(0).getRange());
+                assertEquals("java.lang.", fileChanges.get(0).getNewText());
+                assertEquals(new Range(new Position(3, 30),
+                                       new Position(3, 30)),
+                             fileChanges.get(1).getRange());
+                assertEquals(", int cnt", fileChanges.get(1).getNewText());
+            } else {
+                fail("Unknown file modified");
+            }
+        }
+    }
+
     public void testSurroundWith() throws Exception {
         File src = new File(getWorkDir(), "Test.java");
         src.getParentFile().mkdirs();

---------------------------------------------------------------------
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