You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by db...@apache.org on 2020/12/15 18:01:00 UTC
[netbeans] branch master updated: Existing CodeGenerators exposed
as CodeActions via LSP. (#2587)
This is an automated email from the ASF dual-hosted git repository.
dbalek pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git
The following commit(s) were added to refs/heads/master by this push:
new b6104f5 Existing CodeGenerators exposed as CodeActions via LSP. (#2587)
b6104f5 is described below
commit b6104f59cea7350169f9d0737350bc71225b1e6c
Author: Dusan Balek <du...@oracle.com>
AuthorDate: Tue Dec 15 19:00:41 2020 +0100
Existing CodeGenerators exposed as CodeActions via LSP. (#2587)
* Exisitng CodeGenerators exposed as CodeActions via LSP.
---
.../editor/codegen/DelegateMethodGenerator.java | 2 +-
.../editor/codegen/EqualsHashCodeGenerator.java | 6 +-
.../java/editor/codegen/LoggerGenerator.java | 2 +-
.../java/editor/codegen/ToStringGenerator.java | 2 +-
java/java.lsp.server/nbproject/project.xml | 43 +-
.../java/lsp/server/protocol/CodeGenerator.java | 215 ++++++
.../lsp/server/protocol/ConstructorGenerator.java | 217 ++++++
.../server/protocol/DelegateMethodGenerator.java | 223 ++++++
.../server/protocol/EqualsHashCodeGenerator.java | 172 +++++
.../lsp/server/protocol/GetterSetterGenerator.java | 184 +++--
.../protocol/ImplementOverrideMethodGenerator.java | 183 +++++
.../java/lsp/server/protocol/LoggerGenerator.java | 144 ++++
.../lsp/server/protocol/NbCodeClientWrapper.java | 12 +-
.../lsp/server/protocol/NbCodeLanguageClient.java | 23 +-
.../java/lsp/server/protocol/QuickPickItem.java | 206 ++++++
.../modules/java/lsp/server/protocol/Server.java | 27 +-
.../lsp/server/protocol/ShowInputBoxParams.java | 124 ++++
.../lsp/server/protocol/ShowQuickPickParams.java | 159 +++++
.../server/protocol/TextDocumentServiceImpl.java | 131 +---
.../lsp/server/protocol/ToStringGenerator.java | 165 +++++
.../lsp/server/protocol/WorkspaceServiceImpl.java | 31 +-
.../java/lsp/server/protocol/ServerTest.java | 757 ++++++++++++++++++++-
java/java.lsp.server/vscode/package-lock.json | 30 +-
java/java.lsp.server/vscode/package.json | 50 +-
java/java.lsp.server/vscode/src/extension.ts | 33 +-
java/java.lsp.server/vscode/src/protocol.ts | 36 +
26 files changed, 2845 insertions(+), 332 deletions(-)
diff --git a/java/java.editor/src/org/netbeans/modules/java/editor/codegen/DelegateMethodGenerator.java b/java/java.editor/src/org/netbeans/modules/java/editor/codegen/DelegateMethodGenerator.java
index c4b3277..6b95763 100644
--- a/java/java.editor/src/org/netbeans/modules/java/editor/codegen/DelegateMethodGenerator.java
+++ b/java/java.editor/src/org/netbeans/modules/java/editor/codegen/DelegateMethodGenerator.java
@@ -299,7 +299,7 @@ public class DelegateMethodGenerator implements CodeGenerator {
return ElementNode.Description.create(descriptions);
}
- static void generateDelegatingMethods(WorkingCopy wc, TreePath path, VariableElement delegate, Iterable<? extends ExecutableElement> methods, int offset) {
+ public static void generateDelegatingMethods(WorkingCopy wc, TreePath path, VariableElement delegate, Iterable<? extends ExecutableElement> methods, int offset) {
assert TreeUtilities.CLASS_TREE_KINDS.contains(path.getLeaf().getKind());
TypeElement te = (TypeElement)wc.getTrees().getElement(path);
if (te != null) {
diff --git a/java/java.editor/src/org/netbeans/modules/java/editor/codegen/EqualsHashCodeGenerator.java b/java/java.editor/src/org/netbeans/modules/java/editor/codegen/EqualsHashCodeGenerator.java
index 08ebbac..8795d63 100644
--- a/java/java.editor/src/org/netbeans/modules/java/editor/codegen/EqualsHashCodeGenerator.java
+++ b/java/java.editor/src/org/netbeans/modules/java/editor/codegen/EqualsHashCodeGenerator.java
@@ -393,8 +393,8 @@ public class EqualsHashCodeGenerator implements CodeGenerator {
generateEqualsAndHashCode(wc, path, e, h, -1);
}
-
- static void generateEqualsAndHashCode(WorkingCopy wc, TreePath path, Iterable<? extends VariableElement> equalsFields, Iterable<? extends VariableElement> hashCodeFields, int offset) {
+
+ public static void generateEqualsAndHashCode(WorkingCopy wc, TreePath path, Iterable<? extends VariableElement> equalsFields, Iterable<? extends VariableElement> hashCodeFields, int offset) {
assert TreeUtilities.CLASS_TREE_KINDS.contains(path.getLeaf().getKind());
TypeElement te = (TypeElement)wc.getTrees().getElement(path);
if (te != null) {
@@ -419,7 +419,7 @@ public class EqualsHashCodeGenerator implements CodeGenerator {
members.add(createEqualsMethod(wc, equalsFields, dt, scope));
}
wc.rewrite(nue, GeneratorUtils.insertClassMembers(wc, nue, members, offset));
- }
+ }
}
private static MethodTree createEqualsMethod(WorkingCopy wc, Iterable<? extends VariableElement> equalsFields, DeclaredType type, Scope scope) {
diff --git a/java/java.editor/src/org/netbeans/modules/java/editor/codegen/LoggerGenerator.java b/java/java.editor/src/org/netbeans/modules/java/editor/codegen/LoggerGenerator.java
index bf91dcd..7b11f99 100644
--- a/java/java.editor/src/org/netbeans/modules/java/editor/codegen/LoggerGenerator.java
+++ b/java/java.editor/src/org/netbeans/modules/java/editor/codegen/LoggerGenerator.java
@@ -152,7 +152,7 @@ public class LoggerGenerator implements CodeGenerator {
}
}
- private static VariableTree createLoggerField(TreeMaker make, ClassTree cls, CharSequence name, Set<Modifier> mods) {
+ public static VariableTree createLoggerField(TreeMaker make, ClassTree cls, CharSequence name, Set<Modifier> mods) {
ModifiersTree modifiers = make.Modifiers(mods, Collections.<AnnotationTree>emptyList());
final List<ExpressionTree> none = Collections.<ExpressionTree>emptyList();
IdentifierTree className = make.Identifier(cls.getSimpleName());
diff --git a/java/java.editor/src/org/netbeans/modules/java/editor/codegen/ToStringGenerator.java b/java/java.editor/src/org/netbeans/modules/java/editor/codegen/ToStringGenerator.java
index e0ccca1..c855d3c 100644
--- a/java/java.editor/src/org/netbeans/modules/java/editor/codegen/ToStringGenerator.java
+++ b/java/java.editor/src/org/netbeans/modules/java/editor/codegen/ToStringGenerator.java
@@ -209,7 +209,7 @@ public class ToStringGenerator implements CodeGenerator {
}
}
- private static MethodTree createToStringMethod(WorkingCopy wc, Iterable<? extends VariableElement> fields, String typeName, boolean useStringBuilder) {
+ public static MethodTree createToStringMethod(WorkingCopy wc, Iterable<? extends VariableElement> fields, String typeName, boolean useStringBuilder) {
TreeMaker make = wc.getTreeMaker();
Set<Modifier> mods = EnumSet.of(Modifier.PUBLIC);
List<AnnotationTree> annotations = new LinkedList<>();
diff --git a/java/java.lsp.server/nbproject/project.xml b/java/java.lsp.server/nbproject/project.xml
index b940103..287f215 100644
--- a/java/java.lsp.server/nbproject/project.xml
+++ b/java/java.lsp.server/nbproject/project.xml
@@ -26,6 +26,23 @@
<code-name-base>org.netbeans.modules.java.lsp.server</code-name-base>
<module-dependencies>
<dependency>
+ <code-name-base>com.google.gson</code-name-base>
+ <build-prerequisite/>
+ <compile-dependency/>
+ <run-dependency>
+ <specification-version>2.7</specification-version>
+ </run-dependency>
+ </dependency>
+ <dependency>
+ <code-name-base>org.netbeans.api.annotations.common</code-name-base>
+ <build-prerequisite/>
+ <compile-dependency/>
+ <run-dependency>
+ <release-version>1</release-version>
+ <specification-version>1.36</specification-version>
+ </run-dependency>
+ </dependency>
+ <dependency>
<code-name-base>org.netbeans.api.debugger</code-name-base>
<build-prerequisite/>
<compile-dependency/>
@@ -61,15 +78,6 @@
</run-dependency>
</dependency>
<dependency>
- <code-name-base>org.netbeans.api.annotations.common</code-name-base>
- <build-prerequisite/>
- <compile-dependency/>
- <run-dependency>
- <release-version>1</release-version>
- <specification-version>1.36</specification-version>
- </run-dependency>
- </dependency>
- <dependency>
<code-name-base>org.netbeans.api.java.classpath</code-name-base>
<build-prerequisite/>
<compile-dependency/>
@@ -130,6 +138,15 @@
</run-dependency>
</dependency>
<dependency>
+ <code-name-base>org.netbeans.modules.editor.lib2</code-name-base>
+ <build-prerequisite/>
+ <compile-dependency/>
+ <run-dependency>
+ <release-version>1</release-version>
+ <specification-version>2.31</specification-version>
+ </run-dependency>
+ </dependency>
+ <dependency>
<code-name-base>org.netbeans.modules.editor.mimelookup</code-name-base>
<build-prerequisite/>
<compile-dependency/>
@@ -410,14 +427,6 @@
<specification-version>9.14</specification-version>
</run-dependency>
</dependency>
- <dependency>
- <code-name-base>com.google.gson</code-name-base>
- <build-prerequisite/>
- <compile-dependency/>
- <run-dependency>
- <specification-version>2.7</specification-version>
- </run-dependency>
- </dependency>
</module-dependencies>
<test-dependencies>
<test-type>
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeGenerator.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeGenerator.java
new file mode 100644
index 0000000..9c741f8
--- /dev/null
+++ b/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();) {
+ sb.append(Utilities.getTypeName(info, bIt.next(), false));
+ if (bIt.hasNext()) {
+ sb.append(" & "); // NOI18N
+ }
+ }
+ }
+ }
+ if (it.hasNext()) {
+ sb.append(", "); // NOI18N
+ }
+ }
+ sb.append(">"); // NOI18N
+ }
+ return sb.toString();
+ }
+
+ protected static String createLabel(CompilationInfo info, VariableElement e) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(e.getSimpleName());
+ if (e.getKind() != ElementKind.ENUM_CONSTANT) {
+ sb.append(" : "); // NOI18N
+ sb.append(Utilities.getTypeName(info, e.asType(), false));
+ }
+ return sb.toString();
+ }
+
+ protected static String createLabel(CompilationInfo info, ExecutableElement e) {
+ StringBuilder sb = new StringBuilder();
+ if (e.getKind() == ElementKind.CONSTRUCTOR) {
+ sb.append(e.getEnclosingElement().getSimpleName());
+ } else {
+ sb.append(e.getSimpleName());
+ }
+ sb.append("("); // NOI18N
+ for (Iterator<? extends VariableElement> it = e.getParameters().iterator(); it.hasNext();) {
+ VariableElement param = it.next();
+ if (!it.hasNext() && e.isVarArgs() && param.asType().getKind() == TypeKind.ARRAY) {
+ sb.append(Utilities.getTypeName(info, ((ArrayType) param.asType()).getComponentType(), false));
+ sb.append("...");
+ } else {
+ sb.append(Utilities.getTypeName(info, param.asType(), false));
+ }
+ sb.append(" "); // NOI18N
+ sb.append(param.getSimpleName());
+ if (it.hasNext()) {
+ sb.append(", "); // NOI18N
+ }
+ }
+ sb.append(")"); // NOI18N
+ if (e.getKind() != ElementKind.CONSTRUCTOR) {
+ TypeMirror rt = e.getReturnType();
+ if (rt.getKind() != TypeKind.VOID) {
+ sb.append(" : "); // NOI18N
+ sb.append(Utilities.getTypeName(info, e.getReturnType(), false));
+ }
+ }
+ return sb.toString();
+ }
+
+ public static class ElementData {
+
+ private String kind;
+ private String[] signature;
+
+ public ElementData() {
+ }
+
+ public ElementData(Element element) {
+ ElementHandle<Element> handle = ElementHandle.create(element);
+ this.kind = handle.getKind().name();
+ this.signature = ElementHandleAccessor.getInstance().getJVMSignature(handle);
+ }
+
+ Element resolve(CompilationInfo info) {
+ ElementHandle handle = ElementHandleAccessor.getInstance().create(ElementKind.valueOf(kind), signature);
+ return handle.resolve(info);
+ }
+
+ @Pure
+ public String getKind() {
+ return kind;
+ }
+
+ public void setKind(final String kind) {
+ this.kind = kind;
+ }
+
+ @Pure
+ public String[] getSignature() {
+ return signature;
+ }
+
+ public void setSignature(final String[] signature) {
+ this.signature = signature;
+ }
+
+ @Override
+ @Pure
+ public int hashCode() {
+ int hash = 7;
+ hash = 97 * hash + Objects.hashCode(this.kind);
+ hash = 97 * hash + Arrays.deepHashCode(this.signature);
+ return hash;
+ }
+
+ @Override
+ @Pure
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final ElementData other = (ElementData) obj;
+ if (this.kind != other.kind) {
+ return false;
+ }
+ if (!Arrays.deepEquals(this.signature, other.signature)) {
+ return false;
+ }
+ return true;
+ }
+ }
+}
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ConstructorGenerator.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ConstructorGenerator.java
new file mode 100644
index 0000000..97a39f7
--- /dev/null
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ConstructorGenerator.java
@@ -0,0 +1,217 @@
+/*
+ * 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;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+@ServiceProvider(service = CodeGenerator.class, position = 10)
+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();
+
+ public 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);
+ }
+ }
+ } else {
+ client.logMessage(new MessageParams(MessageType.Error, String.format("Illegal number of arguments received for command: %s", command)));
+ }
+ 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);
+ if (js == null) {
+ throw new IOException("Cannot get JavaSource for: " + uri);
+ }
+ 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());
+ List<VariableElement> selectedFields = fields.stream().map(item -> {
+ ElementData data = gson.fromJson(gson.toJson(item.getUserData()), ElementData.class);
+ return (VariableElement)data.resolve(wc);
+ }).collect(Collectors.toList());
+ GeneratorUtils.generateConstructors(wc, tp, selectedFields, selectedConstructors, -1);
+ }
+ });
+ client.applyEdit(new ApplyWorkspaceEditParams(new WorkspaceEdit(Collections.singletonMap(uri, edits))));
+ } catch (IOException | IllegalArgumentException ex) {
+ client.logMessage(new MessageParams(MessageType.Error, ex.getLocalizedMessage()));
+ }
+ }
+}
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/DelegateMethodGenerator.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/DelegateMethodGenerator.java
new file mode 100644
index 0000000..d59cec4
--- /dev/null
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/DelegateMethodGenerator.java
@@ -0,0 +1,223 @@
+/*
+ * 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;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+@ServiceProvider(service = CodeGenerator.class, position = 60)
+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();
+
+ public 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));
+ }
+ });
+ }
+ } else {
+ client.logMessage(new MessageParams(MessageType.Error, String.format("Illegal number of arguments received for command: %s", command)));
+ }
+ 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);
+ if (js == null) {
+ throw new IOException("Cannot get JavaSource for: " + uri);
+ }
+ 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);
+ if (origin != null && field != null) {
+ 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);
+ if (js == null) {
+ throw new IOException("Cannot get JavaSource for: " + uri);
+ }
+ 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))));
+ } catch (IOException | IllegalArgumentException ex) {
+ client.logMessage(new MessageParams(MessageType.Error, ex.getLocalizedMessage()));
+ }
+ }
+}
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/EqualsHashCodeGenerator.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/EqualsHashCodeGenerator.java
new file mode 100644
index 0000000..45fb6d3
--- /dev/null
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/EqualsHashCodeGenerator.java
@@ -0,0 +1,172 @@
+/*
+ * 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.Tree;
+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.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.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.JavaSource;
+import org.netbeans.modules.java.lsp.server.Utils;
+import org.openide.filesystems.FileObject;
+import org.openide.util.NbBundle;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+@ServiceProvider(service = CodeGenerator.class, position = 40)
+public final class EqualsHashCodeGenerator extends CodeGenerator {
+
+ public static final String GENERATE_EQUALS = "java.generate.equals";
+ public static final String GENERATE_HASH_CODE = "java.generate.hashCode";
+ public static final String GENERATE_EQUALS_HASH_CODE = "java.generate.equals.hashCode";
+
+ private final Set<String> commands = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(GENERATE_EQUALS_HASH_CODE, GENERATE_EQUALS, GENERATE_HASH_CODE)));
+ private final Gson gson = new Gson();
+
+ public EqualsHashCodeGenerator() {
+ }
+
+ @Override
+ @NbBundle.Messages({
+ "DN_GenerateEquals=Generate equals()...",
+ "DN_GenerateHashCode=Generate hashCode()...",
+ "DN_GenerateEqualsHashCode=Generate equals() and hashCode()...",
+ })
+ 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(Tree.Kind.CLASS, tp);
+ if (tp == null) {
+ return Collections.emptyList();
+ }
+ TypeElement type = (TypeElement) info.getTrees().getElement(tp);
+ if (type == null || type.getKind() != ElementKind.CLASS) {
+ return Collections.emptyList();
+ }
+ ExecutableElement[] equalsHashCode = org.netbeans.modules.java.editor.codegen.EqualsHashCodeGenerator.overridesHashCodeAndEquals(info, type, null);
+ if (equalsHashCode[0] != null && equalsHashCode[1] != null) {
+ return Collections.emptyList();
+ }
+ List<QuickPickItem> fields = new ArrayList<>();
+ for (VariableElement variableElement : ElementFilter.fieldsIn(type.getEnclosedElements())) {
+ if (!ERROR.contentEquals(variableElement.getSimpleName()) && !variableElement.getModifiers().contains(Modifier.STATIC)) {
+ QuickPickItem item = new QuickPickItem(createLabel(info, variableElement));
+ item.setUserData(new ElementData(variableElement));
+ fields.add(item);
+ }
+ }
+ if (fields.isEmpty()) {
+ return Collections.emptyList();
+ }
+ String uri = Utils.toUri(info.getFileObject());
+ if (equalsHashCode[0] == null) {
+ if (equalsHashCode[1] == null) {
+ return Collections.singletonList(createCodeAction(Bundle.DN_GenerateEqualsHashCode(), CODE_GENERATOR_KIND, GENERATE_EQUALS_HASH_CODE, uri, offset, fields));
+ }
+ return Collections.singletonList(createCodeAction(Bundle.DN_GenerateEquals(), CODE_GENERATOR_KIND, GENERATE_EQUALS, uri, offset, fields));
+ }
+ return Collections.singletonList(createCodeAction(Bundle.DN_GenerateHashCode(), CODE_GENERATOR_KIND, GENERATE_HASH_CODE, uri, offset, fields));
+ }
+
+ @Override
+ public Set<String> getCommands() {
+ return commands;
+ }
+
+ @Override
+ @NbBundle.Messages({
+ "DN_SelectEquals=Select fields to be included in equals()",
+ "DN_SelectHashCode=Select fields to be included in hashCode()",
+ "DN_SelectEqualsHashCode=Select fields to be included in equals() and hashCode()",
+ })
+ 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> fields = Arrays.asList(gson.fromJson(gson.toJson(arguments.get(2)), QuickPickItem[].class));
+ String text;
+ boolean generateEquals = !GENERATE_HASH_CODE.equals(command);
+ boolean generateHashCode = !GENERATE_EQUALS.equals(command);
+ switch (command) {
+ case GENERATE_EQUALS: text = Bundle.DN_SelectEquals(); break;
+ case GENERATE_HASH_CODE: text = Bundle.DN_SelectHashCode(); break;
+ default: text = Bundle.DN_SelectEqualsHashCode(); break;
+ }
+ client.showQuickPick(new ShowQuickPickParams(text, true, fields)).thenAccept(selected -> {
+ if (selected != null) {
+ try {
+ FileObject file = Utils.fromUri(uri);
+ JavaSource js = JavaSource.forFileObject(file);
+ if (js == null) {
+ throw new IOException("Cannot get JavaSource for: " + uri);
+ }
+ List<TextEdit> edits = TextDocumentServiceImpl.modify2TextEdits(js, wc -> {
+ wc.toPhase(JavaSource.Phase.RESOLVED);
+ TreePath tp = wc.getTreeUtilities().pathFor(offset);
+ tp = wc.getTreeUtilities().getPathElementOfKind(Tree.Kind.CLASS, tp);
+ if (tp != null) {
+ List<VariableElement> selectedFields = selected.stream().map(item -> {
+ ElementData data = gson.fromJson(gson.toJson(item.getUserData()), ElementData.class);
+ return (VariableElement)data.resolve(wc);
+ }).collect(Collectors.toList());
+ org.netbeans.modules.java.editor.codegen.EqualsHashCodeGenerator.generateEqualsAndHashCode(wc, tp, generateEquals ? selectedFields : null, generateHashCode ? selectedFields : null, -1);
+ }
+ });
+ client.applyEdit(new ApplyWorkspaceEditParams(new WorkspaceEdit(Collections.singletonMap(uri, edits))));
+ } catch (IOException | IllegalArgumentException ex) {
+ client.logMessage(new MessageParams(MessageType.Error, ex.getLocalizedMessage()));
+ }
+ }
+ });
+ } else {
+ client.logMessage(new MessageParams(MessageType.Error, String.format("Illegal number of arguments received for command: %s", command)));
+ }
+ return CompletableFuture.completedFuture(true);
+ }
+}
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/GetterSetterGenerator.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/GetterSetterGenerator.java
index e51e341..4b7df97 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/GetterSetterGenerator.java
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/GetterSetterGenerator.java
@@ -18,58 +18,180 @@
*/
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;
+import org.openide.util.lookup.ServiceProvider;
/**
*
* @author lahvac
*/
-public class GetterSetterGenerator {
+@ServiceProvider(service = CodeGenerator.class, position = 30)
+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();
+
+ public GetterSetterGenerator() {
+ }
+
+ @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 && !selected.isEmpty()) {
+ generate(client, kind, uri, offset, selected);
+ }
+ });
+ } else if (fields.size() == 1) {
+ generate(client, kind, uri, offset, fields);
+ }
+ } else {
+ client.logMessage(new MessageParams(MessageType.Error, String.format("Illegal number of arguments received for command: %s", command)));
+ }
+ return CompletableFuture.completedFuture(true);
+ }
- while (tp != null && !TreeUtilities.CLASS_TREE_KINDS.contains(tp.getLeaf().getKind())) {
- tp = tp.getParentPath();
+ 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);
+ if (js == null) {
+ throw new IOException("Cannot get JavaSource for: " + uri);
+ }
+ 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<VariableElement> variableElements = fields.stream().map(item -> {
+ ElementData data = gson.fromJson(gson.toJson(item.getUserData()), ElementData.class);
+ return (VariableElement) data.resolve(wc);
+ }).collect(Collectors.toList());
+ GeneratorUtils.generateGettersAndSetters(wc, tp, variableElements, kind, -1);
+ }
+ });
+ client.applyEdit(new ApplyWorkspaceEditParams(new WorkspaceEdit(Collections.singletonMap(uri, edits))));
+ } catch (IOException ex) {
+ client.logMessage(new MessageParams(MessageType.Error, ex.getLocalizedMessage()));
}
+ }
+ private static Pair<Set<VariableElement>, Set<VariableElement>> findMissingGettersSetters(CompilationInfo info, Range range, boolean all) {
+ TreePath tp = info.getTreeUtilities().pathFor(getOffset(info, range.getStart()));
+ tp = info.getTreeUtilities().getPathElementOfKind(TreeUtilities.CLASS_TREE_KINDS, tp);
if (tp == null) {
return Pair.of(Collections.emptySet(), Collections.emptySet());
}
TypeElement type = (TypeElement) info.getTrees().getElement(tp);
-
if (type == null) {
return Pair.of(Collections.emptySet(), Collections.emptySet());
}
@@ -90,7 +212,7 @@ public class GetterSetterGenerator {
}
}
- Pair<Set<VariableElement>, Set<VariableElement>> pair = GetterSetterGenerator.findMissingGettersSetters(info, type);
+ Pair<Set<VariableElement>, Set<VariableElement>> pair = findMissingGettersSetters(info, type);
pair.first().retainAll(selectedFields);
pair.second().retainAll(selectedFields);
@@ -125,40 +247,4 @@ public class GetterSetterGenerator {
return Pair.of(missingGetters, missingSetters);
}
-
- public static void generateGettersSetters(LanguageClient client, String uri, GenKind kind, Range range, boolean all) throws MalformedURLException, IOException {
- FileObject file = fromUri(uri);
- JavaSource js = JavaSource.forFileObject(file);
-
- List<TextEdit> edits = TextDocumentServiceImpl.modify2TextEdits(js, wc -> {
- wc.toPhase(JavaSource.Phase.RESOLVED);
- Pair<Set<VariableElement>, Set<VariableElement>> missingGettersSetters = findMissingGettersSetters(wc, range, all);
- Set<VariableElement> fields = new LinkedHashSet<>();
- fields.addAll(missingGettersSetters.first());
- fields.addAll(missingGettersSetters.second());
- if (!fields.isEmpty()) {
- TreePath tp = wc.getTrees().getPath(fields.iterator().next().getEnclosingElement());
- GeneratorUtils.generateGettersAndSetters(wc, tp, fields, kind.type, getOffset(wc, range.getStart()));
- }
- });
-
- client.applyEdit(new ApplyWorkspaceEditParams(new WorkspaceEdit(Collections.singletonMap(uri, edits))));
- }
-
- private static int getOffset(CompilationInfo info, Position pos) {
- LineMap lm = info.getCompilationUnit().getLineMap();
- return (int) lm.getPosition(pos.getLine() + 1, pos.getCharacter() + 1);
- }
-
- public enum GenKind {
- GETTERS(GeneratorUtils.GETTERS_ONLY),
- SETTERS(GeneratorUtils.SETTERS_ONLY),
- GETTERS_SETTERS(0);
-
- private final int type;
-
- private GenKind(int type) {
- this.type = type;
- }
- }
}
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ImplementOverrideMethodGenerator.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ImplementOverrideMethodGenerator.java
new file mode 100644
index 0000000..231287d
--- /dev/null
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ImplementOverrideMethodGenerator.java
@@ -0,0 +1,183 @@
+/*
+ * 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;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+@ServiceProvider(service = CodeGenerator.class, position = 70)
+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();
+
+ public 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 && !selected.isEmpty()) {
+ generate(client, uri, offset, isImplement, selected);
+ }
+ });
+ } else {
+ client.logMessage(new MessageParams(MessageType.Error, String.format("Illegal number of arguments received for command: %s", command)));
+ }
+ return CompletableFuture.completedFuture(true);
+ }
+
+ private void generate(NbCodeLanguageClient client, String uri, int offset, boolean isImplement, List<QuickPickItem> methods) {
+ try {
+ FileObject file = Utils.fromUri(uri);
+ JavaSource js = JavaSource.forFileObject(file);
+ if (js == null) {
+ throw new IOException("Cannot get JavaSource for: " + uri);
+ }
+ 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> selectedMethods = methods.stream().map(item -> {
+ ElementData data = gson.fromJson(gson.toJson(item.getUserData()), ElementData.class);
+ return (ExecutableElement)data.resolve(wc);
+ }).collect(Collectors.toList());
+ if (isImplement) {
+ GeneratorUtils.generateAbstractMethodImplementations(wc, tp, selectedMethods, -1);
+ } else {
+ GeneratorUtils.generateMethodOverrides(wc, tp, selectedMethods, -1);
+ }
+ }
+ });
+ client.applyEdit(new ApplyWorkspaceEditParams(new WorkspaceEdit(Collections.singletonMap(uri, edits))));
+ } catch (IOException | IllegalArgumentException ex) {
+ client.logMessage(new MessageParams(MessageType.Error, ex.getLocalizedMessage()));
+ }
+ }
+}
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/LoggerGenerator.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/LoggerGenerator.java
new file mode 100644
index 0000000..e0fcc30
--- /dev/null
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/LoggerGenerator.java
@@ -0,0 +1,144 @@
+/*
+ * 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.BaseUtilities;
+import org.openide.util.NbBundle;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+@ServiceProvider(service = CodeGenerator.class, position = 20)
+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();
+
+ public 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())
+ || type.getKind() == TypeKind.ERROR && ((TypeElement)((DeclaredType)type).asElement()).getSimpleName().contentEquals(Logger.class.getSimpleName())) {
+ 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 && BaseUtilities.isJavaIdentifier(value)) {
+ try {
+ FileObject file = Utils.fromUri(uri);
+ JavaSource js = JavaSource.forFileObject(file);
+ if (js == null) {
+ throw new IOException("Cannot get JavaSource for: " + uri);
+ }
+ 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) {
+ ClassTree cls = (ClassTree) tp.getLeaf();
+ VariableTree field = org.netbeans.modules.java.editor.codegen.LoggerGenerator.createLoggerField(wc.getTreeMaker(), cls, value, EnumSet.of(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL));
+ wc.rewrite(cls, GeneratorUtilities.get(wc).insertClassMember(cls, field));
+ }
+ });
+ client.applyEdit(new ApplyWorkspaceEditParams(new WorkspaceEdit(Collections.singletonMap(uri, edits))));
+ } catch (IOException | IllegalArgumentException ex) {
+ client.logMessage(new MessageParams(MessageType.Error, ex.getLocalizedMessage()));
+ }
+ }
+ });
+ } else {
+ client.logMessage(new MessageParams(MessageType.Error, String.format("Illegal number of arguments received for command: %s", command)));
+ }
+ return CompletableFuture.completedFuture(true);
+ }
+}
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/NbCodeClientWrapper.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/NbCodeClientWrapper.java
index c4ba4b5..afdeb2d 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/NbCodeClientWrapper.java
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/NbCodeClientWrapper.java
@@ -52,7 +52,7 @@ class NbCodeClientWrapper implements NbCodeLanguageClient {
this.clientCaps = clientCaps;
}
}
-
+
@Override
public NbCodeClientCapabilities getNbCodeCapabilities() {
return clientCaps;
@@ -64,6 +64,16 @@ class NbCodeClientWrapper implements NbCodeLanguageClient {
}
@Override
+ public CompletableFuture<List<QuickPickItem>> showQuickPick(ShowQuickPickParams params) {
+ return remote.showQuickPick(params);
+ }
+
+ @Override
+ public CompletableFuture<String> showInputBox(ShowInputBoxParams params) {
+ return remote.showInputBox(params);
+ }
+
+ @Override
public CompletableFuture<ApplyWorkspaceEditResponse> applyEdit(ApplyWorkspaceEditParams params) {
return remote.applyEdit(params);
}
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/NbCodeLanguageClient.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/NbCodeLanguageClient.java
index 06177ae..50fe926 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/NbCodeLanguageClient.java
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/NbCodeLanguageClient.java
@@ -18,8 +18,11 @@
*/
package org.netbeans.modules.java.lsp.server.protocol;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
import org.eclipse.lsp4j.MessageParams;
import org.eclipse.lsp4j.jsonrpc.services.JsonNotification;
+import org.eclipse.lsp4j.jsonrpc.services.JsonRequest;
import org.eclipse.lsp4j.jsonrpc.validation.NonNull;
import org.eclipse.lsp4j.services.LanguageClient;
@@ -40,7 +43,25 @@ public interface NbCodeLanguageClient extends LanguageClient {
*/
@JsonNotification("window/showStatusBarMessage")
public void showStatusBarMessage(@NonNull ShowStatusMessageParams params);
-
+
+ /**
+ * Shows a selection list allowing multiple selections.
+ *
+ * @param params input parameters
+ * @return selected items
+ */
+ @JsonRequest("window/showQuickPick")
+ public CompletableFuture<List<QuickPickItem>> showQuickPick(@NonNull ShowQuickPickParams params);
+
+ /**
+ * Shows an input box to ask the user for input.
+ *
+ * @param params input parameters
+ * @return input value
+ */
+ @JsonRequest("window/showInputBox")
+ public CompletableFuture<String> showInputBox(@NonNull ShowInputBoxParams params);
+
/**
* Returns extended code capabilities.
* @return code capabilities.
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/QuickPickItem.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/QuickPickItem.java
new file mode 100644
index 0000000..3b17385
--- /dev/null
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/QuickPickItem.java
@@ -0,0 +1,206 @@
+/*
+ * 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 java.util.Objects;
+import org.eclipse.lsp4j.jsonrpc.validation.NonNull;
+import org.eclipse.lsp4j.util.Preconditions;
+import org.eclipse.xtext.xbase.lib.Pure;
+import org.eclipse.xtext.xbase.lib.util.ToStringBuilder;
+
+/**
+ * Represents an item that can be selected from a list of items.
+ *
+ * @author Dusan Balek
+ */
+@SuppressWarnings("all")
+public class QuickPickItem {
+
+ /**
+ * A human-readable string which is rendered prominent.
+ */
+ @NonNull
+ private String label;
+
+ /**
+ * A human-readable string which is rendered less prominent in the same line.
+ */
+ private String description;
+
+ /**
+ * A human-readable string which is rendered less prominent in a separate line.
+ */
+ private String detail;
+
+ /**
+ * Optional flag indicating if this item is picked initially.
+ */
+ private boolean picked;
+
+ /**
+ * Optional user data.
+ */
+ private Object userData;
+
+ public QuickPickItem() {
+ }
+
+ public QuickPickItem(@NonNull final String label) {
+ this.label = Preconditions.checkNotNull(label, "label");
+ }
+
+ public QuickPickItem(@NonNull final String label, final String description, final String detail, final boolean picked, final Object userData) {
+ this(label);
+ this.description = description;
+ this.detail = detail;
+ this.picked = picked;
+ this.userData = userData;
+ }
+
+ /**
+ * A human-readable string which is rendered prominent.
+ */
+ @Pure
+ @NonNull
+ public String getLabel() {
+ return label;
+ }
+
+ /**
+ * A human-readable string which is rendered prominent.
+ */
+ public void setLabel(@NonNull final String label) {
+ this.label = label;
+ }
+
+ /**
+ * A human-readable string which is rendered less prominent in the same line.
+ */
+ @Pure
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * A human-readable string which is rendered less prominent in the same line.
+ */
+ public void setDescription(final String description) {
+ this.description = description;
+ }
+
+ /**
+ * A human-readable string which is rendered less prominent in a separate line.
+ */
+ @Pure
+ public String getDetail() {
+ return detail;
+ }
+
+ /**
+ * A human-readable string which is rendered less prominent in a separate line.
+ */
+ public void setDetail(final String detail) {
+ this.detail = detail;
+ }
+
+ /**
+ * Optional flag indicating if this item is picked initially.
+ */
+ @Pure
+ public boolean isPicked() {
+ return picked;
+ }
+
+ /**
+ * Optional flag indicating if this item is picked initially.
+ */
+ public void setPicked(boolean picked) {
+ this.picked = picked;
+ }
+
+ /**
+ * Optional user data.
+ */
+ @Pure
+ public Object getUserData() {
+ return userData;
+ }
+
+ /**
+ * Optional user data.
+ */
+ public void setUserData(final Object userData) {
+ this.userData = userData;
+ }
+
+ @Override
+ @Pure
+ public String toString() {
+ ToStringBuilder b = new ToStringBuilder(this);
+ b.add("label", label);
+ b.add("description", description);
+ b.add("detail", detail);
+ b.add("picked", picked);
+ b.add("userData", userData);
+ return b.toString();
+ }
+
+ @Override
+ @Pure
+ public int hashCode() {
+ int hash = 7;
+ hash = 83 * hash + Objects.hashCode(this.label);
+ hash = 83 * hash + Objects.hashCode(this.description);
+ hash = 83 * hash + Objects.hashCode(this.detail);
+ hash = 83 * hash + (this.picked ? 1 : 0);
+ hash = 83 * hash + Objects.hashCode(this.userData);
+ return hash;
+ }
+
+ @Override
+ @Pure
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final QuickPickItem other = (QuickPickItem) obj;
+ if (this.picked != other.picked) {
+ return false;
+ }
+ if (!Objects.equals(this.label, other.label)) {
+ return false;
+ }
+ if (!Objects.equals(this.description, other.description)) {
+ return false;
+ }
+ if (!Objects.equals(this.detail, other.detail)) {
+ return false;
+ }
+ if (!Objects.equals(this.userData, other.userData)) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java
index 08ac47f..c73e500 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java
@@ -32,6 +32,8 @@ import java.util.concurrent.Future;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.Logger;
+import org.eclipse.lsp4j.CodeActionKind;
+import org.eclipse.lsp4j.CodeActionOptions;
import org.eclipse.lsp4j.CompletionOptions;
import org.eclipse.lsp4j.ExecuteCommandOptions;
import org.eclipse.lsp4j.InitializeParams;
@@ -248,12 +250,16 @@ public final class Server {
completionOptions.setResolveProvider(true);
completionOptions.setTriggerCharacters(Collections.singletonList("."));
capabilities.setCompletionProvider(completionOptions);
- capabilities.setCodeActionProvider(true);
+ capabilities.setCodeActionProvider(new CodeActionOptions(Arrays.asList(CodeActionKind.QuickFix, CodeActionKind.Source)));
capabilities.setDocumentSymbolProvider(true);
capabilities.setDefinitionProvider(true);
capabilities.setDocumentHighlightProvider(true);
capabilities.setReferencesProvider(true);
- capabilities.setExecuteCommandProvider(new ExecuteCommandOptions(Arrays.asList(JAVA_BUILD_WORKSPACE, GRAALVM_PAUSE_SCRIPT, GENERATE_GETTERS, GENERATE_SETTERS, GENERATE_GETTERS_SETTERS)));
+ List<String> commands = new ArrayList<>(Arrays.asList(JAVA_BUILD_WORKSPACE, GRAALVM_PAUSE_SCRIPT));
+ for (CodeGenerator codeGenerator : Lookup.getDefault().lookupAll(CodeGenerator.class)) {
+ commands.addAll(codeGenerator.getCommands());
+ }
+ capabilities.setExecuteCommandProvider(new ExecuteCommandOptions(commands));
capabilities.setWorkspaceSymbolProvider(true);
}
return new InitializeResult(capabilities);
@@ -332,12 +338,9 @@ public final class Server {
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 NbCodeLanguageClient STUB_CLIENT = new NbCodeLanguageClient() {
private final NbCodeClientCapabilities caps = new NbCodeClientCapabilities();
@@ -352,6 +355,18 @@ public final class Server {
}
@Override
+ public CompletableFuture<List<QuickPickItem>> showQuickPick(ShowQuickPickParams params) {
+ logWarning(params);
+ return CompletableFuture.completedFuture(params.getCanPickMany() || params.getItems().isEmpty() ? params.getItems() : Collections.singletonList(params.getItems().get(0)));
+ }
+
+ @Override
+ public CompletableFuture<String> showInputBox(ShowInputBoxParams params) {
+ logWarning(params);
+ return CompletableFuture.completedFuture(params.getValue());
+ }
+
+ @Override
public NbCodeClientCapabilities getNbCodeCapabilities() {
logWarning();
return caps;
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ShowInputBoxParams.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ShowInputBoxParams.java
new file mode 100644
index 0000000..a262919
--- /dev/null
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ShowInputBoxParams.java
@@ -0,0 +1,124 @@
+/*
+ * 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 java.util.Objects;
+import org.eclipse.lsp4j.jsonrpc.validation.NonNull;
+import org.eclipse.lsp4j.util.Preconditions;
+import org.eclipse.xtext.xbase.lib.Pure;
+import org.eclipse.xtext.xbase.lib.util.ToStringBuilder;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+@SuppressWarnings("all")
+public class ShowInputBoxParams {
+
+ /**
+ * The text to display underneath the input box.
+ */
+ @NonNull
+ private String prompt;
+
+ /**
+ * The value to prefill in the input box.
+ */
+ @NonNull
+ private String value;
+
+ public ShowInputBoxParams() {
+ this("", "");
+ }
+
+ public ShowInputBoxParams(@NonNull final String prompt, @NonNull final String value) {
+ this.prompt = Preconditions.checkNotNull(prompt, "prompt");
+ this.value = Preconditions.checkNotNull(value, "value");
+ }
+
+ /**
+ * The text to display underneath the input box.
+ */
+ @Pure
+ @NonNull
+ public String getPrompt() {
+ return prompt;
+ }
+
+ /**
+ * The text to display underneath the input box.
+ */
+ public void setPrompt(@NonNull final String prompt) {
+ this.prompt = prompt;
+ }
+
+ /**
+ * The value to prefill in the input box.
+ */
+ @Pure
+ @NonNull
+ public String getValue() {
+ return value;
+ }
+
+ /**
+ * The value to prefill in the input box.
+ */
+ public void setValue(@NonNull final String value) {
+ this.value = value;
+ }
+
+ @Override
+ @Pure
+ public String toString() {
+ ToStringBuilder b = new ToStringBuilder(this);
+ b.add("prompt", prompt);
+ b.add("value", value);
+ return b.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 5;
+ hash = 59 * hash + Objects.hashCode(this.prompt);
+ hash = 59 * hash + Objects.hashCode(this.value);
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final ShowInputBoxParams other = (ShowInputBoxParams) obj;
+ if (!Objects.equals(this.prompt, other.prompt)) {
+ return false;
+ }
+ if (!Objects.equals(this.value, other.value)) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ShowQuickPickParams.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ShowQuickPickParams.java
new file mode 100644
index 0000000..107084f
--- /dev/null
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ShowQuickPickParams.java
@@ -0,0 +1,159 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import org.eclipse.lsp4j.jsonrpc.validation.NonNull;
+import org.eclipse.lsp4j.util.Preconditions;
+import org.eclipse.xtext.xbase.lib.Pure;
+import org.eclipse.xtext.xbase.lib.util.ToStringBuilder;
+
+/**
+ * A selection list parameters.
+ *
+ * @author Dusan Balek
+ */
+@SuppressWarnings("all")
+public class ShowQuickPickParams {
+
+ /**
+ * A string to show as placeholder in the input box to guide the user what to pick on.
+ */
+ @NonNull
+ private String placeHolder;
+
+ /**
+ * An optional flag to make the picker accept multiple selections.
+ */
+ private boolean canPickMany;
+
+ /**
+ * A list of items.
+ */
+ @NonNull
+ private List<QuickPickItem> items;
+
+ public ShowQuickPickParams() {
+ this("", new ArrayList<>());
+ }
+
+ public ShowQuickPickParams(@NonNull final String placeHolder, @NonNull final List<QuickPickItem> items) {
+ this.placeHolder = Preconditions.checkNotNull(placeHolder, "placeHolder");
+ this.items = Preconditions.checkNotNull(items, "items");
+ }
+
+ public ShowQuickPickParams(final String placeHolder, final boolean canPickMany, @NonNull final List<QuickPickItem> items) {
+ this(placeHolder, items);
+ this.canPickMany = canPickMany;
+ }
+
+ /**
+ * A string to show as placeholder in the input box to guide the user what to pick on.
+ */
+ @Pure
+ @NonNull
+ public String getPlaceHolder() {
+ return placeHolder;
+ }
+
+ /**
+ * A string to show as placeholder in the input box to guide the user what to pick on.
+ */
+ public void setPlaceHolder(@NonNull final String placeHolder) {
+ this.placeHolder = placeHolder;
+ }
+
+ /**
+ * An optional flag to make the picker accept multiple selections.
+ */
+ @Pure
+ public boolean getCanPickMany() {
+ return canPickMany;
+ }
+
+ /**
+ * An optional flag to make the picker accept multiple selections.
+ */
+ public void setCanPickMany(final boolean canPickMany) {
+ this.canPickMany = canPickMany;
+ }
+
+ /**
+ * A list of items.
+ */
+ @Pure
+ @NonNull
+ public List<QuickPickItem> getItems() {
+ return items;
+ }
+
+ /**
+ * A list of items.
+ */
+ public void setItems(@NonNull final List<QuickPickItem> items) {
+ this.items = items;
+ }
+
+ @Override
+ @Pure
+ public String toString() {
+ ToStringBuilder b = new ToStringBuilder(this);
+ b.add("placeHolder", placeHolder);
+ b.add("canPickMany", canPickMany);
+ b.add("items", items);
+ return b.toString();
+ }
+
+ @Override
+ @Pure
+ public int hashCode() {
+ int hash = 7;
+ hash = 29 * hash + Objects.hashCode(this.placeHolder);
+ hash = 29 * hash + (this.canPickMany ? 1 : 0);
+ hash = 29 * hash + Objects.hashCode(this.items);
+ return hash;
+ }
+
+ @Override
+ @Pure
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final ShowQuickPickParams other = (ShowQuickPickParams) obj;
+ if (this.canPickMany != other.canPickMany) {
+ return false;
+ }
+ if (!Objects.equals(this.placeHolder, other.placeHolder)) {
+ return false;
+ }
+ if (!Objects.equals(this.items, other.items)) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java
index 287585b..9054896 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java
@@ -30,16 +30,8 @@ import com.sun.source.tree.Tree.Kind;
import com.sun.source.tree.VariableTree;
import com.sun.source.util.TreePath;
import com.vladsch.flexmark.html2md.converter.FlexmarkHtmlConverter;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.UncheckedIOException;
import java.net.MalformedURLException;
-import java.net.URI;
-import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -51,7 +43,6 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Properties;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
@@ -179,14 +170,10 @@ import org.netbeans.spi.editor.hints.LazyFixList;
import org.netbeans.spi.java.hints.JavaFix;
import org.openide.cookies.EditorCookie;
import org.openide.filesystems.FileObject;
-import org.openide.filesystems.FileUtil;
-import org.openide.filesystems.URLMapper;
-import org.openide.modules.Places;
import org.openide.text.NbDocument;
import org.openide.text.PositionBounds;
import org.openide.util.Exceptions;
-import org.openide.util.NbBundle.Messages;
-import org.openide.util.Pair;
+import org.openide.util.Lookup;
import org.openide.util.RequestProcessor;
import org.openide.util.lookup.Lookups;
@@ -960,11 +947,6 @@ public class TextDocumentServiceImpl implements TextDocumentService, LanguageCli
}
@Override
- @Messages({
- "DN_GenerateGetters=Generate getter(s).",
- "DN_GenerateSetters=Generate setter(s).",
- "DN_GenerateGettersSetters=Generate getter(s) and setter(s).",
- })
public CompletableFuture<List<Either<Command, CodeAction>>> codeAction(CodeActionParams params) {
Document doc = openedDocuments.get(params.getTextDocument().getUri());
if (doc == null) {
@@ -1063,20 +1045,10 @@ public class TextDocumentServiceImpl implements TextDocumentService, LanguageCli
try {
js.runUserActionTask(cc -> {
cc.toPhase(JavaSource.Phase.RESOLVED);
-
- Pair<Set<VariableElement>, Set<VariableElement>> pair = GetterSetterGenerator.findMissingGettersSetters(cc, params.getRange(), false);
- boolean missingGetters = !pair.first().isEmpty();
- boolean missingSetters = !pair.second().isEmpty();
- String uri = toUri(cc.getFileObject());
-
- if (missingGetters) {
- result.add(Either.forRight(createCodeGeneratorAction(Bundle.DN_GenerateGetters(), Server.GENERATE_GETTERS, uri, params.getRange())));
- }
- if (missingSetters) {
- result.add(Either.forRight(createCodeGeneratorAction(Bundle.DN_GenerateSetters(), Server.GENERATE_SETTERS, uri, params.getRange())));
- }
- if (missingGetters && missingSetters) {
- result.add(Either.forRight(createCodeGeneratorAction(Bundle.DN_GenerateGettersSetters(), Server.GENERATE_GETTERS_SETTERS, uri, params.getRange())));
+ for (CodeGenerator codeGenerator : Lookup.getDefault().lookupAll(CodeGenerator.class)) {
+ for (CodeAction codeAction : codeGenerator.getCodeActions(cc, params)) {
+ result.add(Either.forRight(codeAction));
+ }
}
}, true);
} catch (IOException ex) {
@@ -1087,17 +1059,6 @@ public class TextDocumentServiceImpl implements TextDocumentService, LanguageCli
return CompletableFuture.completedFuture(result);
}
- private CodeAction createCodeGeneratorAction(String name, String command, String uri, Range range) {
- CodeAction action = new CodeAction(name);
- List<Object> arguments = new ArrayList<>();
-
- arguments.add(uri);
- arguments.add(range);
- action.setCommand(new Command(name, command, arguments));
- return action;
- }
-
-
//TODO: copied from spi.editor.hints/.../FixData:
private List<Fix> sortFixes(Collection<Fix> fixes) {
List<Fix> result = new ArrayList<Fix>(fixes);
@@ -1348,88 +1309,6 @@ public class TextDocumentServiceImpl implements TextDocumentService, LanguageCli
return LineDocumentUtils.getLineStartFromIndex((LineDocument) doc, pos.getLine()) + pos.getCharacter();
}
- private static String toUri(FileObject file) {
- if (FileUtil.isArchiveArtifact(file)) {
- //VS code cannot open jar:file: URLs, workaround:
- //another workaround, should be:
- //File cacheDir = Places.getCacheSubfile("java-server");
- //but that locks up VS Code, using a temp directory:
- File cacheDir;
- try {
- cacheDir = Files.createTempDirectory("nbcode").toFile();
- } catch (IOException ex) {
- throw new UncheckedIOException(ex);
- }
- File segments = new File(cacheDir, "segments");
- Properties props = new Properties();
-
- try (InputStream in = new FileInputStream(segments)) {
- props.load(in);
- } catch (IOException ex) {
- //OK, may not exist yet
- }
- FileObject archive = FileUtil.getArchiveFile(file);
- String archiveString = archive.toURL().toString();
- File foundSegment = null;
- for (String segment : props.stringPropertyNames()) {
- if (archiveString.equals(props.getProperty(segment))) {
- foundSegment = new File(cacheDir, segment);
- break;
- }
- }
- if (foundSegment == null) {
- int i = 0;
- while (props.getProperty("s" + i) != null)
- i++;
- foundSegment = new File(cacheDir, "s" + i);
- props.put("s" + i, archiveString);
- try (OutputStream in = new FileOutputStream(segments)) {
- props.store(in, "");
- } catch (IOException ex) {
- Exceptions.printStackTrace(ex);
- }
- }
- File cache = new File(foundSegment, FileUtil.getRelativePath(FileUtil.getArchiveRoot(archive), file));
- cache.getParentFile().mkdirs();
- try (OutputStream out = new FileOutputStream(cache)) {
- out.write(file.asBytes());
- return cache.toURI().toString();
- } catch (IOException ex) {
- Exceptions.printStackTrace(ex);
- }
- }
- return file.toURI().toString();
- }
-
- //TODO: move to a separate Utils class:
- public static FileObject fromUri(String uri) throws MalformedURLException {
- File cacheDir = Places.getCacheSubfile("java-server");
- URI uriUri = URI.create(uri);
- URI relative = cacheDir.toURI().relativize(uriUri);
- if (relative != null && new File(cacheDir, relative.toString()).canRead()) {
- String segmentAndPath = relative.toString();
- int slash = segmentAndPath.indexOf('/');
- String segment = segmentAndPath.substring(0, slash);
- String path = segmentAndPath.substring(slash + 1);
- File segments = new File(cacheDir, "segments");
- Properties props = new Properties();
-
- try (InputStream in = new FileInputStream(segments)) {
- props.load(in);
- String archiveUri = props.getProperty(segment);
- FileObject archive = URLMapper.findFileObject(URI.create(archiveUri).toURL());
- archive = archive != null ? FileUtil.getArchiveRoot(archive) : null;
- FileObject file = archive != null ? archive.getFileObject(path) : null;
- if (file != null) {
- return file;
- }
- } catch (IOException ex) {
- Exceptions.printStackTrace(ex);
- }
- }
- return URLMapper.findFileObject(URI.create(uri).toURL());
- }
-
public static List<TextEdit> modify2TextEdits(JavaSource js, Task<WorkingCopy> task) throws IOException {
FileObject[] file = new FileObject[1];
LineMap[] lm = new LineMap[1];
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ToStringGenerator.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ToStringGenerator.java
new file mode 100644
index 0000000..846bd3f
--- /dev/null
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ToStringGenerator.java
@@ -0,0 +1,165 @@
+/*
+ * 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.MethodTree;
+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.Element;
+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 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;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+@ServiceProvider(service = CodeGenerator.class, position = 50)
+public final class ToStringGenerator extends CodeGenerator {
+
+ public static final String GENERATE_TO_STRING = "java.generate.toString";
+
+ private final Set<String> commands = Collections.singleton(GENERATE_TO_STRING);
+ private final Gson gson = new Gson();
+
+ public ToStringGenerator() {
+ }
+
+ @Override
+ @NbBundle.Messages({
+ "DN_GenerateToString=Generate toString()...",
+ })
+ 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 type = (TypeElement) info.getTrees().getElement(tp);
+ if (type == null || !type.getKind().isClass()) {
+ return Collections.emptyList();
+ }
+ List<QuickPickItem> fields = new ArrayList<>();
+ for (Element element : type.getEnclosedElements()) {
+ switch (element.getKind()) {
+ case METHOD:
+ if (element.getSimpleName().contentEquals("toString") && ((ExecutableElement) element).getParameters().isEmpty()) { //NOI18N
+ return Collections.emptyList();
+ }
+ break;
+ case FIELD:
+ if (!ERROR.contentEquals(element.getSimpleName()) && !element.getModifiers().contains(Modifier.STATIC)) {
+ QuickPickItem item = new QuickPickItem(createLabel(info, (VariableElement)element));
+ item.setUserData(new ElementData(element));
+ fields.add(item);
+ }
+ break;
+ }
+ }
+ String uri = Utils.toUri(info.getFileObject());
+ return Collections.singletonList(createCodeAction(Bundle.DN_GenerateToString(), CODE_GENERATOR_KIND, GENERATE_TO_STRING, uri, offset, fields));
+ }
+
+ @Override
+ public Set<String> getCommands() {
+ return commands;
+ }
+
+ @Override
+ @NbBundle.Messages({
+ "DN_SelectToString=Select fields to be included in toString()",
+ })
+ 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> fields = Arrays.asList(gson.fromJson(gson.toJson(arguments.get(2)), QuickPickItem[].class));
+ if (fields.isEmpty()) {
+ generate(client, uri, offset, fields);
+ } else {
+ client.showQuickPick(new ShowQuickPickParams(Bundle.DN_SelectToString(), true, fields)).thenAccept(selected -> {
+ if (selected != null) {
+ generate(client, uri, offset, selected);
+ }
+ });
+ }
+ } else {
+ client.logMessage(new MessageParams(MessageType.Error, String.format("Illegal number of arguments received for command: %s", command)));
+ }
+ return CompletableFuture.completedFuture(true);
+ }
+
+ private void generate(NbCodeLanguageClient client, String uri, int offset, List<QuickPickItem> fields) {
+ try {
+ FileObject file = Utils.fromUri(uri);
+ JavaSource js = JavaSource.forFileObject(file);
+ if (js == null) {
+ throw new IOException("Cannot get JavaSource for: " + uri);
+ }
+ 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) {
+ ClassTree cls = (ClassTree) tp.getLeaf();
+ List<VariableElement> selectedFields = fields.stream().map(item -> {
+ ElementData data = gson.fromJson(gson.toJson(item.getUserData()), ElementData.class);
+ return (VariableElement)data.resolve(wc);
+ }).collect(Collectors.toList());
+ MethodTree method = org.netbeans.modules.java.editor.codegen.ToStringGenerator.createToStringMethod(wc, selectedFields, cls.getSimpleName().toString(), true);
+ wc.rewrite(cls, GeneratorUtilities.get(wc).insertClassMember(cls, method));
+ }
+ });
+ client.applyEdit(new ApplyWorkspaceEditParams(new WorkspaceEdit(Collections.singletonMap(uri, edits))));
+ } catch (IOException | IllegalArgumentException ex) {
+ client.logMessage(new MessageParams(MessageType.Error, ex.getLocalizedMessage()));
+ }
+ }
+}
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java
index 41e481c..e542b39 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java
@@ -18,7 +18,6 @@
*/
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;
@@ -36,9 +35,6 @@ import org.eclipse.lsp4j.DidChangeConfigurationParams;
import org.eclipse.lsp4j.DidChangeWatchedFilesParams;
import org.eclipse.lsp4j.ExecuteCommandParams;
import org.eclipse.lsp4j.Location;
-import org.eclipse.lsp4j.MessageParams;
-import org.eclipse.lsp4j.MessageType;
-import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.SymbolInformation;
import org.eclipse.lsp4j.WorkspaceSymbolParams;
import org.eclipse.lsp4j.services.LanguageClient;
@@ -56,7 +52,6 @@ import org.netbeans.api.java.source.JavaSource.Phase;
import org.netbeans.api.java.source.SourceUtils;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.ui.OpenProjects;
-import org.netbeans.modules.java.lsp.server.protocol.GetterSetterGenerator.GenKind;
import org.netbeans.modules.java.lsp.server.Utils;
import org.netbeans.modules.java.source.ui.JavaSymbolProvider;
import org.netbeans.modules.java.source.ui.JavaSymbolProvider.ResultHandler;
@@ -81,7 +76,6 @@ public final class WorkspaceServiceImpl implements WorkspaceService, LanguageCli
private static final RequestProcessor WORKER = new RequestProcessor(WorkspaceServiceImpl.class.getName(), 1, false, false);
private NbCodeLanguageClient client;
- private final Gson gson = new Gson();
public WorkspaceServiceImpl() {
}
@@ -133,29 +127,14 @@ public final class WorkspaceServiceImpl implements WorkspaceService, LanguageCli
progressOfCompilation.checkStatus();
return compileFinished;
}
- case Server.GENERATE_GETTERS:
- case Server.GENERATE_SETTERS:
- case Server.GENERATE_GETTERS_SETTERS:
- if (params.getArguments().size() >= 2) {
- String uri = gson.fromJson(gson.toJson(params.getArguments().get(0)), String.class);
- Range sel = gson.fromJson(gson.toJson(params.getArguments().get(1)), Range.class);
- boolean all = params.getArguments().size() == 3;
- try {
- GenKind kind;
- switch (command) {
- case Server.GENERATE_GETTERS: kind = GenKind.GETTERS; break;
- case Server.GENERATE_SETTERS: kind = GenKind.SETTERS; break;
- default: kind = GenKind.GETTERS_SETTERS; break;
- }
- GetterSetterGenerator.generateGettersSetters(client, uri, kind, sel, all);
- } catch (IOException ex) {
- client.logMessage(new MessageParams(MessageType.Error, ex.getLocalizedMessage()));
+ default:
+ for (CodeGenerator codeGenerator : Lookup.getDefault().lookupAll(CodeGenerator.class)) {
+ if (codeGenerator.getCommands().contains(command)) {
+ return codeGenerator.processCommand(client, command, params.getArguments());
}
}
- return CompletableFuture.completedFuture(true);
- default:
- throw new UnsupportedOperationException("Command not supported: " + params.getCommand());
}
+ throw new UnsupportedOperationException("Command not supported: " + params.getCommand());
}
@Override
diff --git a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java
index 29e4d05..8e463ba 100644
--- a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java
+++ b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java
@@ -18,7 +18,6 @@
*/
package org.netbeans.modules.java.lsp.server.protocol;
-import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.io.File;
import java.io.FileWriter;
@@ -45,13 +44,13 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
-import java.util.stream.Stream;
import javax.swing.text.Document;
import javax.swing.text.StyledDocument;
import org.eclipse.lsp4j.ApplyWorkspaceEditParams;
import org.eclipse.lsp4j.ApplyWorkspaceEditResponse;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.CodeActionContext;
+import org.eclipse.lsp4j.CodeActionKind;
import org.eclipse.lsp4j.CodeActionParams;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.CompletionItem;
@@ -1136,10 +1135,20 @@ public class ServerTest extends NbTestCase {
}
@Override
+ public CompletableFuture<List<QuickPickItem>> showQuickPick(ShowQuickPickParams params) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public CompletableFuture<String> showInputBox(ShowInputBoxParams params) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
public NbCodeClientCapabilities getNbCodeCapabilities() {
throw new UnsupportedOperationException("Not supported yet.");
}
-
+
@Override
public void telemetryEvent(Object arg0) {
throw new UnsupportedOperationException("Not supported yet.");
@@ -1360,7 +1369,7 @@ public class ServerTest extends NbTestCase {
actual);
}
- public void testCodeActionGetterSetting() throws Exception {
+ public void testCodeActionGetterSetter() throws Exception {
File src = new File(getWorkDir(), "Test.java");
src.getParentFile().mkdirs();
String code = "public class Test {\n" +
@@ -1407,26 +1416,26 @@ public class ServerTest extends NbTestCase {
}, client.getInputStream(), client.getOutputStream());
serverLauncher.startListening();
LanguageServer server = serverLauncher.getRemoteProxy();
- InitializeResult result = server.initialize(new InitializeParams()).get();
+ server.initialize(new InitializeParams()).get();
String uri = src.toURI().toString();
server.getTextDocumentService().didOpen(new DidOpenTextDocumentParams(new TextDocumentItem(uri, "java", 0, code)));
VersionedTextDocumentIdentifier id = new VersionedTextDocumentIdentifier(src.toURI().toString(), 1);
- List<Either<Command, CodeAction>> codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(id, new Range(new Position(2, 6), new Position(3, 7)), new CodeActionContext(Arrays.asList()))).get();
+ List<Either<Command, CodeAction>> codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(id, new Range(new Position(2, 6), new Position(2, 6)), new CodeActionContext(Arrays.asList()))).get();
assertEquals(3, codeActions.size());
- Optional<CodeAction> generateGettersSetters =
+ Optional<CodeAction> generateGetterSetter =
codeActions.stream()
.filter(Either::isRight)
.map(Either::getRight)
- .filter(a -> Bundle.DN_GenerateGettersSetters().equals(a.getTitle()))
+ .filter(a -> Bundle.DN_GenerateGetterSetterFor("f2").equals(a.getTitle()))
.findAny();
- assertTrue(generateGettersSetters.isPresent());
- server.getWorkspaceService().executeCommand(new ExecuteCommandParams(generateGettersSetters.get().getCommand().getCommand(), generateGettersSetters.get().getCommand().getArguments())).get();
+ assertTrue(generateGetterSetter.isPresent());
+ server.getWorkspaceService().executeCommand(new ExecuteCommandParams(generateGetterSetter.get().getCommand().getCommand(), generateGetterSetter.get().getCommand().getArguments())).get();
assertEquals(1, edit[0].getChanges().size());
List<TextEdit> fileChanges = edit[0].getChanges().get(uri);
assertNotNull(fileChanges);
assertEquals(1, fileChanges.size());
- assertEquals(new Range(new Position(3, 0),
- new Position(3, 0)),
+ assertEquals(new Range(new Position(6, 0),
+ new Position(6, 0)),
fileChanges.get(0).getRange());
assertEquals("\n" +
" public String getF2() {\n" +
@@ -1435,18 +1444,734 @@ public class ServerTest extends NbTestCase {
"\n" +
" public void setF2(String f2) {\n" +
" this.f2 = f2;\n" +
+ " }\n",
+ fileChanges.get(0).getNewText());
+ }
+
+ public void testSourceActionGetterSetter() throws Exception {
+ File src = new File(getWorkDir(), "Test.java");
+ src.getParentFile().mkdirs();
+ String code = "public class Test {\n" +
+ " private final String f1;\n" +
+ " private String f2;\n" +
+ "}\n";
+ try (Writer w = new FileWriter(src)) {
+ w.write(code);
+ }
+ WorkspaceEdit[] edit = new WorkspaceEdit[1];
+ Launcher<LanguageServer> serverLauncher = LSPLauncher.createClientLauncher(new LanguageClient() {
+ @Override
+ public void telemetryEvent(Object arg0) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public void publishDiagnostics(PublishDiagnosticsParams params) {
+ }
+
+ @Override
+ public void showMessage(MessageParams arg0) {
+ }
+
+ @Override
+ public CompletableFuture<MessageActionItem> showMessageRequest(ShowMessageRequestParams arg0) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public void logMessage(MessageParams arg0) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public CompletableFuture<ApplyWorkspaceEditResponse> applyEdit(ApplyWorkspaceEditParams params) {
+ edit[0] = params.getEdit();
+ return CompletableFuture.completedFuture(new ApplyWorkspaceEditResponse(false));
+ }
+
+ }, client.getInputStream(), client.getOutputStream());
+ serverLauncher.startListening();
+ LanguageServer server = serverLauncher.getRemoteProxy();
+ server.initialize(new InitializeParams()).get();
+ String uri = src.toURI().toString();
+ server.getTextDocumentService().didOpen(new DidOpenTextDocumentParams(new TextDocumentItem(uri, "java", 0, code)));
+ VersionedTextDocumentIdentifier id = new VersionedTextDocumentIdentifier(src.toURI().toString(), 1);
+ List<Either<Command, CodeAction>> codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(id, new Range(new Position(3, 0), new Position(3, 0)), new CodeActionContext(Arrays.asList(), Arrays.asList(CodeActionKind.Source)))).get();
+ assertEquals(9, codeActions.size());
+ Optional<CodeAction> generateGetterSetter =
+ codeActions.stream()
+ .filter(Either::isRight)
+ .map(Either::getRight)
+ .filter(a -> Bundle.DN_GenerateGetterSetterFor("f2").equals(a.getTitle()))
+ .findAny();
+ assertTrue(generateGetterSetter.isPresent());
+ server.getWorkspaceService().executeCommand(new ExecuteCommandParams(generateGetterSetter.get().getCommand().getCommand(), generateGetterSetter.get().getCommand().getArguments())).get();
+ assertEquals(1, edit[0].getChanges().size());
+ List<TextEdit> fileChanges = edit[0].getChanges().get(uri);
+ assertNotNull(fileChanges);
+ assertEquals(1, fileChanges.size());
+ assertEquals(new Range(new Position(3, 0),
+ new Position(3, 0)),
+ fileChanges.get(0).getRange());
+ assertEquals("\n" +
+ " public String getF2() {\n" +
+ " return f2;\n" +
" }\n" +
"\n" +
- " public String getF3() {\n" +
- " return f3;\n" +
+ " public void setF2(String f2) {\n" +
+ " this.f2 = f2;\n" +
+ " }\n",
+ fileChanges.get(0).getNewText());
+ server.getTextDocumentService().didChange(new DidChangeTextDocumentParams(id, Arrays.asList(new TextDocumentContentChangeEvent(fileChanges.get(0).getRange(), 0, fileChanges.get(0).getNewText()))));
+ codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(id, new Range(new Position(3, 0), new Position(3, 0)), new CodeActionContext(Arrays.asList(), Arrays.asList(CodeActionKind.Source)))).get();
+ assertEquals(7, codeActions.size());
+ Optional<CodeAction> generateGetter =
+ codeActions.stream()
+ .filter(Either::isRight)
+ .map(Either::getRight)
+ .filter(a -> Bundle.DN_GenerateGetterFor("f1").equals(a.getTitle()))
+ .findAny();
+ assertTrue(generateGetter.isPresent());
+ server.getWorkspaceService().executeCommand(new ExecuteCommandParams(generateGetter.get().getCommand().getCommand(), generateGetter.get().getCommand().getArguments())).get();
+ assertEquals(1, edit[0].getChanges().size());
+ fileChanges = edit[0].getChanges().get(uri);
+ assertNotNull(fileChanges);
+ assertEquals(1, fileChanges.size());
+ assertEquals(new Range(new Position(11, 0),
+ new Position(11, 0)),
+ fileChanges.get(0).getRange());
+ assertEquals("\n" +
+ " public String getF1() {\n" +
+ " return f1;\n" +
+ " }\n",
+ fileChanges.get(0).getNewText());
+ }
+
+ public void testSourceActionConstructor() throws Exception {
+ File src = new File(getWorkDir(), "Test.java");
+ src.getParentFile().mkdirs();
+ String code = "public class Test extends Exception {\n" +
+ " private final String f1;\n" +
+ "}\n";
+ try (Writer w = new FileWriter(src)) {
+ w.write(code);
+ }
+ WorkspaceEdit[] edit = new WorkspaceEdit[1];
+ Launcher<LanguageServer> serverLauncher = LSPLauncher.createClientLauncher(new NbCodeLanguageClient() {
+ @Override
+ public void telemetryEvent(Object arg0) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public void publishDiagnostics(PublishDiagnosticsParams params) {
+ }
+
+ @Override
+ public void showMessage(MessageParams arg0) {
+ }
+
+ @Override
+ public CompletableFuture<MessageActionItem> showMessageRequest(ShowMessageRequestParams arg0) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public void logMessage(MessageParams arg0) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public CompletableFuture<ApplyWorkspaceEditResponse> applyEdit(ApplyWorkspaceEditParams params) {
+ edit[0] = params.getEdit();
+ return CompletableFuture.completedFuture(new ApplyWorkspaceEditResponse(false));
+ }
+
+ @Override
+ public void showStatusBarMessage(ShowStatusMessageParams params) {
+ throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+ }
+
+ @Override
+ public CompletableFuture<List<QuickPickItem>> showQuickPick(ShowQuickPickParams params) {
+ return CompletableFuture.completedFuture(params.getItems().size() > 2 ? params.getItems().subList(0, 2) : params.getItems());
+ }
+
+ @Override
+ public CompletableFuture<String> showInputBox(ShowInputBoxParams params) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public NbCodeClientCapabilities getNbCodeCapabilities() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ }, client.getInputStream(), client.getOutputStream());
+ serverLauncher.startListening();
+ LanguageServer server = serverLauncher.getRemoteProxy();
+ server.initialize(new InitializeParams()).get();
+ String uri = src.toURI().toString();
+ server.getTextDocumentService().didOpen(new DidOpenTextDocumentParams(new TextDocumentItem(uri, "java", 0, code)));
+ VersionedTextDocumentIdentifier id = new VersionedTextDocumentIdentifier(src.toURI().toString(), 1);
+ List<Either<Command, CodeAction>> codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(id, new Range(new Position(2, 0), new Position(2, 0)), new CodeActionContext(Arrays.asList(), Arrays.asList(CodeActionKind.Source)))).get();
+ assertEquals(7, codeActions.size());
+ Optional<CodeAction> generateConstructor =
+ codeActions.stream()
+ .filter(Either::isRight)
+ .map(Either::getRight)
+ .filter(a -> Bundle.DN_GenerateConstructor().equals(a.getTitle()))
+ .findAny();
+ assertTrue(generateConstructor.isPresent());
+ server.getWorkspaceService().executeCommand(new ExecuteCommandParams(generateConstructor.get().getCommand().getCommand(), generateConstructor.get().getCommand().getArguments())).get();
+ int cnt = 0;
+ while(edit[0] == null && cnt++ < 10) {
+ Thread.sleep(1000);
+ }
+ assertEquals(1, edit[0].getChanges().size());
+ List<TextEdit> fileChanges = edit[0].getChanges().get(uri);
+ assertNotNull(fileChanges);
+ assertEquals(1, fileChanges.size());
+ assertEquals(new Range(new Position(2, 0),
+ new Position(2, 0)),
+ fileChanges.get(0).getRange());
+ assertEquals("\n" +
+ " public Test(String f1) {\n" +
+ " this.f1 = f1;\n" +
" }\n" +
"\n" +
- " public void setF3(String f3) {\n" +
- " this.f3 = f3;\n" +
+ " public Test(String f1, String string) {\n" +
+ " super(string);\n" +
+ " this.f1 = f1;\n" +
+ " }\n",
+ fileChanges.get(0).getNewText());
+ }
+
+ public void testSourceActionEqualsHashCode() throws Exception {
+ File src = new File(getWorkDir(), "Test.java");
+ src.getParentFile().mkdirs();
+ String code = "import java.util.Objects;\n" +
+ "\n" +
+ "public class Test {\n" +
+ " private final String f1;\n" +
+ " private java.util.List<String> f2;\n" +
+ "\n" +
+ " @Override\n" +
+ " public int hashCode() {\n" +
+ " int hash = 3;\n" +
+ " hash = 71 * hash + Objects.hashCode(this.f1);\n" +
+ " hash = 71 * hash + Objects.hashCode(this.f2);\n" +
+ " return hash;\n" +
+ " }\n" +
+ "}\n";
+ try (Writer w = new FileWriter(src)) {
+ w.write(code);
+ }
+ WorkspaceEdit[] edit = new WorkspaceEdit[1];
+ Launcher<LanguageServer> serverLauncher = LSPLauncher.createClientLauncher(new NbCodeLanguageClient() {
+ @Override
+ public void telemetryEvent(Object arg0) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public void publishDiagnostics(PublishDiagnosticsParams params) {
+ }
+
+ @Override
+ public void showMessage(MessageParams arg0) {
+ }
+
+ @Override
+ public CompletableFuture<MessageActionItem> showMessageRequest(ShowMessageRequestParams arg0) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public void logMessage(MessageParams arg0) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public CompletableFuture<ApplyWorkspaceEditResponse> applyEdit(ApplyWorkspaceEditParams params) {
+ edit[0] = params.getEdit();
+ return CompletableFuture.completedFuture(new ApplyWorkspaceEditResponse(false));
+ }
+
+ @Override
+ public void showStatusBarMessage(ShowStatusMessageParams params) {
+ throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+ }
+
+ @Override
+ public CompletableFuture<List<QuickPickItem>> showQuickPick(ShowQuickPickParams params) {
+ return CompletableFuture.completedFuture(params.getItems().size() > 2 ? params.getItems().subList(0, 2) : params.getItems());
+ }
+
+ @Override
+ public CompletableFuture<String> showInputBox(ShowInputBoxParams params) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public NbCodeClientCapabilities getNbCodeCapabilities() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ }, client.getInputStream(), client.getOutputStream());
+ serverLauncher.startListening();
+ LanguageServer server = serverLauncher.getRemoteProxy();
+ server.initialize(new InitializeParams()).get();
+ String uri = src.toURI().toString();
+ server.getTextDocumentService().didOpen(new DidOpenTextDocumentParams(new TextDocumentItem(uri, "java", 0, code)));
+ VersionedTextDocumentIdentifier id = new VersionedTextDocumentIdentifier(src.toURI().toString(), 1);
+ List<Either<Command, CodeAction>> codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(id, new Range(new Position(5, 0), new Position(5, 0)), new CodeActionContext(Arrays.asList(), Arrays.asList(CodeActionKind.Source)))).get();
+ assertEquals(9, codeActions.size());
+ Optional<CodeAction> generateEquals =
+ codeActions.stream()
+ .filter(Either::isRight)
+ .map(Either::getRight)
+ .filter(a -> Bundle.DN_GenerateEquals().equals(a.getTitle()))
+ .findAny();
+ assertTrue(generateEquals.isPresent());
+ server.getWorkspaceService().executeCommand(new ExecuteCommandParams(generateEquals.get().getCommand().getCommand(), generateEquals.get().getCommand().getArguments())).get();
+ int cnt = 0;
+ while(edit[0] == null && cnt++ < 10) {
+ Thread.sleep(1000);
+ }
+ assertEquals(1, edit[0].getChanges().size());
+ List<TextEdit> fileChanges = edit[0].getChanges().get(uri);
+ assertNotNull(fileChanges);
+ assertEquals(1, fileChanges.size());
+ assertEquals(new Range(new Position(13, 0),
+ new Position(13, 0)),
+ fileChanges.get(0).getRange());
+ assertEquals("\n" +
+ " @Override\n" +
+ " public boolean equals(Object obj) {\n" +
+ " if (this == obj) {\n" +
+ " return true;\n" +
+ " }\n" +
+ " if (obj == null) {\n" +
+ " return false;\n" +
+ " }\n" +
+ " if (getClass() != obj.getClass()) {\n" +
+ " return false;\n" +
+ " }\n" +
+ " final Test other = (Test) obj;\n" +
+ " if (!Objects.equals(this.f1, other.f1)) {\n" +
+ " return false;\n" +
+ " }\n" +
+ " if (!Objects.equals(this.f2, other.f2)) {\n" +
+ " return false;\n" +
+ " }\n" +
+ " return true;\n" +
" }\n",
fileChanges.get(0).getNewText());
}
+ public void testSourceActionToString() throws Exception {
+ File src = new File(getWorkDir(), "Test.java");
+ src.getParentFile().mkdirs();
+ String code = "public class Test {\n" +
+ " private final String f1;\n" +
+ "}\n";
+ try (Writer w = new FileWriter(src)) {
+ w.write(code);
+ }
+ WorkspaceEdit[] edit = new WorkspaceEdit[1];
+ Launcher<LanguageServer> serverLauncher = LSPLauncher.createClientLauncher(new NbCodeLanguageClient() {
+ @Override
+ public void telemetryEvent(Object arg0) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public void publishDiagnostics(PublishDiagnosticsParams params) {
+ }
+
+ @Override
+ public void showMessage(MessageParams arg0) {
+ }
+
+ @Override
+ public CompletableFuture<MessageActionItem> showMessageRequest(ShowMessageRequestParams arg0) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public void logMessage(MessageParams arg0) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public CompletableFuture<ApplyWorkspaceEditResponse> applyEdit(ApplyWorkspaceEditParams params) {
+ edit[0] = params.getEdit();
+ return CompletableFuture.completedFuture(new ApplyWorkspaceEditResponse(false));
+ }
+
+ @Override
+ public void showStatusBarMessage(ShowStatusMessageParams params) {
+ throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+ }
+
+ @Override
+ public CompletableFuture<List<QuickPickItem>> showQuickPick(ShowQuickPickParams params) {
+ return CompletableFuture.completedFuture(params.getItems().size() > 2 ? params.getItems().subList(0, 2) : params.getItems());
+ }
+
+ @Override
+ public CompletableFuture<String> showInputBox(ShowInputBoxParams params) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public NbCodeClientCapabilities getNbCodeCapabilities() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ }, client.getInputStream(), client.getOutputStream());
+ serverLauncher.startListening();
+ LanguageServer server = serverLauncher.getRemoteProxy();
+ server.initialize(new InitializeParams()).get();
+ String uri = src.toURI().toString();
+ server.getTextDocumentService().didOpen(new DidOpenTextDocumentParams(new TextDocumentItem(uri, "java", 0, code)));
+ VersionedTextDocumentIdentifier id = new VersionedTextDocumentIdentifier(src.toURI().toString(), 1);
+ List<Either<Command, CodeAction>> codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(id, new Range(new Position(2, 0), new Position(2, 0)), new CodeActionContext(Arrays.asList(), Arrays.asList(CodeActionKind.Source)))).get();
+ assertEquals(7, codeActions.size());
+ Optional<CodeAction> generateToString =
+ codeActions.stream()
+ .filter(Either::isRight)
+ .map(Either::getRight)
+ .filter(a -> Bundle.DN_GenerateToString().equals(a.getTitle()))
+ .findAny();
+ assertTrue(generateToString.isPresent());
+ server.getWorkspaceService().executeCommand(new ExecuteCommandParams(generateToString.get().getCommand().getCommand(), generateToString.get().getCommand().getArguments())).get();
+ int cnt = 0;
+ while(edit[0] == null && cnt++ < 10) {
+ Thread.sleep(1000);
+ }
+ assertEquals(1, edit[0].getChanges().size());
+ List<TextEdit> fileChanges = edit[0].getChanges().get(uri);
+ assertNotNull(fileChanges);
+ assertEquals(1, fileChanges.size());
+ assertEquals(new Range(new Position(2, 0),
+ new Position(2, 0)),
+ fileChanges.get(0).getRange());
+ assertEquals("\n" +
+ " @Override\n" +
+ " public String toString() {\n" +
+ " StringBuilder sb = new StringBuilder();\n" +
+ " sb.append(\"Test{f1=\").append(f1);\n" +
+ " sb.append('}');\n" +
+ " return sb.toString();\n" +
+ " }\n",
+ fileChanges.get(0).getNewText());
+ }
+
+ public void testSourceActionDelegateMethod() throws Exception {
+ File src = new File(getWorkDir(), "Test.java");
+ src.getParentFile().mkdirs();
+ String code = "public class Test {\n" +
+ " private final String f1;\n" +
+ "}\n";
+ try (Writer w = new FileWriter(src)) {
+ w.write(code);
+ }
+ WorkspaceEdit[] edit = new WorkspaceEdit[1];
+ Launcher<LanguageServer> serverLauncher = LSPLauncher.createClientLauncher(new NbCodeLanguageClient() {
+ @Override
+ public void telemetryEvent(Object arg0) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public void publishDiagnostics(PublishDiagnosticsParams params) {
+ }
+
+ @Override
+ public void showMessage(MessageParams arg0) {
+ }
+
+ @Override
+ public CompletableFuture<MessageActionItem> showMessageRequest(ShowMessageRequestParams arg0) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public void logMessage(MessageParams arg0) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public CompletableFuture<ApplyWorkspaceEditResponse> applyEdit(ApplyWorkspaceEditParams params) {
+ edit[0] = params.getEdit();
+ return CompletableFuture.completedFuture(new ApplyWorkspaceEditResponse(false));
+ }
+
+ @Override
+ public void showStatusBarMessage(ShowStatusMessageParams params) {
+ throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+ }
+
+ @Override
+ public CompletableFuture<List<QuickPickItem>> showQuickPick(ShowQuickPickParams params) {
+ return CompletableFuture.completedFuture(params.getItems().size() > 2 ? params.getItems().subList(0, 2) : params.getItems());
+ }
+
+ @Override
+ public CompletableFuture<String> showInputBox(ShowInputBoxParams params) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public NbCodeClientCapabilities getNbCodeCapabilities() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ }, client.getInputStream(), client.getOutputStream());
+ serverLauncher.startListening();
+ LanguageServer server = serverLauncher.getRemoteProxy();
+ server.initialize(new InitializeParams()).get();
+ String uri = src.toURI().toString();
+ server.getTextDocumentService().didOpen(new DidOpenTextDocumentParams(new TextDocumentItem(uri, "java", 0, code)));
+ VersionedTextDocumentIdentifier id = new VersionedTextDocumentIdentifier(src.toURI().toString(), 1);
+ List<Either<Command, CodeAction>> codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(id, new Range(new Position(2, 0), new Position(2, 0)), new CodeActionContext(Arrays.asList(), Arrays.asList(CodeActionKind.Source)))).get();
+ assertEquals(7, codeActions.size());
+ Optional<CodeAction> generateDelegateMethod =
+ codeActions.stream()
+ .filter(Either::isRight)
+ .map(Either::getRight)
+ .filter(a -> Bundle.DN_GenerateDelegateMethod().equals(a.getTitle()))
+ .findAny();
+ assertTrue(generateDelegateMethod.isPresent());
+ server.getWorkspaceService().executeCommand(new ExecuteCommandParams(generateDelegateMethod.get().getCommand().getCommand(), generateDelegateMethod.get().getCommand().getArguments())).get();
+ int cnt = 0;
+ while(edit[0] == null && cnt++ < 10) {
+ Thread.sleep(1000);
+ }
+ assertEquals(1, edit[0].getChanges().size());
+ List<TextEdit> fileChanges = edit[0].getChanges().get(uri);
+ assertNotNull(fileChanges);
+ assertEquals(2, fileChanges.size());
+ assertEquals(new Range(new Position(0, 0),
+ new Position(0, 0)),
+ fileChanges.get(0).getRange());
+ assertEquals("\nimport java.util.stream.IntStream;\n\n",
+ fileChanges.get(0).getNewText());
+ assertEquals(new Range(new Position(2, 0),
+ new Position(2, 0)),
+ fileChanges.get(1).getRange());
+ assertEquals("\n" +
+ " public IntStream chars() {\n" +
+ " return f1.chars();\n" +
+ " }\n" +
+ "\n" +
+ " public IntStream codePoints() {\n" +
+ " return f1.codePoints();\n" +
+ " }\n",
+ fileChanges.get(1).getNewText());
+ }
+
+ public void testSourceActionOverrideMethod() throws Exception {
+ File src = new File(getWorkDir(), "Test.java");
+ src.getParentFile().mkdirs();
+ String code = "public class Test {\n" +
+ " private final String f1;\n" +
+ "}\n";
+ try (Writer w = new FileWriter(src)) {
+ w.write(code);
+ }
+ WorkspaceEdit[] edit = new WorkspaceEdit[1];
+ Launcher<LanguageServer> serverLauncher = LSPLauncher.createClientLauncher(new NbCodeLanguageClient() {
+ @Override
+ public void telemetryEvent(Object arg0) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public void publishDiagnostics(PublishDiagnosticsParams params) {
+ }
+
+ @Override
+ public void showMessage(MessageParams arg0) {
+ }
+
+ @Override
+ public CompletableFuture<MessageActionItem> showMessageRequest(ShowMessageRequestParams arg0) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public void logMessage(MessageParams arg0) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public CompletableFuture<ApplyWorkspaceEditResponse> applyEdit(ApplyWorkspaceEditParams params) {
+ edit[0] = params.getEdit();
+ return CompletableFuture.completedFuture(new ApplyWorkspaceEditResponse(false));
+ }
+
+ @Override
+ public void showStatusBarMessage(ShowStatusMessageParams params) {
+ throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+ }
+
+ @Override
+ public CompletableFuture<List<QuickPickItem>> showQuickPick(ShowQuickPickParams params) {
+ return CompletableFuture.completedFuture(params.getItems().size() > 2 ? params.getItems().subList(0, 2) : params.getItems());
+ }
+
+ @Override
+ public CompletableFuture<String> showInputBox(ShowInputBoxParams params) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public NbCodeClientCapabilities getNbCodeCapabilities() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ }, client.getInputStream(), client.getOutputStream());
+ serverLauncher.startListening();
+ LanguageServer server = serverLauncher.getRemoteProxy();
+ server.initialize(new InitializeParams()).get();
+ String uri = src.toURI().toString();
+ server.getTextDocumentService().didOpen(new DidOpenTextDocumentParams(new TextDocumentItem(uri, "java", 0, code)));
+ VersionedTextDocumentIdentifier id = new VersionedTextDocumentIdentifier(src.toURI().toString(), 1);
+ List<Either<Command, CodeAction>> codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(id, new Range(new Position(2, 0), new Position(2, 0)), new CodeActionContext(Arrays.asList(), Arrays.asList(CodeActionKind.Source)))).get();
+ assertEquals(7, codeActions.size());
+ Optional<CodeAction> generateOverrideMethod =
+ codeActions.stream()
+ .filter(Either::isRight)
+ .map(Either::getRight)
+ .filter(a -> Bundle.DN_GenerateOverrideMethod().equals(a.getTitle()))
+ .findAny();
+ assertTrue(generateOverrideMethod.isPresent());
+ server.getWorkspaceService().executeCommand(new ExecuteCommandParams(generateOverrideMethod.get().getCommand().getCommand(), generateOverrideMethod.get().getCommand().getArguments())).get();
+ int cnt = 0;
+ while(edit[0] == null && cnt++ < 10) {
+ Thread.sleep(1000);
+ }
+ assertEquals(1, edit[0].getChanges().size());
+ List<TextEdit> fileChanges = edit[0].getChanges().get(uri);
+ assertNotNull(fileChanges);
+ assertEquals(1, fileChanges.size());
+ assertEquals(new Range(new Position(2, 0),
+ new Position(2, 0)),
+ fileChanges.get(0).getRange());
+ assertEquals("\n" +
+ " @Override\n" +
+ " protected void finalize() throws Throwable {\n" +
+ " super.finalize(); //To change body of generated methods, choose Tools | Templates.\n" +
+ " }\n" +
+ "\n" +
+ " @Override\n" +
+ " public String toString() {\n" +
+ " return super.toString(); //To change body of generated methods, choose Tools | Templates.\n" +
+ " }\n",
+ fileChanges.get(0).getNewText());
+ }
+
+ public void testSourceActionLogger() throws Exception {
+ File src = new File(getWorkDir(), "Test.java");
+ src.getParentFile().mkdirs();
+ String code = "public class Test {\n" +
+ " private final String f1;\n" +
+ "}\n";
+ try (Writer w = new FileWriter(src)) {
+ w.write(code);
+ }
+ WorkspaceEdit[] edit = new WorkspaceEdit[1];
+ Launcher<LanguageServer> serverLauncher = LSPLauncher.createClientLauncher(new NbCodeLanguageClient() {
+ @Override
+ public void telemetryEvent(Object arg0) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public void publishDiagnostics(PublishDiagnosticsParams params) {
+ }
+
+ @Override
+ public void showMessage(MessageParams arg0) {
+ }
+
+ @Override
+ public CompletableFuture<MessageActionItem> showMessageRequest(ShowMessageRequestParams arg0) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public void logMessage(MessageParams arg0) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public CompletableFuture<ApplyWorkspaceEditResponse> applyEdit(ApplyWorkspaceEditParams params) {
+ edit[0] = params.getEdit();
+ return CompletableFuture.completedFuture(new ApplyWorkspaceEditResponse(false));
+ }
+
+ @Override
+ public void showStatusBarMessage(ShowStatusMessageParams params) {
+ throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+ }
+
+ @Override
+ public CompletableFuture<List<QuickPickItem>> showQuickPick(ShowQuickPickParams params) {
+ return CompletableFuture.completedFuture(params.getItems().size() > 2 ? params.getItems().subList(0, 2) : params.getItems());
+ }
+
+ @Override
+ public CompletableFuture<String> showInputBox(ShowInputBoxParams params) {
+ return CompletableFuture.completedFuture("LOGGER");
+ }
+
+ @Override
+ public NbCodeClientCapabilities getNbCodeCapabilities() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ }, client.getInputStream(), client.getOutputStream());
+ serverLauncher.startListening();
+ LanguageServer server = serverLauncher.getRemoteProxy();
+ server.initialize(new InitializeParams()).get();
+ String uri = src.toURI().toString();
+ server.getTextDocumentService().didOpen(new DidOpenTextDocumentParams(new TextDocumentItem(uri, "java", 0, code)));
+ VersionedTextDocumentIdentifier id = new VersionedTextDocumentIdentifier(src.toURI().toString(), 1);
+ List<Either<Command, CodeAction>> codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(id, new Range(new Position(2, 0), new Position(2, 0)), new CodeActionContext(Arrays.asList(), Arrays.asList(CodeActionKind.Source)))).get();
+ assertEquals(7, codeActions.size());
+ Optional<CodeAction> generateLogger =
+ codeActions.stream()
+ .filter(Either::isRight)
+ .map(Either::getRight)
+ .filter(a -> Bundle.DN_GenerateLogger().equals(a.getTitle()))
+ .findAny();
+ assertTrue(generateLogger.isPresent());
+ server.getWorkspaceService().executeCommand(new ExecuteCommandParams(generateLogger.get().getCommand().getCommand(), generateLogger.get().getCommand().getArguments())).get();
+ int cnt = 0;
+ while(edit[0] == null && cnt++ < 10) {
+ Thread.sleep(1000);
+ }
+ assertEquals(1, edit[0].getChanges().size());
+ List<TextEdit> fileChanges = edit[0].getChanges().get(uri);
+ assertNotNull(fileChanges);
+ assertEquals(2, fileChanges.size());
+ assertEquals(new Range(new Position(0, 0),
+ new Position(0, 0)),
+ fileChanges.get(0).getRange());
+ assertEquals("\nimport java.util.logging.Logger;\n\n",
+ fileChanges.get(0).getNewText());
+ assertEquals(new Range(new Position(1, 0),
+ new Position(1, 0)),
+ fileChanges.get(1).getRange());
+ assertEquals("\n" +
+ " private static final Logger LOGGER = Logger.getLogger(Test.class.getName());\n",
+ fileChanges.get(1).getNewText());
+ }
+
private String toString(Location location) {
String path = location.getUri();
String simpleName = path.substring(path.lastIndexOf('/') + 1);
diff --git a/java/java.lsp.server/vscode/package-lock.json b/java/java.lsp.server/vscode/package-lock.json
index 0e3253f..3ed2b2e 100644
--- a/java/java.lsp.server/vscode/package-lock.json
+++ b/java/java.lsp.server/vscode/package-lock.json
@@ -810,6 +810,11 @@
"glob": "^7.1.3"
}
},
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
+ },
"set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
@@ -884,18 +889,18 @@
"dev": true
},
"vscode-debugadapter": {
- "version": "1.33.0",
- "resolved": "https://registry.npmjs.org/vscode-debugadapter/-/vscode-debugadapter-1.33.0.tgz",
- "integrity": "sha512-GsSD6PyVokjyW+7LU3FRXbu8SINv9muQtMQEDvwB6SIGQfrQzld8kECpQRr71j3mgXW+sTY+44/XMzueeaU6/Q==",
+ "version": "1.42.1",
+ "resolved": "https://registry.npmjs.org/vscode-debugadapter/-/vscode-debugadapter-1.42.1.tgz",
+ "integrity": "sha512-bICDB8mxReU2kGL13ftjNqeInYtVN3zpRdZKjRZHCpXC/mofrdaj9KRlJsALwcUNivMA9S/LwTZ2n+ros3X0jg==",
"requires": {
- "mkdirp": "^0.5.1",
- "vscode-debugprotocol": "1.33.0"
+ "mkdirp": "^0.5.5",
+ "vscode-debugprotocol": "1.42.0"
}
},
"vscode-debugprotocol": {
- "version": "1.33.0",
- "resolved": "https://registry.npmjs.org/vscode-debugprotocol/-/vscode-debugprotocol-1.33.0.tgz",
- "integrity": "sha512-d+l4lrEz6OP2kmGpweqe37x9H7icAMV8S4m8azTWGAIlNJxBP4rlSTnZa7NMLcbgqWkWG9lTGY7fJ+rSPaW7yg=="
+ "version": "1.42.0",
+ "resolved": "https://registry.npmjs.org/vscode-debugprotocol/-/vscode-debugprotocol-1.42.0.tgz",
+ "integrity": "sha512-nVsfVCat9FZlOso5SYB1LQQiFGifTyOALpkpJdudDlRXGTpI3mSFiDYXWaoFm7UcfqTOzn1SC7Hqw4d89btT0w=="
},
"vscode-jsonrpc": {
"version": "5.0.1",
@@ -903,11 +908,12 @@
"integrity": "sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A=="
},
"vscode-languageclient": {
- "version": "4.4.2",
- "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-4.4.2.tgz",
- "integrity": "sha512-9TUzsg1UM6n1UEyPlWbDf7tK1wJAK7UGFRmGDN8sz4KmbbDiVRh6YicaB/5oRSVTpuV47PdJpYlOl3SJ0RiK1Q==",
+ "version": "6.1.3",
+ "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-6.1.3.tgz",
+ "integrity": "sha512-YciJxk08iU5LmWu7j5dUt9/1OLjokKET6rME3cI4BRpiF6HZlusm2ZwPt0MYJ0lV5y43sZsQHhyon2xBg4ZJVA==",
"requires": {
- "vscode-languageserver-protocol": "^3.10.3"
+ "semver": "^6.3.0",
+ "vscode-languageserver-protocol": "^3.15.3"
}
},
"vscode-languageserver-protocol": {
diff --git a/java/java.lsp.server/vscode/package.json b/java/java.lsp.server/vscode/package.json
index 8753f2b..be8ee42 100644
--- a/java/java.lsp.server/vscode/package.json
+++ b/java/java.lsp.server/vscode/package.json
@@ -141,21 +141,6 @@
"category": "Java"
},
{
- "command": "java.generate.getters.menu",
- "title": "Generate all getters",
- "category": "Java"
- },
- {
- "command": "java.generate.setters.menu",
- "title": "Generate all setters",
- "category": "Java"
- },
- {
- "command": "java.generate.getters.setters.menu",
- "title": "Generate all getters and setters",
- "category": "Java"
- },
- {
"command": "graalvm.pause.script",
"title": "Pause in Script",
"category": "GraalVM"
@@ -178,37 +163,8 @@
"command": "graalvm.pause.script",
"when": "nbJavaLSReady"
}
- ],
- "editor/context": [
- {
- "submenu": "java.generate",
- "group": "1_modification"
- }
- ],
- "java.generate": [
- {
- "command": "java.generate.getters.menu",
- "when": "nbJavaLSReady",
- "group": "1_getters_setters"
- },
- {
- "command": "java.generate.setters.menu",
- "when": "nbJavaLSReady",
- "group": "1_getters_setters"
- },
- {
- "command": "java.generate.getters.setters.menu",
- "when": "nbJavaLSReady",
- "group": "1_getters_setters"
- }
]
- },
- "submenus": [
- {
- "id": "java.generate",
- "label": "Generate"
- }
- ]
+ }
},
"scripts": {
"vscode:prepublish": "npm run compile",
@@ -229,7 +185,7 @@
"vscode-test": "^1.3.0"
},
"dependencies": {
- "vscode-languageclient": "4.4.2",
- "vscode-debugadapter": "1.33.0"
+ "vscode-languageclient": "6.1.3",
+ "vscode-debugadapter": "1.42.1"
}
}
diff --git a/java/java.lsp.server/vscode/src/extension.ts b/java/java.lsp.server/vscode/src/extension.ts
index e2fb699..d0394e3 100644
--- a/java/java.lsp.server/vscode/src/extension.ts
+++ b/java/java.lsp.server/vscode/src/extension.ts
@@ -38,7 +38,7 @@ import * as path from 'path';
import { ChildProcess } from 'child_process';
import * as vscode from 'vscode';
import * as launcher from './nbcode';
-import { StatusMessageRequest, ShowStatusMessageParams } from './protocol';
+import { StatusMessageRequest, ShowStatusMessageParams, QuickPickRequest, InputBoxRequest } from './protocol';
const API_VERSION : string = "1.0";
let client: Promise<LanguageClient>;
@@ -194,30 +194,6 @@ export function activate(context: ExtensionContext): VSNetBeansAPI {
});
});
}));
- for (let generator of ['java.generate.getters', 'java.generate.setters', 'java.generate.getters.setters']) {
- context.subscriptions.push(commands.registerCommand(generator + '.menu', () => {
- return window.withProgress({ location: ProgressLocation.Window }, p => {
- return new Promise(async (resolve, reject) => {
- const commands = await vscode.commands.getCommands();
- const editor = vscode.window.activeTextEditor;
- if (commands.includes(generator + '.menu') && editor != null) {
- const uri = editor.document.uri;
- const res = await vscode.commands.executeCommand(generator,
- uri.scheme + "://" + uri.path,
- editor.selection,
- "all");
- if (res) {
- resolve();
- } else {
- reject();
- }
- } else {
- reject();
- }
- });
- });
- }));
- }
return Object.freeze({
version : API_VERSION
});
@@ -442,6 +418,13 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex
commands.executeCommand('setContext', 'nbJavaLSReady', true);
c.onNotification(StatusMessageRequest.type, showStatusBarMessage);
c.onNotification(LogMessageNotification.type, (param) => handleLog(log, param.message));
+ c.onRequest(QuickPickRequest.type, async param => {
+ const selected = await window.showQuickPick(param.items, { placeHolder: param.placeHolder, canPickMany: param.canPickMany });
+ return selected ? Array.isArray(selected) ? selected : [selected] : undefined;
+ });
+ c.onRequest(InputBoxRequest.type, async param => {
+ return await window.showInputBox({ prompt: param.prompt, value: param.value });
+ });
handleLog(log, 'Language Client: Ready');
setClient[0](c);
}).catch(setClient[1]);
diff --git a/java/java.lsp.server/vscode/src/protocol.ts b/java/java.lsp.server/vscode/src/protocol.ts
index 4b485ba..6f56c6d 100644
--- a/java/java.lsp.server/vscode/src/protocol.ts
+++ b/java/java.lsp.server/vscode/src/protocol.ts
@@ -18,8 +18,10 @@
*/
'use strict';
+import {QuickPickItem} from 'vscode';
import {
NotificationType,
+ RequestType,
ShowMessageParams
} from 'vscode-languageclient';
@@ -33,3 +35,37 @@ export interface ShowStatusMessageParams extends ShowMessageParams {
export namespace StatusMessageRequest {
export const type = new NotificationType<ShowStatusMessageParams, void>('window/showStatusBarMessage');
};
+
+export interface ShowQuickPickParams {
+ /**
+ * A string to show as placeholder in the input box to guide the user what to pick on.
+ */
+ placeHolder: string;
+ /**
+ * An optional flag to make the picker accept multiple selections.
+ */
+ canPickMany?: boolean;
+ /**
+ * A list of items.
+ */
+ items: QuickPickItem[];
+}
+
+export namespace QuickPickRequest {
+ export const type = new RequestType<ShowQuickPickParams, QuickPickItem[], void, void>('window/showQuickPick');
+}
+
+export interface ShowInputBoxParams {
+ /**
+ * The text to display underneath the input box.
+ */
+ prompt: string;
+ /**
+ * The value to prefill in the input box.
+ */
+ value: string;
+}
+
+export namespace InputBoxRequest {
+ export const type = new RequestType<ShowInputBoxParams, string | undefined, void, void>('window/showInputBox');
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org
For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists