You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@netbeans.apache.org by GitBox <gi...@apache.org> on 2020/12/11 16:30:47 UTC

[GitHub] [netbeans] dbalek opened a new pull request #2587: Exisitng CodeGenerators exposed as CodeActions via LSP.

dbalek opened a new pull request #2587:
URL: https://github.com/apache/netbeans/pull/2587


   Following actions are provided:
   
   - Generate Constructor
   - Generate Getters/Setters
   - Generate equals() and hashCode()
   - Generate toString()
   - Generate Delegate Methods
   - Generate Override/Implement Method
   - Generate Logger


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


[GitHub] [netbeans] dbalek commented on a change in pull request #2587: Exisitng CodeGenerators exposed as CodeActions via LSP.

Posted by GitBox <gi...@apache.org>.
dbalek commented on a change in pull request #2587:
URL: https://github.com/apache/netbeans/pull/2587#discussion_r542172412



##########
File path: java/java.lsp.server/vscode/src/extension.ts
##########
@@ -442,6 +418,13 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex
             commands.executeCommand('setContext', 'nbJavaLSReady', true);
             c.onNotification(StatusMessageRequest.type, showStatusBarMessage);
             c.onNotification(LogMessageNotification.type, (param) => handleLog(log, param.message));
+            c.onRequest(QuickPickRequest.type, async param => {
+                const selected = await window.showQuickPick(param.items, { placeHolder: param.placeHolder, canPickMany: param.canPickMany });
+                return selected ? Array.isArray(selected) ? selected : [selected] : undefined;

Review comment:
       It comes as a null value that you get from received JSON. Proper null checks are present.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


[GitHub] [netbeans] dbalek commented on a change in pull request #2587: Existing CodeGenerators exposed as CodeActions via LSP.

Posted by GitBox <gi...@apache.org>.
dbalek commented on a change in pull request #2587:
URL: https://github.com/apache/netbeans/pull/2587#discussion_r543340951



##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/DelegateMethodGenerator.java
##########
@@ -0,0 +1,210 @@
+/*
+ * 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.Scope;
+import com.sun.source.util.TreePath;
+import com.sun.source.util.Trees;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+import javax.lang.model.util.Elements;
+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.eclipse.lsp4j.TextEdit;
+import org.eclipse.lsp4j.WorkspaceEdit;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.ElementUtilities;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.TreeUtilities;
+import org.netbeans.modules.java.lsp.server.Utils;
+import org.openide.filesystems.FileObject;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public final class DelegateMethodGenerator extends CodeGenerator {
+
+    public static final String GENERATE_DELEGATE_METHOD =  "java.generate.delegateMethod";
+
+    private final Set<String> commands = Collections.singleton(GENERATE_DELEGATE_METHOD);
+    private final Gson gson = new Gson();
+
+    DelegateMethodGenerator() {
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_GenerateDelegateMethod=Generate Delegate Method...",
+    })
+    public List<CodeAction> getCodeActions(CompilationInfo info, CodeActionParams params) {
+        List<String> only = params.getContext().getOnly();
+        if (only == null || !only.contains(CodeActionKind.Source)) {
+            return Collections.emptyList();
+        }
+        int offset = getOffset(info, params.getRange().getStart());
+        TreePath tp = info.getTreeUtilities().pathFor(offset);
+        tp = info.getTreeUtilities().getPathElementOfKind(TreeUtilities.CLASS_TREE_KINDS, tp);
+        if (tp == null) {
+            return Collections.emptyList();
+        }
+        TypeElement typeElement = (TypeElement) info.getTrees().getElement(tp);
+        if (typeElement == null || !typeElement.getKind().isClass()) {
+            return Collections.emptyList();
+        }
+        Elements elements = info.getElements();
+        Trees trees = info.getTrees();
+        Scope scope = trees.getScope(tp);
+        List<QuickPickItem> fields = new ArrayList<>();
+        TypeElement cls;
+        while (scope != null && (cls = scope.getEnclosingClass()) != null) {
+            DeclaredType type = (DeclaredType) cls.asType();
+            for (VariableElement field : ElementFilter.fieldsIn(elements.getAllMembers(cls))) {
+                TypeMirror fieldType = field.asType();
+                if (!ERROR.contentEquals(field.getSimpleName()) && !fieldType.getKind().isPrimitive() && fieldType.getKind() != TypeKind.ARRAY
+                        && (fieldType.getKind() != TypeKind.DECLARED || ((DeclaredType)fieldType).asElement() != cls) && trees.isAccessible(scope, field, type)) {
+                    QuickPickItem item = new QuickPickItem(createLabel(info, field));
+                    item.setUserData(new ElementData(field));
+                    fields.add(item);
+                }
+            }
+            scope = scope.getEnclosingScope();
+        }
+        if (fields.isEmpty()) {
+            return Collections.emptyList();
+        }
+        String uri = Utils.toUri(info.getFileObject());
+        QuickPickItem typeItem = new QuickPickItem(createLabel(info, typeElement));
+        typeItem.setUserData(new ElementData(typeElement));
+        return Collections.singletonList(createCodeAction(Bundle.DN_GenerateDelegateMethod(), CODE_GENERATOR_KIND, GENERATE_DELEGATE_METHOD, uri, offset, typeItem, fields));
+    }
+
+    @Override
+    public Set<String> getCommands() {
+        return commands;
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_SelectDelegateMethodField=Select target field to generate delegates for",
+    })
+    public CompletableFuture<Object> processCommand(NbCodeLanguageClient client, String command, List<Object> arguments) {
+        if (arguments.size() > 3) {

Review comment:
       Client sends back the command and arguments received from the server as a part of CodeAction message. But OK, server should log a message if something went terribly bad.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


[GitHub] [netbeans] dbalek commented on a change in pull request #2587: Exisitng CodeGenerators exposed as CodeActions via LSP.

Posted by GitBox <gi...@apache.org>.
dbalek commented on a change in pull request #2587:
URL: https://github.com/apache/netbeans/pull/2587#discussion_r542178680



##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java
##########
@@ -1063,20 +1044,10 @@ protected void performRewrite(JavaFix.TransformationContext ctx) throws Exceptio
         try {
             js.runUserActionTask(cc -> {
                 cc.toPhase(JavaSource.Phase.RESOLVED);
-
-                Pair<Set<VariableElement>, Set<VariableElement>> pair = GetterSetterGenerator.findMissingGettersSetters(cc, params.getRange(), false);
-                boolean missingGetters = !pair.first().isEmpty();
-                boolean missingSetters = !pair.second().isEmpty();
-                String uri = toUri(cc.getFileObject());
-
-                if (missingGetters) {
-                    result.add(Either.forRight(createCodeGeneratorAction(Bundle.DN_GenerateGetters(), Server.GENERATE_GETTERS, uri, params.getRange())));
-                }
-                if (missingSetters) {
-                    result.add(Either.forRight(createCodeGeneratorAction(Bundle.DN_GenerateSetters(), Server.GENERATE_SETTERS, uri, params.getRange())));
-                }
-                if (missingGetters && missingSetters) {
-                    result.add(Either.forRight(createCodeGeneratorAction(Bundle.DN_GenerateGettersSetters(), Server.GENERATE_GETTERS_SETTERS, uri, params.getRange())));
+                for (CodeGenerator codeGenerator : Server.CODE_GENERATORS) {

Review comment:
       Yes.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


[GitHub] [netbeans] dbalek commented on a change in pull request #2587: Existing CodeGenerators exposed as CodeActions via LSP.

Posted by GitBox <gi...@apache.org>.
dbalek commented on a change in pull request #2587:
URL: https://github.com/apache/netbeans/pull/2587#discussion_r543357926



##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/DelegateMethodGenerator.java
##########
@@ -0,0 +1,210 @@
+/*
+ * 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.Scope;
+import com.sun.source.util.TreePath;
+import com.sun.source.util.Trees;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+import javax.lang.model.util.Elements;
+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.eclipse.lsp4j.TextEdit;
+import org.eclipse.lsp4j.WorkspaceEdit;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.ElementUtilities;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.TreeUtilities;
+import org.netbeans.modules.java.lsp.server.Utils;
+import org.openide.filesystems.FileObject;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public final class DelegateMethodGenerator extends CodeGenerator {
+
+    public static final String GENERATE_DELEGATE_METHOD =  "java.generate.delegateMethod";
+
+    private final Set<String> commands = Collections.singleton(GENERATE_DELEGATE_METHOD);
+    private final Gson gson = new Gson();
+
+    DelegateMethodGenerator() {
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_GenerateDelegateMethod=Generate Delegate Method...",
+    })
+    public List<CodeAction> getCodeActions(CompilationInfo info, CodeActionParams params) {
+        List<String> only = params.getContext().getOnly();
+        if (only == null || !only.contains(CodeActionKind.Source)) {
+            return Collections.emptyList();
+        }
+        int offset = getOffset(info, params.getRange().getStart());
+        TreePath tp = info.getTreeUtilities().pathFor(offset);
+        tp = info.getTreeUtilities().getPathElementOfKind(TreeUtilities.CLASS_TREE_KINDS, tp);
+        if (tp == null) {
+            return Collections.emptyList();
+        }
+        TypeElement typeElement = (TypeElement) info.getTrees().getElement(tp);
+        if (typeElement == null || !typeElement.getKind().isClass()) {
+            return Collections.emptyList();
+        }
+        Elements elements = info.getElements();
+        Trees trees = info.getTrees();
+        Scope scope = trees.getScope(tp);
+        List<QuickPickItem> fields = new ArrayList<>();
+        TypeElement cls;
+        while (scope != null && (cls = scope.getEnclosingClass()) != null) {
+            DeclaredType type = (DeclaredType) cls.asType();
+            for (VariableElement field : ElementFilter.fieldsIn(elements.getAllMembers(cls))) {
+                TypeMirror fieldType = field.asType();
+                if (!ERROR.contentEquals(field.getSimpleName()) && !fieldType.getKind().isPrimitive() && fieldType.getKind() != TypeKind.ARRAY
+                        && (fieldType.getKind() != TypeKind.DECLARED || ((DeclaredType)fieldType).asElement() != cls) && trees.isAccessible(scope, field, type)) {
+                    QuickPickItem item = new QuickPickItem(createLabel(info, field));
+                    item.setUserData(new ElementData(field));
+                    fields.add(item);
+                }
+            }
+            scope = scope.getEnclosingScope();
+        }
+        if (fields.isEmpty()) {
+            return Collections.emptyList();
+        }
+        String uri = Utils.toUri(info.getFileObject());
+        QuickPickItem typeItem = new QuickPickItem(createLabel(info, typeElement));
+        typeItem.setUserData(new ElementData(typeElement));
+        return Collections.singletonList(createCodeAction(Bundle.DN_GenerateDelegateMethod(), CODE_GENERATOR_KIND, GENERATE_DELEGATE_METHOD, uri, offset, typeItem, fields));
+    }
+
+    @Override
+    public Set<String> getCommands() {
+        return commands;
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_SelectDelegateMethodField=Select target field to generate delegates for",
+    })
+    public CompletableFuture<Object> processCommand(NbCodeLanguageClient client, String command, List<Object> arguments) {
+        if (arguments.size() > 3) {
+            String uri = gson.fromJson(gson.toJson(arguments.get(0)), String.class);
+            int offset = gson.fromJson(gson.toJson(arguments.get(1)), Integer.class);
+            QuickPickItem type = gson.fromJson(gson.toJson(arguments.get(2)), QuickPickItem.class);
+            List<QuickPickItem> fields = Arrays.asList(gson.fromJson(gson.toJson(arguments.get(3)), QuickPickItem[].class));
+            if (fields.size() == 1) {
+                selectMethods(client, uri, offset, type, fields.get(0));
+            } else {
+                client.showQuickPick(new ShowQuickPickParams(Bundle.DN_SelectDelegateMethodField(), false, fields)).thenAccept(selected -> {
+                    if (selected != null && !selected.isEmpty()) {
+                        selectMethods(client, uri, offset, type, selected.get(0));
+                    }
+                });
+            }
+        }
+        return CompletableFuture.completedFuture(true);
+    }
+
+    @NbBundle.Messages({
+        "DN_SelectDelegateMethods=Select methods to generate delegates for",
+    })
+    private void selectMethods(NbCodeLanguageClient client, String uri, int offset, QuickPickItem type, QuickPickItem selectedField) {
+        try {
+            FileObject file = Utils.fromUri(uri);
+            JavaSource js = JavaSource.forFileObject(file);
+            js.runUserActionTask(info -> {
+                info.toPhase(JavaSource.Phase.RESOLVED);
+                TypeElement origin = (TypeElement) gson.fromJson(gson.toJson(type.getUserData()), ElementData.class).resolve(info);
+                VariableElement field = (VariableElement) gson.fromJson(gson.toJson(selectedField.getUserData()), ElementData.class).resolve(info);
+                final ElementUtilities eu = info.getElementUtilities();
+                final Trees trees = info.getTrees();
+                final Scope scope = info.getTreeUtilities().scopeFor(offset);
+                ElementUtilities.ElementAcceptor acceptor = new ElementUtilities.ElementAcceptor() {
+                    @Override
+                    public boolean accept(Element e, TypeMirror type) {
+                        if (e.getKind() == ElementKind.METHOD && trees.isAccessible(scope, e, (DeclaredType)type)) {
+                            Element impl = eu.getImplementationOf((ExecutableElement)e, origin);
+                            return impl == null || (!impl.getModifiers().contains(Modifier.FINAL) && impl.getEnclosingElement() != origin);
+                        }
+                        return false;
+                    }
+                };
+                List<QuickPickItem> methods = new ArrayList<>();
+                for (ExecutableElement method : ElementFilter.methodsIn(eu.getMembers(field.asType(), acceptor))) {
+                    QuickPickItem item = new QuickPickItem(String.format("%s.%s", field.getSimpleName().toString(), createLabel(info, method)));
+                    item.setUserData(new ElementData(method));
+                    methods.add(item);
+                }
+                client.showQuickPick(new ShowQuickPickParams(Bundle.DN_SelectDelegateMethods(), true, methods)).thenAccept(selected -> {
+                    if (selected != null && !selected.isEmpty())

Review comment:
       Fixed.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


[GitHub] [netbeans] dbalek commented on a change in pull request #2587: Exisitng CodeGenerators exposed as CodeActions via LSP.

Posted by GitBox <gi...@apache.org>.
dbalek commented on a change in pull request #2587:
URL: https://github.com/apache/netbeans/pull/2587#discussion_r542177595



##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeGenerator.java
##########
@@ -0,0 +1,312 @@
+/*
+ * 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.sun.source.tree.LineMap;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UncheckedIOException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.nio.file.Files;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.TypeParameterElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import org.eclipse.lsp4j.CodeAction;
+import org.eclipse.lsp4j.CodeActionParams;
+import org.eclipse.lsp4j.Command;
+import org.eclipse.lsp4j.Position;
+import org.eclipse.xtext.xbase.lib.Pure;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.ElementHandle;
+import org.netbeans.modules.editor.java.Utilities;
+import org.netbeans.modules.java.source.ElementHandleAccessor;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.filesystems.URLMapper;
+import org.openide.modules.Places;
+import org.openide.util.Exceptions;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public abstract class CodeGenerator {
+
+    public static final String CODE_GENERATOR_KIND = "source.generate";
+    protected static final String ERROR = "<error>"; //NOI18N
+
+    public abstract List<CodeAction> getCodeActions(CompilationInfo info, CodeActionParams params);
+
+    public abstract Set<String> getCommands();
+
+    public abstract CompletableFuture<Object> processCommand(NbCodeLanguageClient client, String command, List<Object> arguments);
+
+    protected static int getOffset(CompilationInfo info, Position pos) {
+        LineMap lm = info.getCompilationUnit().getLineMap();
+        return (int) lm.getPosition(pos.getLine() + 1, pos.getCharacter() + 1);
+    }
+
+    protected static CodeAction createCodeAction(String name, String kind, String command, Object... args) {
+        CodeAction action = new CodeAction(name);
+        action.setKind(kind);
+        action.setCommand(new Command(name, command, Arrays.asList(args)));
+        return action;
+    }
+
+    protected static String toUri(FileObject file) {
+        if (FileUtil.isArchiveArtifact(file)) {
+            //VS code cannot open jar:file: URLs, workaround:

Review comment:
       Do we really need to have the code generators working for sources that come from jar files?




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


[GitHub] [netbeans] dbalek commented on a change in pull request #2587: Existing CodeGenerators exposed as CodeActions via LSP.

Posted by GitBox <gi...@apache.org>.
dbalek commented on a change in pull request #2587:
URL: https://github.com/apache/netbeans/pull/2587#discussion_r543375487



##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/DelegateMethodGenerator.java
##########
@@ -0,0 +1,210 @@
+/*
+ * 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.Scope;
+import com.sun.source.util.TreePath;
+import com.sun.source.util.Trees;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+import javax.lang.model.util.Elements;
+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.eclipse.lsp4j.TextEdit;
+import org.eclipse.lsp4j.WorkspaceEdit;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.ElementUtilities;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.TreeUtilities;
+import org.netbeans.modules.java.lsp.server.Utils;
+import org.openide.filesystems.FileObject;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public final class DelegateMethodGenerator extends CodeGenerator {
+
+    public static final String GENERATE_DELEGATE_METHOD =  "java.generate.delegateMethod";
+
+    private final Set<String> commands = Collections.singleton(GENERATE_DELEGATE_METHOD);
+    private final Gson gson = new Gson();
+
+    DelegateMethodGenerator() {
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_GenerateDelegateMethod=Generate Delegate Method...",
+    })
+    public List<CodeAction> getCodeActions(CompilationInfo info, CodeActionParams params) {
+        List<String> only = params.getContext().getOnly();
+        if (only == null || !only.contains(CodeActionKind.Source)) {
+            return Collections.emptyList();
+        }
+        int offset = getOffset(info, params.getRange().getStart());
+        TreePath tp = info.getTreeUtilities().pathFor(offset);
+        tp = info.getTreeUtilities().getPathElementOfKind(TreeUtilities.CLASS_TREE_KINDS, tp);
+        if (tp == null) {
+            return Collections.emptyList();
+        }
+        TypeElement typeElement = (TypeElement) info.getTrees().getElement(tp);
+        if (typeElement == null || !typeElement.getKind().isClass()) {
+            return Collections.emptyList();
+        }
+        Elements elements = info.getElements();
+        Trees trees = info.getTrees();
+        Scope scope = trees.getScope(tp);
+        List<QuickPickItem> fields = new ArrayList<>();
+        TypeElement cls;
+        while (scope != null && (cls = scope.getEnclosingClass()) != null) {
+            DeclaredType type = (DeclaredType) cls.asType();
+            for (VariableElement field : ElementFilter.fieldsIn(elements.getAllMembers(cls))) {
+                TypeMirror fieldType = field.asType();
+                if (!ERROR.contentEquals(field.getSimpleName()) && !fieldType.getKind().isPrimitive() && fieldType.getKind() != TypeKind.ARRAY
+                        && (fieldType.getKind() != TypeKind.DECLARED || ((DeclaredType)fieldType).asElement() != cls) && trees.isAccessible(scope, field, type)) {
+                    QuickPickItem item = new QuickPickItem(createLabel(info, field));
+                    item.setUserData(new ElementData(field));
+                    fields.add(item);
+                }
+            }
+            scope = scope.getEnclosingScope();
+        }
+        if (fields.isEmpty()) {
+            return Collections.emptyList();
+        }
+        String uri = Utils.toUri(info.getFileObject());
+        QuickPickItem typeItem = new QuickPickItem(createLabel(info, typeElement));
+        typeItem.setUserData(new ElementData(typeElement));
+        return Collections.singletonList(createCodeAction(Bundle.DN_GenerateDelegateMethod(), CODE_GENERATOR_KIND, GENERATE_DELEGATE_METHOD, uri, offset, typeItem, fields));
+    }
+
+    @Override
+    public Set<String> getCommands() {
+        return commands;
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_SelectDelegateMethodField=Select target field to generate delegates for",
+    })
+    public CompletableFuture<Object> processCommand(NbCodeLanguageClient client, String command, List<Object> arguments) {
+        if (arguments.size() > 3) {
+            String uri = gson.fromJson(gson.toJson(arguments.get(0)), String.class);
+            int offset = gson.fromJson(gson.toJson(arguments.get(1)), Integer.class);
+            QuickPickItem type = gson.fromJson(gson.toJson(arguments.get(2)), QuickPickItem.class);
+            List<QuickPickItem> fields = Arrays.asList(gson.fromJson(gson.toJson(arguments.get(3)), QuickPickItem[].class));
+            if (fields.size() == 1) {
+                selectMethods(client, uri, offset, type, fields.get(0));
+            } else {
+                client.showQuickPick(new ShowQuickPickParams(Bundle.DN_SelectDelegateMethodField(), false, fields)).thenAccept(selected -> {
+                    if (selected != null && !selected.isEmpty()) {
+                        selectMethods(client, uri, offset, type, selected.get(0));
+                    }
+                });
+            }
+        }
+        return CompletableFuture.completedFuture(true);
+    }
+
+    @NbBundle.Messages({
+        "DN_SelectDelegateMethods=Select methods to generate delegates for",
+    })
+    private void selectMethods(NbCodeLanguageClient client, String uri, int offset, QuickPickItem type, QuickPickItem selectedField) {
+        try {
+            FileObject file = Utils.fromUri(uri);
+            JavaSource js = JavaSource.forFileObject(file);
+            js.runUserActionTask(info -> {
+                info.toPhase(JavaSource.Phase.RESOLVED);
+                TypeElement origin = (TypeElement) gson.fromJson(gson.toJson(type.getUserData()), ElementData.class).resolve(info);
+                VariableElement field = (VariableElement) gson.fromJson(gson.toJson(selectedField.getUserData()), ElementData.class).resolve(info);
+                final ElementUtilities eu = info.getElementUtilities();

Review comment:
       Fixed.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


[GitHub] [netbeans] dbalek commented on a change in pull request #2587: Exisitng CodeGenerators exposed as CodeActions via LSP.

Posted by GitBox <gi...@apache.org>.
dbalek commented on a change in pull request #2587:
URL: https://github.com/apache/netbeans/pull/2587#discussion_r542175970



##########
File path: java/java.lsp.server/vscode/package.json
##########
@@ -140,21 +140,6 @@
 				"title": "Compile Workspace",
 				"category": "Java"
 			},
-			{
-				"command": "java.generate.getters.menu",

Review comment:
       CodeActions that have set `CodeActionKind.Source` automatically appear as commands under the `Source Action...` popup menu item in the VSCode client. There is no need for their manual registration.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


[GitHub] [netbeans] dbalek commented on a change in pull request #2587: Existing CodeGenerators exposed as CodeActions via LSP.

Posted by GitBox <gi...@apache.org>.
dbalek commented on a change in pull request #2587:
URL: https://github.com/apache/netbeans/pull/2587#discussion_r543338383



##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/DelegateMethodGenerator.java
##########
@@ -0,0 +1,210 @@
+/*
+ * 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.Scope;
+import com.sun.source.util.TreePath;
+import com.sun.source.util.Trees;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+import javax.lang.model.util.Elements;
+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.eclipse.lsp4j.TextEdit;
+import org.eclipse.lsp4j.WorkspaceEdit;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.ElementUtilities;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.TreeUtilities;
+import org.netbeans.modules.java.lsp.server.Utils;
+import org.openide.filesystems.FileObject;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public final class DelegateMethodGenerator extends CodeGenerator {
+
+    public static final String GENERATE_DELEGATE_METHOD =  "java.generate.delegateMethod";
+
+    private final Set<String> commands = Collections.singleton(GENERATE_DELEGATE_METHOD);
+    private final Gson gson = new Gson();
+
+    DelegateMethodGenerator() {
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_GenerateDelegateMethod=Generate Delegate Method...",
+    })
+    public List<CodeAction> getCodeActions(CompilationInfo info, CodeActionParams params) {
+        List<String> only = params.getContext().getOnly();
+        if (only == null || !only.contains(CodeActionKind.Source)) {
+            return Collections.emptyList();
+        }
+        int offset = getOffset(info, params.getRange().getStart());
+        TreePath tp = info.getTreeUtilities().pathFor(offset);
+        tp = info.getTreeUtilities().getPathElementOfKind(TreeUtilities.CLASS_TREE_KINDS, tp);
+        if (tp == null) {
+            return Collections.emptyList();
+        }
+        TypeElement typeElement = (TypeElement) info.getTrees().getElement(tp);
+        if (typeElement == null || !typeElement.getKind().isClass()) {
+            return Collections.emptyList();
+        }
+        Elements elements = info.getElements();
+        Trees trees = info.getTrees();
+        Scope scope = trees.getScope(tp);
+        List<QuickPickItem> fields = new ArrayList<>();
+        TypeElement cls;
+        while (scope != null && (cls = scope.getEnclosingClass()) != null) {
+            DeclaredType type = (DeclaredType) cls.asType();
+            for (VariableElement field : ElementFilter.fieldsIn(elements.getAllMembers(cls))) {
+                TypeMirror fieldType = field.asType();
+                if (!ERROR.contentEquals(field.getSimpleName()) && !fieldType.getKind().isPrimitive() && fieldType.getKind() != TypeKind.ARRAY

Review comment:
       ERROR.contentEquals(field.getSimpleName()) filters out fields, that are not syntactically correct (e.g. situation, when you start typing a new field declaration in the editor and you haven't typed the field name yet, just a field type...).
   As a field type, DECLARED types different form self type are required.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


[GitHub] [netbeans] sdedic commented on a change in pull request #2587: Existing CodeGenerators exposed as CodeActions via LSP.

Posted by GitBox <gi...@apache.org>.
sdedic commented on a change in pull request #2587:
URL: https://github.com/apache/netbeans/pull/2587#discussion_r543188769



##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/DelegateMethodGenerator.java
##########
@@ -0,0 +1,210 @@
+/*
+ * 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.Scope;
+import com.sun.source.util.TreePath;
+import com.sun.source.util.Trees;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+import javax.lang.model.util.Elements;
+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.eclipse.lsp4j.TextEdit;
+import org.eclipse.lsp4j.WorkspaceEdit;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.ElementUtilities;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.TreeUtilities;
+import org.netbeans.modules.java.lsp.server.Utils;
+import org.openide.filesystems.FileObject;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public final class DelegateMethodGenerator extends CodeGenerator {
+
+    public static final String GENERATE_DELEGATE_METHOD =  "java.generate.delegateMethod";
+
+    private final Set<String> commands = Collections.singleton(GENERATE_DELEGATE_METHOD);
+    private final Gson gson = new Gson();
+
+    DelegateMethodGenerator() {
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_GenerateDelegateMethod=Generate Delegate Method...",
+    })
+    public List<CodeAction> getCodeActions(CompilationInfo info, CodeActionParams params) {
+        List<String> only = params.getContext().getOnly();
+        if (only == null || !only.contains(CodeActionKind.Source)) {
+            return Collections.emptyList();
+        }
+        int offset = getOffset(info, params.getRange().getStart());
+        TreePath tp = info.getTreeUtilities().pathFor(offset);
+        tp = info.getTreeUtilities().getPathElementOfKind(TreeUtilities.CLASS_TREE_KINDS, tp);
+        if (tp == null) {
+            return Collections.emptyList();
+        }
+        TypeElement typeElement = (TypeElement) info.getTrees().getElement(tp);
+        if (typeElement == null || !typeElement.getKind().isClass()) {
+            return Collections.emptyList();
+        }
+        Elements elements = info.getElements();
+        Trees trees = info.getTrees();
+        Scope scope = trees.getScope(tp);
+        List<QuickPickItem> fields = new ArrayList<>();
+        TypeElement cls;
+        while (scope != null && (cls = scope.getEnclosingClass()) != null) {
+            DeclaredType type = (DeclaredType) cls.asType();
+            for (VariableElement field : ElementFilter.fieldsIn(elements.getAllMembers(cls))) {
+                TypeMirror fieldType = field.asType();
+                if (!ERROR.contentEquals(field.getSimpleName()) && !fieldType.getKind().isPrimitive() && fieldType.getKind() != TypeKind.ARRAY

Review comment:
       Nitpick: isn't `fieldType.getKind() == TypeKind.ERROR` better ?
   
   Questin (self-edu): what type field types are not primitive+array or DECLARED ?

##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/DelegateMethodGenerator.java
##########
@@ -0,0 +1,210 @@
+/*
+ * 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.Scope;
+import com.sun.source.util.TreePath;
+import com.sun.source.util.Trees;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+import javax.lang.model.util.Elements;
+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.eclipse.lsp4j.TextEdit;
+import org.eclipse.lsp4j.WorkspaceEdit;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.ElementUtilities;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.TreeUtilities;
+import org.netbeans.modules.java.lsp.server.Utils;
+import org.openide.filesystems.FileObject;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public final class DelegateMethodGenerator extends CodeGenerator {
+
+    public static final String GENERATE_DELEGATE_METHOD =  "java.generate.delegateMethod";
+
+    private final Set<String> commands = Collections.singleton(GENERATE_DELEGATE_METHOD);
+    private final Gson gson = new Gson();
+
+    DelegateMethodGenerator() {
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_GenerateDelegateMethod=Generate Delegate Method...",
+    })
+    public List<CodeAction> getCodeActions(CompilationInfo info, CodeActionParams params) {
+        List<String> only = params.getContext().getOnly();
+        if (only == null || !only.contains(CodeActionKind.Source)) {
+            return Collections.emptyList();
+        }
+        int offset = getOffset(info, params.getRange().getStart());
+        TreePath tp = info.getTreeUtilities().pathFor(offset);
+        tp = info.getTreeUtilities().getPathElementOfKind(TreeUtilities.CLASS_TREE_KINDS, tp);
+        if (tp == null) {
+            return Collections.emptyList();
+        }
+        TypeElement typeElement = (TypeElement) info.getTrees().getElement(tp);
+        if (typeElement == null || !typeElement.getKind().isClass()) {
+            return Collections.emptyList();
+        }
+        Elements elements = info.getElements();
+        Trees trees = info.getTrees();
+        Scope scope = trees.getScope(tp);
+        List<QuickPickItem> fields = new ArrayList<>();
+        TypeElement cls;
+        while (scope != null && (cls = scope.getEnclosingClass()) != null) {
+            DeclaredType type = (DeclaredType) cls.asType();
+            for (VariableElement field : ElementFilter.fieldsIn(elements.getAllMembers(cls))) {
+                TypeMirror fieldType = field.asType();
+                if (!ERROR.contentEquals(field.getSimpleName()) && !fieldType.getKind().isPrimitive() && fieldType.getKind() != TypeKind.ARRAY
+                        && (fieldType.getKind() != TypeKind.DECLARED || ((DeclaredType)fieldType).asElement() != cls) && trees.isAccessible(scope, field, type)) {
+                    QuickPickItem item = new QuickPickItem(createLabel(info, field));
+                    item.setUserData(new ElementData(field));
+                    fields.add(item);
+                }
+            }
+            scope = scope.getEnclosingScope();
+        }
+        if (fields.isEmpty()) {
+            return Collections.emptyList();
+        }
+        String uri = Utils.toUri(info.getFileObject());
+        QuickPickItem typeItem = new QuickPickItem(createLabel(info, typeElement));
+        typeItem.setUserData(new ElementData(typeElement));
+        return Collections.singletonList(createCodeAction(Bundle.DN_GenerateDelegateMethod(), CODE_GENERATOR_KIND, GENERATE_DELEGATE_METHOD, uri, offset, typeItem, fields));
+    }
+
+    @Override
+    public Set<String> getCommands() {
+        return commands;
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_SelectDelegateMethodField=Select target field to generate delegates for",
+    })
+    public CompletableFuture<Object> processCommand(NbCodeLanguageClient client, String command, List<Object> arguments) {
+        if (arguments.size() > 3) {
+            String uri = gson.fromJson(gson.toJson(arguments.get(0)), String.class);
+            int offset = gson.fromJson(gson.toJson(arguments.get(1)), Integer.class);
+            QuickPickItem type = gson.fromJson(gson.toJson(arguments.get(2)), QuickPickItem.class);
+            List<QuickPickItem> fields = Arrays.asList(gson.fromJson(gson.toJson(arguments.get(3)), QuickPickItem[].class));
+            if (fields.size() == 1) {
+                selectMethods(client, uri, offset, type, fields.get(0));
+            } else {
+                client.showQuickPick(new ShowQuickPickParams(Bundle.DN_SelectDelegateMethodField(), false, fields)).thenAccept(selected -> {
+                    if (selected != null && !selected.isEmpty()) {
+                        selectMethods(client, uri, offset, type, selected.get(0));
+                    }
+                });
+            }
+        }
+        return CompletableFuture.completedFuture(true);
+    }
+
+    @NbBundle.Messages({
+        "DN_SelectDelegateMethods=Select methods to generate delegates for",
+    })
+    private void selectMethods(NbCodeLanguageClient client, String uri, int offset, QuickPickItem type, QuickPickItem selectedField) {
+        try {
+            FileObject file = Utils.fromUri(uri);
+            JavaSource js = JavaSource.forFileObject(file);
+            js.runUserActionTask(info -> {
+                info.toPhase(JavaSource.Phase.RESOLVED);
+                TypeElement origin = (TypeElement) gson.fromJson(gson.toJson(type.getUserData()), ElementData.class).resolve(info);
+                VariableElement field = (VariableElement) gson.fromJson(gson.toJson(selectedField.getUserData()), ElementData.class).resolve(info);
+                final ElementUtilities eu = info.getElementUtilities();

Review comment:
       Check origin / field for != null

##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ConstructorGenerator.java
##########
@@ -0,0 +1,210 @@
+/*
+ * 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.util.TreePath;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.NestingKind;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+import javax.lang.model.util.Elements;
+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.eclipse.lsp4j.TextEdit;
+import org.eclipse.lsp4j.WorkspaceEdit;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.TreeUtilities;
+import org.netbeans.modules.java.editor.codegen.GeneratorUtils;
+import org.netbeans.modules.java.lsp.server.Utils;
+import org.openide.filesystems.FileObject;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public final class ConstructorGenerator extends CodeGenerator {
+
+    public static final String GENERATE_CONSTRUCTOR =  "java.generate.constructor";
+
+    private final Set<String> commands = Collections.singleton(GENERATE_CONSTRUCTOR);
+    private final Gson gson = new Gson();
+
+    ConstructorGenerator() {
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_GenerateConstructor=Generate Constructor...",
+    })
+    public List<CodeAction> getCodeActions(CompilationInfo info, CodeActionParams params) {
+        List<String> only = params.getContext().getOnly();
+        if (only == null || !only.contains(CodeActionKind.Source)) {
+            return Collections.emptyList();
+        }
+        int offset = getOffset(info, params.getRange().getStart());
+        TreePath tp = info.getTreeUtilities().pathFor(offset);
+        tp = info.getTreeUtilities().getPathElementOfKind(TreeUtilities.CLASS_TREE_KINDS, tp);
+        if (tp == null) {
+            return Collections.emptyList();
+        }
+        TypeElement typeElement = (TypeElement) info.getTrees().getElement(tp);
+        if (typeElement == null || !typeElement.getKind().isClass() || NestingKind.ANONYMOUS.equals(typeElement.getNestingKind())) {
+            return Collections.emptyList();
+        }
+        final Set<? extends VariableElement> uninitializedFields = info.getTreeUtilities().getUninitializedFields(tp);
+        final List<ExecutableElement> inheritedConstructors = new ArrayList<>();
+        TypeMirror superClassType = typeElement.getSuperclass();
+        if (superClassType.getKind() == TypeKind.DECLARED) {
+            TypeElement superClass = (TypeElement) ((DeclaredType) superClassType).asElement();
+            Elements elements = info.getElements();
+            for (ExecutableElement executableElement : ElementFilter.constructorsIn(superClass.getEnclosedElements())) {
+                PackageElement currentPackage = elements.getPackageOf(typeElement);
+                PackageElement ctorPackage = elements.getPackageOf(executableElement);
+                Set<Modifier> ctorMods = executableElement.getModifiers();
+                if ((currentPackage != ctorPackage && !(ctorMods.contains(Modifier.PUBLIC) || ctorMods.contains(Modifier.PROTECTED)))
+                        || ctorMods.contains(Modifier.PRIVATE)) {
+                    continue;
+                }
+                inheritedConstructors.add(executableElement);
+            }
+        }
+        List<QuickPickItem> constructors = null;
+        if (typeElement.getKind() != ElementKind.ENUM && inheritedConstructors.size() == 1) {
+            QuickPickItem item = new QuickPickItem(createLabel(info, inheritedConstructors.get(0)));
+            item.setUserData(new ElementData(inheritedConstructors.get(0)));
+            constructors = Collections.singletonList(item);
+        } else if (inheritedConstructors.size() > 1) {
+            constructors = new ArrayList<>(inheritedConstructors.size());
+            for (ExecutableElement constructorElement : inheritedConstructors) {
+                QuickPickItem item = new QuickPickItem(createLabel(info, constructorElement));
+                item.setUserData(new ElementData(constructorElement));
+                constructors.add(item);
+            }
+        }
+        List<QuickPickItem> fields = null;
+        if (!uninitializedFields.isEmpty()) {
+            fields = new ArrayList<>();
+            for (VariableElement variableElement : uninitializedFields) {
+                QuickPickItem item = new QuickPickItem(createLabel(info, variableElement));
+                item.setUserData(new ElementData(variableElement));
+                fields.add(item);
+            }
+        }
+        if (constructors == null && fields == null) {
+            return Collections.emptyList();
+        }
+        String uri = Utils.toUri(info.getFileObject());
+        return Collections.singletonList(createCodeAction(Bundle.DN_GenerateConstructor(), CODE_GENERATOR_KIND, GENERATE_CONSTRUCTOR, uri, offset, constructors, fields));
+    }
+
+    @Override
+    public Set<String> getCommands() {
+        return commands;
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_SelectSuperConstructor=Select super constructor",
+    })
+    public CompletableFuture<Object> processCommand(NbCodeLanguageClient client, String command, List<Object> arguments) {
+        if (arguments.size() > 3) {
+            String uri = gson.fromJson(gson.toJson(arguments.get(0)), String.class);
+            int offset = gson.fromJson(gson.toJson(arguments.get(1)), Integer.class);
+            List<QuickPickItem> constructors = Arrays.asList(gson.fromJson(gson.toJson(arguments.get(2)), QuickPickItem[].class));
+            List<QuickPickItem> fields = Arrays.asList(gson.fromJson(gson.toJson(arguments.get(3)), QuickPickItem[].class));
+            if (constructors.size() < 2 && fields.isEmpty()) {
+                generate(client, uri, offset, constructors, fields);
+            } else {
+                if (constructors.size() > 1) {
+                    client.showQuickPick(new ShowQuickPickParams(Bundle.DN_SelectSuperConstructor(), true, constructors)).thenAccept(selected -> {

Review comment:
       Question: wouldn't it be more appropriate to return Future that describes command's completion (= the actual code generation termination) to the client ? This way it could be possible to reliably chain commands from the client.
   
   

##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ConstructorGenerator.java
##########
@@ -0,0 +1,210 @@
+/*
+ * 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.util.TreePath;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.NestingKind;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+import javax.lang.model.util.Elements;
+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.eclipse.lsp4j.TextEdit;
+import org.eclipse.lsp4j.WorkspaceEdit;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.TreeUtilities;
+import org.netbeans.modules.java.editor.codegen.GeneratorUtils;
+import org.netbeans.modules.java.lsp.server.Utils;
+import org.openide.filesystems.FileObject;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public final class ConstructorGenerator extends CodeGenerator {
+
+    public static final String GENERATE_CONSTRUCTOR =  "java.generate.constructor";
+
+    private final Set<String> commands = Collections.singleton(GENERATE_CONSTRUCTOR);
+    private final Gson gson = new Gson();
+
+    ConstructorGenerator() {
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_GenerateConstructor=Generate Constructor...",
+    })
+    public List<CodeAction> getCodeActions(CompilationInfo info, CodeActionParams params) {
+        List<String> only = params.getContext().getOnly();
+        if (only == null || !only.contains(CodeActionKind.Source)) {
+            return Collections.emptyList();
+        }
+        int offset = getOffset(info, params.getRange().getStart());
+        TreePath tp = info.getTreeUtilities().pathFor(offset);
+        tp = info.getTreeUtilities().getPathElementOfKind(TreeUtilities.CLASS_TREE_KINDS, tp);
+        if (tp == null) {
+            return Collections.emptyList();
+        }
+        TypeElement typeElement = (TypeElement) info.getTrees().getElement(tp);
+        if (typeElement == null || !typeElement.getKind().isClass() || NestingKind.ANONYMOUS.equals(typeElement.getNestingKind())) {
+            return Collections.emptyList();
+        }
+        final Set<? extends VariableElement> uninitializedFields = info.getTreeUtilities().getUninitializedFields(tp);
+        final List<ExecutableElement> inheritedConstructors = new ArrayList<>();
+        TypeMirror superClassType = typeElement.getSuperclass();
+        if (superClassType.getKind() == TypeKind.DECLARED) {
+            TypeElement superClass = (TypeElement) ((DeclaredType) superClassType).asElement();
+            Elements elements = info.getElements();
+            for (ExecutableElement executableElement : ElementFilter.constructorsIn(superClass.getEnclosedElements())) {
+                PackageElement currentPackage = elements.getPackageOf(typeElement);
+                PackageElement ctorPackage = elements.getPackageOf(executableElement);
+                Set<Modifier> ctorMods = executableElement.getModifiers();
+                if ((currentPackage != ctorPackage && !(ctorMods.contains(Modifier.PUBLIC) || ctorMods.contains(Modifier.PROTECTED)))
+                        || ctorMods.contains(Modifier.PRIVATE)) {
+                    continue;
+                }
+                inheritedConstructors.add(executableElement);
+            }
+        }
+        List<QuickPickItem> constructors = null;
+        if (typeElement.getKind() != ElementKind.ENUM && inheritedConstructors.size() == 1) {
+            QuickPickItem item = new QuickPickItem(createLabel(info, inheritedConstructors.get(0)));
+            item.setUserData(new ElementData(inheritedConstructors.get(0)));
+            constructors = Collections.singletonList(item);
+        } else if (inheritedConstructors.size() > 1) {
+            constructors = new ArrayList<>(inheritedConstructors.size());
+            for (ExecutableElement constructorElement : inheritedConstructors) {
+                QuickPickItem item = new QuickPickItem(createLabel(info, constructorElement));
+                item.setUserData(new ElementData(constructorElement));
+                constructors.add(item);
+            }
+        }
+        List<QuickPickItem> fields = null;
+        if (!uninitializedFields.isEmpty()) {
+            fields = new ArrayList<>();
+            for (VariableElement variableElement : uninitializedFields) {
+                QuickPickItem item = new QuickPickItem(createLabel(info, variableElement));
+                item.setUserData(new ElementData(variableElement));
+                fields.add(item);
+            }
+        }
+        if (constructors == null && fields == null) {
+            return Collections.emptyList();
+        }
+        String uri = Utils.toUri(info.getFileObject());
+        return Collections.singletonList(createCodeAction(Bundle.DN_GenerateConstructor(), CODE_GENERATOR_KIND, GENERATE_CONSTRUCTOR, uri, offset, constructors, fields));
+    }
+
+    @Override
+    public Set<String> getCommands() {
+        return commands;
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_SelectSuperConstructor=Select super constructor",
+    })
+    public CompletableFuture<Object> processCommand(NbCodeLanguageClient client, String command, List<Object> arguments) {
+        if (arguments.size() > 3) {
+            String uri = gson.fromJson(gson.toJson(arguments.get(0)), String.class);
+            int offset = gson.fromJson(gson.toJson(arguments.get(1)), Integer.class);
+            List<QuickPickItem> constructors = Arrays.asList(gson.fromJson(gson.toJson(arguments.get(2)), QuickPickItem[].class));
+            List<QuickPickItem> fields = Arrays.asList(gson.fromJson(gson.toJson(arguments.get(3)), QuickPickItem[].class));
+            if (constructors.size() < 2 && fields.isEmpty()) {
+                generate(client, uri, offset, constructors, fields);
+            } else {
+                if (constructors.size() > 1) {
+                    client.showQuickPick(new ShowQuickPickParams(Bundle.DN_SelectSuperConstructor(), true, constructors)).thenAccept(selected -> {
+                        if (selected != null) {
+                            selectFields(client, uri, offset, selected, fields);
+                        }
+                    });
+                } else {
+                    selectFields(client, uri, offset, constructors, fields);
+                }
+            }
+        }
+        return CompletableFuture.completedFuture(true);
+    }
+
+    @NbBundle.Messages({
+        "DN_SelectConstructorFields=Select fields to be initialized by constructor",
+    })
+    private void selectFields(NbCodeLanguageClient client, String uri, int offset, List<QuickPickItem> constructors, List<QuickPickItem> fields) {
+        if (!fields.isEmpty()) {
+            client.showQuickPick(new ShowQuickPickParams(Bundle.DN_SelectConstructorFields(), true, fields)).thenAccept(selected -> {
+                if (selected != null) {
+                    generate(client, uri, offset, constructors, selected);
+                }
+            });
+        } else {
+            generate(client, uri, offset, constructors, fields);
+        }
+    }
+
+    private void generate(NbCodeLanguageClient client, String uri, int offset, List<QuickPickItem> constructors, List<QuickPickItem> fields) {
+        try {
+            FileObject file = Utils.fromUri(uri);
+            JavaSource js = JavaSource.forFileObject(file);
+            List<TextEdit> edits = TextDocumentServiceImpl.modify2TextEdits(js, wc -> {

Review comment:
       possibly check `js` for null

##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/LoggerGenerator.java
##########
@@ -0,0 +1,135 @@
+/*
+ * 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.ClassTree;
+import com.sun.source.tree.VariableTree;
+import com.sun.source.util.TreePath;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.logging.Logger;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+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.eclipse.lsp4j.TextEdit;
+import org.eclipse.lsp4j.WorkspaceEdit;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.GeneratorUtilities;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.TreeUtilities;
+import org.netbeans.modules.java.lsp.server.Utils;
+import org.openide.filesystems.FileObject;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public final class LoggerGenerator extends CodeGenerator {
+
+    public static final String GENERATE_LOGGER =  "java.generate.logger";
+
+    private final Set<String> commands = Collections.singleton(GENERATE_LOGGER);
+    private final Gson gson = new Gson();
+
+    LoggerGenerator() {
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_GenerateLogger=Generate Logger...",
+    })
+    public List<CodeAction> getCodeActions(CompilationInfo info, CodeActionParams params) {
+        List<String> only = params.getContext().getOnly();
+        if (only == null || !only.contains(CodeActionKind.Source)) {
+            return Collections.emptyList();
+        }
+        int offset = getOffset(info, params.getRange().getStart());
+        TreePath tp = info.getTreeUtilities().pathFor(offset);
+        tp = info.getTreeUtilities().getPathElementOfKind(TreeUtilities.CLASS_TREE_KINDS, tp);
+        if (tp == null) {
+            return Collections.emptyList();
+        }
+        TypeElement typeElement = (TypeElement) info.getTrees().getElement(tp);
+        if (typeElement == null || !typeElement.getKind().isClass()) {
+            return Collections.emptyList();
+        }
+        for (VariableElement ve : ElementFilter.fieldsIn(typeElement.getEnclosedElements())) {
+            TypeMirror type = ve.asType();
+            if (type.getKind() == TypeKind.DECLARED && ((TypeElement)((DeclaredType)type).asElement()).getQualifiedName().contentEquals(Logger.class.getName())) {
+                return Collections.emptyList();
+            }
+        }
+        String uri = Utils.toUri(info.getFileObject());
+        return Collections.singletonList(createCodeAction(Bundle.DN_GenerateLogger(), CODE_GENERATOR_KIND, GENERATE_LOGGER, uri, offset));
+    }
+
+    @Override
+    public Set<String> getCommands() {
+        return commands;
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_SelectLoggerName=Logger field name",
+    })
+    public CompletableFuture<Object> processCommand(NbCodeLanguageClient client, String command, List<Object> arguments) {
+        if (arguments.size() > 1) {
+            String uri = gson.fromJson(gson.toJson(arguments.get(0)), String.class);
+            int offset = gson.fromJson(gson.toJson(arguments.get(1)), Integer.class);
+            client.showInputBox(new ShowInputBoxParams(Bundle.DN_SelectLoggerName(), "LOG")).thenAccept(value -> {
+                if (value != null) {

Review comment:
       Maybe check for `value` to be a valid identifier ?

##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/DelegateMethodGenerator.java
##########
@@ -0,0 +1,210 @@
+/*
+ * 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.Scope;
+import com.sun.source.util.TreePath;
+import com.sun.source.util.Trees;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+import javax.lang.model.util.Elements;
+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.eclipse.lsp4j.TextEdit;
+import org.eclipse.lsp4j.WorkspaceEdit;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.ElementUtilities;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.TreeUtilities;
+import org.netbeans.modules.java.lsp.server.Utils;
+import org.openide.filesystems.FileObject;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public final class DelegateMethodGenerator extends CodeGenerator {
+
+    public static final String GENERATE_DELEGATE_METHOD =  "java.generate.delegateMethod";
+
+    private final Set<String> commands = Collections.singleton(GENERATE_DELEGATE_METHOD);
+    private final Gson gson = new Gson();
+
+    DelegateMethodGenerator() {
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_GenerateDelegateMethod=Generate Delegate Method...",
+    })
+    public List<CodeAction> getCodeActions(CompilationInfo info, CodeActionParams params) {
+        List<String> only = params.getContext().getOnly();
+        if (only == null || !only.contains(CodeActionKind.Source)) {
+            return Collections.emptyList();
+        }
+        int offset = getOffset(info, params.getRange().getStart());
+        TreePath tp = info.getTreeUtilities().pathFor(offset);
+        tp = info.getTreeUtilities().getPathElementOfKind(TreeUtilities.CLASS_TREE_KINDS, tp);
+        if (tp == null) {
+            return Collections.emptyList();
+        }
+        TypeElement typeElement = (TypeElement) info.getTrees().getElement(tp);
+        if (typeElement == null || !typeElement.getKind().isClass()) {
+            return Collections.emptyList();
+        }
+        Elements elements = info.getElements();
+        Trees trees = info.getTrees();
+        Scope scope = trees.getScope(tp);
+        List<QuickPickItem> fields = new ArrayList<>();
+        TypeElement cls;
+        while (scope != null && (cls = scope.getEnclosingClass()) != null) {
+            DeclaredType type = (DeclaredType) cls.asType();
+            for (VariableElement field : ElementFilter.fieldsIn(elements.getAllMembers(cls))) {
+                TypeMirror fieldType = field.asType();
+                if (!ERROR.contentEquals(field.getSimpleName()) && !fieldType.getKind().isPrimitive() && fieldType.getKind() != TypeKind.ARRAY
+                        && (fieldType.getKind() != TypeKind.DECLARED || ((DeclaredType)fieldType).asElement() != cls) && trees.isAccessible(scope, field, type)) {
+                    QuickPickItem item = new QuickPickItem(createLabel(info, field));
+                    item.setUserData(new ElementData(field));
+                    fields.add(item);
+                }
+            }
+            scope = scope.getEnclosingScope();
+        }
+        if (fields.isEmpty()) {
+            return Collections.emptyList();
+        }
+        String uri = Utils.toUri(info.getFileObject());
+        QuickPickItem typeItem = new QuickPickItem(createLabel(info, typeElement));
+        typeItem.setUserData(new ElementData(typeElement));
+        return Collections.singletonList(createCodeAction(Bundle.DN_GenerateDelegateMethod(), CODE_GENERATOR_KIND, GENERATE_DELEGATE_METHOD, uri, offset, typeItem, fields));
+    }
+
+    @Override
+    public Set<String> getCommands() {
+        return commands;
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_SelectDelegateMethodField=Select target field to generate delegates for",
+    })
+    public CompletableFuture<Object> processCommand(NbCodeLanguageClient client, String command, List<Object> arguments) {
+        if (arguments.size() > 3) {
+            String uri = gson.fromJson(gson.toJson(arguments.get(0)), String.class);
+            int offset = gson.fromJson(gson.toJson(arguments.get(1)), Integer.class);
+            QuickPickItem type = gson.fromJson(gson.toJson(arguments.get(2)), QuickPickItem.class);
+            List<QuickPickItem> fields = Arrays.asList(gson.fromJson(gson.toJson(arguments.get(3)), QuickPickItem[].class));
+            if (fields.size() == 1) {
+                selectMethods(client, uri, offset, type, fields.get(0));
+            } else {
+                client.showQuickPick(new ShowQuickPickParams(Bundle.DN_SelectDelegateMethodField(), false, fields)).thenAccept(selected -> {
+                    if (selected != null && !selected.isEmpty()) {
+                        selectMethods(client, uri, offset, type, selected.get(0));
+                    }
+                });
+            }
+        }
+        return CompletableFuture.completedFuture(true);
+    }
+
+    @NbBundle.Messages({
+        "DN_SelectDelegateMethods=Select methods to generate delegates for",
+    })
+    private void selectMethods(NbCodeLanguageClient client, String uri, int offset, QuickPickItem type, QuickPickItem selectedField) {
+        try {
+            FileObject file = Utils.fromUri(uri);
+            JavaSource js = JavaSource.forFileObject(file);
+            js.runUserActionTask(info -> {

Review comment:
       Check for null.

##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/DelegateMethodGenerator.java
##########
@@ -0,0 +1,210 @@
+/*
+ * 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.Scope;
+import com.sun.source.util.TreePath;
+import com.sun.source.util.Trees;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+import javax.lang.model.util.Elements;
+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.eclipse.lsp4j.TextEdit;
+import org.eclipse.lsp4j.WorkspaceEdit;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.ElementUtilities;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.TreeUtilities;
+import org.netbeans.modules.java.lsp.server.Utils;
+import org.openide.filesystems.FileObject;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public final class DelegateMethodGenerator extends CodeGenerator {
+
+    public static final String GENERATE_DELEGATE_METHOD =  "java.generate.delegateMethod";
+
+    private final Set<String> commands = Collections.singleton(GENERATE_DELEGATE_METHOD);
+    private final Gson gson = new Gson();
+
+    DelegateMethodGenerator() {
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_GenerateDelegateMethod=Generate Delegate Method...",
+    })
+    public List<CodeAction> getCodeActions(CompilationInfo info, CodeActionParams params) {
+        List<String> only = params.getContext().getOnly();
+        if (only == null || !only.contains(CodeActionKind.Source)) {
+            return Collections.emptyList();
+        }
+        int offset = getOffset(info, params.getRange().getStart());
+        TreePath tp = info.getTreeUtilities().pathFor(offset);
+        tp = info.getTreeUtilities().getPathElementOfKind(TreeUtilities.CLASS_TREE_KINDS, tp);
+        if (tp == null) {
+            return Collections.emptyList();
+        }
+        TypeElement typeElement = (TypeElement) info.getTrees().getElement(tp);
+        if (typeElement == null || !typeElement.getKind().isClass()) {
+            return Collections.emptyList();
+        }
+        Elements elements = info.getElements();
+        Trees trees = info.getTrees();
+        Scope scope = trees.getScope(tp);
+        List<QuickPickItem> fields = new ArrayList<>();
+        TypeElement cls;
+        while (scope != null && (cls = scope.getEnclosingClass()) != null) {
+            DeclaredType type = (DeclaredType) cls.asType();
+            for (VariableElement field : ElementFilter.fieldsIn(elements.getAllMembers(cls))) {
+                TypeMirror fieldType = field.asType();
+                if (!ERROR.contentEquals(field.getSimpleName()) && !fieldType.getKind().isPrimitive() && fieldType.getKind() != TypeKind.ARRAY
+                        && (fieldType.getKind() != TypeKind.DECLARED || ((DeclaredType)fieldType).asElement() != cls) && trees.isAccessible(scope, field, type)) {
+                    QuickPickItem item = new QuickPickItem(createLabel(info, field));
+                    item.setUserData(new ElementData(field));
+                    fields.add(item);
+                }
+            }
+            scope = scope.getEnclosingScope();
+        }
+        if (fields.isEmpty()) {
+            return Collections.emptyList();
+        }
+        String uri = Utils.toUri(info.getFileObject());
+        QuickPickItem typeItem = new QuickPickItem(createLabel(info, typeElement));
+        typeItem.setUserData(new ElementData(typeElement));
+        return Collections.singletonList(createCodeAction(Bundle.DN_GenerateDelegateMethod(), CODE_GENERATOR_KIND, GENERATE_DELEGATE_METHOD, uri, offset, typeItem, fields));
+    }
+
+    @Override
+    public Set<String> getCommands() {
+        return commands;
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_SelectDelegateMethodField=Select target field to generate delegates for",
+    })
+    public CompletableFuture<Object> processCommand(NbCodeLanguageClient client, String command, List<Object> arguments) {
+        if (arguments.size() > 3) {

Review comment:
       Q: shouldn't the server log a message, if client sends malformed msg (too few arguments) ?

##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeGenerator.java
##########
@@ -0,0 +1,215 @@
+/*
+ * 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.sun.source.tree.LineMap;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.TypeParameterElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import org.eclipse.lsp4j.CodeAction;
+import org.eclipse.lsp4j.CodeActionParams;
+import org.eclipse.lsp4j.Command;
+import org.eclipse.lsp4j.Position;
+import org.eclipse.xtext.xbase.lib.Pure;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.ElementHandle;
+import org.netbeans.modules.editor.java.Utilities;
+import org.netbeans.modules.java.source.ElementHandleAccessor;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public abstract class CodeGenerator {
+
+    public static final String CODE_GENERATOR_KIND = "source.generate";
+    protected static final String ERROR = "<error>"; //NOI18N
+
+    public abstract List<CodeAction> getCodeActions(CompilationInfo info, CodeActionParams params);
+
+    public abstract Set<String> getCommands();
+
+    public abstract CompletableFuture<Object> processCommand(NbCodeLanguageClient client, String command, List<Object> arguments);
+
+    protected static int getOffset(CompilationInfo info, Position pos) {
+        LineMap lm = info.getCompilationUnit().getLineMap();
+        return (int) lm.getPosition(pos.getLine() + 1, pos.getCharacter() + 1);
+    }
+
+    protected static CodeAction createCodeAction(String name, String kind, String command, Object... args) {
+        CodeAction action = new CodeAction(name);
+        action.setKind(kind);
+        action.setCommand(new Command(name, command, Arrays.asList(args)));
+        return action;
+    }
+
+    protected static String createLabel(CompilationInfo info, TypeElement e) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(e.getSimpleName());
+        List<? extends TypeParameterElement> typeParams = e.getTypeParameters();
+        if (typeParams != null && !typeParams.isEmpty()) {
+            sb.append("<"); // NOI18N
+            for(Iterator<? extends TypeParameterElement> it = typeParams.iterator(); it.hasNext();) {
+                TypeParameterElement tp = it.next();
+                sb.append(tp.getSimpleName());
+                List<? extends TypeMirror> bounds = tp.getBounds();
+                if (!bounds.isEmpty()) {
+                    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();) {

Review comment:
       Nitpick: the TypeMirror may be erroneous (TypeKind.ERROR)

##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/DelegateMethodGenerator.java
##########
@@ -0,0 +1,210 @@
+/*
+ * 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.Scope;
+import com.sun.source.util.TreePath;
+import com.sun.source.util.Trees;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+import javax.lang.model.util.Elements;
+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.eclipse.lsp4j.TextEdit;
+import org.eclipse.lsp4j.WorkspaceEdit;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.ElementUtilities;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.TreeUtilities;
+import org.netbeans.modules.java.lsp.server.Utils;
+import org.openide.filesystems.FileObject;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public final class DelegateMethodGenerator extends CodeGenerator {
+
+    public static final String GENERATE_DELEGATE_METHOD =  "java.generate.delegateMethod";
+
+    private final Set<String> commands = Collections.singleton(GENERATE_DELEGATE_METHOD);
+    private final Gson gson = new Gson();
+
+    DelegateMethodGenerator() {
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_GenerateDelegateMethod=Generate Delegate Method...",
+    })
+    public List<CodeAction> getCodeActions(CompilationInfo info, CodeActionParams params) {
+        List<String> only = params.getContext().getOnly();
+        if (only == null || !only.contains(CodeActionKind.Source)) {
+            return Collections.emptyList();
+        }
+        int offset = getOffset(info, params.getRange().getStart());
+        TreePath tp = info.getTreeUtilities().pathFor(offset);
+        tp = info.getTreeUtilities().getPathElementOfKind(TreeUtilities.CLASS_TREE_KINDS, tp);
+        if (tp == null) {
+            return Collections.emptyList();
+        }
+        TypeElement typeElement = (TypeElement) info.getTrees().getElement(tp);
+        if (typeElement == null || !typeElement.getKind().isClass()) {
+            return Collections.emptyList();
+        }
+        Elements elements = info.getElements();
+        Trees trees = info.getTrees();
+        Scope scope = trees.getScope(tp);
+        List<QuickPickItem> fields = new ArrayList<>();
+        TypeElement cls;
+        while (scope != null && (cls = scope.getEnclosingClass()) != null) {
+            DeclaredType type = (DeclaredType) cls.asType();
+            for (VariableElement field : ElementFilter.fieldsIn(elements.getAllMembers(cls))) {
+                TypeMirror fieldType = field.asType();
+                if (!ERROR.contentEquals(field.getSimpleName()) && !fieldType.getKind().isPrimitive() && fieldType.getKind() != TypeKind.ARRAY
+                        && (fieldType.getKind() != TypeKind.DECLARED || ((DeclaredType)fieldType).asElement() != cls) && trees.isAccessible(scope, field, type)) {
+                    QuickPickItem item = new QuickPickItem(createLabel(info, field));
+                    item.setUserData(new ElementData(field));
+                    fields.add(item);
+                }
+            }
+            scope = scope.getEnclosingScope();
+        }
+        if (fields.isEmpty()) {
+            return Collections.emptyList();
+        }
+        String uri = Utils.toUri(info.getFileObject());
+        QuickPickItem typeItem = new QuickPickItem(createLabel(info, typeElement));
+        typeItem.setUserData(new ElementData(typeElement));
+        return Collections.singletonList(createCodeAction(Bundle.DN_GenerateDelegateMethod(), CODE_GENERATOR_KIND, GENERATE_DELEGATE_METHOD, uri, offset, typeItem, fields));
+    }
+
+    @Override
+    public Set<String> getCommands() {
+        return commands;
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_SelectDelegateMethodField=Select target field to generate delegates for",
+    })
+    public CompletableFuture<Object> processCommand(NbCodeLanguageClient client, String command, List<Object> arguments) {
+        if (arguments.size() > 3) {
+            String uri = gson.fromJson(gson.toJson(arguments.get(0)), String.class);
+            int offset = gson.fromJson(gson.toJson(arguments.get(1)), Integer.class);
+            QuickPickItem type = gson.fromJson(gson.toJson(arguments.get(2)), QuickPickItem.class);
+            List<QuickPickItem> fields = Arrays.asList(gson.fromJson(gson.toJson(arguments.get(3)), QuickPickItem[].class));
+            if (fields.size() == 1) {
+                selectMethods(client, uri, offset, type, fields.get(0));
+            } else {
+                client.showQuickPick(new ShowQuickPickParams(Bundle.DN_SelectDelegateMethodField(), false, fields)).thenAccept(selected -> {
+                    if (selected != null && !selected.isEmpty()) {
+                        selectMethods(client, uri, offset, type, selected.get(0));
+                    }
+                });
+            }
+        }
+        return CompletableFuture.completedFuture(true);
+    }
+
+    @NbBundle.Messages({
+        "DN_SelectDelegateMethods=Select methods to generate delegates for",
+    })
+    private void selectMethods(NbCodeLanguageClient client, String uri, int offset, QuickPickItem type, QuickPickItem selectedField) {
+        try {
+            FileObject file = Utils.fromUri(uri);
+            JavaSource js = JavaSource.forFileObject(file);
+            js.runUserActionTask(info -> {
+                info.toPhase(JavaSource.Phase.RESOLVED);
+                TypeElement origin = (TypeElement) gson.fromJson(gson.toJson(type.getUserData()), ElementData.class).resolve(info);
+                VariableElement field = (VariableElement) gson.fromJson(gson.toJson(selectedField.getUserData()), ElementData.class).resolve(info);
+                final ElementUtilities eu = info.getElementUtilities();
+                final Trees trees = info.getTrees();
+                final Scope scope = info.getTreeUtilities().scopeFor(offset);
+                ElementUtilities.ElementAcceptor acceptor = new ElementUtilities.ElementAcceptor() {
+                    @Override
+                    public boolean accept(Element e, TypeMirror type) {
+                        if (e.getKind() == ElementKind.METHOD && trees.isAccessible(scope, e, (DeclaredType)type)) {
+                            Element impl = eu.getImplementationOf((ExecutableElement)e, origin);
+                            return impl == null || (!impl.getModifiers().contains(Modifier.FINAL) && impl.getEnclosingElement() != origin);
+                        }
+                        return false;
+                    }
+                };
+                List<QuickPickItem> methods = new ArrayList<>();
+                for (ExecutableElement method : ElementFilter.methodsIn(eu.getMembers(field.asType(), acceptor))) {
+                    QuickPickItem item = new QuickPickItem(String.format("%s.%s", field.getSimpleName().toString(), createLabel(info, method)));
+                    item.setUserData(new ElementData(method));
+                    methods.add(item);
+                }
+                client.showQuickPick(new ShowQuickPickParams(Bundle.DN_SelectDelegateMethods(), true, methods)).thenAccept(selected -> {
+                    if (selected != null && !selected.isEmpty())
+                    generate(client, uri, offset, selectedField, selected);
+                });
+            }, true);
+        } catch (IOException | IllegalArgumentException ex) {
+            client.logMessage(new MessageParams(MessageType.Error, ex.getLocalizedMessage()));
+        }
+    }
+
+    private void generate(NbCodeLanguageClient client, String uri, int offset, QuickPickItem selectedField, List<QuickPickItem> selectedMethods) {
+        try {
+            FileObject file = Utils.fromUri(uri);
+            JavaSource js = JavaSource.forFileObject(file);
+            List<TextEdit> edits = TextDocumentServiceImpl.modify2TextEdits(js, wc -> {
+                wc.toPhase(JavaSource.Phase.RESOLVED);
+                TreePath tp = wc.getTreeUtilities().pathFor(offset);
+                tp = wc.getTreeUtilities().getPathElementOfKind(TreeUtilities.CLASS_TREE_KINDS, tp);
+                if (tp != null) {
+                    VariableElement field = (VariableElement) gson.fromJson(gson.toJson(selectedField.getUserData()), ElementData.class).resolve(wc);
+                    List<ExecutableElement> methods = selectedMethods.stream().map(item -> {
+                        ElementData data = gson.fromJson(gson.toJson(item.getUserData()), ElementData.class);
+                        return (ExecutableElement)data.resolve(wc);
+                    }).collect(Collectors.toList());
+                    org.netbeans.modules.java.editor.codegen.DelegateMethodGenerator.generateDelegatingMethods(wc, tp, field, methods, -1);
+                }
+            });
+            client.applyEdit(new ApplyWorkspaceEditParams(new WorkspaceEdit(Collections.singletonMap(uri, edits))));

Review comment:
       Consider if TextEdit + error handling for `generate` could be centralized for all CodeGenerators.

##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/DelegateMethodGenerator.java
##########
@@ -0,0 +1,210 @@
+/*
+ * 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.Scope;
+import com.sun.source.util.TreePath;
+import com.sun.source.util.Trees;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+import javax.lang.model.util.Elements;
+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.eclipse.lsp4j.TextEdit;
+import org.eclipse.lsp4j.WorkspaceEdit;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.ElementUtilities;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.TreeUtilities;
+import org.netbeans.modules.java.lsp.server.Utils;
+import org.openide.filesystems.FileObject;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public final class DelegateMethodGenerator extends CodeGenerator {
+
+    public static final String GENERATE_DELEGATE_METHOD =  "java.generate.delegateMethod";
+
+    private final Set<String> commands = Collections.singleton(GENERATE_DELEGATE_METHOD);
+    private final Gson gson = new Gson();
+
+    DelegateMethodGenerator() {
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_GenerateDelegateMethod=Generate Delegate Method...",
+    })
+    public List<CodeAction> getCodeActions(CompilationInfo info, CodeActionParams params) {
+        List<String> only = params.getContext().getOnly();
+        if (only == null || !only.contains(CodeActionKind.Source)) {
+            return Collections.emptyList();
+        }
+        int offset = getOffset(info, params.getRange().getStart());
+        TreePath tp = info.getTreeUtilities().pathFor(offset);
+        tp = info.getTreeUtilities().getPathElementOfKind(TreeUtilities.CLASS_TREE_KINDS, tp);
+        if (tp == null) {
+            return Collections.emptyList();
+        }
+        TypeElement typeElement = (TypeElement) info.getTrees().getElement(tp);
+        if (typeElement == null || !typeElement.getKind().isClass()) {
+            return Collections.emptyList();
+        }
+        Elements elements = info.getElements();
+        Trees trees = info.getTrees();
+        Scope scope = trees.getScope(tp);
+        List<QuickPickItem> fields = new ArrayList<>();
+        TypeElement cls;
+        while (scope != null && (cls = scope.getEnclosingClass()) != null) {
+            DeclaredType type = (DeclaredType) cls.asType();
+            for (VariableElement field : ElementFilter.fieldsIn(elements.getAllMembers(cls))) {
+                TypeMirror fieldType = field.asType();
+                if (!ERROR.contentEquals(field.getSimpleName()) && !fieldType.getKind().isPrimitive() && fieldType.getKind() != TypeKind.ARRAY
+                        && (fieldType.getKind() != TypeKind.DECLARED || ((DeclaredType)fieldType).asElement() != cls) && trees.isAccessible(scope, field, type)) {
+                    QuickPickItem item = new QuickPickItem(createLabel(info, field));
+                    item.setUserData(new ElementData(field));
+                    fields.add(item);
+                }
+            }
+            scope = scope.getEnclosingScope();
+        }
+        if (fields.isEmpty()) {
+            return Collections.emptyList();
+        }
+        String uri = Utils.toUri(info.getFileObject());
+        QuickPickItem typeItem = new QuickPickItem(createLabel(info, typeElement));
+        typeItem.setUserData(new ElementData(typeElement));
+        return Collections.singletonList(createCodeAction(Bundle.DN_GenerateDelegateMethod(), CODE_GENERATOR_KIND, GENERATE_DELEGATE_METHOD, uri, offset, typeItem, fields));
+    }
+
+    @Override
+    public Set<String> getCommands() {
+        return commands;
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_SelectDelegateMethodField=Select target field to generate delegates for",
+    })
+    public CompletableFuture<Object> processCommand(NbCodeLanguageClient client, String command, List<Object> arguments) {
+        if (arguments.size() > 3) {
+            String uri = gson.fromJson(gson.toJson(arguments.get(0)), String.class);
+            int offset = gson.fromJson(gson.toJson(arguments.get(1)), Integer.class);
+            QuickPickItem type = gson.fromJson(gson.toJson(arguments.get(2)), QuickPickItem.class);
+            List<QuickPickItem> fields = Arrays.asList(gson.fromJson(gson.toJson(arguments.get(3)), QuickPickItem[].class));
+            if (fields.size() == 1) {
+                selectMethods(client, uri, offset, type, fields.get(0));
+            } else {
+                client.showQuickPick(new ShowQuickPickParams(Bundle.DN_SelectDelegateMethodField(), false, fields)).thenAccept(selected -> {
+                    if (selected != null && !selected.isEmpty()) {
+                        selectMethods(client, uri, offset, type, selected.get(0));
+                    }
+                });
+            }
+        }
+        return CompletableFuture.completedFuture(true);
+    }
+
+    @NbBundle.Messages({
+        "DN_SelectDelegateMethods=Select methods to generate delegates for",
+    })
+    private void selectMethods(NbCodeLanguageClient client, String uri, int offset, QuickPickItem type, QuickPickItem selectedField) {
+        try {
+            FileObject file = Utils.fromUri(uri);
+            JavaSource js = JavaSource.forFileObject(file);
+            js.runUserActionTask(info -> {
+                info.toPhase(JavaSource.Phase.RESOLVED);
+                TypeElement origin = (TypeElement) gson.fromJson(gson.toJson(type.getUserData()), ElementData.class).resolve(info);
+                VariableElement field = (VariableElement) gson.fromJson(gson.toJson(selectedField.getUserData()), ElementData.class).resolve(info);
+                final ElementUtilities eu = info.getElementUtilities();
+                final Trees trees = info.getTrees();
+                final Scope scope = info.getTreeUtilities().scopeFor(offset);
+                ElementUtilities.ElementAcceptor acceptor = new ElementUtilities.ElementAcceptor() {
+                    @Override
+                    public boolean accept(Element e, TypeMirror type) {
+                        if (e.getKind() == ElementKind.METHOD && trees.isAccessible(scope, e, (DeclaredType)type)) {
+                            Element impl = eu.getImplementationOf((ExecutableElement)e, origin);
+                            return impl == null || (!impl.getModifiers().contains(Modifier.FINAL) && impl.getEnclosingElement() != origin);
+                        }
+                        return false;
+                    }
+                };
+                List<QuickPickItem> methods = new ArrayList<>();
+                for (ExecutableElement method : ElementFilter.methodsIn(eu.getMembers(field.asType(), acceptor))) {
+                    QuickPickItem item = new QuickPickItem(String.format("%s.%s", field.getSimpleName().toString(), createLabel(info, method)));
+                    item.setUserData(new ElementData(method));
+                    methods.add(item);
+                }
+                client.showQuickPick(new ShowQuickPickParams(Bundle.DN_SelectDelegateMethods(), true, methods)).thenAccept(selected -> {
+                    if (selected != null && !selected.isEmpty())

Review comment:
       Nitpick: add braces or indent :)

##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ConstructorGenerator.java
##########
@@ -0,0 +1,210 @@
+/*
+ * 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.util.TreePath;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.NestingKind;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+import javax.lang.model.util.Elements;
+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.eclipse.lsp4j.TextEdit;
+import org.eclipse.lsp4j.WorkspaceEdit;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.TreeUtilities;
+import org.netbeans.modules.java.editor.codegen.GeneratorUtils;
+import org.netbeans.modules.java.lsp.server.Utils;
+import org.openide.filesystems.FileObject;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public final class ConstructorGenerator extends CodeGenerator {
+
+    public static final String GENERATE_CONSTRUCTOR =  "java.generate.constructor";
+
+    private final Set<String> commands = Collections.singleton(GENERATE_CONSTRUCTOR);
+    private final Gson gson = new Gson();
+
+    ConstructorGenerator() {
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_GenerateConstructor=Generate Constructor...",
+    })
+    public List<CodeAction> getCodeActions(CompilationInfo info, CodeActionParams params) {
+        List<String> only = params.getContext().getOnly();
+        if (only == null || !only.contains(CodeActionKind.Source)) {
+            return Collections.emptyList();
+        }
+        int offset = getOffset(info, params.getRange().getStart());
+        TreePath tp = info.getTreeUtilities().pathFor(offset);
+        tp = info.getTreeUtilities().getPathElementOfKind(TreeUtilities.CLASS_TREE_KINDS, tp);
+        if (tp == null) {
+            return Collections.emptyList();
+        }
+        TypeElement typeElement = (TypeElement) info.getTrees().getElement(tp);
+        if (typeElement == null || !typeElement.getKind().isClass() || NestingKind.ANONYMOUS.equals(typeElement.getNestingKind())) {
+            return Collections.emptyList();
+        }
+        final Set<? extends VariableElement> uninitializedFields = info.getTreeUtilities().getUninitializedFields(tp);
+        final List<ExecutableElement> inheritedConstructors = new ArrayList<>();
+        TypeMirror superClassType = typeElement.getSuperclass();
+        if (superClassType.getKind() == TypeKind.DECLARED) {
+            TypeElement superClass = (TypeElement) ((DeclaredType) superClassType).asElement();
+            Elements elements = info.getElements();
+            for (ExecutableElement executableElement : ElementFilter.constructorsIn(superClass.getEnclosedElements())) {
+                PackageElement currentPackage = elements.getPackageOf(typeElement);
+                PackageElement ctorPackage = elements.getPackageOf(executableElement);
+                Set<Modifier> ctorMods = executableElement.getModifiers();
+                if ((currentPackage != ctorPackage && !(ctorMods.contains(Modifier.PUBLIC) || ctorMods.contains(Modifier.PROTECTED)))
+                        || ctorMods.contains(Modifier.PRIVATE)) {
+                    continue;
+                }
+                inheritedConstructors.add(executableElement);
+            }
+        }
+        List<QuickPickItem> constructors = null;
+        if (typeElement.getKind() != ElementKind.ENUM && inheritedConstructors.size() == 1) {
+            QuickPickItem item = new QuickPickItem(createLabel(info, inheritedConstructors.get(0)));

Review comment:
       the branches seem to have the same effect; just `ElementKind.ENUM` with  exactly 1 constructor is skipped. OK ?

##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ConstructorGenerator.java
##########
@@ -0,0 +1,210 @@
+/*
+ * 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.util.TreePath;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.NestingKind;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+import javax.lang.model.util.Elements;
+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.eclipse.lsp4j.TextEdit;
+import org.eclipse.lsp4j.WorkspaceEdit;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.TreeUtilities;
+import org.netbeans.modules.java.editor.codegen.GeneratorUtils;
+import org.netbeans.modules.java.lsp.server.Utils;
+import org.openide.filesystems.FileObject;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public final class ConstructorGenerator extends CodeGenerator {
+
+    public static final String GENERATE_CONSTRUCTOR =  "java.generate.constructor";
+
+    private final Set<String> commands = Collections.singleton(GENERATE_CONSTRUCTOR);
+    private final Gson gson = new Gson();
+
+    ConstructorGenerator() {
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_GenerateConstructor=Generate Constructor...",
+    })
+    public List<CodeAction> getCodeActions(CompilationInfo info, CodeActionParams params) {
+        List<String> only = params.getContext().getOnly();
+        if (only == null || !only.contains(CodeActionKind.Source)) {
+            return Collections.emptyList();
+        }
+        int offset = getOffset(info, params.getRange().getStart());
+        TreePath tp = info.getTreeUtilities().pathFor(offset);
+        tp = info.getTreeUtilities().getPathElementOfKind(TreeUtilities.CLASS_TREE_KINDS, tp);
+        if (tp == null) {
+            return Collections.emptyList();
+        }
+        TypeElement typeElement = (TypeElement) info.getTrees().getElement(tp);
+        if (typeElement == null || !typeElement.getKind().isClass() || NestingKind.ANONYMOUS.equals(typeElement.getNestingKind())) {
+            return Collections.emptyList();
+        }
+        final Set<? extends VariableElement> uninitializedFields = info.getTreeUtilities().getUninitializedFields(tp);
+        final List<ExecutableElement> inheritedConstructors = new ArrayList<>();
+        TypeMirror superClassType = typeElement.getSuperclass();
+        if (superClassType.getKind() == TypeKind.DECLARED) {
+            TypeElement superClass = (TypeElement) ((DeclaredType) superClassType).asElement();
+            Elements elements = info.getElements();
+            for (ExecutableElement executableElement : ElementFilter.constructorsIn(superClass.getEnclosedElements())) {
+                PackageElement currentPackage = elements.getPackageOf(typeElement);
+                PackageElement ctorPackage = elements.getPackageOf(executableElement);
+                Set<Modifier> ctorMods = executableElement.getModifiers();
+                if ((currentPackage != ctorPackage && !(ctorMods.contains(Modifier.PUBLIC) || ctorMods.contains(Modifier.PROTECTED)))
+                        || ctorMods.contains(Modifier.PRIVATE)) {
+                    continue;
+                }
+                inheritedConstructors.add(executableElement);
+            }
+        }
+        List<QuickPickItem> constructors = null;
+        if (typeElement.getKind() != ElementKind.ENUM && inheritedConstructors.size() == 1) {
+            QuickPickItem item = new QuickPickItem(createLabel(info, inheritedConstructors.get(0)));
+            item.setUserData(new ElementData(inheritedConstructors.get(0)));
+            constructors = Collections.singletonList(item);
+        } else if (inheritedConstructors.size() > 1) {
+            constructors = new ArrayList<>(inheritedConstructors.size());
+            for (ExecutableElement constructorElement : inheritedConstructors) {
+                QuickPickItem item = new QuickPickItem(createLabel(info, constructorElement));
+                item.setUserData(new ElementData(constructorElement));
+                constructors.add(item);
+            }
+        }
+        List<QuickPickItem> fields = null;
+        if (!uninitializedFields.isEmpty()) {
+            fields = new ArrayList<>();
+            for (VariableElement variableElement : uninitializedFields) {
+                QuickPickItem item = new QuickPickItem(createLabel(info, variableElement));
+                item.setUserData(new ElementData(variableElement));
+                fields.add(item);
+            }
+        }
+        if (constructors == null && fields == null) {
+            return Collections.emptyList();
+        }
+        String uri = Utils.toUri(info.getFileObject());
+        return Collections.singletonList(createCodeAction(Bundle.DN_GenerateConstructor(), CODE_GENERATOR_KIND, GENERATE_CONSTRUCTOR, uri, offset, constructors, fields));
+    }
+
+    @Override
+    public Set<String> getCommands() {
+        return commands;
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_SelectSuperConstructor=Select super constructor",
+    })
+    public CompletableFuture<Object> processCommand(NbCodeLanguageClient client, String command, List<Object> arguments) {
+        if (arguments.size() > 3) {
+            String uri = gson.fromJson(gson.toJson(arguments.get(0)), String.class);
+            int offset = gson.fromJson(gson.toJson(arguments.get(1)), Integer.class);
+            List<QuickPickItem> constructors = Arrays.asList(gson.fromJson(gson.toJson(arguments.get(2)), QuickPickItem[].class));
+            List<QuickPickItem> fields = Arrays.asList(gson.fromJson(gson.toJson(arguments.get(3)), QuickPickItem[].class));
+            if (constructors.size() < 2 && fields.isEmpty()) {
+                generate(client, uri, offset, constructors, fields);
+            } else {
+                if (constructors.size() > 1) {
+                    client.showQuickPick(new ShowQuickPickParams(Bundle.DN_SelectSuperConstructor(), true, constructors)).thenAccept(selected -> {
+                        if (selected != null) {
+                            selectFields(client, uri, offset, selected, fields);
+                        }
+                    });
+                } else {
+                    selectFields(client, uri, offset, constructors, fields);
+                }
+            }
+        }
+        return CompletableFuture.completedFuture(true);
+    }
+
+    @NbBundle.Messages({
+        "DN_SelectConstructorFields=Select fields to be initialized by constructor",
+    })
+    private void selectFields(NbCodeLanguageClient client, String uri, int offset, List<QuickPickItem> constructors, List<QuickPickItem> fields) {
+        if (!fields.isEmpty()) {
+            client.showQuickPick(new ShowQuickPickParams(Bundle.DN_SelectConstructorFields(), true, fields)).thenAccept(selected -> {
+                if (selected != null) {
+                    generate(client, uri, offset, constructors, selected);
+                }
+            });
+        } else {
+            generate(client, uri, offset, constructors, fields);
+        }
+    }
+
+    private void generate(NbCodeLanguageClient client, String uri, int offset, List<QuickPickItem> constructors, List<QuickPickItem> fields) {
+        try {
+            FileObject file = Utils.fromUri(uri);
+            JavaSource js = JavaSource.forFileObject(file);
+            List<TextEdit> edits = TextDocumentServiceImpl.modify2TextEdits(js, wc -> {
+                wc.toPhase(JavaSource.Phase.RESOLVED);
+                TreePath tp = wc.getTreeUtilities().pathFor(offset);
+                tp = wc.getTreeUtilities().getPathElementOfKind(TreeUtilities.CLASS_TREE_KINDS, tp);
+                if (tp != null) {
+                    List<ExecutableElement> selectedConstructors = constructors.stream().map(item -> {
+                        ElementData data = gson.fromJson(gson.toJson(item.getUserData()), ElementData.class);
+                        return (ExecutableElement)data.resolve(wc);
+                    }).collect(Collectors.toList());

Review comment:
       Maybe filter for `null` result of Handle.resolve, `generateConstructors` does not accept null list members.

##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/GetterSetterGenerator.java
##########
@@ -18,58 +18,175 @@
  */
 package org.netbeans.modules.java.lsp.server.protocol;
 
+import com.google.gson.Gson;
 import com.sun.source.tree.ClassTree;
-import com.sun.source.tree.LineMap;
 import com.sun.source.tree.Tree;
 import com.sun.source.util.TreePath;
 import java.io.IOException;
-import java.net.MalformedURLException;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
 import javax.lang.model.element.Modifier;
 import javax.lang.model.element.TypeElement;
 import javax.lang.model.element.VariableElement;
 import javax.lang.model.util.ElementFilter;
 import org.eclipse.lsp4j.ApplyWorkspaceEditParams;
-import org.eclipse.lsp4j.Position;
+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.eclipse.lsp4j.Range;
 import org.eclipse.lsp4j.TextEdit;
 import org.eclipse.lsp4j.WorkspaceEdit;
-import org.eclipse.lsp4j.services.LanguageClient;
 import org.netbeans.api.java.source.CodeStyle;
 import org.netbeans.api.java.source.CompilationInfo;
 import org.netbeans.api.java.source.ElementUtilities;
 import org.netbeans.api.java.source.JavaSource;
 import org.netbeans.api.java.source.TreeUtilities;
 import org.netbeans.modules.java.editor.codegen.GeneratorUtils;
-import static org.netbeans.modules.java.lsp.server.protocol.TextDocumentServiceImpl.fromUri;
+import org.netbeans.modules.java.lsp.server.Utils;
 import org.openide.filesystems.FileObject;
+import org.openide.util.NbBundle;
 import org.openide.util.Pair;
 
 /**
  *
  * @author lahvac
  */
-public class GetterSetterGenerator {
+public final class GetterSetterGenerator extends CodeGenerator {
 
-    private static final String ERROR = "<error>"; //NOI18N
+    public static final String GENERATE_GETTERS =  "java.generate.getters";
+    public static final String GENERATE_SETTERS =  "java.generate.setters";
+    public static final String GENERATE_GETTERS_SETTERS =  "java.generate.getters.setters";
 
-    public static Pair<Set<VariableElement>, Set<VariableElement>> findMissingGettersSetters(CompilationInfo info, Range range, boolean all) {
-        TreePath tp = info.getTreeUtilities().pathFor(getOffset(info, range.getStart()));
+    private final Set<String> commands = Collections.unmodifiableSet(new HashSet(Arrays.asList(GENERATE_GETTERS, GENERATE_SETTERS, GENERATE_GETTERS_SETTERS)));
+    private final Gson gson = new Gson();
+
+    GetterSetterGenerator() {
+    }
 
-        while (tp != null && !TreeUtilities.CLASS_TREE_KINDS.contains(tp.getLeaf().getKind())) {
-            tp = tp.getParentPath();
+    @Override
+    @NbBundle.Messages({
+        "DN_GenerateGetters=Generate Getters...",
+        "DN_GenerateSetters=Generate Setters...",
+        "DN_GenerateGettersSetters=Generate Getters and Setters...",
+        "DN_GenerateGetterFor=Generate Getter for \"{0}\"",
+        "DN_GenerateSetterFor=Generate Setter for \"{0}\"",
+        "DN_GenerateGetterSetterFor=Generate Getter and Setter for \"{0}\"",
+    })
+    public List<CodeAction> getCodeActions(CompilationInfo info, CodeActionParams params) {
+        List<String> only = params.getContext().getOnly();
+        boolean all = only != null && only.contains(CodeActionKind.Source);
+        Pair<Set<VariableElement>, Set<VariableElement>> pair = findMissingGettersSetters(info, params.getRange(), all);
+        boolean missingGetters = !pair.first().isEmpty();
+        boolean missingSetters = !pair.second().isEmpty();
+        String uri = Utils.toUri(info.getFileObject());
+        int offset = getOffset(info, params.getRange().getStart());
+        List<CodeAction> result = new ArrayList<>();
+        if (missingGetters) {
+            String name = pair.first().size() == 1 ? Bundle.DN_GenerateGetterFor(pair.first().iterator().next().getSimpleName().toString()) : Bundle.DN_GenerateGetters();
+            result.add(createCodeAction(name, all ? CODE_GENERATOR_KIND : CodeActionKind.QuickFix, GENERATE_GETTERS, uri, offset, all, pair.first().stream().map(variableElement -> {
+                QuickPickItem item = new QuickPickItem(createLabel(info, variableElement));
+                item.setUserData(new ElementData(variableElement));
+                return item;
+            }).collect(Collectors.toList())));
+        }
+        if (missingSetters) {
+            String name = pair.second().size() == 1 ? Bundle.DN_GenerateSetterFor(pair.second().iterator().next().getSimpleName().toString()) : Bundle.DN_GenerateSetters();
+            result.add(createCodeAction(name, all ? CODE_GENERATOR_KIND : CodeActionKind.QuickFix, GENERATE_SETTERS, uri, offset, all, pair.second().stream().map(variableElement -> {
+                QuickPickItem item = new QuickPickItem(createLabel(info, variableElement));
+                item.setUserData(new ElementData(variableElement));
+                return item;
+            }).collect(Collectors.toList())));
         }
+        if (missingGetters && missingSetters) {
+            pair.first().retainAll(pair.second());
+            String name = pair.first().size() == 1 ? Bundle.DN_GenerateGetterSetterFor(pair.first().iterator().next().getSimpleName().toString()) : Bundle.DN_GenerateGettersSetters();

Review comment:
       `pair.first()` may become empty after `retainAll`

##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/GetterSetterGenerator.java
##########
@@ -18,58 +18,175 @@
  */
 package org.netbeans.modules.java.lsp.server.protocol;
 
+import com.google.gson.Gson;
 import com.sun.source.tree.ClassTree;
-import com.sun.source.tree.LineMap;
 import com.sun.source.tree.Tree;
 import com.sun.source.util.TreePath;
 import java.io.IOException;
-import java.net.MalformedURLException;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
 import javax.lang.model.element.Modifier;
 import javax.lang.model.element.TypeElement;
 import javax.lang.model.element.VariableElement;
 import javax.lang.model.util.ElementFilter;
 import org.eclipse.lsp4j.ApplyWorkspaceEditParams;
-import org.eclipse.lsp4j.Position;
+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.eclipse.lsp4j.Range;
 import org.eclipse.lsp4j.TextEdit;
 import org.eclipse.lsp4j.WorkspaceEdit;
-import org.eclipse.lsp4j.services.LanguageClient;
 import org.netbeans.api.java.source.CodeStyle;
 import org.netbeans.api.java.source.CompilationInfo;
 import org.netbeans.api.java.source.ElementUtilities;
 import org.netbeans.api.java.source.JavaSource;
 import org.netbeans.api.java.source.TreeUtilities;
 import org.netbeans.modules.java.editor.codegen.GeneratorUtils;
-import static org.netbeans.modules.java.lsp.server.protocol.TextDocumentServiceImpl.fromUri;
+import org.netbeans.modules.java.lsp.server.Utils;
 import org.openide.filesystems.FileObject;
+import org.openide.util.NbBundle;
 import org.openide.util.Pair;
 
 /**
  *
  * @author lahvac
  */
-public class GetterSetterGenerator {
+public final class GetterSetterGenerator extends CodeGenerator {
 
-    private static final String ERROR = "<error>"; //NOI18N
+    public static final String GENERATE_GETTERS =  "java.generate.getters";
+    public static final String GENERATE_SETTERS =  "java.generate.setters";
+    public static final String GENERATE_GETTERS_SETTERS =  "java.generate.getters.setters";
 
-    public static Pair<Set<VariableElement>, Set<VariableElement>> findMissingGettersSetters(CompilationInfo info, Range range, boolean all) {
-        TreePath tp = info.getTreeUtilities().pathFor(getOffset(info, range.getStart()));
+    private final Set<String> commands = Collections.unmodifiableSet(new HashSet(Arrays.asList(GENERATE_GETTERS, GENERATE_SETTERS, GENERATE_GETTERS_SETTERS)));
+    private final Gson gson = new Gson();
+
+    GetterSetterGenerator() {
+    }
 
-        while (tp != null && !TreeUtilities.CLASS_TREE_KINDS.contains(tp.getLeaf().getKind())) {
-            tp = tp.getParentPath();
+    @Override
+    @NbBundle.Messages({
+        "DN_GenerateGetters=Generate Getters...",
+        "DN_GenerateSetters=Generate Setters...",
+        "DN_GenerateGettersSetters=Generate Getters and Setters...",
+        "DN_GenerateGetterFor=Generate Getter for \"{0}\"",
+        "DN_GenerateSetterFor=Generate Setter for \"{0}\"",
+        "DN_GenerateGetterSetterFor=Generate Getter and Setter for \"{0}\"",
+    })
+    public List<CodeAction> getCodeActions(CompilationInfo info, CodeActionParams params) {
+        List<String> only = params.getContext().getOnly();
+        boolean all = only != null && only.contains(CodeActionKind.Source);

Review comment:
       The other generators bailed out if only != null, but did not contain `CodeActionKind.Source`; this seem to process all eligible fields later in `processCommand`. Right now just `QuickFix` is declared (in addition to `Source`), but better check explicitly so it own't break when the set of `CodeActionKind`s expand later.

##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ImplementOverrideMethodGenerator.java
##########
@@ -0,0 +1,178 @@
+/*
+ * 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.util.TreePath;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+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.eclipse.lsp4j.TextEdit;
+import org.eclipse.lsp4j.WorkspaceEdit;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.ElementUtilities;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.TreeUtilities;
+import org.netbeans.modules.java.editor.codegen.GeneratorUtils;
+import org.netbeans.modules.java.lsp.server.Utils;
+import org.openide.filesystems.FileObject;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public final class ImplementOverrideMethodGenerator extends CodeGenerator {
+
+    public static final String GENERATE_IMPLEMENT_METHOD =  "java.generate.implementMethod";
+    public static final String GENERATE_OVERRIDE_METHOD =  "java.generate.overrideMethod";
+
+    private final Set<String> commands = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(GENERATE_IMPLEMENT_METHOD, GENERATE_OVERRIDE_METHOD)));
+    private final Gson gson = new Gson();
+
+    ImplementOverrideMethodGenerator() {
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_GenerateImplementMethod=Generate Implement Method...",
+        "DN_GenerateOverrideMethod=Generate Override Method...",
+        "DN_From=(from {0})",
+    })
+    public List<CodeAction> getCodeActions(CompilationInfo info, CodeActionParams params) {
+        List<String> only = params.getContext().getOnly();
+        if (only == null || !only.contains(CodeActionKind.Source)) {
+            return Collections.emptyList();
+        }
+        int offset = getOffset(info, params.getRange().getStart());
+        TreePath tp = info.getTreeUtilities().pathFor(offset);
+        tp = info.getTreeUtilities().getPathElementOfKind(TreeUtilities.CLASS_TREE_KINDS, tp);
+        if (tp == null) {
+            return Collections.emptyList();
+        }
+        TypeElement typeElement = (TypeElement) info.getTrees().getElement(tp);
+        if (typeElement == null || typeElement.getKind() == ElementKind.ANNOTATION_TYPE) {
+            return Collections.emptyList();
+        }
+        List<CodeAction> result = new ArrayList<>();
+        String uri = Utils.toUri(info.getFileObject());
+        ElementUtilities eu = info.getElementUtilities();
+        if (typeElement.getKind().isClass() || typeElement.getKind().isInterface() && SourceVersion.RELEASE_8.compareTo(info.getSourceVersion()) <= 0) {
+            List<QuickPickItem> implementMethods = new ArrayList<>();
+            for (ExecutableElement method : eu.findUnimplementedMethods(typeElement, true)) {
+                boolean mustImplement = !method.getModifiers().contains(Modifier.DEFAULT);
+                Element enclosingElement = method.getEnclosingElement();
+                String enclosingTypeName = enclosingElement.getKind().isClass() || enclosingElement.getKind().isInterface() ? Bundle.DN_From(((TypeElement)enclosingElement).getQualifiedName().toString()) : null;
+                implementMethods.add(new QuickPickItem(createLabel(info, method), enclosingTypeName, null, mustImplement, new ElementData(method)));
+            }
+            if (!implementMethods.isEmpty()) {
+                result.add(createCodeAction(Bundle.DN_GenerateImplementMethod(), CODE_GENERATOR_KIND, GENERATE_IMPLEMENT_METHOD, uri, offset, implementMethods));
+            }
+        }
+        if (typeElement.getKind().isClass() || typeElement.getKind().isInterface()) {
+            List<QuickPickItem> overrideMethods = new ArrayList<>();
+            for (ExecutableElement method : eu.findOverridableMethods(typeElement)) {
+                Element enclosingElement = method.getEnclosingElement();
+                String enclosingTypeName = enclosingElement.getKind().isClass() || enclosingElement.getKind().isInterface() ? Bundle.DN_From(((TypeElement) enclosingElement).getQualifiedName().toString()) : null;
+                QuickPickItem item = new QuickPickItem(createLabel(info, method));
+                if (enclosingTypeName != null) {
+                    item.setDescription(enclosingTypeName);
+                }
+                item.setUserData(new ElementData(method));
+                overrideMethods.add(item);
+            }
+            if (!overrideMethods.isEmpty()) {
+                result.add(createCodeAction(Bundle.DN_GenerateOverrideMethod(), CODE_GENERATOR_KIND, GENERATE_OVERRIDE_METHOD, uri, offset, overrideMethods));
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public Set<String> getCommands() {
+        return commands;
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_SelectImplementMethod=Select methods to implement",
+        "DN_SelectOverrideMethod=Select methods to override",
+    })
+    public CompletableFuture<Object> processCommand(NbCodeLanguageClient client, String command, List<Object> arguments) {
+        if (arguments.size() > 2) {
+            String uri = gson.fromJson(gson.toJson(arguments.get(0)), String.class);
+            int offset = gson.fromJson(gson.toJson(arguments.get(1)), Integer.class);
+            List<QuickPickItem> methods = Arrays.asList(gson.fromJson(gson.toJson(arguments.get(2)), QuickPickItem[].class));
+            String text = command == GENERATE_IMPLEMENT_METHOD ? Bundle.DN_SelectImplementMethod() : Bundle.DN_SelectOverrideMethod();
+            boolean isImplement = command == GENERATE_IMPLEMENT_METHOD;
+            client.showQuickPick(new ShowQuickPickParams(text, true, methods)).thenAccept(selected -> {
+                if (selected != null) {

Review comment:
       `&& !selected.isEmpty()`

##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/GetterSetterGenerator.java
##########
@@ -18,58 +18,175 @@
  */
 package org.netbeans.modules.java.lsp.server.protocol;
 
+import com.google.gson.Gson;
 import com.sun.source.tree.ClassTree;
-import com.sun.source.tree.LineMap;
 import com.sun.source.tree.Tree;
 import com.sun.source.util.TreePath;
 import java.io.IOException;
-import java.net.MalformedURLException;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
 import javax.lang.model.element.Modifier;
 import javax.lang.model.element.TypeElement;
 import javax.lang.model.element.VariableElement;
 import javax.lang.model.util.ElementFilter;
 import org.eclipse.lsp4j.ApplyWorkspaceEditParams;
-import org.eclipse.lsp4j.Position;
+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.eclipse.lsp4j.Range;
 import org.eclipse.lsp4j.TextEdit;
 import org.eclipse.lsp4j.WorkspaceEdit;
-import org.eclipse.lsp4j.services.LanguageClient;
 import org.netbeans.api.java.source.CodeStyle;
 import org.netbeans.api.java.source.CompilationInfo;
 import org.netbeans.api.java.source.ElementUtilities;
 import org.netbeans.api.java.source.JavaSource;
 import org.netbeans.api.java.source.TreeUtilities;
 import org.netbeans.modules.java.editor.codegen.GeneratorUtils;
-import static org.netbeans.modules.java.lsp.server.protocol.TextDocumentServiceImpl.fromUri;
+import org.netbeans.modules.java.lsp.server.Utils;
 import org.openide.filesystems.FileObject;
+import org.openide.util.NbBundle;
 import org.openide.util.Pair;
 
 /**
  *
  * @author lahvac
  */
-public class GetterSetterGenerator {
+public final class GetterSetterGenerator extends CodeGenerator {
 
-    private static final String ERROR = "<error>"; //NOI18N
+    public static final String GENERATE_GETTERS =  "java.generate.getters";
+    public static final String GENERATE_SETTERS =  "java.generate.setters";
+    public static final String GENERATE_GETTERS_SETTERS =  "java.generate.getters.setters";
 
-    public static Pair<Set<VariableElement>, Set<VariableElement>> findMissingGettersSetters(CompilationInfo info, Range range, boolean all) {
-        TreePath tp = info.getTreeUtilities().pathFor(getOffset(info, range.getStart()));
+    private final Set<String> commands = Collections.unmodifiableSet(new HashSet(Arrays.asList(GENERATE_GETTERS, GENERATE_SETTERS, GENERATE_GETTERS_SETTERS)));
+    private final Gson gson = new Gson();
+
+    GetterSetterGenerator() {
+    }
 
-        while (tp != null && !TreeUtilities.CLASS_TREE_KINDS.contains(tp.getLeaf().getKind())) {
-            tp = tp.getParentPath();
+    @Override
+    @NbBundle.Messages({
+        "DN_GenerateGetters=Generate Getters...",
+        "DN_GenerateSetters=Generate Setters...",
+        "DN_GenerateGettersSetters=Generate Getters and Setters...",
+        "DN_GenerateGetterFor=Generate Getter for \"{0}\"",

Review comment:
       Nitpick: document msg parameters.

##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ImplementOverrideMethodGenerator.java
##########
@@ -0,0 +1,178 @@
+/*
+ * 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.util.TreePath;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+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.eclipse.lsp4j.TextEdit;
+import org.eclipse.lsp4j.WorkspaceEdit;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.ElementUtilities;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.TreeUtilities;
+import org.netbeans.modules.java.editor.codegen.GeneratorUtils;
+import org.netbeans.modules.java.lsp.server.Utils;
+import org.openide.filesystems.FileObject;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public final class ImplementOverrideMethodGenerator extends CodeGenerator {
+
+    public static final String GENERATE_IMPLEMENT_METHOD =  "java.generate.implementMethod";
+    public static final String GENERATE_OVERRIDE_METHOD =  "java.generate.overrideMethod";
+
+    private final Set<String> commands = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(GENERATE_IMPLEMENT_METHOD, GENERATE_OVERRIDE_METHOD)));
+    private final Gson gson = new Gson();
+
+    ImplementOverrideMethodGenerator() {
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_GenerateImplementMethod=Generate Implement Method...",
+        "DN_GenerateOverrideMethod=Generate Override Method...",
+        "DN_From=(from {0})",
+    })
+    public List<CodeAction> getCodeActions(CompilationInfo info, CodeActionParams params) {
+        List<String> only = params.getContext().getOnly();
+        if (only == null || !only.contains(CodeActionKind.Source)) {
+            return Collections.emptyList();
+        }
+        int offset = getOffset(info, params.getRange().getStart());
+        TreePath tp = info.getTreeUtilities().pathFor(offset);
+        tp = info.getTreeUtilities().getPathElementOfKind(TreeUtilities.CLASS_TREE_KINDS, tp);
+        if (tp == null) {
+            return Collections.emptyList();
+        }
+        TypeElement typeElement = (TypeElement) info.getTrees().getElement(tp);
+        if (typeElement == null || typeElement.getKind() == ElementKind.ANNOTATION_TYPE) {
+            return Collections.emptyList();
+        }
+        List<CodeAction> result = new ArrayList<>();
+        String uri = Utils.toUri(info.getFileObject());
+        ElementUtilities eu = info.getElementUtilities();
+        if (typeElement.getKind().isClass() || typeElement.getKind().isInterface() && SourceVersion.RELEASE_8.compareTo(info.getSourceVersion()) <= 0) {
+            List<QuickPickItem> implementMethods = new ArrayList<>();
+            for (ExecutableElement method : eu.findUnimplementedMethods(typeElement, true)) {
+                boolean mustImplement = !method.getModifiers().contains(Modifier.DEFAULT);
+                Element enclosingElement = method.getEnclosingElement();
+                String enclosingTypeName = enclosingElement.getKind().isClass() || enclosingElement.getKind().isInterface() ? Bundle.DN_From(((TypeElement)enclosingElement).getQualifiedName().toString()) : null;
+                implementMethods.add(new QuickPickItem(createLabel(info, method), enclosingTypeName, null, mustImplement, new ElementData(method)));
+            }
+            if (!implementMethods.isEmpty()) {
+                result.add(createCodeAction(Bundle.DN_GenerateImplementMethod(), CODE_GENERATOR_KIND, GENERATE_IMPLEMENT_METHOD, uri, offset, implementMethods));
+            }
+        }
+        if (typeElement.getKind().isClass() || typeElement.getKind().isInterface()) {

Review comment:
       I am not sure what `GenreratorUtils.generateMethodOverrides` does when overriding method in the interface type for Java < 8, where `default` modified is not allowed.

##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/GetterSetterGenerator.java
##########
@@ -18,58 +18,175 @@
  */
 package org.netbeans.modules.java.lsp.server.protocol;
 
+import com.google.gson.Gson;
 import com.sun.source.tree.ClassTree;
-import com.sun.source.tree.LineMap;
 import com.sun.source.tree.Tree;
 import com.sun.source.util.TreePath;
 import java.io.IOException;
-import java.net.MalformedURLException;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
 import javax.lang.model.element.Modifier;
 import javax.lang.model.element.TypeElement;
 import javax.lang.model.element.VariableElement;
 import javax.lang.model.util.ElementFilter;
 import org.eclipse.lsp4j.ApplyWorkspaceEditParams;
-import org.eclipse.lsp4j.Position;
+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.eclipse.lsp4j.Range;
 import org.eclipse.lsp4j.TextEdit;
 import org.eclipse.lsp4j.WorkspaceEdit;
-import org.eclipse.lsp4j.services.LanguageClient;
 import org.netbeans.api.java.source.CodeStyle;
 import org.netbeans.api.java.source.CompilationInfo;
 import org.netbeans.api.java.source.ElementUtilities;
 import org.netbeans.api.java.source.JavaSource;
 import org.netbeans.api.java.source.TreeUtilities;
 import org.netbeans.modules.java.editor.codegen.GeneratorUtils;
-import static org.netbeans.modules.java.lsp.server.protocol.TextDocumentServiceImpl.fromUri;
+import org.netbeans.modules.java.lsp.server.Utils;
 import org.openide.filesystems.FileObject;
+import org.openide.util.NbBundle;
 import org.openide.util.Pair;
 
 /**
  *
  * @author lahvac
  */
-public class GetterSetterGenerator {
+public final class GetterSetterGenerator extends CodeGenerator {
 
-    private static final String ERROR = "<error>"; //NOI18N
+    public static final String GENERATE_GETTERS =  "java.generate.getters";
+    public static final String GENERATE_SETTERS =  "java.generate.setters";
+    public static final String GENERATE_GETTERS_SETTERS =  "java.generate.getters.setters";
 
-    public static Pair<Set<VariableElement>, Set<VariableElement>> findMissingGettersSetters(CompilationInfo info, Range range, boolean all) {
-        TreePath tp = info.getTreeUtilities().pathFor(getOffset(info, range.getStart()));
+    private final Set<String> commands = Collections.unmodifiableSet(new HashSet(Arrays.asList(GENERATE_GETTERS, GENERATE_SETTERS, GENERATE_GETTERS_SETTERS)));
+    private final Gson gson = new Gson();
+
+    GetterSetterGenerator() {
+    }
 
-        while (tp != null && !TreeUtilities.CLASS_TREE_KINDS.contains(tp.getLeaf().getKind())) {
-            tp = tp.getParentPath();
+    @Override
+    @NbBundle.Messages({
+        "DN_GenerateGetters=Generate Getters...",
+        "DN_GenerateSetters=Generate Setters...",
+        "DN_GenerateGettersSetters=Generate Getters and Setters...",
+        "DN_GenerateGetterFor=Generate Getter for \"{0}\"",
+        "DN_GenerateSetterFor=Generate Setter for \"{0}\"",
+        "DN_GenerateGetterSetterFor=Generate Getter and Setter for \"{0}\"",
+    })
+    public List<CodeAction> getCodeActions(CompilationInfo info, CodeActionParams params) {
+        List<String> only = params.getContext().getOnly();
+        boolean all = only != null && only.contains(CodeActionKind.Source);
+        Pair<Set<VariableElement>, Set<VariableElement>> pair = findMissingGettersSetters(info, params.getRange(), all);
+        boolean missingGetters = !pair.first().isEmpty();
+        boolean missingSetters = !pair.second().isEmpty();
+        String uri = Utils.toUri(info.getFileObject());
+        int offset = getOffset(info, params.getRange().getStart());
+        List<CodeAction> result = new ArrayList<>();
+        if (missingGetters) {
+            String name = pair.first().size() == 1 ? Bundle.DN_GenerateGetterFor(pair.first().iterator().next().getSimpleName().toString()) : Bundle.DN_GenerateGetters();
+            result.add(createCodeAction(name, all ? CODE_GENERATOR_KIND : CodeActionKind.QuickFix, GENERATE_GETTERS, uri, offset, all, pair.first().stream().map(variableElement -> {
+                QuickPickItem item = new QuickPickItem(createLabel(info, variableElement));
+                item.setUserData(new ElementData(variableElement));
+                return item;
+            }).collect(Collectors.toList())));
+        }
+        if (missingSetters) {
+            String name = pair.second().size() == 1 ? Bundle.DN_GenerateSetterFor(pair.second().iterator().next().getSimpleName().toString()) : Bundle.DN_GenerateSetters();
+            result.add(createCodeAction(name, all ? CODE_GENERATOR_KIND : CodeActionKind.QuickFix, GENERATE_SETTERS, uri, offset, all, pair.second().stream().map(variableElement -> {
+                QuickPickItem item = new QuickPickItem(createLabel(info, variableElement));
+                item.setUserData(new ElementData(variableElement));
+                return item;
+            }).collect(Collectors.toList())));
         }
+        if (missingGetters && missingSetters) {
+            pair.first().retainAll(pair.second());
+            String name = pair.first().size() == 1 ? Bundle.DN_GenerateGetterSetterFor(pair.first().iterator().next().getSimpleName().toString()) : Bundle.DN_GenerateGettersSetters();
+            result.add(createCodeAction(name, all ? CODE_GENERATOR_KIND : CodeActionKind.QuickFix, GENERATE_GETTERS_SETTERS, uri, offset, all, pair.first().stream().map(variableElement -> {
+                QuickPickItem item = new QuickPickItem(createLabel(info, variableElement));
+                item.setUserData(new ElementData(variableElement));
+                return item;
+            }).collect(Collectors.toList())));
+        }
+        return result;
+    }
+
+    @Override
+    public Set<String> getCommands() {
+        return commands;
+    }
 
+    @Override
+    @NbBundle.Messages({
+        "DN_SelectGetters=Select fields to generate getters for",
+        "DN_SelectSetters=Select fields to generate setters for",
+        "DN_SelectGettersSetters=Select fields to generate getters and setters for",
+    })
+    public CompletableFuture<Object> processCommand(NbCodeLanguageClient client, String command, List<Object> arguments) {
+        if (arguments.size() > 3) {
+            String uri = gson.fromJson(gson.toJson(arguments.get(0)), String.class);
+            int offset = gson.fromJson(gson.toJson(arguments.get(1)), Integer.class);
+            boolean all = gson.fromJson(gson.toJson(arguments.get(2)), boolean.class);
+            List<QuickPickItem> fields = Arrays.asList(gson.fromJson(gson.toJson(arguments.get(3)), QuickPickItem[].class));
+            int kind;
+            String text;
+            switch (command) {
+                case GENERATE_GETTERS: kind = GeneratorUtils.GETTERS_ONLY; text = Bundle.DN_SelectGetters(); break;
+                case GENERATE_SETTERS: kind = GeneratorUtils.SETTERS_ONLY; text = Bundle.DN_SelectSetters(); break;
+                default: kind = 0; text = Bundle.DN_SelectGettersSetters(); break;
+            }
+            if (all && fields.size() > 1) {
+                client.showQuickPick(new ShowQuickPickParams(text, true, fields)).thenAccept(selected -> {
+                    if (selected != null) {
+                        generate(client, kind, uri, offset, selected);
+                    }
+                });
+            } else {
+                generate(client, kind, uri, offset, fields);
+            }
+        }
+        return CompletableFuture.completedFuture(true);
+    }
+
+    private void generate(NbCodeLanguageClient client, int kind, String uri, int offset, List<QuickPickItem> fields) throws IllegalArgumentException {
+        try {
+            FileObject file = Utils.fromUri(uri);
+            JavaSource js = JavaSource.forFileObject(file);
+            List<TextEdit> edits = TextDocumentServiceImpl.modify2TextEdits(js, wc -> {
+                wc.toPhase(JavaSource.Phase.RESOLVED);
+                if (!fields.isEmpty()) {
+                    TreePath tp = wc.getTreeUtilities().pathFor(offset);
+                    tp = wc.getTreeUtilities().getPathElementOfKind(TreeUtilities.CLASS_TREE_KINDS, tp);
+                    if (tp != null) {

Review comment:
       I'd check the field's type TypeMirror for not being erroneous. Strange things may happen when generating incomplete types into getter / setter declartions.

##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/LoggerGenerator.java
##########
@@ -0,0 +1,135 @@
+/*
+ * 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.ClassTree;
+import com.sun.source.tree.VariableTree;
+import com.sun.source.util.TreePath;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.logging.Logger;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+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.eclipse.lsp4j.TextEdit;
+import org.eclipse.lsp4j.WorkspaceEdit;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.GeneratorUtilities;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.TreeUtilities;
+import org.netbeans.modules.java.lsp.server.Utils;
+import org.openide.filesystems.FileObject;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public final class LoggerGenerator extends CodeGenerator {
+
+    public static final String GENERATE_LOGGER =  "java.generate.logger";
+
+    private final Set<String> commands = Collections.singleton(GENERATE_LOGGER);
+    private final Gson gson = new Gson();
+
+    LoggerGenerator() {
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_GenerateLogger=Generate Logger...",
+    })
+    public List<CodeAction> getCodeActions(CompilationInfo info, CodeActionParams params) {
+        List<String> only = params.getContext().getOnly();
+        if (only == null || !only.contains(CodeActionKind.Source)) {
+            return Collections.emptyList();
+        }
+        int offset = getOffset(info, params.getRange().getStart());
+        TreePath tp = info.getTreeUtilities().pathFor(offset);
+        tp = info.getTreeUtilities().getPathElementOfKind(TreeUtilities.CLASS_TREE_KINDS, tp);
+        if (tp == null) {
+            return Collections.emptyList();
+        }
+        TypeElement typeElement = (TypeElement) info.getTrees().getElement(tp);
+        if (typeElement == null || !typeElement.getKind().isClass()) {
+            return Collections.emptyList();
+        }
+        for (VariableElement ve : ElementFilter.fieldsIn(typeElement.getEnclosedElements())) {
+            TypeMirror type = ve.asType();
+            if (type.getKind() == TypeKind.DECLARED && ((TypeElement)((DeclaredType)type).asElement()).getQualifiedName().contentEquals(Logger.class.getName())) {
+                return Collections.emptyList();
+            }
+        }
+        String uri = Utils.toUri(info.getFileObject());

Review comment:
       This may suggest to create Logger, even if one is present, but not resolved (i.e. missing `import`).

##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java
##########
@@ -332,12 +338,19 @@ protected LanguageClient client() {
     
     public static final String JAVA_BUILD_WORKSPACE =  "java.build.workspace";
     public static final String GRAALVM_PAUSE_SCRIPT =  "graalvm.pause.script";
-    public static final String GENERATE_GETTERS =  "java.generate.getters";
-    public static final String GENERATE_SETTERS =  "java.generate.setters";
-    public static final String GENERATE_GETTERS_SETTERS =  "java.generate.getters.setters";
     static final String INDEXING_COMPLETED = "Indexing completed.";
     static final String NO_JAVA_SUPPORT = "Cannot initialize Java support on JDK ";
-    
+
+    static final CodeGenerator[] CODE_GENERATORS = new CodeGenerator[] {

Review comment:
       We should invent some 'registration', i.e. using `@ServiceProvider`, when expanding code gens next time.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


[GitHub] [netbeans] dbalek commented on a change in pull request #2587: Exisitng CodeGenerators exposed as CodeActions via LSP.

Posted by GitBox <gi...@apache.org>.
dbalek commented on a change in pull request #2587:
URL: https://github.com/apache/netbeans/pull/2587#discussion_r542177595



##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeGenerator.java
##########
@@ -0,0 +1,312 @@
+/*
+ * 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.sun.source.tree.LineMap;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UncheckedIOException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.nio.file.Files;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.TypeParameterElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import org.eclipse.lsp4j.CodeAction;
+import org.eclipse.lsp4j.CodeActionParams;
+import org.eclipse.lsp4j.Command;
+import org.eclipse.lsp4j.Position;
+import org.eclipse.xtext.xbase.lib.Pure;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.ElementHandle;
+import org.netbeans.modules.editor.java.Utilities;
+import org.netbeans.modules.java.source.ElementHandleAccessor;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.filesystems.URLMapper;
+import org.openide.modules.Places;
+import org.openide.util.Exceptions;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public abstract class CodeGenerator {
+
+    public static final String CODE_GENERATOR_KIND = "source.generate";
+    protected static final String ERROR = "<error>"; //NOI18N
+
+    public abstract List<CodeAction> getCodeActions(CompilationInfo info, CodeActionParams params);
+
+    public abstract Set<String> getCommands();
+
+    public abstract CompletableFuture<Object> processCommand(NbCodeLanguageClient client, String command, List<Object> arguments);
+
+    protected static int getOffset(CompilationInfo info, Position pos) {
+        LineMap lm = info.getCompilationUnit().getLineMap();
+        return (int) lm.getPosition(pos.getLine() + 1, pos.getCharacter() + 1);
+    }
+
+    protected static CodeAction createCodeAction(String name, String kind, String command, Object... args) {
+        CodeAction action = new CodeAction(name);
+        action.setKind(kind);
+        action.setCommand(new Command(name, command, Arrays.asList(args)));
+        return action;
+    }
+
+    protected static String toUri(FileObject file) {
+        if (FileUtil.isArchiveArtifact(file)) {
+            //VS code cannot open jar:file: URLs, workaround:

Review comment:
       Do we really need to have code generators working for sources that come from jar files?




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


[GitHub] [netbeans] dbalek commented on a change in pull request #2587: Existing CodeGenerators exposed as CodeActions via LSP.

Posted by GitBox <gi...@apache.org>.
dbalek commented on a change in pull request #2587:
URL: https://github.com/apache/netbeans/pull/2587#discussion_r543367510



##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ImplementOverrideMethodGenerator.java
##########
@@ -0,0 +1,178 @@
+/*
+ * 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.util.TreePath;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+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.eclipse.lsp4j.TextEdit;
+import org.eclipse.lsp4j.WorkspaceEdit;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.ElementUtilities;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.TreeUtilities;
+import org.netbeans.modules.java.editor.codegen.GeneratorUtils;
+import org.netbeans.modules.java.lsp.server.Utils;
+import org.openide.filesystems.FileObject;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public final class ImplementOverrideMethodGenerator extends CodeGenerator {
+
+    public static final String GENERATE_IMPLEMENT_METHOD =  "java.generate.implementMethod";
+    public static final String GENERATE_OVERRIDE_METHOD =  "java.generate.overrideMethod";
+
+    private final Set<String> commands = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(GENERATE_IMPLEMENT_METHOD, GENERATE_OVERRIDE_METHOD)));
+    private final Gson gson = new Gson();
+
+    ImplementOverrideMethodGenerator() {
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_GenerateImplementMethod=Generate Implement Method...",
+        "DN_GenerateOverrideMethod=Generate Override Method...",
+        "DN_From=(from {0})",
+    })
+    public List<CodeAction> getCodeActions(CompilationInfo info, CodeActionParams params) {
+        List<String> only = params.getContext().getOnly();
+        if (only == null || !only.contains(CodeActionKind.Source)) {
+            return Collections.emptyList();
+        }
+        int offset = getOffset(info, params.getRange().getStart());
+        TreePath tp = info.getTreeUtilities().pathFor(offset);
+        tp = info.getTreeUtilities().getPathElementOfKind(TreeUtilities.CLASS_TREE_KINDS, tp);
+        if (tp == null) {
+            return Collections.emptyList();
+        }
+        TypeElement typeElement = (TypeElement) info.getTrees().getElement(tp);
+        if (typeElement == null || typeElement.getKind() == ElementKind.ANNOTATION_TYPE) {
+            return Collections.emptyList();
+        }
+        List<CodeAction> result = new ArrayList<>();
+        String uri = Utils.toUri(info.getFileObject());
+        ElementUtilities eu = info.getElementUtilities();
+        if (typeElement.getKind().isClass() || typeElement.getKind().isInterface() && SourceVersion.RELEASE_8.compareTo(info.getSourceVersion()) <= 0) {
+            List<QuickPickItem> implementMethods = new ArrayList<>();
+            for (ExecutableElement method : eu.findUnimplementedMethods(typeElement, true)) {
+                boolean mustImplement = !method.getModifiers().contains(Modifier.DEFAULT);
+                Element enclosingElement = method.getEnclosingElement();
+                String enclosingTypeName = enclosingElement.getKind().isClass() || enclosingElement.getKind().isInterface() ? Bundle.DN_From(((TypeElement)enclosingElement).getQualifiedName().toString()) : null;
+                implementMethods.add(new QuickPickItem(createLabel(info, method), enclosingTypeName, null, mustImplement, new ElementData(method)));
+            }
+            if (!implementMethods.isEmpty()) {
+                result.add(createCodeAction(Bundle.DN_GenerateImplementMethod(), CODE_GENERATOR_KIND, GENERATE_IMPLEMENT_METHOD, uri, offset, implementMethods));
+            }
+        }
+        if (typeElement.getKind().isClass() || typeElement.getKind().isInterface()) {

Review comment:
       CodeAction is not available in interfaces for Java < 8 (see `getCodeActions(...)` method).




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


[GitHub] [netbeans] dbalek merged pull request #2587: Existing CodeGenerators exposed as CodeActions via LSP.

Posted by GitBox <gi...@apache.org>.
dbalek merged pull request #2587:
URL: https://github.com/apache/netbeans/pull/2587


   


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


[GitHub] [netbeans] dbalek commented on a change in pull request #2587: Existing CodeGenerators exposed as CodeActions via LSP.

Posted by GitBox <gi...@apache.org>.
dbalek commented on a change in pull request #2587:
URL: https://github.com/apache/netbeans/pull/2587#discussion_r543153250



##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeGenerator.java
##########
@@ -0,0 +1,312 @@
+/*
+ * 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.sun.source.tree.LineMap;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UncheckedIOException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.nio.file.Files;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.TypeParameterElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import org.eclipse.lsp4j.CodeAction;
+import org.eclipse.lsp4j.CodeActionParams;
+import org.eclipse.lsp4j.Command;
+import org.eclipse.lsp4j.Position;
+import org.eclipse.xtext.xbase.lib.Pure;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.ElementHandle;
+import org.netbeans.modules.editor.java.Utilities;
+import org.netbeans.modules.java.source.ElementHandleAccessor;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.filesystems.URLMapper;
+import org.openide.modules.Places;
+import org.openide.util.Exceptions;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public abstract class CodeGenerator {
+
+    public static final String CODE_GENERATOR_KIND = "source.generate";
+    protected static final String ERROR = "<error>"; //NOI18N
+
+    public abstract List<CodeAction> getCodeActions(CompilationInfo info, CodeActionParams params);
+
+    public abstract Set<String> getCommands();
+
+    public abstract CompletableFuture<Object> processCommand(NbCodeLanguageClient client, String command, List<Object> arguments);
+
+    protected static int getOffset(CompilationInfo info, Position pos) {
+        LineMap lm = info.getCompilationUnit().getLineMap();
+        return (int) lm.getPosition(pos.getLine() + 1, pos.getCharacter() + 1);
+    }
+
+    protected static CodeAction createCodeAction(String name, String kind, String command, Object... args) {
+        CodeAction action = new CodeAction(name);
+        action.setKind(kind);
+        action.setCommand(new Command(name, command, Arrays.asList(args)));
+        return action;
+    }
+
+    protected static String toUri(FileObject file) {
+        if (FileUtil.isArchiveArtifact(file)) {
+            //VS code cannot open jar:file: URLs, workaround:

Review comment:
       Using Utils.toUri(...) and Utils.fromUri(...) as suggested.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


[GitHub] [netbeans] dbalek commented on a change in pull request #2587: Existing CodeGenerators exposed as CodeActions via LSP.

Posted by GitBox <gi...@apache.org>.
dbalek commented on a change in pull request #2587:
URL: https://github.com/apache/netbeans/pull/2587#discussion_r543356407



##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/DelegateMethodGenerator.java
##########
@@ -0,0 +1,210 @@
+/*
+ * 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.Scope;
+import com.sun.source.util.TreePath;
+import com.sun.source.util.Trees;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+import javax.lang.model.util.Elements;
+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.eclipse.lsp4j.TextEdit;
+import org.eclipse.lsp4j.WorkspaceEdit;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.ElementUtilities;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.TreeUtilities;
+import org.netbeans.modules.java.lsp.server.Utils;
+import org.openide.filesystems.FileObject;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public final class DelegateMethodGenerator extends CodeGenerator {
+
+    public static final String GENERATE_DELEGATE_METHOD =  "java.generate.delegateMethod";
+
+    private final Set<String> commands = Collections.singleton(GENERATE_DELEGATE_METHOD);
+    private final Gson gson = new Gson();
+
+    DelegateMethodGenerator() {
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_GenerateDelegateMethod=Generate Delegate Method...",
+    })
+    public List<CodeAction> getCodeActions(CompilationInfo info, CodeActionParams params) {
+        List<String> only = params.getContext().getOnly();
+        if (only == null || !only.contains(CodeActionKind.Source)) {
+            return Collections.emptyList();
+        }
+        int offset = getOffset(info, params.getRange().getStart());
+        TreePath tp = info.getTreeUtilities().pathFor(offset);
+        tp = info.getTreeUtilities().getPathElementOfKind(TreeUtilities.CLASS_TREE_KINDS, tp);
+        if (tp == null) {
+            return Collections.emptyList();
+        }
+        TypeElement typeElement = (TypeElement) info.getTrees().getElement(tp);
+        if (typeElement == null || !typeElement.getKind().isClass()) {
+            return Collections.emptyList();
+        }
+        Elements elements = info.getElements();
+        Trees trees = info.getTrees();
+        Scope scope = trees.getScope(tp);
+        List<QuickPickItem> fields = new ArrayList<>();
+        TypeElement cls;
+        while (scope != null && (cls = scope.getEnclosingClass()) != null) {
+            DeclaredType type = (DeclaredType) cls.asType();
+            for (VariableElement field : ElementFilter.fieldsIn(elements.getAllMembers(cls))) {
+                TypeMirror fieldType = field.asType();
+                if (!ERROR.contentEquals(field.getSimpleName()) && !fieldType.getKind().isPrimitive() && fieldType.getKind() != TypeKind.ARRAY
+                        && (fieldType.getKind() != TypeKind.DECLARED || ((DeclaredType)fieldType).asElement() != cls) && trees.isAccessible(scope, field, type)) {
+                    QuickPickItem item = new QuickPickItem(createLabel(info, field));
+                    item.setUserData(new ElementData(field));
+                    fields.add(item);
+                }
+            }
+            scope = scope.getEnclosingScope();
+        }
+        if (fields.isEmpty()) {
+            return Collections.emptyList();
+        }
+        String uri = Utils.toUri(info.getFileObject());
+        QuickPickItem typeItem = new QuickPickItem(createLabel(info, typeElement));
+        typeItem.setUserData(new ElementData(typeElement));
+        return Collections.singletonList(createCodeAction(Bundle.DN_GenerateDelegateMethod(), CODE_GENERATOR_KIND, GENERATE_DELEGATE_METHOD, uri, offset, typeItem, fields));
+    }
+
+    @Override
+    public Set<String> getCommands() {
+        return commands;
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_SelectDelegateMethodField=Select target field to generate delegates for",
+    })
+    public CompletableFuture<Object> processCommand(NbCodeLanguageClient client, String command, List<Object> arguments) {
+        if (arguments.size() > 3) {
+            String uri = gson.fromJson(gson.toJson(arguments.get(0)), String.class);
+            int offset = gson.fromJson(gson.toJson(arguments.get(1)), Integer.class);
+            QuickPickItem type = gson.fromJson(gson.toJson(arguments.get(2)), QuickPickItem.class);
+            List<QuickPickItem> fields = Arrays.asList(gson.fromJson(gson.toJson(arguments.get(3)), QuickPickItem[].class));
+            if (fields.size() == 1) {
+                selectMethods(client, uri, offset, type, fields.get(0));
+            } else {
+                client.showQuickPick(new ShowQuickPickParams(Bundle.DN_SelectDelegateMethodField(), false, fields)).thenAccept(selected -> {
+                    if (selected != null && !selected.isEmpty()) {
+                        selectMethods(client, uri, offset, type, selected.get(0));
+                    }
+                });
+            }
+        }
+        return CompletableFuture.completedFuture(true);
+    }
+
+    @NbBundle.Messages({
+        "DN_SelectDelegateMethods=Select methods to generate delegates for",
+    })
+    private void selectMethods(NbCodeLanguageClient client, String uri, int offset, QuickPickItem type, QuickPickItem selectedField) {
+        try {
+            FileObject file = Utils.fromUri(uri);
+            JavaSource js = JavaSource.forFileObject(file);
+            js.runUserActionTask(info -> {

Review comment:
       Fixed.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


[GitHub] [netbeans] dbalek commented on a change in pull request #2587: Existing CodeGenerators exposed as CodeActions via LSP.

Posted by GitBox <gi...@apache.org>.
dbalek commented on a change in pull request #2587:
URL: https://github.com/apache/netbeans/pull/2587#discussion_r543363814



##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ImplementOverrideMethodGenerator.java
##########
@@ -0,0 +1,178 @@
+/*
+ * 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.util.TreePath;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+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.eclipse.lsp4j.TextEdit;
+import org.eclipse.lsp4j.WorkspaceEdit;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.ElementUtilities;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.TreeUtilities;
+import org.netbeans.modules.java.editor.codegen.GeneratorUtils;
+import org.netbeans.modules.java.lsp.server.Utils;
+import org.openide.filesystems.FileObject;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public final class ImplementOverrideMethodGenerator extends CodeGenerator {
+
+    public static final String GENERATE_IMPLEMENT_METHOD =  "java.generate.implementMethod";
+    public static final String GENERATE_OVERRIDE_METHOD =  "java.generate.overrideMethod";
+
+    private final Set<String> commands = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(GENERATE_IMPLEMENT_METHOD, GENERATE_OVERRIDE_METHOD)));
+    private final Gson gson = new Gson();
+
+    ImplementOverrideMethodGenerator() {
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_GenerateImplementMethod=Generate Implement Method...",
+        "DN_GenerateOverrideMethod=Generate Override Method...",
+        "DN_From=(from {0})",
+    })
+    public List<CodeAction> getCodeActions(CompilationInfo info, CodeActionParams params) {
+        List<String> only = params.getContext().getOnly();
+        if (only == null || !only.contains(CodeActionKind.Source)) {
+            return Collections.emptyList();
+        }
+        int offset = getOffset(info, params.getRange().getStart());
+        TreePath tp = info.getTreeUtilities().pathFor(offset);
+        tp = info.getTreeUtilities().getPathElementOfKind(TreeUtilities.CLASS_TREE_KINDS, tp);
+        if (tp == null) {
+            return Collections.emptyList();
+        }
+        TypeElement typeElement = (TypeElement) info.getTrees().getElement(tp);
+        if (typeElement == null || typeElement.getKind() == ElementKind.ANNOTATION_TYPE) {
+            return Collections.emptyList();
+        }
+        List<CodeAction> result = new ArrayList<>();
+        String uri = Utils.toUri(info.getFileObject());
+        ElementUtilities eu = info.getElementUtilities();
+        if (typeElement.getKind().isClass() || typeElement.getKind().isInterface() && SourceVersion.RELEASE_8.compareTo(info.getSourceVersion()) <= 0) {
+            List<QuickPickItem> implementMethods = new ArrayList<>();
+            for (ExecutableElement method : eu.findUnimplementedMethods(typeElement, true)) {
+                boolean mustImplement = !method.getModifiers().contains(Modifier.DEFAULT);
+                Element enclosingElement = method.getEnclosingElement();
+                String enclosingTypeName = enclosingElement.getKind().isClass() || enclosingElement.getKind().isInterface() ? Bundle.DN_From(((TypeElement)enclosingElement).getQualifiedName().toString()) : null;
+                implementMethods.add(new QuickPickItem(createLabel(info, method), enclosingTypeName, null, mustImplement, new ElementData(method)));
+            }
+            if (!implementMethods.isEmpty()) {
+                result.add(createCodeAction(Bundle.DN_GenerateImplementMethod(), CODE_GENERATOR_KIND, GENERATE_IMPLEMENT_METHOD, uri, offset, implementMethods));
+            }
+        }
+        if (typeElement.getKind().isClass() || typeElement.getKind().isInterface()) {
+            List<QuickPickItem> overrideMethods = new ArrayList<>();
+            for (ExecutableElement method : eu.findOverridableMethods(typeElement)) {
+                Element enclosingElement = method.getEnclosingElement();
+                String enclosingTypeName = enclosingElement.getKind().isClass() || enclosingElement.getKind().isInterface() ? Bundle.DN_From(((TypeElement) enclosingElement).getQualifiedName().toString()) : null;
+                QuickPickItem item = new QuickPickItem(createLabel(info, method));
+                if (enclosingTypeName != null) {
+                    item.setDescription(enclosingTypeName);
+                }
+                item.setUserData(new ElementData(method));
+                overrideMethods.add(item);
+            }
+            if (!overrideMethods.isEmpty()) {
+                result.add(createCodeAction(Bundle.DN_GenerateOverrideMethod(), CODE_GENERATOR_KIND, GENERATE_OVERRIDE_METHOD, uri, offset, overrideMethods));
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public Set<String> getCommands() {
+        return commands;
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_SelectImplementMethod=Select methods to implement",
+        "DN_SelectOverrideMethod=Select methods to override",
+    })
+    public CompletableFuture<Object> processCommand(NbCodeLanguageClient client, String command, List<Object> arguments) {
+        if (arguments.size() > 2) {
+            String uri = gson.fromJson(gson.toJson(arguments.get(0)), String.class);
+            int offset = gson.fromJson(gson.toJson(arguments.get(1)), Integer.class);
+            List<QuickPickItem> methods = Arrays.asList(gson.fromJson(gson.toJson(arguments.get(2)), QuickPickItem[].class));
+            String text = command == GENERATE_IMPLEMENT_METHOD ? Bundle.DN_SelectImplementMethod() : Bundle.DN_SelectOverrideMethod();
+            boolean isImplement = command == GENERATE_IMPLEMENT_METHOD;
+            client.showQuickPick(new ShowQuickPickParams(text, true, methods)).thenAccept(selected -> {
+                if (selected != null) {

Review comment:
       Fixed.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


[GitHub] [netbeans] jlahoda commented on a change in pull request #2587: Existing CodeGenerators exposed as CodeActions via LSP.

Posted by GitBox <gi...@apache.org>.
jlahoda commented on a change in pull request #2587:
URL: https://github.com/apache/netbeans/pull/2587#discussion_r543092116



##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeGenerator.java
##########
@@ -0,0 +1,312 @@
+/*
+ * 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.sun.source.tree.LineMap;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UncheckedIOException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.nio.file.Files;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.TypeParameterElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import org.eclipse.lsp4j.CodeAction;
+import org.eclipse.lsp4j.CodeActionParams;
+import org.eclipse.lsp4j.Command;
+import org.eclipse.lsp4j.Position;
+import org.eclipse.xtext.xbase.lib.Pure;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.ElementHandle;
+import org.netbeans.modules.editor.java.Utilities;
+import org.netbeans.modules.java.source.ElementHandleAccessor;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.filesystems.URLMapper;
+import org.openide.modules.Places;
+import org.openide.util.Exceptions;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public abstract class CodeGenerator {
+
+    public static final String CODE_GENERATOR_KIND = "source.generate";
+    protected static final String ERROR = "<error>"; //NOI18N
+
+    public abstract List<CodeAction> getCodeActions(CompilationInfo info, CodeActionParams params);
+
+    public abstract Set<String> getCommands();
+
+    public abstract CompletableFuture<Object> processCommand(NbCodeLanguageClient client, String command, List<Object> arguments);
+
+    protected static int getOffset(CompilationInfo info, Position pos) {
+        LineMap lm = info.getCompilationUnit().getLineMap();
+        return (int) lm.getPosition(pos.getLine() + 1, pos.getCharacter() + 1);
+    }
+
+    protected static CodeAction createCodeAction(String name, String kind, String command, Object... args) {
+        CodeAction action = new CodeAction(name);
+        action.setKind(kind);
+        action.setCommand(new Command(name, command, Arrays.asList(args)));
+        return action;
+    }
+
+    protected static String toUri(FileObject file) {
+        if (FileUtil.isArchiveArtifact(file)) {
+            //VS code cannot open jar:file: URLs, workaround:

Review comment:
       I think it is a little misleading to say the code was introduced in #2530 - the code is there for years, see:
   https://github.com/apache/netbeans/pull/926/commits/64521088b6cc95a5a1951883ad9106e4839e06b8
   
   Anyway, to the merit: I assume you mean `URLMapper.NETWORK`, not `EXTERNAL`. As far as I can tall, `EXTERNAL` is still jar:file:. Anyway, I just tried with NETWORK (again), and VS Code still seems to reject it, at least in my setup. firefox started next to VS Code can access the URL just fine. It might be just an issue with rejecting http (and preferring https), although I am not sure of that. From the protocol, it is also not clear to me what types of URLs LSP clients are obliged to accept.
   
   Regarding this particular code - wouldn't it be better to use Utils.toUri? That would keep this code on one place, so that when Jaroslav fixes it, it will be automatically fixed on all places?
   




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


[GitHub] [netbeans] dbalek commented on a change in pull request #2587: Existing CodeGenerators exposed as CodeActions via LSP.

Posted by GitBox <gi...@apache.org>.
dbalek commented on a change in pull request #2587:
URL: https://github.com/apache/netbeans/pull/2587#discussion_r543389069



##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/LoggerGenerator.java
##########
@@ -0,0 +1,135 @@
+/*
+ * 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.ClassTree;
+import com.sun.source.tree.VariableTree;
+import com.sun.source.util.TreePath;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.logging.Logger;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+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.eclipse.lsp4j.TextEdit;
+import org.eclipse.lsp4j.WorkspaceEdit;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.GeneratorUtilities;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.TreeUtilities;
+import org.netbeans.modules.java.lsp.server.Utils;
+import org.openide.filesystems.FileObject;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public final class LoggerGenerator extends CodeGenerator {
+
+    public static final String GENERATE_LOGGER =  "java.generate.logger";
+
+    private final Set<String> commands = Collections.singleton(GENERATE_LOGGER);
+    private final Gson gson = new Gson();
+
+    LoggerGenerator() {
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_GenerateLogger=Generate Logger...",
+    })
+    public List<CodeAction> getCodeActions(CompilationInfo info, CodeActionParams params) {
+        List<String> only = params.getContext().getOnly();
+        if (only == null || !only.contains(CodeActionKind.Source)) {
+            return Collections.emptyList();
+        }
+        int offset = getOffset(info, params.getRange().getStart());
+        TreePath tp = info.getTreeUtilities().pathFor(offset);
+        tp = info.getTreeUtilities().getPathElementOfKind(TreeUtilities.CLASS_TREE_KINDS, tp);
+        if (tp == null) {
+            return Collections.emptyList();
+        }
+        TypeElement typeElement = (TypeElement) info.getTrees().getElement(tp);
+        if (typeElement == null || !typeElement.getKind().isClass()) {
+            return Collections.emptyList();
+        }
+        for (VariableElement ve : ElementFilter.fieldsIn(typeElement.getEnclosedElements())) {
+            TypeMirror type = ve.asType();
+            if (type.getKind() == TypeKind.DECLARED && ((TypeElement)((DeclaredType)type).asElement()).getQualifiedName().contentEquals(Logger.class.getName())) {
+                return Collections.emptyList();
+            }
+        }
+        String uri = Utils.toUri(info.getFileObject());
+        return Collections.singletonList(createCodeAction(Bundle.DN_GenerateLogger(), CODE_GENERATOR_KIND, GENERATE_LOGGER, uri, offset));
+    }
+
+    @Override
+    public Set<String> getCommands() {
+        return commands;
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_SelectLoggerName=Logger field name",
+    })
+    public CompletableFuture<Object> processCommand(NbCodeLanguageClient client, String command, List<Object> arguments) {
+        if (arguments.size() > 1) {
+            String uri = gson.fromJson(gson.toJson(arguments.get(0)), String.class);
+            int offset = gson.fromJson(gson.toJson(arguments.get(1)), Integer.class);
+            client.showInputBox(new ShowInputBoxParams(Bundle.DN_SelectLoggerName(), "LOG")).thenAccept(value -> {
+                if (value != null) {

Review comment:
       Fixed.

##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/LoggerGenerator.java
##########
@@ -0,0 +1,135 @@
+/*
+ * 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.ClassTree;
+import com.sun.source.tree.VariableTree;
+import com.sun.source.util.TreePath;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.logging.Logger;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+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.eclipse.lsp4j.TextEdit;
+import org.eclipse.lsp4j.WorkspaceEdit;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.GeneratorUtilities;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.TreeUtilities;
+import org.netbeans.modules.java.lsp.server.Utils;
+import org.openide.filesystems.FileObject;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public final class LoggerGenerator extends CodeGenerator {
+
+    public static final String GENERATE_LOGGER =  "java.generate.logger";
+
+    private final Set<String> commands = Collections.singleton(GENERATE_LOGGER);
+    private final Gson gson = new Gson();
+
+    LoggerGenerator() {
+    }
+
+    @Override
+    @NbBundle.Messages({
+        "DN_GenerateLogger=Generate Logger...",
+    })
+    public List<CodeAction> getCodeActions(CompilationInfo info, CodeActionParams params) {
+        List<String> only = params.getContext().getOnly();
+        if (only == null || !only.contains(CodeActionKind.Source)) {
+            return Collections.emptyList();
+        }
+        int offset = getOffset(info, params.getRange().getStart());
+        TreePath tp = info.getTreeUtilities().pathFor(offset);
+        tp = info.getTreeUtilities().getPathElementOfKind(TreeUtilities.CLASS_TREE_KINDS, tp);
+        if (tp == null) {
+            return Collections.emptyList();
+        }
+        TypeElement typeElement = (TypeElement) info.getTrees().getElement(tp);
+        if (typeElement == null || !typeElement.getKind().isClass()) {
+            return Collections.emptyList();
+        }
+        for (VariableElement ve : ElementFilter.fieldsIn(typeElement.getEnclosedElements())) {
+            TypeMirror type = ve.asType();
+            if (type.getKind() == TypeKind.DECLARED && ((TypeElement)((DeclaredType)type).asElement()).getQualifiedName().contentEquals(Logger.class.getName())) {
+                return Collections.emptyList();
+            }
+        }
+        String uri = Utils.toUri(info.getFileObject());

Review comment:
       Fixed.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


[GitHub] [netbeans] JaroslavTulach commented on a change in pull request #2587: Exisitng CodeGenerators exposed as CodeActions via LSP.

Posted by GitBox <gi...@apache.org>.
JaroslavTulach commented on a change in pull request #2587:
URL: https://github.com/apache/netbeans/pull/2587#discussion_r542112656



##########
File path: java/java.lsp.server/vscode/src/extension.ts
##########
@@ -442,6 +418,13 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex
             commands.executeCommand('setContext', 'nbJavaLSReady', true);
             c.onNotification(StatusMessageRequest.type, showStatusBarMessage);
             c.onNotification(LogMessageNotification.type, (param) => handleLog(log, param.message));
+            c.onRequest(QuickPickRequest.type, async param => {
+                const selected = await window.showQuickPick(param.items, { placeHolder: param.placeHolder, canPickMany: param.canPickMany });
+                return selected ? Array.isArray(selected) ? selected : [selected] : undefined;

Review comment:
       What happens when the dialog is cancelled? I guess here it returns `undefined`. How does that look on the Java side?

##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/NbCodeLanguageClient.java
##########
@@ -40,7 +43,25 @@
      */
     @JsonNotification("window/showStatusBarMessage")
     public void showStatusBarMessage(@NonNull ShowStatusMessageParams params);
-    
+
+    /**
+     * Shows a selection list allowing multiple selections.
+     *
+     * @param params input parameters
+     * @return selected items
+     */
+    @JsonRequest("window/showQuickPick")
+    public CompletableFuture<List<QuickPickItem>> showQuickPick(@NonNull ShowQuickPickParams params);

Review comment:
       Great. This is the _UI interaction_ I was dreaming of.

##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeGenerator.java
##########
@@ -0,0 +1,312 @@
+/*
+ * 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.sun.source.tree.LineMap;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UncheckedIOException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.nio.file.Files;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.TypeParameterElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import org.eclipse.lsp4j.CodeAction;
+import org.eclipse.lsp4j.CodeActionParams;
+import org.eclipse.lsp4j.Command;
+import org.eclipse.lsp4j.Position;
+import org.eclipse.xtext.xbase.lib.Pure;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.ElementHandle;
+import org.netbeans.modules.editor.java.Utilities;
+import org.netbeans.modules.java.source.ElementHandleAccessor;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.filesystems.URLMapper;
+import org.openide.modules.Places;
+import org.openide.util.Exceptions;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public abstract class CodeGenerator {
+
+    public static final String CODE_GENERATOR_KIND = "source.generate";
+    protected static final String ERROR = "<error>"; //NOI18N
+
+    public abstract List<CodeAction> getCodeActions(CompilationInfo info, CodeActionParams params);
+
+    public abstract Set<String> getCommands();
+
+    public abstract CompletableFuture<Object> processCommand(NbCodeLanguageClient client, String command, List<Object> arguments);
+
+    protected static int getOffset(CompilationInfo info, Position pos) {
+        LineMap lm = info.getCompilationUnit().getLineMap();
+        return (int) lm.getPosition(pos.getLine() + 1, pos.getCharacter() + 1);
+    }
+
+    protected static CodeAction createCodeAction(String name, String kind, String command, Object... args) {
+        CodeAction action = new CodeAction(name);
+        action.setKind(kind);
+        action.setCommand(new Command(name, command, Arrays.asList(args)));
+        return action;
+    }
+
+    protected static String toUri(FileObject file) {
+        if (FileUtil.isArchiveArtifact(file)) {
+            //VS code cannot open jar:file: URLs, workaround:

Review comment:
       Using `jar:file` URLs is quite frequent and it would ge good to get it working more properly. There is a `URLMapper` in the file system API where one can request [EXTERNAL URL](https://bits.netbeans.org/12.0/javadoc/org-openide-filesystems/org/openide/filesystems/URLMapper.html#EXTERNAL). Wouldn't it be better to get it working for `jar:file`?

##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java
##########
@@ -1063,20 +1044,10 @@ protected void performRewrite(JavaFix.TransformationContext ctx) throws Exceptio
         try {
             js.runUserActionTask(cc -> {
                 cc.toPhase(JavaSource.Phase.RESOLVED);
-
-                Pair<Set<VariableElement>, Set<VariableElement>> pair = GetterSetterGenerator.findMissingGettersSetters(cc, params.getRange(), false);
-                boolean missingGetters = !pair.first().isEmpty();
-                boolean missingSetters = !pair.second().isEmpty();
-                String uri = toUri(cc.getFileObject());
-
-                if (missingGetters) {
-                    result.add(Either.forRight(createCodeGeneratorAction(Bundle.DN_GenerateGetters(), Server.GENERATE_GETTERS, uri, params.getRange())));
-                }
-                if (missingSetters) {
-                    result.add(Either.forRight(createCodeGeneratorAction(Bundle.DN_GenerateSetters(), Server.GENERATE_SETTERS, uri, params.getRange())));
-                }
-                if (missingGetters && missingSetters) {
-                    result.add(Either.forRight(createCodeGeneratorAction(Bundle.DN_GenerateGettersSetters(), Server.GENERATE_GETTERS_SETTERS, uri, params.getRange())));
+                for (CodeGenerator codeGenerator : Server.CODE_GENERATORS) {

Review comment:
       I guess this is the place that instructs VSCode to show the code generator actions.

##########
File path: java/java.lsp.server/vscode/package.json
##########
@@ -140,21 +140,6 @@
 				"title": "Compile Workspace",
 				"category": "Java"
 			},
-			{
-				"command": "java.generate.getters.menu",

Review comment:
       A lot of registrations is removed. However I assume they are somehow added back, right?




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


[GitHub] [netbeans] JaroslavTulach commented on a change in pull request #2587: Existing CodeGenerators exposed as CodeActions via LSP.

Posted by GitBox <gi...@apache.org>.
JaroslavTulach commented on a change in pull request #2587:
URL: https://github.com/apache/netbeans/pull/2587#discussion_r543063773



##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeGenerator.java
##########
@@ -0,0 +1,312 @@
+/*
+ * 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.sun.source.tree.LineMap;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UncheckedIOException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.nio.file.Files;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.TypeParameterElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import org.eclipse.lsp4j.CodeAction;
+import org.eclipse.lsp4j.CodeActionParams;
+import org.eclipse.lsp4j.Command;
+import org.eclipse.lsp4j.Position;
+import org.eclipse.xtext.xbase.lib.Pure;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.ElementHandle;
+import org.netbeans.modules.editor.java.Utilities;
+import org.netbeans.modules.java.source.ElementHandleAccessor;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.filesystems.URLMapper;
+import org.openide.modules.Places;
+import org.openide.util.Exceptions;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public abstract class CodeGenerator {
+
+    public static final String CODE_GENERATOR_KIND = "source.generate";
+    protected static final String ERROR = "<error>"; //NOI18N
+
+    public abstract List<CodeAction> getCodeActions(CompilationInfo info, CodeActionParams params);
+
+    public abstract Set<String> getCommands();
+
+    public abstract CompletableFuture<Object> processCommand(NbCodeLanguageClient client, String command, List<Object> arguments);
+
+    protected static int getOffset(CompilationInfo info, Position pos) {
+        LineMap lm = info.getCompilationUnit().getLineMap();
+        return (int) lm.getPosition(pos.getLine() + 1, pos.getCharacter() + 1);
+    }
+
+    protected static CodeAction createCodeAction(String name, String kind, String command, Object... args) {
+        CodeAction action = new CodeAction(name);
+        action.setKind(kind);
+        action.setCommand(new Command(name, command, Arrays.asList(args)));
+        return action;
+    }
+
+    protected static String toUri(FileObject file) {
+        if (FileUtil.isArchiveArtifact(file)) {
+            //VS code cannot open jar:file: URLs, workaround:

Review comment:
       I cannot answer your question DuĊĦane. I am just pointing out that rather than converting `jar:file:` URLs manually, we'd rather use exiting API for that. If it is not working properly, we'd rather invest in fixing it.
   
   Now I get that this code has been introduced during #2530. I find the code strange, but it is not a blocker.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


[GitHub] [netbeans] dbalek commented on a change in pull request #2587: Existing CodeGenerators exposed as CodeActions via LSP.

Posted by GitBox <gi...@apache.org>.
dbalek commented on a change in pull request #2587:
URL: https://github.com/apache/netbeans/pull/2587#discussion_r543410760



##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java
##########
@@ -332,12 +338,19 @@ protected LanguageClient client() {
     
     public static final String JAVA_BUILD_WORKSPACE =  "java.build.workspace";
     public static final String GRAALVM_PAUSE_SCRIPT =  "graalvm.pause.script";
-    public static final String GENERATE_GETTERS =  "java.generate.getters";
-    public static final String GENERATE_SETTERS =  "java.generate.setters";
-    public static final String GENERATE_GETTERS_SETTERS =  "java.generate.getters.setters";
     static final String INDEXING_COMPLETED = "Indexing completed.";
     static final String NO_JAVA_SUPPORT = "Cannot initialize Java support on JDK ";
-    
+
+    static final CodeGenerator[] CODE_GENERATORS = new CodeGenerator[] {

Review comment:
       Done.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists