From a0968f906fc991b5970e5ae2b954f102d6c997fa Mon Sep 17 00:00:00 2001 From: Dusan Balek Date: Wed, 24 Jan 2024 11:58:22 +0100 Subject: [PATCH] LSP: Do not compute text edits in source actions on `resolve` call. --- .../db/MicronautDataEndpointGenerator.java | 171 +++++++++++------- .../modules/java/lsp/server/Utils.java | 2 +- .../server/protocol/CodeActionsProvider.java | 6 +- .../CodeActionsProvider2LspApiBridge.java | 11 +- .../server/protocol/ConstructorGenerator.java | 46 +++-- .../protocol/DelegateMethodGenerator.java | 41 +++-- .../protocol/EqualsHashCodeGenerator.java | 36 ++-- .../protocol/GetterSetterGenerator.java | 50 ++--- .../ImplementOverrideMethodGenerator.java | 35 ++-- .../lsp/server/protocol/LoggerGenerator.java | 28 ++- .../protocol/TextDocumentServiceImpl.java | 34 +--- .../server/protocol/ToStringGenerator.java | 37 ++-- .../java/lsp/server/protocol/ServerTest.java | 101 +++++++++-- java/java.lsp.server/vscode/src/extension.ts | 24 ++- 14 files changed, 371 insertions(+), 251 deletions(-) diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/db/MicronautDataEndpointGenerator.java b/enterprise/micronaut/src/org/netbeans/modules/micronaut/db/MicronautDataEndpointGenerator.java index 93c9a6694307..bad8e1beab84 100644 --- a/enterprise/micronaut/src/org/netbeans/modules/micronaut/db/MicronautDataEndpointGenerator.java +++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/db/MicronautDataEndpointGenerator.java @@ -18,14 +18,21 @@ */ package org.netbeans.modules.micronaut.db; +import com.google.gson.Gson; +import com.google.gson.JsonObject; import com.sun.source.tree.ClassTree; import com.sun.source.tree.Tree; import com.sun.source.util.TreePath; import java.awt.Dialog; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.TypeElement; @@ -33,13 +40,13 @@ 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.swing.JButton; import javax.swing.JComponent; import javax.swing.text.Document; import javax.swing.text.JTextComponent; import org.netbeans.api.editor.mimelookup.MimeRegistration; import org.netbeans.api.java.source.CompilationController; -import org.netbeans.api.java.source.ElementHandle; import org.netbeans.api.java.source.GeneratorUtilities; import org.netbeans.api.java.source.JavaSource; import org.netbeans.api.java.source.ModificationResult; @@ -47,7 +54,7 @@ import org.netbeans.api.java.source.TreeUtilities; import org.netbeans.api.java.source.WorkingCopy; import org.netbeans.api.lsp.CodeAction; -import org.netbeans.api.lsp.LazyCodeAction; +import org.netbeans.api.lsp.Command; import org.netbeans.api.lsp.Range; import org.netbeans.api.lsp.TextDocumentEdit; import org.netbeans.api.lsp.TextEdit; @@ -56,25 +63,39 @@ import org.netbeans.modules.parsing.spi.ParseException; import org.netbeans.spi.editor.codegen.CodeGenerator; import org.netbeans.spi.lsp.CodeActionProvider; +import org.netbeans.spi.lsp.CommandProvider; import org.openide.DialogDescriptor; import org.openide.DialogDisplayer; import org.openide.NotifyDescriptor; import org.openide.filesystems.FileObject; +import org.openide.filesystems.URLMapper; import org.openide.util.Exceptions; import org.openide.util.Lookup; import org.openide.util.NbBundle; +import org.openide.util.RequestProcessor; import org.openide.util.Union2; +import org.openide.util.lookup.ServiceProviders; import org.openide.util.lookup.ServiceProvider; /** * * @author Dusan Balek */ -@ServiceProvider(service = CodeActionProvider.class) -public class MicronautDataEndpointGenerator implements CodeActionProvider { +@ServiceProviders({ + @ServiceProvider(service = CodeActionProvider.class), + @ServiceProvider(service = CommandProvider.class) +}) +public class MicronautDataEndpointGenerator implements CodeActionProvider, CommandProvider { private static final String SOURCE = "source"; private static final String CONTROLLER_ANNOTATION_NAME = "io.micronaut.http.annotation.Controller"; + private static final String GENERATE_DATA_ENDPOINT = "nbls.micronaut.generate.data.endpoint"; + private static final String URI = "uri"; + private static final String OFFSET = "offset"; + private static final String REPOSITORIES = "repositories"; + private static final String ENDPOINTS = "endpoints"; + + private final Gson gson = new Gson(); @Override @NbBundle.Messages({ @@ -126,34 +147,12 @@ public List getCodeActions(Document doc, Range range, Lookup context } }); if (!endpoints.isEmpty()) { - FileObject fo = cc.getFileObject(); - List> repositoryHandles = repositories.stream().map(repository -> ElementHandle.create(repository)).collect(Collectors.toList()); - return Collections.singletonList(new LazyCodeAction(Bundle.DN_GenerateDataEndpoint(), SOURCE, null, () -> { - try { - List items = endpoints.stream().map(endpoint -> new NotifyDescriptor.QuickPick.Item(endpoint, null)).collect(Collectors.toList()); - NotifyDescriptor.QuickPick pick = new NotifyDescriptor.QuickPick(Bundle.DN_GenerateDataEndpoint(), Bundle.DN_SelectEndpoints(), items, true); - if (DialogDescriptor.OK_OPTION != DialogDisplayer.getDefault().notify(pick)) { - return null; - } - List selectedIds = new ArrayList<>(); - for (NotifyDescriptor.QuickPick.Item item : pick.getItems()) { - if (item.isSelected()) { - selectedIds.add(item.getLabel()); - } - } - if (selectedIds.isEmpty()) { - return null; - } - JavaSource js = JavaSource.forFileObject(fo); - if (js == null) { - throw new IOException("Cannot get JavaSource for: " + fo.toURL().toString()); - } - return modify2Edit(js, getTask(offset, repositoryHandles, selectedIds)); - } catch (IOException ex) { - Exceptions.printStackTrace(ex); - } - return null; - })); + Map data = new HashMap<>(); + data.put(URI, cc.getFileObject().toURI().toString()); + data.put(OFFSET, offset); + data.put(REPOSITORIES, repositories.stream().map(repository -> repository.getSimpleName().toString()).collect(Collectors.toList())); + data.put(ENDPOINTS, endpoints); + return Collections.singletonList(new CodeAction(Bundle.DN_GenerateDataEndpoint(), SOURCE, new Command(Bundle.DN_GenerateDataEndpoint(), "nbls.generate.code", Arrays.asList(GENERATE_DATA_ENDPOINT, data)), null)); } } catch (IOException | ParseException ex) { Exceptions.printStackTrace(ex); @@ -161,44 +160,94 @@ public List getCodeActions(Document doc, Range range, Lookup context return Collections.emptyList(); } - private static Task getTask(int offset, List> repositoryHandles, List endpointIds) { + @Override + public Set getCommands() { + return Collections.singleton(GENERATE_DATA_ENDPOINT); + } + + @Override + public CompletableFuture runCommand(String command, List arguments) { + if (arguments.isEmpty()) { + return CompletableFuture.completedFuture(null); + } + JsonObject data = (JsonObject) arguments.get(0); + CompletableFuture future = new CompletableFuture<>(); + RequestProcessor.getDefault().post(() -> { + try { + String uri = data.getAsJsonPrimitive(URI).getAsString(); + FileObject fo = URLMapper.findFileObject(java.net.URI.create(uri).toURL()); + JavaSource js = fo != null ? JavaSource.forFileObject(fo) : null; + if (js == null) { + throw new IOException("Cannot get JavaSource for: " + uri); + } + int offset = data.getAsJsonPrimitive(OFFSET).getAsInt(); + List items = Arrays.asList(gson.fromJson(data.get(ENDPOINTS), String[].class)).stream().map(endpoint -> new NotifyDescriptor.QuickPick.Item(endpoint, null)).collect(Collectors.toList()); + NotifyDescriptor.QuickPick pick = new NotifyDescriptor.QuickPick(Bundle.DN_GenerateDataEndpoint(), Bundle.DN_SelectEndpoints(), items, true); + if (DialogDescriptor.OK_OPTION != DialogDisplayer.getDefault().notify(pick)) { + future.complete(null); + } else { + List selectedIds = new ArrayList<>(); + for (NotifyDescriptor.QuickPick.Item item : pick.getItems()) { + if (item.isSelected()) { + selectedIds.add(item.getLabel()); + } + } + if (selectedIds.isEmpty()) { + future.complete(null); + } else { + List repositoryNames = Arrays.asList(gson.fromJson(data.get(REPOSITORIES), String[].class)); + future.complete(modify2Edit(js, getTask(offset, repositoryNames, selectedIds))); + } + } + } catch (IOException ex) { + future.completeExceptionally(ex); + } + }); + return future; + } + + private static Task getTask(int offset, List repositoryNames, List endpointIds) { return copy -> { copy.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED); TreePath tp = copy.getTreeUtilities().pathFor(offset); tp = copy.getTreeUtilities().getPathElementOfKind(TreeUtilities.CLASS_TREE_KINDS, tp); if (tp != null) { ClassTree clazz = (ClassTree) tp.getLeaf(); - List members = new ArrayList<>(); - for (ElementHandle repositoryHandle : repositoryHandles) { - VariableElement repository = repositoryHandle.resolve(copy); - if (repository != null) { - TypeMirror repositoryType = repository.asType(); - if (repositoryType.getKind() == TypeKind.DECLARED) { - TypeElement repositoryTypeElement = (TypeElement) ((DeclaredType) repositoryType).asElement(); - String id = null; - if (repositoryHandles.size() > 1) { - id = '/' + repositoryTypeElement.getSimpleName().toString().toLowerCase(); - if (id.endsWith("repository")) { - id = id.substring(0, id.length() - 10); - } - } - for (String endpointId : endpointIds) { - String delegateMethodName = null; - if (endpointId.equals(id != null ? id + "/ -- GET" : "/ -- GET")) { - delegateMethodName = "findAll"; - } else if (endpointId.equals(id != null ? id + "/{id} -- GET" : "/{id} -- GET")) { - delegateMethodName = "findById"; - } else if (endpointId.equals(id != null ? id + "/{id} -- DELETE" : "/{id} -- DELETE")) { - delegateMethodName = "deleteById"; + TypeElement te = (TypeElement) copy.getTrees().getElement(tp); + if (te != null) { + List fields = ElementFilter.fieldsIn(te.getEnclosedElements()); + List members = new ArrayList<>(); + for (String repositoryName : repositoryNames) { + VariableElement repository = fields.stream().filter(ve -> repositoryName.contentEquals(ve.getSimpleName())).findFirst().orElse(null); + if (repository != null) { + TypeMirror repositoryType = repository.asType(); + if (repositoryType.getKind() == TypeKind.DECLARED) { + TypeElement repositoryTypeElement = (TypeElement) ((DeclaredType) repositoryType).asElement(); + String id = null; + if (repositoryNames.size() > 1) { + id = '/' + repositoryTypeElement.getSimpleName().toString().toLowerCase(); + if (id.endsWith("repository")) { + id = id.substring(0, id.length() - 10); + } } - if (delegateMethodName != null) { - members.add(Utils.createControllerDataEndpointMethod(copy, repositoryTypeElement, repository.getSimpleName().toString(), delegateMethodName, id)); + for (String endpointId : endpointIds) { + String delegateMethodName = null; + if (endpointId.equals(id != null ? id + "/ -- GET" : "/ -- GET")) { + delegateMethodName = "findAll"; + } else if (endpointId.equals(id != null ? id + "/{id} -- GET" : "/{id} -- GET")) { + delegateMethodName = "findById"; + } else if (endpointId.equals(id != null ? id + "/{id} -- DELETE" : "/{id} -- DELETE")) { + delegateMethodName = "deleteById"; + } + if (delegateMethodName != null) { + members.add(Utils.createControllerDataEndpointMethod(copy, repositoryTypeElement, repository.getSimpleName().toString(), delegateMethodName, id)); + } } } } } + copy.rewrite(clazz, GeneratorUtilities.get(copy).insertClassMembers(clazz, members, offset)); } - copy.rewrite(clazz, GeneratorUtilities.get(copy).insertClassMembers(clazz, members, offset)); } }; } @@ -291,7 +340,7 @@ public List create(Lookup context) { if (!endpoints.isEmpty()) { int offset = comp.getCaretPosition(); FileObject fo = cc.getFileObject(); - List> repositoryHandles = repositories.stream().map(repository -> ElementHandle.create(repository)).collect(Collectors.toList()); + List repositoryNames = repositories.stream().map(repository -> repository.getSimpleName().toString()).collect(Collectors.toList()); ret.add(new CodeGenerator() { @Override public String getDisplayName() { @@ -320,8 +369,8 @@ public void invoke() { if (js == null) { throw new IOException("Cannot get JavaSource for: " + fo.toURL().toString()); } - js.runModificationTask(getTask(offset, repositoryHandles, selectedEndpoints)).commit(); - } catch (Exception ex) { + js.runModificationTask(getTask(offset, repositoryNames, selectedEndpoints)).commit(); + } catch (IOException | IllegalArgumentException ex) { Exceptions.printStackTrace(ex); } } diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/Utils.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/Utils.java index 28b296d59964..d818c9315f71 100644 --- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/Utils.java +++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/Utils.java @@ -549,7 +549,7 @@ public static WorkspaceEdit workspaceEditFromApi(org.netbeans.api.lsp.WorkspaceE String docUri = parts.first().getDocument(); try { FileObject file = Utils.fromUri(docUri); - if (file == null) { + if (file == null && uri != null) { file = Utils.fromUri(uri); } FileObject fo = file; diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeActionsProvider.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeActionsProvider.java index f9a1378e348f..47eccb3455dd 100644 --- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeActionsProvider.java +++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeActionsProvider.java @@ -69,10 +69,14 @@ public CompletableFuture processCommand(NbCodeLanguageClient client, Str } protected CodeAction createCodeAction(NbCodeLanguageClient client, String name, String kind, Object data, String command, Object... commandArgs) { + return createCodeAction(client, name, kind, data, command, Arrays.asList(commandArgs)); + } + + protected CodeAction createCodeAction(NbCodeLanguageClient client, String name, String kind, Object data, String command, List commandArgs) { CodeAction action = new CodeAction(name); action.setKind(kind); if (command != null) { - action.setCommand(new Command(name, Utils.encodeCommand(command, client.getNbCodeCapabilities()), Arrays.asList(commandArgs))); + action.setCommand(new Command(name, Utils.encodeCommand(command, client.getNbCodeCapabilities()), commandArgs)); } if (data != null) { Map map = new HashMap<>(); diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeActionsProvider2LspApiBridge.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeActionsProvider2LspApiBridge.java index a901cf1a5d67..7b7f70e9e272 100644 --- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeActionsProvider2LspApiBridge.java +++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeActionsProvider2LspApiBridge.java @@ -61,7 +61,12 @@ public Set getCommands() { public CompletableFuture processCommand(NbCodeLanguageClient client, String command, List arguments) { for (CommandProvider cmdProvider : Lookup.getDefault().lookupAll(CommandProvider.class)) { if (cmdProvider.getCommands().contains(command)) { - return cmdProvider.runCommand(command, arguments); + return cmdProvider.runCommand(command, arguments).thenApply(ret -> { + if (ret instanceof WorkspaceEdit) { + return Utils.workspaceEditFromApi((WorkspaceEdit) ret, null, client); + } + return ret; + }); } } return CompletableFuture.completedFuture(false); @@ -96,7 +101,7 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat } CodeAction codeAction = createCodeAction(client, ca.getTitle(), ca.getKind(), data, command, command != null ? ca.getCommand().getArguments() : null); if (edit != null) { - codeAction.setEdit(TextDocumentServiceImpl.fromAPI(edit, uri, client)); + codeAction.setEdit(Utils.workspaceEditFromApi(edit, uri, client)); } allActions.add(codeAction); } @@ -115,7 +120,7 @@ public CompletableFuture resolve(NbCodeLanguageClient client, CodeAc if (obj.has(URL) && obj.has(INDEX)) { LazyCodeAction inputAction = lastCodeActions.get(obj.getAsJsonPrimitive(INDEX).getAsInt()); if (inputAction != null) { - codeAction.setEdit(TextDocumentServiceImpl.fromAPI(inputAction.getLazyEdit().get(), obj.getAsJsonPrimitive(URL).getAsString(), client)); + codeAction.setEdit(Utils.workspaceEditFromApi(inputAction.getLazyEdit().get(), obj.getAsJsonPrimitive(URL).getAsString(), client)); } } } 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 index 1d16573dae6a..583f90585472 100644 --- 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 @@ -77,6 +77,7 @@ @ServiceProvider(service = CodeActionsProvider.class, position = 10) public final class ConstructorGenerator extends CodeActionsProvider { + private static final String GENERATE_CONSTRUCTOR = "nbls.java.generate.constructor"; private static final String URI = "uri"; private static final String OFFSET = "offset"; private static final String CONSTRUCTORS = "constructors"; @@ -187,7 +188,12 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat data.put(OFFSET, startOffset); data.put(CONSTRUCTORS, constructors); data.put(FIELDS, fields); - return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateConstructor(), isSource ? CODE_GENERATOR_KIND : CodeActionKind.QuickFix, data, "workbench.action.focusActiveEditorGroup")); + return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateConstructor(), isSource ? CODE_GENERATOR_KIND : CodeActionKind.QuickFix, null, "nbls.generate.code", GENERATE_CONSTRUCTOR, data)); + } + + @Override + public Set getCommands() { + return Collections.singleton(GENERATE_CONSTRUCTOR); } @Override @@ -195,19 +201,19 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat "DN_SelectSuperConstructor=Select super constructor", "DN_SelectConstructorFields=Select fields to be initialized by constructor", }) - public CompletableFuture resolve(NbCodeLanguageClient client, CodeAction codeAction, Object data) { - CompletableFuture future = new CompletableFuture<>(); + public CompletableFuture processCommand(NbCodeLanguageClient client, String command, List arguments) { + if (arguments.isEmpty()) { + return CompletableFuture.completedFuture(null); + } + JsonObject data = (JsonObject) arguments.get(0); + CompletableFuture future = new CompletableFuture<>(); try { - String uri = ((JsonObject) data).getAsJsonPrimitive(URI).getAsString(); - int offset = ((JsonObject) data).getAsJsonPrimitive(OFFSET).getAsInt(); - List constructors = Arrays.asList(gson.fromJson(((JsonObject) data).get(CONSTRUCTORS), QuickPickItem[].class)); - List fields = Arrays.asList(gson.fromJson(((JsonObject) data).get(FIELDS), QuickPickItem[].class)); + String uri = data.getAsJsonPrimitive(URI).getAsString(); + int offset = data.getAsJsonPrimitive(OFFSET).getAsInt(); + List constructors = Arrays.asList(gson.fromJson(data.get(CONSTRUCTORS), QuickPickItem[].class)); + List fields = Arrays.asList(gson.fromJson(data.get(FIELDS), QuickPickItem[].class)); if (constructors.size() < 2 && fields.isEmpty()) { - WorkspaceEdit edit = generate(client, uri, offset, constructors, fields); - if (edit != null) { - codeAction.setEdit(edit); - } - future.complete(codeAction); + future.complete(generate(client, uri, offset, constructors, fields)); } else { InputService.Registry inputServiceRegistry = Lookup.getDefault().lookup(InputService.Registry.class); if (inputServiceRegistry != null) { @@ -238,18 +244,10 @@ public CompletableFuture resolve(NbCodeLanguageClient client, CodeAc client.showMultiStepInput(new ShowMutliStepInputParams(inputId, Bundle.DN_GenerateConstructor())).thenAccept(result -> { Either, String> selectedConstructors = result.get(CONSTRUCTORS); Either, String> selectedFields = result.get(FIELDS); - if (selectedFields != null) { - try { - WorkspaceEdit edit = generate(client, uri, offset, selectedConstructors != null ? selectedConstructors.getLeft() : constructors, selectedFields.getLeft()); - if (edit != null) { - codeAction.setEdit(edit); - } - future.complete(codeAction); - } catch (IOException | IllegalArgumentException ex) { - future.completeExceptionally(ex); - } - } else { - future.complete(codeAction); + try { + future.complete(selectedFields != null ? generate(client, uri, offset, selectedConstructors != null ? selectedConstructors.getLeft() : constructors, selectedFields.getLeft()) : null); + } catch (IOException | IllegalArgumentException ex) { + future.completeExceptionally(ex); } }); } 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 index 6b689ebdeb45..74c4523b85be 100644 --- 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 @@ -31,6 +31,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; import javax.lang.model.element.Element; @@ -73,6 +74,7 @@ @ServiceProvider(service = CodeActionsProvider.class, position = 60) public final class DelegateMethodGenerator extends CodeActionsProvider { + private static final String GENERATE_DELEGATE_METHOD = "nbls.java.generate.delegate"; private static final String URI = "uri"; private static final String OFFSET = "offset"; private static final String TYPE = "type"; @@ -135,7 +137,12 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat data.put(OFFSET, offset); data.put(TYPE, typeItem); data.put(FIELDS, fields); - return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateDelegateMethod(), CODE_GENERATOR_KIND, data, "workbench.action.focusActiveEditorGroup")); + return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateDelegateMethod(), CODE_GENERATOR_KIND, null, "nbls.generate.code", GENERATE_DELEGATE_METHOD, data)); + } + + @Override + public Set getCommands() { + return Collections.singleton(GENERATE_DELEGATE_METHOD); } @Override @@ -143,13 +150,17 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat "DN_SelectDelegateMethodField=Select target field to generate delegates for", "DN_SelectDelegateMethods=Select methods to generate delegates for", }) - public CompletableFuture resolve(NbCodeLanguageClient client, CodeAction codeAction, Object data) { - CompletableFuture future = new CompletableFuture<>(); + public CompletableFuture processCommand(NbCodeLanguageClient client, String command, List arguments) { + if (arguments.isEmpty()) { + return CompletableFuture.completedFuture(null); + } + JsonObject data = (JsonObject) arguments.get(0); + CompletableFuture future = new CompletableFuture<>(); try { - String uri = ((JsonObject) data).getAsJsonPrimitive(URI).getAsString(); - int offset = ((JsonObject) data).getAsJsonPrimitive(OFFSET).getAsInt(); - QuickPickItem type = gson.fromJson(gson.toJson(((JsonObject) data).get(TYPE)), QuickPickItem.class); - List fields = Arrays.asList(gson.fromJson(((JsonObject) data).get(FIELDS), QuickPickItem[].class)); + String uri = data.getAsJsonPrimitive(URI).getAsString(); + int offset = data.getAsJsonPrimitive(OFFSET).getAsInt(); + QuickPickItem type = gson.fromJson(gson.toJson(data.get(TYPE)), QuickPickItem.class); + List fields = Arrays.asList(gson.fromJson(data.get(FIELDS), QuickPickItem[].class)); InputService.Registry inputServiceRegistry = Lookup.getDefault().lookup(InputService.Registry.class); if (inputServiceRegistry != null) { int totalSteps = fields.size() > 1 ? 2 : 1; @@ -220,18 +231,10 @@ public boolean accept(Element e, TypeMirror type) { Either, String> selectedFields = result.get(FIELDS); QuickPickItem selectedField = (selectedFields != null ? selectedFields.getLeft() : fields).get(0); Either, String> selectedMethods = result.get(METHODS); - if (selectedField != null && selectedMethods != null) { - try { - WorkspaceEdit edit = generate(uri, offset, selectedField, selectedMethods.getLeft()); - if (edit != null) { - codeAction.setEdit(edit); - } - future.complete(codeAction); - } catch (IOException | IllegalArgumentException ex) { - future.completeExceptionally(ex); - } - } else { - future.complete(codeAction); + try { + future.complete(selectedField != null && selectedMethods != null ? generate(uri, offset, selectedField, selectedMethods.getLeft()) : null); + } catch (IOException | IllegalArgumentException ex) { + future.completeExceptionally(ex); } }); } 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 index 117c174a7d7e..4e9b4f747a59 100644 --- 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 @@ -30,6 +30,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; import javax.lang.model.element.ElementKind; @@ -60,6 +61,7 @@ @ServiceProvider(service = CodeActionsProvider.class, position = 40) public final class EqualsHashCodeGenerator extends CodeActionsProvider { + private static final String GENERATE_EQUALS_HASHCODE = "nbls.java.generate.equals.hashCode"; private static final String KIND = "kind"; private static final String URI = "uri"; private static final String OFFSET = "offset"; @@ -113,11 +115,16 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat String uri = Utils.toUri(info.getFileObject()); if (equalsHashCode[0] == null) { if (equalsHashCode[1] == null) { - return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateEqualsHashCode(), CODE_GENERATOR_KIND, data(0, uri, offset, fields), "workbench.action.focusActiveEditorGroup")); + return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateEqualsHashCode(), CODE_GENERATOR_KIND, null, "nbls.generate.code", GENERATE_EQUALS_HASHCODE, data(0, uri, offset, fields))); } - return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateEquals(), CODE_GENERATOR_KIND, data(EQUALS_ONLY, uri, offset, fields), "workbench.action.focusActiveEditorGroup")); + return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateEquals(), CODE_GENERATOR_KIND, null, "nbls.generate.code", GENERATE_EQUALS_HASHCODE, data(EQUALS_ONLY, uri, offset, fields))); } - return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateHashCode(), CODE_GENERATOR_KIND, data(HASH_CODE_ONLY, uri, offset, fields), "workbench.action.focusActiveEditorGroup")); + return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateHashCode(), CODE_GENERATOR_KIND, null, "nbls.generate.code", GENERATE_EQUALS_HASHCODE, data(HASH_CODE_ONLY, uri, offset, fields))); + } + + @Override + public Set getCommands() { + return Collections.singleton(GENERATE_EQUALS_HASHCODE); } @Override @@ -126,13 +133,17 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat "DN_SelectHashCode=Select fields to be included in hashCode()", "DN_SelectEqualsHashCode=Select fields to be included in equals() and hashCode()", }) - public CompletableFuture resolve(NbCodeLanguageClient client, CodeAction codeAction, Object data) { - CompletableFuture future = new CompletableFuture<>(); + public CompletableFuture processCommand(NbCodeLanguageClient client, String command, List arguments) { + if (arguments.isEmpty()) { + return CompletableFuture.completedFuture(null); + } + JsonObject data = (JsonObject) arguments.get(0); + CompletableFuture future = new CompletableFuture<>(); try { - int kind = ((JsonObject) data).getAsJsonPrimitive(KIND).getAsInt(); - String uri = ((JsonObject) data).getAsJsonPrimitive(URI).getAsString(); - int offset = ((JsonObject) data).getAsJsonPrimitive(OFFSET).getAsInt(); - List fields = Arrays.asList(gson.fromJson(((JsonObject) data).get(FIELDS), QuickPickItem[].class)); + int kind = data.getAsJsonPrimitive(KIND).getAsInt(); + String uri = data.getAsJsonPrimitive(URI).getAsString(); + int offset = data.getAsJsonPrimitive(OFFSET).getAsInt(); + List fields = Arrays.asList(gson.fromJson(data.get(FIELDS), QuickPickItem[].class)); String title; String text; boolean generateEquals = HASH_CODE_ONLY != kind; @@ -162,11 +173,10 @@ public CompletableFuture resolve(NbCodeLanguageClient client, CodeAc org.netbeans.modules.java.editor.codegen.EqualsHashCodeGenerator.generateEqualsAndHashCode(wc, tp, generateEquals ? selectedFields : null, generateHashCode ? selectedFields : null, -1); } }); - if (!edits.isEmpty()) { - codeAction.setEdit(new WorkspaceEdit(Collections.singletonMap(uri, edits))); - } + future.complete(edits.isEmpty() ? null : new WorkspaceEdit(Collections.singletonMap(uri, edits))); + } else { + future.complete(null); } - future.complete(codeAction); } catch (IOException | IllegalArgumentException ex) { future.completeExceptionally(ex); } 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 84b23ba44447..fb85050e92be 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 @@ -69,6 +69,7 @@ @ServiceProvider(service = CodeActionsProvider.class, position = 30) public final class GetterSetterGenerator extends CodeActionsProvider { + private static final String GENERATE_GETTER_SETTER = "nbls.java.generate.getter.setter"; private static final String KIND = "kind"; private static final String URI = "uri"; private static final String OFFSET = "offset"; @@ -102,46 +103,55 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat List 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(client, name, all ? CODE_GENERATOR_KIND : CodeActionKind.QuickFix, data(GeneratorUtils.GETTERS_ONLY, uri, offset, all, pair.first().stream().map(variableElement -> { + result.add(createCodeAction(client, name, all ? CODE_GENERATOR_KIND : CodeActionKind.QuickFix, null, "nbls.generate.code", GENERATE_GETTER_SETTER, data(GeneratorUtils.GETTERS_ONLY, 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())), all && pair.first().size() > 1 ? "workbench.action.focusActiveEditorGroup" : null)); + }).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(client, name, all ? CODE_GENERATOR_KIND : CodeActionKind.QuickFix, data(GeneratorUtils.SETTERS_ONLY, uri, offset, all, pair.second().stream().map(variableElement -> { + result.add(createCodeAction(client, name, all ? CODE_GENERATOR_KIND : CodeActionKind.QuickFix, null, "nbls.generate.code", GENERATE_GETTER_SETTER, data(GeneratorUtils.SETTERS_ONLY, 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())), all && pair.first().size() > 1 ? "workbench.action.focusActiveEditorGroup" : null)); + }).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(client, name, all ? CODE_GENERATOR_KIND : CodeActionKind.QuickFix, data(0, uri, offset, all, pair.first().stream().map(variableElement -> { + result.add(createCodeAction(client, name, all ? CODE_GENERATOR_KIND : CodeActionKind.QuickFix, null, "nbls.generate.code", GENERATE_GETTER_SETTER, data(0, 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())), all && pair.first().size() > 1 ? "workbench.action.focusActiveEditorGroup" : null)); + }).collect(Collectors.toList())))); } return result; } + @Override + public Set getCommands() { + return Collections.singleton(GENERATE_GETTER_SETTER); + } + @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 resolve(NbCodeLanguageClient client, CodeAction codeAction, Object data) { - CompletableFuture future = new CompletableFuture<>(); + public CompletableFuture processCommand(NbCodeLanguageClient client, String command, List arguments) { + if (arguments.isEmpty()) { + return CompletableFuture.completedFuture(null); + } + JsonObject data = (JsonObject) arguments.get(0); + CompletableFuture future = new CompletableFuture<>(); try { - int kind = ((JsonObject) data).getAsJsonPrimitive(KIND).getAsInt(); - String uri = ((JsonObject) data).getAsJsonPrimitive(URI).getAsString(); - int offset = ((JsonObject) data).getAsJsonPrimitive(OFFSET).getAsInt(); - boolean all = ((JsonObject) data).getAsJsonPrimitive(ALL).getAsBoolean(); - List fields = Arrays.asList(gson.fromJson(((JsonObject) data).get(FIELDS), QuickPickItem[].class)); + int kind = data.getAsJsonPrimitive(KIND).getAsInt(); + String uri = data.getAsJsonPrimitive(URI).getAsString(); + int offset = data.getAsJsonPrimitive(OFFSET).getAsInt(); + boolean all = data.getAsJsonPrimitive(ALL).getAsBoolean(); + List fields = Arrays.asList(gson.fromJson(data.get(FIELDS), QuickPickItem[].class)); String title; String text; switch (kind) { @@ -152,23 +162,13 @@ public CompletableFuture resolve(NbCodeLanguageClient client, CodeAc if (all && fields.size() > 1) { client.showQuickPick(new ShowQuickPickParams(title, text, true, fields)).thenAccept(selected -> { try { - if (selected != null && !selected.isEmpty()) { - WorkspaceEdit edit = generate(kind, uri, offset, selected); - if (edit != null) { - codeAction.setEdit(edit); - } - } - future.complete(codeAction); + future.complete(selected != null && !selected.isEmpty() ? generate(kind, uri, offset, selected) : null); } catch (IOException | IllegalArgumentException ex) { future.completeExceptionally(ex); } }); } else { - WorkspaceEdit edit = generate(kind, uri, offset, fields); - if (edit != null) { - codeAction.setEdit(edit); - } - future.complete(codeAction); + future.complete(generate(kind, uri, offset, fields)); } } catch(JsonSyntaxException | IOException | IllegalArgumentException ex) { future.completeExceptionally(ex); 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 index d3d2c8d43a6b..c839e8e60dde 100644 --- 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 @@ -29,6 +29,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; import javax.lang.model.SourceVersion; @@ -62,6 +63,7 @@ @ServiceProvider(service = CodeActionsProvider.class, position = 70) public final class ImplementOverrideMethodGenerator extends CodeActionsProvider { + private static final String GENERATE_IMPLEMENT_OVERRIDE = "nbls.java.generate.implement.override.method"; private static final String URI = "uri"; private static final String OFFSET = "offset"; private static final String IS_IMPLEMET = "isImplement"; @@ -107,7 +109,7 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat implementMethods.add(new QuickPickItem(createLabel(info, method), enclosingTypeName, null, mustImplement, new ElementData(method))); } if (!implementMethods.isEmpty()) { - result.add(createCodeAction(client, Bundle.DN_GenerateImplementMethod(), CODE_GENERATOR_KIND, data(uri, offset, true, implementMethods), "workbench.action.focusActiveEditorGroup")); + result.add(createCodeAction(client, Bundle.DN_GenerateImplementMethod(), CODE_GENERATOR_KIND, null, "nbls.generate.code", GENERATE_IMPLEMENT_OVERRIDE, data(uri, offset, true, implementMethods))); } } if (typeElement.getKind().isClass() || typeElement.getKind().isInterface()) { @@ -123,35 +125,38 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat overrideMethods.add(item); } if (!overrideMethods.isEmpty()) { - result.add(createCodeAction(client, Bundle.DN_GenerateOverrideMethod(), CODE_GENERATOR_KIND, data (uri, offset, false, overrideMethods), "workbench.action.focusActiveEditorGroup")); + result.add(createCodeAction(client, Bundle.DN_GenerateOverrideMethod(), CODE_GENERATOR_KIND, null, "nbls.generate.code", GENERATE_IMPLEMENT_OVERRIDE, data(uri, offset, false, overrideMethods))); } } return result; } + @Override + public Set getCommands() { + return Collections.singleton(GENERATE_IMPLEMENT_OVERRIDE); + } + @Override @NbBundle.Messages({ "DN_SelectImplementMethod=Select methods to implement", "DN_SelectOverrideMethod=Select methods to override", }) - public CompletableFuture resolve(NbCodeLanguageClient client, CodeAction codeAction, Object data) { - CompletableFuture future = new CompletableFuture<>(); + public CompletableFuture processCommand(NbCodeLanguageClient client, String command, List arguments) { + if (arguments.isEmpty()) { + return CompletableFuture.completedFuture(null); + } + JsonObject data = (JsonObject) arguments.get(0); + CompletableFuture future = new CompletableFuture<>(); try { - String uri = ((JsonObject) data).getAsJsonPrimitive(URI).getAsString(); - int offset = ((JsonObject) data).getAsJsonPrimitive(OFFSET).getAsInt(); - boolean isImplement = ((JsonObject) data).getAsJsonPrimitive(IS_IMPLEMET).getAsBoolean(); - List methods = Arrays.asList(gson.fromJson(((JsonObject) data).get(METHODS), QuickPickItem[].class)); + String uri = data.getAsJsonPrimitive(URI).getAsString(); + int offset = data.getAsJsonPrimitive(OFFSET).getAsInt(); + boolean isImplement = data.getAsJsonPrimitive(IS_IMPLEMET).getAsBoolean(); + List methods = Arrays.asList(gson.fromJson(data.get(METHODS), QuickPickItem[].class)); String title = isImplement ? Bundle.DN_GenerateImplementMethod(): Bundle.DN_GenerateOverrideMethod(); String text = isImplement ? Bundle.DN_SelectImplementMethod() : Bundle.DN_SelectOverrideMethod(); client.showQuickPick(new ShowQuickPickParams(title, text, true, methods)).thenAccept(selected -> { try { - if (selected != null && !selected.isEmpty()) { - WorkspaceEdit edit = generate(uri, offset, isImplement, selected); - if (edit != null) { - codeAction.setEdit(edit); - } - } - future.complete(codeAction); + future.complete(selected != null && !selected.isEmpty() ? generate(uri, offset, isImplement, selected) : null); } catch (IOException | IllegalArgumentException ex) { future.completeExceptionally(ex); } 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 index fbea183809ae..4c92f0975fd2 100644 --- 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 @@ -30,6 +30,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.logging.Logger; import javax.lang.model.element.Modifier; @@ -63,6 +64,7 @@ @ServiceProvider(service = CodeActionsProvider.class, position = 20) public final class LoggerGenerator extends CodeActionsProvider { + private static final String GENERATE_LOGGER = "nbls.java.generate.logger"; private static final String URI = "uri"; private static final String OFFSET = "offset"; @@ -106,18 +108,27 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat Map data = new HashMap<>(); data.put(URI, uri); data.put(OFFSET, offset); - return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateLogger(), CODE_GENERATOR_KIND, data, "workbench.action.focusActiveEditorGroup")); + return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateLogger(), CODE_GENERATOR_KIND, null, "nbls.generate.code", GENERATE_LOGGER, data)); + } + + @Override + public Set getCommands() { + return Collections.singleton(GENERATE_LOGGER); } @Override @NbBundle.Messages({ "DN_SelectLoggerName=Logger field name", }) - public CompletableFuture resolve(NbCodeLanguageClient client, CodeAction codeAction, Object data) { - CompletableFuture future = new CompletableFuture<>(); + public CompletableFuture processCommand(NbCodeLanguageClient client, String command, List arguments) { + if (arguments.isEmpty()) { + return CompletableFuture.completedFuture(null); + } + JsonObject data = (JsonObject) arguments.get(0); + CompletableFuture future = new CompletableFuture<>(); try { - String uri = ((JsonObject) data).getAsJsonPrimitive(URI).getAsString(); - int offset = ((JsonObject) data).getAsJsonPrimitive(OFFSET).getAsInt(); + String uri = data.getAsJsonPrimitive(URI).getAsString(); + int offset = data.getAsJsonPrimitive(OFFSET).getAsInt(); client.showInputBox(new ShowInputBoxParams(Bundle.DN_GenerateLogger(), Bundle.DN_SelectLoggerName(), "LOG", false)).thenAccept(value -> { try { if (value != null && BaseUtilities.isJavaIdentifier(value)) { @@ -136,11 +147,10 @@ public CompletableFuture resolve(NbCodeLanguageClient client, CodeAc wc.rewrite(cls, GeneratorUtilities.get(wc).insertClassMember(cls, field)); } }); - if (!edits.isEmpty()) { - codeAction.setEdit(new WorkspaceEdit(Collections.singletonMap(uri, edits))); - } + future.complete(edits.isEmpty() ? null : new WorkspaceEdit(Collections.singletonMap(uri, edits))); + } else { + future.complete(null); } - future.complete(codeAction); } catch (IOException | IllegalArgumentException ex) { future.completeExceptionally(ex); } 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 0fb7627645a9..fdb8307cc6d8 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 @@ -1050,7 +1050,7 @@ public CompletableFuture>> codeAction(CodeActio data.put(INDEX, index.getAndIncrement()); action.setData(data); } else if (inputAction.getEdit() != null) { - action.setEdit(fromAPI(inputAction.getEdit(), uri, client)); + action.setEdit(Utils.workspaceEditFromApi(inputAction.getEdit(), uri, client)); } result.add(Either.forRight(action)); } @@ -1160,7 +1160,7 @@ public CompletableFuture resolveCodeAction(CodeAction unresolved) { LazyCodeAction inputAction = lastCodeActions.get(data.getAsJsonPrimitive(INDEX).getAsInt()); if (inputAction != null) { try { - unresolved.setEdit(fromAPI(inputAction.getLazyEdit().get(), data.getAsJsonPrimitive(URL).getAsString(), client)); + unresolved.setEdit(Utils.workspaceEditFromApi(inputAction.getLazyEdit().get(), data.getAsJsonPrimitive(URL).getAsString(), client)); } catch (Exception e) { future.completeExceptionally(e); return; @@ -2240,36 +2240,6 @@ public Source getSource(String fileUri) { } } - static WorkspaceEdit fromAPI(org.netbeans.api.lsp.WorkspaceEdit edit, String uri, NbCodeLanguageClient client) { - List> documentChanges = new ArrayList<>(); - for (Union2 parts : edit.getDocumentChanges()) { - if (parts.hasFirst()) { - String docUri = parts.first().getDocument(); - try { - FileObject file = Utils.fromUri(docUri); - if (file == null) { - file = Utils.fromUri(uri); - } - FileObject fo = file; - if (fo != null) { - List edits = parts.first().getEdits().stream().map(te -> new TextEdit(new Range(Utils.createPosition(fo, te.getStartOffset()), Utils.createPosition(fo, te.getEndOffset())), te.getNewText())).collect(Collectors.toList()); - TextDocumentEdit tde = new TextDocumentEdit(new VersionedTextDocumentIdentifier(docUri, -1), edits); - documentChanges.add(Either.forLeft(tde)); - } - } catch (Exception ex) { - client.logMessage(new MessageParams(MessageType.Error, ex.getMessage())); - } - } else { - if (parts.second() instanceof org.netbeans.api.lsp.ResourceOperation.CreateFile) { - documentChanges.add(Either.forRight(new CreateFile(((org.netbeans.api.lsp.ResourceOperation.CreateFile) parts.second()).getNewFile()))); - } else { - throw new IllegalStateException(String.valueOf(parts.second())); - } - } - } - return new WorkspaceEdit(documentChanges); - } - static List modify2TextEdits(JavaSource js, Task task) throws IOException {//TODO: is this still used? 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 index 1ce3788cc645..b88b31213572 100644 --- 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 @@ -31,6 +31,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; import javax.lang.model.element.Element; @@ -62,6 +63,7 @@ @ServiceProvider(service = CodeActionsProvider.class, position = 50) public final class ToStringGenerator extends CodeActionsProvider { + private static final String GENERATE_TO_STRING = "nbls.java.generate.toString"; private static final String URI = "uri"; private static final String OFFSET = "offset"; private static final String FIELDS = "fields"; @@ -114,35 +116,34 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat data.put(URI, uri); data.put(OFFSET, offset); data.put(FIELDS, fields); - return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateToString(), CODE_GENERATOR_KIND, data, fields.isEmpty() ? null : "workbench.action.focusActiveEditorGroup")); + return Collections.singletonList(createCodeAction(client, Bundle.DN_GenerateToString(), CODE_GENERATOR_KIND, null, "nbls.generate.code", GENERATE_TO_STRING, data)); + } + + @Override + public Set getCommands() { + return Collections.singleton(GENERATE_TO_STRING); } @Override @NbBundle.Messages({ "DN_SelectToString=Select fields to be included in toString()", }) - public CompletableFuture resolve(NbCodeLanguageClient client, CodeAction codeAction, Object data) { - CompletableFuture future = new CompletableFuture<>(); + public CompletableFuture processCommand(NbCodeLanguageClient client, String command, List arguments) { + if (arguments.isEmpty()) { + return CompletableFuture.completedFuture(null); + } + JsonObject data = (JsonObject) arguments.get(0); + CompletableFuture future = new CompletableFuture<>(); try { - String uri = ((JsonObject) data).getAsJsonPrimitive(URI).getAsString(); - int offset = ((JsonObject) data).getAsJsonPrimitive(OFFSET).getAsInt(); - List fields = Arrays.asList(gson.fromJson(((JsonObject) data).get(FIELDS), QuickPickItem[].class)); + String uri = data.getAsJsonPrimitive(URI).getAsString(); + int offset = data.getAsJsonPrimitive(OFFSET).getAsInt(); + List fields = Arrays.asList(gson.fromJson(data.get(FIELDS), QuickPickItem[].class)); if (fields.isEmpty()) { - WorkspaceEdit edit = generate(uri, offset, fields); - if (edit != null) { - codeAction.setEdit(edit); - } - future.complete(codeAction); + future.complete(generate(uri, offset, fields)); } else { client.showQuickPick(new ShowQuickPickParams(Bundle.DN_GenerateToString(), Bundle.DN_SelectToString(), true, fields)).thenAccept(selected -> { try { - if (selected != null) { - WorkspaceEdit edit = generate(uri, offset, fields); - if (edit != null) { - codeAction.setEdit(edit); - } - } - future.complete(codeAction); + future.complete(selected != null ? generate(uri, offset, fields) : null); } catch (IOException | IllegalArgumentException ex) { future.completeExceptionally(ex); } 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 22447ba8bf92..5447ba3045c5 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 @@ -21,6 +21,7 @@ import com.google.gson.Gson; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import com.google.gson.JsonPrimitive; import java.beans.PropertyChangeListener; import java.io.File; import java.io.FileWriter; @@ -3018,7 +3019,13 @@ public CompletableFuture applyEdit(ApplyWorkspaceEdi assertTrue(generateGetterSetter.isPresent()); CodeAction resolvedCodeAction = server.getTextDocumentService().resolveCodeAction(generateGetterSetter.get()).get(); assertNotNull(resolvedCodeAction); - WorkspaceEdit edit = resolvedCodeAction.getEdit(); + Command command = resolvedCodeAction.getCommand(); + assertNotNull(command); + assertEquals("nbls.generate.code", command.getCommand()); + List args = command.getArguments(); + assertEquals(2, args.size()); + Object ret = server.getWorkspaceService().executeCommand(new ExecuteCommandParams(((JsonPrimitive) args.get(0)).getAsString(), Collections.singletonList(args.get(1)))).get(); + WorkspaceEdit edit = gson.fromJson(gson.toJsonTree(ret), WorkspaceEdit.class); assertNotNull(edit); assertEquals(1, edit.getChanges().size()); List fileChanges = edit.getChanges().get(uri); @@ -3048,7 +3055,7 @@ public void testCodeActionGenerateConstructor() throws Exception { try (Writer w = new FileWriter(src)) { w.write(code); } - AtomicReference data = new AtomicReference<>(); + AtomicReference data = new AtomicReference<>(); Launcher serverLauncher = createClientLauncherWithLogging(new TestCodeLanguageClient() { @Override public CompletableFuture applyEdit(ApplyWorkspaceEditParams params) { @@ -3058,7 +3065,7 @@ public CompletableFuture applyEdit(ApplyWorkspaceEdi @Override public CompletableFuture, String>>> showMultiStepInput(ShowMutliStepInputParams params) { Map, String>> map = new HashMap<>(); - List fields = Arrays.asList(gson.fromJson(((JsonObject)data.get()).getAsJsonObject("data").get("fields"), QuickPickItem[].class)); + List fields = Arrays.asList(gson.fromJson(data.get().get("fields"), QuickPickItem[].class)); map.put("fields", Either.forLeft(fields.stream().filter(item -> item.isPicked()).collect(Collectors.toList()))); return CompletableFuture.completedFuture(map); } @@ -3077,10 +3084,16 @@ public CompletableFuture, String>>> showM .filter(a -> Bundle.DN_GenerateConstructor().equals(a.getTitle())) .findAny(); assertTrue(generateConstructor.isPresent()); - data.set(generateConstructor.get().getData()); CodeAction resolvedCodeAction = server.getTextDocumentService().resolveCodeAction(generateConstructor.get()).get(); assertNotNull(resolvedCodeAction); - WorkspaceEdit edit = resolvedCodeAction.getEdit(); + Command command = resolvedCodeAction.getCommand(); + assertNotNull(command); + assertEquals("nbls.generate.code", command.getCommand()); + List args = command.getArguments(); + assertEquals(2, args.size()); + data.set((JsonObject) args.get(1)); + Object ret = server.getWorkspaceService().executeCommand(new ExecuteCommandParams(((JsonPrimitive) args.get(0)).getAsString(), Collections.singletonList(args.get(1)))).get(); + WorkspaceEdit edit = gson.fromJson(gson.toJsonTree(ret), WorkspaceEdit.class); assertNotNull(edit); assertEquals(1, edit.getChanges().size()); List fileChanges = edit.getChanges().get(uri); @@ -3151,7 +3164,13 @@ public CompletableFuture applyEdit(ApplyWorkspaceEdi assertTrue(generateGetterSetter.isPresent()); CodeAction resolvedCodeAction = server.getTextDocumentService().resolveCodeAction(generateGetterSetter.get()).get(); assertNotNull(resolvedCodeAction); - WorkspaceEdit edit = resolvedCodeAction.getEdit(); + Command command = resolvedCodeAction.getCommand(); + assertNotNull(command); + assertEquals("nbls.generate.code", command.getCommand()); + List args = command.getArguments(); + assertEquals(2, args.size()); + Object ret = server.getWorkspaceService().executeCommand(new ExecuteCommandParams(((JsonPrimitive) args.get(0)).getAsString(), Collections.singletonList(args.get(1)))).get(); + WorkspaceEdit edit = gson.fromJson(gson.toJsonTree(ret), WorkspaceEdit.class); assertNotNull(edit); assertEquals(1, edit.getChanges().size()); List fileChanges = edit.getChanges().get(uri); @@ -3180,7 +3199,13 @@ public CompletableFuture applyEdit(ApplyWorkspaceEdi assertTrue(generateGetter.isPresent()); resolvedCodeAction = server.getTextDocumentService().resolveCodeAction(generateGetter.get()).get(); assertNotNull(resolvedCodeAction); - edit = resolvedCodeAction.getEdit(); + command = resolvedCodeAction.getCommand(); + assertNotNull(command); + assertEquals("nbls.generate.code", command.getCommand()); + args = command.getArguments(); + assertEquals(2, args.size()); + ret = server.getWorkspaceService().executeCommand(new ExecuteCommandParams(((JsonPrimitive) args.get(0)).getAsString(), Collections.singletonList(args.get(1)))).get(); + edit = gson.fromJson(gson.toJsonTree(ret), WorkspaceEdit.class); assertNotNull(edit); assertEquals(1, edit.getChanges().size()); fileChanges = edit.getChanges().get(uri); @@ -3206,7 +3231,7 @@ public void testSourceActionConstructor() throws Exception { try (Writer w = new FileWriter(src)) { w.write(code); } - AtomicReference data = new AtomicReference<>(); + AtomicReference data = new AtomicReference<>(); Launcher serverLauncher = createClientLauncherWithLogging(new TestCodeLanguageClient() { @Override public CompletableFuture applyEdit(ApplyWorkspaceEditParams params) { @@ -3216,9 +3241,9 @@ public CompletableFuture applyEdit(ApplyWorkspaceEdi @Override public CompletableFuture, String>>> showMultiStepInput(ShowMutliStepInputParams params) { Map, String>> map = new HashMap<>(); - List constructors = Arrays.asList(gson.fromJson(((JsonObject)data.get()).getAsJsonObject("data").get("constructors"), QuickPickItem[].class)); + List constructors = Arrays.asList(gson.fromJson(data.get().get("constructors"), QuickPickItem[].class)); map.put("constructors", Either.forLeft(constructors.subList(0, 2))); - List fields = Arrays.asList(gson.fromJson(((JsonObject)data.get()).getAsJsonObject("data").get("fields"), QuickPickItem[].class)); + List fields = Arrays.asList(gson.fromJson(data.get().get("fields"), QuickPickItem[].class)); map.put("fields", Either.forLeft(fields)); return CompletableFuture.completedFuture(map); } @@ -3237,10 +3262,16 @@ public CompletableFuture, String>>> showM .filter(a -> Bundle.DN_GenerateConstructor().equals(a.getTitle())) .findAny(); assertTrue(generateConstructor.isPresent()); - data.set(generateConstructor.get().getData()); CodeAction resolvedCodeAction = server.getTextDocumentService().resolveCodeAction(generateConstructor.get()).get(); assertNotNull(resolvedCodeAction); - WorkspaceEdit edit = resolvedCodeAction.getEdit(); + Command command = resolvedCodeAction.getCommand(); + assertNotNull(command); + assertEquals("nbls.generate.code", command.getCommand()); + List args = command.getArguments(); + assertEquals(2, args.size()); + data.set((JsonObject) args.get(1)); + Object ret = server.getWorkspaceService().executeCommand(new ExecuteCommandParams(((JsonPrimitive) args.get(0)).getAsString(), Collections.singletonList(args.get(1)))).get(); + WorkspaceEdit edit = gson.fromJson(gson.toJsonTree(ret), WorkspaceEdit.class); assertNotNull(edit); assertEquals(1, edit.getChanges().size()); List fileChanges = edit.getChanges().get(uri); @@ -3317,7 +3348,13 @@ public CompletableFuture> showQuickPick(ShowQuickPickParams assertTrue(generateEquals.isPresent()); CodeAction resolvedCodeAction = server.getTextDocumentService().resolveCodeAction(generateEquals.get()).get(); assertNotNull(resolvedCodeAction); - WorkspaceEdit edit = resolvedCodeAction.getEdit(); + Command command = resolvedCodeAction.getCommand(); + assertNotNull(command); + assertEquals("nbls.generate.code", command.getCommand()); + List args = command.getArguments(); + assertEquals(2, args.size()); + Object ret = server.getWorkspaceService().executeCommand(new ExecuteCommandParams(((JsonPrimitive) args.get(0)).getAsString(), Collections.singletonList(args.get(1)))).get(); + WorkspaceEdit edit = gson.fromJson(gson.toJsonTree(ret), WorkspaceEdit.class); assertNotNull(edit); assertEquals(1, edit.getChanges().size()); List fileChanges = edit.getChanges().get(uri); @@ -3384,7 +3421,13 @@ public CompletableFuture> showQuickPick(ShowQuickPickParams assertTrue(generateToString.isPresent()); CodeAction resolvedCodeAction = server.getTextDocumentService().resolveCodeAction(generateToString.get()).get(); assertNotNull(resolvedCodeAction); - WorkspaceEdit edit = resolvedCodeAction.getEdit(); + Command command = resolvedCodeAction.getCommand(); + assertNotNull(command); + assertEquals("nbls.generate.code", command.getCommand()); + List args = command.getArguments(); + assertEquals(2, args.size()); + Object ret = server.getWorkspaceService().executeCommand(new ExecuteCommandParams(((JsonPrimitive) args.get(0)).getAsString(), Collections.singletonList(args.get(1)))).get(); + WorkspaceEdit edit = gson.fromJson(gson.toJsonTree(ret), WorkspaceEdit.class); assertNotNull(edit); assertEquals(1, edit.getChanges().size()); List fileChanges = edit.getChanges().get(uri); @@ -3414,7 +3457,7 @@ public void testSourceActionDelegateMethod() throws Exception { try (Writer w = new FileWriter(src)) { w.write(code); } - AtomicReference data = new AtomicReference<>(); + AtomicReference data = new AtomicReference<>(); Launcher serverLauncher = createClientLauncherWithLogging(new TestCodeLanguageClient() { @Override public CompletableFuture applyEdit(ApplyWorkspaceEditParams params) { @@ -3434,7 +3477,7 @@ public CompletableFuture> showQuickPick(ShowQuickPickParams @Override public CompletableFuture, String>>> showMultiStepInput(ShowMutliStepInputParams params) { Map, String>> map = new HashMap<>(); - List fields = Arrays.asList(gson.fromJson(((JsonObject)data.get()).getAsJsonObject("data").get("fields"), QuickPickItem[].class)); + List fields = Arrays.asList(gson.fromJson(data.get().get("fields"), QuickPickItem[].class)); map.put("methods", Either.forLeft(Arrays.asList(new QuickPickItem[] { new QuickPickItem("s.chars(): IntStream", null, null, false, new CodeActionsProvider.ElementData(ElementHandleAccessor.getInstance().create(ElementKind.METHOD, new String[] { "java.lang.CharSequence", @@ -3464,10 +3507,16 @@ public CompletableFuture, String>>> showM .filter(a -> Bundle.DN_GenerateDelegateMethod().equals(a.getTitle())) .findAny(); assertTrue(generateDelegateMethod.isPresent()); - data.set(generateDelegateMethod.get().getData()); CodeAction resolvedCodeAction = server.getTextDocumentService().resolveCodeAction(generateDelegateMethod.get()).get(); assertNotNull(resolvedCodeAction); - WorkspaceEdit edit = resolvedCodeAction.getEdit(); + Command command = resolvedCodeAction.getCommand(); + assertNotNull(command); + assertEquals("nbls.generate.code", command.getCommand()); + List args = command.getArguments(); + assertEquals(2, args.size()); + data.set((JsonObject) args.get(1)); + Object ret = server.getWorkspaceService().executeCommand(new ExecuteCommandParams(((JsonPrimitive) args.get(0)).getAsString(), Collections.singletonList(args.get(1)))).get(); + WorkspaceEdit edit = gson.fromJson(gson.toJsonTree(ret), WorkspaceEdit.class); assertNotNull(edit); assertEquals(1, edit.getChanges().size()); List fileChanges = edit.getChanges().get(uri); @@ -3529,7 +3578,13 @@ public CompletableFuture> showQuickPick(ShowQuickPickParams assertTrue(generateOverrideMethod.isPresent()); CodeAction resolvedCodeAction = server.getTextDocumentService().resolveCodeAction(generateOverrideMethod.get()).get(); assertNotNull(resolvedCodeAction); - WorkspaceEdit edit = resolvedCodeAction.getEdit(); + Command command = resolvedCodeAction.getCommand(); + assertNotNull(command); + assertEquals("nbls.generate.code", command.getCommand()); + List args = command.getArguments(); + assertEquals(2, args.size()); + Object ret = server.getWorkspaceService().executeCommand(new ExecuteCommandParams(((JsonPrimitive) args.get(0)).getAsString(), Collections.singletonList(args.get(1)))).get(); + WorkspaceEdit edit = gson.fromJson(gson.toJsonTree(ret), WorkspaceEdit.class); assertNotNull(edit); assertEquals(1, edit.getChanges().size()); List fileChanges = edit.getChanges().get(uri); @@ -3593,7 +3648,13 @@ public CompletableFuture showInputBox(ShowInputBoxParams params) { assertTrue(generateLogger.isPresent()); CodeAction resolvedCodeAction = server.getTextDocumentService().resolveCodeAction(generateLogger.get()).get(); assertNotNull(resolvedCodeAction); - WorkspaceEdit edit = resolvedCodeAction.getEdit(); + Command command = resolvedCodeAction.getCommand(); + assertNotNull(command); + assertEquals("nbls.generate.code", command.getCommand()); + List args = command.getArguments(); + assertEquals(2, args.size()); + Object ret = server.getWorkspaceService().executeCommand(new ExecuteCommandParams(((JsonPrimitive) args.get(0)).getAsString(), Collections.singletonList(args.get(1)))).get(); + WorkspaceEdit edit = gson.fromJson(gson.toJsonTree(ret), WorkspaceEdit.class); assertNotNull(edit); assertEquals(1, edit.getChanges().size()); List fileChanges = edit.getChanges().get(uri); diff --git a/java/java.lsp.server/vscode/src/extension.ts b/java/java.lsp.server/vscode/src/extension.ts index d743950e5478..8ca039fd30b3 100644 --- a/java/java.lsp.server/vscode/src/extension.ts +++ b/java/java.lsp.server/vscode/src/extension.ts @@ -47,6 +47,7 @@ import * as fs from 'fs'; import * as path from 'path'; import { ChildProcess } from 'child_process'; import * as vscode from 'vscode'; +import * as ls from 'vscode-languageserver-protocol'; import * as launcher from './nbcode'; import {NbTestAdapter} from './testAdapter'; import { asRanges, StatusMessageRequest, ShowStatusMessageParams, QuickPickRequest, InputBoxRequest, MutliStepInputRequest, TestProgressNotification, DebugConnector, @@ -535,21 +536,15 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.surround.with', async (items) => { const selected: any = await window.showQuickPick(items, { placeHolder: 'Surround with ...' }); if (selected) { - if (selected.userData.edit && selected.userData.edit.changes) { - let edit = new vscode.WorkspaceEdit(); - Object.keys(selected.userData.edit.changes).forEach(key => { - edit.set(vscode.Uri.parse(key), selected.userData.edit.changes[key].map((change: any) => { - let start = new vscode.Position(change.range.start.line, change.range.start.character); - let end = new vscode.Position(change.range.end.line, change.range.end.character); - return new vscode.TextEdit(new vscode.Range(start, end), change.newText); - })); - }); + if (selected.userData.edit) { + const edit = await (await client).protocol2CodeConverter.asWorkspaceEdit(selected.userData.edit as ls.WorkspaceEdit); await workspace.applyEdit(edit); + await commands.executeCommand('workbench.action.focusActiveEditorGroup'); } await commands.executeCommand(selected.userData.command.command, ...(selected.userData.command.arguments || [])); } })); - context.subscriptions.push(commands.registerCommand('nbls.db.add.all.connection', async () => { + context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.db.add.all.connection', async () => { const ADD_JDBC = 'Add Database Connection'; const ADD_ADB = 'Add Oracle Autonomous DB'; const selected: any = await window.showQuickPick([ADD_JDBC, ADD_ADB], { placeHolder: 'Select type...' }); @@ -560,6 +555,15 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { } })); + context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.generate.code', async (command, data) => { + const edit: any = await commands.executeCommand(command, data); + if (edit) { + const wsEdit = await (await client).protocol2CodeConverter.asWorkspaceEdit(edit as ls.WorkspaceEdit); + await workspace.applyEdit(wsEdit); + await commands.executeCommand('workbench.action.focusActiveEditorGroup'); + } + })); + async function findRunConfiguration(uri : vscode.Uri) : Promise { // do not invoke debug start with no (java+) configurations, as it would probably create an user prompt let cfg = vscode.workspace.getConfiguration("launch");