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/15 10:39:48 UTC

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

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