Skip to content

Commit

Permalink
Merge pull request #7368 from lahodaj/JAVAVSCODE-153
Browse files Browse the repository at this point in the history
Filtering TextDocumentService.codeAction results based on the provided kinds.
  • Loading branch information
lahodaj committed May 14, 2024
2 parents cc68158 + 2221df6 commit c6843c4
Show file tree
Hide file tree
Showing 11 changed files with 311 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -93,6 +94,7 @@
public class MicronautDataEndpointGenerator implements CodeActionProvider, CommandProvider {

private static final String SOURCE = "source";
private static final Set<String> SUPPORTED_CODE_ACTION_KINDS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(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";
Expand All @@ -107,6 +109,11 @@ public class MicronautDataEndpointGenerator implements CodeActionProvider, Comma
return new NotifyDescriptor.QuickPick.Item(label, description);
}).create();

@Override
public Set<String> getSupportedCodeActionKinds() {
return SUPPORTED_CODE_ACTION_KINDS;
}

@Override
@NbBundle.Messages({
"DN_GenerateDataEndpoint=Generate Data Endpoint...",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@
public class MicronautEndpointTestGenerator implements CodeActionProvider, CommandProvider {

private static final String SOURCE = "source";
private static final Set<String> SUPPORTED_CODE_ACTION_KINDS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(SOURCE)));
private static final String GENERATE_MICRONAUT_ENDPOINT_TEST = "nbls.micronaut.generate.endpoint.test";
private static final String CONTROLLER_ANNOTATION_NAME = "io.micronaut.http.annotation.Controller";
private static final String MICRONAUT_TEST_ANNOTATION_NAME = "io.micronaut.test.extensions.junit5.annotation.MicronautTest";
Expand Down Expand Up @@ -148,6 +149,11 @@ public class MicronautEndpointTestGenerator implements CodeActionProvider, Comma
return new NotifyDescriptor.QuickPick.Item(label, description);
}).create();

@Override
public Set<String> getSupportedCodeActionKinds() {
return SUPPORTED_CODE_ACTION_KINDS;
}

@Override
@NbBundle.Messages({
"DN_GenerateEndpointTest=Generate Micronaut Endpoint Tests...",
Expand Down
13 changes: 13 additions & 0 deletions ide/api.lsp/apichanges.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,19 @@
<!-- ACTUAL CHANGES BEGIN HERE: -->

<changes>
<change id="CodeActionProvider.getSupportedCodeActionKinds">
<api name="LSP_API"/>
<summary>Adding CodeActionProvider.getSupportedCodeActionKinds method</summary>
<version major="1" minor="27"/>
<date day="8" month="5" year="2024"/>
<author login="jlahoda"/>
<compatibility binary="compatible" source="compatible" addition="yes" deletion="no" />
<description>
A <code>CodeActionProvider.getSupportedCodeActionKinds</code> method is
introduced that allows to specify supported kinds of code actions.
</description>
<class package="org.netbeans.spi.lsp" name="CodeActionProvider"/>
</change>
<change id="ErrorProvider.Context.getHintsConfigFile">
<api name="LSP_API"/>
<summary>Adding ErrorProvider.Context.getHintsConfigFile() method</summary>
Expand Down
2 changes: 1 addition & 1 deletion ide/api.lsp/manifest.mf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Manifest-Version: 1.0
OpenIDE-Module: org.netbeans.api.lsp/1
OpenIDE-Module-Localizing-Bundle: org/netbeans/api/lsp/Bundle.properties
OpenIDE-Module-Specification-Version: 1.26
OpenIDE-Module-Specification-Version: 1.27
AutoUpdate-Show-In-Client: false
13 changes: 13 additions & 0 deletions ide/api.lsp/src/org/netbeans/spi/lsp/CodeActionProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
package org.netbeans.spi.lsp;

import java.util.List;
import java.util.Set;
import javax.swing.text.Document;
import org.netbeans.api.annotations.common.CheckForNull;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.api.lsp.CodeAction;
import org.netbeans.api.lsp.Range;
Expand All @@ -42,4 +44,15 @@ public interface CodeActionProvider {
* @since 1.23
*/
public List<CodeAction> getCodeActions(@NonNull Document doc, @NonNull Range range, @NonNull Lookup context);

/**
* Return the set of code action kinds produced by this provider. May return null
* if unknown/all kinds may be produced.
*
* @return the set of supported code action kinds, or {@code null}
* @since 1.27
*/
public default @CheckForNull Set<String> getSupportedCodeActionKinds() {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
Expand Down Expand Up @@ -571,4 +572,10 @@ public static WorkspaceEdit workspaceEditFromApi(org.netbeans.api.lsp.WorkspaceE
}
return new WorkspaceEdit(documentChanges);
}

public static Predicate<String> codeActionKindFilter(List<String> only) {
return k -> only == null ||
only.stream()
.anyMatch(o -> k.equals(o) || k.startsWith(o + "."));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.Position;
import org.eclipse.xtext.xbase.lib.Pure;
import org.netbeans.api.annotations.common.CheckForNull;
import org.netbeans.api.java.source.CompilationInfo;
import org.netbeans.api.java.source.ElementHandle;
import org.netbeans.modules.java.lsp.server.Utils;
Expand All @@ -54,6 +55,10 @@ public abstract class CodeActionsProvider {
public static final String DATA = "data";
protected static final String ERROR = "<error>"; //NOI18N

public @CheckForNull Set<String> getSupportedCodeActionKinds() {
return null;
}

public abstract List<CodeAction> getCodeActions(NbCodeLanguageClient client, ResultIterator resultIterator, CodeActionParams params) throws Exception;

public CompletableFuture<CodeAction> resolve(NbCodeLanguageClient client, CodeAction codeAction, Object data) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.function.Predicate;
import javax.swing.text.Document;
import javax.swing.text.StyledDocument;
import org.eclipse.lsp4j.CodeAction;
Expand Down Expand Up @@ -72,6 +73,23 @@ public CompletableFuture<Object> processCommand(NbCodeLanguageClient client, Str
return CompletableFuture.completedFuture(false);
}

@Override
public Set<String> getSupportedCodeActionKinds() {
Set<String> supportedCodeActionKinds = new HashSet<>();

for (CodeActionProvider caProvider : Lookup.getDefault().lookupAll(CodeActionProvider.class)) {
Set<String> providerSupportedCodeActionKinds = caProvider.getSupportedCodeActionKinds();

if (providerSupportedCodeActionKinds == null) {
return null;
}

supportedCodeActionKinds.addAll(providerSupportedCodeActionKinds);
}

return supportedCodeActionKinds;
}

@Override
public List<CodeAction> getCodeActions(NbCodeLanguageClient client, ResultIterator resultIterator, CodeActionParams params) throws Exception {
lastCodeActions = new ArrayList<>();
Expand All @@ -84,7 +102,14 @@ public List<CodeAction> getCodeActions(NbCodeLanguageClient client, ResultIterat
org.netbeans.api.lsp.Range r = new org.netbeans.api.lsp.Range(startOffset, endOffset);
List<String> only = params.getContext().getOnly();
Lookup l = only != null ? Lookups.fixed(client, resultIterator, only) : Lookups.fixed(client, resultIterator);
Predicate<String> codeActionKindPermitted = Utils.codeActionKindFilter(only);
for (CodeActionProvider caProvider : Lookup.getDefault().lookupAll(CodeActionProvider.class)) {
Set<String> supportedCodeActionKinds = caProvider.getSupportedCodeActionKinds();
if (supportedCodeActionKinds != null &&
supportedCodeActionKinds.stream()
.noneMatch(kind -> codeActionKindPermitted.test(kind))) {
continue;
}
try {
for (org.netbeans.api.lsp.CodeAction ca : caProvider.getCodeActions(doc, r, l)) {
Object data = null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
* 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.Arrays;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.text.StyledDocument;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.CodeActionKind;
import org.eclipse.lsp4j.CodeActionParams;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.ResourceOperation;
import org.eclipse.lsp4j.TextDocumentEdit;
import org.eclipse.lsp4j.TextEdit;
import org.eclipse.lsp4j.VersionedTextDocumentIdentifier;
import org.eclipse.lsp4j.WorkspaceEdit;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.netbeans.api.java.source.CompilationController;
import org.netbeans.api.java.source.JavaSource;
import org.netbeans.api.java.source.ModificationResult;
import org.netbeans.modules.java.editor.codegen.GeneratorUtils;
import org.netbeans.modules.java.hints.introduce.IntroduceFixBase;
import org.netbeans.modules.java.hints.introduce.IntroduceHint;
import org.netbeans.modules.java.hints.introduce.IntroduceKind;
import org.netbeans.modules.java.lsp.server.Utils;
import org.netbeans.modules.parsing.api.ResultIterator;
import org.netbeans.spi.editor.hints.ErrorDescription;
import org.netbeans.spi.editor.hints.Fix;
import org.openide.filesystems.FileObject;
import org.openide.util.lookup.ServiceProvider;

@ServiceProvider(service = CodeActionsProvider.class)
public final class IntroduceCodeActions extends CodeActionsProvider {

private static final Set<String> SUPPORTED_CODE_ACTION_KINDS =
Collections.unmodifiableSet(new HashSet<>(Arrays.asList(CodeActionKind.RefactorExtract)));

public IntroduceCodeActions() {
}

@Override
public Set<String> getSupportedCodeActionKinds() {
return SUPPORTED_CODE_ACTION_KINDS;
}

@Override
public List<CodeAction> getCodeActions(NbCodeLanguageClient client, ResultIterator resultIterator, CodeActionParams params) throws Exception {
Range range = params.getRange();
List<CodeAction> result = new ArrayList<>();

if (client.getNbCodeCapabilities().wantsJavaSupport() && !range.getStart().equals(range.getEnd())) {
CompilationController cc = resultIterator.getParserResult() != null ? CompilationController.get(resultIterator.getParserResult()) : null;

if (cc != null) {
cc.toPhase(JavaSource.Phase.RESOLVED);

StyledDocument doc = (StyledDocument) cc.getDocument();
int startOffset = Utils.getOffset(doc, range.getStart());
int endOffset = Utils.getOffset(doc, range.getEnd());

for (ErrorDescription err : IntroduceHint.computeError(cc, startOffset, endOffset, new EnumMap<IntroduceKind, Fix>(IntroduceKind.class), new EnumMap<IntroduceKind, String>(IntroduceKind.class), new AtomicBoolean())) {
for (Fix fix : err.getFixes().getFixes()) {
if (fix instanceof IntroduceFixBase) {
try {
ModificationResult changes = ((IntroduceFixBase) fix).getModificationResult();
if (changes != null) {
List<Either<TextDocumentEdit, ResourceOperation>> documentChanges = new ArrayList<>();
Set<? extends FileObject> fos = changes.getModifiedFileObjects();
if (fos.size() == 1) {
FileObject fileObject = fos.iterator().next();
List<? extends ModificationResult.Difference> diffs = changes.getDifferences(fileObject);
if (diffs != null) {
List<TextEdit> edits = new ArrayList<>();
for (ModificationResult.Difference diff : diffs) {
String newText = diff.getNewText();
edits.add(new TextEdit(new Range(Utils.createPosition(fileObject, diff.getStartPosition().getOffset()),
Utils.createPosition(fileObject, diff.getEndPosition().getOffset())),
newText != null ? newText : ""));
}
documentChanges.add(Either.forLeft(new TextDocumentEdit(new VersionedTextDocumentIdentifier(Utils.toUri(fileObject), -1), edits)));
}
CodeAction codeAction = new CodeAction(fix.getText());
codeAction.setKind(CodeActionKind.RefactorExtract);
codeAction.setEdit(new WorkspaceEdit(documentChanges));
int renameOffset = ((IntroduceFixBase) fix).getNameOffset(changes);
if (renameOffset >= 0) {
codeAction.setCommand(new Command("Rename", client.getNbCodeCapabilities().getCommandPrefix() + ".rename.element.at", Collections.singletonList(renameOffset)));
}
result.add(codeAction);
}
}
} catch (GeneratorUtils.DuplicateMemberException dme) {
}
}
}
}
}
}

return result;
}

}

0 comments on commit c6843c4

Please sign in to comment.